diff options
author | Anu Aliyas <anu.aliyas@qt.io> | 2023-04-17 13:34:23 +0200 |
---|---|---|
committer | Anu Aliyas <anu.aliyas@qt.io> | 2023-04-20 16:32:38 +0200 |
commit | 97c3782e2f5ecd3115aab1d0216b989b55f54f21 (patch) | |
tree | 0a8832e5d214707e224585165ae8dd5bc428d84c | |
parent | 421d3c4e0b57170343df57de0b222d0f57a7bcb7 (diff) | |
download | qtwebengine-97c3782e2f5ecd3115aab1d0216b989b55f54f21.tar.gz |
Add WebEngineView::save() convenience API
Add the convenience method QQuickWebEngineView::save for saving pages without
the need to explicitly handle download requests.
Task-number: QTBUG-56093
Change-Id: I67909fdca6472d1ea9b32e0f89c1ab90219bca3b
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
-rw-r--r-- | src/webenginequick/api/qquickwebengineprofile.cpp | 3 | ||||
-rw-r--r-- | src/webenginequick/api/qquickwebengineview.cpp | 7 | ||||
-rw-r--r-- | src/webenginequick/api/qquickwebengineview_p.h | 5 | ||||
-rw-r--r-- | src/webenginequick/doc/src/webengineview_lgpl.qdoc | 19 | ||||
-rw-r--r-- | tests/auto/quick/publicapi/tst_publicapi.cpp | 6 | ||||
-rw-r--r-- | tests/auto/quick/qmltests/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/quick/qmltests/data/tst_save.qml | 123 | ||||
-rw-r--r-- | tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp | 78 |
8 files changed, 240 insertions, 2 deletions
diff --git a/src/webenginequick/api/qquickwebengineprofile.cpp b/src/webenginequick/api/qquickwebengineprofile.cpp index a830e969e..92ab9dc25 100644 --- a/src/webenginequick/api/qquickwebengineprofile.cpp +++ b/src/webenginequick/api/qquickwebengineprofile.cpp @@ -212,7 +212,8 @@ void QQuickWebEngineProfilePrivate::downloadRequested(DownloadItemInfo &info) QWebEngineDownloadRequestPrivate *itemPrivate = new QWebEngineDownloadRequestPrivate(m_profileAdapter); itemPrivate->downloadId = info.id; - itemPrivate->downloadState = QWebEngineDownloadRequest::DownloadRequested; + itemPrivate->downloadState = info.accepted ? QWebEngineDownloadRequest::DownloadInProgress + : QWebEngineDownloadRequest::DownloadRequested; itemPrivate->startTime = info.startTime; itemPrivate->downloadUrl = info.url; itemPrivate->totalBytes = info.totalBytes; diff --git a/src/webenginequick/api/qquickwebengineview.cpp b/src/webenginequick/api/qquickwebengineview.cpp index 2d49c9419..f11274741 100644 --- a/src/webenginequick/api/qquickwebengineview.cpp +++ b/src/webenginequick/api/qquickwebengineview.cpp @@ -2411,6 +2411,13 @@ QQmlComponent *QQuickWebEngineView::touchHandleDelegate() const return d_ptr->m_touchHandleDelegate; } +void QQuickWebEngineView::save(const QString &filePath, + QWebEngineDownloadRequest::SavePageFormat format) const +{ + Q_D(const QQuickWebEngineView); + d->adapter->save(filePath, format); +} + QT_END_NAMESPACE #include "moc_qquickwebengineview_p.cpp" diff --git a/src/webenginequick/api/qquickwebengineview_p.h b/src/webenginequick/api/qquickwebengineview_p.h index 946bf1e0a..f057ba9be 100644 --- a/src/webenginequick/api/qquickwebengineview_p.h +++ b/src/webenginequick/api/qquickwebengineview_p.h @@ -17,6 +17,7 @@ #include <QtWebEngineCore/qtwebenginecoreglobal.h> #include <QtWebEngineCore/qwebenginequotarequest.h> +#include <QtWebEngineCore/qwebenginedownloadrequest.h> #include <QtWebEngineQuick/private/qtwebenginequickglobal_p.h> #include <QtGui/qcolor.h> #include <QtQml/qqmlregistration.h> @@ -465,6 +466,10 @@ QT_WARNING_POP QQmlComponent *touchHandleDelegate() const; void setTouchHandleDelegate(QQmlComponent *delegagte); + Q_REVISION(6, 6) + Q_INVOKABLE void save(const QString &filePath, + QWebEngineDownloadRequest::SavePageFormat format = + QWebEngineDownloadRequest::MimeHtmlSaveFormat) const; public Q_SLOTS: void runJavaScript(const QString&, const QJSValue & = QJSValue()); diff --git a/src/webenginequick/doc/src/webengineview_lgpl.qdoc b/src/webenginequick/doc/src/webengineview_lgpl.qdoc index be626c053..4dc99f00b 100644 --- a/src/webenginequick/doc/src/webengineview_lgpl.qdoc +++ b/src/webenginequick/doc/src/webengineview_lgpl.qdoc @@ -1528,5 +1528,24 @@ */ +/*! + \qmlmethod void WebEngineView::save(const QString &filePath, QWebEngineDownloadRequest::SavePageFormat format) + \since QtWebEngine 6.6 + + Save the current web page to disk. + + The web page is saved to \a filePath in the specified \a{format}. + + This is a shortcut for the following actions: + \list + \li Trigger the Save web action. + \li Accept the next download item and set the specified file path and save format. + \endlist + + This function issues an asynchronous download request for the web page and returns immediately. + + \sa QWebEngineDownloadRequest::SavePageFormat +*/ + \sa {WebEngine Qt Quick Custom Touch Handle Example} */ diff --git a/tests/auto/quick/publicapi/tst_publicapi.cpp b/tests/auto/quick/publicapi/tst_publicapi.cpp index 501b4af29..e18671d0c 100644 --- a/tests/auto/quick/publicapi/tst_publicapi.cpp +++ b/tests/auto/quick/publicapi/tst_publicapi.cpp @@ -72,7 +72,9 @@ static const QList<const QMetaObject *> typesToCheck = QList<const QMetaObject * << &QQuickWebEngineTouchSelectionMenuRequest::staticMetaObject ; -static QList<QMetaEnum> knownEnumNames = QList<QMetaEnum>(); +static QList<QMetaEnum> knownEnumNames = QList<QMetaEnum>() + << QWebEngineDownloadRequest::staticMetaObject.enumerator(QWebEngineDownloadRequest::staticMetaObject.indexOfEnumerator("SavePageFormat")) + ; static const QStringList hardcodedTypes = QStringList() << "QJSValue" @@ -787,6 +789,8 @@ static const QStringList expectedAPI = QStringList() << "QQuickWebEngineView.zoomFactor --> double" << "QQuickWebEngineView.zoomFactorChanged(double) --> void" << "QQuickWebEngineView.acceptAsNewWindow(QWebEngineNewWindowRequest*) --> void" + << "QQuickWebEngineView.save(QString) --> void" + << "QQuickWebEngineView.save(QString,QWebEngineDownloadRequest::SavePageFormat) --> void" << "QWebEngineQuotaRequest.accept() --> void" << "QWebEngineQuotaRequest.origin --> QUrl" << "QWebEngineQuotaRequest.reject() --> void" diff --git a/tests/auto/quick/qmltests/CMakeLists.txt b/tests/auto/quick/qmltests/CMakeLists.txt index ecbb6ac3e..acd783bd5 100644 --- a/tests/auto/quick/qmltests/CMakeLists.txt +++ b/tests/auto/quick/qmltests/CMakeLists.txt @@ -60,6 +60,7 @@ set(testList tst_userScripts.qml tst_userScriptCollection.qml tst_viewSource.qml + tst_save.qml ) if(QT_FEATURE_webengine_webchannel) diff --git a/tests/auto/quick/qmltests/data/tst_save.qml b/tests/auto/quick/qmltests/data/tst_save.qml new file mode 100644 index 000000000..9757f8fac --- /dev/null +++ b/tests/auto/quick/qmltests/data/tst_save.qml @@ -0,0 +1,123 @@ +import QtQuick +import QtTest +import QtWebEngine +import Test.util + +TestWebEngineView { + id: webEngineView + width: 200 + height: 200 + profile: testSaveProfile + + property url downloadUrl: "" + property int totalBytes: 0 + property int receivedBytes: 0 + property string downloadDir: "" + property string downloadFileName: "" + property var downloadState: [] + property int savePageFormat: WebEngineDownloadRequest.MimeHtmlSaveFormat; + + TempDir { + id: tempDir + } + + SignalSpy { + id: downLoadRequestedSpy + target: testSaveProfile + signalName: "downloadRequested" + } + + SignalSpy { + id: downloadFinishedSpy + target: testSaveProfile + signalName: "downloadFinished" + } + + WebEngineProfile { + id: testSaveProfile + + onDownloadRequested: function(download) { + downloadState.push(download.state) + downloadUrl = download.url + savePageFormat = download.savePageFormat + downloadDir = download.downloadDirectory; + downloadFileName = download.downloadFileName + } + onDownloadFinished: function(download) { + receivedBytes = download.receivedBytes + totalBytes = download.totalBytes + downloadState.push(download.state) + } + } + + TestCase { + name: "WebEngineViewSave" + + function verifyData() { + var isDataValid = false + webEngineView.runJavaScript("(function() {" + + "var title = document.title.toString();" + + "var body = document.body.innerText;" + + " return title === \"Test page 1\" && body.includes(\"Hello.\")" + + "})();", function(result) { + isDataValid = result; + }); + tryVerify(function() { return isDataValid }); + return isDataValid; + } + + function init() { + downLoadRequestedSpy.clear() + downloadFinishedSpy.clear() + totalBytes = 0 + receivedBytes = 0 + downloadDir = "" + downloadFileName = "" + downloadState = [] + downloadUrl = "" + } + + function test_savePage_data() { + return [ + { tag: "SingleHtmlSaveFormat", savePageFormat: WebEngineDownloadRequest.SingleHtmlSaveFormat }, + { tag: "CompleteHtmlSaveFormat", savePageFormat: WebEngineDownloadRequest.CompleteHtmlSaveFormat }, + { tag: "MimeHtmlSaveFormat", savePageFormat: WebEngineDownloadRequest.MimeHtmlSaveFormat }, + ]; + } + + function test_savePage(row) { + var saveFormat = row.savePageFormat + + var fileDir = tempDir.path() + var fileName = "saved_page.html" + var filePath = fileDir + "/"+ fileName + + // load data to view + webEngineView.url = Qt.resolvedUrl("test1.html") + verify(webEngineView.waitForLoadSucceeded()) + verify(verifyData()) + + webEngineView.save(filePath, saveFormat) + downLoadRequestedSpy.wait() + compare(downLoadRequestedSpy.count, 1) + compare(downloadUrl, webEngineView.url) + compare(savePageFormat, saveFormat) + compare(downloadDir, fileDir) + compare(downloadFileName, fileName) + compare(downloadState[0], WebEngineDownloadRequest.DownloadInProgress) + downloadFinishedSpy.wait() + compare(downloadFinishedSpy.count, 1) + compare(totalBytes, receivedBytes) + compare(downloadState[1], WebEngineDownloadRequest.DownloadCompleted) + + // load some other data + webEngineView.url = Qt.resolvedUrl("about:blank") + verify(webEngineView.waitForLoadSucceeded()) + + // load save file to view + webEngineView.url = Qt.resolvedUrl(filePath) + verify(webEngineView.waitForLoadSucceeded()) + verify(verifyData()) + } + } +} diff --git a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp index 5ae032061..f82c58264 100644 --- a/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp +++ b/tests/auto/quick/qquickwebengineview/tst_qquickwebengineview.cpp @@ -16,6 +16,7 @@ #include <QtGui/private/qinputmethod_p.h> #include <QtWebEngineQuick/private/qquickwebenginescriptcollection_p.h> #include <QtWebEngineQuick/private/qquickwebenginesettings_p.h> +#include <QtWebEngineQuick/private/qquickwebenginedownloadrequest_p.h> #include <QtWebEngineQuick/private/qquickwebengineview_p.h> #include <QtWebEngineCore/private/qtwebenginecore-config_p.h> #include <qpa/qplatforminputcontext.h> @@ -74,6 +75,8 @@ private Q_SLOTS: void focusChild(); #endif void htmlSelectPopup(); + void savePage_data(); + void savePage(); private: inline QQuickWebEngineView *newWebEngineView(); @@ -1274,6 +1277,81 @@ void tst_QQuickWebEngineView::htmlSelectPopup() QCOMPARE(evaluateJavaScriptSync(&view, "document.getElementById('select').value").toString(), QStringLiteral("O2")); } +void tst_QQuickWebEngineView::savePage_data() +{ + QTest::addColumn<QWebEngineDownloadRequest::SavePageFormat>("savePageFormat"); + + QTest::newRow("SingleHtmlSaveFormat") << QWebEngineDownloadRequest::SingleHtmlSaveFormat; + QTest::newRow("CompleteHtmlSaveFormat") << QWebEngineDownloadRequest::CompleteHtmlSaveFormat; + QTest::newRow("MimeHtmlSaveFormat") << QWebEngineDownloadRequest::MimeHtmlSaveFormat; +} + +void tst_QQuickWebEngineView::savePage() +{ + QFETCH(QWebEngineDownloadRequest::SavePageFormat, savePageFormat); + + QTemporaryDir tempDir(QDir::tempPath() + "/tst_QQuickWebEngineView-XXXXXX"); + QVERIFY(tempDir.isValid()); + const QString filePath = tempDir.path() + "/saved_page.html"; + + QQuickWebEngineView *view = webEngineView(); + connect(view->profile(), &QQuickWebEngineProfile::downloadRequested, + [&](QQuickWebEngineDownloadRequest *downloadRequest) { + QCOMPARE(downloadRequest->state(), + QQuickWebEngineDownloadRequest::DownloadInProgress); + QCOMPARE(downloadRequest->isSavePageDownload(), true); + QCOMPARE(downloadRequest->savePageFormat(), savePageFormat); + QCOMPARE(QDir(downloadRequest->downloadDirectory()) + .filePath(downloadRequest->downloadFileName()), + filePath); + QCOMPARE(downloadRequest->url(), view->url()); + + connect(downloadRequest, &QQuickWebEngineDownloadRequest::isFinishedChanged, + [&, downloadRequest]() { + QCOMPARE(downloadRequest->state(), + QQuickWebEngineDownloadRequest::DownloadCompleted); + QCOMPARE(downloadRequest->isSavePageDownload(), true); + QCOMPARE(downloadRequest->isFinished(), true); + QCOMPARE(downloadRequest->savePageFormat(), savePageFormat); + QCOMPARE(downloadRequest->totalBytes(), + downloadRequest->receivedBytes()); + QVERIFY(downloadRequest->receivedBytes() > 0); + QCOMPARE(QDir(downloadRequest->downloadDirectory()) + .filePath(downloadRequest->downloadFileName()), + filePath); + QCOMPARE(downloadRequest->url(), view->url()); + QTestEventLoop::instance().exitLoop(); + }); + }); + + const QString originalData = QStringLiteral("Basic page"); + view->setUrl(urlFromTestPath("html/basic_page.html")); + QVERIFY(waitForLoadSucceeded(view)); + QCOMPARE(evaluateJavaScriptSync(view, "document.getElementsByTagName('h1')[0].innerText") + .toString(), + originalData); + + // Save the loaded page as HTML. + view->save(filePath, savePageFormat); + QTestEventLoop::instance().enterLoop(10); + QFile file(filePath); + QVERIFY(file.exists()); + + // Load something else. + view->setUrl(urlFromTestPath("html/basic_page2.html")); + QVERIFY(waitForLoadSucceeded(view)); + QVERIFY(evaluateJavaScriptSync(view, "document.getElementsByTagName('h1')[0].innerText") + .toString() + != originalData); + + // Load the saved page and compare the contents. + view->setUrl(QUrl::fromLocalFile(filePath)); + QVERIFY(waitForLoadSucceeded(view)); + QCOMPARE(evaluateJavaScriptSync(view, "document.getElementsByTagName('h1')[0].innerText") + .toString(), + originalData); +} + #if QT_CONFIG(accessibility) static QByteArrayList params = QByteArrayList() << "--force-renderer-accessibility"; |