2 Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3 Copyright (C) 2009 Girish Ramakrishnan <girish@forwardbias.in>
4 Copyright (C) 2010 Holger Hans Peter Freyther
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
23 #include "../WebCoreSupport/DumpRenderTreeSupportQt.h"
26 #include <QGraphicsWidget>
30 #include <QPushButton>
32 #include <QtTest/QtTest>
33 #include <QTextCharFormat>
34 #include <qgraphicsscene.h>
35 #include <qgraphicsview.h>
36 #include <qgraphicswebview.h>
37 #include <qnetworkcookiejar.h>
38 #include <qnetworkrequest.h>
39 #include <qwebdatabase.h>
40 #include <qwebelement.h>
41 #include <qwebframe.h>
42 #include <qwebhistory.h>
44 #include <qwebsecurityorigin.h>
46 #include <qimagewriter.h>
48 class EventSpy : public QObject, public QList<QEvent::Type>
52 EventSpy(QObject* objectToSpy)
54 objectToSpy->installEventFilter(this);
57 virtual bool eventFilter(QObject* receiver, QEvent* event)
59 append(event->type());
64 class tst_QWebPage : public QObject
70 virtual ~tst_QWebPage();
79 void cleanupTestCase();
81 void contextMenuCopy();
82 void acceptNavigationRequest();
83 void geolocationRequestJS();
85 void acceptNavigationRequestWithNewWindow();
86 void userStyleSheet();
88 void contextMenuCrash();
89 void updatePositionDependentActionsCrash();
91 void createPluginWithPluginsEnabled();
92 void createPluginWithPluginsDisabled();
93 void destroyPlugin_data();
95 void createViewlessPlugin_data();
96 void createViewlessPlugin();
97 void graphicsWidgetPlugin();
98 void multiplePageGroupsAndLocalStorage();
99 void cursorMovements();
100 void textSelection();
102 void backActionUpdate();
105 void loadCachedPage();
106 void protectBindingsRuntimeObjectsFromCollector();
107 void localURLSchemes();
108 void testOptionalJSObjects();
109 void testEnablePersistentStorage();
110 void consoleOutput();
111 void inputMethods_data();
113 void inputMethodsTextFormat_data();
114 void inputMethodsTextFormat();
115 void defaultTextEncoding();
116 void errorPageExtension();
117 void errorPageExtensionInIFrames();
118 void errorPageExtensionInFrameset();
119 void userAgentApplicationName();
120 void userAgentLocaleChange();
124 void crashTests_LazyInitializationOfMainFrame();
126 void screenshot_data();
129 void originatingObjectInNetworkRequests();
131 void showModalDialog();
132 void testStopScheduledPageRefresh();
134 void supportedContentType();
135 void infiniteLoopJS();
136 void networkAccessManagerOnDifferentThread();
137 void navigatorCookieEnabled();
138 void navigatorCookieEnabledForNetworkAccessManagerOnDifferentThread();
141 void macCopyUnicodeToClipboard();
149 tst_QWebPage::tst_QWebPage()
153 tst_QWebPage::~tst_QWebPage()
157 void tst_QWebPage::init()
159 m_view = new QWebView();
160 m_page = m_view->page();
163 void tst_QWebPage::cleanup()
168 void tst_QWebPage::cleanupFiles()
170 QFile::remove("Databases.db");
171 QDir::current().rmdir("http_www.myexample.com_0");
172 QFile::remove("http_www.myexample.com_0.localstorage");
175 void tst_QWebPage::initTestCase()
177 cleanupFiles(); // In case there are old files from previous runs
180 void tst_QWebPage::cleanupTestCase()
182 cleanupFiles(); // Be nice
185 class NavigationRequestOverride : public QWebPage
188 NavigationRequestOverride(QWebView* parent, bool initialValue) : QWebPage(parent), m_acceptNavigationRequest(initialValue) {}
190 bool m_acceptNavigationRequest;
192 virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, QWebPage::NavigationType type) {
197 return m_acceptNavigationRequest;
201 void tst_QWebPage::acceptNavigationRequest()
203 QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
205 NavigationRequestOverride* newPage = new NavigationRequestOverride(m_view, false);
206 m_view->setPage(newPage);
208 m_view->setHtml(QString("<html><body><form name='tstform' action='data:text/html,foo'method='get'>"
209 "<input type='text'><input type='submit'></form></body></html>"), QUrl());
210 QTRY_COMPARE(loadSpy.count(), 1);
212 m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
214 newPage->m_acceptNavigationRequest = true;
215 m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
216 QTRY_COMPARE(loadSpy.count(), 2);
218 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("foo?"));
220 // Restore default page
224 class JSTestPage : public QWebPage
228 JSTestPage(QObject* parent = 0)
229 : QWebPage(parent) {}
232 bool shouldInterruptJavaScript() {
235 void requestPermission(QWebFrame* frame, QWebPage::Feature feature)
237 if (m_allowGeolocation)
238 setFeaturePermission(frame, feature, PermissionGrantedByUser);
240 setFeaturePermission(frame, feature, PermissionDeniedByUser);
244 void setGeolocationPermission(bool allow)
246 m_allowGeolocation = allow;
250 bool m_allowGeolocation;
253 void tst_QWebPage::infiniteLoopJS()
255 JSTestPage* newPage = new JSTestPage(m_view);
256 m_view->setPage(newPage);
257 m_view->setHtml(QString("<html><body>test</body></html>"), QUrl());
258 m_view->page()->mainFrame()->evaluateJavaScript("var run = true;var a = 1;while(run){a++;}");
262 void tst_QWebPage::geolocationRequestJS()
264 JSTestPage* newPage = new JSTestPage(m_view);
266 if (newPage->mainFrame()->evaluateJavaScript(QLatin1String("!navigator.geolocation")).toBool()) {
268 QSKIP("Geolocation is not supported.", SkipSingle);
271 connect(newPage, SIGNAL(featurePermissionRequested(QWebFrame*, QWebPage::Feature)),
272 newPage, SLOT(requestPermission(QWebFrame*, QWebPage::Feature)));
274 newPage->setGeolocationPermission(false);
275 m_view->setPage(newPage);
276 m_view->setHtml(QString("<html><body>test</body></html>"), QUrl());
277 m_view->page()->mainFrame()->evaluateJavaScript("var errorCode = 0; function error(err) { errorCode = err.code; } function success(pos) { } navigator.geolocation.getCurrentPosition(success, error)");
279 QVariant empty = m_view->page()->mainFrame()->evaluateJavaScript("errorCode");
281 QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 0);
283 newPage->setGeolocationPermission(true);
284 m_view->page()->mainFrame()->evaluateJavaScript("errorCode = 0; navigator.geolocation.getCurrentPosition(success, error);");
285 empty = m_view->page()->mainFrame()->evaluateJavaScript("errorCode");
287 //http://dev.w3.org/geo/api/spec-source.html#position
288 //PositionError: const unsigned short PERMISSION_DENIED = 1;
289 QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 1);
293 void tst_QWebPage::loadFinished()
295 qRegisterMetaType<QWebFrame*>("QWebFrame*");
296 qRegisterMetaType<QNetworkRequest*>("QNetworkRequest*");
297 QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted()));
298 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
300 m_view->page()->mainFrame()->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
301 "<head><meta http-equiv='refresh' content='1'></head>foo \">"
302 "<frame src=\"data:text/html,bar\"></frameset>"));
303 QTRY_COMPARE(spyLoadFinished.count(), 1);
305 QTRY_VERIFY(spyLoadStarted.count() > 1);
306 QTRY_VERIFY(spyLoadFinished.count() > 1);
308 spyLoadFinished.clear();
310 m_view->page()->mainFrame()->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
311 "foo \"><frame src=\"data:text/html,bar\"></frameset>"));
312 QTRY_COMPARE(spyLoadFinished.count(), 1);
313 QCOMPARE(spyLoadFinished.count(), 1);
316 class ConsolePage : public QWebPage
319 ConsolePage(QObject* parent = 0) : QWebPage(parent) {}
321 virtual void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID)
323 messages.append(message);
324 lineNumbers.append(lineNumber);
325 sourceIDs.append(sourceID);
328 QStringList messages;
329 QList<int> lineNumbers;
330 QStringList sourceIDs;
333 void tst_QWebPage::consoleOutput()
336 page.mainFrame()->evaluateJavaScript("this is not valid JavaScript");
337 QCOMPARE(page.messages.count(), 1);
338 QCOMPARE(page.lineNumbers.at(0), 1);
341 class TestPage : public QWebPage
344 TestPage(QObject* parent = 0) : QWebPage(parent) {}
347 QPointer<QWebFrame> frame;
348 QNetworkRequest request;
352 QList<Navigation> navigations;
353 QList<QWebPage*> createdWindows;
355 virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, NavigationType type) {
360 navigations.append(n);
364 virtual QWebPage* createWindow(WebWindowType) {
365 QWebPage* page = new TestPage(this);
366 createdWindows.append(page);
371 void tst_QWebPage::acceptNavigationRequestWithNewWindow()
373 TestPage* page = new TestPage(m_view);
374 page->settings()->setAttribute(QWebSettings::LinksIncludedInFocusChain, true);
376 m_view->setPage(m_page);
378 m_view->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
379 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
381 QFocusEvent fe(QEvent::FocusIn);
384 QVERIFY(m_page->focusNextPrevChild(/*next*/ true));
386 QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
387 m_page->event(&keyEnter);
389 QCOMPARE(page->navigations.count(), 2);
391 TestPage::Navigation n = page->navigations.at(1);
392 QVERIFY(n.frame.isNull());
393 QCOMPARE(n.request.url().toString(), QString("data:text/html,Reached"));
394 QVERIFY(n.type == QWebPage::NavigationTypeLinkClicked);
396 QCOMPARE(page->createdWindows.count(), 1);
399 class TestNetworkManager : public QNetworkAccessManager
402 TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {}
404 QList<QUrl> requestedUrls;
405 QList<QNetworkRequest> requests;
408 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) {
409 requests.append(request);
410 requestedUrls.append(request.url());
411 return QNetworkAccessManager::createRequest(op, request, outgoingData);
415 void tst_QWebPage::userStyleSheet()
417 TestNetworkManager* networkManager = new TestNetworkManager(m_page);
418 m_page->setNetworkAccessManager(networkManager);
419 networkManager->requestedUrls.clear();
421 m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css;charset=utf-8;base64,"
422 + QByteArray("p { background-image: url('http://does.not/exist.png');}").toBase64()));
423 m_view->setHtml("<p>hello world</p>");
424 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
426 QVERIFY(networkManager->requestedUrls.count() >= 1);
427 QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png"));
430 void tst_QWebPage::viewModes()
432 m_view->setHtml("<body></body>");
433 m_page->setProperty("_q_viewMode", "minimized");
435 QVariant empty = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode)\")");
436 QVERIFY(empty.type() == QVariant::Bool && empty.toBool());
438 QVariant minimized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: minimized)\")");
439 QVERIFY(minimized.type() == QVariant::Bool && minimized.toBool());
441 QVariant maximized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: maximized)\")");
442 QVERIFY(maximized.type() == QVariant::Bool && !maximized.toBool());
445 void tst_QWebPage::modified()
447 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>blub"));
448 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
450 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah"));
451 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
453 QVERIFY(!m_page->isModified());
455 // m_page->mainFrame()->evaluateJavaScript("alert(document.getElementById('foo'))");
456 m_page->mainFrame()->evaluateJavaScript("document.getElementById('foo').focus()");
457 m_page->mainFrame()->evaluateJavaScript("document.execCommand('InsertText', true, 'Test');");
459 QVERIFY(m_page->isModified());
461 m_page->mainFrame()->evaluateJavaScript("document.execCommand('Undo', true);");
463 QVERIFY(!m_page->isModified());
465 m_page->mainFrame()->evaluateJavaScript("document.execCommand('Redo', true);");
467 QVERIFY(m_page->isModified());
469 QVERIFY(m_page->history()->canGoBack());
470 QVERIFY(!m_page->history()->canGoForward());
471 QCOMPARE(m_page->history()->count(), 2);
472 QVERIFY(m_page->history()->backItem().isValid());
473 QVERIFY(!m_page->history()->forwardItem().isValid());
475 m_page->history()->back();
476 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
478 QVERIFY(!m_page->history()->canGoBack());
479 QVERIFY(m_page->history()->canGoForward());
481 QVERIFY(!m_page->isModified());
483 QVERIFY(m_page->history()->currentItemIndex() == 0);
485 m_page->history()->setMaximumItemCount(3);
486 QVERIFY(m_page->history()->maximumItemCount() == 3);
488 QVariant variant("string test");
489 m_page->history()->currentItem().setUserData(variant);
490 QVERIFY(m_page->history()->currentItem().userData().toString() == "string test");
492 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is second page"));
493 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is third page"));
494 QVERIFY(m_page->history()->count() == 2);
495 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fourth page"));
496 QVERIFY(m_page->history()->count() == 2);
497 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fifth page"));
498 QVERIFY(::waitForSignal(m_page, SIGNAL(saveFrameStateRequested(QWebFrame*,QWebHistoryItem*))));
501 // https://bugs.webkit.org/show_bug.cgi?id=51331
502 void tst_QWebPage::updatePositionDependentActionsCrash()
505 view.setHtml("<p>test");
507 view.page()->updatePositionDependentActions(pos);
508 QMenu* contextMenu = 0;
509 foreach (QObject* child, view.children()) {
510 contextMenu = qobject_cast<QMenu*>(child);
514 QVERIFY(!contextMenu);
517 // https://bugs.webkit.org/show_bug.cgi?id=20357
518 void tst_QWebPage::contextMenuCrash()
521 view.setHtml("<p>test");
523 QContextMenuEvent event(QContextMenuEvent::Mouse, pos);
524 view.page()->swallowContextMenuEvent(&event);
525 view.page()->updatePositionDependentActions(pos);
526 QMenu* contextMenu = 0;
527 foreach (QObject* child, view.children()) {
528 contextMenu = qobject_cast<QMenu*>(child);
532 QVERIFY(contextMenu);
536 void tst_QWebPage::database()
538 QString path = QDir::currentPath();
539 m_page->settings()->setOfflineStoragePath(path);
540 QVERIFY(m_page->settings()->offlineStoragePath() == path);
542 QWebSettings::setOfflineStorageDefaultQuota(1024 * 1024);
543 QVERIFY(QWebSettings::offlineStorageDefaultQuota() == 1024 * 1024);
545 m_page->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
546 m_page->settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true);
548 QString dbFileName = path + "Databases.db";
550 if (QFile::exists(dbFileName))
551 QFile::remove(dbFileName);
553 qRegisterMetaType<QWebFrame*>("QWebFrame*");
554 QSignalSpy spy(m_page, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString)));
555 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"));
556 QTRY_COMPARE(spy.count(), 1);
557 m_page->mainFrame()->evaluateJavaScript("var db2; db2=openDatabase('testdb', '1.0', 'test database API', 50000);");
558 QTRY_COMPARE(spy.count(),1);
560 m_page->mainFrame()->evaluateJavaScript("localStorage.test='This is a test for local storage';");
561 m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
563 QVariant s1 = m_page->mainFrame()->evaluateJavaScript("localStorage.test");
564 QCOMPARE(s1.toString(), QString("This is a test for local storage"));
566 m_page->mainFrame()->evaluateJavaScript("sessionStorage.test='This is a test for session storage';");
567 m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
568 QVariant s2 = m_page->mainFrame()->evaluateJavaScript("sessionStorage.test");
569 QCOMPARE(s2.toString(), QString("This is a test for session storage"));
571 m_view->setHtml(QString("<html><head></head><body><div></div></body></html>"), QUrl("http://www.myexample.com"));
572 m_page->mainFrame()->evaluateJavaScript("var db3; db3=openDatabase('testdb', '1.0', 'test database API', 50000);db3.transaction(function(tx) { tx.executeSql('CREATE TABLE IF NOT EXISTS Test (text TEXT)', []); }, function(tx, result) { }, function(tx, error) { });");
575 // Remove all databases.
576 QWebSecurityOrigin origin = m_page->mainFrame()->securityOrigin();
577 QList<QWebDatabase> dbs = origin.databases();
578 for (int i = 0; i < dbs.count(); i++) {
579 QString fileName = dbs[i].fileName();
580 QVERIFY(QFile::exists(fileName));
581 QWebDatabase::removeDatabase(dbs[i]);
582 QVERIFY(!QFile::exists(fileName));
584 QVERIFY(!origin.databases().size());
585 // Remove removed test :-)
586 QWebDatabase::removeAllDatabases();
587 QVERIFY(!origin.databases().size());
590 class PluginPage : public QWebPage
593 PluginPage(QObject *parent = 0)
594 : QWebPage(parent) {}
598 CallInfo(const QString &c, const QUrl &u,
599 const QStringList &pn, const QStringList &pv,
601 : classid(c), url(u), paramNames(pn),
602 paramValues(pv), returnValue(r)
606 QStringList paramNames;
607 QStringList paramValues;
608 QObject *returnValue;
611 QList<CallInfo> calls;
614 virtual QObject *createPlugin(const QString &classid, const QUrl &url,
615 const QStringList ¶mNames,
616 const QStringList ¶mValues)
619 if (classid == "pushbutton")
620 result = new QPushButton();
621 #ifndef QT_NO_INPUTDIALOG
622 else if (classid == "lineedit")
623 result = new QLineEdit();
625 else if (classid == "graphicswidget")
626 result = new QGraphicsWidget();
628 result->setObjectName(classid);
629 calls.append(CallInfo(classid, url, paramNames, paramValues, result));
634 static void createPlugin(QWebView *view)
636 QSignalSpy loadSpy(view, SIGNAL(loadFinished(bool)));
638 PluginPage* newPage = new PluginPage(view);
639 view->setPage(newPage);
641 // type has to be application/x-qt-plugin
642 view->setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='pushbutton' id='mybutton'/></body></html>"));
643 QTRY_COMPARE(loadSpy.count(), 1);
644 QCOMPARE(newPage->calls.count(), 0);
646 view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>"));
647 QTRY_COMPARE(loadSpy.count(), 2);
648 QCOMPARE(newPage->calls.count(), 1);
650 PluginPage::CallInfo ci = newPage->calls.takeFirst();
651 QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
652 QCOMPARE(ci.url, QUrl());
653 QCOMPARE(ci.paramNames.count(), 3);
654 QCOMPARE(ci.paramValues.count(), 3);
655 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
656 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
657 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
658 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton"));
659 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
660 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton"));
661 QVERIFY(ci.returnValue != 0);
662 QVERIFY(ci.returnValue->inherits("QPushButton"));
665 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mybutton').toString()").toString(),
666 QString::fromLatin1("[object HTMLObjectElement]"));
667 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.toString()").toString(),
668 QString::fromLatin1("[object HTMLObjectElement]"));
669 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.objectName").toString(),
670 QString::fromLatin1("string"));
671 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.objectName").toString(),
672 QString::fromLatin1("pushbutton"));
673 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.clicked").toString(),
674 QString::fromLatin1("function"));
675 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.clicked.toString()").toString(),
676 QString::fromLatin1("function clicked() {\n [native code]\n}"));
678 view->setHtml(QString("<html><body><table>"
679 "<tr><object type='application/x-qt-plugin' classid='lineedit' id='myedit'/></tr>"
680 "<tr><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></tr>"
681 "</table></body></html>"), QUrl("http://foo.bar.baz"));
682 QTRY_COMPARE(loadSpy.count(), 3);
683 QCOMPARE(newPage->calls.count(), 2);
685 PluginPage::CallInfo ci = newPage->calls.takeFirst();
686 QCOMPARE(ci.classid, QString::fromLatin1("lineedit"));
687 QCOMPARE(ci.url, QUrl());
688 QCOMPARE(ci.paramNames.count(), 3);
689 QCOMPARE(ci.paramValues.count(), 3);
690 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
691 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
692 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
693 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("lineedit"));
694 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
695 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("myedit"));
696 QVERIFY(ci.returnValue != 0);
697 QVERIFY(ci.returnValue->inherits("QLineEdit"));
700 PluginPage::CallInfo ci = newPage->calls.takeFirst();
701 QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
702 QCOMPARE(ci.url, QUrl());
703 QCOMPARE(ci.paramNames.count(), 3);
704 QCOMPARE(ci.paramValues.count(), 3);
705 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
706 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
707 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
708 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton"));
709 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
710 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton"));
711 QVERIFY(ci.returnValue != 0);
712 QVERIFY(ci.returnValue->inherits("QPushButton"));
716 void tst_QWebPage::graphicsWidgetPlugin()
718 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
719 QGraphicsWebView webView;
721 QSignalSpy loadSpy(&webView, SIGNAL(loadFinished(bool)));
723 PluginPage* newPage = new PluginPage(&webView);
724 webView.setPage(newPage);
726 // type has to be application/x-qt-plugin
727 webView.setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='graphicswidget' id='mygraphicswidget'/></body></html>"));
728 QTRY_COMPARE(loadSpy.count(), 1);
729 QCOMPARE(newPage->calls.count(), 0);
731 webView.setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='graphicswidget' id='mygraphicswidget'/></body></html>"));
732 QTRY_COMPARE(loadSpy.count(), 2);
733 QCOMPARE(newPage->calls.count(), 1);
735 PluginPage::CallInfo ci = newPage->calls.takeFirst();
736 QCOMPARE(ci.classid, QString::fromLatin1("graphicswidget"));
737 QCOMPARE(ci.url, QUrl());
738 QCOMPARE(ci.paramNames.count(), 3);
739 QCOMPARE(ci.paramValues.count(), 3);
740 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
741 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
742 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
743 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("graphicswidget"));
744 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
745 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mygraphicswidget"));
746 QVERIFY(ci.returnValue);
747 QVERIFY(ci.returnValue->inherits("QGraphicsWidget"));
750 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mygraphicswidget').toString()").toString(),
751 QString::fromLatin1("[object HTMLObjectElement]"));
752 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.toString()").toString(),
753 QString::fromLatin1("[object HTMLObjectElement]"));
754 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mygraphicswidget.objectName").toString(),
755 QString::fromLatin1("string"));
756 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.objectName").toString(),
757 QString::fromLatin1("graphicswidget"));
758 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mygraphicswidget.geometryChanged").toString(),
759 QString::fromLatin1("function"));
760 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.geometryChanged.toString()").toString(),
761 QString::fromLatin1("function geometryChanged() {\n [native code]\n}"));
764 void tst_QWebPage::createPluginWithPluginsEnabled()
766 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
767 createPlugin(m_view);
770 void tst_QWebPage::createPluginWithPluginsDisabled()
772 // Qt Plugins should be loaded by QtWebKit even when PluginsEnabled is
773 // false. The client decides whether a Qt plugin is enabled or not when
774 // it decides whether or not to instantiate it.
775 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, false);
776 createPlugin(m_view);
779 // Standard base class for template PluginTracerPage. In tests it is used as interface.
780 class PluginCounterPage : public QWebPage {
783 QPointer<QObject> m_widget;
784 QObject* m_pluginParent;
785 PluginCounterPage(QObject* parent = 0)
791 settings()->setAttribute(QWebSettings::PluginsEnabled, true);
796 m_pluginParent->deleteLater();
801 class PluginTracerPage : public PluginCounterPage {
803 PluginTracerPage(QObject* parent = 0)
804 : PluginCounterPage(parent)
806 // this is a dummy parent object for the created plugin
807 m_pluginParent = new T;
809 virtual QObject* createPlugin(const QString&, const QUrl&, const QStringList&, const QStringList&)
813 // need a cast to the specific type, as QObject::setParent cannot be called,
814 // because it is not virtual. Instead it is necesary to call QWidget::setParent,
815 // which also takes a QWidget* instead of a QObject*. Therefore we need to
816 // upcast to T*, which is a QWidget.
817 static_cast<T*>(m_widget.data())->setParent(static_cast<T*>(m_pluginParent));
822 class PluginFactory {
824 enum FactoredType {QWidgetType, QGraphicsWidgetType};
825 static PluginCounterPage* create(FactoredType type, QObject* parent = 0)
827 PluginCounterPage* result = 0;
830 result = new PluginTracerPage<QWidget>(parent);
832 case QGraphicsWidgetType:
833 result = new PluginTracerPage<QGraphicsWidget>(parent);
840 static void prepareTestData()
842 QTest::addColumn<int>("type");
843 QTest::newRow("QWidget") << (int)PluginFactory::QWidgetType;
844 QTest::newRow("QGraphicsWidget") << (int)PluginFactory::QGraphicsWidgetType;
848 void tst_QWebPage::destroyPlugin_data()
850 PluginFactory::prepareTestData();
853 void tst_QWebPage::destroyPlugin()
856 PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type, m_view);
857 m_view->setPage(page);
859 // we create the plugin, so the widget should be constructed
860 QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
861 m_view->setHtml(content);
862 QVERIFY(page->m_widget);
863 QCOMPARE(page->m_count, 1);
865 // navigate away, the plugin widget should be destructed
866 m_view->setHtml("<html><body>Hi</body></html>");
867 QTestEventLoop::instance().enterLoop(1);
868 QVERIFY(!page->m_widget);
871 void tst_QWebPage::createViewlessPlugin_data()
873 PluginFactory::prepareTestData();
876 void tst_QWebPage::createViewlessPlugin()
879 PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type);
880 QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
881 page->mainFrame()->setHtml(content);
882 QCOMPARE(page->m_count, 1);
883 QVERIFY(page->m_widget);
884 QVERIFY(page->m_pluginParent);
885 QVERIFY(page->m_widget->parent() == page->m_pluginParent);
890 void tst_QWebPage::multiplePageGroupsAndLocalStorage()
892 QDir dir(QDir::currentPath());
899 view1.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
900 view1.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path1"));
901 DumpRenderTreeSupportQt::webPageSetGroupName(view1.page(), "group1");
902 view2.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
903 view2.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path2"));
904 DumpRenderTreeSupportQt::webPageSetGroupName(view2.page(), "group2");
905 QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view1.page()), QString("group1"));
906 QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view2.page()), QString("group2"));
909 view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
910 view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
912 view1.page()->mainFrame()->evaluateJavaScript("localStorage.test='value1';");
913 view2.page()->mainFrame()->evaluateJavaScript("localStorage.test='value2';");
915 view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
916 view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
918 QVariant s1 = view1.page()->mainFrame()->evaluateJavaScript("localStorage.test");
919 QCOMPARE(s1.toString(), QString("value1"));
921 QVariant s2 = view2.page()->mainFrame()->evaluateJavaScript("localStorage.test");
922 QCOMPARE(s2.toString(), QString("value2"));
926 QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path1/http_www.myexample.com_0.localstorage"));
927 QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path2/http_www.myexample.com_0.localstorage"));
928 dir.rmdir(QDir::toNativeSeparators("./path1"));
929 dir.rmdir(QDir::toNativeSeparators("./path2"));
932 class CursorTrackedPage : public QWebPage
936 CursorTrackedPage(QWidget *parent = 0): QWebPage(parent) {
937 setViewportSize(QSize(1024, 768)); // big space
940 QString selectedText() {
941 return mainFrame()->evaluateJavaScript("window.getSelection().toString()").toString();
944 int selectionStartOffset() {
945 return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).startOffset").toInt();
948 int selectionEndOffset() {
949 return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).endOffset").toInt();
952 // true if start offset == end offset, i.e. no selected text
953 int isSelectionCollapsed() {
954 return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).collapsed").toBool();
958 void tst_QWebPage::cursorMovements()
960 CursorTrackedPage* page = new CursorTrackedPage;
961 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>");
962 page->mainFrame()->setHtml(content);
964 // this will select the first paragraph
965 QString script = "var range = document.createRange(); " \
966 "var node = document.getElementById(\"one\"); " \
967 "range.selectNode(node); " \
968 "getSelection().addRange(range);";
969 page->mainFrame()->evaluateJavaScript(script);
970 QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
971 QCOMPARE(page->selectedHtml().trimmed(), QString::fromLatin1("<span class=\"Apple-style-span\" style=\"border-collapse: separate; color: rgb(0, 0, 0); font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; \"><p id=\"one\">The quick brown fox</p></span>"));
973 // these actions must exist
974 QVERIFY(page->action(QWebPage::MoveToNextChar) != 0);
975 QVERIFY(page->action(QWebPage::MoveToPreviousChar) != 0);
976 QVERIFY(page->action(QWebPage::MoveToNextWord) != 0);
977 QVERIFY(page->action(QWebPage::MoveToPreviousWord) != 0);
978 QVERIFY(page->action(QWebPage::MoveToNextLine) != 0);
979 QVERIFY(page->action(QWebPage::MoveToPreviousLine) != 0);
980 QVERIFY(page->action(QWebPage::MoveToStartOfLine) != 0);
981 QVERIFY(page->action(QWebPage::MoveToEndOfLine) != 0);
982 QVERIFY(page->action(QWebPage::MoveToStartOfBlock) != 0);
983 QVERIFY(page->action(QWebPage::MoveToEndOfBlock) != 0);
984 QVERIFY(page->action(QWebPage::MoveToStartOfDocument) != 0);
985 QVERIFY(page->action(QWebPage::MoveToEndOfDocument) != 0);
987 // right now they are disabled because contentEditable is false
988 QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), false);
989 QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), false);
990 QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), false);
991 QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), false);
992 QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), false);
993 QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), false);
994 QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), false);
995 QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), false);
996 QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), false);
997 QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), false);
998 QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), false);
999 QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), false);
1001 // make it editable before navigating the cursor
1002 page->setContentEditable(true);
1004 // here the actions are enabled after contentEditable is true
1005 QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), true);
1006 QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), true);
1007 QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), true);
1008 QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), true);
1009 QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), true);
1010 QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), true);
1011 QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), true);
1012 QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), true);
1013 QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), true);
1014 QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), true);
1015 QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), true);
1016 QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), true);
1018 // cursor will be before the word "jump"
1019 page->triggerAction(QWebPage::MoveToNextChar);
1020 QVERIFY(page->isSelectionCollapsed());
1021 QCOMPARE(page->selectionStartOffset(), 0);
1023 // cursor will be between 'j' and 'u' in the word "jump"
1024 page->triggerAction(QWebPage::MoveToNextChar);
1025 QVERIFY(page->isSelectionCollapsed());
1026 QCOMPARE(page->selectionStartOffset(), 1);
1028 // cursor will be between 'u' and 'm' in the word "jump"
1029 page->triggerAction(QWebPage::MoveToNextChar);
1030 QVERIFY(page->isSelectionCollapsed());
1031 QCOMPARE(page->selectionStartOffset(), 2);
1033 // cursor will be after the word "jump"
1034 page->triggerAction(QWebPage::MoveToNextWord);
1035 QVERIFY(page->isSelectionCollapsed());
1036 QCOMPARE(page->selectionStartOffset(), 5);
1038 // cursor will be after the word "lazy"
1039 page->triggerAction(QWebPage::MoveToNextWord);
1040 page->triggerAction(QWebPage::MoveToNextWord);
1041 page->triggerAction(QWebPage::MoveToNextWord);
1042 QVERIFY(page->isSelectionCollapsed());
1043 QCOMPARE(page->selectionStartOffset(), 19);
1045 // cursor will be between 'z' and 'y' in "lazy"
1046 page->triggerAction(QWebPage::MoveToPreviousChar);
1047 QVERIFY(page->isSelectionCollapsed());
1048 QCOMPARE(page->selectionStartOffset(), 18);
1050 // cursor will be between 'a' and 'z' in "lazy"
1051 page->triggerAction(QWebPage::MoveToPreviousChar);
1052 QVERIFY(page->isSelectionCollapsed());
1053 QCOMPARE(page->selectionStartOffset(), 17);
1055 // cursor will be before the word "lazy"
1056 page->triggerAction(QWebPage::MoveToPreviousWord);
1057 QVERIFY(page->isSelectionCollapsed());
1058 QCOMPARE(page->selectionStartOffset(), 15);
1060 // cursor will be before the word "quick"
1061 page->triggerAction(QWebPage::MoveToPreviousWord);
1062 page->triggerAction(QWebPage::MoveToPreviousWord);
1063 page->triggerAction(QWebPage::MoveToPreviousWord);
1064 page->triggerAction(QWebPage::MoveToPreviousWord);
1065 page->triggerAction(QWebPage::MoveToPreviousWord);
1066 page->triggerAction(QWebPage::MoveToPreviousWord);
1067 QVERIFY(page->isSelectionCollapsed());
1068 QCOMPARE(page->selectionStartOffset(), 4);
1070 // cursor will be between 'p' and 's' in the word "jumps"
1071 page->triggerAction(QWebPage::MoveToNextWord);
1072 page->triggerAction(QWebPage::MoveToNextWord);
1073 page->triggerAction(QWebPage::MoveToNextWord);
1074 page->triggerAction(QWebPage::MoveToNextChar);
1075 page->triggerAction(QWebPage::MoveToNextChar);
1076 page->triggerAction(QWebPage::MoveToNextChar);
1077 page->triggerAction(QWebPage::MoveToNextChar);
1078 page->triggerAction(QWebPage::MoveToNextChar);
1079 QVERIFY(page->isSelectionCollapsed());
1080 QCOMPARE(page->selectionStartOffset(), 4);
1082 // cursor will be before the word "jumps"
1083 page->triggerAction(QWebPage::MoveToStartOfLine);
1084 QVERIFY(page->isSelectionCollapsed());
1085 QCOMPARE(page->selectionStartOffset(), 0);
1087 // cursor will be after the word "dog"
1088 page->triggerAction(QWebPage::MoveToEndOfLine);
1089 QVERIFY(page->isSelectionCollapsed());
1090 QCOMPARE(page->selectionStartOffset(), 23);
1092 // cursor will be between 'w' and 'n' in "brown"
1093 page->triggerAction(QWebPage::MoveToStartOfLine);
1094 page->triggerAction(QWebPage::MoveToPreviousWord);
1095 page->triggerAction(QWebPage::MoveToPreviousWord);
1096 page->triggerAction(QWebPage::MoveToNextChar);
1097 page->triggerAction(QWebPage::MoveToNextChar);
1098 page->triggerAction(QWebPage::MoveToNextChar);
1099 page->triggerAction(QWebPage::MoveToNextChar);
1100 QVERIFY(page->isSelectionCollapsed());
1101 QCOMPARE(page->selectionStartOffset(), 14);
1103 // cursor will be after the word "fox"
1104 page->triggerAction(QWebPage::MoveToEndOfLine);
1105 QVERIFY(page->isSelectionCollapsed());
1106 QCOMPARE(page->selectionStartOffset(), 19);
1108 // cursor will be before the word "The"
1109 page->triggerAction(QWebPage::MoveToStartOfDocument);
1110 QVERIFY(page->isSelectionCollapsed());
1111 QCOMPARE(page->selectionStartOffset(), 0);
1113 // cursor will be after the word "you!"
1114 page->triggerAction(QWebPage::MoveToEndOfDocument);
1115 QVERIFY(page->isSelectionCollapsed());
1116 QCOMPARE(page->selectionStartOffset(), 12);
1118 // cursor will be before the word "be"
1119 page->triggerAction(QWebPage::MoveToStartOfBlock);
1120 QVERIFY(page->isSelectionCollapsed());
1121 QCOMPARE(page->selectionStartOffset(), 0);
1123 // cursor will be after the word "you!"
1124 page->triggerAction(QWebPage::MoveToEndOfBlock);
1125 QVERIFY(page->isSelectionCollapsed());
1126 QCOMPARE(page->selectionStartOffset(), 12);
1128 // try to move before the document start
1129 page->triggerAction(QWebPage::MoveToStartOfDocument);
1130 page->triggerAction(QWebPage::MoveToPreviousChar);
1131 QVERIFY(page->isSelectionCollapsed());
1132 QCOMPARE(page->selectionStartOffset(), 0);
1133 page->triggerAction(QWebPage::MoveToStartOfDocument);
1134 page->triggerAction(QWebPage::MoveToPreviousWord);
1135 QVERIFY(page->isSelectionCollapsed());
1136 QCOMPARE(page->selectionStartOffset(), 0);
1138 // try to move past the document end
1139 page->triggerAction(QWebPage::MoveToEndOfDocument);
1140 page->triggerAction(QWebPage::MoveToNextChar);
1141 QVERIFY(page->isSelectionCollapsed());
1142 QCOMPARE(page->selectionStartOffset(), 12);
1143 page->triggerAction(QWebPage::MoveToEndOfDocument);
1144 page->triggerAction(QWebPage::MoveToNextWord);
1145 QVERIFY(page->isSelectionCollapsed());
1146 QCOMPARE(page->selectionStartOffset(), 12);
1151 void tst_QWebPage::textSelection()
1153 CursorTrackedPage* page = new CursorTrackedPage;
1154 QString content("<html><body><p id=one>The quick brown fox</p>" \
1155 "<p id=two>jumps over the lazy dog</p>" \
1156 "<p>May the source<br/>be with you!</p></body></html>");
1157 page->mainFrame()->setHtml(content);
1159 // these actions must exist
1160 QVERIFY(page->action(QWebPage::SelectAll) != 0);
1161 QVERIFY(page->action(QWebPage::SelectNextChar) != 0);
1162 QVERIFY(page->action(QWebPage::SelectPreviousChar) != 0);
1163 QVERIFY(page->action(QWebPage::SelectNextWord) != 0);
1164 QVERIFY(page->action(QWebPage::SelectPreviousWord) != 0);
1165 QVERIFY(page->action(QWebPage::SelectNextLine) != 0);
1166 QVERIFY(page->action(QWebPage::SelectPreviousLine) != 0);
1167 QVERIFY(page->action(QWebPage::SelectStartOfLine) != 0);
1168 QVERIFY(page->action(QWebPage::SelectEndOfLine) != 0);
1169 QVERIFY(page->action(QWebPage::SelectStartOfBlock) != 0);
1170 QVERIFY(page->action(QWebPage::SelectEndOfBlock) != 0);
1171 QVERIFY(page->action(QWebPage::SelectStartOfDocument) != 0);
1172 QVERIFY(page->action(QWebPage::SelectEndOfDocument) != 0);
1174 // right now they are disabled because contentEditable is false and
1175 // there isn't an existing selection to modify
1176 QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), false);
1177 QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), false);
1178 QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), false);
1179 QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), false);
1180 QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), false);
1181 QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), false);
1182 QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), false);
1183 QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), false);
1184 QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), false);
1185 QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), false);
1186 QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), false);
1187 QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), false);
1189 // ..but SelectAll is awalys enabled
1190 QCOMPARE(page->action(QWebPage::SelectAll)->isEnabled(), true);
1192 // Verify hasSelection returns false since there is no selection yet...
1193 QCOMPARE(page->hasSelection(), false);
1195 // this will select the first paragraph
1196 QString selectScript = "var range = document.createRange(); " \
1197 "var node = document.getElementById(\"one\"); " \
1198 "range.selectNode(node); " \
1199 "getSelection().addRange(range);";
1200 page->mainFrame()->evaluateJavaScript(selectScript);
1201 QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
1202 QCOMPARE(page->selectedHtml().trimmed(), QString::fromLatin1("<span class=\"Apple-style-span\" style=\"border-collapse: separate; color: rgb(0, 0, 0); font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; \"><p id=\"one\">The quick brown fox</p></span>"));
1204 // Make sure hasSelection returns true, since there is selected text now...
1205 QCOMPARE(page->hasSelection(), true);
1207 // here the actions are enabled after a selection has been created
1208 QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true);
1209 QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true);
1210 QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true);
1211 QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true);
1212 QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true);
1213 QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true);
1214 QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true);
1215 QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true);
1216 QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true);
1217 QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true);
1218 QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true);
1219 QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true);
1221 // make it editable before navigating the cursor
1222 page->setContentEditable(true);
1224 // cursor will be before the word "The", this makes sure there is a charet
1225 page->triggerAction(QWebPage::MoveToStartOfDocument);
1226 QVERIFY(page->isSelectionCollapsed());
1227 QCOMPARE(page->selectionStartOffset(), 0);
1229 // here the actions are enabled after contentEditable is true
1230 QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true);
1231 QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true);
1232 QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true);
1233 QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true);
1234 QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true);
1235 QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true);
1236 QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true);
1237 QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true);
1238 QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true);
1239 QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true);
1240 QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true);
1241 QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true);
1246 void tst_QWebPage::textEditing()
1248 CursorTrackedPage* page = new CursorTrackedPage;
1249 QString content("<html><body><p id=one>The quick brown fox</p>" \
1250 "<p id=two>jumps over the lazy dog</p>" \
1251 "<p>May the source<br/>be with you!</p></body></html>");
1252 page->mainFrame()->setHtml(content);
1254 // these actions must exist
1255 QVERIFY(page->action(QWebPage::Cut) != 0);
1256 QVERIFY(page->action(QWebPage::Copy) != 0);
1257 QVERIFY(page->action(QWebPage::Paste) != 0);
1258 QVERIFY(page->action(QWebPage::DeleteStartOfWord) != 0);
1259 QVERIFY(page->action(QWebPage::DeleteEndOfWord) != 0);
1260 QVERIFY(page->action(QWebPage::SetTextDirectionDefault) != 0);
1261 QVERIFY(page->action(QWebPage::SetTextDirectionLeftToRight) != 0);
1262 QVERIFY(page->action(QWebPage::SetTextDirectionRightToLeft) != 0);
1263 QVERIFY(page->action(QWebPage::ToggleBold) != 0);
1264 QVERIFY(page->action(QWebPage::ToggleItalic) != 0);
1265 QVERIFY(page->action(QWebPage::ToggleUnderline) != 0);
1266 QVERIFY(page->action(QWebPage::InsertParagraphSeparator) != 0);
1267 QVERIFY(page->action(QWebPage::InsertLineSeparator) != 0);
1268 QVERIFY(page->action(QWebPage::PasteAndMatchStyle) != 0);
1269 QVERIFY(page->action(QWebPage::RemoveFormat) != 0);
1270 QVERIFY(page->action(QWebPage::ToggleStrikethrough) != 0);
1271 QVERIFY(page->action(QWebPage::ToggleSubscript) != 0);
1272 QVERIFY(page->action(QWebPage::ToggleSuperscript) != 0);
1273 QVERIFY(page->action(QWebPage::InsertUnorderedList) != 0);
1274 QVERIFY(page->action(QWebPage::InsertOrderedList) != 0);
1275 QVERIFY(page->action(QWebPage::Indent) != 0);
1276 QVERIFY(page->action(QWebPage::Outdent) != 0);
1277 QVERIFY(page->action(QWebPage::AlignCenter) != 0);
1278 QVERIFY(page->action(QWebPage::AlignJustified) != 0);
1279 QVERIFY(page->action(QWebPage::AlignLeft) != 0);
1280 QVERIFY(page->action(QWebPage::AlignRight) != 0);
1282 // right now they are disabled because contentEditable is false
1283 QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false);
1284 QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), false);
1285 QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), false);
1286 QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), false);
1287 QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), false);
1288 QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), false);
1289 QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), false);
1290 QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), false);
1291 QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), false);
1292 QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), false);
1293 QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), false);
1294 QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), false);
1295 QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), false);
1296 QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false);
1297 QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), false);
1298 QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), false);
1299 QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), false);
1300 QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), false);
1301 QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), false);
1302 QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), false);
1303 QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), false);
1304 QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), false);
1305 QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), false);
1306 QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), false);
1307 QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), false);
1309 // Select everything
1310 page->triggerAction(QWebPage::SelectAll);
1312 // make sure it is enabled since there is a selection
1313 QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), true);
1315 // make it editable before navigating the cursor
1316 page->setContentEditable(true);
1318 // clear the selection
1319 page->triggerAction(QWebPage::MoveToStartOfDocument);
1320 QVERIFY(page->isSelectionCollapsed());
1321 QCOMPARE(page->selectionStartOffset(), 0);
1323 // make sure it is disabled since there isn't a selection
1324 QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), false);
1326 // here the actions are enabled after contentEditable is true
1327 QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), true);
1328 QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), true);
1329 QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), true);
1330 QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), true);
1331 QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), true);
1332 QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), true);
1333 QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), true);
1334 QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), true);
1335 QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), true);
1336 QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), true);
1337 QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), true);
1338 QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), true);
1339 QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), true);
1340 QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), true);
1341 QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), true);
1342 QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), true);
1343 QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), true);
1344 QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), true);
1345 QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), true);
1346 QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), true);
1347 QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), true);
1348 QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), true);
1349 QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), true);
1351 // make sure these are disabled since there isn't a selection
1352 QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false);
1353 QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false);
1355 // make sure everything is selected
1356 page->triggerAction(QWebPage::SelectAll);
1358 // this is only true if there is an editable selection
1359 QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), true);
1360 QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), true);
1365 void tst_QWebPage::requestCache()
1368 QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
1370 page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
1371 QTRY_COMPARE(loadSpy.count(), 1);
1372 QTRY_COMPARE(page.navigations.count(), 1);
1374 page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me2</a>"));
1375 QTRY_COMPARE(loadSpy.count(), 2);
1376 QTRY_COMPARE(page.navigations.count(), 2);
1378 page.triggerAction(QWebPage::Stop);
1379 QVERIFY(page.history()->canGoBack());
1380 page.triggerAction(QWebPage::Back);
1382 QTRY_COMPARE(loadSpy.count(), 3);
1383 QTRY_COMPARE(page.navigations.count(), 3);
1384 QCOMPARE(page.navigations.at(0).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
1385 (int)QNetworkRequest::PreferNetwork);
1386 QCOMPARE(page.navigations.at(1).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
1387 (int)QNetworkRequest::PreferNetwork);
1388 QCOMPARE(page.navigations.at(2).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
1389 (int)QNetworkRequest::PreferCache);
1392 void tst_QWebPage::loadCachedPage()
1395 QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
1396 page.settings()->setMaximumPagesInCache(3);
1398 page.mainFrame()->load(QUrl("data:text/html,This is first page"));
1400 QTRY_COMPARE(loadSpy.count(), 1);
1401 QTRY_COMPARE(page.navigations.count(), 1);
1403 QUrl firstPageUrl = page.mainFrame()->url();
1404 page.mainFrame()->load(QUrl("data:text/html,This is second page"));
1406 QTRY_COMPARE(loadSpy.count(), 2);
1407 QTRY_COMPARE(page.navigations.count(), 2);
1409 page.triggerAction(QWebPage::Stop);
1410 QVERIFY(page.history()->canGoBack());
1412 QSignalSpy urlSpy(page.mainFrame(), SIGNAL(urlChanged(QUrl)));
1413 QVERIFY(urlSpy.isValid());
1415 page.triggerAction(QWebPage::Back);
1416 ::waitForSignal(page.mainFrame(), SIGNAL(urlChanged(QUrl)));
1417 QCOMPARE(urlSpy.size(), 1);
1419 QList<QVariant> arguments1 = urlSpy.takeFirst();
1420 QCOMPARE(arguments1.at(0).toUrl(), firstPageUrl);
1423 void tst_QWebPage::backActionUpdate()
1426 QWebPage *page = view.page();
1427 QAction *action = page->action(QWebPage::Back);
1428 QVERIFY(!action->isEnabled());
1429 QSignalSpy loadSpy(page, SIGNAL(loadFinished(bool)));
1430 QUrl url = QUrl("qrc:///resources/index.html");
1431 page->mainFrame()->load(url);
1432 QTRY_COMPARE(loadSpy.count(), 1);
1433 QVERIFY(!action->isEnabled());
1434 QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(10, 10));
1435 QTRY_COMPARE(loadSpy.count(), 2);
1437 QVERIFY(action->isEnabled());
1440 void frameAtHelper(QWebPage* webPage, QWebFrame* webFrame, QPoint framePosition)
1445 framePosition += QPoint(webFrame->pos());
1446 QList<QWebFrame*> children = webFrame->childFrames();
1447 for (int i = 0; i < children.size(); ++i) {
1448 if (children.at(i)->childFrames().size() > 0)
1449 frameAtHelper(webPage, children.at(i), framePosition);
1451 QRect frameRect(children.at(i)->pos() + framePosition, children.at(i)->geometry().size());
1452 QVERIFY(children.at(i) == webPage->frameAt(frameRect.topLeft()));
1456 void tst_QWebPage::frameAt()
1459 QWebPage* webPage = webView.page();
1460 QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool)));
1461 QUrl url = QUrl("qrc:///resources/iframe.html");
1462 webPage->mainFrame()->load(url);
1463 QTRY_COMPARE(loadSpy.count(), 1);
1464 frameAtHelper(webPage, webPage->mainFrame(), webPage->mainFrame()->pos());
1467 void tst_QWebPage::inputMethods_data()
1469 QTest::addColumn<QString>("viewType");
1470 QTest::newRow("QWebView") << "QWebView";
1471 QTest::newRow("QGraphicsWebView") << "QGraphicsWebView";
1474 static Qt::InputMethodHints inputMethodHints(QObject* object)
1476 if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
1477 return o->inputMethodHints();
1478 if (QWidget* w = qobject_cast<QWidget*>(object))
1479 return w->inputMethodHints();
1480 return Qt::InputMethodHints();
1483 static bool inputMethodEnabled(QObject* object)
1485 if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
1486 return o->flags() & QGraphicsItem::ItemAcceptsInputMethod;
1487 if (QWidget* w = qobject_cast<QWidget*>(object))
1488 return w->testAttribute(Qt::WA_InputMethodEnabled);
1492 static void clickOnPage(QWebPage* page, const QPoint& position)
1494 QMouseEvent evpres(QEvent::MouseButtonPress, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1495 page->event(&evpres);
1496 QMouseEvent evrel(QEvent::MouseButtonRelease, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1497 page->event(&evrel);
1500 void tst_QWebPage::inputMethods()
1502 QFETCH(QString, viewType);
1503 QWebPage* page = new QWebPage;
1505 QObject* container = 0;
1506 if (viewType == "QWebView") {
1507 QWebView* wv = new QWebView;
1511 } else if (viewType == "QGraphicsWebView") {
1512 QGraphicsWebView* wv = new QGraphicsWebView;
1516 QGraphicsView* gv = new QGraphicsView;
1517 QGraphicsScene* scene = new QGraphicsScene(gv);
1518 gv->setScene(scene);
1520 wv->setGeometry(QRect(0, 0, 500, 500));
1524 QVERIFY2(false, "Unknown view type");
1526 page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont");
1527 page->mainFrame()->setHtml("<html><body>" \
1528 "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/><br>" \
1529 "<input type='password'/>" \
1531 page->mainFrame()->setFocus();
1533 EventSpy viewEventSpy(container);
1535 QWebElementCollection inputs = page->mainFrame()->documentElement().findAll("input");
1536 QPoint textInputCenter = inputs.at(0).geometry().center();
1538 clickOnPage(page, textInputCenter);
1540 // This part of the test checks if the SIP (Software Input Panel) is triggered,
1541 // which normally happens on mobile platforms, when a user input form receives
1544 if (viewType == "QWebView") {
1545 if (QWebView* wv = qobject_cast<QWebView*>(view))
1546 inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel);
1547 } else if (viewType == "QGraphicsWebView") {
1548 if (QGraphicsWebView* wv = qobject_cast<QGraphicsWebView*>(view))
1549 inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel);
1552 // For non-mobile platforms RequestSoftwareInputPanel event is not called
1553 // because there is no SIP (Software Input Panel) triggered. In the case of a
1554 // mobile platform, an input panel, e.g. virtual keyboard, is usually invoked
1555 // and the RequestSoftwareInputPanel event is called. For these two situations
1556 // this part of the test can verified as the checks below.
1558 QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1560 QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1561 viewEventSpy.clear();
1563 clickOnPage(page, textInputCenter);
1564 QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1567 QVariant variant = page->inputMethodQuery(Qt::ImMicroFocus);
1568 QRect focusRect = variant.toRect();
1569 QVERIFY(inputs.at(0).geometry().contains(variant.toRect().topLeft()));
1572 variant = page->inputMethodQuery(Qt::ImFont);
1573 QFont font = variant.value<QFont>();
1574 QCOMPARE(page->settings()->fontFamily(QWebSettings::SerifFont), font.family());
1576 QList<QInputMethodEvent::Attribute> inputAttributes;
1580 QInputMethodEvent eventText("QtWebKit", inputAttributes);
1581 QSignalSpy signalSpy(page, SIGNAL(microFocusChanged()));
1582 page->event(&eventText);
1583 QCOMPARE(signalSpy.count(), 0);
1587 QInputMethodEvent eventText("", inputAttributes);
1588 eventText.setCommitString(QString("QtWebKit"), 0, 0);
1589 page->event(&eventText);
1592 //ImMaximumTextLength
1593 variant = page->inputMethodQuery(Qt::ImMaximumTextLength);
1594 QCOMPARE(20, variant.toInt());
1597 inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant());
1598 QInputMethodEvent eventSelection("",inputAttributes);
1599 page->event(&eventSelection);
1602 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1603 int anchorPosition = variant.toInt();
1604 QCOMPARE(anchorPosition, 3);
1607 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1608 int cursorPosition = variant.toInt();
1609 QCOMPARE(cursorPosition, 5);
1611 //ImCurrentSelection
1612 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1613 QString selectionValue = variant.value<QString>();
1614 QCOMPARE(selectionValue, QString("eb"));
1616 //Set selection with negative length
1617 inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 6, -5, QVariant());
1618 QInputMethodEvent eventSelection3("",inputAttributes);
1619 page->event(&eventSelection3);
1622 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1623 anchorPosition = variant.toInt();
1624 QCOMPARE(anchorPosition, 1);
1627 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1628 cursorPosition = variant.toInt();
1629 QCOMPARE(cursorPosition, 6);
1631 //ImCurrentSelection
1632 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1633 selectionValue = variant.value<QString>();
1634 QCOMPARE(selectionValue, QString("tWebK"));
1637 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1638 QString value = variant.value<QString>();
1639 QCOMPARE(value, QString("QtWebKit"));
1642 QList<QInputMethodEvent::Attribute> attributes;
1643 // Clear the selection, so the next test does not clear any contents.
1644 QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
1645 attributes.append(newSelection);
1646 QInputMethodEvent event("composition", attributes);
1647 page->event(&event);
1650 // A ongoing composition should not change the surrounding text before it is committed.
1651 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1652 value = variant.value<QString>();
1653 QCOMPARE(value, QString("QtWebKit"));
1655 // Cancel current composition first
1656 inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant());
1657 QInputMethodEvent eventSelection4("", inputAttributes);
1658 page->event(&eventSelection4);
1660 // START - Tests for Selection when the Editor is NOT in Composition mode
1662 // LEFT to RIGHT selection
1663 // Deselect the selection by sending MouseButtonPress events
1664 // This moves the current cursor to the end of the text
1665 clickOnPage(page, textInputCenter);
1668 QList<QInputMethodEvent::Attribute> attributes;
1669 QInputMethodEvent event(QString(), attributes);
1670 event.setCommitString("XXX", 0, 0);
1671 page->event(&event);
1672 event.setCommitString(QString(), -2, 2); // Erase two characters.
1673 page->event(&event);
1674 event.setCommitString(QString(), -1, 1); // Erase one character.
1675 page->event(&event);
1676 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1677 value = variant.value<QString>();
1678 QCOMPARE(value, QString("QtWebKit"));
1681 //Move to the start of the line
1682 page->triggerAction(QWebPage::MoveToStartOfLine);
1684 QKeyEvent keyRightEventPress(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier);
1685 QKeyEvent keyRightEventRelease(QEvent::KeyRelease, Qt::Key_Right, Qt::NoModifier);
1687 //Move 2 characters RIGHT
1688 for (int j = 0; j < 2; ++j) {
1689 page->event(&keyRightEventPress);
1690 page->event(&keyRightEventRelease);
1693 //Select to the end of the line
1694 page->triggerAction(QWebPage::SelectEndOfLine);
1696 //ImAnchorPosition QtWebKit
1697 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1698 anchorPosition = variant.toInt();
1699 QCOMPARE(anchorPosition, 2);
1702 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1703 cursorPosition = variant.toInt();
1704 QCOMPARE(cursorPosition, 8);
1706 //ImCurrentSelection
1707 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1708 selectionValue = variant.value<QString>();
1709 QCOMPARE(selectionValue, QString("WebKit"));
1711 //RIGHT to LEFT selection
1712 //Deselect the selection (this moves the current cursor to the end of the text)
1713 clickOnPage(page, textInputCenter);
1716 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1717 anchorPosition = variant.toInt();
1718 QCOMPARE(anchorPosition, 8);
1721 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1722 cursorPosition = variant.toInt();
1723 QCOMPARE(cursorPosition, 8);
1725 //ImCurrentSelection
1726 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1727 selectionValue = variant.value<QString>();
1728 QCOMPARE(selectionValue, QString(""));
1730 QKeyEvent keyLeftEventPress(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier);
1731 QKeyEvent keyLeftEventRelease(QEvent::KeyRelease, Qt::Key_Left, Qt::NoModifier);
1733 //Move 2 characters LEFT
1734 for (int i = 0; i < 2; ++i) {
1735 page->event(&keyLeftEventPress);
1736 page->event(&keyLeftEventRelease);
1739 //Select to the start of the line
1740 page->triggerAction(QWebPage::SelectStartOfLine);
1743 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1744 anchorPosition = variant.toInt();
1745 QCOMPARE(anchorPosition, 6);
1748 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1749 cursorPosition = variant.toInt();
1750 QCOMPARE(cursorPosition, 0);
1752 //ImCurrentSelection
1753 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1754 selectionValue = variant.value<QString>();
1755 QCOMPARE(selectionValue, QString("QtWebK"));
1757 //END - Tests for Selection when the Editor is not in Composition mode
1760 QPoint passwordInputCenter = inputs.at(1).geometry().center();
1761 clickOnPage(page, passwordInputCenter);
1763 QVERIFY(inputMethodEnabled(view));
1764 QVERIFY(inputMethodHints(view) & Qt::ImhHiddenText);
1766 clickOnPage(page, textInputCenter);
1767 QVERIFY(!(inputMethodHints(view) & Qt::ImhHiddenText));
1769 page->mainFrame()->setHtml("<html><body><p>nothing to input here");
1770 viewEventSpy.clear();
1772 QWebElement para = page->mainFrame()->findFirstElement("p");
1773 clickOnPage(page, para.geometry().center());
1775 QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1777 //START - Test for sending empty QInputMethodEvent
1778 page->mainFrame()->setHtml("<html><body>" \
1779 "<input type='text' id='input3' value='QtWebKit2'/>" \
1781 page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input3'); inputEle.focus(); inputEle.select();");
1783 //Send empty QInputMethodEvent
1784 QInputMethodEvent emptyEvent;
1785 page->event(&emptyEvent);
1787 QString inputValue = page->mainFrame()->evaluateJavaScript("document.getElementById('input3').value").toString();
1788 QCOMPARE(inputValue, QString("QtWebKit2"));
1789 //END - Test for sending empty QInputMethodEvent
1791 page->mainFrame()->setHtml("<html><body>" \
1792 "<input type='text' id='input4' value='QtWebKit inputMethod'/>" \
1794 page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input4'); inputEle.focus(); inputEle.select();");
1796 // Clear the selection, also cancel the ongoing composition if there is one.
1798 QList<QInputMethodEvent::Attribute> attributes;
1799 QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
1800 attributes.append(newSelection);
1801 QInputMethodEvent event("", attributes);
1802 page->event(&event);
1805 // ImCurrentSelection
1806 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1807 selectionValue = variant.value<QString>();
1808 QCOMPARE(selectionValue, QString(""));
1810 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1811 QString surroundingValue = variant.value<QString>();
1812 QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
1815 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1816 anchorPosition = variant.toInt();
1817 QCOMPARE(anchorPosition, 0);
1820 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1821 cursorPosition = variant.toInt();
1822 QCOMPARE(cursorPosition, 0);
1824 // 1. Insert a character to the begining of the line.
1825 // Send temporary text, which makes the editor has composition 'm'.
1827 QList<QInputMethodEvent::Attribute> attributes;
1828 QInputMethodEvent event("m", attributes);
1829 page->event(&event);
1832 // ImCurrentSelection
1833 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1834 selectionValue = variant.value<QString>();
1835 QCOMPARE(selectionValue, QString(""));
1837 // ImSurroundingText
1838 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1839 surroundingValue = variant.value<QString>();
1840 QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
1843 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1844 cursorPosition = variant.toInt();
1845 QCOMPARE(cursorPosition, 0);
1848 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1849 anchorPosition = variant.toInt();
1850 QCOMPARE(anchorPosition, 0);
1852 // Send temporary text, which makes the editor has composition 'n'.
1854 QList<QInputMethodEvent::Attribute> attributes;
1855 QInputMethodEvent event("n", attributes);
1856 page->event(&event);
1859 // ImCurrentSelection
1860 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1861 selectionValue = variant.value<QString>();
1862 QCOMPARE(selectionValue, QString(""));
1864 // ImSurroundingText
1865 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1866 surroundingValue = variant.value<QString>();
1867 QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
1870 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1871 cursorPosition = variant.toInt();
1872 QCOMPARE(cursorPosition, 0);
1875 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1876 anchorPosition = variant.toInt();
1877 QCOMPARE(anchorPosition, 0);
1879 // Send commit text, which makes the editor conforms composition.
1881 QList<QInputMethodEvent::Attribute> attributes;
1882 QInputMethodEvent event("", attributes);
1883 event.setCommitString("o");
1884 page->event(&event);
1887 // ImCurrentSelection
1888 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1889 selectionValue = variant.value<QString>();
1890 QCOMPARE(selectionValue, QString(""));
1892 // ImSurroundingText
1893 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1894 surroundingValue = variant.value<QString>();
1895 QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod"));
1898 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1899 cursorPosition = variant.toInt();
1900 QCOMPARE(cursorPosition, 1);
1903 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1904 anchorPosition = variant.toInt();
1905 QCOMPARE(anchorPosition, 1);
1907 // 2. insert a character to the middle of the line.
1908 // Send temporary text, which makes the editor has composition 'd'.
1910 QList<QInputMethodEvent::Attribute> attributes;
1911 QInputMethodEvent event("d", attributes);
1912 page->event(&event);
1915 // ImCurrentSelection
1916 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1917 selectionValue = variant.value<QString>();
1918 QCOMPARE(selectionValue, QString(""));
1920 // ImSurroundingText
1921 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1922 surroundingValue = variant.value<QString>();
1923 QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod"));
1926 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1927 cursorPosition = variant.toInt();
1928 QCOMPARE(cursorPosition, 1);
1931 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1932 anchorPosition = variant.toInt();
1933 QCOMPARE(anchorPosition, 1);
1935 // Send commit text, which makes the editor conforms composition.
1937 QList<QInputMethodEvent::Attribute> attributes;
1938 QInputMethodEvent event("", attributes);
1939 event.setCommitString("e");
1940 page->event(&event);
1943 // ImCurrentSelection
1944 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1945 selectionValue = variant.value<QString>();
1946 QCOMPARE(selectionValue, QString(""));
1948 // ImSurroundingText
1949 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1950 surroundingValue = variant.value<QString>();
1951 QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod"));
1954 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1955 cursorPosition = variant.toInt();
1956 QCOMPARE(cursorPosition, 2);
1959 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1960 anchorPosition = variant.toInt();
1961 QCOMPARE(anchorPosition, 2);
1963 // 3. Insert a character to the end of the line.
1964 page->triggerAction(QWebPage::MoveToEndOfLine);
1966 // Send temporary text, which makes the editor has composition 't'.
1968 QList<QInputMethodEvent::Attribute> attributes;
1969 QInputMethodEvent event("t", attributes);
1970 page->event(&event);
1973 // ImCurrentSelection
1974 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1975 selectionValue = variant.value<QString>();
1976 QCOMPARE(selectionValue, QString(""));
1978 // ImSurroundingText
1979 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1980 surroundingValue = variant.value<QString>();
1981 QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod"));
1984 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1985 cursorPosition = variant.toInt();
1986 QCOMPARE(cursorPosition, 22);
1989 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1990 anchorPosition = variant.toInt();
1991 QCOMPARE(anchorPosition, 22);
1993 // Send commit text, which makes the editor conforms composition.
1995 QList<QInputMethodEvent::Attribute> attributes;
1996 QInputMethodEvent event("", attributes);
1997 event.setCommitString("t");
1998 page->event(&event);
2001 // ImCurrentSelection
2002 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
2003 selectionValue = variant.value<QString>();
2004 QCOMPARE(selectionValue, QString(""));
2006 // ImSurroundingText
2007 variant = page->inputMethodQuery(Qt::ImSurroundingText);
2008 surroundingValue = variant.value<QString>();
2009 QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt"));
2012 variant = page->inputMethodQuery(Qt::ImCursorPosition);
2013 cursorPosition = variant.toInt();
2014 QCOMPARE(cursorPosition, 23);
2017 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
2018 anchorPosition = variant.toInt();
2019 QCOMPARE(anchorPosition, 23);
2021 // 4. Replace the selection.
2022 page->triggerAction(QWebPage::SelectPreviousWord);
2024 // ImCurrentSelection
2025 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
2026 selectionValue = variant.value<QString>();
2027 QCOMPARE(selectionValue, QString("inputMethodt"));
2029 // ImSurroundingText
2030 variant = page->inputMethodQuery(Qt::ImSurroundingText);
2031 surroundingValue = variant.value<QString>();
2032 QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt"));
2035 variant = page->inputMethodQuery(Qt::ImCursorPosition);
2036 cursorPosition = variant.toInt();
2037 QCOMPARE(cursorPosition, 11);
2040 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
2041 anchorPosition = variant.toInt();
2042 QCOMPARE(anchorPosition, 23);
2044 // Send temporary text, which makes the editor has composition 'w'.
2046 QList<QInputMethodEvent::Attribute> attributes;
2047 QInputMethodEvent event("w", attributes);
2048 page->event(&event);
2051 // ImCurrentSelection
2052 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
2053 selectionValue = variant.value<QString>();
2054 QCOMPARE(selectionValue, QString(""));
2056 // ImSurroundingText
2057 variant = page->inputMethodQuery(Qt::ImSurroundingText);
2058 surroundingValue = variant.value<QString>();
2059 QCOMPARE(surroundingValue, QString("oeQtWebKit "));
2062 variant = page->inputMethodQuery(Qt::ImCursorPosition);
2063 cursorPosition = variant.toInt();
2064 QCOMPARE(cursorPosition, 11);
2067 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
2068 anchorPosition = variant.toInt();
2069 QCOMPARE(anchorPosition, 11);
2071 // Send commit text, which makes the editor conforms composition.
2073 QList<QInputMethodEvent::Attribute> attributes;
2074 QInputMethodEvent event("", attributes);
2075 event.setCommitString("2");
2076 page->event(&event);
2079 // ImCurrentSelection
2080 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
2081 selectionValue = variant.value<QString>();
2082 QCOMPARE(selectionValue, QString(""));
2084 // ImSurroundingText
2085 variant = page->inputMethodQuery(Qt::ImSurroundingText);
2086 surroundingValue = variant.value<QString>();
2087 QCOMPARE(surroundingValue, QString("oeQtWebKit 2"));
2090 variant = page->inputMethodQuery(Qt::ImCursorPosition);
2091 cursorPosition = variant.toInt();
2092 QCOMPARE(cursorPosition, 12);
2095 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
2096 anchorPosition = variant.toInt();
2097 QCOMPARE(anchorPosition, 12);
2099 // Check sending RequestSoftwareInputPanel event
2100 page->mainFrame()->setHtml("<html><body>" \
2101 "<input type='text' id='input5' value='QtWebKit inputMethod'/>" \
2102 "<div id='btnDiv' onclick='i=document.getElementById("input5"); i.focus();'>abc</div>"\
2104 QWebElement inputElement = page->mainFrame()->findFirstElement("div");
2105 clickOnPage(page, inputElement.geometry().center());
2107 QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
2111 void tst_QWebPage::inputMethodsTextFormat_data()
2113 QTest::addColumn<QString>("string");
2114 QTest::addColumn<int>("start");
2115 QTest::addColumn<int>("length");
2117 QTest::newRow("") << QString("") << 0 << 0;
2118 QTest::newRow("Q") << QString("Q") << 0 << 1;
2119 QTest::newRow("Qt") << QString("Qt") << 0 << 1;
2120 QTest::newRow("Qt") << QString("Qt") << 0 << 2;
2121 QTest::newRow("Qt") << QString("Qt") << 1 << 1;
2122 QTest::newRow("Qt ") << QString("Qt ") << 0 << 1;
2123 QTest::newRow("Qt ") << QString("Qt ") << 1 << 1;
2124 QTest::newRow("Qt ") << QString("Qt ") << 2 << 1;
2125 QTest::newRow("Qt ") << QString("Qt ") << 2 << -1;
2126 QTest::newRow("Qt ") << QString("Qt ") << -2 << 3;
2127 QTest::newRow("Qt ") << QString("Qt ") << 0 << 3;
2128 QTest::newRow("Qt by") << QString("Qt by") << 0 << 1;
2129 QTest::newRow("Qt by Nokia") << QString("Qt by Nokia") << 0 << 1;
2133 void tst_QWebPage::inputMethodsTextFormat()
2135 QWebPage* page = new QWebPage;
2136 QWebView* view = new QWebView;
2137 view->setPage(page);
2138 page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont");
2139 page->mainFrame()->setHtml("<html><body>" \
2140 "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/>");
2141 page->mainFrame()->evaluateJavaScript("document.getElementById('input1').focus()");
2142 page->mainFrame()->setFocus();
2145 QFETCH(QString, string);
2147 QFETCH(int, length);
2149 QList<QInputMethodEvent::Attribute> attrs;
2150 QTextCharFormat format;
2151 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
2152 format.setUnderlineColor(Qt::red);
2153 attrs.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, length, format));
2154 QInputMethodEvent im(string, attrs);
2162 void tst_QWebPage::protectBindingsRuntimeObjectsFromCollector()
2164 QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
2166 PluginPage* newPage = new PluginPage(m_view);
2167 m_view->setPage(newPage);
2169 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
2171 m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='lineedit' id='mylineedit'/></body></html>"));
2172 QTRY_COMPARE(loadSpy.count(), 1);
2174 newPage->mainFrame()->evaluateJavaScript("function testme(text) { var lineedit = document.getElementById('mylineedit'); lineedit.setText(text); lineedit.selectAll(); }");
2176 newPage->mainFrame()->evaluateJavaScript("testme('foo')");
2178 DumpRenderTreeSupportQt::garbageCollectorCollect();
2181 newPage->mainFrame()->evaluateJavaScript("testme('bar')");
2184 void tst_QWebPage::localURLSchemes()
2186 int i = QWebSecurityOrigin::localSchemes().size();
2188 QWebSecurityOrigin::removeLocalScheme("file");
2189 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2190 QWebSecurityOrigin::addLocalScheme("file");
2191 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2193 QWebSecurityOrigin::removeLocalScheme("qrc");
2194 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i - 1);
2195 QWebSecurityOrigin::addLocalScheme("qrc");
2196 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2198 QString myscheme = "myscheme";
2199 QWebSecurityOrigin::addLocalScheme(myscheme);
2200 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i + 1);
2201 QVERIFY(QWebSecurityOrigin::localSchemes().contains(myscheme));
2202 QWebSecurityOrigin::removeLocalScheme(myscheme);
2203 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2204 QWebSecurityOrigin::removeLocalScheme(myscheme);
2205 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2208 static inline bool testFlag(QWebPage& webPage, QWebSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue)
2210 webPage.settings()->setAttribute(settingAttribute, settingValue);
2211 return webPage.mainFrame()->evaluateJavaScript(QString("(window.%1 != undefined)").arg(jsObjectName)).toBool();
2214 void tst_QWebPage::testOptionalJSObjects()
2216 // Once a feature is enabled and the JS object is accessed turning off the setting will not turn off
2217 // the visibility of the JS object any more. For this reason this test uses two QWebPage instances.
2218 // Part of the test is to make sure that the QWebPage instances do not interfere with each other so turning on
2219 // a feature for one instance will not turn it on for another.
2224 webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
2225 webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
2227 QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
2228 QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
2229 QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", true), true);
2230 QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
2231 QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
2232 QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), true);
2234 QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false);
2235 QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", true), true);
2236 QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false);
2237 QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", false), true);
2240 void tst_QWebPage::testEnablePersistentStorage()
2244 // By default all persistent options should be disabled
2245 QCOMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), false);
2246 QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), false);
2247 QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), false);
2248 QVERIFY(webPage.settings()->iconDatabasePath().isEmpty());
2250 QWebSettings::enablePersistentStorage();
2253 QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), true);
2254 QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), true);
2255 QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), true);
2257 QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty());
2258 QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty());
2259 QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty());
2262 void tst_QWebPage::defaultTextEncoding()
2264 QWebFrame* mainFrame = m_page->mainFrame();
2266 QString defaultCharset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2267 QVERIFY(!defaultCharset.isEmpty());
2268 QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), defaultCharset);
2270 m_page->settings()->setDefaultTextEncoding(QString("utf-8"));
2271 QString charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2272 QCOMPARE(charset, QString("utf-8"));
2273 QCOMPARE(m_page->settings()->defaultTextEncoding(), charset);
2275 m_page->settings()->setDefaultTextEncoding(QString());
2276 charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2277 QVERIFY(!charset.isEmpty());
2278 QCOMPARE(charset, defaultCharset);
2280 QWebSettings::globalSettings()->setDefaultTextEncoding(QString("utf-8"));
2281 charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2282 QCOMPARE(charset, QString("utf-8"));
2283 QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), charset);
2286 class ErrorPage : public QWebPage
2290 ErrorPage(QWidget* parent = 0): QWebPage(parent)
2294 virtual bool supportsExtension(Extension extension) const
2296 return extension == ErrorPageExtension;
2299 virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output)
2301 ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output);
2303 errorPage->contentType = "text/html";
2304 errorPage->content = "error";
2309 void tst_QWebPage::errorPageExtension()
2311 ErrorPage* page = new ErrorPage;
2312 m_view->setPage(page);
2314 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
2316 m_view->setUrl(QUrl("data:text/html,foo"));
2317 QTRY_COMPARE(spyLoadFinished.count(), 1);
2319 page->mainFrame()->setUrl(QUrl("http://non.existent/url"));
2320 QTRY_COMPARE(spyLoadFinished.count(), 2);
2321 QCOMPARE(page->mainFrame()->toPlainText(), QString("error"));
2322 QCOMPARE(page->history()->count(), 2);
2323 QCOMPARE(page->history()->currentItem().url(), QUrl("http://non.existent/url"));
2324 QCOMPARE(page->history()->canGoBack(), true);
2325 QCOMPARE(page->history()->canGoForward(), false);
2327 page->triggerAction(QWebPage::Back);
2328 QTRY_COMPARE(page->history()->canGoBack(), false);
2329 QTRY_COMPARE(page->history()->canGoForward(), true);
2331 page->triggerAction(QWebPage::Forward);
2332 QTRY_COMPARE(page->history()->canGoBack(), true);
2333 QTRY_COMPARE(page->history()->canGoForward(), false);
2335 page->triggerAction(QWebPage::Back);
2336 QTRY_COMPARE(page->history()->canGoBack(), false);
2337 QTRY_COMPARE(page->history()->canGoForward(), true);
2338 QTRY_COMPARE(page->history()->currentItem().url(), QUrl("data:text/html,foo"));
2343 void tst_QWebPage::errorPageExtensionInIFrames()
2345 ErrorPage* page = new ErrorPage;
2346 m_view->setPage(page);
2348 m_view->page()->mainFrame()->load(QUrl(
2351 "<iframe src='data:text/html,<p/>p'></iframe>"
2352 "<iframe src='http://non.existent/url'></iframe>"));
2353 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
2354 QTRY_COMPARE(spyLoadFinished.count(), 1);
2356 QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("error"));
2361 void tst_QWebPage::errorPageExtensionInFrameset()
2363 ErrorPage* page = new ErrorPage;
2364 m_view->setPage(page);
2366 m_view->load(QUrl("qrc:///resources/index.html"));
2368 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
2369 QTRY_COMPARE(spyLoadFinished.count(), 1);
2370 QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("error"));
2375 class FriendlyWebPage : public QWebPage
2378 friend class tst_QWebPage;
2381 void tst_QWebPage::userAgentApplicationName()
2383 const QString oldApplicationName = QCoreApplication::applicationName();
2384 FriendlyWebPage page;
2386 const QString applicationNameMarker = QString::fromUtf8("StrangeName\342\210\236");
2387 QCoreApplication::setApplicationName(applicationNameMarker);
2388 QVERIFY(page.userAgentForUrl(QUrl()).contains(applicationNameMarker));
2390 QCoreApplication::setApplicationName(oldApplicationName);
2393 void tst_QWebPage::userAgentLocaleChange()
2395 FriendlyWebPage page;
2396 m_view->setPage(&page);
2398 const QString markerString = QString::fromLatin1(" nn-NO)");
2400 if (page.userAgentForUrl(QUrl()).contains(markerString))
2401 QSKIP("marker string already present", SkipSingle);
2403 m_view->setLocale(QLocale(QString::fromLatin1("nn_NO")));
2404 QVERIFY(page.userAgentForUrl(QUrl()).contains(markerString));
2407 void tst_QWebPage::crashTests_LazyInitializationOfMainFrame()
2414 webPage.selectedText();
2418 webPage.selectedHtml();
2422 webPage.triggerAction(QWebPage::Back, true);
2427 webPage.updatePositionDependentActions(pos);
2431 static void takeScreenshot(QWebPage* page)
2433 QWebFrame* mainFrame = page->mainFrame();
2434 page->setViewportSize(mainFrame->contentsSize());
2435 QImage image(page->viewportSize(), QImage::Format_ARGB32);
2436 QPainter painter(&image);
2437 mainFrame->render(&painter);
2441 void tst_QWebPage::screenshot_data()
2443 QTest::addColumn<QString>("html");
2444 QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>";
2445 QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>");
2446 QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode='transparent'></embed></body></html>");
2449 void tst_QWebPage::screenshot()
2451 if (!QDir(TESTS_SOURCE_DIR).exists())
2452 QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
2454 QDir::setCurrent(TESTS_SOURCE_DIR);
2456 QFETCH(QString, html);
2457 QWebPage* page = new QWebPage;
2458 page->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
2459 QWebFrame* mainFrame = page->mainFrame();
2460 mainFrame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR));
2461 ::waitForSignal(mainFrame, SIGNAL(loadFinished(bool)), 2000);
2463 // take screenshot without a view
2464 takeScreenshot(page);
2466 QWebView* view = new QWebView;
2467 view->setPage(page);
2469 // take screenshot when attached to a view
2470 takeScreenshot(page);
2475 QDir::setCurrent(QApplication::applicationDirPath());
2478 void tst_QWebPage::originatingObjectInNetworkRequests()
2480 TestNetworkManager* networkManager = new TestNetworkManager(m_page);
2481 m_page->setNetworkAccessManager(networkManager);
2482 networkManager->requests.clear();
2484 m_view->setHtml(QString("<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
2485 "<head><meta http-equiv='refresh' content='1'></head>foo \">"
2486 "<frame src=\"data:text/html,bar\"></frameset>"), QUrl());
2487 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
2489 QCOMPARE(networkManager->requests.count(), 2);
2491 QList<QWebFrame*> childFrames = m_page->mainFrame()->childFrames();
2492 QCOMPARE(childFrames.count(), 2);
2494 for (int i = 0; i < 2; ++i)
2495 QVERIFY(qobject_cast<QWebFrame*>(networkManager->requests.at(i).originatingObject()) == childFrames.at(i));
2499 * Test fixups for https://bugs.webkit.org/show_bug.cgi?id=30914
2501 * From JS we test the following conditions.
2503 * OK + QString() => SUCCESS, empty string (but not null)
2504 * OK + "text" => SUCCESS, "text"
2505 * CANCEL + QString() => CANCEL, null string
2506 * CANCEL + "text" => CANCEL, null string
2508 class JSPromptPage : public QWebPage {
2514 bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result)
2516 if (msg == QLatin1String("test1")) {
2517 *result = QString();
2519 } else if (msg == QLatin1String("test2")) {
2520 *result = QLatin1String("text");
2522 } else if (msg == QLatin1String("test3")) {
2523 *result = QString();
2525 } else if (msg == QLatin1String("test4")) {
2526 *result = QLatin1String("text");
2530 qFatal("Unknown msg.");
2531 return QWebPage::javaScriptPrompt(frame, msg, defaultValue, result);
2535 void tst_QWebPage::testJSPrompt()
2541 res = page.mainFrame()->evaluateJavaScript(
2542 "var retval = prompt('test1');"
2543 "retval=='' && retval.length == 0;").toBool();
2547 res = page.mainFrame()->evaluateJavaScript(
2548 "var retval = prompt('test2');"
2549 "retval=='text' && retval.length == 4;").toBool();
2552 // Cancel + QString()
2553 res = page.mainFrame()->evaluateJavaScript(
2554 "var retval = prompt('test3');"
2555 "retval===null;").toBool();
2559 res = page.mainFrame()->evaluateJavaScript(
2560 "var retval = prompt('test4');"
2561 "retval===null;").toBool();
2565 class TestModalPage : public QWebPage
2569 TestModalPage(QObject* parent = 0) : QWebPage(parent) {
2571 virtual QWebPage* createWindow(WebWindowType) {
2572 QWebPage* page = new TestModalPage();
2573 connect(page, SIGNAL(windowCloseRequested()), page, SLOT(deleteLater()));
2578 void tst_QWebPage::showModalDialog()
2581 page.mainFrame()->setHtml(QString("<html></html>"));
2582 QString res = page.mainFrame()->evaluateJavaScript("window.showModalDialog('javascript:window.returnValue=dialogArguments; window.close();', 'This is a test');").toString();
2583 QCOMPARE(res, QString("This is a test"));
2586 void tst_QWebPage::testStopScheduledPageRefresh()
2588 // Without QWebPage::StopScheduledPageRefresh
2590 page1.setNetworkAccessManager(new TestNetworkManager(&page1));
2591 page1.mainFrame()->setHtml("<html><head>"
2592 "<meta http-equiv=\"refresh\"content=\"0;URL=qrc:///resources/index.html\">"
2593 "</head><body><h1>Page redirects immediately...</h1>"
2595 QVERIFY(::waitForSignal(&page1, SIGNAL(loadFinished(bool))));
2597 QCOMPARE(page1.mainFrame()->url(), QUrl(QLatin1String("qrc:///resources/index.html")));
2599 // With QWebPage::StopScheduledPageRefresh
2601 page2.setNetworkAccessManager(new TestNetworkManager(&page2));
2602 page2.mainFrame()->setHtml("<html><head>"
2603 "<meta http-equiv=\"refresh\"content=\"1;URL=qrc:///resources/index.html\">"
2604 "</head><body><h1>Page redirect test with 1 sec timeout...</h1>"
2606 page2.triggerAction(QWebPage::StopScheduledPageRefresh);
2608 QCOMPARE(page2.mainFrame()->url().toString(), QLatin1String("about:blank"));
2611 void tst_QWebPage::findText()
2613 m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>"));
2614 m_page->triggerAction(QWebPage::SelectAll);
2615 QVERIFY(!m_page->selectedText().isEmpty());
2616 QVERIFY(!m_page->selectedHtml().isEmpty());
2617 m_page->findText("");
2618 QVERIFY(m_page->selectedText().isEmpty());
2619 QVERIFY(m_page->selectedHtml().isEmpty());
2620 QStringList words = (QStringList() << "foo" << "bar");
2621 foreach (QString subString, words) {
2622 m_page->findText(subString, QWebPage::FindWrapsAroundDocument);
2623 QCOMPARE(m_page->selectedText(), subString);
2624 QCOMPARE(m_page->selectedHtml(), QString("<span class=\"Apple-style-span\" style=\"border-collapse: separate; color: rgb(0, 0, 0); font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; \">%1</span>").arg(subString));
2625 m_page->findText("");
2626 QVERIFY(m_page->selectedText().isEmpty());
2627 QVERIFY(m_page->selectedHtml().isEmpty());
2631 struct ImageExtensionMap {
2632 const char* extension;
2633 const char* mimeType;
2636 static const ImageExtensionMap extensionMap[] = {
2637 { "bmp", "image/bmp" },
2638 { "css", "text/css" },
2639 { "gif", "image/gif" },
2640 { "html", "text/html" },
2641 { "htm", "text/html" },
2642 { "ico", "image/x-icon" },
2643 { "jpeg", "image/jpeg" },
2644 { "jpg", "image/jpeg" },
2645 { "js", "application/x-javascript" },
2646 { "mng", "video/x-mng" },
2647 { "pbm", "image/x-portable-bitmap" },
2648 { "pgm", "image/x-portable-graymap" },
2649 { "pdf", "application/pdf" },
2650 { "png", "image/png" },
2651 { "ppm", "image/x-portable-pixmap" },
2652 { "rss", "application/rss+xml" },
2653 { "svg", "image/svg+xml" },
2654 { "text", "text/plain" },
2655 { "tif", "image/tiff" },
2656 { "tiff", "image/tiff" },
2657 { "txt", "text/plain" },
2658 { "xbm", "image/x-xbitmap" },
2659 { "xml", "text/xml" },
2660 { "xpm", "image/x-xpm" },
2661 { "xsl", "text/xsl" },
2662 { "xhtml", "application/xhtml+xml" },
2663 { "wml", "text/vnd.wap.wml" },
2664 { "wmlc", "application/vnd.wap.wmlc" },
2668 static QString getMimeTypeForExtension(const QString &ext)
2670 const ImageExtensionMap *e = extensionMap;
2671 while (e->extension) {
2672 if (ext.compare(QLatin1String(e->extension), Qt::CaseInsensitive) == 0)
2673 return QLatin1String(e->mimeType);
2680 void tst_QWebPage::supportedContentType()
2682 QStringList contentTypes;
2684 // Add supported non image types...
2685 contentTypes << "text/html" << "text/xml" << "text/xsl" << "text/plain" << "text/"
2686 << "application/xml" << "application/xhtml+xml" << "application/vnd.wap.xhtml+xml"
2687 << "application/rss+xml" << "application/atom+xml" << "application/json";
2689 // Add supported image types...
2690 Q_FOREACH(const QByteArray& imageType, QImageWriter::supportedImageFormats()) {
2691 const QString mimeType = getMimeTypeForExtension(imageType);
2692 if (!mimeType.isEmpty())
2693 contentTypes << mimeType;
2696 // Get the mime types supported by webkit...
2697 const QStringList supportedContentTypes = m_page->supportedContentTypes();
2699 Q_FOREACH(const QString& mimeType, contentTypes)
2700 QVERIFY2(supportedContentTypes.contains(mimeType), QString("'%1' is not a supported content type!").arg(mimeType).toLatin1());
2702 Q_FOREACH(const QString& mimeType, contentTypes)
2703 QVERIFY2(m_page->supportsContentType(mimeType), QString("Cannot handle content types '%1'!").arg(mimeType).toLatin1());
2706 class QtNAMThread : public QThread {
2718 QNetworkAccessManager* networkAccessManager()
2720 QMutexLocker lock(&m_mutex);
2722 m_waitCondition.wait(&m_mutex);
2730 QMutexLocker lock(&m_mutex);
2731 m_qnam = new QNetworkAccessManager;
2732 m_waitCondition.wakeAll();
2738 QNetworkAccessManager* m_qnam;
2740 QWaitCondition m_waitCondition;
2743 void tst_QWebPage::networkAccessManagerOnDifferentThread()
2745 QtNAMThread qnamThread;
2747 m_page->setNetworkAccessManager(qnamThread.networkAccessManager());
2748 QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool)));
2749 QUrl url = QUrl("qrc:///resources/index.html");
2750 m_page->mainFrame()->load(url);
2751 QTRY_COMPARE(loadSpy.count(), 1);
2752 QCOMPARE(m_page->mainFrame()->childFrames()[0]->url(), QUrl("qrc:///resources/frame_a.html"));
2755 void tst_QWebPage::navigatorCookieEnabled()
2757 m_page->networkAccessManager()->setCookieJar(0);
2758 QVERIFY(!m_page->networkAccessManager()->cookieJar());
2759 QVERIFY(!m_page->mainFrame()->evaluateJavaScript("navigator.cookieEnabled").toBool());
2761 m_page->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
2762 QVERIFY(m_page->networkAccessManager()->cookieJar());
2763 QVERIFY(m_page->mainFrame()->evaluateJavaScript("navigator.cookieEnabled").toBool());
2766 void tst_QWebPage::navigatorCookieEnabledForNetworkAccessManagerOnDifferentThread()
2768 QtNAMThread qnamThread;
2770 m_page->setNetworkAccessManager(qnamThread.networkAccessManager());
2772 // This call access the cookie jar, the cookie jar must be in the same thread as
2773 // the network access manager.
2774 QVERIFY(m_page->mainFrame()->evaluateJavaScript("navigator.cookieEnabled").toBool());
2776 QCOMPARE(qnamThread.networkAccessManager()->cookieJar()->thread(), &qnamThread);
2780 void tst_QWebPage::macCopyUnicodeToClipboard()
2782 QString unicodeText = QString::fromUtf8("αβγδεζηθικλμπ");
2783 m_page->mainFrame()->setHtml(QString("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /></head><body>%1</body></html>").arg(unicodeText));
2784 m_page->triggerAction(QWebPage::SelectAll);
2785 m_page->triggerAction(QWebPage::Copy);
2787 QString clipboardData = QString::fromUtf8(QApplication::clipboard()->mimeData()->data(QLatin1String("text/html")));
2789 QVERIFY(clipboardData.contains(QLatin1String("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />")));
2790 QVERIFY(clipboardData.contains(unicodeText));
2794 void tst_QWebPage::contextMenuCopy()
2798 view.setHtml("<a href=\"http://www.google.com\">You cant miss this</a>");
2800 view.page()->triggerAction(QWebPage::SelectAll);
2801 QVERIFY(!view.page()->selectedText().isEmpty());
2803 QWebElement link = view.page()->mainFrame()->findFirstElement("a");
2804 QPoint pos(link.geometry().center());
2805 QContextMenuEvent event(QContextMenuEvent::Mouse, pos);
2806 view.page()->swallowContextMenuEvent(&event);
2807 view.page()->updatePositionDependentActions(pos);
2809 QList<QMenu*> contextMenus = view.findChildren<QMenu*>();
2810 QVERIFY(!contextMenus.isEmpty());
2811 QMenu* contextMenu = contextMenus.first();
2812 QVERIFY(contextMenu);
2814 QList<QAction *> list = contextMenu->actions();
2815 int index = list.indexOf(view.page()->action(QWebPage::Copy));
2816 QVERIFY(index != -1);
2818 QTEST_MAIN(tst_QWebPage)
2819 #include "tst_qwebpage.moc"