diff options
author | Jüri Valdmann <juri.valdmann@qt.io> | 2019-04-12 16:44:18 +0200 |
---|---|---|
committer | Jüri Valdmann <juri.valdmann@qt.io> | 2019-05-24 16:14:51 +0200 |
commit | a69029cf9fcfd0c1fcdaafe5cbcbff2d5dd6b5c5 (patch) | |
tree | a38cae2c082ee3c9a0108942b8406947d826a782 /tests/auto/widgets | |
parent | 0f081baa31facec779057de29eec14c6f458f6a6 (diff) |
Implement page lifecycle API
[ChangeLog][QtWebEngine][WebEngineView] WebEngineView now supports lifecycle
states that can be used for reducing CPU and memory consumption of invisible
views.
[ChangeLog][QtWebEngineWidgets][QWebEnginePage] QWebEnginePage now supports
lifecycle states that can be used for reducing CPU and memory consumption of
invisible pages.
Fixes: QTBUG-74166
Fixes: QTBUG-55079
Change-Id: I7d70c85dc995bd17c9fe91385a8e2750dbc0a627
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Peter Varga <pvarga@inf.u-szeged.hu>
Diffstat (limited to 'tests/auto/widgets')
5 files changed, 716 insertions, 0 deletions
diff --git a/tests/auto/widgets/qwebenginepage/resources/lifecycle.html b/tests/auto/widgets/qwebenginepage/resources/lifecycle.html new file mode 100644 index 000000000..aa477a359 --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/lifecycle.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<html> + <head> + <title>Lifecycle</title> + <script> + let frozenness = 0; + document.addEventListener("freeze", function() { + frozenness += 1; + }); + document.addEventListener("resume", function() { + frozenness -= 1; + }); + </script> + </head> + <body> + </body> +</html> diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp index 985d3edf7..90a881ff7 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -202,6 +202,19 @@ private Q_SLOTS: void sendNotification(); void contentsSize(); + void setLifecycleState(); + void setVisible(); + void discardPreservesProperties(); + void discardBeforeInitialization(); + void automaticUndiscard(); + void setLifecycleStateWithDevTools(); + void discardPreservesCommittedLoad(); + void discardAbortsPendingLoad(); + void discardAbortsPendingLoadAndPreservesCommittedLoad(); + void recommendedState(); + void recommendedStateAuto(); + void setLifecycleStateAndReload(); + private: static QPoint elementCenter(QWebEnginePage *page, const QString &id); @@ -3386,6 +3399,645 @@ void tst_QWebEnginePage::contentsSize() QCOMPARE(m_page->contentsSize().height(), 1216); } +void tst_QWebEnginePage::setLifecycleState() +{ + qRegisterMetaType<QWebEnginePage::LifecycleState>("LifecycleState"); + + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + QSignalSpy lifecycleSpy(&page, &QWebEnginePage::lifecycleStateChanged); + QSignalSpy visibleSpy(&page, &QWebEnginePage::visibleChanged); + + page.load(QStringLiteral("qrc:/resources/lifecycle.html")); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); + QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(page.isVisible(), false); + QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(false)); + QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(0)); + + // Active -> Frozen + page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); + QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Frozen); + QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(page.isVisible(), false); + QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(false)); + QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(1)); + + // Frozen -> Active + page.setLifecycleState(QWebEnginePage::LifecycleState::Active); + QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(page.isVisible(), false); + QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(false)); + QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(0)); + + // Active -> Discarded + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); + QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(page.isVisible(), false); + QTest::ignoreMessage(QtWarningMsg, "runJavaScript: disabled in Discarded state"); + QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant()); + QTest::ignoreMessage(QtWarningMsg, "runJavaScript: disabled in Discarded state"); + QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant()); + QCOMPARE(loadSpy.count(), 0); + + // Discarded -> Frozen (illegal!) + QTest::ignoreMessage(QtWarningMsg, + "setLifecycleState: failed to transition from Discarded to Frozen state: " + "illegal transition"); + page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); + QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); + + // Discarded -> Active + page.setLifecycleState(QWebEnginePage::LifecycleState::Active); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); + QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(page.isVisible(), false); + QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(true)); + QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(0)); + + // Active -> Frozen -> Discarded -> Active + page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + page.setLifecycleState(QWebEnginePage::LifecycleState::Active); + QCOMPARE(lifecycleSpy.count(), 3); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(page.isVisible(), false); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); + QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(true)); + QCOMPARE(evaluateJavaScriptSync(&page, "frozenness"), QVariant(0)); + + // Reload clears document.wasDiscarded + page.triggerAction(QWebEnginePage::Reload); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); + QCOMPARE(evaluateJavaScriptSync(&page, "document.wasDiscarded"), QVariant(false)); +} + +void tst_QWebEnginePage::setVisible() +{ + qRegisterMetaType<QWebEnginePage::LifecycleState>("LifecycleState"); + + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + QSignalSpy lifecycleSpy(&page, &QWebEnginePage::lifecycleStateChanged); + QSignalSpy visibleSpy(&page, &QWebEnginePage::visibleChanged); + + page.load(QStringLiteral("about:blank")); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); + QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(visibleSpy.count(), 0); + QCOMPARE(page.isVisible(), false); + + // hidden -> visible + page.setVisible(true); + QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(true)); + QCOMPARE(page.isVisible(), true); + + // Active -> Frozen (illegal) + QTest::ignoreMessage( + QtWarningMsg, + "setLifecycleState: failed to transition from Active to Frozen state: page is visible"); + page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); + QCOMPARE(lifecycleSpy.count(), 0); + + // visible -> hidden + page.setVisible(false); + QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(false)); + QCOMPARE(page.isVisible(), false); + + // Active -> Frozen + page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); + QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Frozen); + + // hidden -> visible (triggers Frozen -> Active) + page.setVisible(true); + QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(true)); + QCOMPARE(page.isVisible(), true); + + // Active -> Discarded (illegal) + QTest::ignoreMessage(QtWarningMsg, + "setLifecycleState: failed to transition from Active to Discarded state: " + "page is visible"); + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + QCOMPARE(lifecycleSpy.count(), 0); + + // visible -> hidden + page.setVisible(false); + QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(false)); + QCOMPARE(page.isVisible(), false); + + // Active -> Discarded + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); + + // hidden -> visible (triggers Discarded -> Active) + page.setVisible(true); + QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(visibleSpy.count(), 1); + QCOMPARE(visibleSpy.takeFirst().value(0), QVariant(true)); + QCOMPARE(page.isVisible(), true); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); +} + +void tst_QWebEnginePage::discardPreservesProperties() +{ + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + + page.load(QStringLiteral("about:blank")); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); + + // Change as many properties as possible to non-default values + bool audioMuted = true; + QVERIFY(page.isAudioMuted() != audioMuted); + page.setAudioMuted(audioMuted); + QColor backgroundColor = Qt::black; + QVERIFY(page.backgroundColor() != backgroundColor); + page.setBackgroundColor(backgroundColor); + qreal zoomFactor = 2; + QVERIFY(page.zoomFactor() != zoomFactor); + page.setZoomFactor(zoomFactor); +#if QT_CONFIG(webengine_webchannel) + QWebChannel *webChannel = new QWebChannel(&page); + page.setWebChannel(webChannel); +#endif + + // Take snapshot of the rest + QSizeF contentsSize = page.contentsSize(); + QIcon icon = page.icon(); + QUrl iconUrl = page.iconUrl(); + QUrl requestedUrl = page.requestedUrl(); + QString title = page.title(); + QUrl url = page.url(); + + // History should be preserved too + int historyCount = page.history()->count(); + QCOMPARE(historyCount, 1); + int historyIndex = page.history()->currentItemIndex(); + QCOMPARE(historyIndex, 0); + QWebEngineHistoryItem historyItem = page.history()->currentItem(); + QVERIFY(historyItem.isValid()); + + // Discard + undiscard + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + page.setLifecycleState(QWebEnginePage::LifecycleState::Active); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); + + // Property changes should be preserved + QCOMPARE(page.isAudioMuted(), audioMuted); + QCOMPARE(page.backgroundColor(), backgroundColor); + QCOMPARE(page.contentsSize(), contentsSize); + QCOMPARE(page.icon(), icon); + QCOMPARE(page.iconUrl(), iconUrl); + QCOMPARE(page.requestedUrl(), requestedUrl); + QCOMPARE(page.title(), title); + QCOMPARE(page.url(), url); + QCOMPARE(page.zoomFactor(), zoomFactor); +#if QT_CONFIG(webengine_webchannel) + QCOMPARE(page.webChannel(), webChannel); +#endif + QCOMPARE(page.history()->count(), historyCount); + QCOMPARE(page.history()->currentItemIndex(), historyIndex); + QCOMPARE(page.history()->currentItem().url(), historyItem.url()); + QCOMPARE(page.history()->currentItem().originalUrl(), historyItem.originalUrl()); + QCOMPARE(page.history()->currentItem().title(), historyItem.title()); +} + +void tst_QWebEnginePage::discardBeforeInitialization() +{ + QWebEngineProfile profile; + QWebEnginePage page(&profile); + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + // The call is ignored + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); +} + +void tst_QWebEnginePage::automaticUndiscard() +{ + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + + page.load(QStringLiteral("about:blank")); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); + + // setUrl + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + page.setUrl(QStringLiteral("qrc:/resources/lifecycle.html")); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + + // setContent + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + page.setContent(QByteArrayLiteral("foo")); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); +} + +void tst_QWebEnginePage::setLifecycleStateWithDevTools() +{ + QWebEngineProfile profile; + QWebEnginePage inspectedPage(&profile); + QWebEnginePage devToolsPage(&profile); + QSignalSpy devToolsSpy(&devToolsPage, &QWebEnginePage::loadFinished); + QSignalSpy inspectedSpy(&inspectedPage, &QWebEnginePage::loadFinished); + + // Ensure pages are initialized + inspectedPage.load(QStringLiteral("about:blank")); + devToolsPage.load(QStringLiteral("about:blank")); + QTRY_COMPARE(inspectedSpy.count(), 1); + QCOMPARE(inspectedSpy.takeFirst().value(0), QVariant(true)); + QTRY_COMPARE(devToolsSpy.count(), 1); + QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); + + // Open DevTools with Frozen inspectedPage + inspectedPage.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); + inspectedPage.setDevToolsPage(&devToolsPage); + QCOMPARE(inspectedPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QTRY_COMPARE(devToolsSpy.count(), 1); + QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); + inspectedPage.setDevToolsPage(nullptr); + + // Open DevTools with Discarded inspectedPage + inspectedPage.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + inspectedPage.setDevToolsPage(&devToolsPage); + QCOMPARE(inspectedPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QTRY_COMPARE(devToolsSpy.count(), 1); + QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); + QTRY_COMPARE(inspectedSpy.count(), 1); + QCOMPARE(inspectedSpy.takeFirst().value(0), QVariant(true)); + inspectedPage.setDevToolsPage(nullptr); + + // Open DevTools with Frozen devToolsPage + devToolsPage.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); + devToolsPage.setInspectedPage(&inspectedPage); + QCOMPARE(devToolsPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QTRY_COMPARE(devToolsSpy.count(), 1); + QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); + devToolsPage.setInspectedPage(nullptr); + + // Open DevTools with Discarded devToolsPage + devToolsPage.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + devToolsPage.setInspectedPage(&inspectedPage); + QCOMPARE(devToolsPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QTRY_COMPARE(devToolsSpy.count(), 2); + QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(false)); + QCOMPARE(devToolsSpy.takeFirst().value(0), QVariant(true)); + // keep DevTools open + + // Try to change state while DevTools are open + QTest::ignoreMessage( + QtWarningMsg, + "setLifecycleState: failed to transition from Active to Frozen state: DevTools open"); + inspectedPage.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); + QCOMPARE(inspectedPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QTest::ignoreMessage(QtWarningMsg, + "setLifecycleState: failed to transition from Active to Discarded state: " + "DevTools open"); + inspectedPage.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + QCOMPARE(inspectedPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QTest::ignoreMessage( + QtWarningMsg, + "setLifecycleState: failed to transition from Active to Frozen state: DevTools open"); + devToolsPage.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); + QCOMPARE(devToolsPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QTest::ignoreMessage(QtWarningMsg, + "setLifecycleState: failed to transition from Active to Discarded state: " + "DevTools open"); + devToolsPage.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + QCOMPARE(devToolsPage.lifecycleState(), QWebEnginePage::LifecycleState::Active); +} + +void tst_QWebEnginePage::discardPreservesCommittedLoad() +{ + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy loadStartedSpy(&page, &QWebEnginePage::loadStarted); + QSignalSpy loadFinishedSpy(&page, &QWebEnginePage::loadFinished); + QSignalSpy urlChangedSpy(&page, &QWebEnginePage::urlChanged); + QSignalSpy titleChangedSpy(&page, &QWebEnginePage::titleChanged); + + QString url = QStringLiteral("qrc:/resources/lifecycle.html"); + page.setUrl(url); + QTRY_COMPARE(loadStartedSpy.count(), 1); + loadStartedSpy.clear(); + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(true)); + QCOMPARE(urlChangedSpy.count(), 1); + QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl(url))); + QCOMPARE(page.url(), url); + QCOMPARE(titleChangedSpy.count(), 2); + QCOMPARE(titleChangedSpy.takeFirst().value(0), QVariant(url)); + QString title = QStringLiteral("Lifecycle"); + QCOMPARE(titleChangedSpy.takeFirst().value(0), QVariant(title)); + QCOMPARE(page.title(), title); + + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + QCOMPARE(loadStartedSpy.count(), 0); + QCOMPARE(loadFinishedSpy.count(), 0); + QCOMPARE(urlChangedSpy.count(), 0); + QCOMPARE(page.url(), QUrl(url)); + QCOMPARE(titleChangedSpy.count(), 0); + QCOMPARE(page.title(), title); + + page.setLifecycleState(QWebEnginePage::LifecycleState::Active); + QTRY_COMPARE(loadStartedSpy.count(), 1); + loadStartedSpy.clear(); + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(true)); + QCOMPARE(urlChangedSpy.count(), 0); + QCOMPARE(page.url(), url); + QCOMPARE(titleChangedSpy.count(), 0); + QCOMPARE(page.title(), title); +} + +void tst_QWebEnginePage::discardAbortsPendingLoad() +{ + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy loadStartedSpy(&page, &QWebEnginePage::loadStarted); + QSignalSpy loadFinishedSpy(&page, &QWebEnginePage::loadFinished); + QSignalSpy urlChangedSpy(&page, &QWebEnginePage::urlChanged); + QSignalSpy titleChangedSpy(&page, &QWebEnginePage::titleChanged); + + connect(&page, &QWebEnginePage::loadStarted, + [&]() { page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); }); + QUrl url = QStringLiteral("qrc:/resources/lifecycle.html"); + page.setUrl(url); + QTRY_COMPARE(loadStartedSpy.count(), 1); + loadStartedSpy.clear(); + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(false)); + QCOMPARE(urlChangedSpy.count(), 2); + QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(url)); + QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl())); + QCOMPARE(titleChangedSpy.count(), 0); + QCOMPARE(page.url(), QUrl()); + QCOMPARE(page.title(), QString()); + + page.setLifecycleState(QWebEnginePage::LifecycleState::Active); + QCOMPARE(loadStartedSpy.count(), 0); + QCOMPARE(loadFinishedSpy.count(), 0); + QCOMPARE(urlChangedSpy.count(), 0); + QCOMPARE(page.url(), QUrl()); + QCOMPARE(page.title(), QString()); +} + +void tst_QWebEnginePage::discardAbortsPendingLoadAndPreservesCommittedLoad() +{ + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy loadStartedSpy(&page, &QWebEnginePage::loadStarted); + QSignalSpy loadFinishedSpy(&page, &QWebEnginePage::loadFinished); + QSignalSpy urlChangedSpy(&page, &QWebEnginePage::urlChanged); + QSignalSpy titleChangedSpy(&page, &QWebEnginePage::titleChanged); + + QString url1 = QStringLiteral("qrc:/resources/lifecycle.html"); + page.setUrl(url1); + QTRY_COMPARE(loadStartedSpy.count(), 1); + loadStartedSpy.clear(); + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(true)); + QCOMPARE(urlChangedSpy.count(), 1); + QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl(url1))); + QCOMPARE(page.url(), url1); + QCOMPARE(titleChangedSpy.count(), 2); + QCOMPARE(titleChangedSpy.takeFirst().value(0), QVariant(url1)); + QString title = QStringLiteral("Lifecycle"); + QCOMPARE(titleChangedSpy.takeFirst().value(0), QVariant(title)); + QCOMPARE(page.title(), title); + + connect(&page, &QWebEnginePage::loadStarted, + [&]() { page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); }); + QString url2 = QStringLiteral("about:blank"); + page.setUrl(url2); + QTRY_COMPARE(loadStartedSpy.count(), 1); + loadStartedSpy.clear(); + QTRY_COMPARE(loadFinishedSpy.count(), 1); + QCOMPARE(loadFinishedSpy.takeFirst().value(0), QVariant(false)); + QCOMPARE(urlChangedSpy.count(), 2); + QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl(url2))); + QCOMPARE(urlChangedSpy.takeFirst().value(0), QVariant(QUrl(url1))); + QCOMPARE(titleChangedSpy.count(), 0); + QCOMPARE(page.url(), url1); + QCOMPARE(page.title(), title); + + page.setLifecycleState(QWebEnginePage::LifecycleState::Active); + QCOMPARE(loadStartedSpy.count(), 0); + QCOMPARE(loadFinishedSpy.count(), 0); + QCOMPARE(urlChangedSpy.count(), 0); + QCOMPARE(page.url(), url1); + QCOMPARE(page.title(), title); +} + +void tst_QWebEnginePage::recommendedState() +{ + qRegisterMetaType<QWebEnginePage::LifecycleState>("LifecycleState"); + + QWebEngineProfile profile; + QWebEnginePage page(&profile); + + struct Event { + enum { StateChange, RecommendationChange } key; + QWebEnginePage::LifecycleState value; + }; + std::vector<Event> events; + connect(&page, &QWebEnginePage::lifecycleStateChanged, [&](QWebEnginePage::LifecycleState state) { + events.push_back(Event { Event::StateChange, state }); + }); + connect(&page, &QWebEnginePage::recommendedStateChanged, [&](QWebEnginePage::LifecycleState state) { + events.push_back(Event { Event::RecommendationChange, state }); + }); + + page.load(QStringLiteral("qrc:/resources/lifecycle.html")); + QTRY_COMPARE(events.size(), 1u); + QCOMPARE(events[0].key, Event::RecommendationChange); + QCOMPARE(events[0].value, QWebEnginePage::LifecycleState::Frozen); + events.clear(); + QCOMPARE(page.recommendedState(), QWebEnginePage::LifecycleState::Frozen); + + page.setVisible(true); + QTRY_COMPARE(events.size(), 1u); + QCOMPARE(events[0].key, Event::RecommendationChange); + QCOMPARE(events[0].value, QWebEnginePage::LifecycleState::Active); + events.clear(); + QCOMPARE(page.recommendedState(), QWebEnginePage::LifecycleState::Active); + + page.setVisible(false); + QTRY_COMPARE(events.size(), 1u); + QCOMPARE(events[0].key, Event::RecommendationChange); + QCOMPARE(events[0].value, QWebEnginePage::LifecycleState::Frozen); + events.clear(); + QCOMPARE(page.recommendedState(), QWebEnginePage::LifecycleState::Frozen); + + page.triggerAction(QWebEnginePage::Reload); + QTRY_COMPARE(events.size(), 2u); + QCOMPARE(events[0].key, Event::RecommendationChange); + QCOMPARE(events[0].value, QWebEnginePage::LifecycleState::Active); + QCOMPARE(events[1].key, Event::RecommendationChange); + QCOMPARE(events[1].value, QWebEnginePage::LifecycleState::Frozen); + events.clear(); + QCOMPARE(page.recommendedState(), QWebEnginePage::LifecycleState::Frozen); + + QWebEnginePage devTools; + page.setDevToolsPage(&devTools); + QTRY_COMPARE(events.size(), 1u); + QCOMPARE(events[0].key, Event::RecommendationChange); + QCOMPARE(events[0].value, QWebEnginePage::LifecycleState::Active); + events.clear(); + QCOMPARE(page.recommendedState(), QWebEnginePage::LifecycleState::Active); + + page.setDevToolsPage(nullptr); + QTRY_COMPARE(events.size(), 1u); + QCOMPARE(events[0].key, Event::RecommendationChange); + QCOMPARE(events[0].value, QWebEnginePage::LifecycleState::Frozen); + events.clear(); + QCOMPARE(page.recommendedState(), QWebEnginePage::LifecycleState::Frozen); + + page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); + QTRY_COMPARE(events.size(), 2u); + QCOMPARE(events[0].key, Event::StateChange); + QCOMPARE(events[0].value, QWebEnginePage::LifecycleState::Frozen); + QCOMPARE(events[1].key, Event::RecommendationChange); + QCOMPARE(events[1].value, QWebEnginePage::LifecycleState::Discarded); + events.clear(); + QCOMPARE(page.recommendedState(), QWebEnginePage::LifecycleState::Discarded); + + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + QTRY_COMPARE(events.size(), 1u); + QCOMPARE(events[0].key, Event::StateChange); + QCOMPARE(events[0].value, QWebEnginePage::LifecycleState::Discarded); + events.clear(); + QCOMPARE(page.recommendedState(), QWebEnginePage::LifecycleState::Discarded); +} + +void tst_QWebEnginePage::recommendedStateAuto() +{ + qRegisterMetaType<QWebEnginePage::LifecycleState>("LifecycleState"); + + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy lifecycleSpy(&page, &QWebEnginePage::lifecycleStateChanged); + connect(&page, &QWebEnginePage::recommendedStateChanged, &page, &QWebEnginePage::setLifecycleState); + + page.load(QStringLiteral("qrc:/resources/lifecycle.html")); + QTRY_COMPARE(lifecycleSpy.count(), 2); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); + + page.setVisible(true); + QTRY_COMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); + + page.setVisible(false); + QTRY_COMPARE(lifecycleSpy.count(), 2); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); + + page.triggerAction(QWebEnginePage::Reload); + QTRY_COMPARE(lifecycleSpy.count(), 3); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); + + QWebEnginePage devTools; + page.setDevToolsPage(&devTools); + QTRY_COMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); + + page.setDevToolsPage(nullptr); + QTRY_COMPARE(lifecycleSpy.count(), 2); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); +} + +void tst_QWebEnginePage::setLifecycleStateAndReload() +{ + qRegisterMetaType<QWebEnginePage::LifecycleState>("LifecycleState"); + + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QSignalSpy loadSpy(&page, &QWebEnginePage::loadFinished); + QSignalSpy lifecycleSpy(&page, &QWebEnginePage::lifecycleStateChanged); + + page.load(QStringLiteral("qrc:/resources/lifecycle.html")); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); + QCOMPARE(lifecycleSpy.count(), 0); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + + page.setLifecycleState(QWebEnginePage::LifecycleState::Frozen); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Frozen); + QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Frozen)); + + page.triggerAction(QWebEnginePage::Reload); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); + + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); + QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Discarded)); + + page.triggerAction(QWebEnginePage::Reload); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + QCOMPARE(lifecycleSpy.count(), 1); + QCOMPARE(lifecycleSpy.takeFirst().value(0), QVariant::fromValue(QWebEnginePage::LifecycleState::Active)); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(loadSpy.takeFirst().value(0), QVariant(true)); +} + static QByteArrayList params = {QByteArrayLiteral("--use-fake-device-for-media-stream")}; W_QTEST_MAIN(tst_QWebEnginePage, params) diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc index cf32486e7..013a307de 100644 --- a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc @@ -23,6 +23,7 @@ <file>resources/foo.txt</file> <file>resources/bar.txt</file> <file>resources/path with spaces.txt</file> + <file>resources/lifecycle.html</file> </qresource> <qresource prefix='/shared'> <file alias='notification.html'>../../shared/data/notification.html</file> diff --git a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp index 9a2ee9311..487e70d28 100644 --- a/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp +++ b/tests/auto/widgets/qwebenginescript/tst_qwebenginescript.cpp @@ -159,6 +159,14 @@ void tst_QWebEngineScript::loadEvents() QCOMPARE(page.eval("window.log", QWebEngineScript::MainWorld).toStringList(), expected); QCOMPARE(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList(), expected); + // After discard + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + page.setLifecycleState(QWebEnginePage::LifecycleState::Active); + QTRY_COMPARE(page.spy.count(), 1); + QCOMPARE(page.spy.takeFirst().value(0).toBool(), true); + QCOMPARE(page.eval("window.log", QWebEngineScript::MainWorld).toStringList(), expected); + QCOMPARE(page.eval("window.log", QWebEngineScript::ApplicationWorld).toStringList(), expected); + // Multiple frames page.load(QUrl("qrc:/resources/test_iframe_main.html")); QTRY_COMPARE(page.spy.count(), 1); @@ -531,6 +539,11 @@ void tst_QWebEngineScript::navigation() page.setUrl(url3); QTRY_COMPARE(spyTextChanged.count(), 3); QCOMPARE(testObject.text(), url3); + + page.setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + page.setUrl(url1); + QTRY_COMPARE(spyTextChanged.count(), 4); + QCOMPARE(testObject.text(), url1); } // Try to set TestObject::text to an invalid UTF-16 string. diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp index d3ea27fc2..f542c09d9 100644 --- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp +++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp @@ -201,6 +201,7 @@ private Q_SLOTS: void setViewDeletesImplicitPage(); void setPagePreservesExplicitPage(); void setViewPreservesExplicitPage(); + void closeDiscardsPage(); }; // This will be called before the first test function is executed. @@ -2194,6 +2195,22 @@ void tst_QWebEngineView::textSelectionOutOfInputField() QVERIFY(!view.hasSelection()); QVERIFY(view.page()->selectedText().isEmpty()); + // Select text by ctrl+a + QTest::keyClick(view.windowHandle(), Qt::Key_A, Qt::ControlModifier); + QVERIFY(selectionChangedSpy.wait()); + QCOMPARE(selectionChangedSpy.count(), 3); + QVERIFY(view.hasSelection()); + QCOMPARE(view.page()->selectedText(), QString("This is a text")); + + // Deselect text via discard+undiscard + view.hide(); + view.page()->setLifecycleState(QWebEnginePage::LifecycleState::Discarded); + view.show(); + QVERIFY(loadFinishedSpy.wait()); + QCOMPARE(selectionChangedSpy.count(), 4); + QVERIFY(!view.hasSelection()); + QVERIFY(view.page()->selectedText().isEmpty()); + selectionChangedSpy.clear(); view.setHtml("<html><body>" " This is a text" @@ -3258,5 +3275,21 @@ void tst_QWebEngineView::setViewPreservesExplicitPage() QVERIFY(explicitPage1); // should not be deleted } +void tst_QWebEngineView::closeDiscardsPage() +{ + QWebEngineProfile profile; + QWebEnginePage page(&profile); + QWebEngineView view; + view.setPage(&page); + view.resize(300, 300); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QCOMPARE(page.isVisible(), true); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Active); + view.close(); + QCOMPARE(page.isVisible(), false); + QCOMPARE(page.lifecycleState(), QWebEnginePage::LifecycleState::Discarded); +} + QTEST_MAIN(tst_QWebEngineView) #include "tst_qwebengineview.moc" |