summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2017-09-14 10:08:51 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-09-14 11:06:37 +0200
commitbdeb81b73122d6962ca4796c573dcbc636ccb195 (patch)
tree946530bfe0f7bd506ea26633f8374fde5180c6cb
parent529623a9f3590ac0ac3baf3ccece793c0ce7f825 (diff)
parentc7d46325e635d1fb4482b53dce866e2c55026a7e (diff)
Merge remote-tracking branch 'origin/5.9' into 5.10
m---------src/3rdparty0
-rw-r--r--src/core/config/linux.pri4
-rw-r--r--src/core/gl_surface_qt.cpp6
-rw-r--r--src/core/renderer_host/user_resource_controller_host.cpp5
-rw-r--r--src/core/renderer_host/web_channel_ipc_transport_host.cpp14
-rw-r--r--src/core/renderer_host/web_channel_ipc_transport_host.h1
-rw-r--r--src/core/web_contents_adapter.cpp25
-rw-r--r--src/core/web_contents_adapter.h4
-rw-r--r--src/core/web_contents_adapter_client.h42
-rw-r--r--src/core/web_contents_view_qt.cpp3
-rw-r--r--src/core/web_engine_context.cpp48
-rw-r--r--src/core/web_engine_context.h2
-rw-r--r--src/core/web_event_factory.cpp5
-rw-r--r--src/webengine/api/qquickwebengineview.cpp6
-rw-r--r--src/webenginewidgets/api/qwebenginepage.cpp10
-rw-r--r--src/webenginewidgets/api/qwebengineview.cpp3
-rw-r--r--src/webenginewidgets/api/qwebengineview_p.h1
-rw-r--r--tests/auto/shared/http.pri3
-rw-r--r--tests/auto/shared/httpreqrep.cpp91
-rw-r--r--tests/auto/shared/httpreqrep.h75
-rw-r--r--tests/auto/shared/httpserver.cpp70
-rw-r--r--tests/auto/shared/httpserver.h61
-rw-r--r--tests/auto/shared/waitforsignal.h90
-rw-r--r--tests/auto/widgets/qwebenginedownloads/qwebenginedownloads.pro3
-rw-r--r--tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp595
-rw-r--r--tests/auto/widgets/widgets.pro4
26 files changed, 1127 insertions, 44 deletions
diff --git a/src/3rdparty b/src/3rdparty
-Subproject 0f35665bef3fc26a423ee7770356749c9a6d7dc
+Subproject d23df0c25c334dbde96ef5eb65cc098eee47859
diff --git a/src/core/config/linux.pri b/src/core/config/linux.pri
index c2d41b4d0..c0f2f6289 100644
--- a/src/core/config/linux.pri
+++ b/src/core/config/linux.pri
@@ -41,7 +41,7 @@ cross_compile:!host_build {
!isEmpty(TOOLCHAIN_SYSROOT): gn_args += target_sysroot=\"$${TOOLCHAIN_SYSROOT}\"
}
-contains(QT_ARCH, "arm"):!host_build {
+contains(QT_ARCH, "arm") {
# Extract ARM specific compiler options that we have to pass to gn,
# but let gn figure out a default if an option is not present.
MTUNE = $$extractCFlag("-mtune=.*")
@@ -78,7 +78,7 @@ contains(QT_ARCH, "arm"):!host_build {
else: contains(QMAKE_CFLAGS, "-mthumb"): gn_args += arm_use_thumb=true
}
-contains(QT_ARCH, "mips"):!host_build {
+contains(QT_ARCH, "mips") {
MARCH = $$extractCFlag("-march=.*")
!isEmpty(MARCH) {
equals(MARCH, "mips32r6"): gn_args += mips_arch_variant=\"r6\"
diff --git a/src/core/gl_surface_qt.cpp b/src/core/gl_surface_qt.cpp
index 69609f4d4..93c1c5293 100644
--- a/src/core/gl_surface_qt.cpp
+++ b/src/core/gl_surface_qt.cpp
@@ -48,6 +48,7 @@
#include <QGuiApplication>
#include "gl_context_qt.h"
#include "qtwebenginecoreglobal_p.h"
+#include "web_engine_context.h"
#include "base/logging.h"
#include "gpu/ipc/service/image_transport_surface.h"
@@ -652,6 +653,11 @@ bool InitializeGLOneOffPlatform()
return false;
}
+bool usingSoftwareDynamicGL()
+{
+ return QtWebEngineCore::usingSoftwareDynamicGL();
+}
+
scoped_refptr<GLSurface>
CreateOffscreenGLSurfaceWithFormat(const gfx::Size& size, GLSurfaceFormat format)
{
diff --git a/src/core/renderer_host/user_resource_controller_host.cpp b/src/core/renderer_host/user_resource_controller_host.cpp
index f90aebda6..2799d5d85 100644
--- a/src/core/renderer_host/user_resource_controller_host.cpp
+++ b/src/core/renderer_host/user_resource_controller_host.cpp
@@ -88,11 +88,6 @@ void UserResourceControllerHost::WebContentsObserverHelper::RenderFrameHostChang
{
if (oldHost)
oldHost->Send(new RenderFrameObserverHelper_ClearScripts(oldHost->GetRoutingID()));
-
- content::WebContents *contents = web_contents();
- Q_FOREACH (const UserScript &script, m_controllerHost->m_perContentsScripts.value(contents))
- newHost->Send(new RenderFrameObserverHelper_AddScript(newHost->GetRoutingID(),
- script.data()));
}
void UserResourceControllerHost::WebContentsObserverHelper::WebContentsDestroyed()
diff --git a/src/core/renderer_host/web_channel_ipc_transport_host.cpp b/src/core/renderer_host/web_channel_ipc_transport_host.cpp
index 1cd4e4063..c47b255b7 100644
--- a/src/core/renderer_host/web_channel_ipc_transport_host.cpp
+++ b/src/core/renderer_host/web_channel_ipc_transport_host.cpp
@@ -40,6 +40,7 @@
#include "web_channel_ipc_transport_host.h"
#include "base/strings/string16.h"
+#include "content/public/browser/render_view_host.h"
#include "common/qt_messages.h"
#include "type_conversion.h"
@@ -61,11 +62,16 @@ WebChannelIPCTransportHost::~WebChannelIPCTransportHost()
{
}
-void WebChannelIPCTransportHost::RenderViewHostChanged(content::RenderViewHost *, content::RenderViewHost *)
+void WebChannelIPCTransportHost::RenderViewHostChanged(content::RenderViewHost *oldHost, content::RenderViewHost *)
{
- // This means that we were moved into a different RenderView, possibly in a different
- // render process and that we lost our WebChannelIPCTransport object and its state.
- Send(new WebChannelIPCTransport_Install(routing_id(), m_worldId));
+ if (oldHost)
+ oldHost->Send(new WebChannelIPCTransport_Uninstall(oldHost->GetRoutingID(), m_worldId));
+}
+
+void WebChannelIPCTransportHost::RenderViewCreated(content::RenderViewHost *view_host)
+{
+ // Make sure the new view knows a webchannel is installed and in which world.
+ view_host->Send(new WebChannelIPCTransport_Install(view_host->GetRoutingID(), m_worldId));
}
void WebChannelIPCTransportHost::setWorldId(uint worldId)
diff --git a/src/core/renderer_host/web_channel_ipc_transport_host.h b/src/core/renderer_host/web_channel_ipc_transport_host.h
index aa406471c..a1e697a91 100644
--- a/src/core/renderer_host/web_channel_ipc_transport_host.h
+++ b/src/core/renderer_host/web_channel_ipc_transport_host.h
@@ -60,6 +60,7 @@ public:
// WebContentsObserver
void RenderViewHostChanged(content::RenderViewHost* old_host, content::RenderViewHost* new_host) override;
+ void RenderViewCreated(content::RenderViewHost* render_view_host) override;
// QWebChannelAbstractTransport
void sendMessage(const QJsonObject &message) override;
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index 6181c0b72..827349115 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -926,7 +926,9 @@ void WebContentsAdapter::updateWebPreferences(const content::WebPreferences & we
d->webContents->GetRenderViewHost()->UpdateWebkitPreferences(webPreferences);
}
-void WebContentsAdapter::download(const QUrl &url, const QString &suggestedFileName)
+void WebContentsAdapter::download(const QUrl &url, const QString &suggestedFileName,
+ const QUrl &referrerUrl,
+ ReferrerPolicy referrerPolicy)
{
Q_D(WebContentsAdapter);
content::BrowserContext *bctx = webContents()->GetBrowserContext();
@@ -939,9 +941,19 @@ void WebContentsAdapter::download(const QUrl &url, const QString &suggestedFileN
dlmd->setDownloadType(BrowserContextAdapterClient::UserRequested);
dlm->SetDelegate(dlmd);
+ GURL gurl = toGurl(url);
std::unique_ptr<content::DownloadUrlParameters> params(
- content::DownloadUrlParameters::CreateForWebContentsMainFrame(webContents(), toGurl(url)));
+ content::DownloadUrlParameters::CreateForWebContentsMainFrame(webContents(), gurl));
+
params->set_suggested_name(toString16(suggestedFileName));
+
+ // referrer logic based on chrome/browser/renderer_context_menu/render_view_context_menu.cc:
+ params->set_referrer(
+ content::Referrer::SanitizeForRequest(
+ gurl,
+ content::Referrer(toGurl(referrerUrl).GetAsReferrer(),
+ static_cast<blink::WebReferrerPolicy>(referrerPolicy))));
+
dlm->DownloadUrl(std::move(params));
}
@@ -1473,4 +1485,13 @@ ASSERT_ENUMS_MATCH(WebContentsAdapterClient::SaveToDiskDisposition, WindowOpenDi
ASSERT_ENUMS_MATCH(WebContentsAdapterClient::OffTheRecordDisposition, WindowOpenDisposition::OFF_THE_RECORD)
ASSERT_ENUMS_MATCH(WebContentsAdapterClient::IgnoreActionDisposition, WindowOpenDisposition::IGNORE_ACTION)
+ASSERT_ENUMS_MATCH(ReferrerPolicy::Always, blink::kWebReferrerPolicyAlways)
+ASSERT_ENUMS_MATCH(ReferrerPolicy::Default, blink::kWebReferrerPolicyDefault)
+ASSERT_ENUMS_MATCH(ReferrerPolicy::NoReferrerWhenDowngrade, blink::kWebReferrerPolicyNoReferrerWhenDowngrade)
+ASSERT_ENUMS_MATCH(ReferrerPolicy::Never, blink::kWebReferrerPolicyNever)
+ASSERT_ENUMS_MATCH(ReferrerPolicy::Origin, blink::kWebReferrerPolicyOrigin)
+ASSERT_ENUMS_MATCH(ReferrerPolicy::OriginWhenCrossOrigin, blink::kWebReferrerPolicyOriginWhenCrossOrigin)
+ASSERT_ENUMS_MATCH(ReferrerPolicy::NoReferrerWhenDowngradeOriginWhenCrossOrigin, blink::kWebReferrerPolicyNoReferrerWhenDowngradeOriginWhenCrossOrigin)
+ASSERT_ENUMS_MATCH(ReferrerPolicy::Last, blink::kWebReferrerPolicyLast)
+
} // namespace QtWebEngineCore
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index 46c8d2604..67fcbe7af 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -124,7 +124,9 @@ public:
quint64 findText(const QString &subString, bool caseSensitively, bool findBackward);
void stopFinding();
void updateWebPreferences(const content::WebPreferences &webPreferences);
- void download(const QUrl &url, const QString &suggestedFileName);
+ void download(const QUrl &url, const QString &suggestedFileName,
+ const QUrl &referrerUrl = QUrl(),
+ ReferrerPolicy referrerPolicy = ReferrerPolicy::Default);
bool isAudioMuted() const;
void setAudioMuted(bool mute);
bool recentlyAudible();
diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h
index 8b7365342..4280dbfe1 100644
--- a/src/core/web_contents_adapter_client.h
+++ b/src/core/web_contents_adapter_client.h
@@ -71,6 +71,17 @@ class WebContentsAdapter;
class WebContentsDelegateQt;
class WebEngineSettings;
+// Must match blink::WebReferrerPolicy
+enum class ReferrerPolicy {
+ Always,
+ Default,
+ NoReferrerWhenDowngrade,
+ Never,
+ Origin,
+ OriginWhenCrossOrigin,
+ NoReferrerWhenDowngradeOriginWhenCrossOrigin,
+ Last = NoReferrerWhenDowngradeOriginWhenCrossOrigin,
+};
class WebEngineContextMenuSharedData : public QSharedData {
@@ -97,6 +108,9 @@ public:
QString suggestedFileName;
QString misspelledWord;
QStringList spellCheckerSuggestions;
+ QUrl pageUrl;
+ QUrl frameUrl;
+ ReferrerPolicy referrerPolicy = ReferrerPolicy::Default;
// Some likely candidates for future additions as we add support for the related actions:
// bool isImageBlocked;
// <enum tbd> mediaType;
@@ -253,6 +267,34 @@ public:
return d->spellCheckerSuggestions;
}
+ void setFrameUrl(const QUrl &url) {
+ d->frameUrl = url;
+ }
+
+ QUrl frameUrl() const {
+ return d->frameUrl;
+ }
+
+ void setPageUrl(const QUrl &url) {
+ d->pageUrl = url;
+ }
+
+ QUrl pageUrl() const {
+ return d->pageUrl;
+ }
+
+ QUrl referrerUrl() const {
+ return !d->frameUrl.isEmpty() ? d->frameUrl : d->pageUrl;
+ }
+
+ void setReferrerPolicy(ReferrerPolicy referrerPolicy) {
+ d->referrerPolicy = referrerPolicy;
+ }
+
+ ReferrerPolicy referrerPolicy() const {
+ return d->referrerPolicy;
+ }
+
private:
QSharedDataPointer<WebEngineContextMenuSharedData> d;
};
diff --git a/src/core/web_contents_view_qt.cpp b/src/core/web_contents_view_qt.cpp
index f9435ac34..28f202e24 100644
--- a/src/core/web_contents_view_qt.cpp
+++ b/src/core/web_contents_view_qt.cpp
@@ -176,6 +176,9 @@ static inline WebEngineContextMenuData fromParams(const content::ContextMenuPara
ret.setMisspelledWord(toQt(params.misspelled_word));
ret.setSpellCheckerSuggestions(fromVector(params.dictionary_suggestions));
#endif
+ ret.setFrameUrl(toQt(params.frame_url));
+ ret.setPageUrl(toQt(params.page_url));
+ ret.setReferrerPolicy((ReferrerPolicy)params.referrer_policy);
return ret;
}
diff --git a/src/core/web_engine_context.cpp b/src/core/web_engine_context.cpp
index 4df4dbfae..83b7c2d36 100644
--- a/src/core/web_engine_context.cpp
+++ b/src/core/web_engine_context.cpp
@@ -135,21 +135,6 @@ bool usingANGLE()
#endif
}
-bool usingSoftwareDynamicGL()
-{
- if (QCoreApplication::testAttribute(Qt::AA_UseSoftwareOpenGL))
- return true;
-#if defined(Q_OS_WIN)
- HMODULE handle = static_cast<HMODULE>(QOpenGLContext::openGLModuleHandle());
- wchar_t path[MAX_PATH];
- DWORD size = GetModuleFileName(handle, path, MAX_PATH);
- QFileInfo openGLModule(QString::fromWCharArray(path, size));
- return openGLModule.fileName() == QLatin1String("opengl32sw.dll");
-#else
- return false;
-#endif
-}
-
bool usingQtQuick2DRenderer()
{
const QStringList args = QGuiApplication::arguments();
@@ -186,6 +171,21 @@ void dummyGetPluginCallback(const std::vector<content::WebPluginInfo>&)
namespace QtWebEngineCore {
+bool usingSoftwareDynamicGL()
+{
+ if (QCoreApplication::testAttribute(Qt::AA_UseSoftwareOpenGL))
+ return true;
+#if defined(Q_OS_WIN)
+ HMODULE handle = static_cast<HMODULE>(QOpenGLContext::openGLModuleHandle());
+ wchar_t path[MAX_PATH];
+ DWORD size = GetModuleFileName(handle, path, MAX_PATH);
+ QFileInfo openGLModule(QString::fromWCharArray(path, size));
+ return openGLModule.fileName() == QLatin1String("opengl32sw.dll");
+#else
+ return false;
+#endif
+}
+
void WebEngineContext::destroyBrowserContext()
{
m_defaultBrowserContext.reset();
@@ -287,6 +287,9 @@ WebEngineContext::WebEngineContext()
appArgs.append(QString::fromLocal8Bit(qgetenv(kChromiumFlagsEnv)).split(' '));
}
+ bool enableWebGLSoftwareRendering =
+ appArgs.removeAll(QStringLiteral("--enable-webgl-software-rendering"));
+
bool useEmbeddedSwitches = false;
#if defined(QTWEBENGINE_EMBEDDED_SWITCHES)
useEmbeddedSwitches = !appArgs.removeAll(QStringLiteral("--disable-embedded-switches"));
@@ -371,7 +374,20 @@ WebEngineContext::WebEngineContext()
const char *glType = 0;
#ifndef QT_NO_OPENGL
- if (!usingANGLE() && !usingSoftwareDynamicGL() && !usingQtQuick2DRenderer()) {
+
+ bool tryGL =
+ !usingANGLE()
+ && (!usingSoftwareDynamicGL()
+#ifdef Q_OS_WIN
+ // If user requested WebGL support on Windows, instead of using Skia rendering to
+ // bitmaps, use software rendering via opengl32sw.dll. This might be less
+ // performant, but at least provides WebGL support.
+ || enableWebGLSoftwareRendering
+#endif
+ )
+ && !usingQtQuick2DRenderer();
+
+ if (tryGL) {
if (qt_gl_global_share_context() && qt_gl_global_share_context()->isValid()) {
// If the native handle is QEGLNativeContext try to use GL ES/2, if there is no native handle
// assume we are using wayland and try GL ES/2, and finally Ozone demands GL ES/2 too.
diff --git a/src/core/web_engine_context.h b/src/core/web_engine_context.h
index 386f43035..92c45e260 100644
--- a/src/core/web_engine_context.h
+++ b/src/core/web_engine_context.h
@@ -74,6 +74,8 @@ class ContentMainDelegateQt;
class DevToolsServerQt;
class SurfaceFactoryQt;
+bool usingSoftwareDynamicGL();
+
class WebEngineContext : public base::RefCounted<WebEngineContext> {
public:
static scoped_refptr<WebEngineContext> current();
diff --git a/src/core/web_event_factory.cpp b/src/core/web_event_factory.cpp
index e6ac63c0d..18f8d393f 100644
--- a/src/core/web_event_factory.cpp
+++ b/src/core/web_event_factory.cpp
@@ -144,7 +144,6 @@ static int windowsKeyCodeForKeyEvent(unsigned int keycode, bool isKeypad)
return VK_SHIFT; // (10) SHIFT key
case Qt::Key_Control:
return VK_CONTROL; // (11) CTRL key
- case Qt::Key_Menu:
case Qt::Key_Alt:
return VK_MENU; // (12) ALT key
default:
@@ -167,7 +166,6 @@ static int windowsKeyCodeForKeyEvent(unsigned int keycode, bool isKeypad)
return VK_SHIFT; // (10) SHIFT key
case Qt::Key_Control:
return VK_CONTROL; // (11) CTRL key
- case Qt::Key_Menu:
case Qt::Key_Alt:
return VK_MENU; // (12) ALT key
@@ -357,7 +355,8 @@ static int windowsKeyCodeForKeyEvent(unsigned int keycode, bool isKeypad)
return VK_LWIN; // (5B) Left Windows key (Microsoft Natural keyboard)
// case Qt::Key_Meta_R: FIXME: What to do here?
// return VK_RWIN; // (5C) Right Windows key (Natural keyboard)
- // VK_APPS (5D) Applications key (Natural keyboard)
+ case Qt::Key_Menu: // (5D) Applications key (Natural keyboard)
+ return VK_APPS;
// VK_SLEEP (5F) Computer Sleep key
// VK_SEPARATOR (6C) Separator key
// VK_SUBTRACT (6D) Subtract key
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index 123acb20c..78fdd8fa8 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -1639,7 +1639,8 @@ void QQuickWebEngineView::triggerWebAction(WebAction action)
break;
case DownloadLinkToDisk:
if (d->m_contextMenuData.linkUrl().isValid())
- d->adapter->download(d->m_contextMenuData.linkUrl(), d->m_contextMenuData.suggestedFileName());
+ d->adapter->download(d->m_contextMenuData.linkUrl(), d->m_contextMenuData.suggestedFileName(),
+ d->m_contextMenuData.referrerUrl(), d->m_contextMenuData.referrerPolicy());
break;
case CopyImageToClipboard:
if (d->m_contextMenuData.hasImageContent() &&
@@ -1666,7 +1667,8 @@ void QQuickWebEngineView::triggerWebAction(WebAction action)
case DownloadImageToDisk:
case DownloadMediaToDisk:
if (d->m_contextMenuData.mediaUrl().isValid())
- d->adapter->download(d->m_contextMenuData.mediaUrl(), d->m_contextMenuData.suggestedFileName());
+ d->adapter->download(d->m_contextMenuData.mediaUrl(), d->m_contextMenuData.suggestedFileName(),
+ d->m_contextMenuData.referrerUrl(), d->m_contextMenuData.referrerPolicy());
break;
case CopyMediaUrlToClipboard:
if (d->m_contextMenuData.mediaUrl().isValid() &&
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index 178f6ec98..76c705c6e 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -1275,7 +1275,9 @@ void QWebEnginePage::triggerAction(WebAction action, bool)
break;
case DownloadLinkToDisk:
if (menuData.linkUrl().isValid())
- d->adapter->download(menuData.linkUrl(), menuData.suggestedFileName());
+ d->adapter->download(menuData.linkUrl(), menuData.suggestedFileName(),
+ menuData.referrerUrl(), menuData.referrerPolicy());
+
break;
case CopyImageToClipboard:
if (menuData.hasImageContent() &&
@@ -1302,7 +1304,8 @@ void QWebEnginePage::triggerAction(WebAction action, bool)
case DownloadImageToDisk:
case DownloadMediaToDisk:
if (menuData.mediaUrl().isValid())
- d->adapter->download(menuData.mediaUrl(), menuData.suggestedFileName());
+ d->adapter->download(menuData.mediaUrl(), menuData.suggestedFileName(),
+ menuData.referrerUrl(), menuData.referrerPolicy());
break;
case CopyMediaUrlToClipboard:
if (menuData.mediaUrl().isValid() &&
@@ -1466,7 +1469,7 @@ void QWebEnginePagePrivate::wasHidden()
bool QWebEnginePagePrivate::contextMenuRequested(const WebEngineContextMenuData &data)
{
- if (!view || !view->d_func()->m_pendingContextMenuEvent)
+ if (!view)
return false;
contextData.reset();
@@ -1492,7 +1495,6 @@ bool QWebEnginePagePrivate::contextMenuRequested(const WebEngineContextMenuData
event.ignore();
return false;
}
- view->d_func()->m_pendingContextMenuEvent = false;
return true;
}
diff --git a/src/webenginewidgets/api/qwebengineview.cpp b/src/webenginewidgets/api/qwebengineview.cpp
index 56948bb18..0037f7e50 100644
--- a/src/webenginewidgets/api/qwebengineview.cpp
+++ b/src/webenginewidgets/api/qwebengineview.cpp
@@ -106,7 +106,6 @@ static QAccessibleInterface *webAccessibleFactory(const QString &, QObject *obje
QWebEngineViewPrivate::QWebEngineViewPrivate()
: page(0)
- , m_pendingContextMenuEvent(false)
, m_dragEntered(false)
{
#ifndef QT_NO_ACCESSIBILITY
@@ -316,12 +315,10 @@ void QWebEngineView::setZoomFactor(qreal factor)
*/
bool QWebEngineView::event(QEvent *ev)
{
- Q_D(QWebEngineView);
// We swallow spontaneous contextMenu events and synthethize those back later on when we get the
// HandleContextMenu callback from chromium
if (ev->type() == QEvent::ContextMenu) {
ev->accept();
- d->m_pendingContextMenuEvent = true;
return true;
}
return QWidget::event(ev);
diff --git a/src/webenginewidgets/api/qwebengineview_p.h b/src/webenginewidgets/api/qwebengineview_p.h
index 45b3e266e..f3a37225b 100644
--- a/src/webenginewidgets/api/qwebengineview_p.h
+++ b/src/webenginewidgets/api/qwebengineview_p.h
@@ -70,7 +70,6 @@ public:
QWebEngineViewPrivate();
QWebEnginePage *page;
- bool m_pendingContextMenuEvent;
bool m_dragEntered;
};
diff --git a/tests/auto/shared/http.pri b/tests/auto/shared/http.pri
new file mode 100644
index 000000000..5236e9d26
--- /dev/null
+++ b/tests/auto/shared/http.pri
@@ -0,0 +1,3 @@
+HEADERS += $$PWD/httpserver.h $$PWD/httpreqrep.h
+SOURCES += $$PWD/httpserver.cpp $$PWD/httpreqrep.cpp
+INCLUDEPATH += $$PWD
diff --git a/tests/auto/shared/httpreqrep.cpp b/tests/auto/shared/httpreqrep.cpp
new file mode 100644
index 000000000..eb2db6890
--- /dev/null
+++ b/tests/auto/shared/httpreqrep.cpp
@@ -0,0 +1,91 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "httpreqrep.h"
+
+HttpReqRep::HttpReqRep(QTcpSocket *socket, QObject *parent)
+ : QObject(parent), m_socket(socket)
+{
+ m_socket->setParent(this);
+ connect(m_socket, &QIODevice::readyRead, this, &HttpReqRep::handleReadyRead);
+}
+
+void HttpReqRep::sendResponse()
+{
+ m_socket->write("HTTP/1.1 ");
+ m_socket->write(QByteArray::number(m_responseStatusCode));
+ m_socket->write(" OK?\r\n");
+ for (const auto & kv : m_responseHeaders) {
+ m_socket->write(kv.first);
+ m_socket->write(": ");
+ m_socket->write(kv.second);
+ m_socket->write("\r\n");
+ }
+ m_socket->write("\r\n");
+ m_socket->write(m_responseBody);
+ m_socket->close();
+}
+
+QByteArray HttpReqRep::requestHeader(const QByteArray &key) const
+{
+ auto it = m_requestHeaders.find(key);
+ if (it != m_requestHeaders.end())
+ return it->second;
+ return {};
+}
+
+void HttpReqRep::handleReadyRead()
+{
+ const auto requestLine = m_socket->readLine();
+ const auto requestLineParts = requestLine.split(' ');
+ if (requestLineParts.size() != 3 || !requestLineParts[2].toUpper().startsWith("HTTP/")) {
+ qWarning("HttpReqRep: invalid request line");
+ Q_EMIT readFinished(false);
+ return;
+ }
+
+ decltype(m_requestHeaders) headers;
+ for (;;) {
+ const auto headerLine = m_socket->readLine();
+ if (headerLine == QByteArrayLiteral("\r\n"))
+ break;
+ int colonIndex = headerLine.indexOf(':');
+ if (colonIndex < 0) {
+ qWarning("HttpReqRep: invalid header line");
+ Q_EMIT readFinished(false);
+ return;
+ }
+ auto headerKey = headerLine.left(colonIndex).trimmed().toLower();
+ auto headerValue = headerLine.mid(colonIndex + 1).trimmed().toLower();
+ headers.emplace(headerKey, headerValue);
+ }
+
+ m_requestMethod = requestLineParts[0];
+ m_requestPath = requestLineParts[1];
+ m_requestHeaders = headers;
+ Q_EMIT readFinished(true);
+}
diff --git a/tests/auto/shared/httpreqrep.h b/tests/auto/shared/httpreqrep.h
new file mode 100644
index 000000000..4e9f10dff
--- /dev/null
+++ b/tests/auto/shared/httpreqrep.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef HTTPREQREP_H
+#define HTTPREQREP_H
+
+#include <QTcpSocket>
+
+#include <utility>
+
+// Represents an HTTP request-response exchange.
+class HttpReqRep : public QObject
+{
+ Q_OBJECT
+public:
+ HttpReqRep(QTcpSocket *socket, QObject *parent = nullptr);
+ void sendResponse();
+ QByteArray requestMethod() const { return m_requestMethod; }
+ QByteArray requestPath() const { return m_requestPath; }
+ QByteArray requestHeader(const QByteArray &key) const;
+ void setResponseStatus(int statusCode)
+ {
+ m_responseStatusCode = statusCode;
+ }
+ void setResponseHeader(const QByteArray &key, QByteArray value)
+ {
+ m_responseHeaders[key.toLower()] = std::move(value);
+ }
+ void setResponseBody(QByteArray content)
+ {
+ m_responseHeaders["content-length"] = QByteArray::number(content.size());
+ m_responseBody = std::move(content);
+ }
+
+Q_SIGNALS:
+ void readFinished(bool ok);
+
+private Q_SLOTS:
+ void handleReadyRead();
+
+private:
+ QTcpSocket *m_socket = nullptr;
+ QByteArray m_requestMethod;
+ QByteArray m_requestPath;
+ std::map<QByteArray, QByteArray> m_requestHeaders;
+ int m_responseStatusCode = 200;
+ std::map<QByteArray, QByteArray> m_responseHeaders;
+ QByteArray m_responseBody;
+};
+
+#endif // !HTTPREQREP_H
diff --git a/tests/auto/shared/httpserver.cpp b/tests/auto/shared/httpserver.cpp
new file mode 100644
index 000000000..6012379f2
--- /dev/null
+++ b/tests/auto/shared/httpserver.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "httpserver.h"
+
+#include "waitforsignal.h"
+
+HttpServer::HttpServer(QObject *parent) : QObject(parent)
+{
+ connect(&m_tcpServer, &QTcpServer::newConnection, this, &HttpServer::handleNewConnection);
+ if (!m_tcpServer.listen())
+ qWarning("HttpServer: listen() failed");
+ m_url = QStringLiteral("http://127.0.0.1:") + QString::number(m_tcpServer.serverPort());
+}
+
+QUrl HttpServer::url(const QString &path) const
+{
+ auto copy = m_url;
+ copy.setPath(path);
+ return copy;
+}
+
+void HttpServer::handleNewConnection()
+{
+ auto reqRep = new HttpReqRep(m_tcpServer.nextPendingConnection(), this);
+ connect(reqRep, &HttpReqRep::readFinished, this, &HttpServer::handleReadFinished);
+}
+
+void HttpServer::handleReadFinished(bool ok)
+{
+ auto reqRep = qobject_cast<HttpReqRep *>(sender());
+ if (ok)
+ Q_EMIT newRequest(reqRep);
+ else
+ reqRep->deleteLater();
+}
+
+std::unique_ptr<HttpReqRep> waitForRequest(HttpServer *server)
+{
+ std::unique_ptr<HttpReqRep> result;
+ waitForSignal(server, &HttpServer::newRequest, [&](HttpReqRep *rr) {
+ rr->setParent(nullptr);
+ result.reset(rr);
+ });
+ return result;
+}
diff --git a/tests/auto/shared/httpserver.h b/tests/auto/shared/httpserver.h
new file mode 100644
index 000000000..e45743b7b
--- /dev/null
+++ b/tests/auto/shared/httpserver.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef HTTPSERVER_H
+#define HTTPSERVER_H
+
+#include "httpreqrep.h"
+
+#include <QTcpServer>
+#include <QUrl>
+
+#include <memory>
+
+// Listens on a TCP socket and creates HttpReqReps for each connection.
+class HttpServer : public QObject
+{
+ Q_OBJECT
+
+ QTcpServer m_tcpServer;
+ QUrl m_url;
+
+public:
+ HttpServer(QObject *parent = nullptr);
+ QUrl url(const QString &path = QStringLiteral("/")) const;
+
+Q_SIGNALS:
+ void newRequest(HttpReqRep *reqRep);
+
+private Q_SLOTS:
+ void handleNewConnection();
+ void handleReadFinished(bool ok);
+};
+
+// Wait for an HTTP request to be received.
+std::unique_ptr<HttpReqRep> waitForRequest(HttpServer *server);
+
+#endif // !HTTPSERVER_H
diff --git a/tests/auto/shared/waitforsignal.h b/tests/auto/shared/waitforsignal.h
new file mode 100644
index 000000000..9b2daf7af
--- /dev/null
+++ b/tests/auto/shared/waitforsignal.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef WAITFORSIGNAL_H
+#define WAITFORSIGNAL_H
+
+#include <QObject>
+#include <QTestEventLoop>
+
+#include <utility>
+
+// Implementation details of waitForSignal.
+namespace {
+ // Wraps a functor to set a flag and exit from event loop if called.
+ template <class SignalHandler>
+ struct waitForSignal_SignalHandlerWrapper {
+ waitForSignal_SignalHandlerWrapper(SignalHandler &&sh)
+ : signalHandler(std::forward<SignalHandler>(sh)) {}
+
+ template <class... Args>
+ void operator()(Args && ... args) {
+ signalHandler(std::forward<Args>(args)...);
+ hasBeenCalled = true;
+ loop.exitLoop();
+ }
+
+ SignalHandler &&signalHandler;
+ QTestEventLoop loop;
+ bool hasBeenCalled = false;
+ };
+
+ // No-op functor.
+ struct waitForSignal_DefaultSignalHandler {
+ template <class... Args>
+ void operator()(Args && ...) {}
+ };
+} // namespace
+
+// Wait for a signal to be emitted.
+//
+// The given functor is called with the signal arguments allowing the arguments
+// to be modified before returning from the signal handler (unlike QSignalSpy).
+template <class Sender, class Signal, class SignalHandler>
+bool waitForSignal(Sender &&sender, Signal &&signal, SignalHandler &&signalHandler, int timeout = 15000)
+{
+ waitForSignal_SignalHandlerWrapper<SignalHandler> signalHandlerWrapper(
+ std::forward<SignalHandler>(signalHandler));
+ auto connection = QObject::connect(
+ std::forward<Sender>(sender),
+ std::forward<Signal>(signal),
+ std::ref(signalHandlerWrapper));
+ signalHandlerWrapper.loop.enterLoopMSecs(timeout);
+ QObject::disconnect(connection);
+ return signalHandlerWrapper.hasBeenCalled;
+}
+
+template <class Sender, class Signal>
+bool waitForSignal(Sender &&sender, Signal &&signal, int timeout = 15000)
+{
+ return waitForSignal(std::forward<Sender>(sender),
+ std::forward<Signal>(signal),
+ waitForSignal_DefaultSignalHandler(),
+ timeout);
+}
+
+#endif // !WAITFORSIGNAL_H
diff --git a/tests/auto/widgets/qwebenginedownloads/qwebenginedownloads.pro b/tests/auto/widgets/qwebenginedownloads/qwebenginedownloads.pro
new file mode 100644
index 000000000..18a66c466
--- /dev/null
+++ b/tests/auto/widgets/qwebenginedownloads/qwebenginedownloads.pro
@@ -0,0 +1,3 @@
+include(../tests.pri)
+include(../../shared/http.pri)
+QT *= core-private
diff --git a/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp b/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp
new file mode 100644
index 000000000..6ba6d78a4
--- /dev/null
+++ b/tests/auto/widgets/qwebenginedownloads/tst_qwebenginedownloads.cpp
@@ -0,0 +1,595 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 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:GPL-EXCEPT$
+** 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 General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** 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-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QCoreApplication>
+#include <QStandardPaths>
+#include <QTemporaryDir>
+#include <QTest>
+#include <QWebEngineDownloadItem>
+#include <QWebEnginePage>
+#include <QWebEngineProfile>
+#include <QWebEngineView>
+#include <httpserver.h>
+#include <waitforsignal.h>
+
+class tst_QWebEngineDownloads : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void downloadLink_data();
+ void downloadLink();
+ void downloadTwoLinks();
+ void downloadPage_data();
+ void downloadPage();
+};
+
+enum DownloadTestUserAction {
+ SaveLink,
+ Navigate,
+};
+
+enum DownloadTestFileAction {
+ FileIsDownloaded,
+ FileIsDisplayed,
+};
+
+Q_DECLARE_METATYPE(DownloadTestUserAction);
+Q_DECLARE_METATYPE(DownloadTestFileAction);
+
+void tst_QWebEngineDownloads::downloadLink_data()
+{
+ QTest::addColumn<DownloadTestUserAction>("userAction");
+ QTest::addColumn<bool>("anchorHasDownloadAttribute");
+ QTest::addColumn<QByteArray>("fileName");
+ QTest::addColumn<QByteArray>("fileContents");
+ QTest::addColumn<QByteArray>("fileMimeTypeDeclared");
+ QTest::addColumn<QByteArray>("fileMimeTypeDetected");
+ QTest::addColumn<QByteArray>("fileDisposition");
+ QTest::addColumn<bool>("fileHasReferer");
+ QTest::addColumn<DownloadTestFileAction>("fileAction");
+ QTest::addColumn<QWebEngineDownloadItem::DownloadType>("downloadType");
+
+ // SaveLink should always trigger a download, even for empty files.
+ QTest::newRow("save link to empty file")
+ /* userAction */ << SaveLink
+ /* anchorHasDownloadAttribute */ << false
+ /* fileName */ << QByteArrayLiteral("foo.txt")
+ /* fileContents */ << QByteArrayLiteral("")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("")
+ /* fileDisposition */ << QByteArrayLiteral("")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDownloaded
+ /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+
+ // SaveLink should always trigger a download, also for text files.
+ QTest::newRow("save link to text file")
+ /* userAction */ << SaveLink
+ /* anchorHasDownloadAttribute */ << false
+ /* fileName */ << QByteArrayLiteral("foo.txt")
+ /* fileContents */ << QByteArrayLiteral("bar")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
+ /* fileDisposition */ << QByteArrayLiteral("")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDownloaded
+ /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+
+ // ... adding the "download" attribute should have no effect.
+ QTest::newRow("save link to text file (attribute)")
+ /* userAction */ << SaveLink
+ /* anchorHasDownloadAttribute */ << true
+ /* fileName */ << QByteArrayLiteral("foo.txt")
+ /* fileContents */ << QByteArrayLiteral("bar")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
+ /* fileDisposition */ << QByteArrayLiteral("")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDownloaded
+ /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+
+ // ... adding the "attachment" content disposition should also have no effect.
+ QTest::newRow("save link to text file (attachment)")
+ /* userAction */ << SaveLink
+ /* anchorHasDownloadAttribute */ << false
+ /* fileName */ << QByteArrayLiteral("foo.txt")
+ /* fileContents */ << QByteArrayLiteral("bar")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
+ /* fileDisposition */ << QByteArrayLiteral("attachment")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDownloaded
+ /* downloadType */ << QWebEngineDownloadItem::UserRequested;
+
+ // ... even adding both should have no effect.
+ QTest::newRow("save link to text file (attribute+attachment)")
+ /* userAction */ << SaveLink
+ /* anchorHasDownloadAttribute */ << true
+ /* fileName */ << QByteArrayLiteral("foo.txt")
+ /* fileContents */ << QByteArrayLiteral("bar")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
+ /* fileDisposition */ << QByteArrayLiteral("attachment")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDownloaded
+ /* downloadType */ << QWebEngineDownloadItem::UserRequested;
+
+ // Navigating to an empty file should show an empty page.
+ QTest::newRow("navigate to empty file")
+ /* userAction */ << Navigate
+ /* anchorHasDownloadAttribute */ << false
+ /* fileName */ << QByteArrayLiteral("foo.txt")
+ /* fileContents */ << QByteArrayLiteral("")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("")
+ /* fileDisposition */ << QByteArrayLiteral("")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDisplayed
+ /* downloadType */ << /* unused */ QWebEngineDownloadItem::DownloadAttribute;
+
+ // Navigating to a text file should show the text file.
+ QTest::newRow("navigate to text file")
+ /* userAction */ << Navigate
+ /* anchorHasDownloadAttribute */ << false
+ /* fileName */ << QByteArrayLiteral("foo.txt")
+ /* fileContents */ << QByteArrayLiteral("bar")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
+ /* fileDisposition */ << QByteArrayLiteral("")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDisplayed
+ /* downloadType */ << /* unused */ QWebEngineDownloadItem::DownloadAttribute;
+
+ // ... unless the link has the "download" attribute: then the file should be downloaded.
+ QTest::newRow("navigate to text file (attribute)")
+ /* userAction */ << Navigate
+ /* anchorHasDownloadAttribute */ << true
+ /* fileName */ << QByteArrayLiteral("foo.txt")
+ /* fileContents */ << QByteArrayLiteral("bar")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
+ /* fileDisposition */ << QByteArrayLiteral("")
+ /* fileHasReferer */ << false // crbug.com/455987
+ /* fileAction */ << FileIsDownloaded
+ /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+
+ // ... same with the content disposition header save for the download type.
+ QTest::newRow("navigate to text file (attachment)")
+ /* userAction */ << Navigate
+ /* anchorHasDownloadAttribute */ << false
+ /* fileName */ << QByteArrayLiteral("foo.txt")
+ /* fileContents */ << QByteArrayLiteral("bar")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
+ /* fileDisposition */ << QByteArrayLiteral("attachment")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDownloaded
+ /* downloadType */ << QWebEngineDownloadItem::Attachment;
+
+ // ... and both.
+ QTest::newRow("navigate to text file (attribute+attachment)")
+ /* userAction */ << Navigate
+ /* anchorHasDownloadAttribute */ << true
+ /* fileName */ << QByteArrayLiteral("foo.txt")
+ /* fileContents */ << QByteArrayLiteral("bar")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("text/plain")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("text/plain")
+ /* fileDisposition */ << QByteArrayLiteral("attachment")
+ /* fileHasReferer */ << false // crbug.com/455987
+ /* fileAction */ << FileIsDownloaded
+ /* downloadType */ << QWebEngineDownloadItem::Attachment;
+
+ // The file's extension has no effect.
+ QTest::newRow("navigate to supposed zip file")
+ /* userAction */ << Navigate
+ /* anchorHasDownloadAttribute */ << false
+ /* fileName */ << QByteArrayLiteral("foo.zip")
+ /* fileContents */ << QByteArrayLiteral("bar")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("")
+ /* fileDisposition */ << QByteArrayLiteral("")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDisplayed
+ /* downloadType */ << /* unused */ QWebEngineDownloadItem::DownloadAttribute;
+
+ // ... the file's mime type however does.
+ QTest::newRow("navigate to supposed zip file (application/zip)")
+ /* userAction */ << Navigate
+ /* anchorHasDownloadAttribute */ << false
+ /* fileName */ << QByteArrayLiteral("foo.zip")
+ /* fileContents */ << QByteArrayLiteral("bar")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("application/zip")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("application/zip")
+ /* fileDisposition */ << QByteArrayLiteral("")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDownloaded
+ /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+
+ // ... but we're not very picky about the exact type.
+ QTest::newRow("navigate to supposed zip file (application/octet-stream)")
+ /* userAction */ << Navigate
+ /* anchorHasDownloadAttribute */ << false
+ /* fileName */ << QByteArrayLiteral("foo.zip")
+ /* fileContents */ << QByteArrayLiteral("bar")
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("application/octet-stream")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("application/octet-stream")
+ /* fileDisposition */ << QByteArrayLiteral("")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDownloaded
+ /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+
+ // empty zip file (consisting only of "end of central directory record")
+ QByteArray zipFile = QByteArrayLiteral("PK\x05\x06") + QByteArray(18, 0);
+
+ // The mime type is guessed automatically if not provided by the server.
+ QTest::newRow("navigate to actual zip file")
+ /* userAction */ << Navigate
+ /* anchorHasDownloadAttribute */ << false
+ /* fileName */ << QByteArrayLiteral("foo.zip")
+ /* fileContents */ << zipFile
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("application/octet-stream")
+ /* fileDisposition */ << QByteArrayLiteral("")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDownloaded
+ /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+
+ // The mime type is not guessed automatically if provided by the server.
+ QTest::newRow("navigate to actual zip file (application/zip)")
+ /* userAction */ << Navigate
+ /* anchorHasDownloadAttribute */ << false
+ /* fileName */ << QByteArrayLiteral("foo.zip")
+ /* fileContents */ << zipFile
+ /* fileMimeTypeDeclared */ << QByteArrayLiteral("application/zip")
+ /* fileMimeTypeDetected */ << QByteArrayLiteral("application/zip")
+ /* fileDisposition */ << QByteArrayLiteral("")
+ /* fileHasReferer */ << true
+ /* fileAction */ << FileIsDownloaded
+ /* downloadType */ << QWebEngineDownloadItem::DownloadAttribute;
+}
+
+void tst_QWebEngineDownloads::downloadLink()
+{
+ QFETCH(DownloadTestUserAction, userAction);
+ QFETCH(bool, anchorHasDownloadAttribute);
+ QFETCH(QByteArray, fileName);
+ QFETCH(QByteArray, fileContents);
+ QFETCH(QByteArray, fileMimeTypeDeclared);
+ QFETCH(QByteArray, fileMimeTypeDetected);
+ QFETCH(QByteArray, fileDisposition);
+ QFETCH(bool, fileHasReferer);
+ QFETCH(DownloadTestFileAction, fileAction);
+ QFETCH(QWebEngineDownloadItem::DownloadType, downloadType);
+
+ HttpServer server;
+ QWebEngineProfile profile;
+ QWebEnginePage page(&profile);
+ QWebEngineView view;
+ view.setPage(&page);
+
+ // 1. Load an HTML page with a link
+ //
+ // The only variation being whether the <a> element has a "download"
+ // attribute or not.
+ view.load(server.url());
+ view.show();
+ auto indexRR = waitForRequest(&server);
+ QVERIFY(indexRR);
+ QCOMPARE(indexRR->requestMethod(), QByteArrayLiteral("GET"));
+ QCOMPARE(indexRR->requestPath(), QByteArrayLiteral("/"));
+ indexRR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html"));
+ QByteArray html;
+ html += QByteArrayLiteral("<html><body><a href=\"");
+ html += fileName;
+ html += QByteArrayLiteral("\" ");
+ if (anchorHasDownloadAttribute)
+ html += QByteArrayLiteral("download");
+ html += QByteArrayLiteral(">Link</a></body></html>");
+ indexRR->setResponseBody(html);
+ indexRR->sendResponse();
+ bool loadOk = false;
+ QVERIFY(waitForSignal(&page, &QWebEnginePage::loadFinished, [&](bool ok) { loadOk = ok; }));
+ QVERIFY(loadOk);
+
+ // 1.1. Ignore favicon request
+ auto favIconRR = waitForRequest(&server);
+ QVERIFY(favIconRR);
+ QCOMPARE(favIconRR->requestMethod(), QByteArrayLiteral("GET"));
+ QCOMPARE(favIconRR->requestPath(), QByteArrayLiteral("/favicon.ico"));
+ favIconRR->setResponseStatus(404);
+ favIconRR->sendResponse();
+
+ // 2. Simulate user action
+ //
+ // - Navigate: user left-clicks on link
+ // - SaveLink: user right-clicks on link and chooses "save link as" from menu
+ QWidget *renderWidget = view.focusWidget();
+ QPoint linkPos(10, 10);
+ if (userAction == SaveLink) {
+ view.setContextMenuPolicy(Qt::CustomContextMenu);
+ auto event1 = new QContextMenuEvent(QContextMenuEvent::Mouse, linkPos);
+ auto event2 = new QMouseEvent(QEvent::MouseButtonPress, linkPos, Qt::RightButton, {}, {});
+ auto event3 = new QMouseEvent(QEvent::MouseButtonRelease, linkPos, Qt::RightButton, {}, {});
+ QCoreApplication::postEvent(renderWidget, event1);
+ QCoreApplication::postEvent(renderWidget, event2);
+ QCoreApplication::postEvent(renderWidget, event3);
+ QVERIFY(waitForSignal(&view, &QWidget::customContextMenuRequested));
+ page.triggerAction(QWebEnginePage::DownloadLinkToDisk);
+ } else
+ QTest::mouseClick(renderWidget, Qt::LeftButton, {}, linkPos);
+
+ // 3. Deliver requested file
+ //
+ // Request/response headers vary.
+ auto fileRR = waitForRequest(&server);
+ QVERIFY(fileRR);
+ QCOMPARE(fileRR->requestMethod(), QByteArrayLiteral("GET"));
+ QCOMPARE(fileRR->requestPath(), QByteArrayLiteral("/") + fileName);
+ if (fileHasReferer)
+ QCOMPARE(fileRR->requestHeader(QByteArrayLiteral("referer")), server.url().toEncoded());
+ else
+ QCOMPARE(fileRR->requestHeader(QByteArrayLiteral("referer")), QByteArray());
+ if (!fileDisposition.isEmpty())
+ fileRR->setResponseHeader(QByteArrayLiteral("content-disposition"), fileDisposition);
+ if (!fileMimeTypeDeclared.isEmpty())
+ fileRR->setResponseHeader(QByteArrayLiteral("content-type"), fileMimeTypeDeclared);
+ fileRR->setResponseBody(fileContents);
+ fileRR->sendResponse();
+
+ // 4a. File is displayed and not downloaded - end test
+ if (fileAction == FileIsDisplayed) {
+ QVERIFY(waitForSignal(&page, &QWebEnginePage::loadFinished, [&](bool ok) { loadOk = ok; }));
+ QVERIFY(loadOk);
+ return;
+ }
+
+ // 4b. File is downloaded - check QWebEngineDownloadItem attributes
+ QTemporaryDir tmpDir;
+ QVERIFY(tmpDir.isValid());
+ QByteArray slashFileName = QByteArrayLiteral("/") + fileName;
+ QString suggestedPath =
+ QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + slashFileName;
+ QString downloadPath = tmpDir.path() + slashFileName;
+ QUrl downloadUrl = server.url(slashFileName);
+ QWebEngineDownloadItem *downloadItem = nullptr;
+ QVERIFY(waitForSignal(&profile, &QWebEngineProfile::downloadRequested,
+ [&](QWebEngineDownloadItem *item) {
+ QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadRequested);
+ QCOMPARE(item->isFinished(), false);
+ QCOMPARE(item->totalBytes(), -1);
+ QCOMPARE(item->receivedBytes(), 0);
+ QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
+ QCOMPARE(item->type(), downloadType);
+ QCOMPARE(item->mimeType(), QString(fileMimeTypeDetected));
+ QCOMPARE(item->path(), suggestedPath);
+ QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat);
+ QCOMPARE(item->url(), downloadUrl);
+ item->setPath(downloadPath);
+ item->accept();
+ downloadItem = item;
+ }));
+ QVERIFY(downloadItem);
+ bool finishOk = false;
+ QVERIFY(waitForSignal(downloadItem, &QWebEngineDownloadItem::finished, [&]() {
+ auto item = downloadItem;
+ QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted);
+ QCOMPARE(item->isFinished(), true);
+ QCOMPARE(item->totalBytes(), fileContents.size());
+ QCOMPARE(item->receivedBytes(), fileContents.size());
+ QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
+ QCOMPARE(item->type(), downloadType);
+ QCOMPARE(item->mimeType(), QString(fileMimeTypeDetected));
+ QCOMPARE(item->path(), downloadPath);
+ QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat);
+ QCOMPARE(item->url(), downloadUrl);
+ finishOk = true;
+ }));
+ QVERIFY(finishOk);
+
+ // 5. Check actual file contents
+ QFile file(downloadPath);
+ QVERIFY(file.open(QIODevice::ReadOnly));
+ QCOMPARE(file.readAll(), fileContents);
+}
+
+void tst_QWebEngineDownloads::downloadTwoLinks()
+{
+ HttpServer server;
+ QWebEngineProfile profile;
+ QWebEnginePage page(&profile);
+ QWebEngineView view;
+ view.setPage(&page);
+
+ view.load(server.url());
+ view.show();
+ auto indexRR = waitForRequest(&server);
+ QVERIFY(indexRR);
+ QCOMPARE(indexRR->requestMethod(), QByteArrayLiteral("GET"));
+ QCOMPARE(indexRR->requestPath(), QByteArrayLiteral("/"));
+ indexRR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html"));
+ indexRR->setResponseBody(QByteArrayLiteral("<html><body><a href=\"file1\" download>Link1</a><br/><a href=\"file2\">Link2</a></body></html>"));
+ indexRR->sendResponse();
+ bool loadOk = false;
+ QVERIFY(waitForSignal(&page, &QWebEnginePage::loadFinished, [&](bool ok){ loadOk = ok; }));
+ QVERIFY(loadOk);
+
+ auto favIconRR = waitForRequest(&server);
+ QVERIFY(favIconRR);
+ QCOMPARE(favIconRR->requestMethod(), QByteArrayLiteral("GET"));
+ QCOMPARE(favIconRR->requestPath(), QByteArrayLiteral("/favicon.ico"));
+ favIconRR->setResponseStatus(404);
+ favIconRR->sendResponse();
+
+ QWidget *renderWidget = view.focusWidget();
+ QTest::mouseClick(renderWidget, Qt::LeftButton, {}, QPoint(10, 10));
+ QTest::mouseClick(renderWidget, Qt::LeftButton, {}, QPoint(10, 30));
+
+ auto file1RR = waitForRequest(&server);
+ QVERIFY(file1RR);
+ QCOMPARE(file1RR->requestMethod(), QByteArrayLiteral("GET"));
+ QCOMPARE(file1RR->requestPath(), QByteArrayLiteral("/file1"));
+ auto file2RR = waitForRequest(&server);
+ QVERIFY(file2RR);
+ QCOMPARE(file2RR->requestMethod(), QByteArrayLiteral("GET"));
+ QCOMPARE(file2RR->requestPath(), QByteArrayLiteral("/file2"));
+
+ file1RR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/plain"));
+ file1RR->setResponseBody(QByteArrayLiteral("file1"));
+ file1RR->sendResponse();
+ file2RR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/plain"));
+ file2RR->setResponseHeader(QByteArrayLiteral("content-disposition"), QByteArrayLiteral("attachment"));
+ file2RR->setResponseBody(QByteArrayLiteral("file2"));
+ file2RR->sendResponse();
+
+ QTemporaryDir tmpDir;
+ QVERIFY(tmpDir.isValid());
+ QString standardDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
+ QWebEngineDownloadItem *item1 = nullptr;
+ QVERIFY(waitForSignal(&profile, &QWebEngineProfile::downloadRequested,
+ [&](QWebEngineDownloadItem *item) {
+ QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadRequested);
+ QCOMPARE(item->isFinished(), false);
+ QCOMPARE(item->totalBytes(), -1);
+ QCOMPARE(item->receivedBytes(), 0);
+ QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
+ QCOMPARE(item->type(), QWebEngineDownloadItem::DownloadAttribute);
+ QCOMPARE(item->mimeType(), QStringLiteral("text/plain"));
+ QCOMPARE(item->path(), standardDir + QByteArrayLiteral("/file1"));
+ QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat);
+ QCOMPARE(item->url(), server.url(QByteArrayLiteral("/file1")));
+ item->setPath(tmpDir.path() + QByteArrayLiteral("/file1"));
+ item->accept();
+ item1 = item;
+ }));
+ QVERIFY(item1);
+
+ QWebEngineDownloadItem *item2 = nullptr;
+ QVERIFY(waitForSignal(&profile, &QWebEngineProfile::downloadRequested,
+ [&](QWebEngineDownloadItem *item) {
+ QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadRequested);
+ QCOMPARE(item->isFinished(), false);
+ QCOMPARE(item->totalBytes(), -1);
+ QCOMPARE(item->receivedBytes(), 0);
+ QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
+ QCOMPARE(item->type(), QWebEngineDownloadItem::DownloadAttribute);
+ QCOMPARE(item->mimeType(), QStringLiteral("text/plain"));
+ QCOMPARE(item->path(), standardDir + QByteArrayLiteral("/file2"));
+ QCOMPARE(item->savePageFormat(), QWebEngineDownloadItem::UnknownSaveFormat);
+ QCOMPARE(item->url(), server.url(QByteArrayLiteral("/file2")));
+ item->setPath(tmpDir.path() + QByteArrayLiteral("/file2"));
+ item->accept();
+ item2 = item;
+ }));
+ QVERIFY(item2);
+}
+
+void tst_QWebEngineDownloads::downloadPage_data()
+{
+ QTest::addColumn<QWebEngineDownloadItem::SavePageFormat>("savePageFormat");
+ QTest::newRow("SingleHtmlSaveFormat") << QWebEngineDownloadItem::SingleHtmlSaveFormat;
+ QTest::newRow("CompleteHtmlSaveFormat") << QWebEngineDownloadItem::CompleteHtmlSaveFormat;
+ QTest::newRow("MimeHtmlSaveFormat") << QWebEngineDownloadItem::MimeHtmlSaveFormat;
+}
+
+void tst_QWebEngineDownloads::downloadPage()
+{
+ QFETCH(QWebEngineDownloadItem::SavePageFormat, savePageFormat);
+
+ HttpServer server;
+ QWebEngineProfile profile;
+ QWebEnginePage page(&profile);
+ QWebEngineView view;
+ view.setPage(&page);
+
+ view.load(server.url());
+ view.show();
+ auto indexRR = waitForRequest(&server);
+ QVERIFY(indexRR);
+ QCOMPARE(indexRR->requestMethod(), QByteArrayLiteral("GET"));
+ QCOMPARE(indexRR->requestPath(), QByteArrayLiteral("/"));
+ indexRR->setResponseHeader(QByteArrayLiteral("content-type"), QByteArrayLiteral("text/html"));
+ indexRR->setResponseBody(QByteArrayLiteral("<html><body>Hello</body></html>"));
+ indexRR->sendResponse();
+ bool loadOk = false;
+ QVERIFY(waitForSignal(&page, &QWebEnginePage::loadFinished, [&](bool ok){ loadOk = ok; }));
+ QVERIFY(loadOk);
+
+ auto favIconRR = waitForRequest(&server);
+ QVERIFY(favIconRR);
+ QCOMPARE(favIconRR->requestMethod(), QByteArrayLiteral("GET"));
+ QCOMPARE(favIconRR->requestPath(), QByteArrayLiteral("/favicon.ico"));
+ favIconRR->setResponseStatus(404);
+ favIconRR->sendResponse();
+
+ QTemporaryDir tmpDir;
+ QVERIFY(tmpDir.isValid());
+ QString downloadPath = tmpDir.path() + QStringLiteral("/test.html");
+ page.save(downloadPath, savePageFormat);
+
+ QWebEngineDownloadItem *downloadItem = nullptr;
+ QUrl downloadUrl = server.url("/");
+ QVERIFY(waitForSignal(&profile, &QWebEngineProfile::downloadRequested,
+ [&](QWebEngineDownloadItem *item) {
+ QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadInProgress);
+ QCOMPARE(item->isFinished(), false);
+ QCOMPARE(item->totalBytes(), -1);
+ QCOMPARE(item->receivedBytes(), 0);
+ QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
+ QCOMPARE(item->type(), QWebEngineDownloadItem::SavePage);
+ // FIXME why is mimeType always the same?
+ QCOMPARE(item->mimeType(), QStringLiteral("application/x-mimearchive"));
+ QCOMPARE(item->path(), downloadPath);
+ QCOMPARE(item->savePageFormat(), savePageFormat);
+ QCOMPARE(item->url(), downloadUrl);
+ // no need to call item->accept()
+ downloadItem = item;
+ }));
+ QVERIFY(downloadItem);
+ bool finishOk = false;
+ QVERIFY(waitForSignal(downloadItem, &QWebEngineDownloadItem::finished, [&]() {
+ auto item = downloadItem;
+ QCOMPARE(item->state(), QWebEngineDownloadItem::DownloadCompleted);
+ QCOMPARE(item->isFinished(), true);
+ QCOMPARE(item->totalBytes(), item->receivedBytes());
+ QVERIFY(item->receivedBytes() > 0);
+ QCOMPARE(item->interruptReason(), QWebEngineDownloadItem::NoReason);
+ QCOMPARE(item->type(), QWebEngineDownloadItem::SavePage);
+ QCOMPARE(item->mimeType(), QStringLiteral("application/x-mimearchive"));
+ QCOMPARE(item->path(), downloadPath);
+ QCOMPARE(item->savePageFormat(), savePageFormat);
+ QCOMPARE(item->url(), downloadUrl);
+ finishOk = true;
+ }));
+ QVERIFY(finishOk);
+
+ QFile file(downloadPath);
+ QVERIFY(file.exists());
+}
+
+QTEST_MAIN(tst_QWebEngineDownloads)
+#include "tst_qwebenginedownloads.moc"
diff --git a/tests/auto/widgets/widgets.pro b/tests/auto/widgets/widgets.pro
index 90352310e..441eea0fa 100644
--- a/tests/auto/widgets/widgets.pro
+++ b/tests/auto/widgets/widgets.pro
@@ -3,6 +3,7 @@ TEMPLATE = subdirs
SUBDIRS += \
qwebengineaccessibility \
qwebenginedefaultsurfaceformat \
+ qwebenginedownloads \
qwebenginefaviconmanager \
qwebenginepage \
qwebenginehistory \
@@ -24,4 +25,5 @@ contains(WEBENGINE_CONFIG, use_spellchecker):!cross_compile {
# QTBUG-60268
boot2qt: SUBDIRS -= qwebengineaccessibility qwebenginedefaultsurfaceformat \
qwebenginefaviconmanager qwebenginepage qwebenginehistory \
- qwebengineprofile qwebenginescript qwebengineview
+ qwebengineprofile qwebenginescript qwebengineview \
+ qwebenginedownloads