An error occurred while loading the file. Please try again.
-
Jocelyn Turcotte authored
A few changes to the API: - Return the success result asynchronously. - FindWrapsAroundDocument and HighlightAllOccurrences are enabled by defaults and cannot be disabled. - Found text isn't updating the selection on the page like QtWebKit did, but triggers a separate state not available. A find count and current index could be exposed, but isn't in this case to keep the API delta lower. This also adds the possibility to pass bool results through the CallbackDirectory and add a new tst_QWebEnginePage::findTextResult test since the old test relied on the selection to be updated when the searched text is found. Change-Id: I8189b5aea8d832df183c6c1ae03e3f08198a9c45 Reviewed-by:
Simon Hausmann <simon.hausmann@digia.com>
324706a5
/*
Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in>
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 <QClipboard>
#include <QDir>
#include <QGraphicsWidget>
#include <QLineEdit>
#include <QMainWindow>
#include <QMenu>
#include <QMimeDatabase>
#include <QPushButton>
#include <QStateMachine>
#include <QStyle>
#include <QtTest/QtTest>
#include <QTextCharFormat>
#include <private/qinputmethod_p.h>
#include <qnetworkcookiejar.h>
#include <qnetworkreply.h>
#include <qnetworkrequest.h>
#include <qpa/qplatforminputcontext.h>
#include <qwebenginehistory.h>
#include <qwebenginepage.h>
#include <qwebengineview.h>
#include <qimagewriter.h>
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;
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
}
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();
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
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 renderWidgetHostViewNotShowTopLevel();
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 findTextResult();
void supportedContentType();
// [Qt] tst_QWebEnginePage::infiniteLoopJS() timeouts with DFG JIT
// https://bugs.webkit.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()
{
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
}
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("<html><body><form name='tstform' action='data:text/html,foo'method='get'>"
"<input type='text'><input type='submit'></form></body></html>"), QUrl());
QTRY_COMPARE(loadSpy.count(), 1);
evaluateJavaScriptSync(m_view->page(), "tstform.submit();");
newPage->m_acceptNavigationRequest = true;
evaluateJavaScriptSync(m_view->page(), "tstform.submit();");
QTRY_COMPARE(loadSpy.count(), 2);
QCOMPARE(m_view->page()->toPlainText(), QString("foo?"));
// Restore default page
m_view->setPage(0);
#endif
}
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
#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.webkit.org/show_bug.cgi?id=79040
/*
void tst_QWebEnginePage::infiniteLoopJS()
{
JSTestPage* newPage = new JSTestPage(m_view);
m_view->setPage(newPage);
m_view->setHtml(QString("<html><body>test</body></html>"), 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 (evaluateJavaScriptSync(newPage, 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("<html><body>test</body></html>"), QUrl());
evaluateJavaScriptSync(m_view->page(), "var errorCode = 0; function error(err) { errorCode = err.code; } function success(pos) { } navigator.geolocation.getCurrentPosition(success, error)");
QTest::qWait(2000);
QVariant empty = evaluateJavaScriptSync(m_view->page(), "errorCode");
QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=102235", Continue);
QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 0);
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
newPage->setGeolocationPermission(true);
evaluateJavaScriptSync(m_view->page(), "errorCode = 0; navigator.geolocation.getCurrentPosition(success, error);");
empty = evaluateJavaScriptSync(m_view->page(), "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*>("QNetworkRequest*");
QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted()));
QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
m_view->page()->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
"<head><meta http-equiv='refresh' content='1'></head>foo \">"
"<frame src=\"data:text/html,bar\"></frameset>"));
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,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
"foo \"><frame src=\"data:text/html,bar\"></frameset>"));
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(int level, const QString& message, int lineNumber, const QString& sourceID)
{
levels.append(level);
messages.append(message);
lineNumbers.append(lineNumber);
sourceIDs.append(sourceID);
}
QList<int> levels;
QStringList messages;
QList<int> lineNumbers;
QStringList sourceIDs;
};
void tst_QWebEnginePage::consoleOutput()
{
ConsolePage page;
// We don't care about the result but want this to be synchronous
421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
evaluateJavaScriptSync(&page, "this is not valid JavaScript");
QCOMPARE(page.messages.count(), 1);
QCOMPARE(page.lineNumbers.at(0), 1);
}
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<Navigation> navigations;
QList<TestPage*> 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_SETTINGS)
QSKIP("QWEBENGINEPAGE_SETTINGS");
#else
TestPage page;
page.settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
page.setHtml("<form name=form1 method=get action='' target=myNewWin>"\
"<input type=hidden name=foo value='bar'>"\
"</form>");
page.runJavaScript("window.open('', 'myNewWin', 'width=500,height=300,toolbar=0')");
evaluateJavaScriptSync(&page, "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.webkit.org/show_bug.cgi?id=118597", Continue);
QVERIFY(url.contains("?foo=bar"));
#endif
}
void tst_QWebEnginePage::acceptNavigationRequestWithNewWindow()
{
491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
#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,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
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<QUrl> requestedUrls;
QList<QNetworkRequest> 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("<p>hello world</p>");
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);
561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
m_page->setNetworkAccessManager(networkManager);
QUrl styleSheetUrl = QUrl::fromLocalFile(TESTS_SOURCE_DIR + QLatin1String("qwebenginepage/resources/user.css"));
m_page->settings()->setUserStyleSheetUrl(styleSheetUrl);
m_view->setHtml("<p>hello world</p>");
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("<p>hello world</p>");
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("<p><video id ='video' src='" + url + "' autoplay/></p>");
QTest::qWait(2000);
QUrl mUrl = DumpRenderTreeSupportQt::mediaContentUrlByElementId(m_page->mainFrame()->handle(), "video");
QEXPECT_FAIL("", "https://bugs.webkit.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_VIEW_MODES)
QSKIP("QWEBENGINEPAGE_VIEW_MODES");
#else
m_view->setHtml("<body></body>");
m_page->setProperty("_q_viewMode", "minimized");
QVariant empty = evaluateJavaScriptSync(m_page, "window.styleMedia.matchMedium(\"(-webengine-view-mode)\")");
QVERIFY(empty.type() == QVariant::Bool && empty.toBool());
QVariant minimized = evaluateJavaScriptSync(m_page, "window.styleMedia.matchMedium(\"(-webengine-view-mode: minimized)\")");
QVERIFY(minimized.type() == QVariant::Bool && minimized.toBool());
QVariant maximized = evaluateJavaScriptSync(m_page, "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,<body>blub"));
QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700
m_page->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah"));
QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
QVERIFY(!m_page->isModified());
m_page->runJavaScript("document.getElementById('foo').focus()");
evaluateJavaScriptSync(m_page, "document.execCommand('InsertText', true, 'Test');");
QVERIFY(m_page->isModified());
evaluateJavaScriptSync(m_page, "document.execCommand('Undo', true);");
QVERIFY(!m_page->isModified());
evaluateJavaScriptSync(m_page, "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());
QCOMPARE(m_page->history()->currentItemIndex(), 0);
m_page->history()->setMaximumItemCount(3);
QCOMPARE(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,<body>This is second page"));
m_page->setUrl(QUrl("data:text/html,<body>This is third page"));
QCOMPARE(m_page->history()->count(), 2);
m_page->setUrl(QUrl("data:text/html,<body>This is fourth page"));
QCOMPARE(m_page->history()->count(), 2);
m_page->setUrl(QUrl("data:text/html,<body>This is fifth page"));
QVERIFY(::waitForSignal(m_page, SIGNAL(saveFrameStateRequested(QWebEngineFrame*,QWebEngineHistoryItem*))));
#endif
}
// https://bugs.webkit.org/show_bug.cgi?id=51331
void tst_QWebEnginePage::updatePositionDependentActionsCrash()
{
#if !defined(QWEBENGINEPAGE_UPDATEPOSITIONDEPENDENTACTIONS)
QSKIP("QWEBENGINEPAGE_UPDATEPOSITIONDEPENDENTACTIONS");
#else
QWebEngineView view;
view.setHtml("<p>test");
QPoint pos(0, 0);
view.page()->updatePositionDependentActions(pos);
QMenu* contextMenu = 0;
foreach (QObject* child, view.children()) {
contextMenu = qobject_cast<QMenu*>(child);
if (contextMenu)
break;
}
QVERIFY(!contextMenu);
#endif
}
701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770
// https://bugs.webkit.org/show_bug.cgi?id=20357
void tst_QWebEnginePage::contextMenuCrash()
{
#if !defined(QWEBENGINEPAGE_SWALLOWCONTEXTMENUEVENT)
QSKIP("QWEBENGINEPAGE_SWALLOWCONTEXTMENUEVENT");
#else
QWebEngineView view;
view.setHtml("<p>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<QMenu*>(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*>("QWebEngineFrame*");
QSignalSpy spy(m_page, SIGNAL(databaseQuotaExceeded(QWebEngineFrame*,QString)));
m_view->setHtml(QString("<html><head><script>var db; db=openDatabase('testdb', '1.0', 'test database API', 50000); </script></head><body><div></div></body></html>"), QUrl("http://www.myexample.com"));
QTRY_COMPARE(spy.count(), 1);
evaluateJavaScriptSync(m_page, "var db2; db2=openDatabase('testdb', '1.0', 'test database API', 50000);");
QTRY_COMPARE(spy.count(),1);
evaluateJavaScriptSync(m_page, "localStorage.test='This is a test for local storage';");
m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
QVariant s1 = evaluateJavaScriptSync(m_page, "localStorage.test");
QCOMPARE(s1.toString(), QString("This is a test for local storage"));
evaluateJavaScriptSync(m_page, "sessionStorage.test='This is a test for session storage';");
m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
QVariant s2 = evaluateJavaScriptSync(m_page, "sessionStorage.test");
QCOMPARE(s2.toString(), QString("This is a test for session storage"));
m_view->setHtml(QString("<html><head></head><body><div></div></body></html>"), QUrl("http://www.myexample.com"));
evaluateJavaScriptSync(m_page, "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<QWebEngineDatabase> dbs = origin.databases();
for (int i = 0; i < dbs.count(); i++) {
771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
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<CallInfo> 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("<html><body><object type='application/x-foobarbaz' classid='pushbutton' id='mybutton'/></body></html>"));
QTRY_COMPARE(loadSpy.count(), 1);
QCOMPARE(newPage->calls.count(), 0);
view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>"));
841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
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(evaluateJavaScriptSync(newPage, "document.getElementById('mybutton').toString()").toString(),
QString::fromLatin1("[object HTMLObjectElement]"));
QCOMPARE(evaluateJavaScriptSync(newPage, "mybutton.toString()").toString(),
QString::fromLatin1("[object HTMLObjectElement]"));
QCOMPARE(evaluateJavaScriptSync(newPage, "typeof mybutton.objectName").toString(),
QString::fromLatin1("string"));
QCOMPARE(evaluateJavaScriptSync(newPage, "mybutton.objectName").toString(),
QString::fromLatin1("pushbutton"));
QCOMPARE(evaluateJavaScriptSync(newPage, "typeof mybutton.clicked").toString(),
QString::fromLatin1("function"));
QCOMPARE(evaluateJavaScriptSync(newPage, "mybutton.clicked.toString()").toString(),
QString::fromLatin1("function clicked() {\n [native code]\n}"));
view->setHtml(QString("<html><body><table>"
"<tr><object type='application/x-qt-plugin' classid='lineedit' id='myedit'/></tr>"
"<tr><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></tr>"
"</table></body></html>"), 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
911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980
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("<html><body><object type='application/x-foobarbaz' classid='graphicswidget' id='mygraphicswidget'/></body></html>"));
QTRY_COMPARE(loadSpy.count(), 1);
QCOMPARE(newPage->calls.count(), 0);
webView.setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='graphicswidget' id='mygraphicswidget'/></body></html>"));
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(evaluateJavaScriptSync(newPage, "document.getElementById('mygraphicswidget').toString()").toString(),
QString::fromLatin1("[object HTMLObjectElement]"));
QCOMPARE(evaluateJavaScriptSync(newPage, "mygraphicswidget.toString()").toString(),
QString::fromLatin1("[object HTMLObjectElement]"));
QCOMPARE(evaluateJavaScriptSync(newPage, "typeof mygraphicswidget.objectName").toString(),
QString::fromLatin1("string"));
QCOMPARE(evaluateJavaScriptSync(newPage, "mygraphicswidget.objectName").toString(),
QString::fromLatin1("graphicswidget"));
QCOMPARE(evaluateJavaScriptSync(newPage, "typeof mygraphicswidget.geometryChanged").toString(),
QString::fromLatin1("function"));
QCOMPARE(evaluateJavaScriptSync(newPage, "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.
981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
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<QObject> 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 T>
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<T*>(m_widget.data())->setParent(static_cast<T*>(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<QWidget>(parent);
break;
case QGraphicsWidgetType:
result = new PluginTracerPage<QGraphicsWidget>(parent);
break;
default: {/*Oops*/};
}
return result;
}
static void prepareTestData()
{
QTest::addColumn<int>("type");
QTest::newRow("QWidget") << (int)PluginFactory::QWidgetType;
1051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120
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("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
m_view->setHtml(content);
QVERIFY(page->m_widget);
QCOMPARE(page->m_count, 1);
// navigate away, the plugin widget should be destructed
m_view->setHtml("<html><body>Hi</body></html>");
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("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
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;
1121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
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("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
evaluateJavaScriptSync(view1.page(), "localStorage.test='value1';");
evaluateJavaScriptSync(view2.page(), "localStorage.test='value2';");
view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
QVariant s1 = evaluateJavaScriptSync(view1.page(), "localStorage.test");
QCOMPARE(s1.toString(), QString("value1"));
QVariant s2 = evaluateJavaScriptSync(view2.page(), "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
}
class CursorTrackedPage : public QWebEnginePage
{
public:
CursorTrackedPage(QWidget *parent = 0): QWebEnginePage(parent) {
setViewportSize(QSize(1024, 768)); // big space
}
QString selectedText() {
return evaluateJavaScriptSync(this, "window.getSelection().toString()").toString();
}
int selectionStartOffset() {
return evaluateJavaScriptSync(this, "window.getSelection().getRangeAt(0).startOffset").toInt();
}
int selectionEndOffset() {
return evaluateJavaScriptSync(this, "window.getSelection().getRangeAt(0).endOffset").toInt();
}
// true if start offset == end offset, i.e. no selected text
int isSelectionCollapsed() {
return evaluateJavaScriptSync(this, "window.getSelection().getRangeAt(0).collapsed").toBool();
}
};
void tst_QWebEnginePage::cursorMovements()
{
#if !defined(QWEBENGINEPAGE_SELECTEDTEXT)
QSKIP("QWEBENGINEPAGE_SELECTEDTEXT");
#else
CursorTrackedPage* page = new CursorTrackedPage;
QString content("<html><body><p id=one>The quick brown fox</p><p id=two>jumps over the lazy dog</p><p>May the source<br/>be with you!</p></body></html>");
page->setHtml(content);
// this will select the first paragraph
1191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260
QString script = "var range = document.createRange(); " \
"var node = document.getElementById(\"one\"); " \
"range.selectNode(node); " \
"getSelection().addRange(range);";
evaluateJavaScriptSync(page, 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("<p id=\"one\">The quick brown fox</p>"));
// 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);
1261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330
// 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);
1331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
// 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("<html><body><p id=one>The quick brown fox</p>" \
"<p id=two>jumps over the lazy dog</p>" \
"<p>May the source<br/>be with you!</p></body></html>");
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);
1401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470
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);";
evaluateJavaScriptSync(page, 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("<p id=\"one\">The quick brown fox</p>"));
// 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);
1471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540
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("<html><body><p id=one>The quick brown fox</p>" \
"<p id=two>jumps over the lazy dog</p>" \
"<p>May the source<br/>be with you!</p></body></html>");
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);
1541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610
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,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
1611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680
QTRY_COMPARE(loadSpy.count(), 1);
QTRY_COMPARE(page.navigations.count(), 1);
page.setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me2</a>"));
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<QVariant> 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);
1681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750
QVERIFY(action->isEnabled());
}
#if defined(QWEBENGINEFRAME)
void frameAtHelper(QWebEnginePage* webPage, QWebEngineFrame* webFrame, QPoint framePosition)
{
if (!webFrame)
return;
framePosition += QPoint(webFrame->pos());
QList<QWebEngineFrame*> 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<QString>("viewType");
QTest::newRow("QWebEngineView") << "QWebEngineView";
QTest::newRow("QGraphicsWebView") << "QGraphicsWebView";
}
static Qt::InputMethodHints inputMethodHints(QObject* object)
{
if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
return o->inputMethodHints();
if (QWidget* w = qobject_cast<QWidget*>(object))
return w->inputMethodHints();
return Qt::InputMethodHints();
}
static bool inputMethodEnabled(QObject* object)
{
if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
return o->flags() & QGraphicsItem::ItemAcceptsInputMethod;
if (QWidget* w = qobject_cast<QWidget*>(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()
1751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820
{
#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("<html><body>" \
"<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/><br>" \
"<input type='password'/>" \
"</body></html>");
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<QWebEngineView*>(view))
inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel);
} else if (viewType == "QGraphicsWebView") {
if (QGraphicsWebView* wv = qobject_cast<QGraphicsWebView*>(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);
1821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890
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<QFont>();
QCOMPARE(page->settings()->fontFamily(QWebEngineSettings::FixedFont), font.family());
QList<QInputMethodEvent::Attribute> 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<QString>();
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<QString>();
QCOMPARE(selectionValue, QString("tWebK"));
//ImSurroundingText
variant = page->inputMethodQuery(Qt::ImSurroundingText);
1891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960
QString value = variant.value<QString>();
QCOMPARE(value, QString("QtWebEngine"));
{
QList<QInputMethodEvent::Attribute> 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<QString>();
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<QInputMethodEvent::Attribute> 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<QString>();
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);
1961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030
selectionValue = variant.value<QString>();
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<QString>();
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<QString>();
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("<html><body><p>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
2031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100
page->setHtml("<html><body>" \
"<input type='text' id='input3' value='QtWebEngine2'/>" \
"</body></html>");
evaluateJavaScriptSync(page, "var inputEle = document.getElementById('input3'); inputEle.focus(); inputEle.select();");
//Send empty QInputMethodEvent
QInputMethodEvent emptyEvent;
page->event(&emptyEvent);
QString inputValue = evaluateJavaScriptSync(page, "document.getElementById('input3').value").toString();
QCOMPARE(inputValue, QString("QtWebEngine2"));
//END - Test for sending empty QInputMethodEvent
page->setHtml("<html><body>" \
"<input type='text' id='input4' value='QtWebEngine inputMethod'/>" \
"</body></html>");
evaluateJavaScriptSync(page, "var inputEle = document.getElementById('input4'); inputEle.focus(); inputEle.select();");
// Clear the selection, also cancel the ongoing composition if there is one.
{
QList<QInputMethodEvent::Attribute> 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<QString>();
QCOMPARE(selectionValue, QString(""));
variant = page->inputMethodQuery(Qt::ImSurroundingText);
QString surroundingValue = variant.value<QString>();
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<QInputMethodEvent::Attribute> attributes;
QInputMethodEvent event("m", attributes);
page->event(&event);
}
// ImCurrentSelection
variant = page->inputMethodQuery(Qt::ImCurrentSelection);
selectionValue = variant.value<QString>();
QCOMPARE(selectionValue, QString(""));
// ImSurroundingText
variant = page->inputMethodQuery(Qt::ImSurroundingText);
surroundingValue = variant.value<QString>();
QCOMPARE(surroundingValue, QString("QtWebEngine inputMethod"));
// ImCursorPosition
variant = page->inputMethodQuery(Qt::ImCursorPosition);
cursorPosition = variant.toInt();
QCOMPARE(cursorPosition, 0);
// ImAnchorPosition
2101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170
variant = page->inputMethodQuery(Qt::ImAnchorPosition);
anchorPosition = variant.toInt();
QCOMPARE(anchorPosition, 0);
// Send temporary text, which makes the editor has composition 'n'.
{
QList<QInputMethodEvent::Attribute> attributes;
QInputMethodEvent event("n", attributes);
page->event(&event);
}
// ImCurrentSelection
variant = page->inputMethodQuery(Qt::ImCurrentSelection);
selectionValue = variant.value<QString>();
QCOMPARE(selectionValue, QString(""));
// ImSurroundingText
variant = page->inputMethodQuery(Qt::ImSurroundingText);
surroundingValue = variant.value<QString>();
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<QInputMethodEvent::Attribute> attributes;
QInputMethodEvent event("", attributes);
event.setCommitString("o");
page->event(&event);
}
// ImCurrentSelection
variant = page->inputMethodQuery(Qt::ImCurrentSelection);
selectionValue = variant.value<QString>();
QCOMPARE(selectionValue, QString(""));
// ImSurroundingText
variant = page->inputMethodQuery(Qt::ImSurroundingText);
surroundingValue = variant.value<QString>();
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<QInputMethodEvent::Attribute> attributes;
QInputMethodEvent event("d", attributes);
page->event(&event);
}
// ImCurrentSelection
variant = page->inputMethodQuery(Qt::ImCurrentSelection);
selectionValue = variant.value<QString>();
2171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240
QCOMPARE(selectionValue, QString(""));
// ImSurroundingText
variant = page->inputMethodQuery(Qt::ImSurroundingText);
surroundingValue = variant.value<QString>();
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<QInputMethodEvent::Attribute> attributes;
QInputMethodEvent event("", attributes);
event.setCommitString("e");
page->event(&event);
}
// ImCurrentSelection
variant = page->inputMethodQuery(Qt::ImCurrentSelection);
selectionValue = variant.value<QString>();
QCOMPARE(selectionValue, QString(""));
// ImSurroundingText
variant = page->inputMethodQuery(Qt::ImSurroundingText);
surroundingValue = variant.value<QString>();
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<QInputMethodEvent::Attribute> attributes;
QInputMethodEvent event("t", attributes);
page->event(&event);
}
// ImCurrentSelection
variant = page->inputMethodQuery(Qt::ImCurrentSelection);
selectionValue = variant.value<QString>();
QCOMPARE(selectionValue, QString(""));
// ImSurroundingText
variant = page->inputMethodQuery(Qt::ImSurroundingText);
surroundingValue = variant.value<QString>();
QCOMPARE(surroundingValue, QString("oeQtWebEngine inputMethod"));
// ImCursorPosition
variant = page->inputMethodQuery(Qt::ImCursorPosition);
cursorPosition = variant.toInt();
QCOMPARE(cursorPosition, 22);
2241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310
// ImAnchorPosition
variant = page->inputMethodQuery(Qt::ImAnchorPosition);
anchorPosition = variant.toInt();
QCOMPARE(anchorPosition, 22);
// Send commit text, which makes the editor conforms composition.
{
QList<QInputMethodEvent::Attribute> attributes;
QInputMethodEvent event("", attributes);
event.setCommitString("t");
page->event(&event);
}
// ImCurrentSelection
variant = page->inputMethodQuery(Qt::ImCurrentSelection);
selectionValue = variant.value<QString>();
QCOMPARE(selectionValue, QString(""));
// ImSurroundingText
variant = page->inputMethodQuery(Qt::ImSurroundingText);
surroundingValue = variant.value<QString>();
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<QString>();
QCOMPARE(selectionValue, QString("inputMethodt"));
// ImSurroundingText
variant = page->inputMethodQuery(Qt::ImSurroundingText);
surroundingValue = variant.value<QString>();
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<QInputMethodEvent::Attribute> attributes;
QInputMethodEvent event("w", attributes);
page->event(&event);
}
// ImCurrentSelection
variant = page->inputMethodQuery(Qt::ImCurrentSelection);
selectionValue = variant.value<QString>();
QCOMPARE(selectionValue, QString(""));
// ImSurroundingText
variant = page->inputMethodQuery(Qt::ImSurroundingText);
2311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380
surroundingValue = variant.value<QString>();
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<QInputMethodEvent::Attribute> attributes;
QInputMethodEvent event("", attributes);
event.setCommitString("2");
page->event(&event);
}
// ImCurrentSelection
variant = page->inputMethodQuery(Qt::ImCurrentSelection);
selectionValue = variant.value<QString>();
QCOMPARE(selectionValue, QString(""));
// ImSurroundingText
variant = page->inputMethodQuery(Qt::ImSurroundingText);
surroundingValue = variant.value<QString>();
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("<html><body>" \
"<input type='text' id='input5' value='QtWebEngine inputMethod'/>" \
"<div id='btnDiv' onclick='i=document.getElementById("input5"); i.focus();'>abc</div>"\
"</body></html>");
QWebEngineElement inputElement = page->mainFrame()->findFirstElement("div");
clickOnPage(page, inputElement.geometry().center());
QVERIFY(!testContext.isInputPanelVisible());
// START - Newline test for textarea
qApp->processEvents();
page->setHtml("<html><body>" \
"<textarea rows='5' cols='1' id='input5' value=''/>" \
"</body></html>");
evaluateJavaScriptSync(page, "var inputEle = document.getElementById('input5'); inputEle.focus(); inputEle.select();");
// Enter Key without key text
QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
page->event(&keyEnter);
QList<QInputMethodEvent::Attribute> attribs;
QInputMethodEvent eventText(QString(), attribs);
eventText.setCommitString("\n");
page->event(&eventText);
QInputMethodEvent eventText2(QString(), attribs);
eventText2.setCommitString("third line");
page->event(&eventText2);
2381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450
qApp->processEvents();
QString inputValue2 = evaluateJavaScriptSync(page, "document.getElementById('input5').value").toString();
QCOMPARE(inputValue2, QString("\n\nthird line"));
// Enter Key with key text '\r'
evaluateJavaScriptSync(page, "var inputEle = document.getElementById('input5'); inputEle.value = ''; inputEle.focus(); inputEle.select();");
inputValue2 = evaluateJavaScriptSync(page, "document.getElementById('input5').value").toString();
QCOMPARE(inputValue2, QString(""));
QKeyEvent keyEnterWithCarriageReturn(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier, "\r");
page->event(&keyEnterWithCarriageReturn);
page->event(&eventText);
page->event(&eventText2);
qApp->processEvents();
inputValue2 = evaluateJavaScriptSync(page, "document.getElementById('input5').value").toString();
QCOMPARE(inputValue2, QString("\n\nthird line"));
// Enter Key with key text '\n'
page->runJavaScript("var inputEle = document.getElementById('input5'); inputEle.value = ''; inputEle.focus(); inputEle.select();");
inputValue2 = evaluateJavaScriptSync(page, "document.getElementById('input5').value").toString();
QCOMPARE(inputValue2, QString(""));
QKeyEvent keyEnterWithLineFeed(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier, "\n");
page->event(&keyEnterWithLineFeed);
page->event(&eventText);
page->event(&eventText2);
qApp->processEvents();
inputValue2 = evaluateJavaScriptSync(page, "document.getElementById('input5').value").toString();
QCOMPARE(inputValue2, QString("\n\nthird line"));
// Enter Key with key text "\n\r"
page->runJavaScript("var inputEle = document.getElementById('input5'); inputEle.value = ''; inputEle.focus(); inputEle.select();");
inputValue2 = evaluateJavaScriptSync(page, "document.getElementById('input5').value").toString();
QCOMPARE(inputValue2, QString(""));
QKeyEvent keyEnterWithLFCR(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier, "\n\r");
page->event(&keyEnterWithLFCR);
page->event(&eventText);
page->event(&eventText2);
qApp->processEvents();
inputValue2 = evaluateJavaScriptSync(page, "document.getElementById('input5').value").toString();
QCOMPARE(inputValue2, QString("\n\nthird line"));
// Return Key without key text
page->runJavaScript("var inputEle = document.getElementById('input5'); inputEle.value = ''; inputEle.focus(); inputEle.select();");
inputValue2 = evaluateJavaScriptSync(page, "document.getElementById('input5').value").toString();
QCOMPARE(inputValue2, QString(""));
QKeyEvent keyReturn(QEvent::KeyPress, Qt::Key_Return, Qt::NoModifier);
page->event(&keyReturn);
page->event(&eventText);
page->event(&eventText2);
qApp->processEvents();
inputValue2 = evaluateJavaScriptSync(page, "document.getElementById('input5').value").toString();
QCOMPARE(inputValue2, QString("\n\nthird line"));
// END - Newline test for textarea
delete container;
#endif
}
void tst_QWebEnginePage::inputMethodsTextFormat_data()
{
QTest::addColumn<QString>("string");
2451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520
QTest::addColumn<int>("start");
QTest::addColumn<int>("length");
QTest::newRow("") << QString("") << 0 << 0;
QTest::newRow("Q") << QString("Q") << 0 << 1;
QTest::newRow("Qt") << QString("Qt") << 0 << 1;
QTest::newRow("Qt") << QString("Qt") << 0 << 2;
QTest::newRow("Qt") << QString("Qt") << 1 << 1;
QTest::newRow("Qt ") << QString("Qt ") << 0 << 1;
QTest::newRow("Qt ") << QString("Qt ") << 1 << 1;
QTest::newRow("Qt ") << QString("Qt ") << 2 << 1;
QTest::newRow("Qt ") << QString("Qt ") << 2 << -1;
QTest::newRow("Qt ") << QString("Qt ") << -2 << 3;
QTest::newRow("Qt ") << QString("Qt ") << 0 << 3;
QTest::newRow("Qt by") << QString("Qt by") << 0 << 1;
QTest::newRow("Qt by Nokia") << QString("Qt by Nokia") << 0 << 1;
}
void tst_QWebEnginePage::inputMethodsTextFormat()
{
#if !defined(QINPUTMETHODEVENT_TEXTFORMAT)
QSKIP("QINPUTMETHODEVENT_TEXTFORMAT");
#else
QWebEnginePage* page = new QWebEnginePage;
QWebEngineView* view = new QWebEngineView;
view->setPage(page);
page->settings()->setFontFamily(QWebEngineSettings::SerifFont, "FooSerifFont");
page->setHtml("<html><body>" \
"<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/>");
evaluateJavaScriptSync(page, "document.getElementById('input1').focus()");
page->mainFrame()->setFocus();
view->show();
QFETCH(QString, string);
QFETCH(int, start);
QFETCH(int, length);
QList<QInputMethodEvent::Attribute> attrs;
QTextCharFormat format;
format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
format.setUnderlineColor(Qt::red);
attrs.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, length, format));
QInputMethodEvent im(string, attrs);
page->event(&im);
QTest::qWait(1000);
delete view;
#endif
}
void tst_QWebEnginePage::protectBindingsRuntimeObjectsFromCollector()
{
#if !defined(QWEBENGINEPAGE_CREATEPLUGIN)
QSKIP("QWEBENGINEPAGE_CREATEPLUGIN");
#else
QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
PluginPage* newPage = new PluginPage(m_view);
m_view->setPage(newPage);
m_view->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true);
m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='lineedit' id='mylineedit'/></body></html>"));
QTRY_COMPARE(loadSpy.count(), 1);
newPage->runJavaScript("function testme(text) { var lineedit = document.getElementById('mylineedit'); lineedit.setText(text); lineedit.selectAll(); }");
evaluateJavaScriptSync(newPage, "testme('foo')");
2521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590
DumpRenderTreeSupportQt::garbageCollectorCollect();
// don't crash!
evaluateJavaScriptSync(newPage, "testme('bar')");
#endif
}
void tst_QWebEnginePage::localURLSchemes()
{
#if !defined(QWEBENGINESECURITYORIGIN)
QSKIP("QWEBENGINESECURITYORIGIN");
#else
int i = QWebEngineSecurityOrigin::localSchemes().size();
QWebEngineSecurityOrigin::removeLocalScheme("file");
QTRY_COMPARE(QWebEngineSecurityOrigin::localSchemes().size(), i);
QWebEngineSecurityOrigin::addLocalScheme("file");
QTRY_COMPARE(QWebEngineSecurityOrigin::localSchemes().size(), i);
QWebEngineSecurityOrigin::removeLocalScheme("qrc");
QTRY_COMPARE(QWebEngineSecurityOrigin::localSchemes().size(), i - 1);
QWebEngineSecurityOrigin::addLocalScheme("qrc");
QTRY_COMPARE(QWebEngineSecurityOrigin::localSchemes().size(), i);
QString myscheme = "myscheme";
QWebEngineSecurityOrigin::addLocalScheme(myscheme);
QTRY_COMPARE(QWebEngineSecurityOrigin::localSchemes().size(), i + 1);
QVERIFY(QWebEngineSecurityOrigin::localSchemes().contains(myscheme));
QWebEngineSecurityOrigin::removeLocalScheme(myscheme);
QTRY_COMPARE(QWebEngineSecurityOrigin::localSchemes().size(), i);
QWebEngineSecurityOrigin::removeLocalScheme(myscheme);
QTRY_COMPARE(QWebEngineSecurityOrigin::localSchemes().size(), i);
#endif
}
#if defined(QWEBENGINEPAGE_SETTINGS)
static inline bool testFlag(QWebEnginePage& webPage, QWebEngineSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue)
{
webPage.settings()->setAttribute(settingAttribute, settingValue);
return evaluateJavaScriptSync(&webPage, QString("(window.%1 != undefined)").arg(jsObjectName)).toBool();
}
#endif
void tst_QWebEnginePage::testOptionalJSObjects()
{
#if !defined(QWEBENGINESETTINGS)
QSKIP("QWEBENGINSETTINGS");
#else
// Once a feature is enabled and the JS object is accessed turning off the setting will not turn off
// the visibility of the JS object any more. For this reason this test uses two QWebEnginePage instances.
// Part of the test is to make sure that the QWebEnginePage instances do not interfere with each other so turning on
// a feature for one instance will not turn it on for another.
QWebEnginePage webPage1;
QWebEnginePage webPage2;
webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/"));
webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com/"));
QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
QCOMPARE(testFlag(webPage1, QWebEngineSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
QCOMPARE(testFlag(webPage2, QWebEngineSettings::OfflineWebApplicationCacheEnabled, "applicationCache", true), true);
QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
QCOMPARE(testFlag(webPage1, QWebEngineSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
QCOMPARE(testFlag(webPage2, QWebEngineSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), true);
QCOMPARE(testFlag(webPage1, QWebEngineSettings::LocalStorageEnabled, "localStorage", false), false);
QCOMPARE(testFlag(webPage2, QWebEngineSettings::LocalStorageEnabled, "localStorage", true), true);
QCOMPARE(testFlag(webPage1, QWebEngineSettings::LocalStorageEnabled, "localStorage", false), false);
2591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660
QCOMPARE(testFlag(webPage2, QWebEngineSettings::LocalStorageEnabled, "localStorage", false), true);
#endif
}
#if defined(QWEBENGINEPAGE_SETTINGS)
static inline bool checkLocalStorageVisibility(QWebEnginePage& webPage, bool localStorageEnabled)
{
webPage.settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, localStorageEnabled);
return evaluateJavaScriptSync(&webPage, QString("(window.localStorage != undefined)")).toBool();
}
#endif
void tst_QWebEnginePage::testLocalStorageVisibility()
{
#if !defined(QWEBENGINEPAGE_SETTINGS)
QSKIP("QWEBENGINEPAGE_SETTINGS");
#else
// Local storage's visibility depends on its security origin, which depends on base url.
// Initially, it will test it with base urls that get a globally unique origin, which may not
// be able to use local storage even if the feature is enabled. Then later the same test is
// done but with urls that would get a valid origin, so local storage could be used.
// Before every test case it checks if local storage is not already visible.
QWebEnginePage webPage;
webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
QCOMPARE(checkLocalStorageVisibility(webPage, true), false);
webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("invalid"));
QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
QCOMPARE(checkLocalStorageVisibility(webPage, true), false);
webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("://misparsed.com"));
QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
QCOMPARE(checkLocalStorageVisibility(webPage, true), false);
webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://"));
QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
QCOMPARE(checkLocalStorageVisibility(webPage, true), false);
webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("about:blank"));
QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
QCOMPARE(checkLocalStorageVisibility(webPage, true), false);
webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("data:text/html,test"));
QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
QCOMPARE(checkLocalStorageVisibility(webPage, true), false);
webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("file:///"));
QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
QCOMPARE(checkLocalStorageVisibility(webPage, true), true);
webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("http://www.example.com"));
QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
QCOMPARE(checkLocalStorageVisibility(webPage, true), true);
webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("https://www.example.com"));
QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
QCOMPARE(checkLocalStorageVisibility(webPage, true), true);
2661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730
webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("ftp://files.example.com"));
QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
QCOMPARE(checkLocalStorageVisibility(webPage, true), true);
webPage.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl("file:///path/to/index.html"));
QCOMPARE(checkLocalStorageVisibility(webPage, false), false);
QCOMPARE(checkLocalStorageVisibility(webPage, true), true);
#endif
}
void tst_QWebEnginePage::testEnablePersistentStorage()
{
#if !defined(QWEBENGINESETTINGS)
QSKIP("QWEBENGINESETTINGS");
#else
QWebEnginePage webPage;
// By default all persistent options should be disabled
QCOMPARE(webPage.settings()->testAttribute(QWebEngineSettings::LocalStorageEnabled), false);
QCOMPARE(webPage.settings()->testAttribute(QWebEngineSettings::OfflineStorageDatabaseEnabled), false);
QCOMPARE(webPage.settings()->testAttribute(QWebEngineSettings::OfflineWebApplicationCacheEnabled), false);
QVERIFY(webPage.settings()->iconDatabasePath().isEmpty());
QWebEngineSettings::enablePersistentStorage();
QTRY_COMPARE(webPage.settings()->testAttribute(QWebEngineSettings::LocalStorageEnabled), true);
QTRY_COMPARE(webPage.settings()->testAttribute(QWebEngineSettings::OfflineStorageDatabaseEnabled), true);
QTRY_COMPARE(webPage.settings()->testAttribute(QWebEngineSettings::OfflineWebApplicationCacheEnabled), true);
QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty());
QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty());
QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty());
#endif
}
void tst_QWebEnginePage::defaultTextEncoding()
{
#if !defined(QWEBENGINESETTINGS_SETDEFAULTTEXTENCODING)
QSKIP("QWEBENGINESETTINGS_SETDEFAULTTEXTENCODING");
#else
QString defaultCharset = evaluateJavaScriptSync(m_page, "document.defaultCharset").toString();
QVERIFY(!defaultCharset.isEmpty());
QCOMPARE(QWebEngineSettings::globalSettings()->defaultTextEncoding(), defaultCharset);
m_page->settings()->setDefaultTextEncoding(QString("utf-8"));
QString charset = evaluateJavaScriptSync(m_page, "document.defaultCharset").toString();
QCOMPARE(charset, QString("utf-8"));
QCOMPARE(m_page->settings()->defaultTextEncoding(), charset);
m_page->settings()->setDefaultTextEncoding(QString());
charset = evaluateJavaScriptSync(m_page, "document.defaultCharset").toString();
QVERIFY(!charset.isEmpty());
QCOMPARE(charset, defaultCharset);
QWebEngineSettings::globalSettings()->setDefaultTextEncoding(QString("utf-8"));
charset = evaluateJavaScriptSync(m_page, "document.defaultCharset").toString();
QCOMPARE(charset, QString("utf-8"));
QCOMPARE(QWebEngineSettings::globalSettings()->defaultTextEncoding(), charset);
#endif
}
class ErrorPage : public QWebEnginePage
{
public:
ErrorPage(QWidget* parent = 0): QWebEnginePage(parent)
2731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800
{
}
virtual bool supportsExtension(Extension extension) const
{
return extension == ErrorPageExtension;
}
virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output)
{
ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output);
errorPage->contentType = "text/html";
errorPage->content = "error";
return true;
}
};
void tst_QWebEnginePage::errorPageExtension()
{
#if !defined(QWEBENGINEPAGE_ERRORPAGEEXTENSION)
QSKIP("QWEBENGINEPAGE_ERRORPAGEEXTENSION");
#else
ErrorPage page;
m_view->setPage(&page);
QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
m_view->setUrl(QUrl("data:text/html,foo"));
QTRY_COMPARE(spyLoadFinished.count(), 1);
page.setUrl(QUrl("http://non.existent/url"));
QTRY_COMPARE(spyLoadFinished.count(), 2);
QCOMPARE(toPlainTextSync(&page), QString("error"));
QCOMPARE(page.history()->count(), 2);
QCOMPARE(page.history()->currentItem().url(), QUrl("http://non.existent/url"));
QCOMPARE(page.history()->canGoBack(), true);
QCOMPARE(page.history()->canGoForward(), false);
page.triggerAction(QWebEnginePage::Back);
QTRY_COMPARE(page.history()->canGoBack(), false);
QTRY_COMPARE(page.history()->canGoForward(), true);
page.triggerAction(QWebEnginePage::Forward);
QTRY_COMPARE(page.history()->canGoBack(), true);
QTRY_COMPARE(page.history()->canGoForward(), false);
page.triggerAction(QWebEnginePage::Back);
QTRY_COMPARE(page.history()->canGoBack(), false);
QTRY_COMPARE(page.history()->canGoForward(), true);
QTRY_COMPARE(page.history()->currentItem().url(), QUrl("data:text/html,foo"));
m_view->setPage(0);
#endif
}
void tst_QWebEnginePage::errorPageExtensionInIFrames()
{
#if !defined(QWEBENGINEFRAME)
QSKIP("QWEBENGINEFRAME");
#else
ErrorPage page;
m_view->setPage(&page);
m_view->page()->load(QUrl(
"data:text/html,"
"<h1>h1</h1>"
"<iframe src='data:text/html,<p/>p'></iframe>"
"<iframe src='http://non.existent/url'></iframe>"));
QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
2801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870
QTRY_COMPARE(spyLoadFinished.count(), 1);
QCOMPARE(page.mainFrame()->childFrames()[1]->toPlainText(), QString("error"));
m_view->setPage(0);
#endif
}
void tst_QWebEnginePage::errorPageExtensionInFrameset()
{
#if !defined(QWEBENGINEFRAME)
QSKIP("QWEBENGINEFRAME");
#else
ErrorPage page;
m_view->setPage(&page);
m_view->load(QUrl("qrc:///resources/index.html"));
QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
QTRY_COMPARE(spyLoadFinished.count(), 1);
QCOMPARE(page.mainFrame()->childFrames().count(), 2);
QCOMPARE(page.mainFrame()->childFrames()[1]->toPlainText(), QString("error"));
m_view->setPage(0);
#endif
}
void tst_QWebEnginePage::errorPageExtensionLoadFinished()
{
ErrorPage page;
m_view->setPage(&page);
QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
QSignalSpy spyFrameLoadFinished(m_view->page(), SIGNAL(loadFinished(bool)));
m_view->setUrl(QUrl("data:text/html,foo"));
QTRY_COMPARE(spyLoadFinished.count(), 1);
QTRY_COMPARE(spyFrameLoadFinished.count(), 1);
const bool loadSucceded = spyLoadFinished.at(0).at(0).toBool();
QVERIFY(loadSucceded);
const bool frameLoadSucceded = spyFrameLoadFinished.at(0).at(0).toBool();
QVERIFY(frameLoadSucceded);
m_view->page()->setUrl(QUrl("http://non.existent/url"));
QTRY_COMPARE(spyLoadFinished.count(), 2);
QTRY_COMPARE(spyFrameLoadFinished.count(), 2);
const bool nonExistantLoadSucceded = spyLoadFinished.at(1).at(0).toBool();
QVERIFY(nonExistantLoadSucceded);
const bool nonExistantFrameLoadSucceded = spyFrameLoadFinished.at(1).at(0).toBool();
QVERIFY(nonExistantFrameLoadSucceded);
m_view->setPage(0);
}
class FriendlyWebPage : public QWebEnginePage
{
public:
friend class tst_QWebEnginePage;
};
void tst_QWebEnginePage::userAgentApplicationName()
{
#if !defined(QWEBENGINEPAGE_USERAGENTFORURL)
QSKIP("QWEBENGINEPAGE_USERAGENTFORURL");
#else
const QString oldApplicationName = QCoreApplication::applicationName();
FriendlyWebPage page;
2871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940
const QString applicationNameMarker = QString::fromUtf8("StrangeName\342\210\236");
QCoreApplication::setApplicationName(applicationNameMarker);
QVERIFY(page.userAgentForUrl(QUrl()).contains(applicationNameMarker));
QCoreApplication::setApplicationName(oldApplicationName);
#endif
}
class CustomUserAgentWebPage : public QWebEnginePage
{
public:
static const QLatin1String filteredUserAgent;
protected:
virtual QString userAgentForUrl(const QUrl& url) const
{
return QString("My User Agent\nX-New-Http-Header: Oh Noes!");
}
};
const QLatin1String CustomUserAgentWebPage::filteredUserAgent("My User AgentX-New-Http-Header: Oh Noes!");
void tst_QWebEnginePage::userAgentNewlineStripping()
{
#if !defined(QWEBENGINEPAGE_USERAGENTFORURL)
QSKIP("QWEBENGINEPAGE_USERAGENTFORURL");
#else
CustomUserAgentWebPage page;
page.setHtml("<html><body></body></html>");
QCOMPARE(evaluateJavaScriptSync(&page, "navigator.userAgent").toString(), CustomUserAgentWebPage::filteredUserAgent);
#endif
}
void tst_QWebEnginePage::crashTests_LazyInitializationOfMainFrame()
{
{
QWebEnginePage webPage;
}
{
QWebEnginePage webPage;
webPage.selectedText();
}
{
#if defined(QWEBENGINEPAGE_SELECTEDHTML)
QWebEnginePage webPage;
webPage.selectedHtml();
#endif
}
{
QWebEnginePage webPage;
webPage.triggerAction(QWebEnginePage::Back, true);
}
{
#if defined(QWEBENGINEPAGE_UPDATEPOSITIONDEPENDENTACTIONS)
QWebEnginePage webPage;
QPoint pos(10,10);
webPage.updatePositionDependentActions(pos);
#endif
}
}
#if defined(QWEBENGINEPAGE_RENDER)
static void takeScreenshot(QWebEnginePage* page)
{
page->setViewportSize(page->contentsSize());
QImage image(page->viewportSize(), QImage::Format_ARGB32);
QPainter painter(&image);
page->render(&painter);
painter.end();
}
#endif
2941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010
void tst_QWebEnginePage::screenshot_data()
{
QTest::addColumn<QString>("html");
QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>";
QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>");
QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode='transparent'></embed></body></html>");
}
void tst_QWebEnginePage::screenshot()
{
#if !defined(QWEBENGINESETTINGS)
QSKIP("QWEBENGINESETTINGS");
#else
if (!QDir(TESTS_SOURCE_DIR).exists())
W_QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
QDir::setCurrent(TESTS_SOURCE_DIR);
QFETCH(QString, html);
QWebEnginePage* page = new QWebEnginePage;
page->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, true);
page->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR));
::waitForSignal(page, SIGNAL(loadFinished(bool)), 2000);
// take screenshot without a view
takeScreenshot(page);
QWebEngineView* view = new QWebEngineView;
view->setPage(page);
// take screenshot when attached to a view
takeScreenshot(page);
delete page;
delete view;
QDir::setCurrent(QApplication::applicationDirPath());
#endif
}
#if defined(ENABLE_WEBGL) && ENABLE_WEBGL
// https://bugs.webkit.org/show_bug.cgi?id=54138
static void webGLScreenshotWithoutView(bool accelerated)
{
QWebEnginePage page;
page.settings()->setAttribute(QWebEngineSettings::WebGLEnabled, true);
page.settings()->setAttribute(QWebEngineSettings::AcceleratedCompositingEnabled, accelerated);
page.setHtml("<html><body>"
"<canvas id='webgl' width='300' height='300'></canvas>"
"<script>document.getElementById('webgl').getContext('experimental-webgl')</script>"
"</body></html>");
takeScreenshot(&page);
}
void tst_QWebEnginePage::acceleratedWebGLScreenshotWithoutView()
{
webGLScreenshotWithoutView(true);
}
void tst_QWebEnginePage::unacceleratedWebGLScreenshotWithoutView()
{
webGLScreenshotWithoutView(false);
}
#endif
void tst_QWebEnginePage::originatingObjectInNetworkRequests()
{
#if !defined(QWEBENGINEFRAME)
QSKIP("QWEBENGINEFRAME");
3011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080
#else
TestNetworkManager* networkManager = new TestNetworkManager(m_page);
m_page->setNetworkAccessManager(networkManager);
networkManager->requests.clear();
m_view->setHtml(QString("<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
"<head><meta http-equiv='refresh' content='1'></head>foo \">"
"<frame src=\"data:text/html,bar\"></frameset>"), QUrl());
QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
QCOMPARE(networkManager->requests.count(), 2);
QList<QWebEngineFrame*> childFrames = m_page->mainFrame()->childFrames();
QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118660", Continue);
QCOMPARE(childFrames.count(), 2);
for (int i = 0; i < 2; ++i)
QVERIFY(qobject_cast<QWebEngineFrame*>(networkManager->requests.at(i).originatingObject()) == childFrames.at(i));
#endif
}
void tst_QWebEnginePage::networkReplyParentDidntChange()
{
#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER)
QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER");
#else
TestNetworkManager* networkManager = new TestNetworkManager(m_page);
m_page->setNetworkAccessManager(networkManager);
networkManager->requests.clear();
// Trigger a load and check that pending QNetworkReplies haven't been reparented before returning to the event loop.
m_view->load(QUrl("qrc:///resources/content.html"));
QVERIFY(networkManager->requests.count() > 0);
QVERIFY(networkManager->findChildren<QNetworkReply*>().size() > 0);
#endif
}
void tst_QWebEnginePage::destroyQNAMBeforeAbortDoesntCrash()
{
#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER)
QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER");
#else
QNetworkAccessManager* networkManager = new QNetworkAccessManager;
m_page->setNetworkAccessManager(networkManager);
m_view->load(QUrl("qrc:///resources/content.html"));
delete networkManager;
// This simulates what PingLoader does with its QNetworkReply when it times out.
// PingLoader isn't attached to a QWebEnginePage and can be kept alive
// for 60000 seconds (~16.7 hours) to then cancel its ResourceHandle.
m_view->stop();
#endif
}
/**
* Test fixups for https://bugs.webkit.org/show_bug.cgi?id=30914
*
* From JS we test the following conditions.
*
* OK + QString() => SUCCESS, empty string (but not null)
* OK + "text" => SUCCESS, "text"
* CANCEL + QString() => CANCEL, null string
* CANCEL + "text" => CANCEL, null string
*/
class JSPromptPage : public QWebEnginePage {
Q_OBJECT
public:
JSPromptPage()
{}
3081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150
bool javaScriptPrompt(QWebEngineFrame* frame, const QString& msg, const QString& defaultValue, QString* result)
{
if (msg == QLatin1String("test1")) {
*result = QString();
return true;
} else if (msg == QLatin1String("test2")) {
*result = QLatin1String("text");
return true;
} else if (msg == QLatin1String("test3")) {
*result = QString();
return false;
} else if (msg == QLatin1String("test4")) {
*result = QLatin1String("text");
return false;
}
qFatal("Unknown msg.");
return QWebEnginePage::javaScriptPrompt(frame, msg, defaultValue, result);
}
};
void tst_QWebEnginePage::testJSPrompt()
{
JSPromptPage page;
bool res;
// OK + QString()
res = evaluateJavaScriptSync(&page,
"var retval = prompt('test1');"
"retval=='' && retval.length == 0;").toBool();
QVERIFY(res);
// OK + "text"
res = evaluateJavaScriptSync(&page,
"var retval = prompt('test2');"
"retval=='text' && retval.length == 4;").toBool();
QVERIFY(res);
// Cancel + QString()
res = evaluateJavaScriptSync(&page,
"var retval = prompt('test3');"
"retval===null;").toBool();
QVERIFY(res);
// Cancel + "text"
res = evaluateJavaScriptSync(&page,
"var retval = prompt('test4');"
"retval===null;").toBool();
QVERIFY(res);
}
class TestModalPage : public QWebEnginePage
{
Q_OBJECT
public:
TestModalPage(QObject* parent = 0) : QWebEnginePage(parent) {
}
virtual QWebEnginePage* createWindow(WebWindowType) {
QWebEnginePage* page = new TestModalPage();
connect(page, SIGNAL(windowCloseRequested()), page, SLOT(deleteLater()));
return page;
}
};
void tst_QWebEnginePage::showModalDialog()
{
#if !defined(QWEBENGINESETTINGS)
QSKIP("QWEBENGINESETTINGS");
#else
3151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220
TestModalPage page;
page.settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
page.setHtml(QString("<html></html>"));
QString res = evaluateJavaScriptSync(&page, "window.showModalDialog('javascript:window.returnValue=dialogArguments; window.close();', 'This is a test');").toString();
QCOMPARE(res, QString("This is a test"));
#endif
}
void tst_QWebEnginePage::testStopScheduledPageRefresh()
{
#if !defined(QWEBENGINEPAGE_SETNETWORKACCESSMANAGER)
QSKIP("QWEBENGINEPAGE_SETNETWORKACCESSMANAGER");
#else
// Without QWebEnginePage::StopScheduledPageRefresh
QWebEnginePage page1;
page1.setNetworkAccessManager(new TestNetworkManager(&page1));
page1.setHtml("<html><head>"
"<meta http-equiv=\"refresh\"content=\"0;URL=qrc:///resources/index.html\">"
"</head><body><h1>Page redirects immediately...</h1>"
"</body></html>");
QVERIFY(::waitForSignal(&page1, SIGNAL(loadFinished(bool))));
QTest::qWait(500);
QCOMPARE(page1.url(), QUrl(QLatin1String("qrc:///resources/index.html")));
// With QWebEnginePage::StopScheduledPageRefresh
QWebEnginePage page2;
page2.setNetworkAccessManager(new TestNetworkManager(&page2));
page2.setHtml("<html><head>"
"<meta http-equiv=\"refresh\"content=\"1;URL=qrc:///resources/index.html\">"
"</head><body><h1>Page redirect test with 1 sec timeout...</h1>"
"</body></html>");
page2.triggerAction(QWebEnginePage::StopScheduledPageRefresh);
QTest::qWait(1500);
QEXPECT_FAIL("", "https://bugs.webkit.org/show_bug.cgi?id=118673", Continue);
QCOMPARE(page2.url().toString(), QLatin1String("about:blank"));
#endif
}
void tst_QWebEnginePage::findText()
{
m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>"));
#if defined(QWEBENGINEPAGE_TRIGGERACTION_SELECTALL)
m_page->triggerAction(QWebEnginePage::SelectAll);
QVERIFY(!m_page->selectedText().isEmpty());
QVERIFY(!m_page->selectedHtml().isEmpty());
#endif
m_page->findText("");
QVERIFY(m_page->selectedText().isEmpty());
#if defined(QWEBENGINEPAGE_SELECTEDHTML)
QVERIFY(m_page->selectedHtml().isEmpty());
#endif
QStringList words = (QStringList() << "foo" << "bar");
foreach (QString subString, words) {
m_page->findText(subString);
QEXPECT_FAIL("", "Unsupported: findText only highlights and doesn't update the selection.", Continue);
QCOMPARE(m_page->selectedText(), subString);
#if defined(QWEBENGINEPAGE_SELECTEDHTML)
QVERIFY(m_page->selectedHtml().contains(subString));
#endif
m_page->findText("");
QVERIFY(m_page->selectedText().isEmpty());
#if defined(QWEBENGINEPAGE_SELECTEDHTML)
QVERIFY(m_page->selectedHtml().isEmpty());
#endif
}
}
void tst_QWebEnginePage::findTextResult()
{
// findText will abort in blink if the view has an empty size.
3221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290
m_view->resize(800, 600);
m_view->show();
QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>"));
QTRY_COMPARE(loadSpy.count(), 1);
QCOMPARE(findTextSync(m_page, ""), false);
QStringList words = (QStringList() << "foo" << "bar");
foreach (QString subString, words) {
QCOMPARE(findTextSync(m_page, subString), true);
QCOMPARE(findTextSync(m_page, ""), false);
}
QCOMPARE(findTextSync(m_page, "blahhh"), false);
QCOMPARE(findTextSync(m_page, ""), false);
}
static QString getMimeTypeForExtension(const QString &ext)
{
QMimeType mimeType = QMimeDatabase().mimeTypeForFile(QStringLiteral("filename.") + ext.toLower(), QMimeDatabase::MatchExtension);
if (mimeType.isValid() && !mimeType.isDefault())
return mimeType.name();
return QString();
}
void tst_QWebEnginePage::supportedContentType()
{
#if !defined(QWEBENGINEPAGE_SUPPORTEDCONTENTTYPES)
QSKIP("QWEBENGINEPAGE_SUPPORTEDCONTENTTYPES");
#else
QStringList contentTypes;
// Add supported non image types...
contentTypes << "text/html" << "text/xml" << "text/xsl" << "text/plain" << "text/"
<< "application/xml" << "application/xhtml+xml" << "application/vnd.wap.xhtml+xml"
<< "application/rss+xml" << "application/atom+xml" << "application/json";
#if ENABLE_MHTML
contentTypes << "application/x-mimearchive";
#endif
// Add supported image types...
Q_FOREACH (const QByteArray& imageType, QImageWriter::supportedImageFormats()) {
const QString mimeType = getMimeTypeForExtension(imageType);
if (!mimeType.isEmpty())
contentTypes << mimeType;
}
// Get the mime types supported by webengine...
const QStringList supportedContentTypes = m_page->supportedContentTypes();
Q_FOREACH (const QString& mimeType, contentTypes)
QVERIFY2(supportedContentTypes.contains(mimeType), QString("'%1' is not a supported content type!").arg(mimeType).toLatin1());
Q_FOREACH (const QString& mimeType, contentTypes)
QVERIFY2(m_page->supportsContentType(mimeType), QString("Cannot handle content types '%1'!").arg(mimeType).toLatin1());
#endif
}
void tst_QWebEnginePage::navigatorCookieEnabled()
{
#if !defined(QWEBENGINEPAGE_NETWORKACCESSMANAGER)
QSKIP("QWEBENGINEPAGE_NETWORKACCESSMANAGER");
#else
m_page->networkAccessManager()->setCookieJar(0);
QVERIFY(!m_page->networkAccessManager()->cookieJar());
3291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360
QVERIFY(!evaluateJavaScriptSync(m_page, "navigator.cookieEnabled").toBool());
m_page->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
QVERIFY(m_page->networkAccessManager()->cookieJar());
QVERIFY(evaluateJavaScriptSync(m_page, "navigator.cookieEnabled").toBool());
#endif
}
void tst_QWebEnginePage::thirdPartyCookiePolicy()
{
#if !defined(DUMPRENDERTREESUPPORTQT)
QSKIP("DUMPRENDERTREESUPPORTQT");
#else
QWebEngineSettings::globalSettings()->setThirdPartyCookiePolicy(QWebEngineSettings::AlwaysBlockThirdPartyCookies);
m_page->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
QVERIFY(m_page->networkAccessManager()->cookieJar());
// These are all first-party cookies, so should pass.
QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://www.example.com"), QUrl("http://example.com")));
QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://www.example.com"), QUrl("http://doc.example.com")));
QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://aaa.www.example.com"), QUrl("http://doc.example.com")));
QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://example.com"), QUrl("http://www.example.com")));
QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://www.example.co.uk"), QUrl("http://example.co.uk")));
QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://www.example.co.uk"), QUrl("http://doc.example.co.uk")));
QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://aaa.www.example.co.uk"), QUrl("http://doc.example.co.uk")));
QVERIFY(DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://example.co.uk"), QUrl("http://www.example.co.uk")));
// These are all third-party cookies, so should fail.
QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://www.example.com"), QUrl("http://slashdot.org")));
QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://example.com"), QUrl("http://anotherexample.com")));
QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://anotherexample.com"), QUrl("http://example.com")));
QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://www.example.co.uk"), QUrl("http://slashdot.co.uk")));
QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://example.co.uk"), QUrl("http://anotherexample.co.uk")));
QVERIFY(!DumpRenderTreeSupportQt::thirdPartyCookiePolicyAllows(m_page->handle(),
QUrl("http://anotherexample.co.uk"), QUrl("http://example.co.uk")));
#endif
}
#ifdef Q_OS_MAC
void tst_QWebEnginePage::macCopyUnicodeToClipboard()
{
QString unicodeText = QString::fromUtf8("αβγδεζηθικλμπ");
m_page->setHtml(QString("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /></head><body>%1</body></html>").arg(unicodeText));
m_page->triggerAction(QWebEnginePage::SelectAll);
m_page->triggerAction(QWebEnginePage::Copy);
QString clipboardData = QString::fromUtf8(QApplication::clipboard()->mimeData()->data(QLatin1String("text/html")));
QVERIFY(clipboardData.contains(QLatin1String("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />")));
QVERIFY(clipboardData.contains(unicodeText));
}
#endif
void tst_QWebEnginePage::contextMenuCopy()
{
#if !defined(QWEBENGINEELEMENT)
QSKIP("QWEBENGINEELEMENT");
3361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430
#else
QWebEngineView view;
view.setHtml("<a href=\"http://www.google.com\">You cant miss this</a>");
view.page()->triggerAction(QWebEnginePage::SelectAll);
QVERIFY(!view.page()->selectedText().isEmpty());
QWebEngineElement link = view.page()->mainFrame()->findFirstElement("a");
QPoint pos(link.geometry().center());
QContextMenuEvent event(QContextMenuEvent::Mouse, pos);
view.page()->swallowContextMenuEvent(&event);
view.page()->updatePositionDependentActions(pos);
QList<QMenu*> contextMenus = view.findChildren<QMenu*>();
QVERIFY(!contextMenus.isEmpty());
QMenu* contextMenu = contextMenus.first();
QVERIFY(contextMenu);
QList<QAction *> list = contextMenu->actions();
int index = list.indexOf(view.page()->action(QWebEnginePage::Copy));
QVERIFY(index != -1);
#endif
}
// https://bugs.webkit.org/show_bug.cgi?id=62139
void tst_QWebEnginePage::contextMenuPopulatedOnce()
{
#if !defined(QWEBENGINEELEMENT)
QSKIP("QWEBENGINEELEMENT");
#else
QWebEngineView view;
view.setHtml("<input type=\"text\">");
QWebEngineElement link = view.page()->mainFrame()->findFirstElement("input");
QPoint pos(link.geometry().center());
QContextMenuEvent event(QContextMenuEvent::Mouse, pos);
view.page()->swallowContextMenuEvent(&event);
view.page()->updatePositionDependentActions(pos);
QList<QMenu*> contextMenus = view.findChildren<QMenu*>();
QVERIFY(!contextMenus.isEmpty());
QMenu* contextMenu = contextMenus.first();
QVERIFY(contextMenu);
QList<QAction *> list = contextMenu->actions();
QStringList entries;
while (!list.isEmpty()) {
QString entry = list.takeFirst()->text();
QVERIFY(!entries.contains(entry));
entries << entry;
}
#endif
}
void tst_QWebEnginePage::deleteQWebEngineViewTwice()
{
for (int i = 0; i < 2; ++i) {
QMainWindow mainWindow;
QWebEngineView* webView = new QWebEngineView(&mainWindow);
mainWindow.setCentralWidget(webView);
webView->load(QUrl("qrc:///resources/frame_a.html"));
mainWindow.show();
QVERIFY(::waitForSignal(webView, SIGNAL(loadFinished(bool))));
}
}
#if defined(QWEBENGINEPAGE_RENDER)
class RepaintRequestedRenderer : public QObject {
3431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500
Q_OBJECT
public:
RepaintRequestedRenderer(QWebEnginePage* page, QPainter* painter)
: m_page(page)
, m_painter(painter)
, m_recursionCount(0)
{
connect(m_page, SIGNAL(repaintRequested(QRect)), this, SLOT(onRepaintRequested(QRect)));
}
Q_SIGNALS:
void finished();
private Q_SLOTS:
void onRepaintRequested(const QRect& rect)
{
QCOMPARE(m_recursionCount, 0);
m_recursionCount++;
m_page->render(m_painter, rect);
m_recursionCount--;
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
}
private:
QWebEnginePage* m_page;
QPainter* m_painter;
int m_recursionCount;
};
#endif
void tst_QWebEnginePage::renderOnRepaintRequestedShouldNotRecurse()
{
#if !defined(QWEBENGINEPAGE_RENDER)
QSKIP("QWEBENGINEPAGE_RENDER");
#else
QSize viewportSize(720, 576);
QWebEnginePage page;
QImage image(viewportSize, QImage::Format_ARGB32);
QPainter painter(&image);
page.setPreferredContentsSize(viewportSize);
page.setViewportSize(viewportSize);
RepaintRequestedRenderer r(&page, &painter);
page.setHtml("zalan loves trunk", QUrl());
QVERIFY(::waitForSignal(&r, SIGNAL(finished())));
#endif
}
class SpyForLoadSignalsOrder : public QStateMachine {
Q_OBJECT
public:
SpyForLoadSignalsOrder(QWebEnginePage* page, QObject* parent = 0)
: QStateMachine(parent)
{
connect(page, SIGNAL(loadProgress(int)), SLOT(onLoadProgress(int)));
QState* waitingForLoadStarted = new QState(this);
QState* waitingForLastLoadProgress = new QState(this);
QState* waitingForLoadFinished = new QState(this);
QFinalState* final = new QFinalState(this);
waitingForLoadStarted->addTransition(page, SIGNAL(loadStarted()), waitingForLastLoadProgress);
waitingForLastLoadProgress->addTransition(this, SIGNAL(lastLoadProgress()), waitingForLoadFinished);
waitingForLoadFinished->addTransition(page, SIGNAL(loadFinished(bool)), final);
3501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570
setInitialState(waitingForLoadStarted);
start();
}
bool isFinished() const
{
return !isRunning();
}
public Q_SLOTS:
void onLoadProgress(int progress)
{
if (progress == 100)
emit lastLoadProgress();
}
Q_SIGNALS:
void lastLoadProgress();
};
void tst_QWebEnginePage::loadSignalsOrder_data()
{
QTest::addColumn<QUrl>("url");
QTest::newRow("inline data") << QUrl("data:text/html,This is first page");
QTest::newRow("simple page") << QUrl("qrc:///resources/content.html");
QTest::newRow("frameset page") << QUrl("qrc:///resources/index.html");
}
void tst_QWebEnginePage::loadSignalsOrder()
{
QFETCH(QUrl, url);
QWebEnginePage page;
SpyForLoadSignalsOrder loadSpy(&page);
waitForSignal(&loadSpy, SIGNAL(started()));
page.load(url);
QTRY_VERIFY(loadSpy.isFinished());
}
void tst_QWebEnginePage::undoActionHaveCustomText()
{
#if !defined(QWEBENGINEPAGE_UNDOACTION)
QSKIP("QWEBENGINEPAGE_UNDOACTION");
#else
m_page->setHtml("<div id=test contenteditable></div>");
evaluateJavaScriptSync(m_page, "document.getElementById('test').focus()");
evaluateJavaScriptSync(m_page, "document.execCommand('insertText', true, 'Test');");
QString typingActionText = m_page->action(QWebEnginePage::Undo)->text();
evaluateJavaScriptSync(m_page, "document.execCommand('indent', true);");
QString alignActionText = m_page->action(QWebEnginePage::Undo)->text();
QVERIFY(typingActionText != alignActionText);
#endif
}
void tst_QWebEnginePage::renderWidgetHostViewNotShowTopLevel()
{
QWebEnginePage page;
QSignalSpy spyLoadFinished(&page, SIGNAL(loadFinished(bool)));
page.load(QUrl("http://qt-project.org"));
if (!spyLoadFinished.wait(10000) || !spyLoadFinished.at(0).at(0).toBool())
QSKIP("Couldn't load page from network, skipping test.");
spyLoadFinished.clear();
// Loading a different domain will force the creation of a separate render
// process and should therefore create a new RenderWidgetHostViewQtDelegateWidget.
page.load(QUrl("http://www.wikipedia.org/"));
if (!spyLoadFinished.wait(10000) || !spyLoadFinished.at(0).at(0).toBool())
QSKIP("Couldn't load page from network, skipping test.");
// Make sure that RenderWidgetHostViewQtDelegateWidgets are not shown as top-level.
3571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640
// They should only be made visible when parented to a QWebEngineView.
foreach (QWidget *widget, QApplication::topLevelWidgets())
QCOMPARE(widget->isVisible(), false);
}
void tst_QWebEnginePage::openWindowDefaultSize()
{
#if !defined(QWEBENGINEPAGE_SETTINGS)
QSKIP("QWEBENGINEPAGE_SETTINGS");
#else
TestPage page;
page.settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, true);
// Open a default window.
page.runJavaScript("window.open()");
// Open a too small window.
evaluateJavaScriptSync(&page, "window.open('', '', 'width=10,height=10')");
QTest::qWait(500);
// The number of popups created should be two.
QVERIFY(page.createdWindows.size() == 2);
QRect requestedGeometry = page.createdWindows[0]->requestedGeometry;
// Check default size has been requested.
QVERIFY(requestedGeometry.width() == 0);
QVERIFY(requestedGeometry.height() == 0);
requestedGeometry = page.createdWindows[1]->requestedGeometry;
// Check minimum size has been requested.
QVERIFY(requestedGeometry.width() == 100);
QVERIFY(requestedGeometry.height() == 100);
#endif
}
void tst_QWebEnginePage::cssMediaTypeGlobalSetting()
{
#if !defined(QWEBENGINESETTINGS_SETCSSMEDIATYPE)
QSKIP("QWEBENGINESETTINGS_SETCSSMEDIATYPE");
#else
QString testHtml("<style>@media tv {body{background-color:red;}}@media handheld {body{background-color:green;}}@media screen {body{background-color:blue;}}</style>");
QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
QWebEngineSettings::globalSettings()->setCSSMediaType("tv");
// Clear page specific setting to read from global setting
m_view->page()->settings()->setCSSMediaType(QString());
m_view->setHtml(testHtml);
QTRY_COMPARE(loadSpy.count(), 1);
QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('tv').matches == true").toBool());
QVERIFY(QWebEngineSettings::globalSettings()->cssMediaType() == "tv");
QWebEngineSettings::globalSettings()->setCSSMediaType("handheld");
// Clear page specific setting to read from global setting
m_view->page()->settings()->setCSSMediaType(QString());
m_view->setHtml(testHtml);
QTRY_COMPARE(loadSpy.count(), 2);
QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('handheld').matches == true").toBool());
QVERIFY(QWebEngineSettings::globalSettings()->cssMediaType() == "handheld");
QWebEngineSettings::globalSettings()->setCSSMediaType("screen");
// Clear page specific setting to read from global setting
m_view->page()->settings()->setCSSMediaType(QString());
m_view->setHtml(testHtml);
QTRY_COMPARE(loadSpy.count(), 3);
QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('screen').matches == true").toBool());
QVERIFY(QWebEngineSettings::globalSettings()->cssMediaType() == "screen");
#endif
}
void tst_QWebEnginePage::cssMediaTypePageSetting()
{
#if !defined(QWEBENGINESETTINGS_SETCSSMEDIATYPE)
3641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668
QSKIP("QWEBENGINESETTINGS_SETCSSMEDIATYPE");
#else
QString testHtml("<style>@media tv {body{background-color:red;}}@media handheld {body{background-color:green;}}@media screen {body{background-color:blue;}}</style>");
QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
m_view->page()->settings()->setCSSMediaType("tv");
m_view->setHtml(testHtml);
QTRY_COMPARE(loadSpy.count(), 1);
QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('tv').matches == true").toBool());
QVERIFY(m_view->page()->settings()->cssMediaType() == "tv");
m_view->page()->settings()->setCSSMediaType("handheld");
m_view->setHtml(testHtml);
QTRY_COMPARE(loadSpy.count(), 2);
QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('handheld').matches == true").toBool());
QVERIFY(m_view->page()->settings()->cssMediaType() == "handheld");
m_view->page()->settings()->setCSSMediaType("screen");
m_view->setHtml(testHtml);
QTRY_COMPARE(loadSpy.count(), 3);
QVERIFY(evaluateJavaScriptSync(m_view->page(), "window.matchMedia('screen').matches == true").toBool());
QVERIFY(m_view->page()->settings()->cssMediaType() == "screen");
#endif
}
QTEST_MAIN(tst_QWebEnginePage)
#include "tst_qwebenginepage.moc"