summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorViktor Engelmann <viktor.engelmann@qt.io>2016-08-09 11:51:19 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2017-01-11 10:33:07 +0000
commitc4e1aa2c4fff93c71eed3f8115170f314d969234 (patch)
tree43a1937891dc034de05e35de0066a00af8449bc3
parent4804e331304c5bcb79ebc785485793e5f1d8759e (diff)
Add methods to issue various types of HTTP requests
Added class QWebEngineHttpRequest, which describes a GET or POST HTTP Request. Also added overloads of method "load" to QWebEngineView, QWebEnginePage and WebContentsAdapter, which issue such a request. These can be used for example to simulate form-submissions. Task-number: QTBUG-53314 Task-number: QTBUG-53372 Change-Id: I85ac8cdd3d1557905b35e3172b922aba356d1c41 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
-rw-r--r--src/core/api/core_api.pro2
-rw-r--r--src/core/api/qwebenginehttprequest.cpp419
-rw-r--r--src/core/api/qwebenginehttprequest.h105
-rw-r--r--src/core/web_contents_adapter.cpp51
-rw-r--r--src/core/web_contents_adapter.h4
-rw-r--r--src/webenginewidgets/api/qwebenginepage.cpp12
-rw-r--r--src/webenginewidgets/api/qwebenginepage.h3
-rw-r--r--src/webenginewidgets/api/qwebengineview.cpp11
-rw-r--r--src/webenginewidgets/api/qwebengineview.h4
-rw-r--r--src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc2
-rw-r--r--tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp135
11 files changed, 739 insertions, 9 deletions
diff --git a/src/core/api/core_api.pro b/src/core/api/core_api.pro
index 37f8885bb..cda01db40 100644
--- a/src/core/api/core_api.pro
+++ b/src/core/api/core_api.pro
@@ -35,6 +35,7 @@ HEADERS = \
qtwebenginecoreglobal_p.h \
qwebenginecookiestore.h \
qwebenginecookiestore_p.h \
+ qwebenginehttprequest.h \
qwebengineurlrequestinterceptor.h \
qwebengineurlrequestinfo.h \
qwebengineurlrequestinfo_p.h \
@@ -44,6 +45,7 @@ HEADERS = \
SOURCES = \
qtwebenginecoreglobal.cpp \
qwebenginecookiestore.cpp \
+ qwebenginehttprequest.cpp \
qwebengineurlrequestinfo.cpp \
qwebengineurlrequestjob.cpp \
qwebengineurlschemehandler.cpp
diff --git a/src/core/api/qwebenginehttprequest.cpp b/src/core/api/qwebenginehttprequest.cpp
new file mode 100644
index 000000000..b64af4466
--- /dev/null
+++ b/src/core/api/qwebenginehttprequest.cpp
@@ -0,0 +1,419 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qplatformdefs.h"
+#include <QtCore/qshareddata.h>
+#include <QtWebEngineCore/qwebenginehttprequest.h>
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QWebEngineHttpRequest
+ \since 5.9
+ \ingroup webengine
+ \inmodule QtWebEngineCore
+
+ \brief The QWebEngineHttpRequest class holds a request to be sent with WebEngine.
+
+ QWebEngineHttpRequest represents an HTTP request in the WebEngine networking stack.
+ It holds the information necessary to send a request over the network. It contains
+ a URL and some ancillary information that can be used to modify the request.
+ Both QWebEnginePage::load() and QWebEngineView::load() accept a QWebEngineHttpRequest
+ as a parameter.
+*/
+
+/*!
+ \enum QWebEngineHttpRequest::Method
+ \brief This enum type describes the method used to send the HTTP request:
+
+ \value Get The GET method.
+ \value Post The POST method.
+*/
+
+class QWebEngineHttpRequestPrivate : public QSharedData
+{
+public:
+ QUrl url;
+ QWebEngineHttpRequest::Method method;
+ typedef QPair<QByteArray, QByteArray> HeaderPair;
+ typedef QVector<HeaderPair> Headers;
+ Headers headers;
+ QByteArray postData;
+
+ inline QWebEngineHttpRequestPrivate()
+ {
+ }
+
+ ~QWebEngineHttpRequestPrivate()
+ {
+ }
+
+ QWebEngineHttpRequestPrivate(const QWebEngineHttpRequestPrivate &other)
+ : QSharedData(other)
+ {
+ method = other.method;
+ url = other.url;
+ headers = other.headers;
+ }
+
+ inline bool operator==(const QWebEngineHttpRequestPrivate &other) const
+ {
+ return method == other.method
+ && url == other.url
+ && headers == other.headers;
+ }
+
+ Headers::ConstIterator findHeader(const QByteArray &key) const;
+ Headers allHeaders() const;
+ QVector<QByteArray> headersKeys() const;
+ void setHeader(const QByteArray &key, const QByteArray &value);
+ void unsetHeader(const QByteArray &key);
+ void setAllHeaders(const Headers &list);
+
+private:
+ void setHeaderInternal(const QByteArray &key, const QByteArray &value);
+};
+
+/*!
+ Constructs a QWebEngineHttpRequest object with \a url as the URL to be
+ requested and \a method as the method to be used.
+
+ \sa url(), setUrl()
+*/
+QWebEngineHttpRequest::QWebEngineHttpRequest(const QUrl &url,
+ const QWebEngineHttpRequest::Method &method)
+ : d(new QWebEngineHttpRequestPrivate)
+{
+ d->method = method;
+ d->url = url;
+}
+
+/*!
+ Creates a copy of \a other.
+*/
+QWebEngineHttpRequest::QWebEngineHttpRequest(const QWebEngineHttpRequest &other)
+ : d(other.d)
+{
+}
+
+/*!
+ Disposes of the QWebEngineHttpRequest object.
+*/
+QWebEngineHttpRequest::~QWebEngineHttpRequest()
+{
+ // QSharedDataPointer auto deletes
+ d = 0;
+}
+
+/*!
+ Returns \c true if this object is the same as \a other (that is, if they
+ have the same method, URL, and headers).
+
+ \sa operator!=()
+*/
+bool QWebEngineHttpRequest::operator==(const QWebEngineHttpRequest &other) const
+{
+ return d == other.d || *d == *other.d;
+}
+
+/*!
+ \fn bool QWebEngineHttpRequest::operator!=(const QWebEngineHttpRequest &other) const
+
+ Returns \c false if this object is not the same as \a other.
+
+ \sa operator==()
+*/
+
+/*!
+ Creates a copy of \a other.
+*/
+QWebEngineHttpRequest &QWebEngineHttpRequest::operator=(const QWebEngineHttpRequest &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \fn void QWebEngineHttpRequest::swap(QWebEngineHttpRequest &other)
+
+ Swaps this WebEngine request with \a other. This function is very
+ fast and never fails.
+*/
+
+/*!
+ Constructs a QWebEngineHttpRequest to \a url that uses the POST method.
+
+ \note \a postData may contain arbitrary strings. They are translated
+ to appropriate raw data.
+
+ \sa postData, setPostData()
+*/
+QWebEngineHttpRequest QWebEngineHttpRequest::postRequest(const QUrl &url,
+ const QMap<QString, QString> &postData)
+{
+ QWebEngineHttpRequest result(url);
+ result.setMethod(QWebEngineHttpRequest::Post);
+
+ QString buffer;
+ for (QMap<QString, QString>::const_iterator it = postData.begin(); it != postData.end(); it++) {
+ QByteArray key = QUrl::toPercentEncoding(it.key());
+ QByteArray value = QUrl::toPercentEncoding(it.value());
+
+ if (buffer.length() > 0)
+ buffer += QLatin1Char('&');
+ buffer += key + QLatin1Char('=') + value;
+ }
+ result.setPostData(buffer.toLatin1());
+
+ result.setHeader(QByteArrayLiteral("Content-Type"),
+ QByteArrayLiteral("application/x-www-form-urlencoded"));
+ return result;
+}
+
+
+/*!
+ Returns the method this WebEngine request is using.
+
+ \sa setMethod()
+*/
+QWebEngineHttpRequest::Method QWebEngineHttpRequest::method() const
+{
+ return d->method;
+}
+
+/*!
+ Sets the method this WebEngine request is using to be \a method.
+
+ \sa method()
+*/
+void QWebEngineHttpRequest::setMethod(QWebEngineHttpRequest::Method method)
+{
+ d->method = method;
+}
+
+/*!
+ Returns the URL this WebEngine request is referring to.
+
+ \sa setUrl()
+*/
+QUrl QWebEngineHttpRequest::url() const
+{
+ return d->url;
+}
+
+/*!
+ Sets the URL this WebEngine request is referring to be \a url.
+
+ \sa url()
+*/
+void QWebEngineHttpRequest::setUrl(const QUrl &url)
+{
+ d->url = url;
+}
+
+/*!
+ Returns the (raw) POST data this WebEngine request contains.
+
+ \sa setPostData()
+*/
+QByteArray QWebEngineHttpRequest::postData() const
+{
+ return d->postData;
+}
+
+/*!
+ Sets the (raw) POST data this WebEngine request contains to be \a postData.
+
+ \sa postData()
+*/
+void QWebEngineHttpRequest::setPostData(const QByteArray &postData)
+{
+ d->postData = postData;
+}
+
+/*!
+ Returns \c true if the header \a headerName is present in this
+ WebEngine request.
+
+ \sa setHeader(), header(), unsetHeader(), headers()
+*/
+bool QWebEngineHttpRequest::hasHeader(const QByteArray &headerName) const
+{
+ return d->findHeader(headerName) != d->headers.constEnd();
+}
+
+/*!
+ Returns the header specified by \a headerName. If no such header is
+ present, an empty QByteArray is returned, which may be
+ indistinguishable from a header that is present but has no content
+ (use hasHeader() to find out if the header exists or not).
+
+ Headers can be set with setHeader().
+
+ \sa setHeader(), hasHeader(), unsetHeader(), headers()
+*/
+QByteArray QWebEngineHttpRequest::header(const QByteArray &headerName) const
+{
+ QWebEngineHttpRequestPrivate::Headers::ConstIterator it =
+ d->findHeader(headerName);
+ if (it != d->headers.constEnd())
+ return it->second;
+ return QByteArray();
+}
+
+/*!
+ Returns a list of all headers that are set in this WebEngine
+ request. The list is in the order that the headers were set.
+
+ \sa setHeader(), header(), hasHeader(), unsetHeader()
+*/
+QVector<QByteArray> QWebEngineHttpRequest::headers() const
+{
+ return d->headersKeys();
+}
+
+/*!
+ Sets the header \a headerName to be of value \a headerValue.
+
+ \note Setting the same header twice overrides the previous
+ setting. To accomplish the behavior of multiple HTTP headers of
+ the same name, you should concatenate the two values, separating
+ them with a comma (",") and set one single header.
+
+ \sa header(), hasHeader(), unsetHeader(), headers()
+*/
+void QWebEngineHttpRequest::setHeader(const QByteArray &headerName, const QByteArray &headerValue)
+{
+ d->setHeader(headerName, headerValue);
+}
+
+/*!
+ Removes the header specified by \a key, if present.
+
+ \sa setHeader(), header(), hasHeader(), headers()
+*/
+void QWebEngineHttpRequest::unsetHeader(const QByteArray &key)
+{
+ d->setHeader(key, QByteArray());
+}
+
+QWebEngineHttpRequestPrivate::Headers::ConstIterator
+QWebEngineHttpRequestPrivate::findHeader(const QByteArray &key) const
+{
+ Headers::ConstIterator it = headers.constBegin();
+ Headers::ConstIterator end = headers.constEnd();
+ for ( ; it != end; ++it)
+ if (qstricmp(it->first.constData(), key.constData()) == 0)
+ return it;
+
+ return end; // not found
+}
+
+QWebEngineHttpRequestPrivate::Headers QWebEngineHttpRequestPrivate::allHeaders() const
+{
+ return headers;
+}
+
+QVector<QByteArray> QWebEngineHttpRequestPrivate::headersKeys() const
+{
+ QVector<QByteArray> result;
+ result.reserve(headers.size());
+ Headers::ConstIterator it = headers.constBegin(),
+ end = headers.constEnd();
+ for ( ; it != end; ++it)
+ result << it->first;
+
+ return result;
+}
+
+/*!
+ \internal
+ Sets the header specified by \a key to \a value.
+*/
+void QWebEngineHttpRequestPrivate::setHeader(const QByteArray &key, const QByteArray &value)
+{
+ if (key.isEmpty())
+ // refuse to accept an empty header
+ return;
+
+ setHeaderInternal(key, value);
+}
+
+/*!
+ \internal
+ Removes the header specified by \a key, if present.
+*/
+void QWebEngineHttpRequestPrivate::unsetHeader(const QByteArray &key)
+{
+ auto firstEqualsKey = [&key](const HeaderPair &header) {
+ return qstricmp(header.first.constData(), key.constData()) == 0;
+ };
+ headers.erase(std::remove_if(headers.begin(), headers.end(), firstEqualsKey),
+ headers.end());
+}
+
+/*!
+ \internal
+ Sets the internal headers list to match \a list.
+*/
+void QWebEngineHttpRequestPrivate::setAllHeaders(const Headers &list)
+{
+ headers = list;
+}
+
+/*!
+ \internal
+ Sets the header specified by \a key to \a value.
+ \note key must not be empty. When unsure, use \a setHeader() instead.
+*/
+void QWebEngineHttpRequestPrivate::setHeaderInternal(const QByteArray &key, const QByteArray &value)
+{
+ unsetHeader(key);
+
+ if (value.isNull())
+ return; // only wanted to erase key
+
+ HeaderPair pair;
+ pair.first = key;
+ pair.second = value;
+ headers.append(pair);
+}
+
+QT_END_NAMESPACE
diff --git a/src/core/api/qwebenginehttprequest.h b/src/core/api/qwebenginehttprequest.h
new file mode 100644
index 000000000..5b5948ba1
--- /dev/null
+++ b/src/core/api/qwebenginehttprequest.h
@@ -0,0 +1,105 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWEBENGINEHTTPREQUEST_H
+#define QWEBENGINEHTTPREQUEST_H
+
+#include <QtWebEngineCore/qtwebenginecoreglobal.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qvector.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+
+class QWebEngineHttpRequestPrivate;
+
+class QWEBENGINE_EXPORT QWebEngineHttpRequest
+{
+public:
+ enum Method {
+ Get,
+ Post
+ };
+
+ explicit QWebEngineHttpRequest(const QUrl &url = QUrl(),
+ const QWebEngineHttpRequest::Method &method = QWebEngineHttpRequest::Get);
+ QWebEngineHttpRequest(const QWebEngineHttpRequest &other);
+ ~QWebEngineHttpRequest();
+#ifdef Q_COMPILER_RVALUE_REFS
+ QWebEngineHttpRequest &operator=(QWebEngineHttpRequest &&other) Q_DECL_NOTHROW { swap(other);
+ return *this; }
+#endif
+ QWebEngineHttpRequest &operator=(const QWebEngineHttpRequest &other);
+
+ static QWebEngineHttpRequest postRequest(const QUrl &url,
+ const QMap<QString, QString> &postData);
+ void swap(QWebEngineHttpRequest &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+
+ bool operator==(const QWebEngineHttpRequest &other) const;
+ inline bool operator!=(const QWebEngineHttpRequest &other) const
+ { return !operator==(other); }
+
+ Method method() const;
+ void setMethod(QWebEngineHttpRequest::Method method);
+
+ QUrl url() const;
+ void setUrl(const QUrl &url);
+
+ QByteArray postData() const;
+ void setPostData(const QByteArray &postData);
+
+ bool hasHeader(const QByteArray &headerName) const;
+ QVector<QByteArray> headers() const;
+ QByteArray header(const QByteArray &headerName) const;
+ void setHeader(const QByteArray &headerName, const QByteArray &value);
+ void unsetHeader(const QByteArray &headerName);
+
+private:
+ QSharedDataPointer<QWebEngineHttpRequestPrivate> d;
+ friend class QWebEngineHttpRequestPrivate;
+};
+
+Q_DECLARE_SHARED(QWebEngineHttpRequest)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp
index 030d3ea89..228c37010 100644
--- a/src/core/web_contents_adapter.cpp
+++ b/src/core/web_contents_adapter.cpp
@@ -77,6 +77,7 @@
#include "content/public/common/page_state.h"
#include "content/public/common/page_zoom.h"
#include "content/public/common/renderer_preferences.h"
+#include "content/public/common/resource_request_body.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/web_preferences.h"
#include "third_party/WebKit/public/web/WebFindOptions.h"
@@ -499,6 +500,12 @@ void WebContentsAdapter::reloadAndBypassCache()
void WebContentsAdapter::load(const QUrl &url)
{
+ QWebEngineHttpRequest request(url);
+ load(request);
+}
+
+void WebContentsAdapter::load(const QWebEngineHttpRequest &request)
+{
// The situation can occur when relying on the editingFinished signal in QML to set the url
// of the WebView.
// When enter is pressed, onEditingFinished fires and the url of the webview is set, which
@@ -513,21 +520,55 @@ void WebContentsAdapter::load(const QUrl &url)
Q_UNUSED(guard);
Q_D(WebContentsAdapter);
- GURL gurl = toGurl(url);
+ GURL gurl = toGurl(request.url());
// Add URL scheme if missing from view-source URL.
- if (url.scheme() == content::kViewSourceScheme) {
- QUrl pageUrl = QUrl(url.toString().remove(0, strlen(content::kViewSourceScheme) + 1));
+ if (request.url().scheme() == content::kViewSourceScheme) {
+ QUrl pageUrl = QUrl(request.url().toString().remove(0,
+ strlen(content::kViewSourceScheme) + 1));
if (pageUrl.scheme().isEmpty()) {
QUrl extendedUrl = QUrl::fromUserInput(pageUrl.toString());
- extendedUrl = QUrl(QString("%1:%2").arg(content::kViewSourceScheme, extendedUrl.toString()));
+ extendedUrl = QUrl(QString("%1:%2").arg(content::kViewSourceScheme,
+ extendedUrl.toString()));
gurl = toGurl(extendedUrl);
}
}
content::NavigationController::LoadURLParams params(gurl);
- params.transition_type = ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
+ params.transition_type = ui::PageTransitionFromInt(ui::PAGE_TRANSITION_TYPED
+ | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
params.override_user_agent = content::NavigationController::UA_OVERRIDE_TRUE;
+
+ switch (request.method()) {
+ case QWebEngineHttpRequest::Get:
+ params.load_type = content::NavigationController::LOAD_TYPE_DEFAULT;
+ break;
+
+ case QWebEngineHttpRequest::Post:
+ params.load_type = content::NavigationController::LOAD_TYPE_HTTP_POST;
+ // chromium accepts LOAD_TYPE_HTTP_POST only for the HTTP and HTTPS protocols
+ if (!params.url.SchemeIsHTTPOrHTTPS()) {
+ d->adapterClient->loadFinished(false, request.url(), false,
+ net::ERR_DISALLOWED_URL_SCHEME,
+ QCoreApplication::translate("WebContentsAdapter",
+ "HTTP-POST data can only be sent over HTTP(S) protocol"));
+ return;
+ }
+ break;
+ }
+
+ params.post_data = content::ResourceRequestBody::CreateFromBytes(
+ (const char*)request.postData().constData(),
+ request.postData().length());
+
+ // convert the custom headers into the format that chromium expects
+ QVector<QByteArray> headers = request.headers();
+ for (QVector<QByteArray>::const_iterator it = headers.cbegin(); it != headers.cend(); ++it) {
+ if (params.extra_headers.length() > 0)
+ params.extra_headers += '\n';
+ params.extra_headers += (*it).toStdString() + ": " + request.header(*it).toStdString();
+ }
+
d->webContents->GetController().LoadURLWithParams(params);
focusIfNecessary();
}
diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h
index 3befe6d27..cb7f9b461 100644
--- a/src/core/web_contents_adapter.h
+++ b/src/core/web_contents_adapter.h
@@ -42,6 +42,7 @@
#include "qtwebenginecoreglobal.h"
#include "web_contents_adapter_client.h"
+#include <QtWebEngineCore/qwebenginehttprequest.h>
#include <QScopedPointer>
#include <QSharedPointer>
@@ -83,7 +84,8 @@ public:
void stop();
void reload();
void reloadAndBypassCache();
- void load(const QUrl&);
+ void load(const QUrl &url);
+ void load(const QWebEngineHttpRequest &request);
void setContent(const QByteArray &data, const QString &mimeType, const QUrl &baseUrl);
void save(const QString &filePath = QString(), int savePageFormat = -1);
QUrl activeUrl() const;
diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp
index c22736cab..ae7b209e4 100644
--- a/src/webenginewidgets/api/qwebenginepage.cpp
+++ b/src/webenginewidgets/api/qwebenginepage.cpp
@@ -1703,6 +1703,18 @@ void QWebEnginePage::load(const QUrl& url)
d->adapter->load(url);
}
+/*!
+ \since 5.9
+ Issues the specified \a request and loads the response.
+
+ \sa load(), setUrl(), url(), urlChanged(), QUrl::fromUserInput()
+*/
+void QWebEnginePage::load(const QWebEngineHttpRequest& request)
+{
+ Q_D(QWebEnginePage);
+ d->adapter->load(request);
+}
+
void QWebEnginePage::toHtml(const QWebEngineCallback<const QString &> &resultCallback) const
{
Q_D(const QWebEnginePage);
diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h
index 2ff9ad928..75621304b 100644
--- a/src/webenginewidgets/api/qwebenginepage.h
+++ b/src/webenginewidgets/api/qwebenginepage.h
@@ -44,6 +44,7 @@
#include <QtWebEngineWidgets/qwebenginecertificateerror.h>
#include <QtWebEngineWidgets/qwebenginedownloaditem.h>
#include <QtWebEngineCore/qwebenginecallback.h>
+#include <QtWebEngineCore/qwebenginehttprequest.h>
#include <QtCore/qobject.h>
#include <QtCore/qurl.h>
@@ -224,8 +225,8 @@ public:
void setFeaturePermission(const QUrl &securityOrigin, Feature feature, PermissionPolicy policy);
- // Ex-QWebFrame methods
void load(const QUrl &url);
+ void load(const QWebEngineHttpRequest &request);
void setHtml(const QString &html, const QUrl &baseUrl = QUrl());
void setContent(const QByteArray &data, const QString &mimeType = QString(), const QUrl &baseUrl = QUrl());
diff --git a/src/webenginewidgets/api/qwebengineview.cpp b/src/webenginewidgets/api/qwebengineview.cpp
index 8b4053e73..58d805fcb 100644
--- a/src/webenginewidgets/api/qwebengineview.cpp
+++ b/src/webenginewidgets/api/qwebengineview.cpp
@@ -175,6 +175,17 @@ void QWebEngineView::load(const QUrl& url)
page()->load(url);
}
+/*!
+ \since 5.9
+ Issues the specified \a request and loads the response.
+
+ \sa load(), setUrl(), url(), urlChanged(), QUrl::fromUserInput()
+*/
+void QWebEngineView::load(const QWebEngineHttpRequest &request)
+{
+ page()->load(request);
+}
+
void QWebEngineView::setHtml(const QString& html, const QUrl& baseUrl)
{
page()->setHtml(html, baseUrl);
diff --git a/src/webenginewidgets/api/qwebengineview.h b/src/webenginewidgets/api/qwebengineview.h
index d82a25eac..ef3bf1f00 100644
--- a/src/webenginewidgets/api/qwebengineview.h
+++ b/src/webenginewidgets/api/qwebengineview.h
@@ -46,6 +46,7 @@
#include <QtWebEngineWidgets/qtwebenginewidgetsglobal.h>
#include <QtWebEngineWidgets/qwebenginepage.h>
+#include <QtWebEngineCore/qwebenginehttprequest.h>
QT_BEGIN_NAMESPACE
class QContextMenuEvent;
@@ -71,7 +72,8 @@ public:
QWebEnginePage* page() const;
void setPage(QWebEnginePage* page);
- void load(const QUrl& url);
+ void load(const QUrl &url);
+ void load(const QWebEngineHttpRequest &request);
void setHtml(const QString& html, const QUrl& baseUrl = QUrl());
void setContent(const QByteArray& data, const QString& mimeType = QString(), const QUrl& baseUrl = QUrl());
diff --git a/src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc
index 5b96459af..3b27ca146 100644
--- a/src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc
+++ b/src/webenginewidgets/doc/src/qwebengineview_lgpl.qdoc
@@ -117,7 +117,7 @@
\note The view remains the same until enough data has arrived to display the new URL.
- \sa setUrl(), url(), urlChanged(), QUrl::fromUserInput()
+ \sa load(), setUrl(), url(), urlChanged(), QUrl::fromUserInput()
*/
/*!
diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
index 2baadd869..53c7650fb 100644
--- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
+++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
@@ -36,6 +36,9 @@
#include <QHBoxLayout>
#include <QQuickItem>
#include <QQuickWidget>
+#include <QtWebEngineCore/qwebenginehttprequest.h>
+#include <QTcpServer>
+#include <QTcpSocket>
#define VERIFY_INPUTMETHOD_HINTS(actual, expect) \
QVERIFY(actual == expect);
@@ -87,6 +90,7 @@ private Q_SLOTS:
void inputMethodsTextFormat();
void keyboardEvents();
void keyboardFocusAfterPopup();
+ void postData();
};
// This will be called before the first test function is executed.
@@ -1081,5 +1085,136 @@ void tst_QWebEngineView::keyboardFocusAfterPopup()
QTRY_COMPARE(evaluateJavaScriptSync(webView->page(), "document.getElementById('input1').value").toString(), QStringLiteral("x"));
}
+void tst_QWebEngineView::postData()
+{
+ QMap<QString, QString> postData;
+ // use reserved characters to make the test harder to pass
+ postData[QStringLiteral("Spä=m")] = QStringLiteral("ëgg:s");
+ postData[QStringLiteral("foo\r\n")] = QStringLiteral("ba&r");
+
+ QEventLoop eventloop;
+
+ // Set up dummy "HTTP" server
+ QTcpServer server;
+ connect(&server, &QTcpServer::newConnection, this, [this, &server, &eventloop, &postData](){
+ QTcpSocket* socket = server.nextPendingConnection();
+
+ connect(socket, &QAbstractSocket::disconnected, this, [&eventloop](){
+ eventloop.quit();
+ });
+
+ connect(socket, &QIODevice::readyRead, this, [this, socket, &server, &postData](){
+ QByteArray rawData = socket->readAll();
+ QStringList lines = QString::fromLocal8Bit(rawData).split("\r\n");
+
+ // examine request
+ QStringList request = lines[0].split(" ", QString::SkipEmptyParts);
+ bool requestOk = request.length() > 2
+ && request[2].toUpper().startsWith("HTTP/")
+ && request[0].toUpper() == "POST"
+ && request[1] == "/";
+ if (!requestOk) // POST and HTTP/... can be switched(?)
+ requestOk = request.length() > 2
+ && request[0].toUpper().startsWith("HTTP/")
+ && request[2].toUpper() == "POST"
+ && request[1] == "/";
+
+ // examine headers
+ int line = 1;
+ bool headersOk = true;
+ for (; headersOk && line < lines.length(); line++) {
+ QStringList headerParts = lines[line].split(":");
+ if (headerParts.length() < 2)
+ break;
+ QString headerKey = headerParts[0].trimmed().toLower();
+ QString headerValue = headerParts[1].trimmed().toLower();
+
+ if (headerKey == "host")
+ headersOk = headersOk && (headerValue == "127.0.0.1")
+ && (headerParts.length() == 3)
+ && (headerParts[2].trimmed()
+ == QString::number(server.serverPort()));
+ if (headerKey == "content-type")
+ headersOk = headersOk && (headerValue == "application/x-www-form-urlencoded");
+ }
+
+ // examine body
+ bool bodyOk = true;
+ if (lines.length() == line+2) {
+ QStringList postedFields = lines[line+1].split("&");
+ QMap<QString, QString> postedData;
+ for (int i = 0; bodyOk && i < postedFields.length(); i++) {
+ QStringList postedField = postedFields[i].split("=");
+ if (postedField.length() == 2)
+ postedData[QUrl::fromPercentEncoding(postedField[0].toLocal8Bit())]
+ = QUrl::fromPercentEncoding(postedField[1].toLocal8Bit());
+ else
+ bodyOk = false;
+ }
+ bodyOk = bodyOk && (postedData == postData);
+ } else { // no body at all or more than 1 line
+ bodyOk = false;
+ }
+
+ // send response
+ socket->write("HTTP/1.1 200 OK\r\n");
+ socket->write("Content-Type: text/html\r\n");
+ socket->write("Content-Length: 39\r\n\r\n");
+ if (requestOk && headersOk && bodyOk)
+ // 6 6 11 7 7 2 = 39 (Content-Length)
+ socket->write("<html><body>Test Passed</body></html>\r\n");
+ else
+ socket->write("<html><body>Test Failed</body></html>\r\n");
+ socket->flush();
+
+ if (!requestOk || !headersOk || !bodyOk) {
+ qDebug() << "Dummy HTTP Server: received request was not as expected";
+ qDebug() << rawData;
+ QVERIFY(requestOk); // one of them will yield useful output and make the test fail
+ QVERIFY(headersOk);
+ QVERIFY(bodyOk);
+ }
+
+ socket->close();
+ });
+ });
+ if (!server.listen())
+ QFAIL("Dummy HTTP Server: listen() failed");
+
+ // Manual, hard coded client (commented out, but not removed - for reference and just in case)
+ /*
+ QTcpSocket client;
+ connect(&client, &QIODevice::readyRead, this, [&client, &eventloop](){
+ qDebug() << "Dummy HTTP client: data received";
+ qDebug() << client.readAll();
+ eventloop.quit();
+ });
+ connect(&client, &QAbstractSocket::connected, this, [&client](){
+ client.write("HTTP/1.1 / GET\r\n\r\n");
+ });
+ client.connectToHost(QHostAddress::LocalHost, server.serverPort());
+ */
+
+ // send the POST request
+ QWebEngineView view;
+ QString sPort = QString::number(server.serverPort());
+ view.load(QWebEngineHttpRequest::postRequest(QUrl("http://127.0.0.1:"+sPort), postData));
+
+ // timeout after 10 seconds
+ QTimer timeoutGuard(this);
+ connect(&timeoutGuard, &QTimer::timeout, this, [&eventloop](){
+ eventloop.quit();
+ QFAIL("Dummy HTTP Server: waiting for data timed out");
+ });
+ timeoutGuard.setSingleShot(true);
+ timeoutGuard.start(10000);
+
+ // start the test
+ eventloop.exec();
+
+ timeoutGuard.stop();
+ server.close();
+}
+
QTEST_MAIN(tst_QWebEngineView)
#include "tst_qwebengineview.moc"