From 338e24b48601a3cfdcde0977b6c9390d33d51c9f Mon Sep 17 00:00:00 2001 From: Adam Kallai Date: Mon, 14 Oct 2013 05:57:02 -0700 Subject: Turn on to run all widgets tests by make check. Move widgets test to tests/auto. Change-Id: If3617d86ea44f665a44a54b6ba57935b69220a9e Reviewed-by: Jocelyn Turcotte Reviewed-by: Andras Becsi --- .../auto/widgets/qwebenginepage/qwebenginepage.pro | 3 + .../widgets/qwebenginepage/resources/content.html | 5 + .../widgets/qwebenginepage/resources/frame_a.html | 2 + .../widgets/qwebenginepage/resources/frame_c.html | 1 + .../qwebenginepage/resources/framedindex.html | 6 + .../widgets/qwebenginepage/resources/iframe.html | 6 + .../widgets/qwebenginepage/resources/iframe2.html | 6 + .../widgets/qwebenginepage/resources/iframe3.html | 5 + .../widgets/qwebenginepage/resources/index.html | 6 + .../widgets/qwebenginepage/resources/script.html | 3 + .../auto/widgets/qwebenginepage/resources/user.css | 3 + .../widgets/qwebenginepage/tst_qwebenginepage.cpp | 3628 ++++++++++++++++++++ .../widgets/qwebenginepage/tst_qwebenginepage.qrc | 14 + 13 files changed, 3688 insertions(+) create mode 100644 tests/auto/widgets/qwebenginepage/qwebenginepage.pro create mode 100644 tests/auto/widgets/qwebenginepage/resources/content.html create mode 100644 tests/auto/widgets/qwebenginepage/resources/frame_a.html create mode 100644 tests/auto/widgets/qwebenginepage/resources/frame_c.html create mode 100644 tests/auto/widgets/qwebenginepage/resources/framedindex.html create mode 100644 tests/auto/widgets/qwebenginepage/resources/iframe.html create mode 100644 tests/auto/widgets/qwebenginepage/resources/iframe2.html create mode 100644 tests/auto/widgets/qwebenginepage/resources/iframe3.html create mode 100644 tests/auto/widgets/qwebenginepage/resources/index.html create mode 100644 tests/auto/widgets/qwebenginepage/resources/script.html create mode 100644 tests/auto/widgets/qwebenginepage/resources/user.css create mode 100644 tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp create mode 100644 tests/auto/widgets/qwebenginepage/tst_qwebenginepage.qrc (limited to 'tests/auto/widgets/qwebenginepage') diff --git a/tests/auto/widgets/qwebenginepage/qwebenginepage.pro b/tests/auto/widgets/qwebenginepage/qwebenginepage.pro new file mode 100644 index 000000000..e56bbe8f7 --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/qwebenginepage.pro @@ -0,0 +1,3 @@ +include(../tests.pri) +exists($${TARGET}.qrc):RESOURCES += $${TARGET}.qrc +QT *= core-private gui-private diff --git a/tests/auto/widgets/qwebenginepage/resources/content.html b/tests/auto/widgets/qwebenginepage/resources/content.html new file mode 100644 index 000000000..360ad65ef --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/content.html @@ -0,0 +1,5 @@ + + +This is test content + + diff --git a/tests/auto/widgets/qwebenginepage/resources/frame_a.html b/tests/auto/widgets/qwebenginepage/resources/frame_a.html new file mode 100644 index 000000000..9ff68f13a --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/frame_a.html @@ -0,0 +1,2 @@ +Google +Yahoo diff --git a/tests/auto/widgets/qwebenginepage/resources/frame_c.html b/tests/auto/widgets/qwebenginepage/resources/frame_c.html new file mode 100644 index 000000000..eba9ca0eb --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/frame_c.html @@ -0,0 +1 @@ +Google diff --git a/tests/auto/widgets/qwebenginepage/resources/framedindex.html b/tests/auto/widgets/qwebenginepage/resources/framedindex.html new file mode 100644 index 000000000..be4500483 --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/framedindex.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/tests/auto/widgets/qwebenginepage/resources/iframe.html b/tests/auto/widgets/qwebenginepage/resources/iframe.html new file mode 100644 index 000000000..f17027c7a --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/iframe.html @@ -0,0 +1,6 @@ + + +

top

+ + + diff --git a/tests/auto/widgets/qwebenginepage/resources/iframe3.html b/tests/auto/widgets/qwebenginepage/resources/iframe3.html new file mode 100644 index 000000000..ed6ac5b94 --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/iframe3.html @@ -0,0 +1,5 @@ + + +

inner

+ + diff --git a/tests/auto/widgets/qwebenginepage/resources/index.html b/tests/auto/widgets/qwebenginepage/resources/index.html new file mode 100644 index 000000000..638df364e --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/index.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/tests/auto/widgets/qwebenginepage/resources/script.html b/tests/auto/widgets/qwebenginepage/resources/script.html new file mode 100644 index 000000000..ede986415 --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/script.html @@ -0,0 +1,3 @@ + + + diff --git a/tests/auto/widgets/qwebenginepage/resources/user.css b/tests/auto/widgets/qwebenginepage/resources/user.css new file mode 100644 index 000000000..4ccb2f0fc --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/resources/user.css @@ -0,0 +1,3 @@ +p { + background-image: url('http://does.not/exist.png'); +} \ No newline at end of file diff --git a/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp new file mode 100644 index 000000000..bbdc6c2e9 --- /dev/null +++ b/tests/auto/widgets/qwebenginepage/tst_qwebenginepage.cpp @@ -0,0 +1,3628 @@ +/* + Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + Copyright (C) 2009 Girish Ramakrishnan + Copyright (C) 2010 Holger Hans Peter Freyther + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "../util.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void removeRecursive(const QString& dirname) +{ + QDir dir(dirname); + QFileInfoList entries(dir.entryInfoList(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot)); + for (int i = 0; i < entries.count(); ++i) + if (entries[i].isDir()) + removeRecursive(entries[i].filePath()); + else + dir.remove(entries[i].fileName()); + QDir().rmdir(dirname); +} + +class TestInputContext : public QPlatformInputContext +{ +public: + TestInputContext() + : m_visible(false) + { + QInputMethodPrivate* inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod()); + inputMethodPrivate->testContext = this; + } + + ~TestInputContext() + { + QInputMethodPrivate* inputMethodPrivate = QInputMethodPrivate::get(qApp->inputMethod()); + inputMethodPrivate->testContext = 0; + } + + virtual void showInputPanel() + { + m_visible = true; + } + virtual void hideInputPanel() + { + m_visible = false; + } + virtual bool isInputPanelVisible() const + { + return m_visible; + } + + bool m_visible; +}; + +class tst_QWebEnginePage : public QObject +{ + Q_OBJECT + +public: + tst_QWebEnginePage(); + virtual ~tst_QWebEnginePage(); + +public Q_SLOTS: + void init(); + void cleanup(); + void cleanupFiles(); + +private Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + void thirdPartyCookiePolicy(); + void contextMenuCopy(); + void contextMenuPopulatedOnce(); + void acceptNavigationRequest(); + void geolocationRequestJS(); + void loadFinished(); + void actionStates(); + void popupFormSubmission(); + void acceptNavigationRequestWithNewWindow(); + void userStyleSheet(); + void userStyleSheetFromLocalFileUrl(); + void userStyleSheetFromQrcUrl(); + void loadHtml5Video(); + void modified(); + void contextMenuCrash(); + void updatePositionDependentActionsCrash(); + void database(); + void createPluginWithPluginsEnabled(); + void createPluginWithPluginsDisabled(); + void destroyPlugin_data(); + void destroyPlugin(); + void createViewlessPlugin_data(); + void createViewlessPlugin(); + void graphicsWidgetPlugin(); + void multiplePageGroupsAndLocalStorage(); + void cursorMovements(); + void textSelection(); + void textEditing(); + void backActionUpdate(); + void frameAt(); + void requestCache(); + void loadCachedPage(); + void protectBindingsRuntimeObjectsFromCollector(); + void localURLSchemes(); + void testOptionalJSObjects(); + void testLocalStorageVisibility(); + void testEnablePersistentStorage(); + void consoleOutput(); + void inputMethods_data(); + void inputMethods(); + void inputMethodsTextFormat_data(); + void inputMethodsTextFormat(); + void defaultTextEncoding(); + void errorPageExtension(); + void errorPageExtensionInIFrames(); + void errorPageExtensionInFrameset(); + void errorPageExtensionLoadFinished(); + void userAgentApplicationName(); + void userAgentNewlineStripping(); + void undoActionHaveCustomText(); + + void viewModes(); + + void crashTests_LazyInitializationOfMainFrame(); + + void screenshot_data(); + void screenshot(); + +#if defined(ENABLE_WEBGL) && ENABLE_WEBGL + void acceleratedWebGLScreenshotWithoutView(); + void unacceleratedWebGLScreenshotWithoutView(); +#endif + + void originatingObjectInNetworkRequests(); + void networkReplyParentDidntChange(); + void destroyQNAMBeforeAbortDoesntCrash(); + void testJSPrompt(); + void showModalDialog(); + void testStopScheduledPageRefresh(); + void findText(); + void supportedContentType(); + // [Qt] tst_QWebEnginePage::infiniteLoopJS() timeouts with DFG JIT + // https://bugs.webengine.org/show_bug.cgi?id=79040 + // void infiniteLoopJS(); + void navigatorCookieEnabled(); + void deleteQWebEngineViewTwice(); + void renderOnRepaintRequestedShouldNotRecurse(); + void loadSignalsOrder_data(); + void loadSignalsOrder(); + void openWindowDefaultSize(); + void cssMediaTypeGlobalSetting(); + void cssMediaTypePageSetting(); + +#ifdef Q_OS_MAC + void macCopyUnicodeToClipboard(); +#endif + +private: + QWebEngineView* m_view; + QWebEnginePage* m_page; + QString tmpDirPath() const + { + static QString tmpd = QDir::tempPath() + "/tst_qwebenginepage-" + + QDateTime::currentDateTime().toString(QLatin1String("yyyyMMddhhmmss")); + return tmpd; + } +}; + +tst_QWebEnginePage::tst_QWebEnginePage() +{ +} + +tst_QWebEnginePage::~tst_QWebEnginePage() +{ +} + +void tst_QWebEnginePage::init() +{ + m_view = new QWebEngineView(); + m_page = m_view->page(); +} + +void tst_QWebEnginePage::cleanup() +{ + delete m_view; +} + +void tst_QWebEnginePage::cleanupFiles() +{ + removeRecursive(tmpDirPath()); +} + +void tst_QWebEnginePage::initTestCase() +{ + cleanupFiles(); // In case there are old files from previous runs +} + +void tst_QWebEnginePage::cleanupTestCase() +{ + cleanupFiles(); // Be nice +} + +class NavigationRequestOverride : public QWebEnginePage +{ +public: + NavigationRequestOverride(QWebEngineView* parent, bool initialValue) : QWebEnginePage(parent), m_acceptNavigationRequest(initialValue) {} + + bool m_acceptNavigationRequest; +protected: + virtual bool acceptNavigationRequest(QWebEngineFrame* frame, const QNetworkRequest &request, QWebEnginePage::NavigationType type) { + Q_UNUSED(frame); + Q_UNUSED(request); + Q_UNUSED(type); + + return m_acceptNavigationRequest; + } +}; + +void tst_QWebEnginePage::acceptNavigationRequest() +{ +#if !defined(QWEBENGINEPAGE_ACCEPTNAVIGATIONREQUEST) + QSKIP("QWEBENGINEPAGE_ACCEPTNAVIGATIONREQUEST"); +#else + QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool))); + + NavigationRequestOverride* newPage = new NavigationRequestOverride(m_view, false); + m_view->setPage(newPage); + + m_view->setHtml(QString("
" + "
"), QUrl()); + QTRY_COMPARE(loadSpy.count(), 1); + + m_view->page()->evaluateJavaScript("tstform.submit();"); + + newPage->m_acceptNavigationRequest = true; + m_view->page()->evaluateJavaScript("tstform.submit();"); + QTRY_COMPARE(loadSpy.count(), 2); + + QCOMPARE(m_view->page()->toPlainText(), QString("foo?")); + + // Restore default page + m_view->setPage(0); +#endif +} + +#if defined(QWEBENGINEPAGE_SETFEATUREPERMISSION) +class JSTestPage : public QWebEnginePage +{ +Q_OBJECT +public: + JSTestPage(QObject* parent = 0) + : QWebEnginePage(parent) {} + + virtual bool shouldInterruptJavaScript() + { + return true; + } +public Q_SLOTS: + void requestPermission(QWebEngineFrame* frame, QWebEnginePage::Feature feature) + { + if (m_allowGeolocation) + setFeaturePermission(frame, feature, PermissionGrantedByUser); + else + setFeaturePermission(frame, feature, PermissionDeniedByUser); + } + +public: + void setGeolocationPermission(bool allow) + { + m_allowGeolocation = allow; + } + +private: + bool m_allowGeolocation; +}; +#endif + +// [Qt] tst_QWebEnginePage::infiniteLoopJS() timeouts with DFG JIT +// https://bugs.webengine.org/show_bug.cgi?id=79040 +/* +void tst_QWebEnginePage::infiniteLoopJS() +{ + JSTestPage* newPage = new JSTestPage(m_view); + m_view->setPage(newPage); + m_view->setHtml(QString("test"), QUrl()); + m_view->page()->evaluateJavaScript("var run = true; var a = 1; while (run) { a++; }"); + delete newPage; +} +*/ + +void tst_QWebEnginePage::geolocationRequestJS() +{ +#if !defined(QWEBENGINEPAGE_SETFEATUREPERMISSION) + QSKIP("QWEBENGINEPAGE_SETFEATUREPERMISSION"); +#else + JSTestPage* newPage = new JSTestPage(m_view); + + if (newPage->evaluateJavaScript(QLatin1String("!navigator.geolocation")).toBool()) { + delete newPage; + W_QSKIP("Geolocation is not supported.", SkipSingle); + } + + connect(newPage, SIGNAL(featurePermissionRequested(QWebEngineFrame*, QWebEnginePage::Feature)), + newPage, SLOT(requestPermission(QWebEngineFrame*, QWebEnginePage::Feature))); + + newPage->setGeolocationPermission(false); + m_view->setPage(newPage); + m_view->setHtml(QString("test"), QUrl()); + m_view->page()->evaluateJavaScript("var errorCode = 0; function error(err) { errorCode = err.code; } function success(pos) { } navigator.geolocation.getCurrentPosition(success, error)"); + QTest::qWait(2000); + QVariant empty = m_view->page()->evaluateJavaScript("errorCode"); + + QEXPECT_FAIL("", "https://bugs.webengine.org/show_bug.cgi?id=102235", Continue); + QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 0); + + newPage->setGeolocationPermission(true); + m_view->page()->evaluateJavaScript("errorCode = 0; navigator.geolocation.getCurrentPosition(success, error);"); + empty = m_view->page()->evaluateJavaScript("errorCode"); + + //http://dev.w3.org/geo/api/spec-source.html#position + //PositionError: const unsigned short PERMISSION_DENIED = 1; + QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 1); + delete newPage; +#endif +} + +void tst_QWebEnginePage::loadFinished() +{ + qRegisterMetaType("QNetworkRequest*"); + QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted())); + QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool))); + + m_view->page()->load(QUrl("data:text/html,foo \">" + "")); + QTRY_COMPARE(spyLoadFinished.count(), 1); + + QTRY_VERIFY(spyLoadStarted.count() > 1); + QTRY_VERIFY(spyLoadFinished.count() > 1); + + spyLoadFinished.clear(); + + m_view->page()->load(QUrl("data:text/html,")); + QTRY_COMPARE(spyLoadFinished.count(), 1); + QCOMPARE(spyLoadFinished.count(), 1); +} + +void tst_QWebEnginePage::actionStates() +{ + QWebEnginePage* page = m_view->page(); + + page->load(QUrl("qrc:///resources/script.html")); + + QAction* reloadAction = page->action(QWebEnginePage::Reload); + QAction* stopAction = page->action(QWebEnginePage::Stop); + + QTRY_VERIFY(reloadAction->isEnabled()); + QTRY_VERIFY(!stopAction->isEnabled()); +} + +class ConsolePage : public QWebEnginePage +{ +public: + ConsolePage(QObject* parent = 0) : QWebEnginePage(parent) {} + + virtual void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID) + { + messages.append(message); + lineNumbers.append(lineNumber); + sourceIDs.append(sourceID); + } + + QStringList messages; + QList lineNumbers; + QStringList sourceIDs; +}; + +void tst_QWebEnginePage::consoleOutput() +{ +#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) + QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); +#else + ConsolePage page; + page.evaluateJavaScript("this is not valid JavaScript"); + QCOMPARE(page.messages.count(), 1); + QCOMPARE(page.lineNumbers.at(0), 1); +#endif +} + +class TestPage : public QWebEnginePage { + Q_OBJECT +public: + TestPage(QObject* parent = 0) : QWebEnginePage(parent) + { + connect(this, SIGNAL(geometryChangeRequested(QRect)), this, SLOT(slotGeometryChangeRequested(QRect))); + } + + struct Navigation { + QWebEngineFrame *frame; + QNetworkRequest request; + NavigationType type; + }; + + QList navigations; + QList createdWindows; + QRect requestedGeometry; + + virtual bool acceptNavigationRequest(QWebEngineFrame* frame, const QNetworkRequest &request, NavigationType type) { + Navigation n; + n.frame = frame; + n.request = request; + n.type = type; + navigations.append(n); + return true; + } + + virtual QWebEnginePage* createWindow(WebWindowType) { + TestPage* page = new TestPage(this); + createdWindows.append(page); + return page; + } + +private Q_SLOTS: + void slotGeometryChangeRequested(const QRect& geom) { + requestedGeometry = geom; + } +}; + +void tst_QWebEnginePage::popupFormSubmission() +{ +#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) + QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); +#else + TestPage page; + page.settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true); + page.setHtml("
"\ + ""\ + "
"); + page.evaluateJavaScript("window.open('', 'myNewWin', 'width=500,height=300,toolbar=0')"); + page.evaluateJavaScript("document.form1.submit();"); + + QTest::qWait(500); + // The number of popup created should be one. + QVERIFY(page.createdWindows.size() == 1); + + QString url = page.createdWindows.takeFirst()->url().toString(); + // Check if the form submission was OK. + QEXPECT_FAIL("", "https://bugs.webengine.org/show_bug.cgi?id=118597", Continue); + QVERIFY(url.contains("?foo=bar")); +#endif +} + +void tst_QWebEnginePage::acceptNavigationRequestWithNewWindow() +{ +#if !defined(QWEBENGINESETTINGS) + QSKIP("QWEBENGINESETTINGS"); +#else + TestPage* page = new TestPage(m_view); + page->settings()->setAttribute(QWebEngineSettings::LinksIncludedInFocusChain, true); + m_page = page; + m_view->setPage(m_page); + + m_view->setUrl(QString("data:text/html,Click me")); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + QFocusEvent fe(QEvent::FocusIn); + m_page->event(&fe); + + QVERIFY(m_page->focusNextPrevChild(/*next*/ true)); + + QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); + m_page->event(&keyEnter); + + QCOMPARE(page->navigations.count(), 2); + + TestPage::Navigation n = page->navigations.at(1); + QVERIFY(!n.frame); + QCOMPARE(n.request.url().toString(), QString("data:text/html,Reached")); + QVERIFY(n.type == QWebEnginePage::NavigationTypeLinkClicked); + + QCOMPARE(page->createdWindows.count(), 1); +#endif +} + +class TestNetworkManager : public QNetworkAccessManager +{ +public: + TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {} + + QList requestedUrls; + QList requests; + +protected: + virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) { + requests.append(request); + requestedUrls.append(request.url()); + return QNetworkAccessManager::createRequest(op, request, outgoingData); + } +}; + +void tst_QWebEnginePage::userStyleSheet() +{ +#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) + QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); +#else + TestNetworkManager* networkManager = new TestNetworkManager(m_page); + m_page->setNetworkAccessManager(networkManager); + + m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css;charset=utf-8;base64," + + QByteArray("p { background-image: url('http://does.not/exist.png');}").toBase64())); + m_view->setHtml("

hello world

"); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + QVERIFY(networkManager->requestedUrls.count() >= 1); + QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png")); +#endif +} + +void tst_QWebEnginePage::userStyleSheetFromLocalFileUrl() +{ +#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) + QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); +#else + TestNetworkManager* networkManager = new TestNetworkManager(m_page); + m_page->setNetworkAccessManager(networkManager); + + QUrl styleSheetUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginepage/resources/user.css")); + m_page->settings()->setUserStyleSheetUrl(styleSheetUrl); + m_view->setHtml("

hello world

"); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + QVERIFY(networkManager->requestedUrls.count() >= 1); + QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png")); +#endif +} + +void tst_QWebEnginePage::userStyleSheetFromQrcUrl() +{ +#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER) + QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER"); +#else + TestNetworkManager* networkManager = new TestNetworkManager(m_page); + m_page->setNetworkAccessManager(networkManager); + + m_page->settings()->setUserStyleSheetUrl(QUrl("qrc:///resources/user.css")); + m_view->setHtml("

hello world

"); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + QVERIFY(networkManager->requestedUrls.count() >= 1); + QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png")); +#endif +} + +void tst_QWebEnginePage::loadHtml5Video() +{ +#if defined(WTF_USE_QT_MULTIMEDIA) && WTF_USE_QT_MULTIMEDIA + QByteArray url("http://does.not/exist?a=1%2Cb=2"); + m_view->setHtml("

"); + QTest::qWait(2000); + QUrl mUrl = DumpRenderTreeSupportQt::mediaContentUrlByElementId(m_page->mainFrame()->handle(), "video"); + QEXPECT_FAIL("", "https://bugs.webengine.org/show_bug.cgi?id=65452", Continue); + QCOMPARE(mUrl.toEncoded(), url); +#else + W_QSKIP("This test requires Qt Multimedia", SkipAll); +#endif +} + +void tst_QWebEnginePage::viewModes() +{ +#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) + QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); +#else + m_view->setHtml(""); + m_page->setProperty("_q_viewMode", "minimized"); + + QVariant empty = m_page->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webengine-view-mode)\")"); + QVERIFY(empty.type() == QVariant::Bool && empty.toBool()); + + QVariant minimized = m_page->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webengine-view-mode: minimized)\")"); + QVERIFY(minimized.type() == QVariant::Bool && minimized.toBool()); + + QVariant maximized = m_page->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webengine-view-mode: maximized)\")"); + QVERIFY(maximized.type() == QVariant::Bool && !maximized.toBool()); +#endif +} + +void tst_QWebEnginePage::modified() +{ +#if !defined(QWEBENGINEPAGE_ISMODIFIED) + QSKIP("QWEBENGINEPAGE_ISMODIFIED"); +#else + m_page->setUrl(QUrl("data:text/html,blub")); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + m_page->setUrl(QUrl("data:text/html,blah")); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + QVERIFY(!m_page->isModified()); + +// m_page->evaluateJavaScript("alert(document.getElementById('foo'))"); + m_page->evaluateJavaScript("document.getElementById('foo').focus()"); + m_page->evaluateJavaScript("document.execCommand('InsertText', true, 'Test');"); + + QVERIFY(m_page->isModified()); + + m_page->evaluateJavaScript("document.execCommand('Undo', true);"); + + QVERIFY(!m_page->isModified()); + + m_page->evaluateJavaScript("document.execCommand('Redo', true);"); + + QVERIFY(m_page->isModified()); + + QVERIFY(m_page->history()->canGoBack()); + QVERIFY(!m_page->history()->canGoForward()); + QCOMPARE(m_page->history()->count(), 2); + QVERIFY(m_page->history()->backItem().isValid()); + QVERIFY(!m_page->history()->forwardItem().isValid()); + + m_page->history()->back(); + QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool)))); + + QVERIFY(!m_page->history()->canGoBack()); + QVERIFY(m_page->history()->canGoForward()); + + QVERIFY(!m_page->isModified()); + + QVERIFY(m_page->history()->currentItemIndex() == 0); + + m_page->history()->setMaximumItemCount(3); + QVERIFY(m_page->history()->maximumItemCount() == 3); + + QVariant variant("string test"); + m_page->history()->currentItem().setUserData(variant); + QVERIFY(m_page->history()->currentItem().userData().toString() == "string test"); + + m_page->setUrl(QUrl("data:text/html,This is second page")); + m_page->setUrl(QUrl("data:text/html,This is third page")); + QVERIFY(m_page->history()->count() == 2); + m_page->setUrl(QUrl("data:text/html,This is fourth page")); + QVERIFY(m_page->history()->count() == 2); + m_page->setUrl(QUrl("data:text/html,This is fifth page")); + QVERIFY(::waitForSignal(m_page, SIGNAL(saveFrameStateRequested(QWebEngineFrame*,QWebEngineHistoryItem*)))); +#endif +} + +// https://bugs.webengine.org/show_bug.cgi?id=51331 +void tst_QWebEnginePage::updatePositionDependentActionsCrash() +{ +#if !defined(QWEBENGINEPAGE_UPDATEPOSITIONDEPENDENTACTIONS) + QSKIP("QWEBENGINEPAGE_UPDATEPOSITIONDEPENDENTACTIONS"); +#else + QWebEngineView view; + view.setHtml("

test"); + QPoint pos(0, 0); + view.page()->updatePositionDependentActions(pos); + QMenu* contextMenu = 0; + foreach (QObject* child, view.children()) { + contextMenu = qobject_cast(child); + if (contextMenu) + break; + } + QVERIFY(!contextMenu); +#endif +} + +// https://bugs.webengine.org/show_bug.cgi?id=20357 +void tst_QWebEnginePage::contextMenuCrash() +{ +#if !defined(QWEBENGINEPAGE_SWALLOWCONTEXTMENUEVENT) + QSKIP("QWEBENGINEPAGE_SWALLOWCONTEXTMENUEVENT"); +#else + QWebEngineView view; + view.setHtml("

test"); + QPoint pos(0, 0); + QContextMenuEvent event(QContextMenuEvent::Mouse, pos); + view.page()->swallowContextMenuEvent(&event); + view.page()->updatePositionDependentActions(pos); + QMenu* contextMenu = 0; + foreach (QObject* child, view.children()) { + contextMenu = qobject_cast(child); + if (contextMenu) + break; + } + QVERIFY(contextMenu); + delete contextMenu; +#endif +} + +void tst_QWebEnginePage::database() +{ +#if !defined(QWEBENGINEDATABASE) + QSKIP("QWEBENGINEDATABASE"); +#else + QString path = tmpDirPath(); + m_page->settings()->setOfflineStoragePath(path); + QVERIFY(m_page->settings()->offlineStoragePath() == path); + + QWebEngineSettings::setOfflineStorageDefaultQuota(1024 * 1024); + QVERIFY(QWebEngineSettings::offlineStorageDefaultQuota() == 1024 * 1024); + + m_page->settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true); + m_page->settings()->setAttribute(QWebEngineSettings::OfflineStorageDatabaseEnabled, true); + + QString dbFileName = path + "Databases.db"; + + if (QFile::exists(dbFileName)) + QFile::remove(dbFileName); + + qRegisterMetaType("QWebEngineFrame*"); + QSignalSpy spy(m_page, SIGNAL(databaseQuotaExceeded(QWebEngineFrame*,QString))); + m_view->setHtml(QString("

"), QUrl("http://www.myexample.com")); + QTRY_COMPARE(spy.count(), 1); + m_page->evaluateJavaScript("var db2; db2=openDatabase('testdb', '1.0', 'test database API', 50000);"); + QTRY_COMPARE(spy.count(),1); + + m_page->evaluateJavaScript("localStorage.test='This is a test for local storage';"); + m_view->setHtml(QString("text"), QUrl("http://www.myexample.com")); + + QVariant s1 = m_page->evaluateJavaScript("localStorage.test"); + QCOMPARE(s1.toString(), QString("This is a test for local storage")); + + m_page->evaluateJavaScript("sessionStorage.test='This is a test for session storage';"); + m_view->setHtml(QString("text"), QUrl("http://www.myexample.com")); + QVariant s2 = m_page->evaluateJavaScript("sessionStorage.test"); + QCOMPARE(s2.toString(), QString("This is a test for session storage")); + + m_view->setHtml(QString("
"), QUrl("http://www.myexample.com")); + m_page->evaluateJavaScript("var db3; db3=openDatabase('testdb', '1.0', 'test database API', 50000);db3.transaction(function(tx) { tx.executeSql('CREATE TABLE IF NOT EXISTS Test (text TEXT)', []); }, function(tx, result) { }, function(tx, error) { });"); + QTest::qWait(200); + + // Remove all databases. + QWebEngineSecurityOrigin origin = m_page->mainFrame()->securityOrigin(); + QList dbs = origin.databases(); + for (int i = 0; i < dbs.count(); i++) { + QString fileName = dbs[i].fileName(); + QVERIFY(QFile::exists(fileName)); + QWebEngineDatabase::removeDatabase(dbs[i]); + QVERIFY(!QFile::exists(fileName)); + } + QVERIFY(!origin.databases().size()); + // Remove removed test :-) + QWebEngineDatabase::removeAllDatabases(); + QVERIFY(!origin.databases().size()); +#endif +} + +#if defined(QWEBENGINEPAGE_CREATEPLUGIN) +class PluginPage : public QWebEnginePage +{ +public: + PluginPage(QObject *parent = 0) + : QWebEnginePage(parent) {} + + struct CallInfo + { + CallInfo(const QString &c, const QUrl &u, + const QStringList &pn, const QStringList &pv, + QObject *r) + : classid(c), url(u), paramNames(pn), + paramValues(pv), returnValue(r) + {} + QString classid; + QUrl url; + QStringList paramNames; + QStringList paramValues; + QObject *returnValue; + }; + + QList calls; + +protected: + virtual QObject *createPlugin(const QString &classid, const QUrl &url, + const QStringList ¶mNames, + const QStringList ¶mValues) + { + QObject *result = 0; + if (classid == "pushbutton") + result = new QPushButton(); +#ifndef QT_NO_INPUTDIALOG + else if (classid == "lineedit") + result = new QLineEdit(); +#endif + else if (classid == "graphicswidget") + result = new QGraphicsWidget(); + if (result) + result->setObjectName(classid); + calls.append(CallInfo(classid, url, paramNames, paramValues, result)); + return result; + } +}; + +static void createPlugin(QWebEngineView *view) +{ + QSignalSpy loadSpy(view, SIGNAL(loadFinished(bool))); + + PluginPage* newPage = new PluginPage(view); + view->setPage(newPage); + + // type has to be application/x-qt-plugin + view->setHtml(QString("")); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(newPage->calls.count(), 0); + + view->setHtml(QString("")); + QTRY_COMPARE(loadSpy.count(), 2); + QCOMPARE(newPage->calls.count(), 1); + { + PluginPage::CallInfo ci = newPage->calls.takeFirst(); + QCOMPARE(ci.classid, QString::fromLatin1("pushbutton")); + QCOMPARE(ci.url, QUrl()); + QCOMPARE(ci.paramNames.count(), 3); + QCOMPARE(ci.paramValues.count(), 3); + QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); + QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); + QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); + QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton")); + QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); + QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton")); + QVERIFY(ci.returnValue != 0); + QVERIFY(ci.returnValue->inherits("QPushButton")); + } + // test JS bindings + QCOMPARE(newPage->evaluateJavaScript("document.getElementById('mybutton').toString()").toString(), + QString::fromLatin1("[object HTMLObjectElement]")); + QCOMPARE(newPage->evaluateJavaScript("mybutton.toString()").toString(), + QString::fromLatin1("[object HTMLObjectElement]")); + QCOMPARE(newPage->evaluateJavaScript("typeof mybutton.objectName").toString(), + QString::fromLatin1("string")); + QCOMPARE(newPage->evaluateJavaScript("mybutton.objectName").toString(), + QString::fromLatin1("pushbutton")); + QCOMPARE(newPage->evaluateJavaScript("typeof mybutton.clicked").toString(), + QString::fromLatin1("function")); + QCOMPARE(newPage->evaluateJavaScript("mybutton.clicked.toString()").toString(), + QString::fromLatin1("function clicked() {\n [native code]\n}")); + + view->setHtml(QString("" + "" + "" + "
"), QUrl("http://foo.bar.baz")); + QTRY_COMPARE(loadSpy.count(), 3); + QCOMPARE(newPage->calls.count(), 2); + { + PluginPage::CallInfo ci = newPage->calls.takeFirst(); + QCOMPARE(ci.classid, QString::fromLatin1("lineedit")); + QCOMPARE(ci.url, QUrl()); + QCOMPARE(ci.paramNames.count(), 3); + QCOMPARE(ci.paramValues.count(), 3); + QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); + QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); + QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); + QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("lineedit")); + QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); + QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("myedit")); + QVERIFY(ci.returnValue != 0); + QVERIFY(ci.returnValue->inherits("QLineEdit")); + } + { + PluginPage::CallInfo ci = newPage->calls.takeFirst(); + QCOMPARE(ci.classid, QString::fromLatin1("pushbutton")); + QCOMPARE(ci.url, QUrl()); + QCOMPARE(ci.paramNames.count(), 3); + QCOMPARE(ci.paramValues.count(), 3); + QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); + QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); + QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); + QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton")); + QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); + QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton")); + QVERIFY(ci.returnValue != 0); + QVERIFY(ci.returnValue->inherits("QPushButton")); + } +} +#endif + +void tst_QWebEnginePage::graphicsWidgetPlugin() +{ +#if !defined(QWEBENGINEPAGE_CREATEPLUGIN) + QSKIP("QWEBENGINEPAGE_CREATEPLUGIN"); +#else + m_view->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); + QGraphicsWebView webView; + + QSignalSpy loadSpy(&webView, SIGNAL(loadFinished(bool))); + + PluginPage* newPage = new PluginPage(&webView); + webView.setPage(newPage); + + // type has to be application/x-qt-plugin + webView.setHtml(QString("")); + QTRY_COMPARE(loadSpy.count(), 1); + QCOMPARE(newPage->calls.count(), 0); + + webView.setHtml(QString("")); + QTRY_COMPARE(loadSpy.count(), 2); + QCOMPARE(newPage->calls.count(), 1); + { + PluginPage::CallInfo ci = newPage->calls.takeFirst(); + QCOMPARE(ci.classid, QString::fromLatin1("graphicswidget")); + QCOMPARE(ci.url, QUrl()); + QCOMPARE(ci.paramNames.count(), 3); + QCOMPARE(ci.paramValues.count(), 3); + QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type")); + QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin")); + QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid")); + QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("graphicswidget")); + QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id")); + QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mygraphicswidget")); + QVERIFY(ci.returnValue); + QVERIFY(ci.returnValue->inherits("QGraphicsWidget")); + } + // test JS bindings + QCOMPARE(newPage->evaluateJavaScript("document.getElementById('mygraphicswidget').toString()").toString(), + QString::fromLatin1("[object HTMLObjectElement]")); + QCOMPARE(newPage->evaluateJavaScript("mygraphicswidget.toString()").toString(), + QString::fromLatin1("[object HTMLObjectElement]")); + QCOMPARE(newPage->evaluateJavaScript("typeof mygraphicswidget.objectName").toString(), + QString::fromLatin1("string")); + QCOMPARE(newPage->evaluateJavaScript("mygraphicswidget.objectName").toString(), + QString::fromLatin1("graphicswidget")); + QCOMPARE(newPage->evaluateJavaScript("typeof mygraphicswidget.geometryChanged").toString(), + QString::fromLatin1("function")); + QCOMPARE(newPage->evaluateJavaScript("mygraphicswidget.geometryChanged.toString()").toString(), + QString::fromLatin1("function geometryChanged() {\n [native code]\n}")); +#endif +} + +void tst_QWebEnginePage::createPluginWithPluginsEnabled() +{ +#if !defined(QWEBENGINEPAGE_CREATEPLUGIN) + QSKIP("QWEBENGINEPAGE_CREATEPLUGIN"); +#else + m_view->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); + createPlugin(m_view); +#endif +} + +void tst_QWebEnginePage::createPluginWithPluginsDisabled() +{ +#if !defined(QWEBENGINEPAGE_CREATEPLUGIN) + QSKIP("QWEBENGINEPAGE_CREATEPLUGIN"); +#else + // Qt Plugins should be loaded by QtWebEngine even when PluginsEnabled is + // false. The client decides whether a Qt plugin is enabled or not when + // it decides whether or not to instantiate it. + m_view->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, false); + createPlugin(m_view); +#endif +} + +#if defined(QWEBENGINEPAGE_CREATEPLUGIN) +// Standard base class for template PluginTracerPage. In tests it is used as interface. +class PluginCounterPage : public QWebEnginePage { +public: + int m_count; + QPointer m_widget; + QObject* m_pluginParent; + PluginCounterPage(QObject* parent = 0) + : QWebEnginePage(parent) + , m_count(0) + , m_pluginParent(0) + { + settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true); + } + ~PluginCounterPage() + { + if (m_pluginParent) + m_pluginParent->deleteLater(); + } +}; + +template +class PluginTracerPage : public PluginCounterPage { +public: + PluginTracerPage(QObject* parent = 0) + : PluginCounterPage(parent) + { + // this is a dummy parent object for the created plugin + m_pluginParent = new T; + } + virtual QObject* createPlugin(const QString&, const QUrl&, const QStringList&, const QStringList&) + { + m_count++; + m_widget = new T; + // need a cast to the specific type, as QObject::setParent cannot be called, + // because it is not virtual. Instead it is necessary to call QWidget::setParent, + // which also takes a QWidget* instead of a QObject*. Therefore we need to + // upcast to T*, which is a QWidget. + static_cast(m_widget.data())->setParent(static_cast(m_pluginParent)); + return m_widget.data(); + } +}; + +class PluginFactory { +public: + enum FactoredType {QWidgetType, QGraphicsWidgetType}; + static PluginCounterPage* create(FactoredType type, QObject* parent = 0) + { + PluginCounterPage* result = 0; + switch (type) { + case QWidgetType: + result = new PluginTracerPage(parent); + break; + case QGraphicsWidgetType: + result = new PluginTracerPage(parent); + break; + default: {/*Oops*/}; + } + return result; + } + + static void prepareTestData() + { + QTest::addColumn("type"); + QTest::newRow("QWidget") << (int)PluginFactory::QWidgetType; + QTest::newRow("QGraphicsWidget") << (int)PluginFactory::QGraphicsWidgetType; + } +}; +#endif + +void tst_QWebEnginePage::destroyPlugin_data() +{ +#if defined(QWEBENGINEPAGE_CREATEPLUGIN) + PluginFactory::prepareTestData(); +#endif +} + +void tst_QWebEnginePage::destroyPlugin() +{ +#if !defined(QWEBENGINEPAGE_CREATEPLUGIN) + QSKIP("QWEBENGINEPAGE_CREATEPLUGIN"); +#else + QFETCH(int, type); + PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type, m_view); + m_view->setPage(page); + + // we create the plugin, so the widget should be constructed + QString content(""); + m_view->setHtml(content); + QVERIFY(page->m_widget); + QCOMPARE(page->m_count, 1); + + // navigate away, the plugin widget should be destructed + m_view->setHtml("Hi"); + QTestEventLoop::instance().enterLoop(1); + QVERIFY(!page->m_widget); +#endif +} + +void tst_QWebEnginePage::createViewlessPlugin_data() +{ +#if defined(QWEBENGINEPAGE_CREATEPLUGIN) + PluginFactory::prepareTestData(); +#endif +} + +void tst_QWebEnginePage::createViewlessPlugin() +{ +#if !defined(QWEBENGINEPAGE_CREATEPLUGIN) + QSKIP("QWEBENGINEPAGE_CREATEPLUGIN"); +#else + QFETCH(int, type); + PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type); + QString content(""); + page->setHtml(content); + QCOMPARE(page->m_count, 1); + QVERIFY(page->m_widget); + QVERIFY(page->m_pluginParent); + QVERIFY(page->m_widget.data()->parent() == page->m_pluginParent); + delete page; +#endif +} + +void tst_QWebEnginePage::multiplePageGroupsAndLocalStorage() +{ +#if !defined(QWEBENGINESETTINGS_SETLOCALSTORAGEPATH) + QSKIP("QWEBENGINESETTINGS_SETLOCALSTORAGEPATH"); +#else + QDir dir(tmpDirPath()); + dir.mkdir("path1"); + dir.mkdir("path2"); + + QWebEngineView view1; + QWebEngineView view2; + + view1.page()->settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true); + view1.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(tmpDirPath() + "/path1")); + DumpRenderTreeSupportQt::webPageSetGroupName(view1.page()->handle(), "group1"); + view2.page()->settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, true); + view2.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(tmpDirPath() + "/path2")); + DumpRenderTreeSupportQt::webPageSetGroupName(view2.page()->handle(), "group2"); + QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view1.page()->handle()), QString("group1")); + QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view2.page()->handle()), QString("group2")); + + + view1.setHtml(QString(" "), QUrl("http://www.myexample.com")); + view2.setHtml(QString(" "), QUrl("http://www.myexample.com")); + + view1.page()->evaluateJavaScript("localStorage.test='value1';"); + view2.page()->evaluateJavaScript("localStorage.test='value2';"); + + view1.setHtml(QString(" "), QUrl("http://www.myexample.com")); + view2.setHtml(QString(" "), QUrl("http://www.myexample.com")); + + QVariant s1 = view1.page()->evaluateJavaScript("localStorage.test"); + QCOMPARE(s1.toString(), QString("value1")); + + QVariant s2 = view2.page()->evaluateJavaScript("localStorage.test"); + QCOMPARE(s2.toString(), QString("value2")); + + QTest::qWait(1000); + + QFile::remove(QDir::toNativeSeparators(tmpDirPath() + "/path1/http_www.myexample.com_0.localstorage")); + QFile::remove(QDir::toNativeSeparators(tmpDirPath() + "/path2/http_www.myexample.com_0.localstorage")); + dir.rmdir(QDir::toNativeSeparators("./path1")); + dir.rmdir(QDir::toNativeSeparators("./path2")); +#endif +} + +#if defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) +class CursorTrackedPage : public QWebEnginePage +{ +public: + + CursorTrackedPage(QWidget *parent = 0): QWebEnginePage(parent) { + setViewportSize(QSize(1024, 768)); // big space + } + + QString selectedText() { + return evaluateJavaScript("window.getSelection().toString()").toString(); + } + + int selectionStartOffset() { + return evaluateJavaScript("window.getSelection().getRangeAt(0).startOffset").toInt(); + } + + int selectionEndOffset() { + return evaluateJavaScript("window.getSelection().getRangeAt(0).endOffset").toInt(); + } + + // true if start offset == end offset, i.e. no selected text + int isSelectionCollapsed() { + return evaluateJavaScript("window.getSelection().getRangeAt(0).collapsed").toBool(); + } +}; +#endif + +void tst_QWebEnginePage::cursorMovements() +{ +#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) + QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); +#else + CursorTrackedPage* page = new CursorTrackedPage; + QString content("

The quick brown fox

jumps over the lazy dog

May the source
be with you!

"); + page->setHtml(content); + + // this will select the first paragraph + QString script = "var range = document.createRange(); " \ + "var node = document.getElementById(\"one\"); " \ + "range.selectNode(node); " \ + "getSelection().addRange(range);"; + page->evaluateJavaScript(script); + QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); + + QRegExp regExp(" style=\".*\""); + regExp.setMinimal(true); + QCOMPARE(page->selectedHtml().trimmed().replace(regExp, ""), QString::fromLatin1("

The quick brown fox

")); + + // these actions must exist + QVERIFY(page->action(QWebEnginePage::MoveToNextChar) != 0); + QVERIFY(page->action(QWebEnginePage::MoveToPreviousChar) != 0); + QVERIFY(page->action(QWebEnginePage::MoveToNextWord) != 0); + QVERIFY(page->action(QWebEnginePage::MoveToPreviousWord) != 0); + QVERIFY(page->action(QWebEnginePage::MoveToNextLine) != 0); + QVERIFY(page->action(QWebEnginePage::MoveToPreviousLine) != 0); + QVERIFY(page->action(QWebEnginePage::MoveToStartOfLine) != 0); + QVERIFY(page->action(QWebEnginePage::MoveToEndOfLine) != 0); + QVERIFY(page->action(QWebEnginePage::MoveToStartOfBlock) != 0); + QVERIFY(page->action(QWebEnginePage::MoveToEndOfBlock) != 0); + QVERIFY(page->action(QWebEnginePage::MoveToStartOfDocument) != 0); + QVERIFY(page->action(QWebEnginePage::MoveToEndOfDocument) != 0); + + // right now they are disabled because contentEditable is false + QCOMPARE(page->action(QWebEnginePage::MoveToNextChar)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::MoveToPreviousChar)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::MoveToNextWord)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::MoveToPreviousWord)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::MoveToNextLine)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::MoveToPreviousLine)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::MoveToStartOfLine)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::MoveToEndOfLine)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::MoveToStartOfBlock)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::MoveToEndOfBlock)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::MoveToStartOfDocument)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::MoveToEndOfDocument)->isEnabled(), false); + + // make it editable before navigating the cursor + page->setContentEditable(true); + + // here the actions are enabled after contentEditable is true + QCOMPARE(page->action(QWebEnginePage::MoveToNextChar)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::MoveToPreviousChar)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::MoveToNextWord)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::MoveToPreviousWord)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::MoveToNextLine)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::MoveToPreviousLine)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::MoveToStartOfLine)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::MoveToEndOfLine)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::MoveToStartOfBlock)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::MoveToEndOfBlock)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::MoveToStartOfDocument)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::MoveToEndOfDocument)->isEnabled(), true); + + // cursor will be before the word "jump" + page->triggerAction(QWebEnginePage::MoveToNextChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // cursor will be between 'j' and 'u' in the word "jump" + page->triggerAction(QWebEnginePage::MoveToNextChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 1); + + // cursor will be between 'u' and 'm' in the word "jump" + page->triggerAction(QWebEnginePage::MoveToNextChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 2); + + // cursor will be after the word "jump" + page->triggerAction(QWebEnginePage::MoveToNextWord); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 5); + + // cursor will be after the word "lazy" + page->triggerAction(QWebEnginePage::MoveToNextWord); + page->triggerAction(QWebEnginePage::MoveToNextWord); + page->triggerAction(QWebEnginePage::MoveToNextWord); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 19); + + // cursor will be between 'z' and 'y' in "lazy" + page->triggerAction(QWebEnginePage::MoveToPreviousChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 18); + + // cursor will be between 'a' and 'z' in "lazy" + page->triggerAction(QWebEnginePage::MoveToPreviousChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 17); + + // cursor will be before the word "lazy" + page->triggerAction(QWebEnginePage::MoveToPreviousWord); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 15); + + // cursor will be before the word "quick" + page->triggerAction(QWebEnginePage::MoveToPreviousWord); + page->triggerAction(QWebEnginePage::MoveToPreviousWord); + page->triggerAction(QWebEnginePage::MoveToPreviousWord); + page->triggerAction(QWebEnginePage::MoveToPreviousWord); + page->triggerAction(QWebEnginePage::MoveToPreviousWord); + page->triggerAction(QWebEnginePage::MoveToPreviousWord); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 4); + + // cursor will be between 'p' and 's' in the word "jumps" + page->triggerAction(QWebEnginePage::MoveToNextWord); + page->triggerAction(QWebEnginePage::MoveToNextWord); + page->triggerAction(QWebEnginePage::MoveToNextWord); + page->triggerAction(QWebEnginePage::MoveToNextChar); + page->triggerAction(QWebEnginePage::MoveToNextChar); + page->triggerAction(QWebEnginePage::MoveToNextChar); + page->triggerAction(QWebEnginePage::MoveToNextChar); + page->triggerAction(QWebEnginePage::MoveToNextChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 4); + + // cursor will be before the word "jumps" + page->triggerAction(QWebEnginePage::MoveToStartOfLine); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // cursor will be after the word "dog" + page->triggerAction(QWebEnginePage::MoveToEndOfLine); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 23); + + // cursor will be between 'w' and 'n' in "brown" + page->triggerAction(QWebEnginePage::MoveToStartOfLine); + page->triggerAction(QWebEnginePage::MoveToPreviousWord); + page->triggerAction(QWebEnginePage::MoveToPreviousWord); + page->triggerAction(QWebEnginePage::MoveToNextChar); + page->triggerAction(QWebEnginePage::MoveToNextChar); + page->triggerAction(QWebEnginePage::MoveToNextChar); + page->triggerAction(QWebEnginePage::MoveToNextChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 14); + + // cursor will be after the word "fox" + page->triggerAction(QWebEnginePage::MoveToEndOfLine); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 19); + + // cursor will be before the word "The" + page->triggerAction(QWebEnginePage::MoveToStartOfDocument); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // cursor will be after the word "you!" + page->triggerAction(QWebEnginePage::MoveToEndOfDocument); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 12); + + // cursor will be before the word "be" + page->triggerAction(QWebEnginePage::MoveToStartOfBlock); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // cursor will be after the word "you!" + page->triggerAction(QWebEnginePage::MoveToEndOfBlock); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 12); + + // try to move before the document start + page->triggerAction(QWebEnginePage::MoveToStartOfDocument); + page->triggerAction(QWebEnginePage::MoveToPreviousChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + page->triggerAction(QWebEnginePage::MoveToStartOfDocument); + page->triggerAction(QWebEnginePage::MoveToPreviousWord); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // try to move past the document end + page->triggerAction(QWebEnginePage::MoveToEndOfDocument); + page->triggerAction(QWebEnginePage::MoveToNextChar); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 12); + page->triggerAction(QWebEnginePage::MoveToEndOfDocument); + page->triggerAction(QWebEnginePage::MoveToNextWord); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 12); + + delete page; +#endif +} + +void tst_QWebEnginePage::textSelection() +{ +#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) + QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); +#else + CursorTrackedPage* page = new CursorTrackedPage; + QString content("

The quick brown fox

" \ + "

jumps over the lazy dog

" \ + "

May the source
be with you!

"); + page->setHtml(content); + + // these actions must exist + QVERIFY(page->action(QWebEnginePage::SelectAll) != 0); + QVERIFY(page->action(QWebEnginePage::SelectNextChar) != 0); + QVERIFY(page->action(QWebEnginePage::SelectPreviousChar) != 0); + QVERIFY(page->action(QWebEnginePage::SelectNextWord) != 0); + QVERIFY(page->action(QWebEnginePage::SelectPreviousWord) != 0); + QVERIFY(page->action(QWebEnginePage::SelectNextLine) != 0); + QVERIFY(page->action(QWebEnginePage::SelectPreviousLine) != 0); + QVERIFY(page->action(QWebEnginePage::SelectStartOfLine) != 0); + QVERIFY(page->action(QWebEnginePage::SelectEndOfLine) != 0); + QVERIFY(page->action(QWebEnginePage::SelectStartOfBlock) != 0); + QVERIFY(page->action(QWebEnginePage::SelectEndOfBlock) != 0); + QVERIFY(page->action(QWebEnginePage::SelectStartOfDocument) != 0); + QVERIFY(page->action(QWebEnginePage::SelectEndOfDocument) != 0); + + // right now they are disabled because contentEditable is false and + // there isn't an existing selection to modify + QCOMPARE(page->action(QWebEnginePage::SelectNextChar)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SelectPreviousChar)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SelectNextWord)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SelectPreviousWord)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SelectNextLine)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SelectPreviousLine)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SelectStartOfLine)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SelectEndOfLine)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SelectStartOfBlock)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SelectEndOfBlock)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SelectStartOfDocument)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), false); + + // ..but SelectAll is awalys enabled + QCOMPARE(page->action(QWebEnginePage::SelectAll)->isEnabled(), true); + + // Verify hasSelection returns false since there is no selection yet... + QCOMPARE(page->hasSelection(), false); + + // this will select the first paragraph + QString selectScript = "var range = document.createRange(); " \ + "var node = document.getElementById(\"one\"); " \ + "range.selectNode(node); " \ + "getSelection().addRange(range);"; + page->evaluateJavaScript(selectScript); + QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox")); + QRegExp regExp(" style=\".*\""); + regExp.setMinimal(true); + QCOMPARE(page->selectedHtml().trimmed().replace(regExp, ""), QString::fromLatin1("

The quick brown fox

")); + + // Make sure hasSelection returns true, since there is selected text now... + QCOMPARE(page->hasSelection(), true); + + // here the actions are enabled after a selection has been created + QCOMPARE(page->action(QWebEnginePage::SelectNextChar)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectPreviousChar)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectNextWord)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectPreviousWord)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectNextLine)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectPreviousLine)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectStartOfLine)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectEndOfLine)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectStartOfBlock)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectEndOfBlock)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectStartOfDocument)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), true); + + // make it editable before navigating the cursor + page->setContentEditable(true); + + // cursor will be before the word "The", this makes sure there is a charet + page->triggerAction(QWebEnginePage::MoveToStartOfDocument); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // here the actions are enabled after contentEditable is true + QCOMPARE(page->action(QWebEnginePage::SelectNextChar)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectPreviousChar)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectNextWord)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectPreviousWord)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectNextLine)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectPreviousLine)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectStartOfLine)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectEndOfLine)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectStartOfBlock)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectEndOfBlock)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectStartOfDocument)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SelectEndOfDocument)->isEnabled(), true); + + delete page; +#endif +} + +void tst_QWebEnginePage::textEditing() +{ +#if !defined(QWEBENGINEPAGE_EVALUATEJAVASCRIPT) + QSKIP("QWEBENGINEPAGE_EVALUATEJAVASCRIPT"); +#else + CursorTrackedPage* page = new CursorTrackedPage; + QString content("

The quick brown fox

" \ + "

jumps over the lazy dog

" \ + "

May the source
be with you!

"); + page->setHtml(content); + + // these actions must exist + QVERIFY(page->action(QWebEnginePage::Cut) != 0); + QVERIFY(page->action(QWebEnginePage::Copy) != 0); + QVERIFY(page->action(QWebEnginePage::Paste) != 0); + QVERIFY(page->action(QWebEnginePage::DeleteStartOfWord) != 0); + QVERIFY(page->action(QWebEnginePage::DeleteEndOfWord) != 0); + QVERIFY(page->action(QWebEnginePage::SetTextDirectionDefault) != 0); + QVERIFY(page->action(QWebEnginePage::SetTextDirectionLeftToRight) != 0); + QVERIFY(page->action(QWebEnginePage::SetTextDirectionRightToLeft) != 0); + QVERIFY(page->action(QWebEnginePage::ToggleBold) != 0); + QVERIFY(page->action(QWebEnginePage::ToggleItalic) != 0); + QVERIFY(page->action(QWebEnginePage::ToggleUnderline) != 0); + QVERIFY(page->action(QWebEnginePage::InsertParagraphSeparator) != 0); + QVERIFY(page->action(QWebEnginePage::InsertLineSeparator) != 0); + QVERIFY(page->action(QWebEnginePage::PasteAndMatchStyle) != 0); + QVERIFY(page->action(QWebEnginePage::RemoveFormat) != 0); + QVERIFY(page->action(QWebEnginePage::ToggleStrikethrough) != 0); + QVERIFY(page->action(QWebEnginePage::ToggleSubscript) != 0); + QVERIFY(page->action(QWebEnginePage::ToggleSuperscript) != 0); + QVERIFY(page->action(QWebEnginePage::InsertUnorderedList) != 0); + QVERIFY(page->action(QWebEnginePage::InsertOrderedList) != 0); + QVERIFY(page->action(QWebEnginePage::Indent) != 0); + QVERIFY(page->action(QWebEnginePage::Outdent) != 0); + QVERIFY(page->action(QWebEnginePage::AlignCenter) != 0); + QVERIFY(page->action(QWebEnginePage::AlignJustified) != 0); + QVERIFY(page->action(QWebEnginePage::AlignLeft) != 0); + QVERIFY(page->action(QWebEnginePage::AlignRight) != 0); + + // right now they are disabled because contentEditable is false + QCOMPARE(page->action(QWebEnginePage::Cut)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::Paste)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::DeleteStartOfWord)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::DeleteEndOfWord)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SetTextDirectionDefault)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SetTextDirectionLeftToRight)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::SetTextDirectionRightToLeft)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::ToggleBold)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::ToggleItalic)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::ToggleUnderline)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::InsertParagraphSeparator)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::InsertLineSeparator)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::PasteAndMatchStyle)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::RemoveFormat)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::ToggleStrikethrough)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::ToggleSubscript)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::ToggleSuperscript)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::InsertUnorderedList)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::InsertOrderedList)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::Indent)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::Outdent)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::AlignCenter)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::AlignJustified)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::AlignLeft)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::AlignRight)->isEnabled(), false); + + // Select everything + page->triggerAction(QWebEnginePage::SelectAll); + + // make sure it is enabled since there is a selection + QCOMPARE(page->action(QWebEnginePage::Copy)->isEnabled(), true); + + // make it editable before navigating the cursor + page->setContentEditable(true); + + // clear the selection + page->triggerAction(QWebEnginePage::MoveToStartOfDocument); + QVERIFY(page->isSelectionCollapsed()); + QCOMPARE(page->selectionStartOffset(), 0); + + // make sure it is disabled since there isn't a selection + QCOMPARE(page->action(QWebEnginePage::Copy)->isEnabled(), false); + + // here the actions are enabled after contentEditable is true + QCOMPARE(page->action(QWebEnginePage::Paste)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::DeleteStartOfWord)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::DeleteEndOfWord)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SetTextDirectionDefault)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SetTextDirectionLeftToRight)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::SetTextDirectionRightToLeft)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::ToggleBold)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::ToggleItalic)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::ToggleUnderline)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::InsertParagraphSeparator)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::InsertLineSeparator)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::PasteAndMatchStyle)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::ToggleStrikethrough)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::ToggleSubscript)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::ToggleSuperscript)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::InsertUnorderedList)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::InsertOrderedList)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::Indent)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::Outdent)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::AlignCenter)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::AlignJustified)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::AlignLeft)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::AlignRight)->isEnabled(), true); + + // make sure these are disabled since there isn't a selection + QCOMPARE(page->action(QWebEnginePage::Cut)->isEnabled(), false); + QCOMPARE(page->action(QWebEnginePage::RemoveFormat)->isEnabled(), false); + + // make sure everything is selected + page->triggerAction(QWebEnginePage::SelectAll); + + // this is only true if there is an editable selection + QCOMPARE(page->action(QWebEnginePage::Cut)->isEnabled(), true); + QCOMPARE(page->action(QWebEnginePage::RemoveFormat)->isEnabled(), true); + + delete page; +#endif +} + +void tst_QWebEnginePage::requestCache() +{ + TestPage page; + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + + page.setUrl(QString("data:text/html,Click me")); + QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(page.navigations.count(), 1); + + page.setUrl(QString("data:text/html,Click me2")); + QTRY_COMPARE(loadSpy.count(), 2); + QTRY_COMPARE(page.navigations.count(), 2); + + page.triggerAction(QWebEnginePage::Stop); + QVERIFY(page.history()->canGoBack()); + page.triggerAction(QWebEnginePage::Back); + + QTRY_COMPARE(loadSpy.count(), 3); + QTRY_COMPARE(page.navigations.count(), 3); + QCOMPARE(page.navigations.at(0).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(), + (int)QNetworkRequest::PreferNetwork); + QCOMPARE(page.navigations.at(1).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(), + (int)QNetworkRequest::PreferNetwork); + QCOMPARE(page.navigations.at(2).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(), + (int)QNetworkRequest::PreferCache); +} + +void tst_QWebEnginePage::loadCachedPage() +{ +#if !defined(QWEBENGINESETTINGS) + QSKIP("QWEBENGINESETTINGS"); +#else + TestPage page; + QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool))); + page.settings()->setMaximumPagesInCache(3); + + page.load(QUrl("data:text/html,This is first page")); + + QTRY_COMPARE(loadSpy.count(), 1); + QTRY_COMPARE(page.navigations.count(), 1); + + QUrl firstPageUrl = page.url(); + page.load(QUrl("data:text/html,This is second page")); + + QTRY_COMPARE(loadSpy.count(), 2); + QTRY_COMPARE(page.navigations.count(), 2); + + page.triggerAction(QWebEnginePage::Stop); + QVERIFY(page.history()->canGoBack()); + + QSignalSpy urlSpy(&page, SIGNAL(urlChanged(QUrl))); + QVERIFY(urlSpy.isValid()); + + page.triggerAction(QWebEnginePage::Back); + ::waitForSignal(&page, SIGNAL(urlChanged(QUrl))); + QCOMPARE(urlSpy.size(), 1); + + QList arguments1 = urlSpy.takeFirst(); + QCOMPARE(arguments1.at(0).toUrl(), firstPageUrl); +#endif +} + +void tst_QWebEnginePage::backActionUpdate() +{ + QWebEngineView view; + QWebEnginePage *page = view.page(); + QAction *action = page->action(QWebEnginePage::Back); + QVERIFY(!action->isEnabled()); + QSignalSpy loadSpy(page, SIGNAL(loadFinished(bool))); + QUrl url = QUrl("qrc:///resources/framedindex.html"); + page->load(url); + QTRY_COMPARE(loadSpy.count(), 1); + QVERIFY(!action->isEnabled()); + QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(10, 10)); + QTRY_COMPARE(loadSpy.count(), 2); + + QVERIFY(action->isEnabled()); +} + +#if defined(QWEBENGINEFRAME) +void frameAtHelper(QWebEnginePage* webPage, QWebEngineFrame* webFrame, QPoint framePosition) +{ + if (!webFrame) + return; + + framePosition += QPoint(webFrame->pos()); + QList children = webFrame->childFrames(); + for (int i = 0; i < children.size(); ++i) { + if (children.at(i)->childFrames().size() > 0) + frameAtHelper(webPage, children.at(i), framePosition); + + QRect frameRect(children.at(i)->pos() + framePosition, children.at(i)->geometry().size()); + QVERIFY(children.at(i) == webPage->frameAt(frameRect.topLeft())); + } +} +#endif + +void tst_QWebEnginePage::frameAt() +{ +#if !defined(QWEBENGINEFRAME) + QSKIP("QWEBENGINEFRAME"); +#else + QWebEngineView webView; + QWebEnginePage* webPage = webView.page(); + QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool))); + QUrl url = QUrl("qrc:///resources/iframe.html"); + webPage->load(url); + QTRY_COMPARE(loadSpy.count(), 1); + frameAtHelper(webPage, webPage->mainFrame(), webPage->mainFrame()->pos()); +#endif +} + +void tst_QWebEnginePage::inputMethods_data() +{ + QTest::addColumn("viewType"); + QTest::newRow("QWebEngineView") << "QWebEngineView"; + QTest::newRow("QGraphicsWebView") << "QGraphicsWebView"; +} + +static Qt::InputMethodHints inputMethodHints(QObject* object) +{ + if (QGraphicsObject* o = qobject_cast(object)) + return o->inputMethodHints(); + if (QWidget* w = qobject_cast(object)) + return w->inputMethodHints(); + return Qt::InputMethodHints(); +} + +static bool inputMethodEnabled(QObject* object) +{ + if (QGraphicsObject* o = qobject_cast(object)) + return o->flags() & QGraphicsItem::ItemAcceptsInputMethod; + if (QWidget* w = qobject_cast(object)) + return w->testAttribute(Qt::WA_InputMethodEnabled); + return false; +} + +static void clickOnPage(QWebEnginePage* page, const QPoint& position) +{ + QMouseEvent evpres(QEvent::MouseButtonPress, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier); + page->event(&evpres); + QMouseEvent evrel(QEvent::MouseButtonRelease, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier); + page->event(&evrel); +} + +void tst_QWebEnginePage::inputMethods() +{ +#if !defined(QWEBENGINEPAGE_INPUTMETHODQUERY) + QSKIP("QWEBENGINEPAGE_INPUTMETHODQUERY"); +#else + QFETCH(QString, viewType); + QWebEnginePage* page = new QWebEnginePage; + QObject* view = 0; + QObject* container = 0; + if (viewType == "QWebEngineView") { + QWebEngineView* wv = new QWebEngineView; + wv->setPage(page); + view = wv; + container = view; + } else if (viewType == "QGraphicsWebView") { + QGraphicsWebView* wv = new QGraphicsWebView; + wv->setPage(page); + view = wv; + + QGraphicsView* gv = new QGraphicsView; + QGraphicsScene* scene = new QGraphicsScene(gv); + gv->setScene(scene); + scene->addItem(wv); + wv->setGeometry(QRect(0, 0, 500, 500)); + + container = gv; + } else + QVERIFY2(false, "Unknown view type"); + + page->settings()->setFontFamily(QWebEngineSettings::SerifFont, page->settings()->fontFamily(QWebEngineSettings::FixedFont)); + page->setHtml("" \ + "
" \ + "" \ + ""); + page->mainFrame()->setFocus(); + + TestInputContext testContext; + + QWebEngineElementCollection inputs = page->mainFrame()->documentElement().findAll("input"); + QPoint textInputCenter = inputs.at(0).geometry().center(); + + clickOnPage(page, textInputCenter); + + // This part of the test checks if the SIP (Software Input Panel) is triggered, + // which normally happens on mobile platforms, when a user input form receives + // a mouse click. + int inputPanel = 0; + if (viewType == "QWebEngineView") { + if (QWebEngineView* wv = qobject_cast(view)) + inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel); + } else if (viewType == "QGraphicsWebView") { + if (QGraphicsWebView* wv = qobject_cast(view)) + inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel); + } + + // For non-mobile platforms RequestSoftwareInputPanel event is not called + // because there is no SIP (Software Input Panel) triggered. In the case of a + // mobile platform, an input panel, e.g. virtual keyboard, is usually invoked + // and the RequestSoftwareInputPanel event is called. For these two situations + // this part of the test can verified as the checks below. + if (inputPanel) + QVERIFY(testContext.isInputPanelVisible()); + else + QVERIFY(!testContext.isInputPanelVisible()); + testContext.hideInputPanel(); + + clickOnPage(page, textInputCenter); + QVERIFY(testContext.isInputPanelVisible()); + + //ImMicroFocus + QVariant variant = page->inputMethodQuery(Qt::ImMicroFocus); + QVERIFY(inputs.at(0).geometry().contains(variant.toRect().topLeft())); + + // We assigned the serif font famility to be the same as the fixef font family. + // Then test ImFont on a serif styled element, we should get our fixef font family. + variant = page->inputMethodQuery(Qt::ImFont); + QFont font = variant.value(); + QCOMPARE(page->settings()->fontFamily(QWebEngineSettings::FixedFont), font.family()); + + QList inputAttributes; + + //Insert text. + { + QInputMethodEvent eventText("QtWebEngine", inputAttributes); + QSignalSpy signalSpy(page, SIGNAL(microFocusChanged())); + page->event(&eventText); + QCOMPARE(signalSpy.count(), 0); + } + + { + QInputMethodEvent eventText("", inputAttributes); + eventText.setCommitString(QString("QtWebEngine"), 0, 0); + page->event(&eventText); + } + + //ImMaximumTextLength + variant = page->inputMethodQuery(Qt::ImMaximumTextLength); + QCOMPARE(20, variant.toInt()); + + //Set selection + inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant()); + QInputMethodEvent eventSelection("",inputAttributes); + page->event(&eventSelection); + + //ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + int anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 3); + + //ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + int cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 5); + + //ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + QString selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("eb")); + + //Set selection with negative length + inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 6, -5, QVariant()); + QInputMethodEvent eventSelection3("",inputAttributes); + page->event(&eventSelection3); + + //ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 1); + + //ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 6); + + //ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("tWebK")); + + //ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + QString value = variant.value(); + QCOMPARE(value, QString("QtWebEngine")); + + { + QList attributes; + // Clear the selection, so the next test does not clear any contents. + QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant()); + attributes.append(newSelection); + QInputMethodEvent event("composition", attributes); + page->event(&event); + } + + // A ongoing composition should not change the surrounding text before it is committed. + variant = page->inputMethodQuery(Qt::ImSurroundingText); + value = variant.value(); + QCOMPARE(value, QString("QtWebEngine")); + + // Cancel current composition first + inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant()); + QInputMethodEvent eventSelection4("", inputAttributes); + page->event(&eventSelection4); + + // START - Tests for Selection when the Editor is NOT in Composition mode + + // LEFT to RIGHT selection + // Deselect the selection by sending MouseButtonPress events + // This moves the current cursor to the end of the text + clickOnPage(page, textInputCenter); + + { + QList attributes; + QInputMethodEvent event(QString(), attributes); + event.setCommitString("XXX", 0, 0); + page->event(&event); + event.setCommitString(QString(), -2, 2); // Erase two characters. + page->event(&event); + event.setCommitString(QString(), -1, 1); // Erase one character. + page->event(&event); + variant = page->inputMethodQuery(Qt::ImSurroundingText); + value = variant.value(); + QCOMPARE(value, QString("QtWebEngine")); + } + + //Move to the start of the line + page->triggerAction(QWebEnginePage::MoveToStartOfLine); + + QKeyEvent keyRightEventPress(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier); + QKeyEvent keyRightEventRelease(QEvent::KeyRelease, Qt::Key_Right, Qt::NoModifier); + + //Move 2 characters RIGHT + for (int j = 0; j < 2; ++j) { + page->event(&keyRightEventPress); + page->event(&keyRightEventRelease); + } + + //Select to the end of the line + page->triggerAction(QWebEnginePage::SelectEndOfLine); + + //ImAnchorPosition QtWebEngine + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 2); + + //ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 8); + + //ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("WebKit")); + + //RIGHT to LEFT selection + //Deselect the selection (this moves the current cursor to the end of the text) + clickOnPage(page, textInputCenter); + + //ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 8); + + //ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 8); + + //ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("")); + + QKeyEvent keyLeftEventPress(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier); + QKeyEvent keyLeftEventRelease(QEvent::KeyRelease, Qt::Key_Left, Qt::NoModifier); + + //Move 2 characters LEFT + for (int i = 0; i < 2; ++i) { + page->event(&keyLeftEventPress); + page->event(&keyLeftEventRelease); + } + + //Select to the start of the line + page->triggerAction(QWebEnginePage::SelectStartOfLine); + + //ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 6); + + //ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 0); + + //ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("QtWebK")); + + //END - Tests for Selection when the Editor is not in Composition mode + + //ImhHiddenText + QPoint passwordInputCenter = inputs.at(1).geometry().center(); + clickOnPage(page, passwordInputCenter); + + QVERIFY(inputMethodEnabled(view)); + QVERIFY(inputMethodHints(view) & Qt::ImhHiddenText); + + clickOnPage(page, textInputCenter); + QVERIFY(!(inputMethodHints(view) & Qt::ImhHiddenText)); + + page->setHtml("

nothing to input here"); + testContext.hideInputPanel(); + + QWebEngineElement para = page->mainFrame()->findFirstElement("p"); + clickOnPage(page, para.geometry().center()); + + QVERIFY(!testContext.isInputPanelVisible()); + + //START - Test for sending empty QInputMethodEvent + page->setHtml("" \ + "" \ + ""); + page->evaluateJavaScript("var inputEle = document.getElementById('input3'); inputEle.focus(); inputEle.select();"); + + //Send empty QInputMethodEvent + QInputMethodEvent emptyEvent; + page->event(&emptyEvent); + + QString inputValue = page->evaluateJavaScript("document.getElementById('input3').value").toString(); + QCOMPARE(inputValue, QString("QtWebEngine2")); + //END - Test for sending empty QInputMethodEvent + + page->setHtml("" \ + "" \ + ""); + page->evaluateJavaScript("var inputEle = document.getElementById('input4'); inputEle.focus(); inputEle.select();"); + + // Clear the selection, also cancel the ongoing composition if there is one. + { + QList attributes; + QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant()); + attributes.append(newSelection); + QInputMethodEvent event("", attributes); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("")); + + variant = page->inputMethodQuery(Qt::ImSurroundingText); + QString surroundingValue = variant.value(); + QCOMPARE(surroundingValue, QString("QtWebEngine inputMethod")); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 0); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 0); + + // 1. Insert a character to the beginning of the line. + // Send temporary text, which makes the editor has composition 'm'. + { + QList attributes; + QInputMethodEvent event("m", attributes); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value(); + QCOMPARE(surroundingValue, QString("QtWebEngine inputMethod")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 0); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 0); + + // Send temporary text, which makes the editor has composition 'n'. + { + QList attributes; + QInputMethodEvent event("n", attributes); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value(); + QCOMPARE(surroundingValue, QString("QtWebEngine inputMethod")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 0); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 0); + + // Send commit text, which makes the editor conforms composition. + { + QList attributes; + QInputMethodEvent event("", attributes); + event.setCommitString("o"); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value(); + QCOMPARE(surroundingValue, QString("oQtWebEngine inputMethod")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 1); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 1); + + // 2. insert a character to the middle of the line. + // Send temporary text, which makes the editor has composition 'd'. + { + QList attributes; + QInputMethodEvent event("d", attributes); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value(); + QCOMPARE(surroundingValue, QString("oQtWebEngine inputMethod")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 1); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 1); + + // Send commit text, which makes the editor conforms composition. + { + QList attributes; + QInputMethodEvent event("", attributes); + event.setCommitString("e"); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value(); + QCOMPARE(surroundingValue, QString("oeQtWebEngine inputMethod")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 2); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 2); + + // 3. Insert a character to the end of the line. + page->triggerAction(QWebEnginePage::MoveToEndOfLine); + + // Send temporary text, which makes the editor has composition 't'. + { + QList attributes; + QInputMethodEvent event("t", attributes); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value(); + QCOMPARE(surroundingValue, QString("oeQtWebEngine inputMethod")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 22); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 22); + + // Send commit text, which makes the editor conforms composition. + { + QList attributes; + QInputMethodEvent event("", attributes); + event.setCommitString("t"); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value(); + QCOMPARE(surroundingValue, QString("oeQtWebEngine inputMethodt")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 23); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 23); + + // 4. Replace the selection. + page->triggerAction(QWebEnginePage::SelectPreviousWord); + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("inputMethodt")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value(); + QCOMPARE(surroundingValue, QString("oeQtWebEngine inputMethodt")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 11); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 23); + + // Send temporary text, which makes the editor has composition 'w'. + { + QList attributes; + QInputMethodEvent event("w", attributes); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value(); + QCOMPARE(surroundingValue, QString("oeQtWebEngine ")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 11); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 11); + + // Send commit text, which makes the editor conforms composition. + { + QList attributes; + QInputMethodEvent event("", attributes); + event.setCommitString("2"); + page->event(&event); + } + + // ImCurrentSelection + variant = page->inputMethodQuery(Qt::ImCurrentSelection); + selectionValue = variant.value(); + QCOMPARE(selectionValue, QString("")); + + // ImSurroundingText + variant = page->inputMethodQuery(Qt::ImSurroundingText); + surroundingValue = variant.value(); + QCOMPARE(surroundingValue, QString("oeQtWebEngine 2")); + + // ImCursorPosition + variant = page->inputMethodQuery(Qt::ImCursorPosition); + cursorPosition = variant.toInt(); + QCOMPARE(cursorPosition, 12); + + // ImAnchorPosition + variant = page->inputMethodQuery(Qt::ImAnchorPosition); + anchorPosition = variant.toInt(); + QCOMPARE(anchorPosition, 12); + + // Check sending RequestSoftwareInputPanel event + page->setHtml("" \ + "" \ + "

abc
"\ + ""); + QWebEngineElement inputElement = page->mainFrame()->findFirstElement("div"); + clickOnPage(page, inputElement.geometry().center()); + + QVERIFY(!testContext.isInputPanelVisible()); + + // START - Newline test for textarea + qApp->processEvents(); + page->setHtml("" \ + "