diff options
Diffstat (limited to 'tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp')
-rw-r--r-- | tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp | 764 |
1 files changed, 630 insertions, 134 deletions
diff --git a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp index c0762aa14..7cea14c0c 100644 --- a/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp +++ b/tests/auto/core/qwebengineurlrequestinterceptor/tst_qwebengineurlrequestinterceptor.cpp @@ -1,38 +1,17 @@ -/**************************************************************************** -** -** 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 "../../widgets/util.h" +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses + +#include <util.h> #include <QtTest/QtTest> #include <QtWebEngineCore/qwebengineurlrequestinfo.h> +#include <QtWebEngineCore/private/qwebengineurlrequestinfo_p.h> #include <QtWebEngineCore/qwebengineurlrequestinterceptor.h> -#include <QtWebEngineWidgets/qwebenginepage.h> -#include <QtWebEngineWidgets/qwebengineprofile.h> -#include <QtWebEngineWidgets/qwebenginesettings.h> +#include <QtWebEngineCore/qwebenginesettings.h> +#include <QtWebEngineCore/qwebengineprofile.h> +#include <QtWebEngineCore/qwebenginepage.h> +#include <QtWebEngineCore/qwebenginehttprequest.h> #include <httpserver.h> #include <httpreqrep.h> @@ -64,8 +43,18 @@ private Q_SLOTS: void requestInterceptorByResourceType_data(); void requestInterceptorByResourceType(); void firstPartyUrlHttp(); - void passRefererHeader(); + void headers(); + void customHeaders(); void initiator(); + void jsServiceWorker(); + void replaceInterceptor_data(); + void replaceInterceptor(); + void replaceOnIntercept(); + void multipleRedirects(); + void postWithBody_data(); + void postWithBody(); + void profilePreventsPageInterception_data(); + void profilePreventsPageInterception(); }; tst_QWebEngineUrlRequestInterceptor::tst_QWebEngineUrlRequestInterceptor() @@ -98,39 +87,61 @@ struct RequestInfo { , firstPartyUrl(info.firstPartyUrl()) , initiator(info.initiator()) , resourceType(info.resourceType()) + , headers(info.httpHeaders()) {} QUrl requestUrl; QUrl firstPartyUrl; QUrl initiator; int resourceType; + QHash<QByteArray, QByteArray> headers; }; -static const QByteArray kHttpHeaderReferrerValue = QByteArrayLiteral("http://somereferrer.com/"); -static const QByteArray kHttpHeaderRefererName = QByteArrayLiteral("referer"); +static const QUrl kRedirectUrl = QUrl("qrc:///resources/content.html"); + +Q_LOGGING_CATEGORY(lc, "qt.webengine.tests") class TestRequestInterceptor : public QWebEngineUrlRequestInterceptor { public: QList<RequestInfo> requestInfos; - bool shouldIntercept; - QMap<QUrl, QUrl> requestInitiatorUrls; + bool shouldRedirect = false; + QUrl redirectUrl; + QMap<QUrl, QSet<QUrl>> requestInitiatorUrls; + QMap<QByteArray, QByteArray> headers; + std::function<bool (QWebEngineUrlRequestInfo &)> onIntercept; void interceptRequest(QWebEngineUrlRequestInfo &info) override { QVERIFY(QThread::currentThread() == QCoreApplication::instance()->thread()); + qCDebug(lc) << this << "Type:" << info.resourceType() << info.requestMethod() << "Navigation:" << info.navigationType() + << info.requestUrl() << "Initiator:" << info.initiator(); + // Since 63 we also intercept some unrelated blob requests.. if (info.requestUrl().scheme() == QLatin1String("blob")) return; - info.block(info.requestMethod() != QByteArrayLiteral("GET")); - if (shouldIntercept && info.requestUrl().toString().endsWith(QLatin1String("__placeholder__"))) - info.redirect(QUrl("qrc:///resources/content.html")); - // Set referrer header - info.setHttpHeader(kHttpHeaderRefererName, kHttpHeaderReferrerValue); + if (onIntercept && !onIntercept(info)) + return; + + bool block = info.requestMethod() != QByteArrayLiteral("GET"); + bool redirect = shouldRedirect && info.requestUrl() != redirectUrl; - requestInitiatorUrls.insert(info.requestUrl(), info.initiator()); + // set additional headers if any required by test + for (auto it = headers.begin(); it != headers.end(); ++it) info.setHttpHeader(it.key(), it.value()); + + if (block) { + info.block(true); + } else if (redirect) { + info.redirect(redirectUrl); + } + + requestInitiatorUrls[info.requestUrl()].insert(info.initiator()); requestInfos.append(info); + + // MEMO avoid unintentionally changing request when it is not needed for test logic + // since api behavior depends on 'changed' state of the info object + Q_ASSERT(info.changed() == (block || redirect)); } bool shouldSkipRequest(const RequestInfo &requestInfo) @@ -172,24 +183,65 @@ public: return false; } - TestRequestInterceptor(bool intercept) - : shouldIntercept(intercept) + TestRequestInterceptor(bool redirect = false, const QUrl &url = kRedirectUrl) + : shouldRedirect(redirect), redirectUrl(url) + { + } +}; + +class TestMultipleRedirectsInterceptor : public QWebEngineUrlRequestInterceptor { +public: + QList<RequestInfo> requestInfos; + QMap<QUrl, QUrl> redirectPairs; + int redirectCount = 0; + void interceptRequest(QWebEngineUrlRequestInfo &info) override + { + QVERIFY(QThread::currentThread() == QCoreApplication::instance()->thread()); + qCDebug(lc) << this << "Type:" << info.resourceType() << info.requestMethod() << "Navigation:" << info.navigationType() + << info.requestUrl() << "Initiator:" << info.initiator(); + auto redirectUrl = redirectPairs.constFind(info.requestUrl()); + if (redirectUrl != redirectPairs.constEnd()) { + info.redirect(redirectUrl.value()); + requestInfos.append(info); + redirectCount++; + } + } + + TestMultipleRedirectsInterceptor() { } }; +class ConsolePage : public QWebEnginePage { + Q_OBJECT +public: + ConsolePage(QWebEngineProfile* profile) : QWebEnginePage(profile) {} + + void javaScriptConsoleMessage(JavaScriptConsoleMessageLevel level, const QString& message, int lineNumber, const QString& sourceID) override + { + levels.append(level); + messages.append(message); + lineNumbers.append(lineNumber); + sourceIDs.append(sourceID); + } + + QList<int> levels; + QStringList messages; + QList<int> lineNumbers; + QStringList sourceIDs; +}; + void tst_QWebEngineUrlRequestInterceptor::interceptRequest() { QWebEngineProfile profile; profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); - TestRequestInterceptor interceptor(/* intercept */ true); + TestRequestInterceptor interceptor(/* intercept */ false); profile.setUrlRequestInterceptor(&interceptor); - QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.load(QUrl("qrc:///resources/index.html")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); QVariant success = loadSpy.takeFirst().takeFirst(); QVERIFY(success.toBool()); loadSpy.clear(); @@ -197,29 +249,30 @@ void tst_QWebEngineUrlRequestInterceptor::interceptRequest() page.runJavaScript("post();", [&ok](const QVariant result){ ok = result; }); QTRY_VERIFY(ok.toBool()); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(loadSpy.size(), 1); success = loadSpy.takeFirst().takeFirst(); // We block non-GET requests, so this should not succeed. QVERIFY(!success.toBool()); loadSpy.clear(); + interceptor.shouldRedirect = true; page.load(QUrl("qrc:///resources/__placeholder__")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); success = loadSpy.takeFirst().takeFirst(); // The redirection for __placeholder__ should succeed. QVERIFY(success.toBool()); loadSpy.clear(); - QCOMPARE(interceptor.requestInfos.count(), 4); + QCOMPARE(interceptor.requestInfos.size(), 4); // Make sure that registering an observer does not modify the request. TestRequestInterceptor observer(/* intercept */ false); profile.setUrlRequestInterceptor(&observer); page.load(QUrl("qrc:///resources/__placeholder__")); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); success = loadSpy.takeFirst().takeFirst(); // Since we do not intercept, loading an invalid path should not succeed. QVERIFY(!success.toBool()); - QCOMPARE(observer.requestInfos.count(), 1); + QCOMPARE(observer.requestInfos.size(), 1); } class LocalhostContentProvider : public QWebEngineUrlRequestInterceptor @@ -252,24 +305,23 @@ void tst_QWebEngineUrlRequestInterceptor::ipv6HostEncoding() QSignalSpy spyLoadFinished(&page, SIGNAL(loadFinished(bool))); page.setHtml("<p>Hi", QUrl::fromEncoded("http://[::1]/index.html")); - QTRY_COMPARE(spyLoadFinished.count(), 1); - QCOMPARE(contentProvider.requestedUrls.count(), 0); + QTRY_COMPARE(spyLoadFinished.size(), 1); + QCOMPARE(contentProvider.requestedUrls.size(), 0); evaluateJavaScriptSync(&page, "var r = new XMLHttpRequest();" "r.open('GET', 'http://[::1]/test.xml', false);" "r.send(null);" ); - QCOMPARE(contentProvider.requestedUrls.count(), 1); + QCOMPARE(contentProvider.requestedUrls.size(), 1); QCOMPARE(contentProvider.requestedUrls.at(0), QUrl::fromEncoded("http://[::1]/test.xml")); } void tst_QWebEngineUrlRequestInterceptor::requestedUrl_data() { QTest::addColumn<bool>("interceptInPage"); - - QTest::newRow("Profile intercept") << false; - QTest::newRow("Page intercept") << true; + QTest::newRow("profile intercept") << false; + QTest::newRow("page intercept") << true; } void tst_QWebEngineUrlRequestInterceptor::requestedUrl() @@ -289,19 +341,24 @@ void tst_QWebEngineUrlRequestInterceptor::requestedUrl() page.setUrl(QUrl("qrc:///resources/__placeholder__")); QVERIFY(spy.wait()); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 20000); + QVERIFY(interceptor.requestInfos.size() >= 1); QCOMPARE(interceptor.requestInfos.at(0).requestUrl, QUrl("qrc:///resources/content.html")); QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__")); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); + interceptor.shouldRedirect = false; + page.setUrl(QUrl("qrc:/non-existent.html")); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 2, 20000); + QVERIFY(interceptor.requestInfos.size() >= 3); QCOMPARE(interceptor.requestInfos.at(2).requestUrl, QUrl("qrc:/non-existent.html")); QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__")); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); page.setUrl(QUrl("http://abcdef.abcdef")); - QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 3, 15000); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 3, 20000); + QVERIFY(interceptor.requestInfos.size() >= 4); QCOMPARE(interceptor.requestInfos.at(3).requestUrl, QUrl("http://abcdef.abcdef/")); QCOMPARE(page.requestedUrl(), QUrl("qrc:///resources/__placeholder__")); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); @@ -329,23 +386,23 @@ void tst_QWebEngineUrlRequestInterceptor::setUrlSameUrl() page.setUrl(QUrl("qrc:///resources/__placeholder__")); QVERIFY(spy.wait()); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); page.setUrl(QUrl("qrc:///resources/__placeholder__")); QVERIFY(spy.wait()); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); // Now a case without redirect. page.setUrl(QUrl("qrc:///resources/content.html")); QVERIFY(spy.wait()); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); - QCOMPARE(spy.count(), 3); + QCOMPARE(spy.size(), 3); page.setUrl(QUrl("qrc:///resources/__placeholder__")); QVERIFY(spy.wait()); QCOMPARE(page.url(), QUrl("qrc:///resources/content.html")); - QCOMPARE(spy.count(), 4); + QCOMPARE(spy.size(), 4); } void tst_QWebEngineUrlRequestInterceptor::firstPartyUrl() @@ -359,28 +416,34 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrl() page.setUrl(QUrl("qrc:///resources/firstparty.html")); QVERIFY(spy.wait()); + QVERIFY(interceptor.requestInfos.size() >= 2); QCOMPARE(interceptor.requestInfos.at(0).requestUrl, QUrl("qrc:///resources/firstparty.html")); QCOMPARE(interceptor.requestInfos.at(1).requestUrl, QUrl("qrc:///resources/content.html")); QCOMPARE(interceptor.requestInfos.at(0).firstPartyUrl, QUrl("qrc:///resources/firstparty.html")); QCOMPARE(interceptor.requestInfos.at(1).firstPartyUrl, QUrl("qrc:///resources/firstparty.html")); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlNestedIframes_data() { - QUrl url = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebengineurlrequestinterceptor/resources/iframe.html")); - + QUrl url = QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/resources/iframe.html")); QTest::addColumn<QUrl>("requestUrl"); - QTest::newRow("file") << url; - QTest::newRow("qrc") << QUrl("qrc:///resources/iframe.html"); + QTest::newRow("ui file") << url; + QTest::newRow("ui qrc") << QUrl("qrc:///resources/iframe.html"); } void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlNestedIframes() { QFETCH(QUrl, requestUrl); - if (requestUrl.scheme() == "file" && !QDir(TESTS_SOURCE_DIR).exists()) - W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); + if (requestUrl.scheme() == "file" + && !QDir(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()).exists()) + W_QSKIP(QString("This test requires access to resources found in '%1'") + .arg(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()) + .toLatin1() + .constData(), + SkipAll); QString adjustedUrl = requestUrl.adjusted(QUrl::RemoveFilename).toString(); @@ -391,18 +454,21 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlNestedIframes() QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.setUrl(requestUrl); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); + QVERIFY(interceptor.requestInfos.size() >= 1); RequestInfo info = interceptor.requestInfos.at(0); QCOMPARE(info.requestUrl, requestUrl); QCOMPARE(info.firstPartyUrl, requestUrl); QCOMPARE(info.resourceType, QWebEngineUrlRequestInfo::ResourceTypeMainFrame); + QVERIFY(interceptor.requestInfos.size() >= 2); info = interceptor.requestInfos.at(1); QCOMPARE(info.requestUrl, QUrl(adjustedUrl + "iframe2.html")); QCOMPARE(info.firstPartyUrl, requestUrl); QCOMPARE(info.resourceType, QWebEngineUrlRequestInfo::ResourceTypeSubFrame); + QVERIFY(interceptor.requestInfos.size() >= 3); info = interceptor.requestInfos.at(2); QCOMPARE(info.requestUrl, QUrl(adjustedUrl + "iframe3.html")); QCOMPARE(info.firstPartyUrl, requestUrl); @@ -411,36 +477,62 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlNestedIframes() void tst_QWebEngineUrlRequestInterceptor::requestInterceptorByResourceType_data() { - QUrl firstPartyUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebengineurlrequestinterceptor/resources/resource_in_iframe.html")); - QUrl styleRequestUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebengineurlrequestinterceptor/resources/style.css")); - QUrl scriptRequestUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebengineurlrequestinterceptor/resources/script.js")); - QUrl fontRequestUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebengineurlrequestinterceptor/resources/fontawesome.woff")); - QUrl xhrRequestUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebengineurlrequestinterceptor/resources/test")); - QUrl imageFirstPartyUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebengineurlrequestinterceptor/resources/image_in_iframe.html")); - QUrl imageRequestUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebengineurlrequestinterceptor/resources/icons/favicon.png")); - QUrl mediaFirstPartyUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebengineurlrequestinterceptor/resources/media_in_iframe.html")); - QUrl mediaRequestUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebengineurlrequestinterceptor/resources/media.mp4")); - QUrl faviconFirstPartyUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebengineurlrequestinterceptor/resources/favicon.html")); - QUrl faviconRequestUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebengineurlrequestinterceptor/resources/icons/favicon.png")); + QUrl firstPartyUrl = QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/resources/resource_in_iframe.html")); + QUrl styleRequestUrl = QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/resources/style.css")); + QUrl scriptRequestUrl = QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/resources/script.js")); + QUrl fontRequestUrl = QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/resources/fontawesome.woff")); + QUrl xhrRequestUrl = QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/resources/test")); + QUrl imageFirstPartyUrl = + QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/resources/image_in_iframe.html")); + QUrl imageRequestUrl = QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/resources/icons/favicon.png")); + QUrl mediaFirstPartyUrl = + QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/resources/media_in_iframe.html")); + QUrl mediaRequestUrl = QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/resources/media.mp4")); + QUrl faviconFirstPartyUrl = QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/resources/favicon.html")); + QUrl faviconRequestUrl = QUrl::fromLocalFile(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + QLatin1String("/resources/icons/favicon.png")); QTest::addColumn<QUrl>("requestUrl"); QTest::addColumn<QUrl>("firstPartyUrl"); QTest::addColumn<int>("resourceType"); - QTest::newRow("StyleSheet") << styleRequestUrl << firstPartyUrl << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeStylesheet); - QTest::newRow("Script") << scriptRequestUrl << firstPartyUrl << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeScript); - QTest::newRow("Image") << imageRequestUrl << imageFirstPartyUrl << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeImage); - QTest::newRow("FontResource") << fontRequestUrl << firstPartyUrl << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeFontResource); - QTest::newRow("Media") << mediaRequestUrl << mediaFirstPartyUrl << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeMedia); - QTest::newRow("Favicon") << faviconRequestUrl << faviconFirstPartyUrl << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeFavicon); - QTest::newRow("Xhr") << xhrRequestUrl << firstPartyUrl << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeXhr); + QTest::newRow("StyleSheet") + << styleRequestUrl << firstPartyUrl + << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeStylesheet); + QTest::newRow("Script") << scriptRequestUrl << firstPartyUrl + << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeScript); + QTest::newRow("Image") << imageRequestUrl << imageFirstPartyUrl + << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeImage); + QTest::newRow("FontResource") + << fontRequestUrl << firstPartyUrl + << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeFontResource); + QTest::newRow(qPrintable("Media")) << mediaRequestUrl << mediaFirstPartyUrl + << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeMedia); + QTest::newRow("Favicon") + << faviconRequestUrl << faviconFirstPartyUrl + << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeFavicon); + QTest::newRow(qPrintable("Xhr")) << xhrRequestUrl << firstPartyUrl + << static_cast<int>(QWebEngineUrlRequestInfo::ResourceTypeXhr); } void tst_QWebEngineUrlRequestInterceptor::requestInterceptorByResourceType() { - if (!QDir(TESTS_SOURCE_DIR).exists()) - W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll); - + if (!QDir(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()).exists()) + W_QSKIP(QString("This test requires access to resources found in '%1'") + .arg(QDir(QT_TESTCASE_SOURCEDIR).canonicalPath()) + .toLatin1() + .constData(), + SkipAll); QFETCH(QUrl, requestUrl); QFETCH(QUrl, firstPartyUrl); QFETCH(int, resourceType); @@ -452,10 +544,11 @@ void tst_QWebEngineUrlRequestInterceptor::requestInterceptorByResourceType() QWebEnginePage page(&profile); QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); page.setUrl(firstPartyUrl); - QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE_WITH_TIMEOUT(loadSpy.size(), 1, 20000); - QTRY_COMPARE(interceptor.getUrlRequestForType(static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType)).count(), 1); + QTRY_COMPARE(interceptor.getUrlRequestForType(static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType)).size(), 1); QList<RequestInfo> infos = interceptor.getUrlRequestForType(static_cast<QWebEngineUrlRequestInfo::ResourceType>(resourceType)); + QVERIFY(infos.size() >= 1); QCOMPARE(infos.at(0).requestUrl, requestUrl); QCOMPARE(infos.at(0).firstPartyUrl, firstPartyUrl); QCOMPARE(infos.at(0).resourceType, resourceType); @@ -476,12 +569,6 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlHttp() QList<RequestInfo> infos; - // SubFrame - QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeSubFrame)); - infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeSubFrame); - foreach (auto info, infos) - QCOMPARE(info.firstPartyUrl, firstPartyUrl); - // Stylesheet QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeStylesheet)); infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeStylesheet); @@ -525,37 +612,98 @@ void tst_QWebEngineUrlRequestInterceptor::firstPartyUrlHttp() QCOMPARE(info.firstPartyUrl, firstPartyUrl); } -void tst_QWebEngineUrlRequestInterceptor::passRefererHeader() +void tst_QWebEngineUrlRequestInterceptor::headers() { - // Create HTTP Server to parse the request. HttpServer httpServer; + httpServer.setResourceDirs({ QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + "/resources" }); + QVERIFY(httpServer.start()); + QWebEngineProfile profile; + TestRequestInterceptor interceptor(false); + profile.setUrlRequestInterceptor(&interceptor); - if (!httpServer.start()) - QSKIP("Failed to start http server"); + QWebEnginePage page(&profile); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); - bool succeeded = false; - connect(&httpServer, &HttpServer::newRequest, [&succeeded](HttpReqRep *rr) { - const QByteArray headerValue = rr->requestHeader(kHttpHeaderRefererName); - QCOMPARE(headerValue, kHttpHeaderReferrerValue); - succeeded = headerValue == kHttpHeaderReferrerValue; - rr->setResponseStatus(200); - rr->sendResponse(); - }); + QWebEngineHttpRequest request(httpServer.url("/content.html")); + request.setHeader("X-HEADERNAME", "HEADERVALUE"); + page.load(request); + QVERIFY(spy.wait()); + QVERIFY(interceptor.requestInfos.last().headers.contains("X-HEADERNAME")); + QCOMPARE(interceptor.requestInfos.last().headers.value("X-HEADERNAME"), + QByteArray("HEADERVALUE")); + + bool jsFinished = false; + + page.runJavaScript(R"( +var request = new XMLHttpRequest(); +request.open('GET', 'resource.html', /* async = */ false); +request.setRequestHeader('X-FOO', 'BAR'); +request.send(); +)", + [&](const QVariant &) { jsFinished = true; }); + QTRY_VERIFY(jsFinished); + QVERIFY(interceptor.requestInfos.last().headers.contains("X-FOO")); + QCOMPARE(interceptor.requestInfos.last().headers.value("X-FOO"), QByteArray("BAR")); +} + +void tst_QWebEngineUrlRequestInterceptor::customHeaders() +{ + // Create HTTP Server to parse the request. + HttpServer httpServer; + httpServer.setResourceDirs({ QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + + "/resources" }); + QVERIFY(httpServer.start()); QWebEngineProfile profile; - TestRequestInterceptor interceptor(true); + TestRequestInterceptor interceptor(false); profile.setUrlRequestInterceptor(&interceptor); QWebEnginePage page(&profile); QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); - QWebEngineHttpRequest httpRequest; - QUrl requestUrl = httpServer.url(); - httpRequest.setUrl(requestUrl); - page.load(httpRequest); + interceptor.headers = { + { "referer", "http://somereferrer.com/" }, + { "from", "user@example.com" }, + { "user-agent", "mozilla/5.0 (x11; linux x86_64; rv:12.0) gecko/20100101 firefox/12.0" }, + }; + + QMap<QByteArray, QByteArray> actual, expected; + connect(&httpServer, &HttpServer::newRequest, [&] (HttpReqRep *rr) { + for (auto it = expected.begin(); it != expected.end(); ++it) { + auto headerValue = rr->requestHeader(it.key()); + actual[it.key()] = headerValue; + QCOMPARE(headerValue, it.value()); + } + }); + + auto dumpHeaders = [&] () { + QString s; QDebug d(&s); + for (auto it = expected.begin(); it != expected.end(); ++it) + d << "\n\tHeader:" << it.key() << "| actual:" << actual[it.key()] << "expected:" << it.value(); + return s; + }; + + expected = interceptor.headers; + page.load(httpServer.url("/content.html")); QVERIFY(spy.wait()); + QVERIFY2(actual == expected, qPrintable(dumpHeaders())); + + // test that custom headers are also applied on redirect + interceptor.shouldRedirect = true; + interceptor.redirectUrl = httpServer.url("/content2.html"); + interceptor.headers = { + { "referer", "http://somereferrer2.com/" }, + { "from", "user2@example.com" }, + { "user-agent", "mozilla/5.0 (compatible; googlebot/2.1; +http://www.google.com/bot.html)" }, + }; + + actual.clear(); + expected = interceptor.headers; + page.triggerAction(QWebEnginePage::Reload); + QVERIFY(spy.wait()); + QVERIFY2(actual == expected, qPrintable(dumpHeaders())); + (void) httpServer.stop(); - QVERIFY(succeeded); } void tst_QWebEngineUrlRequestInterceptor::initiator() @@ -573,53 +721,401 @@ void tst_QWebEngineUrlRequestInterceptor::initiator() QList<RequestInfo> infos; - // SubFrame - QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeSubFrame)); - infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeSubFrame); - foreach (auto info, infos) - QCOMPARE(info.initiator, interceptor.requestInitiatorUrls[info.requestUrl]); - // Stylesheet QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeStylesheet)); infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeStylesheet); foreach (auto info, infos) - QCOMPARE(info.initiator, interceptor.requestInitiatorUrls[info.requestUrl]); + QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator)); // Script QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeScript)); infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeScript); foreach (auto info, infos) - QCOMPARE(info.initiator, interceptor.requestInitiatorUrls[info.requestUrl]); + QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator)); // Image QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeImage)); infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeImage); foreach (auto info, infos) - QCOMPARE(info.initiator, interceptor.requestInitiatorUrls[info.requestUrl]); + QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator)); // FontResource QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeFontResource)); infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeFontResource); foreach (auto info, infos) - QCOMPARE(info.initiator, interceptor.requestInitiatorUrls[info.requestUrl]); + QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator)); // Media QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeMedia)); infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeMedia); foreach (auto info, infos) - QCOMPARE(info.initiator, interceptor.requestInitiatorUrls[info.requestUrl]); + QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator)); // Favicon QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeFavicon)); infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeFavicon); foreach (auto info, infos) - QCOMPARE(info.initiator, interceptor.requestInitiatorUrls[info.requestUrl]); + QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator)); // XMLHttpRequest QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeXhr)); infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeXhr); foreach (auto info, infos) - QCOMPARE(info.initiator, interceptor.requestInitiatorUrls[info.requestUrl]); + QVERIFY(interceptor.requestInitiatorUrls[info.requestUrl].contains(info.initiator)); +} + +void tst_QWebEngineUrlRequestInterceptor::jsServiceWorker() +{ + + HttpServer server; + server.setResourceDirs({ QDir(QT_TESTCASE_SOURCEDIR).canonicalPath() + "/resources" }); + QVERIFY(server.start()); + QWebEngineProfile profile; + std::unique_ptr<ConsolePage> page; + page.reset(new ConsolePage(&profile)); + TestRequestInterceptor interceptor(/* intercept */ false); + profile.setUrlRequestInterceptor(&interceptor); + QVERIFY(loadSync(page.get(), server.url("/sw.html"))); + + // We expect only one message here, because logging of services workers is not exposed in our API. + // Note this is very fragile setup , you need fresh profile otherwise install event might not get triggered + // and this in turn can lead to incorrect intercepted requests, therefore we should keep this off the record. + QTRY_COMPARE_WITH_TIMEOUT(page->messages.size(), 5, 20000); + + QCOMPARE(page->levels.at(0), QWebEnginePage::InfoMessageLevel); + QCOMPARE(page->messages.at(0),QLatin1String("Service worker installing")); + QCOMPARE(page->messages.at(1),QLatin1String("Service worker installed")); + QCOMPARE(page->messages.at(2),QLatin1String("Service worker activating")); + QCOMPARE(page->messages.at(3),QLatin1String("Service worker activated")); + QCOMPARE(page->messages.at(4),QLatin1String("Service worker done")); + QUrl firstPartyUrl = QUrl(server.url().toString() + "sw.html"); + QList<RequestInfo> infos; + // Service Worker + QTRY_VERIFY(interceptor.hasUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeServiceWorker)); + infos = interceptor.getUrlRequestForType(QWebEngineUrlRequestInfo::ResourceTypeServiceWorker); + foreach (auto info, infos) + QCOMPARE(info.firstPartyUrl, firstPartyUrl); + + QVERIFY(server.stop()); +} + +void tst_QWebEngineUrlRequestInterceptor::replaceInterceptor_data() +{ + QTest::addColumn<bool>("firstInterceptIsInPage"); + QTest::addColumn<bool>("keepInterceptionPoint"); + QTest::newRow("page") << true << true; + QTest::newRow("page-profile") << true << false; + QTest::newRow("profile") << false << true; + QTest::newRow("profile-page") << false << false; +} + +void tst_QWebEngineUrlRequestInterceptor::replaceInterceptor() +{ + QFETCH(bool, firstInterceptIsInPage); + QFETCH(bool, keepInterceptionPoint); + + HttpServer server; + server.setResourceDirs({ ":/resources" }); + QVERIFY(server.start()); + + QWebEngineProfile profile; + profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + QWebEnginePage page(&profile); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + bool fetchFinished = false; + + auto setInterceptor = [&] (QWebEngineUrlRequestInterceptor *interceptor, bool interceptInPage) { + interceptInPage ? page.setUrlRequestInterceptor(interceptor) : profile.setUrlRequestInterceptor(interceptor); + }; + + std::vector<TestRequestInterceptor> interceptors(3); + std::vector<int> requestsOnReplace; + setInterceptor(&interceptors.front(), firstInterceptIsInPage); + + auto sc = connect(&page, &QWebEnginePage::loadFinished, [&] () { + auto currentInterceptorIndex = requestsOnReplace.size(); + requestsOnReplace.push_back(interceptors[currentInterceptorIndex].requestInfos.size()); + + bool isFirstReinstall = currentInterceptorIndex == 0; + bool interceptInPage = keepInterceptionPoint ? firstInterceptIsInPage : (isFirstReinstall ^ firstInterceptIsInPage); + setInterceptor(&interceptors[++currentInterceptorIndex], interceptInPage); + if (!keepInterceptionPoint) + setInterceptor(nullptr, !interceptInPage); + + if (isFirstReinstall) { + page.triggerAction(QWebEnginePage::Reload); + } else { + page.runJavaScript("fetch('http://nonexistent.invalid').catch(() => {})", [&, interceptInPage] (const QVariant &) { + requestsOnReplace.push_back(interceptors.back().requestInfos.size()); + setInterceptor(nullptr, interceptInPage); + fetchFinished = true; + }); + } + }); + + page.setUrl(server.url("/favicon.html")); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 2, 20000); + QTRY_VERIFY(fetchFinished); + + QString s; QDebug d(&s); + for (auto i = 0u; i < interceptors.size(); ++i) { + auto &&interceptor = interceptors[i]; + auto &&requests = interceptor.requestInfos; + d << "\nInterceptor [" << i << "] with" << requestsOnReplace[i] << "requests on replace and" << requests.size() << "in the end:"; + for (int j = 0; j < requests.size(); ++j) { + auto &&r = requests[j]; + d << "\n\t" << j << "| url:" << r.requestUrl << "firstPartyUrl:" << r.firstPartyUrl; + } + QVERIFY2(!requests.isEmpty(), qPrintable(s)); + QVERIFY2(requests.size() == requestsOnReplace[i], qPrintable(s)); + } +} + +void tst_QWebEngineUrlRequestInterceptor::replaceOnIntercept() +{ + HttpServer server; + server.setResourceDirs({ ":/resources" }); + QVERIFY(server.start()); + + QWebEngineProfile profile; + profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + QWebEnginePage page(&profile); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + struct Interceptor : QWebEngineUrlRequestInterceptor { + Interceptor(const std::function<void ()> &a) : action(a) { } + void interceptRequest(QWebEngineUrlRequestInfo &) override { action(); } + std::function<void ()> action; + int interceptRequestReceived = 0; + }; + + TestRequestInterceptor profileInterceptor, pageInterceptor1, pageInterceptor2; + page.setUrlRequestInterceptor(&pageInterceptor1); + profile.setUrlRequestInterceptor(&profileInterceptor); + profileInterceptor.onIntercept = [&] (QWebEngineUrlRequestInfo &) { + page.setUrlRequestInterceptor(&pageInterceptor2); + return true; + }; + + page.setUrl(server.url("/favicon.html")); + QTRY_COMPARE_WITH_TIMEOUT(spy.size(), 1, 20000); + QTRY_COMPARE(profileInterceptor.requestInfos.size(), 2); + + // if interceptor for page was replaced on intercept call in profile then, since request first + // comes to profile, forward to page's interceptor should land to second one + QCOMPARE(pageInterceptor1.requestInfos.size(), 0); + QCOMPARE(profileInterceptor.requestInfos.size(), pageInterceptor2.requestInfos.size()); + + page.setUrlRequestInterceptor(&pageInterceptor1); + bool fetchFinished = false; + page.runJavaScript("fetch('http://nonexistent.invalid').catch(() => {})", [&] (const QVariant &) { + page.setUrlRequestInterceptor(&pageInterceptor2); + fetchFinished = true; + }); + + QTRY_VERIFY(fetchFinished); + QCOMPARE(profileInterceptor.requestInfos.size(), 3); + QCOMPARE(pageInterceptor1.requestInfos.size(), 0); + QCOMPARE(profileInterceptor.requestInfos.size(), pageInterceptor2.requestInfos.size()); +} + +void tst_QWebEngineUrlRequestInterceptor::multipleRedirects() +{ + HttpServer server; + server.setResourceDirs({ ":/resources" }); + QVERIFY(server.start()); + + TestMultipleRedirectsInterceptor multiInterceptor; + multiInterceptor.redirectPairs.insert(QUrl(server.url("/content.html")), QUrl(server.url("/content2.html"))); + multiInterceptor.redirectPairs.insert(QUrl(server.url("/content2.html")), QUrl(server.url("/content3.html"))); + + QWebEngineProfile profile; + profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + profile.setUrlRequestInterceptor(&multiInterceptor); + QWebEnginePage page(&profile); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + page.setUrl(server.url("/content.html")); + + QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 20000); + QTRY_COMPARE(multiInterceptor.redirectCount, 2); + QTRY_COMPARE(multiInterceptor.requestInfos.size(), 2); +} + +class TestPostRequestInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + TestPostRequestInterceptor(QString expected, bool isAppendFile, QObject *parent = nullptr) + : QWebEngineUrlRequestInterceptor(parent) + , m_expected(expected) + , m_isAppendFile(isAppendFile) + {}; + + void interceptRequest(QWebEngineUrlRequestInfo &info) override + { + info.block(true); + isCalled = true; + + QIODevice *requestBodyDevice = info.requestBody(); + + if (m_isAppendFile) { + info.d_ptr->appendFileToResourceRequestBodyForTest(":/resources/postBodyFile.txt"); + } + + requestBodyDevice->open(QIODevice::ReadOnly); + + const QString webKitBoundary = requestBodyDevice->read(40); + QVERIFY(webKitBoundary.contains("------WebKitFormBoundary")); + + const QString fullBodyWithoutBoundaries = (webKitBoundary + requestBodyDevice->readAll()) + .replace(webKitBoundary, "") + .replace("\r", "") + .replace("\n", "") + .replace(" ", ""); + + QCOMPARE(fullBodyWithoutBoundaries, m_expected); + + requestBodyDevice->close(); + } + + bool isCalled = false; + QString m_expected; + bool m_isAppendFile; +}; + +void tst_QWebEngineUrlRequestInterceptor::postWithBody_data() +{ + QTest::addColumn<QString>("input"); + QTest::addColumn<QString>("output"); + QTest::addColumn<bool>("isAppendFile"); + QTest::addRow("FormData append (DataElementByte)") + << "fd.append('userId', 1);" + "fd.append('title',' Test123');" + "fd.append('completed', false);" + << "Content-Disposition:form-data;name=\"userId" + "\"1Content-Disposition:form-data" + ";name=\"title\"Test123Content-Di" + "sposition:form-data;name=\"completed\"f" + "alse--" + << false; + QTest::addRow("FormData blob (DataElementPipe)") + << "const blob1 = new Blob(['blob1thisisablob']," + "{type: 'text/plain'});" + "fd.append('blob1', blob1);" + << "Content-Disposition:form-data;name=\"blob1" + "\";filename=\"blob\"Content-Type:text/plai" + "nblob1thisisablob--" + << false; + QTest::addRow("Append file (DataElementFile)") << "" + << "--{\"test\":\"1234\"}\"1234\"}" << true; + QTest::addRow("All combined") << "fd.append('userId', 1);" + "fd.append('title', 'Test123');" + "fd.append('completed', false);" + "const blob1 = new Blob(['blob1thisisablob']," + "{type: 'text/plain'});" + "const blob2 = new Blob(['blob2thisisanotherblob']," + "{type: 'text/plain'});" + "fd.append('blob1', blob1);" + "fd.append('userId', 2);" + "fd.append('title', 'Test456');" + "fd.append('completed', true);" + "fd.append('blob2', blob2);" + << "Content-Disposition:form-data;name=\"userId\"" + "1Content-Disposition:form-data;na" + "me=\"title\"Test123Content-Disposit" + "ion:form-data;name=\"completed\"false" + "Content-Disposition:form-data;name=\"blob1\";" + "filename=\"blob\"Content-Type:text/plain" + "blob1thisisablobContent-Disposition:form-" + "data;name=\"userId\"2Content-Dispos" + "ition:form-data;name=\"title\"Test456" + "Content-Disposition:form-data;name=\"complete" + "d\"trueContent-Disposition:form-da" + "ta;name=\"blob2\";filename=\"blob\"Content-Ty" + "pe:text/plainblob2thisisanotherblob--" + "{\"test\":\"1234\"}\"1234\"}" + << true; +} + +void tst_QWebEngineUrlRequestInterceptor::postWithBody() +{ + QFETCH(QString, input); + QFETCH(QString, output); + QFETCH(bool, isAppendFile); + + QString script; + script.append("const fd = new FormData();"); + script.append(input); + script.append("fetch('http://127.0.0.1', {method: 'POST',body: fd});"); + + QWebEngineProfile profile; + profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + TestPostRequestInterceptor interceptor(output, isAppendFile); + profile.setUrlRequestInterceptor(&interceptor); + QWebEnginePage page(&profile); + bool ok = false; + + page.runJavaScript(script, [&ok](const QVariant) { ok = true; }); + + QTRY_VERIFY(ok); + QVERIFY(interceptor.isCalled); +} + +class PageOrProfileInterceptor : public QWebEngineUrlRequestInterceptor +{ +public: + PageOrProfileInterceptor(const QString &profileAction) + : profileAction(profileAction) + { + } + + void interceptRequest(QWebEngineUrlRequestInfo &info) override + { + if (profileAction == "block") + info.block(true); + else if (profileAction == "redirect") + info.redirect(QUrl("data:text/html,<p>redirected")); + else if (profileAction == "add header") + info.setHttpHeader("Custom-Header", "Value"); + else + QVERIFY(info.httpHeaders().contains("Custom-Header")); + ran = true; + } + + QString profileAction; + bool ran = false; +}; + +void tst_QWebEngineUrlRequestInterceptor::profilePreventsPageInterception_data() +{ + QTest::addColumn<QString>("profileAction"); + QTest::addColumn<bool>("interceptInProfile"); + QTest::addColumn<bool>("interceptInPage"); + QTest::newRow("block") << "block" << true << false; + QTest::newRow("redirect") << "redirect" << true << false; + QTest::newRow("add header") << "add header" << true << true; +} + +void tst_QWebEngineUrlRequestInterceptor::profilePreventsPageInterception() +{ + QFETCH(QString, profileAction); + QFETCH(bool, interceptInProfile); + QFETCH(bool, interceptInPage); + + QWebEngineProfile profile; + PageOrProfileInterceptor profileInterceptor(profileAction); + profile.setUrlRequestInterceptor(&profileInterceptor); + profile.settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false); + + QWebEnginePage page(&profile); + PageOrProfileInterceptor pageInterceptor(""); + page.setUrlRequestInterceptor(&pageInterceptor); + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + + page.load(QUrl("qrc:///resources/index.html")); + QTRY_COMPARE(loadSpy.size(), 1); + QCOMPARE(profileInterceptor.ran, interceptInProfile); + QCOMPARE(pageInterceptor.ran, interceptInPage); } QTEST_MAIN(tst_QWebEngineUrlRequestInterceptor) |