2010-11-24 Kristian Amlie <kristian.amlie@nokia.com>
[WebKit.git] / WebKit / qt / tests / qwebpage / tst_qwebpage.cpp
1 /*
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
5
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.
10
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.
15
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.
20 */
21
22 #include "../util.h"
23 #include "../WebCoreSupport/DumpRenderTreeSupportQt.h"
24 #include <QDir>
25 #include <QGraphicsWidget>
26 #include <QLineEdit>
27 #include <QLocale>
28 #include <QMenu>
29 #include <QPushButton>
30 #include <QStyle>
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>
41 #include <qwebpage.h>
42 #include <qwebsecurityorigin.h>
43 #include <qwebview.h>
44 #include <qimagewriter.h>
45
46 class EventSpy : public QObject, public QList<QEvent::Type>
47 {
48     Q_OBJECT
49 public:
50     EventSpy(QObject* objectToSpy)
51     {
52         objectToSpy->installEventFilter(this);
53     }
54
55     virtual bool eventFilter(QObject* receiver, QEvent* event)
56     {
57         append(event->type());
58         return false;
59     }
60 };
61
62 class tst_QWebPage : public QObject
63 {
64     Q_OBJECT
65
66 public:
67     tst_QWebPage();
68     virtual ~tst_QWebPage();
69
70 public slots:
71     void init();
72     void cleanup();
73     void cleanupFiles();
74
75 private slots:
76     void initTestCase();
77     void cleanupTestCase();
78
79     void acceptNavigationRequest();
80     void geolocationRequestJS();
81     void loadFinished();
82     void acceptNavigationRequestWithNewWindow();
83     void userStyleSheet();
84     void modified();
85     void contextMenuCrash();
86     void database();
87     void createPluginWithPluginsEnabled();
88     void createPluginWithPluginsDisabled();
89     void destroyPlugin_data();
90     void destroyPlugin();
91     void createViewlessPlugin_data();
92     void createViewlessPlugin();
93     void multiplePageGroupsAndLocalStorage();
94     void cursorMovements();
95     void textSelection();
96     void textEditing();
97     void backActionUpdate();
98     void frameAt();
99     void requestCache();
100     void loadCachedPage();
101     void protectBindingsRuntimeObjectsFromCollector();
102     void localURLSchemes();
103     void testOptionalJSObjects();
104     void testEnablePersistentStorage();
105     void consoleOutput();
106     void inputMethods_data();
107     void inputMethods();
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();
116
117     void viewModes();
118
119     void crashTests_LazyInitializationOfMainFrame();
120
121     void screenshot_data();
122     void screenshot();
123
124     void originatingObjectInNetworkRequests();
125     void testJSPrompt();
126     void showModalDialog();
127     void testStopScheduledPageRefresh();
128     void findText();
129     void supportedContentType();
130     void infiniteLoopJS();
131     
132 private:
133     QWebView* m_view;
134     QWebPage* m_page;
135 };
136
137 tst_QWebPage::tst_QWebPage()
138 {
139 }
140
141 tst_QWebPage::~tst_QWebPage()
142 {
143 }
144
145 void tst_QWebPage::init()
146 {
147     m_view = new QWebView();
148     m_page = m_view->page();
149 }
150
151 void tst_QWebPage::cleanup()
152 {
153     delete m_view;
154 }
155
156 void tst_QWebPage::cleanupFiles()
157 {
158     QFile::remove("Databases.db");
159     QDir::current().rmdir("http_www.myexample.com_0");
160     QFile::remove("http_www.myexample.com_0.localstorage");
161 }
162
163 void tst_QWebPage::initTestCase()
164 {
165     cleanupFiles(); // In case there are old files from previous runs
166 }
167
168 void tst_QWebPage::cleanupTestCase()
169 {
170     cleanupFiles(); // Be nice
171 }
172
173 class NavigationRequestOverride : public QWebPage
174 {
175 public:
176     NavigationRequestOverride(QWebView* parent, bool initialValue) : QWebPage(parent), m_acceptNavigationRequest(initialValue) {}
177
178     bool m_acceptNavigationRequest;
179 protected:
180     virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, QWebPage::NavigationType type) {
181         Q_UNUSED(frame);
182         Q_UNUSED(request);
183         Q_UNUSED(type);
184
185         return m_acceptNavigationRequest;
186     }
187 };
188
189 void tst_QWebPage::acceptNavigationRequest()
190 {
191     QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
192
193     NavigationRequestOverride* newPage = new NavigationRequestOverride(m_view, false);
194     m_view->setPage(newPage);
195
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);
199
200     m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
201
202     newPage->m_acceptNavigationRequest = true;
203     m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
204     QTRY_COMPARE(loadSpy.count(), 2);
205
206     QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("foo?"));
207
208     // Restore default page
209     m_view->setPage(0);
210 }
211
212 class JSTestPage : public QWebPage
213 {
214 Q_OBJECT
215 public:
216     JSTestPage(QObject* parent = 0)
217     : QWebPage(parent) {}
218
219 public slots:
220     bool shouldInterruptJavaScript() {
221         return true;
222     }
223     void requestPermission(QWebFrame* frame, QWebPage::Feature feature)
224     {
225         if (m_allowGeolocation)
226             setFeaturePermission(frame, feature, PermissionGrantedByUser);
227         else 
228             setFeaturePermission(frame, feature, PermissionDeniedByUser);
229     }
230
231 public:
232     void setGeolocationPermission(bool allow) 
233     {
234         m_allowGeolocation = allow;
235     }
236
237 private: 
238     bool m_allowGeolocation;
239 };
240
241 void tst_QWebPage::infiniteLoopJS()
242 {
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++;}");
247     delete newPage;
248 }
249
250 void tst_QWebPage::geolocationRequestJS()
251 {
252     /*
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.
255     */
256     QSKIP("Test disabled as long as geolocation is disabled from the build.", SkipSingle);
257
258     JSTestPage* newPage = new JSTestPage(m_view);
259     connect(newPage, SIGNAL(requestPermissionFromUser(QWebFrame*, QWebPage::Feature)), 
260             newPage, SLOT(requestPermission(QWebFrame*, QWebPage::Feature)));
261
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)");
266     QTest::qWait(2000);
267     QVariant empty = m_view->page()->mainFrame()->evaluateJavaScript("errorCode");
268
269     QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 0);
270
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");
274
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);
278     delete newPage;
279 }
280
281 void tst_QWebPage::loadFinished()
282 {
283     qRegisterMetaType<QWebFrame*>("QWebFrame*");
284     qRegisterMetaType<QNetworkRequest*>("QNetworkRequest*");
285     QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted()));
286     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
287
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);
292
293     QTRY_VERIFY(spyLoadStarted.count() > 1);
294     QTRY_VERIFY(spyLoadFinished.count() > 1);
295
296     spyLoadFinished.clear();
297
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);
302 }
303
304 class ConsolePage : public QWebPage
305 {
306 public:
307     ConsolePage(QObject* parent = 0) : QWebPage(parent) {}
308
309     virtual void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID)
310     {
311         messages.append(message);
312         lineNumbers.append(lineNumber);
313         sourceIDs.append(sourceID);
314     }
315
316     QStringList messages;
317     QList<int> lineNumbers;
318     QStringList sourceIDs;
319 };
320
321 void tst_QWebPage::consoleOutput()
322 {
323     ConsolePage page;
324     page.mainFrame()->evaluateJavaScript("this is not valid JavaScript");
325     QCOMPARE(page.messages.count(), 1);
326     QCOMPARE(page.lineNumbers.at(0), 1);
327 }
328
329 class TestPage : public QWebPage
330 {
331 public:
332     TestPage(QObject* parent = 0) : QWebPage(parent) {}
333
334     struct Navigation {
335         QPointer<QWebFrame> frame;
336         QNetworkRequest request;
337         NavigationType type;
338     };
339
340     QList<Navigation> navigations;
341     QList<QWebPage*> createdWindows;
342
343     virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, NavigationType type) {
344         Navigation n;
345         n.frame = frame;
346         n.request = request;
347         n.type = type;
348         navigations.append(n);
349         return true;
350     }
351
352     virtual QWebPage* createWindow(WebWindowType) {
353         QWebPage* page = new TestPage(this);
354         createdWindows.append(page);
355         return page;
356     }
357 };
358
359 void tst_QWebPage::acceptNavigationRequestWithNewWindow()
360 {
361     TestPage* page = new TestPage(m_view);
362     page->settings()->setAttribute(QWebSettings::LinksIncludedInFocusChain, true);
363     m_page = page;
364     m_view->setPage(m_page);
365
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))));
368
369     QFocusEvent fe(QEvent::FocusIn);
370     m_page->event(&fe);
371
372     QVERIFY(m_page->focusNextPrevChild(/*next*/ true));
373
374     QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
375     m_page->event(&keyEnter);
376
377     QCOMPARE(page->navigations.count(), 2);
378
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);
383
384     QCOMPARE(page->createdWindows.count(), 1);
385 }
386
387 class TestNetworkManager : public QNetworkAccessManager
388 {
389 public:
390     TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {}
391
392     QList<QUrl> requestedUrls;
393     QList<QNetworkRequest> requests;
394
395 protected:
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);
400     }
401 };
402
403 void tst_QWebPage::userStyleSheet()
404 {
405     TestNetworkManager* networkManager = new TestNetworkManager(m_page);
406     m_page->setNetworkAccessManager(networkManager);
407     networkManager->requestedUrls.clear();
408
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))));
413
414     QVERIFY(networkManager->requestedUrls.count() >= 1);
415     QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png"));
416 }
417
418 void tst_QWebPage::viewModes()
419 {
420     m_view->setHtml("<body></body>");
421     m_page->setProperty("_q_viewMode", "minimized");
422
423     QVariant empty = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode)\")");
424     QVERIFY(empty.type() == QVariant::Bool && empty.toBool());
425
426     QVariant minimized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: minimized)\")");
427     QVERIFY(minimized.type() == QVariant::Bool && minimized.toBool());
428
429     QVariant maximized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: maximized)\")");
430     QVERIFY(maximized.type() == QVariant::Bool && !maximized.toBool());
431 }
432
433 void tst_QWebPage::modified()
434 {
435     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>blub"));
436     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
437
438     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah"));
439     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
440
441     QVERIFY(!m_page->isModified());
442
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');");
446
447     QVERIFY(m_page->isModified());
448
449     m_page->mainFrame()->evaluateJavaScript("document.execCommand('Undo', true);");
450
451     QVERIFY(!m_page->isModified());
452
453     m_page->mainFrame()->evaluateJavaScript("document.execCommand('Redo', true);");
454
455     QVERIFY(m_page->isModified());
456
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());
462
463     m_page->history()->back();
464     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
465
466     QVERIFY(!m_page->history()->canGoBack());
467     QVERIFY(m_page->history()->canGoForward());
468
469     QVERIFY(!m_page->isModified());
470
471     QVERIFY(m_page->history()->currentItemIndex() == 0);
472
473     m_page->history()->setMaximumItemCount(3);
474     QVERIFY(m_page->history()->maximumItemCount() == 3);
475
476     QVariant variant("string test");
477     m_page->history()->currentItem().setUserData(variant);
478     QVERIFY(m_page->history()->currentItem().userData().toString() == "string test");
479
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*))));
487 }
488
489 void tst_QWebPage::contextMenuCrash()
490 {
491     QWebView view;
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);
497         if (contextMenu)
498             break;
499     }
500     QVERIFY(contextMenu);
501     delete contextMenu;
502 }
503
504 void tst_QWebPage::database()
505 {
506     QString path = QDir::currentPath();
507     m_page->settings()->setOfflineStoragePath(path);
508     QVERIFY(m_page->settings()->offlineStoragePath() == path);
509
510     QWebSettings::setOfflineStorageDefaultQuota(1024 * 1024);
511     QVERIFY(QWebSettings::offlineStorageDefaultQuota() == 1024 * 1024);
512
513     m_page->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
514     m_page->settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true);
515
516     QString dbFileName = path + "Databases.db";
517
518     if (QFile::exists(dbFileName))
519         QFile::remove(dbFileName);
520
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);
527
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"));
530
531     QVariant s1 = m_page->mainFrame()->evaluateJavaScript("localStorage.test");
532     QCOMPARE(s1.toString(), QString("This is a test for local storage"));
533
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"));
538
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) { });");
541     QTest::qWait(200);
542
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));
551     }
552     QVERIFY(!origin.databases().size());
553     // Remove removed test :-)
554     QWebDatabase::removeAllDatabases();
555     QVERIFY(!origin.databases().size());
556 }
557
558 class PluginPage : public QWebPage
559 {
560 public:
561     PluginPage(QObject *parent = 0)
562         : QWebPage(parent) {}
563
564     struct CallInfo
565     {
566         CallInfo(const QString &c, const QUrl &u,
567                  const QStringList &pn, const QStringList &pv,
568                  QObject *r)
569             : classid(c), url(u), paramNames(pn),
570               paramValues(pv), returnValue(r)
571             {}
572         QString classid;
573         QUrl url;
574         QStringList paramNames;
575         QStringList paramValues;
576         QObject *returnValue;
577     };
578
579     QList<CallInfo> calls;
580
581 protected:
582     virtual QObject *createPlugin(const QString &classid, const QUrl &url,
583                                   const QStringList &paramNames,
584                                   const QStringList &paramValues)
585     {
586         QObject *result = 0;
587         if (classid == "pushbutton")
588             result = new QPushButton();
589         else if (classid == "lineedit")
590             result = new QLineEdit();
591         if (result)
592             result->setObjectName(classid);
593         calls.append(CallInfo(classid, url, paramNames, paramValues, result));
594         return result;
595     }
596 };
597
598 static void createPlugin(QWebView *view)
599 {
600     QSignalSpy loadSpy(view, SIGNAL(loadFinished(bool)));
601
602     PluginPage* newPage = new PluginPage(view);
603     view->setPage(newPage);
604
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);
609
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);
613     {
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"));
627     }
628     // test JS bindings
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}"));
641
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);
648     {
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"));
662     }
663     {
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"));
677     }
678 }
679
680 void tst_QWebPage::createPluginWithPluginsEnabled()
681 {
682     m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
683     createPlugin(m_view);
684 }
685
686 void tst_QWebPage::createPluginWithPluginsDisabled()
687 {
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);
693 }
694
695 // Standard base class for template PluginTracerPage. In tests it is used as interface.
696 class PluginCounterPage : public QWebPage {
697 public:
698     int m_count;
699     QPointer<QObject> m_widget;
700     QObject* m_pluginParent;
701     PluginCounterPage(QObject* parent = 0)
702         : QWebPage(parent)
703         , m_count(0)
704         , m_widget(0)
705         , m_pluginParent(0)
706     {
707        settings()->setAttribute(QWebSettings::PluginsEnabled, true);
708     }
709     ~PluginCounterPage()
710     {
711         if (m_pluginParent)
712             m_pluginParent->deleteLater();
713     }
714 };
715
716 template<class T>
717 class PluginTracerPage : public PluginCounterPage {
718 public:
719     PluginTracerPage(QObject* parent = 0)
720         : PluginCounterPage(parent)
721     {
722         // this is a dummy parent object for the created plugin
723         m_pluginParent = new T;
724     }
725     virtual QObject* createPlugin(const QString&, const QUrl&, const QStringList&, const QStringList&)
726     {
727         m_count++;
728         m_widget = new T;
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));
734         return m_widget;
735     }
736 };
737
738 class PluginFactory {
739 public:
740     enum FactoredType {QWidgetType, QGraphicsWidgetType};
741     static PluginCounterPage* create(FactoredType type, QObject* parent = 0)
742     {
743         PluginCounterPage* result = 0;
744         switch (type) {
745         case QWidgetType:
746             result = new PluginTracerPage<QWidget>(parent);
747             break;
748         case QGraphicsWidgetType:
749             result = new PluginTracerPage<QGraphicsWidget>(parent);
750             break;
751         default: {/*Oops*/};
752         }
753         return result;
754     }
755
756     static void prepareTestData()
757     {
758         QTest::addColumn<int>("type");
759         QTest::newRow("QWidget") << (int)PluginFactory::QWidgetType;
760         QTest::newRow("QGraphicsWidget") << (int)PluginFactory::QGraphicsWidgetType;
761     }
762 };
763
764 void tst_QWebPage::destroyPlugin_data()
765 {
766     PluginFactory::prepareTestData();
767 }
768
769 void tst_QWebPage::destroyPlugin()
770 {
771     QFETCH(int, type);
772     PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type, m_view);
773     m_view->setPage(page);
774
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);
780
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);
785 }
786
787 void tst_QWebPage::createViewlessPlugin_data()
788 {
789     PluginFactory::prepareTestData();
790 }
791
792 void tst_QWebPage::createViewlessPlugin()
793 {
794     QFETCH(int, type);
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);
802     delete page;
803
804 }
805
806 void tst_QWebPage::multiplePageGroupsAndLocalStorage()
807 {
808     QDir dir(QDir::currentPath());
809     dir.mkdir("path1");
810     dir.mkdir("path2");
811
812     QWebView view1;
813     QWebView view2;
814
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"));
823
824
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"));
827
828     view1.page()->mainFrame()->evaluateJavaScript("localStorage.test='value1';");
829     view2.page()->mainFrame()->evaluateJavaScript("localStorage.test='value2';");
830
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"));
833
834     QVariant s1 = view1.page()->mainFrame()->evaluateJavaScript("localStorage.test");
835     QCOMPARE(s1.toString(), QString("value1"));
836
837     QVariant s2 = view2.page()->mainFrame()->evaluateJavaScript("localStorage.test");
838     QCOMPARE(s2.toString(), QString("value2"));
839
840     QTest::qWait(1000);
841
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"));
846 }
847
848 class CursorTrackedPage : public QWebPage
849 {
850 public:
851
852     CursorTrackedPage(QWidget *parent = 0): QWebPage(parent) {
853         setViewportSize(QSize(1024, 768)); // big space
854     }
855
856     QString selectedText() {
857         return mainFrame()->evaluateJavaScript("window.getSelection().toString()").toString();
858     }
859
860     int selectionStartOffset() {
861         return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).startOffset").toInt();
862     }
863
864     int selectionEndOffset() {
865         return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).endOffset").toInt();
866     }
867
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();
871     }
872 };
873
874 void tst_QWebPage::cursorMovements()
875 {
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);
879
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"));
887
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);
901
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);
915
916     // make it editable before navigating the cursor
917     page->setContentEditable(true);
918
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);
932
933     // cursor will be before the word "jump"
934     page->triggerAction(QWebPage::MoveToNextChar);
935     QVERIFY(page->isSelectionCollapsed());
936     QCOMPARE(page->selectionStartOffset(), 0);
937
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);
942
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);
947
948     // cursor will be after the word "jump"
949     page->triggerAction(QWebPage::MoveToNextWord);
950     QVERIFY(page->isSelectionCollapsed());
951     QCOMPARE(page->selectionStartOffset(), 5);
952
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);
959
960     // cursor will be between 'z' and 'y' in "lazy"
961     page->triggerAction(QWebPage::MoveToPreviousChar);
962     QVERIFY(page->isSelectionCollapsed());
963     QCOMPARE(page->selectionStartOffset(), 18);
964
965     // cursor will be between 'a' and 'z' in "lazy"
966     page->triggerAction(QWebPage::MoveToPreviousChar);
967     QVERIFY(page->isSelectionCollapsed());
968     QCOMPARE(page->selectionStartOffset(), 17);
969
970     // cursor will be before the word "lazy"
971     page->triggerAction(QWebPage::MoveToPreviousWord);
972     QVERIFY(page->isSelectionCollapsed());
973     QCOMPARE(page->selectionStartOffset(), 15);
974
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);
984
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);
996
997     // cursor will be before the word "jumps"
998     page->triggerAction(QWebPage::MoveToStartOfLine);
999     QVERIFY(page->isSelectionCollapsed());
1000     QCOMPARE(page->selectionStartOffset(), 0);
1001
1002     // cursor will be after the word "dog"
1003     page->triggerAction(QWebPage::MoveToEndOfLine);
1004     QVERIFY(page->isSelectionCollapsed());
1005     QCOMPARE(page->selectionStartOffset(), 23);
1006
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);
1017
1018     // cursor will be after the word "fox"
1019     page->triggerAction(QWebPage::MoveToEndOfLine);
1020     QVERIFY(page->isSelectionCollapsed());
1021     QCOMPARE(page->selectionStartOffset(), 19);
1022
1023     // cursor will be before the word "The"
1024     page->triggerAction(QWebPage::MoveToStartOfDocument);
1025     QVERIFY(page->isSelectionCollapsed());
1026     QCOMPARE(page->selectionStartOffset(), 0);
1027
1028     // cursor will be after the word "you!"
1029     page->triggerAction(QWebPage::MoveToEndOfDocument);
1030     QVERIFY(page->isSelectionCollapsed());
1031     QCOMPARE(page->selectionStartOffset(), 12);
1032
1033     // cursor will be before the word "be"
1034     page->triggerAction(QWebPage::MoveToStartOfBlock);
1035     QVERIFY(page->isSelectionCollapsed());
1036     QCOMPARE(page->selectionStartOffset(), 0);
1037
1038     // cursor will be after the word "you!"
1039     page->triggerAction(QWebPage::MoveToEndOfBlock);
1040     QVERIFY(page->isSelectionCollapsed());
1041     QCOMPARE(page->selectionStartOffset(), 12);
1042
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);
1052
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);
1062
1063     delete page;
1064 }
1065
1066 void tst_QWebPage::textSelection()
1067 {
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);
1073
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);
1088
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);
1103
1104     // ..but SelectAll is awalys enabled
1105     QCOMPARE(page->action(QWebPage::SelectAll)->isEnabled(), true);
1106
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"));
1114
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);
1128
1129     // make it editable before navigating the cursor
1130     page->setContentEditable(true);
1131
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);
1136
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);
1150
1151     delete page;
1152 }
1153
1154 void tst_QWebPage::textEditing()
1155 {
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);
1161
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);
1189
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);
1216
1217     // Select everything
1218     page->triggerAction(QWebPage::SelectAll);
1219
1220     // make sure it is enabled since there is a selection
1221     QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), true);
1222
1223     // make it editable before navigating the cursor
1224     page->setContentEditable(true);
1225
1226     // clear the selection
1227     page->triggerAction(QWebPage::MoveToStartOfDocument);
1228     QVERIFY(page->isSelectionCollapsed());
1229     QCOMPARE(page->selectionStartOffset(), 0);
1230
1231     // make sure it is disabled since there isn't a selection
1232     QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), false);
1233
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);
1258     
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);
1262     
1263     // make sure everything is selected
1264     page->triggerAction(QWebPage::SelectAll);
1265     
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);
1269
1270     delete page;
1271 }
1272
1273 void tst_QWebPage::requestCache()
1274 {
1275     TestPage page;
1276     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
1277
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);
1281
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);
1285
1286     page.triggerAction(QWebPage::Stop);
1287     QVERIFY(page.history()->canGoBack());
1288     page.triggerAction(QWebPage::Back);
1289
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);
1298 }
1299
1300 void tst_QWebPage::loadCachedPage()
1301 {
1302     TestPage page;
1303     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
1304     page.settings()->setMaximumPagesInCache(3);
1305
1306     page.mainFrame()->load(QUrl("data:text/html,This is first page"));
1307
1308     QTRY_COMPARE(loadSpy.count(), 1);
1309     QTRY_COMPARE(page.navigations.count(), 1);
1310
1311     QUrl firstPageUrl = page.mainFrame()->url();
1312     page.mainFrame()->load(QUrl("data:text/html,This is second page"));
1313
1314     QTRY_COMPARE(loadSpy.count(), 2);
1315     QTRY_COMPARE(page.navigations.count(), 2);
1316
1317     page.triggerAction(QWebPage::Stop);
1318     QVERIFY(page.history()->canGoBack());
1319
1320     QSignalSpy urlSpy(page.mainFrame(), SIGNAL(urlChanged(QUrl)));
1321     QVERIFY(urlSpy.isValid());
1322
1323     page.triggerAction(QWebPage::Back);
1324     ::waitForSignal(page.mainFrame(), SIGNAL(urlChanged(QUrl)));
1325     QCOMPARE(urlSpy.size(), 1);
1326
1327     QList<QVariant> arguments1 = urlSpy.takeFirst();
1328     QCOMPARE(arguments1.at(0).toUrl(), firstPageUrl);
1329
1330 }
1331 void tst_QWebPage::backActionUpdate()
1332 {
1333     QWebView view;
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);
1344
1345     QVERIFY(action->isEnabled());
1346 }
1347
1348 void frameAtHelper(QWebPage* webPage, QWebFrame* webFrame, QPoint framePosition)
1349 {
1350     if (!webFrame)
1351         return;
1352
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);
1358
1359         QRect frameRect(children.at(i)->pos() + framePosition, children.at(i)->geometry().size());
1360         QVERIFY(children.at(i) == webPage->frameAt(frameRect.topLeft()));
1361     }
1362 }
1363
1364 void tst_QWebPage::frameAt()
1365 {
1366     QWebView webView;
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());
1373 }
1374
1375 void tst_QWebPage::inputMethods_data()
1376 {
1377     QTest::addColumn<QString>("viewType");
1378     QTest::newRow("QWebView") << "QWebView";
1379     QTest::newRow("QGraphicsWebView") << "QGraphicsWebView";
1380 }
1381
1382 static Qt::InputMethodHints inputMethodHints(QObject* object)
1383 {
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();
1389 }
1390
1391 static bool inputMethodEnabled(QObject* object)
1392 {
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);
1397     return false;
1398 }
1399
1400 void tst_QWebPage::inputMethods()
1401 {
1402     QFETCH(QString, viewType);
1403     QWebPage* page = new QWebPage;
1404     QObject* view = 0;
1405     QObject* container = 0;
1406     if (viewType == "QWebView") {
1407         QWebView* wv = new QWebView;
1408         wv->setPage(page);
1409         view = wv;
1410         container = view;
1411     } else if (viewType == "QGraphicsWebView") {
1412         QGraphicsWebView* wv = new QGraphicsWebView;
1413         wv->setPage(page);
1414         view = wv;
1415
1416         QGraphicsView* gv = new QGraphicsView;
1417         QGraphicsScene* scene = new QGraphicsScene(gv);
1418         gv->setScene(scene);
1419         scene->addItem(wv);
1420         wv->setGeometry(QRect(0, 0, 500, 500));
1421
1422         container = gv;
1423     } else
1424         QVERIFY2(false, "Unknown view type");
1425
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'/>" \
1430                                             "</body></html>");
1431     page->mainFrame()->setFocus();
1432
1433     EventSpy viewEventSpy(container);
1434
1435     QWebElementCollection inputs = page->mainFrame()->documentElement().findAll("input");
1436
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);
1441
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
1444     // a mouse click.
1445     int  inputPanel = 0;
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);
1452     }
1453
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.
1459     if (inputPanel)
1460         QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1461     else
1462         QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1463     viewEventSpy.clear();
1464
1465     page->event(&evpres);
1466     page->event(&evrel);
1467     QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1468
1469     //ImMicroFocus
1470     QVariant variant = page->inputMethodQuery(Qt::ImMicroFocus);
1471     QRect focusRect = variant.toRect();
1472     QVERIFY(inputs.at(0).geometry().contains(variant.toRect().topLeft()));
1473
1474     //ImFont
1475     variant = page->inputMethodQuery(Qt::ImFont);
1476     QFont font = variant.value<QFont>();
1477     QCOMPARE(page->settings()->fontFamily(QWebSettings::SerifFont), font.family());
1478
1479     QList<QInputMethodEvent::Attribute> inputAttributes;
1480
1481     //Insert text.
1482     {
1483         QInputMethodEvent eventText("QtWebKit", inputAttributes);
1484         QSignalSpy signalSpy(page, SIGNAL(microFocusChanged()));
1485         page->event(&eventText);
1486         QCOMPARE(signalSpy.count(), 0);
1487     }
1488
1489     {
1490         QInputMethodEvent eventText("", inputAttributes);
1491         eventText.setCommitString(QString("QtWebKit"), 0, 0);
1492         page->event(&eventText);
1493     }
1494
1495     //ImMaximumTextLength
1496     variant = page->inputMethodQuery(Qt::ImMaximumTextLength);
1497     QCOMPARE(20, variant.toInt());
1498
1499     //Set selection
1500     inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant());
1501     QInputMethodEvent eventSelection("",inputAttributes);
1502     page->event(&eventSelection);
1503
1504     //ImAnchorPosition
1505     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1506     int anchorPosition =  variant.toInt();
1507     QCOMPARE(anchorPosition, 3);
1508
1509     //ImCursorPosition
1510     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1511     int cursorPosition =  variant.toInt();
1512     QCOMPARE(cursorPosition, 5);
1513
1514     //ImCurrentSelection
1515     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1516     QString selectionValue = variant.value<QString>();
1517     QCOMPARE(selectionValue, QString("eb"));
1518
1519     //Set selection with negative length
1520     inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 6, -5, QVariant());
1521     QInputMethodEvent eventSelection3("",inputAttributes);
1522     page->event(&eventSelection3);
1523
1524     //ImAnchorPosition
1525     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1526     anchorPosition =  variant.toInt();
1527     QCOMPARE(anchorPosition, 1);
1528
1529     //ImCursorPosition
1530     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1531     cursorPosition =  variant.toInt();
1532     QCOMPARE(cursorPosition, 6);
1533
1534     //ImCurrentSelection
1535     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1536     selectionValue = variant.value<QString>();
1537     QCOMPARE(selectionValue, QString("tWebK"));
1538
1539     //ImSurroundingText
1540     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1541     QString value = variant.value<QString>();
1542     QCOMPARE(value, QString("QtWebKit"));
1543
1544     {
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);
1551     }
1552
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"));
1557
1558     // Cancel current composition first
1559     inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant());
1560     QInputMethodEvent eventSelection4("", inputAttributes);
1561     page->event(&eventSelection4);
1562
1563     // START - Tests for Selection when the Editor is NOT in Composition mode
1564
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);
1570
1571     {
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"));
1583     }
1584
1585     //Move to the start of the line
1586     page->triggerAction(QWebPage::MoveToStartOfLine);
1587
1588     QKeyEvent keyRightEventPress(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier);
1589     QKeyEvent keyRightEventRelease(QEvent::KeyRelease, Qt::Key_Right, Qt::NoModifier);
1590
1591     //Move 2 characters RIGHT
1592     for (int j = 0; j < 2; ++j) {
1593         page->event(&keyRightEventPress);
1594         page->event(&keyRightEventRelease);
1595     }
1596
1597     //Select to the end of the line
1598     page->triggerAction(QWebPage::SelectEndOfLine);
1599
1600     //ImAnchorPosition QtWebKit
1601     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1602     anchorPosition =  variant.toInt();
1603     QCOMPARE(anchorPosition, 2);
1604
1605     //ImCursorPosition
1606     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1607     cursorPosition =  variant.toInt();
1608     QCOMPARE(cursorPosition, 8);
1609
1610     //ImCurrentSelection
1611     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1612     selectionValue = variant.value<QString>();
1613     QCOMPARE(selectionValue, QString("WebKit"));
1614
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);
1619
1620     //ImAnchorPosition
1621     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1622     anchorPosition =  variant.toInt();
1623     QCOMPARE(anchorPosition, 8);
1624
1625     //ImCursorPosition
1626     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1627     cursorPosition =  variant.toInt();
1628     QCOMPARE(cursorPosition, 8);
1629
1630     //ImCurrentSelection
1631     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1632     selectionValue = variant.value<QString>();
1633     QCOMPARE(selectionValue, QString(""));
1634
1635     QKeyEvent keyLeftEventPress(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier);
1636     QKeyEvent keyLeftEventRelease(QEvent::KeyRelease, Qt::Key_Left, Qt::NoModifier);
1637
1638     //Move 2 characters LEFT
1639     for (int i = 0; i < 2; ++i) {
1640         page->event(&keyLeftEventPress);
1641         page->event(&keyLeftEventRelease);
1642     }
1643
1644     //Select to the start of the line
1645     page->triggerAction(QWebPage::SelectStartOfLine);
1646
1647     //ImAnchorPosition
1648     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1649     anchorPosition =  variant.toInt();
1650     QCOMPARE(anchorPosition, 6);
1651
1652     //ImCursorPosition
1653     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1654     cursorPosition =  variant.toInt();
1655     QCOMPARE(cursorPosition, 0);
1656
1657     //ImCurrentSelection
1658     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1659     selectionValue = variant.value<QString>();
1660     QCOMPARE(selectionValue, QString("QtWebK"));
1661
1662     //END - Tests for Selection when the Editor is not in Composition mode
1663
1664     //ImhHiddenText
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);
1669
1670     QVERIFY(inputMethodEnabled(view));
1671     QVERIFY(inputMethodHints(view) & Qt::ImhHiddenText);
1672
1673     page->event(&evpres);
1674     page->event(&evrel);
1675     QVERIFY(!(inputMethodHints(view) & Qt::ImhHiddenText));
1676
1677     page->mainFrame()->setHtml("<html><body><p>nothing to input here");
1678     viewEventSpy.clear();
1679
1680     QWebElement para = page->mainFrame()->findFirstElement("p");
1681     {
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);
1686     }
1687
1688     QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1689
1690     //START - Test for sending empty QInputMethodEvent
1691     page->mainFrame()->setHtml("<html><body>" \
1692                                             "<input type='text' id='input3' value='QtWebKit2'/>" \
1693                                             "</body></html>");
1694     page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input3'); inputEle.focus(); inputEle.select();");
1695
1696     //Send empty QInputMethodEvent
1697     QInputMethodEvent emptyEvent;
1698     page->event(&emptyEvent);
1699
1700     QString inputValue = page->mainFrame()->evaluateJavaScript("document.getElementById('input3').value").toString();
1701     QCOMPARE(inputValue, QString("QtWebKit2"));
1702     //END - Test for sending empty QInputMethodEvent
1703
1704     page->mainFrame()->setHtml("<html><body>" \
1705                                             "<input type='text' id='input4' value='QtWebKit inputMethod'/>" \
1706                                             "</body></html>");
1707     page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input4'); inputEle.focus(); inputEle.select();");
1708
1709     // Clear the selection, also cancel the ongoing composition if there is one.
1710     {
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);
1716     }
1717
1718     // ImCurrentSelection
1719     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1720     selectionValue = variant.value<QString>();
1721     QCOMPARE(selectionValue, QString(""));
1722
1723     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1724     QString surroundingValue = variant.value<QString>();
1725     QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
1726
1727     // ImAnchorPosition
1728     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1729     anchorPosition =  variant.toInt();
1730     QCOMPARE(anchorPosition, 0);
1731
1732     // ImCursorPosition
1733     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1734     cursorPosition =  variant.toInt();
1735     QCOMPARE(cursorPosition, 0);
1736
1737     // 1. Insert a character to the begining of the line.
1738     // Send temporary text, which makes the editor has composition 'm'.
1739     {
1740         QList<QInputMethodEvent::Attribute> attributes;
1741         QInputMethodEvent event("m", attributes);
1742         page->event(&event);
1743     }
1744
1745     // ImCurrentSelection
1746     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1747     selectionValue = variant.value<QString>();
1748     QCOMPARE(selectionValue, QString(""));
1749
1750     // ImSurroundingText
1751     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1752     surroundingValue = variant.value<QString>();
1753     QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
1754
1755     // ImCursorPosition
1756     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1757     cursorPosition =  variant.toInt();
1758     QCOMPARE(cursorPosition, 0);
1759
1760     // ImAnchorPosition
1761     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1762     anchorPosition =  variant.toInt();
1763     QCOMPARE(anchorPosition, 0);
1764
1765     // Send temporary text, which makes the editor has composition 'n'.
1766     {
1767         QList<QInputMethodEvent::Attribute> attributes;
1768         QInputMethodEvent event("n", attributes);
1769         page->event(&event);
1770     }
1771
1772     // ImCurrentSelection
1773     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1774     selectionValue = variant.value<QString>();
1775     QCOMPARE(selectionValue, QString(""));
1776
1777     // ImSurroundingText
1778     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1779     surroundingValue = variant.value<QString>();
1780     QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
1781
1782     // ImCursorPosition
1783     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1784     cursorPosition =  variant.toInt();
1785     QCOMPARE(cursorPosition, 0);
1786
1787     // ImAnchorPosition
1788     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1789     anchorPosition =  variant.toInt();
1790     QCOMPARE(anchorPosition, 0);
1791
1792     // Send commit text, which makes the editor conforms composition.
1793     {
1794         QList<QInputMethodEvent::Attribute> attributes;
1795         QInputMethodEvent event("", attributes);
1796         event.setCommitString("o");
1797         page->event(&event);
1798     }
1799
1800     // ImCurrentSelection
1801     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1802     selectionValue = variant.value<QString>();
1803     QCOMPARE(selectionValue, QString(""));
1804
1805     // ImSurroundingText
1806     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1807     surroundingValue = variant.value<QString>();
1808     QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod"));
1809
1810     // ImCursorPosition
1811     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1812     cursorPosition =  variant.toInt();
1813     QCOMPARE(cursorPosition, 1);
1814
1815     // ImAnchorPosition
1816     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1817     anchorPosition =  variant.toInt();
1818     QCOMPARE(anchorPosition, 1);
1819
1820     // 2. insert a character to the middle of the line.
1821     // Send temporary text, which makes the editor has composition 'd'.
1822     {
1823         QList<QInputMethodEvent::Attribute> attributes;
1824         QInputMethodEvent event("d", attributes);
1825         page->event(&event);
1826     }
1827
1828     // ImCurrentSelection
1829     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1830     selectionValue = variant.value<QString>();
1831     QCOMPARE(selectionValue, QString(""));
1832
1833     // ImSurroundingText
1834     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1835     surroundingValue = variant.value<QString>();
1836     QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod"));
1837
1838     // ImCursorPosition
1839     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1840     cursorPosition =  variant.toInt();
1841     QCOMPARE(cursorPosition, 1);
1842
1843     // ImAnchorPosition
1844     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1845     anchorPosition =  variant.toInt();
1846     QCOMPARE(anchorPosition, 1);
1847
1848     // Send commit text, which makes the editor conforms composition.
1849     {
1850         QList<QInputMethodEvent::Attribute> attributes;
1851         QInputMethodEvent event("", attributes);
1852         event.setCommitString("e");
1853         page->event(&event);
1854     }
1855
1856     // ImCurrentSelection
1857     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1858     selectionValue = variant.value<QString>();
1859     QCOMPARE(selectionValue, QString(""));
1860
1861     // ImSurroundingText
1862     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1863     surroundingValue = variant.value<QString>();
1864     QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod"));
1865
1866     // ImCursorPosition
1867     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1868     cursorPosition =  variant.toInt();
1869     QCOMPARE(cursorPosition, 2);
1870
1871     // ImAnchorPosition
1872     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1873     anchorPosition =  variant.toInt();
1874     QCOMPARE(anchorPosition, 2);
1875
1876     // 3. Insert a character to the end of the line.
1877     page->triggerAction(QWebPage::MoveToEndOfLine);
1878     
1879     // Send temporary text, which makes the editor has composition 't'.
1880     {
1881         QList<QInputMethodEvent::Attribute> attributes;
1882         QInputMethodEvent event("t", attributes);
1883         page->event(&event);
1884     }
1885
1886     // ImCurrentSelection
1887     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1888     selectionValue = variant.value<QString>();
1889     QCOMPARE(selectionValue, QString(""));
1890
1891     // ImSurroundingText
1892     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1893     surroundingValue = variant.value<QString>();
1894     QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod"));
1895
1896     // ImCursorPosition
1897     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1898     cursorPosition =  variant.toInt();
1899     QCOMPARE(cursorPosition, 22);
1900
1901     // ImAnchorPosition
1902     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1903     anchorPosition =  variant.toInt();
1904     QCOMPARE(anchorPosition, 22);
1905
1906     // Send commit text, which makes the editor conforms composition.
1907     {
1908         QList<QInputMethodEvent::Attribute> attributes;
1909         QInputMethodEvent event("", attributes);
1910         event.setCommitString("t");
1911         page->event(&event);
1912     }
1913
1914     // ImCurrentSelection
1915     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1916     selectionValue = variant.value<QString>();
1917     QCOMPARE(selectionValue, QString(""));
1918
1919     // ImSurroundingText
1920     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1921     surroundingValue = variant.value<QString>();
1922     QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt"));
1923
1924     // ImCursorPosition
1925     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1926     cursorPosition =  variant.toInt();
1927     QCOMPARE(cursorPosition, 23);
1928
1929     // ImAnchorPosition
1930     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1931     anchorPosition =  variant.toInt();
1932     QCOMPARE(anchorPosition, 23);
1933
1934     // 4. Replace the selection.
1935     page->triggerAction(QWebPage::SelectPreviousWord);
1936
1937     // ImCurrentSelection
1938     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1939     selectionValue = variant.value<QString>();
1940     QCOMPARE(selectionValue, QString("inputMethodt"));
1941
1942     // ImSurroundingText
1943     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1944     surroundingValue = variant.value<QString>();
1945     QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt"));
1946
1947     // ImCursorPosition
1948     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1949     cursorPosition =  variant.toInt();
1950     QCOMPARE(cursorPosition, 11);
1951
1952     // ImAnchorPosition
1953     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1954     anchorPosition =  variant.toInt();
1955     QCOMPARE(anchorPosition, 23);
1956
1957     // Send temporary text, which makes the editor has composition 'w'.
1958     {
1959         QList<QInputMethodEvent::Attribute> attributes;
1960         QInputMethodEvent event("w", attributes);
1961         page->event(&event);
1962     }
1963
1964     // ImCurrentSelection
1965     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1966     selectionValue = variant.value<QString>();
1967     QCOMPARE(selectionValue, QString(""));
1968
1969     // ImSurroundingText
1970     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1971     surroundingValue = variant.value<QString>();
1972     QCOMPARE(surroundingValue, QString("oeQtWebKit "));
1973
1974     // ImCursorPosition
1975     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1976     cursorPosition =  variant.toInt();
1977     QCOMPARE(cursorPosition, 11);
1978
1979     // ImAnchorPosition
1980     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1981     anchorPosition =  variant.toInt();
1982     QCOMPARE(anchorPosition, 11);
1983
1984     // Send commit text, which makes the editor conforms composition.
1985     {
1986         QList<QInputMethodEvent::Attribute> attributes;
1987         QInputMethodEvent event("", attributes);
1988         event.setCommitString("2");
1989         page->event(&event);
1990     }
1991
1992     // ImCurrentSelection
1993     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1994     selectionValue = variant.value<QString>();
1995     QCOMPARE(selectionValue, QString(""));
1996
1997     // ImSurroundingText
1998     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1999     surroundingValue = variant.value<QString>();
2000     QCOMPARE(surroundingValue, QString("oeQtWebKit 2"));
2001
2002     // ImCursorPosition
2003     variant = page->inputMethodQuery(Qt::ImCursorPosition);
2004     cursorPosition =  variant.toInt();
2005     QCOMPARE(cursorPosition, 12);
2006
2007     // ImAnchorPosition
2008     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
2009     anchorPosition =  variant.toInt();
2010     QCOMPARE(anchorPosition, 12);
2011
2012     delete container;
2013 }
2014
2015 void tst_QWebPage::inputMethodsTextFormat_data()
2016 {
2017     QTest::addColumn<QString>("string");
2018     QTest::addColumn<int>("start");
2019     QTest::addColumn<int>("length");
2020
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;
2034 }
2035
2036
2037 void tst_QWebPage::inputMethodsTextFormat()
2038 {
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();
2047     view->show();
2048
2049     QFETCH(QString, string);
2050     QFETCH(int, start);
2051     QFETCH(int, length);
2052
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);
2059     page->event(&im);
2060
2061     QTest::qWait(1000);
2062
2063     delete view;
2064 }
2065
2066 void tst_QWebPage::protectBindingsRuntimeObjectsFromCollector()
2067 {
2068     QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
2069
2070     PluginPage* newPage = new PluginPage(m_view);
2071     m_view->setPage(newPage);
2072
2073     m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
2074
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);
2077
2078     newPage->mainFrame()->evaluateJavaScript("function testme(text) { var lineedit = document.getElementById('mylineedit'); lineedit.setText(text); lineedit.selectAll(); }");
2079
2080     newPage->mainFrame()->evaluateJavaScript("testme('foo')");
2081
2082     DumpRenderTreeSupportQt::garbageCollectorCollect();
2083
2084     // don't crash!
2085     newPage->mainFrame()->evaluateJavaScript("testme('bar')");
2086 }
2087
2088 void tst_QWebPage::localURLSchemes()
2089 {
2090     int i = QWebSecurityOrigin::localSchemes().size();
2091
2092     QWebSecurityOrigin::removeLocalScheme("file");
2093     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2094     QWebSecurityOrigin::addLocalScheme("file");
2095     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2096
2097     QWebSecurityOrigin::removeLocalScheme("qrc");
2098     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i - 1);
2099     QWebSecurityOrigin::addLocalScheme("qrc");
2100     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2101
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);
2110 }
2111
2112 static inline bool testFlag(QWebPage& webPage, QWebSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue)
2113 {
2114     webPage.settings()->setAttribute(settingAttribute, settingValue);
2115     return webPage.mainFrame()->evaluateJavaScript(QString("(window.%1 != undefined)").arg(jsObjectName)).toBool();
2116 }
2117
2118 void tst_QWebPage::testOptionalJSObjects()
2119 {
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.
2124
2125     QWebPage webPage1;
2126     QWebPage webPage2;
2127
2128     webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
2129     webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
2130
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);
2137
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);
2142 }
2143
2144 void tst_QWebPage::testEnablePersistentStorage()
2145 {
2146     QWebPage webPage;
2147
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());
2153
2154     QWebSettings::enablePersistentStorage();
2155
2156
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);
2160
2161     QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty());
2162     QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty());
2163     QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty());
2164 }
2165
2166 void tst_QWebPage::defaultTextEncoding()
2167 {
2168     QWebFrame* mainFrame = m_page->mainFrame();
2169
2170     QString defaultCharset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2171     QVERIFY(!defaultCharset.isEmpty());
2172     QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), defaultCharset);
2173
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);
2178
2179     m_page->settings()->setDefaultTextEncoding(QString());
2180     charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2181     QVERIFY(!charset.isEmpty());
2182     QCOMPARE(charset, defaultCharset);
2183
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);
2188 }
2189
2190 class ErrorPage : public QWebPage
2191 {
2192 public:
2193
2194     ErrorPage(QWidget* parent = 0): QWebPage(parent)
2195     {
2196     }
2197
2198     virtual bool supportsExtension(Extension extension) const
2199     {
2200         return extension == ErrorPageExtension;
2201     }
2202
2203     virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output)
2204     {
2205         ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output);
2206
2207         errorPage->contentType = "text/html";
2208         errorPage->content = "error";
2209         return true;
2210     }
2211 };
2212
2213 void tst_QWebPage::errorPageExtension()
2214 {
2215     ErrorPage* page = new ErrorPage;
2216     m_view->setPage(page);
2217
2218     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
2219
2220     m_view->setUrl(QUrl("data:text/html,foo"));
2221     QTRY_COMPARE(spyLoadFinished.count(), 1);
2222
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);
2230
2231     page->triggerAction(QWebPage::Back);
2232     QTRY_COMPARE(page->history()->canGoBack(), false);
2233     QTRY_COMPARE(page->history()->canGoForward(), true);
2234
2235     page->triggerAction(QWebPage::Forward);
2236     QTRY_COMPARE(page->history()->canGoBack(), true);
2237     QTRY_COMPARE(page->history()->canGoForward(), false);
2238
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"));
2243
2244     m_view->setPage(0);
2245 }
2246
2247 void tst_QWebPage::errorPageExtensionInIFrames()
2248 {
2249     ErrorPage* page = new ErrorPage;
2250     m_view->setPage(page);
2251
2252     m_view->page()->mainFrame()->load(QUrl(
2253         "data:text/html,"
2254         "<h1>h1</h1>"
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);
2259
2260     QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("error"));
2261
2262     m_view->setPage(0);
2263 }
2264
2265 void tst_QWebPage::errorPageExtensionInFrameset()
2266 {
2267     ErrorPage* page = new ErrorPage;
2268     m_view->setPage(page);
2269
2270     m_view->load(QUrl("qrc:///resources/index.html"));
2271
2272     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
2273     QTRY_COMPARE(spyLoadFinished.count(), 1);
2274     QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("error"));
2275
2276     m_view->setPage(0);
2277 }
2278
2279 class FriendlyWebPage : public QWebPage
2280 {
2281 public:
2282     friend class tst_QWebPage;
2283 };
2284
2285 void tst_QWebPage::userAgentApplicationName()
2286 {
2287     const QString oldApplicationName = QCoreApplication::applicationName();
2288     FriendlyWebPage page;
2289
2290     const QString applicationNameMarker = QString::fromUtf8("StrangeName\342\210\236");
2291     QCoreApplication::setApplicationName(applicationNameMarker);
2292     QVERIFY(page.userAgentForUrl(QUrl()).contains(applicationNameMarker));
2293
2294     QCoreApplication::setApplicationName(oldApplicationName);
2295 }
2296
2297 void tst_QWebPage::userAgentLocaleChange()
2298 {
2299     FriendlyWebPage page;
2300     m_view->setPage(&page);
2301
2302     const QString markerString = QString::fromLatin1(" nn-NO)");
2303
2304     if (page.userAgentForUrl(QUrl()).contains(markerString))
2305         QSKIP("marker string already present", SkipSingle);
2306
2307     m_view->setLocale(QLocale(QString::fromLatin1("nn_NO")));
2308     QVERIFY(page.userAgentForUrl(QUrl()).contains(markerString));
2309 }
2310
2311 void tst_QWebPage::crashTests_LazyInitializationOfMainFrame()
2312 {
2313     {
2314         QWebPage webPage;
2315     }
2316     {
2317         QWebPage webPage;
2318         webPage.selectedText();
2319     }
2320     {
2321         QWebPage webPage;
2322         webPage.triggerAction(QWebPage::Back, true);
2323     }
2324     {
2325         QWebPage webPage;
2326         QPoint pos(10,10);
2327         webPage.updatePositionDependentActions(pos);
2328     }
2329 }
2330
2331 static void takeScreenshot(QWebPage* page)
2332 {
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);
2338     painter.end();
2339 }
2340
2341 void tst_QWebPage::screenshot_data()
2342 {
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>");
2347 }
2348
2349 void tst_QWebPage::screenshot()
2350 {
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);
2353
2354     QDir::setCurrent(TESTS_SOURCE_DIR);
2355
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);
2362
2363     // take screenshot without a view
2364     takeScreenshot(page);
2365
2366     QWebView* view = new QWebView;
2367     view->setPage(page);
2368
2369     // take screenshot when attached to a view
2370     takeScreenshot(page);
2371
2372     delete page;
2373     delete view;
2374
2375     QDir::setCurrent(QApplication::applicationDirPath());
2376 }
2377
2378 void tst_QWebPage::originatingObjectInNetworkRequests()
2379 {
2380     TestNetworkManager* networkManager = new TestNetworkManager(m_page);
2381     m_page->setNetworkAccessManager(networkManager);
2382     networkManager->requests.clear();
2383
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))));
2388
2389     QCOMPARE(networkManager->requests.count(), 2);
2390
2391     QList<QWebFrame*> childFrames = m_page->mainFrame()->childFrames();
2392     QCOMPARE(childFrames.count(), 2);
2393
2394     for (int i = 0; i < 2; ++i)
2395         QVERIFY(qobject_cast<QWebFrame*>(networkManager->requests.at(i).originatingObject()) == childFrames.at(i));
2396 }
2397
2398 /**
2399  * Test fixups for https://bugs.webkit.org/show_bug.cgi?id=30914
2400  *
2401  * From JS we test the following conditions.
2402  *
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
2407  */
2408 class JSPromptPage : public QWebPage {
2409     Q_OBJECT
2410 public:
2411     JSPromptPage()
2412     {}
2413
2414     bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result)
2415     {
2416         if (msg == QLatin1String("test1")) {
2417             *result = QString();
2418             return true;
2419         } else if (msg == QLatin1String("test2")) {
2420             *result = QLatin1String("text");
2421             return true;
2422         } else if (msg == QLatin1String("test3")) {
2423             *result = QString();
2424             return false;
2425         } else if (msg == QLatin1String("test4")) {
2426             *result = QLatin1String("text");
2427             return false;
2428         }
2429
2430         qFatal("Unknown msg.");
2431         return QWebPage::javaScriptPrompt(frame, msg, defaultValue, result);
2432     }
2433 };
2434
2435 void tst_QWebPage::testJSPrompt()
2436 {
2437     JSPromptPage page;
2438     bool res;
2439
2440     // OK + QString()
2441     res = page.mainFrame()->evaluateJavaScript(
2442             "var retval = prompt('test1');"
2443             "retval=='' && retval.length == 0;").toBool();
2444     QVERIFY(res);
2445
2446     // OK + "text"
2447     res = page.mainFrame()->evaluateJavaScript(
2448             "var retval = prompt('test2');"
2449             "retval=='text' && retval.length == 4;").toBool();
2450     QVERIFY(res);
2451
2452     // Cancel + QString()
2453     res = page.mainFrame()->evaluateJavaScript(
2454             "var retval = prompt('test3');"
2455             "retval===null;").toBool();
2456     QVERIFY(res);
2457
2458     // Cancel + "text"
2459     res = page.mainFrame()->evaluateJavaScript(
2460             "var retval = prompt('test4');"
2461             "retval===null;").toBool();
2462     QVERIFY(res);
2463 }
2464
2465 class TestModalPage : public QWebPage
2466 {
2467     Q_OBJECT
2468 public:
2469     TestModalPage(QObject* parent = 0) : QWebPage(parent) {
2470     }
2471     virtual QWebPage* createWindow(WebWindowType) {
2472         QWebPage* page = new TestModalPage();
2473         connect(page, SIGNAL(windowCloseRequested()), page, SLOT(deleteLater()));
2474         return page;
2475     }
2476 };
2477
2478 void tst_QWebPage::showModalDialog()
2479 {
2480     TestModalPage page;
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"));
2484 }
2485
2486 void tst_QWebPage::testStopScheduledPageRefresh()
2487 {    
2488     // Without QWebPage::StopScheduledPageRefresh
2489     QWebPage page1;
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>"
2494                                 "</body></html>");
2495     QVERIFY(::waitForSignal(&page1, SIGNAL(loadFinished(bool))));
2496     QTest::qWait(500);
2497     QCOMPARE(page1.mainFrame()->url(), QUrl(QLatin1String("qrc:///resources/index.html")));
2498     
2499     // With QWebPage::StopScheduledPageRefresh
2500     QWebPage page2;
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>"
2505                                "</body></html>");
2506     page2.triggerAction(QWebPage::StopScheduledPageRefresh);
2507     QTest::qWait(1500);
2508     QCOMPARE(page2.mainFrame()->url().toString(), QLatin1String("about:blank"));
2509 }
2510
2511 void tst_QWebPage::findText()
2512 {
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());
2524     }
2525 }
2526
2527 struct ImageExtensionMap {
2528     const char* extension;
2529     const char* mimeType;
2530 };
2531
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" },
2561     { 0, 0 }
2562 };
2563
2564 static QString getMimeTypeForExtension(const QString &ext)
2565 {
2566     const ImageExtensionMap *e = extensionMap;
2567     while (e->extension) {
2568         if (ext.compare(QLatin1String(e->extension), Qt::CaseInsensitive) == 0)
2569             return QLatin1String(e->mimeType);
2570         ++e;
2571     }
2572
2573     return QString();
2574 }
2575
2576 void tst_QWebPage::supportedContentType()
2577 {
2578    QStringList contentTypes;
2579
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";
2584
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;
2590    }
2591
2592    // Get the mime types supported by webkit...
2593    const QStringList supportedContentTypes = m_page->supportedContentTypes();
2594
2595    Q_FOREACH(const QString& mimeType, contentTypes)
2596       QVERIFY2(supportedContentTypes.contains(mimeType), QString("'%1' is not a supported content type!").arg(mimeType).toLatin1());
2597       
2598    Q_FOREACH(const QString& mimeType, contentTypes)
2599       QVERIFY2(m_page->supportsContentType(mimeType), QString("Cannot handle content types '%1'!").arg(mimeType).toLatin1());
2600 }
2601
2602 QTEST_MAIN(tst_QWebPage)
2603 #include "tst_qwebpage.moc"