From 95f70c9a0776ea0c51ec3f6c73ffb56cf9e956e6 Mon Sep 17 00:00:00 2001 From: Yigit Akcay Date: Fri, 27 Jan 2023 13:07:58 +0100 Subject: QWebEngineLoadingInfo: Add response headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change adds a member variable that contains the response headers to QWebEngineLoadingInfo. It is filled when a QWebEngineLoadingInfo instance is constructed inside WebContentsDelegateQt::emitLoadFinished(bool). The response headers are extracted from the navigation handle when WebContentsDelegateQt::DidFinishNavigation(content::NavigationHandle *) is called. The response headers are non-empty when QWebEngineLoadingInfo::status() is equal to QWebEngineLoadingInfo::LoadSucceededStatus or QWebEngineLoadingInfo::LoadFailedStatus. Fixes: QTBUG-106862 Change-Id: I4d196e3cc71725ddad9a5832af72d1b4e50924c8 Reviewed-by: Qt CI Bot Reviewed-by: Michael BrĂ¼ning --- src/core/api/qwebengineloadinginfo.cpp | 23 +++++- src/core/api/qwebengineloadinginfo.h | 6 +- src/core/web_contents_delegate_qt.cpp | 18 ++++- src/core/web_contents_delegate_qt.h | 1 + tests/auto/core/CMakeLists.txt | 1 + .../auto/core/qwebengineloadinginfo/CMakeLists.txt | 10 +++ .../tst_qwebengineloadinginfo.cpp | 91 ++++++++++++++++++++++ tests/auto/quick/publicapi/tst_publicapi.cpp | 4 +- 8 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 tests/auto/core/qwebengineloadinginfo/CMakeLists.txt create mode 100644 tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp diff --git a/src/core/api/qwebengineloadinginfo.cpp b/src/core/api/qwebengineloadinginfo.cpp index 14ec26be4..ed9618f49 100644 --- a/src/core/api/qwebengineloadinginfo.cpp +++ b/src/core/api/qwebengineloadinginfo.cpp @@ -22,13 +22,15 @@ Q_STATIC_ASSERT(static_cast(WebEngineError::HttpStatusCodeDomain) == static class QWebEngineLoadingInfo::QWebEngineLoadingInfoPrivate : public QSharedData { public: QWebEngineLoadingInfoPrivate(const QUrl& url, LoadStatus status, bool isErrorPage, - const QString& errorString, int errorCode, ErrorDomain errorDomain) + const QString& errorString, int errorCode, ErrorDomain errorDomain, + const QHash& responseHeaders) : url(url) , status(status) , isErrorPage(isErrorPage) , errorString(errorString) , errorCode(errorCode) , errorDomain(errorDomain) + , responseHeaders(responseHeaders) { } @@ -38,6 +40,7 @@ public: QString errorString; int errorCode; ErrorDomain errorDomain; + QHash responseHeaders; }; /*! @@ -52,8 +55,10 @@ public: \sa QWebEnginePage::loadStarted, QWebEnginePage::loadFinished, WebEngineView::loadingChanged */ QWebEngineLoadingInfo::QWebEngineLoadingInfo(const QUrl& url, LoadStatus status, bool isErrorPage, - const QString& errorString, int errorCode, ErrorDomain errorDomain) - : d_ptr(new QWebEngineLoadingInfoPrivate(url, status, isErrorPage, errorString, errorCode, errorDomain)) + const QString& errorString, int errorCode, ErrorDomain errorDomain, + const QHash& responseHeaders) + : d_ptr(new QWebEngineLoadingInfoPrivate(url, status, isErrorPage, errorString, errorCode, errorDomain, + responseHeaders)) { } @@ -157,6 +162,18 @@ int QWebEngineLoadingInfo::errorCode() const return d->errorCode; } +/*! + \property QWebEngineLoadingInfo::responseHeaders + \brief Holds the response headers when \c QWebEngineLoadingInfo::status() + is equal to \c QWebEngineLoadingInfo::LoadSucceededStatus or + \c QWebEngineLoadingInfo::LoadFailedStatus. +*/ +QHash QWebEngineLoadingInfo::responseHeaders() const +{ + Q_D(const QWebEngineLoadingInfo); + return d->responseHeaders; +} + QT_END_NAMESPACE #include "moc_qwebengineloadinginfo.cpp" diff --git a/src/core/api/qwebengineloadinginfo.h b/src/core/api/qwebengineloadinginfo.h index a17588a57..11cce7365 100644 --- a/src/core/api/qwebengineloadinginfo.h +++ b/src/core/api/qwebengineloadinginfo.h @@ -9,6 +9,7 @@ #include #include #include +#include namespace QtWebEngineCore { class WebContentsAdapter; @@ -26,6 +27,7 @@ class Q_WEBENGINECORE_EXPORT QWebEngineLoadingInfo Q_PROPERTY(QString errorString READ errorString CONSTANT FINAL) Q_PROPERTY(ErrorDomain errorDomain READ errorDomain CONSTANT FINAL) Q_PROPERTY(int errorCode READ errorCode CONSTANT FINAL) + Q_PROPERTY(QHash responseHeaders READ responseHeaders CONSTANT FINAL) public: enum LoadStatus { @@ -60,11 +62,13 @@ public: QString errorString() const; ErrorDomain errorDomain() const; int errorCode() const; + QHash responseHeaders() const; private: QWebEngineLoadingInfo(const QUrl &url, LoadStatus status, bool isErrorPage = false, const QString &errorString = QString(), int errorCode = 0, - ErrorDomain errorDomain = NoErrorDomain); + ErrorDomain errorDomain = NoErrorDomain, + const QHash &responseHeaders = {}); class QWebEngineLoadingInfoPrivate; Q_DECLARE_PRIVATE(QWebEngineLoadingInfo) QExplicitlySharedDataPointer d_ptr; diff --git a/src/core/web_contents_delegate_qt.cpp b/src/core/web_contents_delegate_qt.cpp index b58b56500..7470e5cc2 100644 --- a/src/core/web_contents_delegate_qt.cpp +++ b/src/core/web_contents_delegate_qt.cpp @@ -383,7 +383,8 @@ void WebContentsDelegateQt::emitLoadFinished(bool isErrorPage) ? QWebEngineLoadingInfo::LoadStoppedStatus : QWebEngineLoadingInfo::LoadFailedStatus); QWebEngineLoadingInfo info(m_loadingInfo.url, loadStatus, m_loadingInfo.isErrorPage, m_loadingInfo.errorDescription, m_loadingInfo.errorCode, - QWebEngineLoadingInfo::ErrorDomain(m_loadingInfo.errorDomain)); + QWebEngineLoadingInfo::ErrorDomain(m_loadingInfo.errorDomain), + m_loadingInfo.responseHeaders); m_viewClient->loadFinished(std::move(info)); m_viewClient->updateNavigationActions(); } @@ -411,6 +412,21 @@ void WebContentsDelegateQt::DidFinishNavigation(content::NavigationHandle *navig emitLoadCommitted(); } + const net::HttpResponseHeaders * const responseHeaders = navigation_handle->GetResponseHeaders(); + QHash responseHeadersMap; + if (responseHeaders != nullptr) { + m_loadingInfo.responseHeaders.clear(); + std::size_t iter = 0; + std::string headerName; + std::string headerValue; + while (responseHeaders->EnumerateHeaderLines(&iter, &headerName, &headerValue)) { + m_loadingInfo.responseHeaders.insert( + QByteArray::fromStdString(headerName), + QByteArray::fromStdString(headerValue) + ); + } + } + // Success is reported by DidFinishLoad, but DidFailLoad is now dead code and needs to be handled below if (navigation_handle->GetNetErrorCode() == net::OK) return; diff --git a/src/core/web_contents_delegate_qt.h b/src/core/web_contents_delegate_qt.h index eda4e1e57..37c2c7341 100644 --- a/src/core/web_contents_delegate_qt.h +++ b/src/core/web_contents_delegate_qt.h @@ -200,6 +200,7 @@ private: int errorCode = 0, errorDomain = 0; QString errorDescription; bool triggersErrorPage = false; + QHash responseHeaders; void clear() { *this = LoadingInfo(); } } m_loadingInfo; diff --git a/tests/auto/core/CMakeLists.txt b/tests/auto/core/CMakeLists.txt index 3f10e8303..6ba6ffd92 100644 --- a/tests/auto/core/CMakeLists.txt +++ b/tests/auto/core/CMakeLists.txt @@ -2,6 +2,7 @@ # SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qwebenginecookiestore) +add_subdirectory(qwebengineloadinginfo) add_subdirectory(qwebenginesettings) add_subdirectory(qwebengineurlrequestinterceptor) add_subdirectory(qwebengineurlrequestjob) diff --git a/tests/auto/core/qwebengineloadinginfo/CMakeLists.txt b/tests/auto/core/qwebengineloadinginfo/CMakeLists.txt new file mode 100644 index 000000000..09d9c30f5 --- /dev/null +++ b/tests/auto/core/qwebengineloadinginfo/CMakeLists.txt @@ -0,0 +1,10 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qwebengineloadinginfo + SOURCES + tst_qwebengineloadinginfo.cpp + LIBRARIES + Qt::WebEngineCore + Test::HttpServer +) diff --git a/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp b/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp new file mode 100644 index 000000000..064abe816 --- /dev/null +++ b/tests/auto/core/qwebengineloadinginfo/tst_qwebengineloadinginfo.cpp @@ -0,0 +1,91 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include +#include +#include +#include +#include + +#include +#include + +typedef QHash Map; + +class tst_QWebEngineLoadingInfo : public QObject +{ + Q_OBJECT + +public: + tst_QWebEngineLoadingInfo() { } + +public slots: + void loadingInfoChanged(QWebEngineLoadingInfo loadingInfo) + { + const auto responseHeaders = loadingInfo.responseHeaders(); + QFETCH(Map, expected); + + if (loadingInfo.status() == QWebEngineLoadingInfo::LoadSucceededStatus + || loadingInfo.status() == QWebEngineLoadingInfo::LoadFailedStatus) { + if (!expected.empty()) + QCOMPARE(responseHeaders, expected); + } else { + QVERIFY(responseHeaders.size() == 0); + } + } + +private Q_SLOTS: + void responseHeaders_data() + { + QTest::addColumn("responseCode"); + QTest::addColumn("input"); + QTest::addColumn("expected"); + + const Map empty; + const Map input { + std::make_pair("header1", "value1"), + std::make_pair("header2", "value2") + }; + const Map expected { + std::make_pair("header1", "value1"), + std::make_pair("header2", "value2"), + std::make_pair("Connection", "close") + }; + + + QTest::newRow("with headers HTTP 200") << 200 << input << expected; + QTest::newRow("with headers HTTP 500") << 500 << input << expected; + QTest::newRow("without headers HTTP 200") << 200 << empty << empty; + QTest::newRow("without headers HTTP 500") << 500 << empty << empty; + } + + void responseHeaders() + { + HttpServer httpServer; + + QFETCH(Map, input); + QFETCH(int, responseCode); + QObject::connect(&httpServer, &HttpServer::newRequest, this, [&](HttpReqRep *rr) { + for (auto it = input.cbegin(); it != input.cend(); ++it) + rr->setResponseHeader(it.key(), it.value()); + + rr->sendResponse(responseCode); + }); + QVERIFY(httpServer.start()); + + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + QObject::connect(&page, &QWebEnginePage::loadingChanged, this, &tst_QWebEngineLoadingInfo::loadingInfoChanged); + + + QWebEngineHttpRequest request(httpServer.url("/somepage.html")); + page.load(request); + + QVERIFY(spy.wait()); + QVERIFY(httpServer.stop()); + } +}; + +QTEST_MAIN(tst_QWebEngineLoadingInfo) +#include "tst_qwebengineloadinginfo.moc" diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index 0cd031940..8b366f413 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -84,7 +84,8 @@ static const QStringList hardcodedTypes = QStringList() << "QWebEngineCookieStore*" << "Qt::LayoutDirection" << "QQuickWebEngineScriptCollection*" - << "QQmlComponent*"; + << "QQmlComponent*" + << "QHash"; static const QStringList expectedAPI = QStringList() << "QQuickWebEngineAction.text --> QString" @@ -293,6 +294,7 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineJavaScriptDialogRequest.title --> QString" << "QQuickWebEngineJavaScriptDialogRequest.type --> QQuickWebEngineJavaScriptDialogRequest::DialogType" << "QWebEngineLoadingInfo.errorCode --> int" + << "QWebEngineLoadingInfo.responseHeaders --> QHash" << "QWebEngineLoadingInfo.errorDomain --> QWebEngineLoadingInfo::ErrorDomain" << "QWebEngineLoadingInfo.errorString --> QString" << "QWebEngineLoadingInfo.status --> QWebEngineLoadingInfo::LoadStatus" -- cgit v1.2.3