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"
25 #include <QGraphicsWidget>
29 #include <QPushButton>
31 #include <QtTest/QtTest>
32 #include <QTextCharFormat>
33 #include <qgraphicsscene.h>
34 #include <qgraphicsview.h>
35 #include <qgraphicswebview.h>
36 #include <qnetworkrequest.h>
37 #include <qwebdatabase.h>
38 #include <qwebelement.h>
39 #include <qwebframe.h>
40 #include <qwebhistory.h>
42 #include <qwebsecurityorigin.h>
44 #include <qimagewriter.h>
46 class EventSpy : public QObject, public QList<QEvent::Type>
50 EventSpy(QObject* objectToSpy)
52 objectToSpy->installEventFilter(this);
55 virtual bool eventFilter(QObject* receiver, QEvent* event)
57 append(event->type());
62 class tst_QWebPage : public QObject
68 virtual ~tst_QWebPage();
77 void cleanupTestCase();
79 void acceptNavigationRequest();
80 void geolocationRequestJS();
82 void acceptNavigationRequestWithNewWindow();
83 void userStyleSheet();
85 void contextMenuCrash();
87 void createPluginWithPluginsEnabled();
88 void createPluginWithPluginsDisabled();
89 void destroyPlugin_data();
91 void createViewlessPlugin_data();
92 void createViewlessPlugin();
93 void multiplePageGroupsAndLocalStorage();
94 void cursorMovements();
97 void backActionUpdate();
100 void loadCachedPage();
101 void protectBindingsRuntimeObjectsFromCollector();
102 void localURLSchemes();
103 void testOptionalJSObjects();
104 void testEnablePersistentStorage();
105 void consoleOutput();
106 void inputMethods_data();
108 void inputMethodsTextFormat_data();
109 void inputMethodsTextFormat();
110 void defaultTextEncoding();
111 void errorPageExtension();
112 void errorPageExtensionInIFrames();
113 void errorPageExtensionInFrameset();
114 void userAgentApplicationName();
115 void userAgentLocaleChange();
119 void crashTests_LazyInitializationOfMainFrame();
121 void screenshot_data();
124 void originatingObjectInNetworkRequests();
126 void showModalDialog();
127 void testStopScheduledPageRefresh();
129 void supportedContentType();
130 void infiniteLoopJS();
137 tst_QWebPage::tst_QWebPage()
141 tst_QWebPage::~tst_QWebPage()
145 void tst_QWebPage::init()
147 m_view = new QWebView();
148 m_page = m_view->page();
151 void tst_QWebPage::cleanup()
156 void tst_QWebPage::cleanupFiles()
158 QFile::remove("Databases.db");
159 QDir::current().rmdir("http_www.myexample.com_0");
160 QFile::remove("http_www.myexample.com_0.localstorage");
163 void tst_QWebPage::initTestCase()
165 cleanupFiles(); // In case there are old files from previous runs
168 void tst_QWebPage::cleanupTestCase()
170 cleanupFiles(); // Be nice
173 class NavigationRequestOverride : public QWebPage
176 NavigationRequestOverride(QWebView* parent, bool initialValue) : QWebPage(parent), m_acceptNavigationRequest(initialValue) {}
178 bool m_acceptNavigationRequest;
180 virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, QWebPage::NavigationType type) {
185 return m_acceptNavigationRequest;
189 void tst_QWebPage::acceptNavigationRequest()
191 QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
193 NavigationRequestOverride* newPage = new NavigationRequestOverride(m_view, false);
194 m_view->setPage(newPage);
196 m_view->setHtml(QString("<html><body><form name='tstform' action='data:text/html,foo'method='get'>"
197 "<input type='text'><input type='submit'></form></body></html>"), QUrl());
198 QTRY_COMPARE(loadSpy.count(), 1);
200 m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
202 newPage->m_acceptNavigationRequest = true;
203 m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
204 QTRY_COMPARE(loadSpy.count(), 2);
206 QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("foo?"));
208 // Restore default page
212 class JSTestPage : public QWebPage
216 JSTestPage(QObject* parent = 0)
217 : QWebPage(parent) {}
220 bool shouldInterruptJavaScript() {
223 void requestPermission(QWebFrame* frame, QWebPage::Feature feature)
225 if (m_allowGeolocation)
226 setFeaturePermission(frame, feature, PermissionGrantedByUser);
228 setFeaturePermission(frame, feature, PermissionDeniedByUser);
232 void setGeolocationPermission(bool allow)
234 m_allowGeolocation = allow;
238 bool m_allowGeolocation;
241 void tst_QWebPage::infiniteLoopJS()
243 JSTestPage* newPage = new JSTestPage(m_view);
244 m_view->setPage(newPage);
245 m_view->setHtml(QString("<html><body>test</body></html>"), QUrl());
246 m_view->page()->mainFrame()->evaluateJavaScript("var run = true;var a = 1;while(run){a++;}");
250 void tst_QWebPage::geolocationRequestJS()
253 This test is disabled because it can only succeed if ENABLE(GEOLOCATION) is true.
254 The code needs to be updated when the API of requestPermissionFromUser is updated.
256 QSKIP("Test disabled as long as geolocation is disabled from the build.", SkipSingle);
258 JSTestPage* newPage = new JSTestPage(m_view);
259 connect(newPage, SIGNAL(requestPermissionFromUser(QWebFrame*, QWebPage::Feature)),
260 newPage, SLOT(requestPermission(QWebFrame*, QWebPage::Feature)));
262 newPage->setGeolocationPermission(false);
263 m_view->setPage(newPage);
264 m_view->setHtml(QString("<html><body>test</body></html>"), QUrl());
265 m_view->page()->mainFrame()->evaluateJavaScript("var errorCode = 0; function error(err) { errorCode = err.code; } function success(pos) { } navigator.geolocation.getCurrentPosition(success, error)");
267 QVariant empty = m_view->page()->mainFrame()->evaluateJavaScript("errorCode");
269 QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 0);
271 newPage->setGeolocationPermission(true);
272 m_view->page()->mainFrame()->evaluateJavaScript("errorCode = 0; navigator.geolocation.getCurrentPosition(success, error);");
273 empty = m_view->page()->mainFrame()->evaluateJavaScript("errorCode");
275 //http://dev.w3.org/geo/api/spec-source.html#position
276 //PositionError: const unsigned short PERMISSION_DENIED = 1;
277 QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 1);
281 void tst_QWebPage::loadFinished()
283 qRegisterMetaType<QWebFrame*>("QWebFrame*");
284 qRegisterMetaType<QNetworkRequest*>("QNetworkRequest*");
285 QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted()));
286 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
288 m_view->page()->mainFrame()->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
289 "<head><meta http-equiv='refresh' content='1'></head>foo \">"
290 "<frame src=\"data:text/html,bar\"></frameset>"));
291 QTRY_COMPARE(spyLoadFinished.count(), 1);
293 QTRY_VERIFY(spyLoadStarted.count() > 1);
294 QTRY_VERIFY(spyLoadFinished.count() > 1);
296 spyLoadFinished.clear();
298 m_view->page()->mainFrame()->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
299 "foo \"><frame src=\"data:text/html,bar\"></frameset>"));
300 QTRY_COMPARE(spyLoadFinished.count(), 1);
301 QCOMPARE(spyLoadFinished.count(), 1);
304 class ConsolePage : public QWebPage
307 ConsolePage(QObject* parent = 0) : QWebPage(parent) {}
309 virtual void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID)
311 messages.append(message);
312 lineNumbers.append(lineNumber);
313 sourceIDs.append(sourceID);
316 QStringList messages;
317 QList<int> lineNumbers;
318 QStringList sourceIDs;
321 void tst_QWebPage::consoleOutput()
324 page.mainFrame()->evaluateJavaScript("this is not valid JavaScript");
325 QCOMPARE(page.messages.count(), 1);
326 QCOMPARE(page.lineNumbers.at(0), 1);
329 class TestPage : public QWebPage
332 TestPage(QObject* parent = 0) : QWebPage(parent) {}
335 QPointer<QWebFrame> frame;
336 QNetworkRequest request;
340 QList<Navigation> navigations;
341 QList<QWebPage*> createdWindows;
343 virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, NavigationType type) {
348 navigations.append(n);
352 virtual QWebPage* createWindow(WebWindowType) {
353 QWebPage* page = new TestPage(this);
354 createdWindows.append(page);
359 void tst_QWebPage::acceptNavigationRequestWithNewWindow()
361 TestPage* page = new TestPage(m_view);
362 page->settings()->setAttribute(QWebSettings::LinksIncludedInFocusChain, true);
364 m_view->setPage(m_page);
366 m_view->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
367 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
369 QFocusEvent fe(QEvent::FocusIn);
372 QVERIFY(m_page->focusNextPrevChild(/*next*/ true));
374 QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
375 m_page->event(&keyEnter);
377 QCOMPARE(page->navigations.count(), 2);
379 TestPage::Navigation n = page->navigations.at(1);
380 QVERIFY(n.frame.isNull());
381 QCOMPARE(n.request.url().toString(), QString("data:text/html,Reached"));
382 QVERIFY(n.type == QWebPage::NavigationTypeLinkClicked);
384 QCOMPARE(page->createdWindows.count(), 1);
387 class TestNetworkManager : public QNetworkAccessManager
390 TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {}
392 QList<QUrl> requestedUrls;
393 QList<QNetworkRequest> requests;
396 virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) {
397 requests.append(request);
398 requestedUrls.append(request.url());
399 return QNetworkAccessManager::createRequest(op, request, outgoingData);
403 void tst_QWebPage::userStyleSheet()
405 TestNetworkManager* networkManager = new TestNetworkManager(m_page);
406 m_page->setNetworkAccessManager(networkManager);
407 networkManager->requestedUrls.clear();
409 m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css;charset=utf-8;base64,"
410 + QByteArray("p { background-image: url('http://does.not/exist.png');}").toBase64()));
411 m_view->setHtml("<p>hello world</p>");
412 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
414 QVERIFY(networkManager->requestedUrls.count() >= 1);
415 QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png"));
418 void tst_QWebPage::viewModes()
420 m_view->setHtml("<body></body>");
421 m_page->setProperty("_q_viewMode", "minimized");
423 QVariant empty = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode)\")");
424 QVERIFY(empty.type() == QVariant::Bool && empty.toBool());
426 QVariant minimized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: minimized)\")");
427 QVERIFY(minimized.type() == QVariant::Bool && minimized.toBool());
429 QVariant maximized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: maximized)\")");
430 QVERIFY(maximized.type() == QVariant::Bool && !maximized.toBool());
433 void tst_QWebPage::modified()
435 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>blub"));
436 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
438 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah"));
439 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
441 QVERIFY(!m_page->isModified());
443 // m_page->mainFrame()->evaluateJavaScript("alert(document.getElementById('foo'))");
444 m_page->mainFrame()->evaluateJavaScript("document.getElementById('foo').focus()");
445 m_page->mainFrame()->evaluateJavaScript("document.execCommand('InsertText', true, 'Test');");
447 QVERIFY(m_page->isModified());
449 m_page->mainFrame()->evaluateJavaScript("document.execCommand('Undo', true);");
451 QVERIFY(!m_page->isModified());
453 m_page->mainFrame()->evaluateJavaScript("document.execCommand('Redo', true);");
455 QVERIFY(m_page->isModified());
457 QVERIFY(m_page->history()->canGoBack());
458 QVERIFY(!m_page->history()->canGoForward());
459 QCOMPARE(m_page->history()->count(), 2);
460 QVERIFY(m_page->history()->backItem().isValid());
461 QVERIFY(!m_page->history()->forwardItem().isValid());
463 m_page->history()->back();
464 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
466 QVERIFY(!m_page->history()->canGoBack());
467 QVERIFY(m_page->history()->canGoForward());
469 QVERIFY(!m_page->isModified());
471 QVERIFY(m_page->history()->currentItemIndex() == 0);
473 m_page->history()->setMaximumItemCount(3);
474 QVERIFY(m_page->history()->maximumItemCount() == 3);
476 QVariant variant("string test");
477 m_page->history()->currentItem().setUserData(variant);
478 QVERIFY(m_page->history()->currentItem().userData().toString() == "string test");
480 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is second page"));
481 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is third page"));
482 QVERIFY(m_page->history()->count() == 2);
483 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fourth page"));
484 QVERIFY(m_page->history()->count() == 2);
485 m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fifth page"));
486 QVERIFY(::waitForSignal(m_page, SIGNAL(saveFrameStateRequested(QWebFrame*,QWebHistoryItem*))));
489 void tst_QWebPage::contextMenuCrash()
492 view.setHtml("<p>test");
493 view.page()->updatePositionDependentActions(QPoint(0, 0));
494 QMenu* contextMenu = 0;
495 foreach (QObject* child, view.children()) {
496 contextMenu = qobject_cast<QMenu*>(child);
500 QVERIFY(contextMenu);
504 void tst_QWebPage::database()
506 QString path = QDir::currentPath();
507 m_page->settings()->setOfflineStoragePath(path);
508 QVERIFY(m_page->settings()->offlineStoragePath() == path);
510 QWebSettings::setOfflineStorageDefaultQuota(1024 * 1024);
511 QVERIFY(QWebSettings::offlineStorageDefaultQuota() == 1024 * 1024);
513 m_page->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
514 m_page->settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true);
516 QString dbFileName = path + "Databases.db";
518 if (QFile::exists(dbFileName))
519 QFile::remove(dbFileName);
521 qRegisterMetaType<QWebFrame*>("QWebFrame*");
522 QSignalSpy spy(m_page, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString)));
523 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"));
524 QTRY_COMPARE(spy.count(), 1);
525 m_page->mainFrame()->evaluateJavaScript("var db2; db2=openDatabase('testdb', '1.0', 'test database API', 50000);");
526 QTRY_COMPARE(spy.count(),1);
528 m_page->mainFrame()->evaluateJavaScript("localStorage.test='This is a test for local storage';");
529 m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
531 QVariant s1 = m_page->mainFrame()->evaluateJavaScript("localStorage.test");
532 QCOMPARE(s1.toString(), QString("This is a test for local storage"));
534 m_page->mainFrame()->evaluateJavaScript("sessionStorage.test='This is a test for session storage';");
535 m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
536 QVariant s2 = m_page->mainFrame()->evaluateJavaScript("sessionStorage.test");
537 QCOMPARE(s2.toString(), QString("This is a test for session storage"));
539 m_view->setHtml(QString("<html><head></head><body><div></div></body></html>"), QUrl("http://www.myexample.com"));
540 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) { });");
543 // Remove all databases.
544 QWebSecurityOrigin origin = m_page->mainFrame()->securityOrigin();
545 QList<QWebDatabase> dbs = origin.databases();
546 for (int i = 0; i < dbs.count(); i++) {
547 QString fileName = dbs[i].fileName();
548 QVERIFY(QFile::exists(fileName));
549 QWebDatabase::removeDatabase(dbs[i]);
550 QVERIFY(!QFile::exists(fileName));
552 QVERIFY(!origin.databases().size());
553 // Remove removed test :-)
554 QWebDatabase::removeAllDatabases();
555 QVERIFY(!origin.databases().size());
558 class PluginPage : public QWebPage
561 PluginPage(QObject *parent = 0)
562 : QWebPage(parent) {}
566 CallInfo(const QString &c, const QUrl &u,
567 const QStringList &pn, const QStringList &pv,
569 : classid(c), url(u), paramNames(pn),
570 paramValues(pv), returnValue(r)
574 QStringList paramNames;
575 QStringList paramValues;
576 QObject *returnValue;
579 QList<CallInfo> calls;
582 virtual QObject *createPlugin(const QString &classid, const QUrl &url,
583 const QStringList ¶mNames,
584 const QStringList ¶mValues)
587 if (classid == "pushbutton")
588 result = new QPushButton();
589 else if (classid == "lineedit")
590 result = new QLineEdit();
592 result->setObjectName(classid);
593 calls.append(CallInfo(classid, url, paramNames, paramValues, result));
598 static void createPlugin(QWebView *view)
600 QSignalSpy loadSpy(view, SIGNAL(loadFinished(bool)));
602 PluginPage* newPage = new PluginPage(view);
603 view->setPage(newPage);
605 // type has to be application/x-qt-plugin
606 view->setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='pushbutton' id='mybutton'/></body></html>"));
607 QTRY_COMPARE(loadSpy.count(), 1);
608 QCOMPARE(newPage->calls.count(), 0);
610 view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>"));
611 QTRY_COMPARE(loadSpy.count(), 2);
612 QCOMPARE(newPage->calls.count(), 1);
614 PluginPage::CallInfo ci = newPage->calls.takeFirst();
615 QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
616 QCOMPARE(ci.url, QUrl());
617 QCOMPARE(ci.paramNames.count(), 3);
618 QCOMPARE(ci.paramValues.count(), 3);
619 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
620 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
621 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
622 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton"));
623 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
624 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton"));
625 QVERIFY(ci.returnValue != 0);
626 QVERIFY(ci.returnValue->inherits("QPushButton"));
629 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mybutton').toString()").toString(),
630 QString::fromLatin1("[object HTMLObjectElement]"));
631 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.toString()").toString(),
632 QString::fromLatin1("[object HTMLObjectElement]"));
633 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.objectName").toString(),
634 QString::fromLatin1("string"));
635 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.objectName").toString(),
636 QString::fromLatin1("pushbutton"));
637 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.clicked").toString(),
638 QString::fromLatin1("function"));
639 QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.clicked.toString()").toString(),
640 QString::fromLatin1("function clicked() {\n [native code]\n}"));
642 view->setHtml(QString("<html><body><table>"
643 "<tr><object type='application/x-qt-plugin' classid='lineedit' id='myedit'/></tr>"
644 "<tr><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></tr>"
645 "</table></body></html>"), QUrl("http://foo.bar.baz"));
646 QTRY_COMPARE(loadSpy.count(), 3);
647 QCOMPARE(newPage->calls.count(), 2);
649 PluginPage::CallInfo ci = newPage->calls.takeFirst();
650 QCOMPARE(ci.classid, QString::fromLatin1("lineedit"));
651 QCOMPARE(ci.url, QUrl());
652 QCOMPARE(ci.paramNames.count(), 3);
653 QCOMPARE(ci.paramValues.count(), 3);
654 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
655 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
656 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
657 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("lineedit"));
658 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
659 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("myedit"));
660 QVERIFY(ci.returnValue != 0);
661 QVERIFY(ci.returnValue->inherits("QLineEdit"));
664 PluginPage::CallInfo ci = newPage->calls.takeFirst();
665 QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
666 QCOMPARE(ci.url, QUrl());
667 QCOMPARE(ci.paramNames.count(), 3);
668 QCOMPARE(ci.paramValues.count(), 3);
669 QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
670 QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
671 QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
672 QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton"));
673 QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
674 QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton"));
675 QVERIFY(ci.returnValue != 0);
676 QVERIFY(ci.returnValue->inherits("QPushButton"));
680 void tst_QWebPage::createPluginWithPluginsEnabled()
682 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
683 createPlugin(m_view);
686 void tst_QWebPage::createPluginWithPluginsDisabled()
688 // Qt Plugins should be loaded by QtWebKit even when PluginsEnabled is
689 // false. The client decides whether a Qt plugin is enabled or not when
690 // it decides whether or not to instantiate it.
691 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, false);
692 createPlugin(m_view);
695 // Standard base class for template PluginTracerPage. In tests it is used as interface.
696 class PluginCounterPage : public QWebPage {
699 QPointer<QObject> m_widget;
700 QObject* m_pluginParent;
701 PluginCounterPage(QObject* parent = 0)
707 settings()->setAttribute(QWebSettings::PluginsEnabled, true);
712 m_pluginParent->deleteLater();
717 class PluginTracerPage : public PluginCounterPage {
719 PluginTracerPage(QObject* parent = 0)
720 : PluginCounterPage(parent)
722 // this is a dummy parent object for the created plugin
723 m_pluginParent = new T;
725 virtual QObject* createPlugin(const QString&, const QUrl&, const QStringList&, const QStringList&)
729 // need a cast to the specific type, as QObject::setParent cannot be called,
730 // because it is not virtual. Instead it is necesary to call QWidget::setParent,
731 // which also takes a QWidget* instead of a QObject*. Therefore we need to
732 // upcast to T*, which is a QWidget.
733 static_cast<T*>(m_widget.data())->setParent(static_cast<T*>(m_pluginParent));
738 class PluginFactory {
740 enum FactoredType {QWidgetType, QGraphicsWidgetType};
741 static PluginCounterPage* create(FactoredType type, QObject* parent = 0)
743 PluginCounterPage* result = 0;
746 result = new PluginTracerPage<QWidget>(parent);
748 case QGraphicsWidgetType:
749 result = new PluginTracerPage<QGraphicsWidget>(parent);
756 static void prepareTestData()
758 QTest::addColumn<int>("type");
759 QTest::newRow("QWidget") << (int)PluginFactory::QWidgetType;
760 QTest::newRow("QGraphicsWidget") << (int)PluginFactory::QGraphicsWidgetType;
764 void tst_QWebPage::destroyPlugin_data()
766 PluginFactory::prepareTestData();
769 void tst_QWebPage::destroyPlugin()
772 PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type, m_view);
773 m_view->setPage(page);
775 // we create the plugin, so the widget should be constructed
776 QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
777 m_view->setHtml(content);
778 QVERIFY(page->m_widget);
779 QCOMPARE(page->m_count, 1);
781 // navigate away, the plugin widget should be destructed
782 m_view->setHtml("<html><body>Hi</body></html>");
783 QTestEventLoop::instance().enterLoop(1);
784 QVERIFY(!page->m_widget);
787 void tst_QWebPage::createViewlessPlugin_data()
789 PluginFactory::prepareTestData();
792 void tst_QWebPage::createViewlessPlugin()
795 PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type);
796 QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
797 page->mainFrame()->setHtml(content);
798 QCOMPARE(page->m_count, 1);
799 QVERIFY(page->m_widget);
800 QVERIFY(page->m_pluginParent);
801 QVERIFY(page->m_widget->parent() == page->m_pluginParent);
806 void tst_QWebPage::multiplePageGroupsAndLocalStorage()
808 QDir dir(QDir::currentPath());
815 view1.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
816 view1.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path1"));
817 DumpRenderTreeSupportQt::webPageSetGroupName(view1.page(), "group1");
818 view2.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
819 view2.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path2"));
820 DumpRenderTreeSupportQt::webPageSetGroupName(view2.page(), "group2");
821 QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view1.page()), QString("group1"));
822 QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view2.page()), QString("group2"));
825 view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
826 view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
828 view1.page()->mainFrame()->evaluateJavaScript("localStorage.test='value1';");
829 view2.page()->mainFrame()->evaluateJavaScript("localStorage.test='value2';");
831 view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
832 view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
834 QVariant s1 = view1.page()->mainFrame()->evaluateJavaScript("localStorage.test");
835 QCOMPARE(s1.toString(), QString("value1"));
837 QVariant s2 = view2.page()->mainFrame()->evaluateJavaScript("localStorage.test");
838 QCOMPARE(s2.toString(), QString("value2"));
842 QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path1/http_www.myexample.com_0.localstorage"));
843 QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path2/http_www.myexample.com_0.localstorage"));
844 dir.rmdir(QDir::toNativeSeparators("./path1"));
845 dir.rmdir(QDir::toNativeSeparators("./path2"));
848 class CursorTrackedPage : public QWebPage
852 CursorTrackedPage(QWidget *parent = 0): QWebPage(parent) {
853 setViewportSize(QSize(1024, 768)); // big space
856 QString selectedText() {
857 return mainFrame()->evaluateJavaScript("window.getSelection().toString()").toString();
860 int selectionStartOffset() {
861 return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).startOffset").toInt();
864 int selectionEndOffset() {
865 return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).endOffset").toInt();
868 // true if start offset == end offset, i.e. no selected text
869 int isSelectionCollapsed() {
870 return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).collapsed").toBool();
874 void tst_QWebPage::cursorMovements()
876 CursorTrackedPage* page = new CursorTrackedPage;
877 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>");
878 page->mainFrame()->setHtml(content);
880 // this will select the first paragraph
881 QString script = "var range = document.createRange(); " \
882 "var node = document.getElementById(\"one\"); " \
883 "range.selectNode(node); " \
884 "getSelection().addRange(range);";
885 page->mainFrame()->evaluateJavaScript(script);
886 QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
888 // these actions must exist
889 QVERIFY(page->action(QWebPage::MoveToNextChar) != 0);
890 QVERIFY(page->action(QWebPage::MoveToPreviousChar) != 0);
891 QVERIFY(page->action(QWebPage::MoveToNextWord) != 0);
892 QVERIFY(page->action(QWebPage::MoveToPreviousWord) != 0);
893 QVERIFY(page->action(QWebPage::MoveToNextLine) != 0);
894 QVERIFY(page->action(QWebPage::MoveToPreviousLine) != 0);
895 QVERIFY(page->action(QWebPage::MoveToStartOfLine) != 0);
896 QVERIFY(page->action(QWebPage::MoveToEndOfLine) != 0);
897 QVERIFY(page->action(QWebPage::MoveToStartOfBlock) != 0);
898 QVERIFY(page->action(QWebPage::MoveToEndOfBlock) != 0);
899 QVERIFY(page->action(QWebPage::MoveToStartOfDocument) != 0);
900 QVERIFY(page->action(QWebPage::MoveToEndOfDocument) != 0);
902 // right now they are disabled because contentEditable is false
903 QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), false);
904 QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), false);
905 QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), false);
906 QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), false);
907 QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), false);
908 QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), false);
909 QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), false);
910 QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), false);
911 QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), false);
912 QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), false);
913 QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), false);
914 QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), false);
916 // make it editable before navigating the cursor
917 page->setContentEditable(true);
919 // here the actions are enabled after contentEditable is true
920 QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), true);
921 QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), true);
922 QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), true);
923 QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), true);
924 QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), true);
925 QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), true);
926 QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), true);
927 QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), true);
928 QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), true);
929 QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), true);
930 QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), true);
931 QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), true);
933 // cursor will be before the word "jump"
934 page->triggerAction(QWebPage::MoveToNextChar);
935 QVERIFY(page->isSelectionCollapsed());
936 QCOMPARE(page->selectionStartOffset(), 0);
938 // cursor will be between 'j' and 'u' in the word "jump"
939 page->triggerAction(QWebPage::MoveToNextChar);
940 QVERIFY(page->isSelectionCollapsed());
941 QCOMPARE(page->selectionStartOffset(), 1);
943 // cursor will be between 'u' and 'm' in the word "jump"
944 page->triggerAction(QWebPage::MoveToNextChar);
945 QVERIFY(page->isSelectionCollapsed());
946 QCOMPARE(page->selectionStartOffset(), 2);
948 // cursor will be after the word "jump"
949 page->triggerAction(QWebPage::MoveToNextWord);
950 QVERIFY(page->isSelectionCollapsed());
951 QCOMPARE(page->selectionStartOffset(), 5);
953 // cursor will be after the word "lazy"
954 page->triggerAction(QWebPage::MoveToNextWord);
955 page->triggerAction(QWebPage::MoveToNextWord);
956 page->triggerAction(QWebPage::MoveToNextWord);
957 QVERIFY(page->isSelectionCollapsed());
958 QCOMPARE(page->selectionStartOffset(), 19);
960 // cursor will be between 'z' and 'y' in "lazy"
961 page->triggerAction(QWebPage::MoveToPreviousChar);
962 QVERIFY(page->isSelectionCollapsed());
963 QCOMPARE(page->selectionStartOffset(), 18);
965 // cursor will be between 'a' and 'z' in "lazy"
966 page->triggerAction(QWebPage::MoveToPreviousChar);
967 QVERIFY(page->isSelectionCollapsed());
968 QCOMPARE(page->selectionStartOffset(), 17);
970 // cursor will be before the word "lazy"
971 page->triggerAction(QWebPage::MoveToPreviousWord);
972 QVERIFY(page->isSelectionCollapsed());
973 QCOMPARE(page->selectionStartOffset(), 15);
975 // cursor will be before the word "quick"
976 page->triggerAction(QWebPage::MoveToPreviousWord);
977 page->triggerAction(QWebPage::MoveToPreviousWord);
978 page->triggerAction(QWebPage::MoveToPreviousWord);
979 page->triggerAction(QWebPage::MoveToPreviousWord);
980 page->triggerAction(QWebPage::MoveToPreviousWord);
981 page->triggerAction(QWebPage::MoveToPreviousWord);
982 QVERIFY(page->isSelectionCollapsed());
983 QCOMPARE(page->selectionStartOffset(), 4);
985 // cursor will be between 'p' and 's' in the word "jumps"
986 page->triggerAction(QWebPage::MoveToNextWord);
987 page->triggerAction(QWebPage::MoveToNextWord);
988 page->triggerAction(QWebPage::MoveToNextWord);
989 page->triggerAction(QWebPage::MoveToNextChar);
990 page->triggerAction(QWebPage::MoveToNextChar);
991 page->triggerAction(QWebPage::MoveToNextChar);
992 page->triggerAction(QWebPage::MoveToNextChar);
993 page->triggerAction(QWebPage::MoveToNextChar);
994 QVERIFY(page->isSelectionCollapsed());
995 QCOMPARE(page->selectionStartOffset(), 4);
997 // cursor will be before the word "jumps"
998 page->triggerAction(QWebPage::MoveToStartOfLine);
999 QVERIFY(page->isSelectionCollapsed());
1000 QCOMPARE(page->selectionStartOffset(), 0);
1002 // cursor will be after the word "dog"
1003 page->triggerAction(QWebPage::MoveToEndOfLine);
1004 QVERIFY(page->isSelectionCollapsed());
1005 QCOMPARE(page->selectionStartOffset(), 23);
1007 // cursor will be between 'w' and 'n' in "brown"
1008 page->triggerAction(QWebPage::MoveToStartOfLine);
1009 page->triggerAction(QWebPage::MoveToPreviousWord);
1010 page->triggerAction(QWebPage::MoveToPreviousWord);
1011 page->triggerAction(QWebPage::MoveToNextChar);
1012 page->triggerAction(QWebPage::MoveToNextChar);
1013 page->triggerAction(QWebPage::MoveToNextChar);
1014 page->triggerAction(QWebPage::MoveToNextChar);
1015 QVERIFY(page->isSelectionCollapsed());
1016 QCOMPARE(page->selectionStartOffset(), 14);
1018 // cursor will be after the word "fox"
1019 page->triggerAction(QWebPage::MoveToEndOfLine);
1020 QVERIFY(page->isSelectionCollapsed());
1021 QCOMPARE(page->selectionStartOffset(), 19);
1023 // cursor will be before the word "The"
1024 page->triggerAction(QWebPage::MoveToStartOfDocument);
1025 QVERIFY(page->isSelectionCollapsed());
1026 QCOMPARE(page->selectionStartOffset(), 0);
1028 // cursor will be after the word "you!"
1029 page->triggerAction(QWebPage::MoveToEndOfDocument);
1030 QVERIFY(page->isSelectionCollapsed());
1031 QCOMPARE(page->selectionStartOffset(), 12);
1033 // cursor will be before the word "be"
1034 page->triggerAction(QWebPage::MoveToStartOfBlock);
1035 QVERIFY(page->isSelectionCollapsed());
1036 QCOMPARE(page->selectionStartOffset(), 0);
1038 // cursor will be after the word "you!"
1039 page->triggerAction(QWebPage::MoveToEndOfBlock);
1040 QVERIFY(page->isSelectionCollapsed());
1041 QCOMPARE(page->selectionStartOffset(), 12);
1043 // try to move before the document start
1044 page->triggerAction(QWebPage::MoveToStartOfDocument);
1045 page->triggerAction(QWebPage::MoveToPreviousChar);
1046 QVERIFY(page->isSelectionCollapsed());
1047 QCOMPARE(page->selectionStartOffset(), 0);
1048 page->triggerAction(QWebPage::MoveToStartOfDocument);
1049 page->triggerAction(QWebPage::MoveToPreviousWord);
1050 QVERIFY(page->isSelectionCollapsed());
1051 QCOMPARE(page->selectionStartOffset(), 0);
1053 // try to move past the document end
1054 page->triggerAction(QWebPage::MoveToEndOfDocument);
1055 page->triggerAction(QWebPage::MoveToNextChar);
1056 QVERIFY(page->isSelectionCollapsed());
1057 QCOMPARE(page->selectionStartOffset(), 12);
1058 page->triggerAction(QWebPage::MoveToEndOfDocument);
1059 page->triggerAction(QWebPage::MoveToNextWord);
1060 QVERIFY(page->isSelectionCollapsed());
1061 QCOMPARE(page->selectionStartOffset(), 12);
1066 void tst_QWebPage::textSelection()
1068 CursorTrackedPage* page = new CursorTrackedPage;
1069 QString content("<html><body><p id=one>The quick brown fox</p>" \
1070 "<p id=two>jumps over the lazy dog</p>" \
1071 "<p>May the source<br/>be with you!</p></body></html>");
1072 page->mainFrame()->setHtml(content);
1074 // these actions must exist
1075 QVERIFY(page->action(QWebPage::SelectAll) != 0);
1076 QVERIFY(page->action(QWebPage::SelectNextChar) != 0);
1077 QVERIFY(page->action(QWebPage::SelectPreviousChar) != 0);
1078 QVERIFY(page->action(QWebPage::SelectNextWord) != 0);
1079 QVERIFY(page->action(QWebPage::SelectPreviousWord) != 0);
1080 QVERIFY(page->action(QWebPage::SelectNextLine) != 0);
1081 QVERIFY(page->action(QWebPage::SelectPreviousLine) != 0);
1082 QVERIFY(page->action(QWebPage::SelectStartOfLine) != 0);
1083 QVERIFY(page->action(QWebPage::SelectEndOfLine) != 0);
1084 QVERIFY(page->action(QWebPage::SelectStartOfBlock) != 0);
1085 QVERIFY(page->action(QWebPage::SelectEndOfBlock) != 0);
1086 QVERIFY(page->action(QWebPage::SelectStartOfDocument) != 0);
1087 QVERIFY(page->action(QWebPage::SelectEndOfDocument) != 0);
1089 // right now they are disabled because contentEditable is false and
1090 // there isn't an existing selection to modify
1091 QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), false);
1092 QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), false);
1093 QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), false);
1094 QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), false);
1095 QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), false);
1096 QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), false);
1097 QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), false);
1098 QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), false);
1099 QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), false);
1100 QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), false);
1101 QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), false);
1102 QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), false);
1104 // ..but SelectAll is awalys enabled
1105 QCOMPARE(page->action(QWebPage::SelectAll)->isEnabled(), true);
1107 // this will select the first paragraph
1108 QString selectScript = "var range = document.createRange(); " \
1109 "var node = document.getElementById(\"one\"); " \
1110 "range.selectNode(node); " \
1111 "getSelection().addRange(range);";
1112 page->mainFrame()->evaluateJavaScript(selectScript);
1113 QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
1115 // here the actions are enabled after a selection has been created
1116 QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true);
1117 QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true);
1118 QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true);
1119 QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true);
1120 QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true);
1121 QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true);
1122 QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true);
1123 QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true);
1124 QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true);
1125 QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true);
1126 QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true);
1127 QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true);
1129 // make it editable before navigating the cursor
1130 page->setContentEditable(true);
1132 // cursor will be before the word "The", this makes sure there is a charet
1133 page->triggerAction(QWebPage::MoveToStartOfDocument);
1134 QVERIFY(page->isSelectionCollapsed());
1135 QCOMPARE(page->selectionStartOffset(), 0);
1137 // here the actions are enabled after contentEditable is true
1138 QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true);
1139 QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true);
1140 QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true);
1141 QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true);
1142 QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true);
1143 QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true);
1144 QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true);
1145 QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true);
1146 QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true);
1147 QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true);
1148 QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true);
1149 QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true);
1154 void tst_QWebPage::textEditing()
1156 CursorTrackedPage* page = new CursorTrackedPage;
1157 QString content("<html><body><p id=one>The quick brown fox</p>" \
1158 "<p id=two>jumps over the lazy dog</p>" \
1159 "<p>May the source<br/>be with you!</p></body></html>");
1160 page->mainFrame()->setHtml(content);
1162 // these actions must exist
1163 QVERIFY(page->action(QWebPage::Cut) != 0);
1164 QVERIFY(page->action(QWebPage::Copy) != 0);
1165 QVERIFY(page->action(QWebPage::Paste) != 0);
1166 QVERIFY(page->action(QWebPage::DeleteStartOfWord) != 0);
1167 QVERIFY(page->action(QWebPage::DeleteEndOfWord) != 0);
1168 QVERIFY(page->action(QWebPage::SetTextDirectionDefault) != 0);
1169 QVERIFY(page->action(QWebPage::SetTextDirectionLeftToRight) != 0);
1170 QVERIFY(page->action(QWebPage::SetTextDirectionRightToLeft) != 0);
1171 QVERIFY(page->action(QWebPage::ToggleBold) != 0);
1172 QVERIFY(page->action(QWebPage::ToggleItalic) != 0);
1173 QVERIFY(page->action(QWebPage::ToggleUnderline) != 0);
1174 QVERIFY(page->action(QWebPage::InsertParagraphSeparator) != 0);
1175 QVERIFY(page->action(QWebPage::InsertLineSeparator) != 0);
1176 QVERIFY(page->action(QWebPage::PasteAndMatchStyle) != 0);
1177 QVERIFY(page->action(QWebPage::RemoveFormat) != 0);
1178 QVERIFY(page->action(QWebPage::ToggleStrikethrough) != 0);
1179 QVERIFY(page->action(QWebPage::ToggleSubscript) != 0);
1180 QVERIFY(page->action(QWebPage::ToggleSuperscript) != 0);
1181 QVERIFY(page->action(QWebPage::InsertUnorderedList) != 0);
1182 QVERIFY(page->action(QWebPage::InsertOrderedList) != 0);
1183 QVERIFY(page->action(QWebPage::Indent) != 0);
1184 QVERIFY(page->action(QWebPage::Outdent) != 0);
1185 QVERIFY(page->action(QWebPage::AlignCenter) != 0);
1186 QVERIFY(page->action(QWebPage::AlignJustified) != 0);
1187 QVERIFY(page->action(QWebPage::AlignLeft) != 0);
1188 QVERIFY(page->action(QWebPage::AlignRight) != 0);
1190 // right now they are disabled because contentEditable is false
1191 QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false);
1192 QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), false);
1193 QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), false);
1194 QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), false);
1195 QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), false);
1196 QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), false);
1197 QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), false);
1198 QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), false);
1199 QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), false);
1200 QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), false);
1201 QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), false);
1202 QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), false);
1203 QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), false);
1204 QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false);
1205 QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), false);
1206 QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), false);
1207 QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), false);
1208 QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), false);
1209 QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), false);
1210 QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), false);
1211 QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), false);
1212 QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), false);
1213 QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), false);
1214 QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), false);
1215 QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), false);
1217 // Select everything
1218 page->triggerAction(QWebPage::SelectAll);
1220 // make sure it is enabled since there is a selection
1221 QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), true);
1223 // make it editable before navigating the cursor
1224 page->setContentEditable(true);
1226 // clear the selection
1227 page->triggerAction(QWebPage::MoveToStartOfDocument);
1228 QVERIFY(page->isSelectionCollapsed());
1229 QCOMPARE(page->selectionStartOffset(), 0);
1231 // make sure it is disabled since there isn't a selection
1232 QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), false);
1234 // here the actions are enabled after contentEditable is true
1235 QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), true);
1236 QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), true);
1237 QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), true);
1238 QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), true);
1239 QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), true);
1240 QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), true);
1241 QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), true);
1242 QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), true);
1243 QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), true);
1244 QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), true);
1245 QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), true);
1246 QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), true);
1247 QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), true);
1248 QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), true);
1249 QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), true);
1250 QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), true);
1251 QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), true);
1252 QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), true);
1253 QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), true);
1254 QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), true);
1255 QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), true);
1256 QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), true);
1257 QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), true);
1259 // make sure these are disabled since there isn't a selection
1260 QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false);
1261 QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false);
1263 // make sure everything is selected
1264 page->triggerAction(QWebPage::SelectAll);
1266 // this is only true if there is an editable selection
1267 QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), true);
1268 QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), true);
1273 void tst_QWebPage::requestCache()
1276 QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
1278 page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
1279 QTRY_COMPARE(loadSpy.count(), 1);
1280 QTRY_COMPARE(page.navigations.count(), 1);
1282 page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me2</a>"));
1283 QTRY_COMPARE(loadSpy.count(), 2);
1284 QTRY_COMPARE(page.navigations.count(), 2);
1286 page.triggerAction(QWebPage::Stop);
1287 QVERIFY(page.history()->canGoBack());
1288 page.triggerAction(QWebPage::Back);
1290 QTRY_COMPARE(loadSpy.count(), 3);
1291 QTRY_COMPARE(page.navigations.count(), 3);
1292 QCOMPARE(page.navigations.at(0).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
1293 (int)QNetworkRequest::PreferNetwork);
1294 QCOMPARE(page.navigations.at(1).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
1295 (int)QNetworkRequest::PreferNetwork);
1296 QCOMPARE(page.navigations.at(2).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
1297 (int)QNetworkRequest::PreferCache);
1300 void tst_QWebPage::loadCachedPage()
1303 QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
1304 page.settings()->setMaximumPagesInCache(3);
1306 page.mainFrame()->load(QUrl("data:text/html,This is first page"));
1308 QTRY_COMPARE(loadSpy.count(), 1);
1309 QTRY_COMPARE(page.navigations.count(), 1);
1311 QUrl firstPageUrl = page.mainFrame()->url();
1312 page.mainFrame()->load(QUrl("data:text/html,This is second page"));
1314 QTRY_COMPARE(loadSpy.count(), 2);
1315 QTRY_COMPARE(page.navigations.count(), 2);
1317 page.triggerAction(QWebPage::Stop);
1318 QVERIFY(page.history()->canGoBack());
1320 QSignalSpy urlSpy(page.mainFrame(), SIGNAL(urlChanged(QUrl)));
1321 QVERIFY(urlSpy.isValid());
1323 page.triggerAction(QWebPage::Back);
1324 ::waitForSignal(page.mainFrame(), SIGNAL(urlChanged(QUrl)));
1325 QCOMPARE(urlSpy.size(), 1);
1327 QList<QVariant> arguments1 = urlSpy.takeFirst();
1328 QCOMPARE(arguments1.at(0).toUrl(), firstPageUrl);
1331 void tst_QWebPage::backActionUpdate()
1334 QWebPage *page = view.page();
1335 QAction *action = page->action(QWebPage::Back);
1336 QVERIFY(!action->isEnabled());
1337 QSignalSpy loadSpy(page, SIGNAL(loadFinished(bool)));
1338 QUrl url = QUrl("qrc:///resources/index.html");
1339 page->mainFrame()->load(url);
1340 QTRY_COMPARE(loadSpy.count(), 1);
1341 QVERIFY(!action->isEnabled());
1342 QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(10, 10));
1343 QTRY_COMPARE(loadSpy.count(), 2);
1345 QVERIFY(action->isEnabled());
1348 void frameAtHelper(QWebPage* webPage, QWebFrame* webFrame, QPoint framePosition)
1353 framePosition += QPoint(webFrame->pos());
1354 QList<QWebFrame*> children = webFrame->childFrames();
1355 for (int i = 0; i < children.size(); ++i) {
1356 if (children.at(i)->childFrames().size() > 0)
1357 frameAtHelper(webPage, children.at(i), framePosition);
1359 QRect frameRect(children.at(i)->pos() + framePosition, children.at(i)->geometry().size());
1360 QVERIFY(children.at(i) == webPage->frameAt(frameRect.topLeft()));
1364 void tst_QWebPage::frameAt()
1367 QWebPage* webPage = webView.page();
1368 QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool)));
1369 QUrl url = QUrl("qrc:///resources/iframe.html");
1370 webPage->mainFrame()->load(url);
1371 QTRY_COMPARE(loadSpy.count(), 1);
1372 frameAtHelper(webPage, webPage->mainFrame(), webPage->mainFrame()->pos());
1375 void tst_QWebPage::inputMethods_data()
1377 QTest::addColumn<QString>("viewType");
1378 QTest::newRow("QWebView") << "QWebView";
1379 QTest::newRow("QGraphicsWebView") << "QGraphicsWebView";
1382 static Qt::InputMethodHints inputMethodHints(QObject* object)
1384 if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
1385 return o->inputMethodHints();
1386 if (QWidget* w = qobject_cast<QWidget*>(object))
1387 return w->inputMethodHints();
1388 return Qt::InputMethodHints();
1391 static bool inputMethodEnabled(QObject* object)
1393 if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
1394 return o->flags() & QGraphicsItem::ItemAcceptsInputMethod;
1395 if (QWidget* w = qobject_cast<QWidget*>(object))
1396 return w->testAttribute(Qt::WA_InputMethodEnabled);
1400 void tst_QWebPage::inputMethods()
1402 QFETCH(QString, viewType);
1403 QWebPage* page = new QWebPage;
1405 QObject* container = 0;
1406 if (viewType == "QWebView") {
1407 QWebView* wv = new QWebView;
1411 } else if (viewType == "QGraphicsWebView") {
1412 QGraphicsWebView* wv = new QGraphicsWebView;
1416 QGraphicsView* gv = new QGraphicsView;
1417 QGraphicsScene* scene = new QGraphicsScene(gv);
1418 gv->setScene(scene);
1420 wv->setGeometry(QRect(0, 0, 500, 500));
1424 QVERIFY2(false, "Unknown view type");
1426 page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont");
1427 page->mainFrame()->setHtml("<html><body>" \
1428 "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/><br>" \
1429 "<input type='password'/>" \
1431 page->mainFrame()->setFocus();
1433 EventSpy viewEventSpy(container);
1435 QWebElementCollection inputs = page->mainFrame()->documentElement().findAll("input");
1437 QMouseEvent evpres(QEvent::MouseButtonPress, inputs.at(0).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1438 page->event(&evpres);
1439 QMouseEvent evrel(QEvent::MouseButtonRelease, inputs.at(0).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1440 page->event(&evrel);
1442 // This part of the test checks if the SIP (Software Input Panel) is triggered,
1443 // which normally happens on mobile platforms, when a user input form receives
1446 if (viewType == "QWebView") {
1447 if (QWebView* wv = qobject_cast<QWebView*>(view))
1448 inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel);
1449 } else if (viewType == "QGraphicsWebView") {
1450 if (QGraphicsWebView* wv = qobject_cast<QGraphicsWebView*>(view))
1451 inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel);
1454 // For non-mobile platforms RequestSoftwareInputPanel event is not called
1455 // because there is no SIP (Software Input Panel) triggered. In the case of a
1456 // mobile platform, an input panel, e.g. virtual keyboard, is usually invoked
1457 // and the RequestSoftwareInputPanel event is called. For these two situations
1458 // this part of the test can verified as the checks below.
1460 QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1462 QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1463 viewEventSpy.clear();
1465 page->event(&evpres);
1466 page->event(&evrel);
1467 QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1470 QVariant variant = page->inputMethodQuery(Qt::ImMicroFocus);
1471 QRect focusRect = variant.toRect();
1472 QVERIFY(inputs.at(0).geometry().contains(variant.toRect().topLeft()));
1475 variant = page->inputMethodQuery(Qt::ImFont);
1476 QFont font = variant.value<QFont>();
1477 QCOMPARE(page->settings()->fontFamily(QWebSettings::SerifFont), font.family());
1479 QList<QInputMethodEvent::Attribute> inputAttributes;
1483 QInputMethodEvent eventText("QtWebKit", inputAttributes);
1484 QSignalSpy signalSpy(page, SIGNAL(microFocusChanged()));
1485 page->event(&eventText);
1486 QCOMPARE(signalSpy.count(), 0);
1490 QInputMethodEvent eventText("", inputAttributes);
1491 eventText.setCommitString(QString("QtWebKit"), 0, 0);
1492 page->event(&eventText);
1495 //ImMaximumTextLength
1496 variant = page->inputMethodQuery(Qt::ImMaximumTextLength);
1497 QCOMPARE(20, variant.toInt());
1500 inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant());
1501 QInputMethodEvent eventSelection("",inputAttributes);
1502 page->event(&eventSelection);
1505 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1506 int anchorPosition = variant.toInt();
1507 QCOMPARE(anchorPosition, 3);
1510 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1511 int cursorPosition = variant.toInt();
1512 QCOMPARE(cursorPosition, 5);
1514 //ImCurrentSelection
1515 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1516 QString selectionValue = variant.value<QString>();
1517 QCOMPARE(selectionValue, QString("eb"));
1519 //Set selection with negative length
1520 inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 6, -5, QVariant());
1521 QInputMethodEvent eventSelection3("",inputAttributes);
1522 page->event(&eventSelection3);
1525 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1526 anchorPosition = variant.toInt();
1527 QCOMPARE(anchorPosition, 1);
1530 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1531 cursorPosition = variant.toInt();
1532 QCOMPARE(cursorPosition, 6);
1534 //ImCurrentSelection
1535 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1536 selectionValue = variant.value<QString>();
1537 QCOMPARE(selectionValue, QString("tWebK"));
1540 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1541 QString value = variant.value<QString>();
1542 QCOMPARE(value, QString("QtWebKit"));
1545 QList<QInputMethodEvent::Attribute> attributes;
1546 // Clear the selection, so the next test does not clear any contents.
1547 QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
1548 attributes.append(newSelection);
1549 QInputMethodEvent event("composition", attributes);
1550 page->event(&event);
1553 // A ongoing composition should not change the surrounding text before it is committed.
1554 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1555 value = variant.value<QString>();
1556 QCOMPARE(value, QString("QtWebKit"));
1558 // Cancel current composition first
1559 inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant());
1560 QInputMethodEvent eventSelection4("", inputAttributes);
1561 page->event(&eventSelection4);
1563 // START - Tests for Selection when the Editor is NOT in Composition mode
1565 // LEFT to RIGHT selection
1566 // Deselect the selection by sending MouseButtonPress events
1567 // This moves the current cursor to the end of the text
1568 page->event(&evpres);
1569 page->event(&evrel);
1572 QList<QInputMethodEvent::Attribute> attributes;
1573 QInputMethodEvent event(QString(), attributes);
1574 event.setCommitString("XXX", 0, 0);
1575 page->event(&event);
1576 event.setCommitString(QString(), -2, 2); // Erase two characters.
1577 page->event(&event);
1578 event.setCommitString(QString(), -1, 1); // Erase one character.
1579 page->event(&event);
1580 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1581 value = variant.value<QString>();
1582 QCOMPARE(value, QString("QtWebKit"));
1585 //Move to the start of the line
1586 page->triggerAction(QWebPage::MoveToStartOfLine);
1588 QKeyEvent keyRightEventPress(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier);
1589 QKeyEvent keyRightEventRelease(QEvent::KeyRelease, Qt::Key_Right, Qt::NoModifier);
1591 //Move 2 characters RIGHT
1592 for (int j = 0; j < 2; ++j) {
1593 page->event(&keyRightEventPress);
1594 page->event(&keyRightEventRelease);
1597 //Select to the end of the line
1598 page->triggerAction(QWebPage::SelectEndOfLine);
1600 //ImAnchorPosition QtWebKit
1601 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1602 anchorPosition = variant.toInt();
1603 QCOMPARE(anchorPosition, 2);
1606 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1607 cursorPosition = variant.toInt();
1608 QCOMPARE(cursorPosition, 8);
1610 //ImCurrentSelection
1611 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1612 selectionValue = variant.value<QString>();
1613 QCOMPARE(selectionValue, QString("WebKit"));
1615 //RIGHT to LEFT selection
1616 //Deselect the selection (this moves the current cursor to the end of the text)
1617 page->event(&evpres);
1618 page->event(&evrel);
1621 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1622 anchorPosition = variant.toInt();
1623 QCOMPARE(anchorPosition, 8);
1626 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1627 cursorPosition = variant.toInt();
1628 QCOMPARE(cursorPosition, 8);
1630 //ImCurrentSelection
1631 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1632 selectionValue = variant.value<QString>();
1633 QCOMPARE(selectionValue, QString(""));
1635 QKeyEvent keyLeftEventPress(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier);
1636 QKeyEvent keyLeftEventRelease(QEvent::KeyRelease, Qt::Key_Left, Qt::NoModifier);
1638 //Move 2 characters LEFT
1639 for (int i = 0; i < 2; ++i) {
1640 page->event(&keyLeftEventPress);
1641 page->event(&keyLeftEventRelease);
1644 //Select to the start of the line
1645 page->triggerAction(QWebPage::SelectStartOfLine);
1648 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1649 anchorPosition = variant.toInt();
1650 QCOMPARE(anchorPosition, 6);
1653 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1654 cursorPosition = variant.toInt();
1655 QCOMPARE(cursorPosition, 0);
1657 //ImCurrentSelection
1658 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1659 selectionValue = variant.value<QString>();
1660 QCOMPARE(selectionValue, QString("QtWebK"));
1662 //END - Tests for Selection when the Editor is not in Composition mode
1665 QMouseEvent evpresPassword(QEvent::MouseButtonPress, inputs.at(1).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1666 page->event(&evpresPassword);
1667 QMouseEvent evrelPassword(QEvent::MouseButtonRelease, inputs.at(1).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1668 page->event(&evrelPassword);
1670 QVERIFY(inputMethodEnabled(view));
1671 QVERIFY(inputMethodHints(view) & Qt::ImhHiddenText);
1673 page->event(&evpres);
1674 page->event(&evrel);
1675 QVERIFY(!(inputMethodHints(view) & Qt::ImhHiddenText));
1677 page->mainFrame()->setHtml("<html><body><p>nothing to input here");
1678 viewEventSpy.clear();
1680 QWebElement para = page->mainFrame()->findFirstElement("p");
1682 QMouseEvent evpres(QEvent::MouseButtonPress, para.geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1683 page->event(&evpres);
1684 QMouseEvent evrel(QEvent::MouseButtonRelease, para.geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1685 page->event(&evrel);
1688 QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1690 //START - Test for sending empty QInputMethodEvent
1691 page->mainFrame()->setHtml("<html><body>" \
1692 "<input type='text' id='input3' value='QtWebKit2'/>" \
1694 page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input3'); inputEle.focus(); inputEle.select();");
1696 //Send empty QInputMethodEvent
1697 QInputMethodEvent emptyEvent;
1698 page->event(&emptyEvent);
1700 QString inputValue = page->mainFrame()->evaluateJavaScript("document.getElementById('input3').value").toString();
1701 QCOMPARE(inputValue, QString("QtWebKit2"));
1702 //END - Test for sending empty QInputMethodEvent
1704 page->mainFrame()->setHtml("<html><body>" \
1705 "<input type='text' id='input4' value='QtWebKit inputMethod'/>" \
1707 page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input4'); inputEle.focus(); inputEle.select();");
1709 // Clear the selection, also cancel the ongoing composition if there is one.
1711 QList<QInputMethodEvent::Attribute> attributes;
1712 QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
1713 attributes.append(newSelection);
1714 QInputMethodEvent event("", attributes);
1715 page->event(&event);
1718 // ImCurrentSelection
1719 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1720 selectionValue = variant.value<QString>();
1721 QCOMPARE(selectionValue, QString(""));
1723 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1724 QString surroundingValue = variant.value<QString>();
1725 QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
1728 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1729 anchorPosition = variant.toInt();
1730 QCOMPARE(anchorPosition, 0);
1733 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1734 cursorPosition = variant.toInt();
1735 QCOMPARE(cursorPosition, 0);
1737 // 1. Insert a character to the begining of the line.
1738 // Send temporary text, which makes the editor has composition 'm'.
1740 QList<QInputMethodEvent::Attribute> attributes;
1741 QInputMethodEvent event("m", attributes);
1742 page->event(&event);
1745 // ImCurrentSelection
1746 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1747 selectionValue = variant.value<QString>();
1748 QCOMPARE(selectionValue, QString(""));
1750 // ImSurroundingText
1751 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1752 surroundingValue = variant.value<QString>();
1753 QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
1756 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1757 cursorPosition = variant.toInt();
1758 QCOMPARE(cursorPosition, 0);
1761 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1762 anchorPosition = variant.toInt();
1763 QCOMPARE(anchorPosition, 0);
1765 // Send temporary text, which makes the editor has composition 'n'.
1767 QList<QInputMethodEvent::Attribute> attributes;
1768 QInputMethodEvent event("n", attributes);
1769 page->event(&event);
1772 // ImCurrentSelection
1773 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1774 selectionValue = variant.value<QString>();
1775 QCOMPARE(selectionValue, QString(""));
1777 // ImSurroundingText
1778 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1779 surroundingValue = variant.value<QString>();
1780 QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
1783 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1784 cursorPosition = variant.toInt();
1785 QCOMPARE(cursorPosition, 0);
1788 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1789 anchorPosition = variant.toInt();
1790 QCOMPARE(anchorPosition, 0);
1792 // Send commit text, which makes the editor conforms composition.
1794 QList<QInputMethodEvent::Attribute> attributes;
1795 QInputMethodEvent event("", attributes);
1796 event.setCommitString("o");
1797 page->event(&event);
1800 // ImCurrentSelection
1801 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1802 selectionValue = variant.value<QString>();
1803 QCOMPARE(selectionValue, QString(""));
1805 // ImSurroundingText
1806 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1807 surroundingValue = variant.value<QString>();
1808 QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod"));
1811 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1812 cursorPosition = variant.toInt();
1813 QCOMPARE(cursorPosition, 1);
1816 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1817 anchorPosition = variant.toInt();
1818 QCOMPARE(anchorPosition, 1);
1820 // 2. insert a character to the middle of the line.
1821 // Send temporary text, which makes the editor has composition 'd'.
1823 QList<QInputMethodEvent::Attribute> attributes;
1824 QInputMethodEvent event("d", attributes);
1825 page->event(&event);
1828 // ImCurrentSelection
1829 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1830 selectionValue = variant.value<QString>();
1831 QCOMPARE(selectionValue, QString(""));
1833 // ImSurroundingText
1834 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1835 surroundingValue = variant.value<QString>();
1836 QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod"));
1839 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1840 cursorPosition = variant.toInt();
1841 QCOMPARE(cursorPosition, 1);
1844 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1845 anchorPosition = variant.toInt();
1846 QCOMPARE(anchorPosition, 1);
1848 // Send commit text, which makes the editor conforms composition.
1850 QList<QInputMethodEvent::Attribute> attributes;
1851 QInputMethodEvent event("", attributes);
1852 event.setCommitString("e");
1853 page->event(&event);
1856 // ImCurrentSelection
1857 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1858 selectionValue = variant.value<QString>();
1859 QCOMPARE(selectionValue, QString(""));
1861 // ImSurroundingText
1862 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1863 surroundingValue = variant.value<QString>();
1864 QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod"));
1867 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1868 cursorPosition = variant.toInt();
1869 QCOMPARE(cursorPosition, 2);
1872 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1873 anchorPosition = variant.toInt();
1874 QCOMPARE(anchorPosition, 2);
1876 // 3. Insert a character to the end of the line.
1877 page->triggerAction(QWebPage::MoveToEndOfLine);
1879 // Send temporary text, which makes the editor has composition 't'.
1881 QList<QInputMethodEvent::Attribute> attributes;
1882 QInputMethodEvent event("t", attributes);
1883 page->event(&event);
1886 // ImCurrentSelection
1887 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1888 selectionValue = variant.value<QString>();
1889 QCOMPARE(selectionValue, QString(""));
1891 // ImSurroundingText
1892 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1893 surroundingValue = variant.value<QString>();
1894 QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod"));
1897 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1898 cursorPosition = variant.toInt();
1899 QCOMPARE(cursorPosition, 22);
1902 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1903 anchorPosition = variant.toInt();
1904 QCOMPARE(anchorPosition, 22);
1906 // Send commit text, which makes the editor conforms composition.
1908 QList<QInputMethodEvent::Attribute> attributes;
1909 QInputMethodEvent event("", attributes);
1910 event.setCommitString("t");
1911 page->event(&event);
1914 // ImCurrentSelection
1915 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1916 selectionValue = variant.value<QString>();
1917 QCOMPARE(selectionValue, QString(""));
1919 // ImSurroundingText
1920 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1921 surroundingValue = variant.value<QString>();
1922 QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt"));
1925 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1926 cursorPosition = variant.toInt();
1927 QCOMPARE(cursorPosition, 23);
1930 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1931 anchorPosition = variant.toInt();
1932 QCOMPARE(anchorPosition, 23);
1934 // 4. Replace the selection.
1935 page->triggerAction(QWebPage::SelectPreviousWord);
1937 // ImCurrentSelection
1938 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1939 selectionValue = variant.value<QString>();
1940 QCOMPARE(selectionValue, QString("inputMethodt"));
1942 // ImSurroundingText
1943 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1944 surroundingValue = variant.value<QString>();
1945 QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt"));
1948 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1949 cursorPosition = variant.toInt();
1950 QCOMPARE(cursorPosition, 11);
1953 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1954 anchorPosition = variant.toInt();
1955 QCOMPARE(anchorPosition, 23);
1957 // Send temporary text, which makes the editor has composition 'w'.
1959 QList<QInputMethodEvent::Attribute> attributes;
1960 QInputMethodEvent event("w", attributes);
1961 page->event(&event);
1964 // ImCurrentSelection
1965 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1966 selectionValue = variant.value<QString>();
1967 QCOMPARE(selectionValue, QString(""));
1969 // ImSurroundingText
1970 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1971 surroundingValue = variant.value<QString>();
1972 QCOMPARE(surroundingValue, QString("oeQtWebKit "));
1975 variant = page->inputMethodQuery(Qt::ImCursorPosition);
1976 cursorPosition = variant.toInt();
1977 QCOMPARE(cursorPosition, 11);
1980 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1981 anchorPosition = variant.toInt();
1982 QCOMPARE(anchorPosition, 11);
1984 // Send commit text, which makes the editor conforms composition.
1986 QList<QInputMethodEvent::Attribute> attributes;
1987 QInputMethodEvent event("", attributes);
1988 event.setCommitString("2");
1989 page->event(&event);
1992 // ImCurrentSelection
1993 variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1994 selectionValue = variant.value<QString>();
1995 QCOMPARE(selectionValue, QString(""));
1997 // ImSurroundingText
1998 variant = page->inputMethodQuery(Qt::ImSurroundingText);
1999 surroundingValue = variant.value<QString>();
2000 QCOMPARE(surroundingValue, QString("oeQtWebKit 2"));
2003 variant = page->inputMethodQuery(Qt::ImCursorPosition);
2004 cursorPosition = variant.toInt();
2005 QCOMPARE(cursorPosition, 12);
2008 variant = page->inputMethodQuery(Qt::ImAnchorPosition);
2009 anchorPosition = variant.toInt();
2010 QCOMPARE(anchorPosition, 12);
2015 void tst_QWebPage::inputMethodsTextFormat_data()
2017 QTest::addColumn<QString>("string");
2018 QTest::addColumn<int>("start");
2019 QTest::addColumn<int>("length");
2021 QTest::newRow("") << QString("") << 0 << 0;
2022 QTest::newRow("Q") << QString("Q") << 0 << 1;
2023 QTest::newRow("Qt") << QString("Qt") << 0 << 1;
2024 QTest::newRow("Qt") << QString("Qt") << 0 << 2;
2025 QTest::newRow("Qt") << QString("Qt") << 1 << 1;
2026 QTest::newRow("Qt ") << QString("Qt ") << 0 << 1;
2027 QTest::newRow("Qt ") << QString("Qt ") << 1 << 1;
2028 QTest::newRow("Qt ") << QString("Qt ") << 2 << 1;
2029 QTest::newRow("Qt ") << QString("Qt ") << 2 << -1;
2030 QTest::newRow("Qt ") << QString("Qt ") << -2 << 3;
2031 QTest::newRow("Qt ") << QString("Qt ") << 0 << 3;
2032 QTest::newRow("Qt by") << QString("Qt by") << 0 << 1;
2033 QTest::newRow("Qt by Nokia") << QString("Qt by Nokia") << 0 << 1;
2037 void tst_QWebPage::inputMethodsTextFormat()
2039 QWebPage* page = new QWebPage;
2040 QWebView* view = new QWebView;
2041 view->setPage(page);
2042 page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont");
2043 page->mainFrame()->setHtml("<html><body>" \
2044 "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/>");
2045 page->mainFrame()->evaluateJavaScript("document.getElementById('input1').focus()");
2046 page->mainFrame()->setFocus();
2049 QFETCH(QString, string);
2051 QFETCH(int, length);
2053 QList<QInputMethodEvent::Attribute> attrs;
2054 QTextCharFormat format;
2055 format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
2056 format.setUnderlineColor(Qt::red);
2057 attrs.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, length, format));
2058 QInputMethodEvent im(string, attrs);
2066 void tst_QWebPage::protectBindingsRuntimeObjectsFromCollector()
2068 QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
2070 PluginPage* newPage = new PluginPage(m_view);
2071 m_view->setPage(newPage);
2073 m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
2075 m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='lineedit' id='mylineedit'/></body></html>"));
2076 QTRY_COMPARE(loadSpy.count(), 1);
2078 newPage->mainFrame()->evaluateJavaScript("function testme(text) { var lineedit = document.getElementById('mylineedit'); lineedit.setText(text); lineedit.selectAll(); }");
2080 newPage->mainFrame()->evaluateJavaScript("testme('foo')");
2082 DumpRenderTreeSupportQt::garbageCollectorCollect();
2085 newPage->mainFrame()->evaluateJavaScript("testme('bar')");
2088 void tst_QWebPage::localURLSchemes()
2090 int i = QWebSecurityOrigin::localSchemes().size();
2092 QWebSecurityOrigin::removeLocalScheme("file");
2093 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2094 QWebSecurityOrigin::addLocalScheme("file");
2095 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2097 QWebSecurityOrigin::removeLocalScheme("qrc");
2098 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i - 1);
2099 QWebSecurityOrigin::addLocalScheme("qrc");
2100 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2102 QString myscheme = "myscheme";
2103 QWebSecurityOrigin::addLocalScheme(myscheme);
2104 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i + 1);
2105 QVERIFY(QWebSecurityOrigin::localSchemes().contains(myscheme));
2106 QWebSecurityOrigin::removeLocalScheme(myscheme);
2107 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2108 QWebSecurityOrigin::removeLocalScheme(myscheme);
2109 QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2112 static inline bool testFlag(QWebPage& webPage, QWebSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue)
2114 webPage.settings()->setAttribute(settingAttribute, settingValue);
2115 return webPage.mainFrame()->evaluateJavaScript(QString("(window.%1 != undefined)").arg(jsObjectName)).toBool();
2118 void tst_QWebPage::testOptionalJSObjects()
2120 // Once a feature is enabled and the JS object is accessed turning off the setting will not turn off
2121 // the visibility of the JS object any more. For this reason this test uses two QWebPage instances.
2122 // Part of the test is to make sure that the QWebPage instances do not interfere with each other so turning on
2123 // a feature for one instance will not turn it on for another.
2128 webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
2129 webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
2131 QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
2132 QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
2133 QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", true), true);
2134 QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
2135 QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
2136 QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), true);
2138 QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false);
2139 QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", true), true);
2140 QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false);
2141 QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", false), true);
2144 void tst_QWebPage::testEnablePersistentStorage()
2148 // By default all persistent options should be disabled
2149 QCOMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), false);
2150 QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), false);
2151 QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), false);
2152 QVERIFY(webPage.settings()->iconDatabasePath().isEmpty());
2154 QWebSettings::enablePersistentStorage();
2157 QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), true);
2158 QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), true);
2159 QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), true);
2161 QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty());
2162 QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty());
2163 QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty());
2166 void tst_QWebPage::defaultTextEncoding()
2168 QWebFrame* mainFrame = m_page->mainFrame();
2170 QString defaultCharset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2171 QVERIFY(!defaultCharset.isEmpty());
2172 QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), defaultCharset);
2174 m_page->settings()->setDefaultTextEncoding(QString("utf-8"));
2175 QString charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2176 QCOMPARE(charset, QString("utf-8"));
2177 QCOMPARE(m_page->settings()->defaultTextEncoding(), charset);
2179 m_page->settings()->setDefaultTextEncoding(QString());
2180 charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2181 QVERIFY(!charset.isEmpty());
2182 QCOMPARE(charset, defaultCharset);
2184 QWebSettings::globalSettings()->setDefaultTextEncoding(QString("utf-8"));
2185 charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2186 QCOMPARE(charset, QString("utf-8"));
2187 QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), charset);
2190 class ErrorPage : public QWebPage
2194 ErrorPage(QWidget* parent = 0): QWebPage(parent)
2198 virtual bool supportsExtension(Extension extension) const
2200 return extension == ErrorPageExtension;
2203 virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output)
2205 ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output);
2207 errorPage->contentType = "text/html";
2208 errorPage->content = "error";
2213 void tst_QWebPage::errorPageExtension()
2215 ErrorPage* page = new ErrorPage;
2216 m_view->setPage(page);
2218 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
2220 m_view->setUrl(QUrl("data:text/html,foo"));
2221 QTRY_COMPARE(spyLoadFinished.count(), 1);
2223 page->mainFrame()->setUrl(QUrl("http://non.existent/url"));
2224 QTRY_COMPARE(spyLoadFinished.count(), 2);
2225 QCOMPARE(page->mainFrame()->toPlainText(), QString("error"));
2226 QCOMPARE(page->history()->count(), 2);
2227 QCOMPARE(page->history()->currentItem().url(), QUrl("http://non.existent/url"));
2228 QCOMPARE(page->history()->canGoBack(), true);
2229 QCOMPARE(page->history()->canGoForward(), false);
2231 page->triggerAction(QWebPage::Back);
2232 QTRY_COMPARE(page->history()->canGoBack(), false);
2233 QTRY_COMPARE(page->history()->canGoForward(), true);
2235 page->triggerAction(QWebPage::Forward);
2236 QTRY_COMPARE(page->history()->canGoBack(), true);
2237 QTRY_COMPARE(page->history()->canGoForward(), false);
2239 page->triggerAction(QWebPage::Back);
2240 QTRY_COMPARE(page->history()->canGoBack(), false);
2241 QTRY_COMPARE(page->history()->canGoForward(), true);
2242 QTRY_COMPARE(page->history()->currentItem().url(), QUrl("data:text/html,foo"));
2247 void tst_QWebPage::errorPageExtensionInIFrames()
2249 ErrorPage* page = new ErrorPage;
2250 m_view->setPage(page);
2252 m_view->page()->mainFrame()->load(QUrl(
2255 "<iframe src='data:text/html,<p/>p'></iframe>"
2256 "<iframe src='http://non.existent/url'></iframe>"));
2257 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
2258 QTRY_COMPARE(spyLoadFinished.count(), 1);
2260 QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("error"));
2265 void tst_QWebPage::errorPageExtensionInFrameset()
2267 ErrorPage* page = new ErrorPage;
2268 m_view->setPage(page);
2270 m_view->load(QUrl("qrc:///resources/index.html"));
2272 QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
2273 QTRY_COMPARE(spyLoadFinished.count(), 1);
2274 QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("error"));
2279 class FriendlyWebPage : public QWebPage
2282 friend class tst_QWebPage;
2285 void tst_QWebPage::userAgentApplicationName()
2287 const QString oldApplicationName = QCoreApplication::applicationName();
2288 FriendlyWebPage page;
2290 const QString applicationNameMarker = QString::fromUtf8("StrangeName\342\210\236");
2291 QCoreApplication::setApplicationName(applicationNameMarker);
2292 QVERIFY(page.userAgentForUrl(QUrl()).contains(applicationNameMarker));
2294 QCoreApplication::setApplicationName(oldApplicationName);
2297 void tst_QWebPage::userAgentLocaleChange()
2299 FriendlyWebPage page;
2300 m_view->setPage(&page);
2302 const QString markerString = QString::fromLatin1(" nn-NO)");
2304 if (page.userAgentForUrl(QUrl()).contains(markerString))
2305 QSKIP("marker string already present", SkipSingle);
2307 m_view->setLocale(QLocale(QString::fromLatin1("nn_NO")));
2308 QVERIFY(page.userAgentForUrl(QUrl()).contains(markerString));
2311 void tst_QWebPage::crashTests_LazyInitializationOfMainFrame()
2318 webPage.selectedText();
2322 webPage.triggerAction(QWebPage::Back, true);
2327 webPage.updatePositionDependentActions(pos);
2331 static void takeScreenshot(QWebPage* page)
2333 QWebFrame* mainFrame = page->mainFrame();
2334 page->setViewportSize(mainFrame->contentsSize());
2335 QImage image(page->viewportSize(), QImage::Format_ARGB32);
2336 QPainter painter(&image);
2337 mainFrame->render(&painter);
2341 void tst_QWebPage::screenshot_data()
2343 QTest::addColumn<QString>("html");
2344 QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>";
2345 QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>");
2346 QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode='transparent'></embed></body></html>");
2349 void tst_QWebPage::screenshot()
2351 if (!QDir(TESTS_SOURCE_DIR).exists())
2352 QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
2354 QDir::setCurrent(TESTS_SOURCE_DIR);
2356 QFETCH(QString, html);
2357 QWebPage* page = new QWebPage;
2358 page->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
2359 QWebFrame* mainFrame = page->mainFrame();
2360 mainFrame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR));
2361 ::waitForSignal(mainFrame, SIGNAL(loadFinished(bool)), 2000);
2363 // take screenshot without a view
2364 takeScreenshot(page);
2366 QWebView* view = new QWebView;
2367 view->setPage(page);
2369 // take screenshot when attached to a view
2370 takeScreenshot(page);
2375 QDir::setCurrent(QApplication::applicationDirPath());
2378 void tst_QWebPage::originatingObjectInNetworkRequests()
2380 TestNetworkManager* networkManager = new TestNetworkManager(m_page);
2381 m_page->setNetworkAccessManager(networkManager);
2382 networkManager->requests.clear();
2384 m_view->setHtml(QString("<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
2385 "<head><meta http-equiv='refresh' content='1'></head>foo \">"
2386 "<frame src=\"data:text/html,bar\"></frameset>"), QUrl());
2387 QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
2389 QCOMPARE(networkManager->requests.count(), 2);
2391 QList<QWebFrame*> childFrames = m_page->mainFrame()->childFrames();
2392 QCOMPARE(childFrames.count(), 2);
2394 for (int i = 0; i < 2; ++i)
2395 QVERIFY(qobject_cast<QWebFrame*>(networkManager->requests.at(i).originatingObject()) == childFrames.at(i));
2399 * Test fixups for https://bugs.webkit.org/show_bug.cgi?id=30914
2401 * From JS we test the following conditions.
2403 * OK + QString() => SUCCESS, empty string (but not null)
2404 * OK + "text" => SUCCESS, "text"
2405 * CANCEL + QString() => CANCEL, null string
2406 * CANCEL + "text" => CANCEL, null string
2408 class JSPromptPage : public QWebPage {
2414 bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result)
2416 if (msg == QLatin1String("test1")) {
2417 *result = QString();
2419 } else if (msg == QLatin1String("test2")) {
2420 *result = QLatin1String("text");
2422 } else if (msg == QLatin1String("test3")) {
2423 *result = QString();
2425 } else if (msg == QLatin1String("test4")) {
2426 *result = QLatin1String("text");
2430 qFatal("Unknown msg.");
2431 return QWebPage::javaScriptPrompt(frame, msg, defaultValue, result);
2435 void tst_QWebPage::testJSPrompt()
2441 res = page.mainFrame()->evaluateJavaScript(
2442 "var retval = prompt('test1');"
2443 "retval=='' && retval.length == 0;").toBool();
2447 res = page.mainFrame()->evaluateJavaScript(
2448 "var retval = prompt('test2');"
2449 "retval=='text' && retval.length == 4;").toBool();
2452 // Cancel + QString()
2453 res = page.mainFrame()->evaluateJavaScript(
2454 "var retval = prompt('test3');"
2455 "retval===null;").toBool();
2459 res = page.mainFrame()->evaluateJavaScript(
2460 "var retval = prompt('test4');"
2461 "retval===null;").toBool();
2465 class TestModalPage : public QWebPage
2469 TestModalPage(QObject* parent = 0) : QWebPage(parent) {
2471 virtual QWebPage* createWindow(WebWindowType) {
2472 QWebPage* page = new TestModalPage();
2473 connect(page, SIGNAL(windowCloseRequested()), page, SLOT(deleteLater()));
2478 void tst_QWebPage::showModalDialog()
2481 page.mainFrame()->setHtml(QString("<html></html>"));
2482 QString res = page.mainFrame()->evaluateJavaScript("window.showModalDialog('javascript:window.returnValue=dialogArguments; window.close();', 'This is a test');").toString();
2483 QCOMPARE(res, QString("This is a test"));
2486 void tst_QWebPage::testStopScheduledPageRefresh()
2488 // Without QWebPage::StopScheduledPageRefresh
2490 page1.setNetworkAccessManager(new TestNetworkManager(&page1));
2491 page1.mainFrame()->setHtml("<html><head>"
2492 "<meta http-equiv=\"refresh\"content=\"0;URL=qrc:///resources/index.html\">"
2493 "</head><body><h1>Page redirects immediately...</h1>"
2495 QVERIFY(::waitForSignal(&page1, SIGNAL(loadFinished(bool))));
2497 QCOMPARE(page1.mainFrame()->url(), QUrl(QLatin1String("qrc:///resources/index.html")));
2499 // With QWebPage::StopScheduledPageRefresh
2501 page2.setNetworkAccessManager(new TestNetworkManager(&page2));
2502 page2.mainFrame()->setHtml("<html><head>"
2503 "<meta http-equiv=\"refresh\"content=\"1;URL=qrc:///resources/index.html\">"
2504 "</head><body><h1>Page redirect test with 1 sec timeout...</h1>"
2506 page2.triggerAction(QWebPage::StopScheduledPageRefresh);
2508 QCOMPARE(page2.mainFrame()->url().toString(), QLatin1String("about:blank"));
2511 void tst_QWebPage::findText()
2513 m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>"));
2514 m_page->triggerAction(QWebPage::SelectAll);
2515 QVERIFY(!m_page->selectedText().isEmpty());
2516 m_page->findText("");
2517 QVERIFY(m_page->selectedText().isEmpty());
2518 QStringList words = (QStringList() << "foo" << "bar");
2519 foreach (QString subString, words) {
2520 m_page->findText(subString, QWebPage::FindWrapsAroundDocument);
2521 QCOMPARE(m_page->selectedText(), subString);
2522 m_page->findText("");
2523 QVERIFY(m_page->selectedText().isEmpty());
2527 struct ImageExtensionMap {
2528 const char* extension;
2529 const char* mimeType;
2532 static const ImageExtensionMap extensionMap[] = {
2533 { "bmp", "image/bmp" },
2534 { "css", "text/css" },
2535 { "gif", "image/gif" },
2536 { "html", "text/html" },
2537 { "htm", "text/html" },
2538 { "ico", "image/x-icon" },
2539 { "jpeg", "image/jpeg" },
2540 { "jpg", "image/jpeg" },
2541 { "js", "application/x-javascript" },
2542 { "mng", "video/x-mng" },
2543 { "pbm", "image/x-portable-bitmap" },
2544 { "pgm", "image/x-portable-graymap" },
2545 { "pdf", "application/pdf" },
2546 { "png", "image/png" },
2547 { "ppm", "image/x-portable-pixmap" },
2548 { "rss", "application/rss+xml" },
2549 { "svg", "image/svg+xml" },
2550 { "text", "text/plain" },
2551 { "tif", "image/tiff" },
2552 { "tiff", "image/tiff" },
2553 { "txt", "text/plain" },
2554 { "xbm", "image/x-xbitmap" },
2555 { "xml", "text/xml" },
2556 { "xpm", "image/x-xpm" },
2557 { "xsl", "text/xsl" },
2558 { "xhtml", "application/xhtml+xml" },
2559 { "wml", "text/vnd.wap.wml" },
2560 { "wmlc", "application/vnd.wap.wmlc" },
2564 static QString getMimeTypeForExtension(const QString &ext)
2566 const ImageExtensionMap *e = extensionMap;
2567 while (e->extension) {
2568 if (ext.compare(QLatin1String(e->extension), Qt::CaseInsensitive) == 0)
2569 return QLatin1String(e->mimeType);
2576 void tst_QWebPage::supportedContentType()
2578 QStringList contentTypes;
2580 // Add supported non image types...
2581 contentTypes << "text/html" << "text/xml" << "text/xsl" << "text/plain" << "text/"
2582 << "application/xml" << "application/xhtml+xml" << "application/vnd.wap.xhtml+xml"
2583 << "application/rss+xml" << "application/atom+xml" << "application/json";
2585 // Add supported image types...
2586 Q_FOREACH(const QByteArray& imageType, QImageWriter::supportedImageFormats()) {
2587 const QString mimeType = getMimeTypeForExtension(imageType);
2588 if (!mimeType.isEmpty())
2589 contentTypes << mimeType;
2592 // Get the mime types supported by webkit...
2593 const QStringList supportedContentTypes = m_page->supportedContentTypes();
2595 Q_FOREACH(const QString& mimeType, contentTypes)
2596 QVERIFY2(supportedContentTypes.contains(mimeType), QString("'%1' is not a supported content type!").arg(mimeType).toLatin1());
2598 Q_FOREACH(const QString& mimeType, contentTypes)
2599 QVERIFY2(m_page->supportsContentType(mimeType), QString("Cannot handle content types '%1'!").arg(mimeType).toLatin1());
2602 QTEST_MAIN(tst_QWebPage)
2603 #include "tst_qwebpage.moc"