2011-01-10 Laszlo Gombos <laszlo.1.gombos@nokia.com>
[WebKit-https.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 <QClipboard>
25 #include <QDir>
26 #include <QGraphicsWidget>
27 #include <QLineEdit>
28 #include <QLocale>
29 #include <QMenu>
30 #include <QPushButton>
31 #include <QStyle>
32 #include <QtTest/QtTest>
33 #include <QTextCharFormat>
34 #include <qgraphicsscene.h>
35 #include <qgraphicsview.h>
36 #include <qgraphicswebview.h>
37 #include <qnetworkcookiejar.h>
38 #include <qnetworkrequest.h>
39 #include <qwebdatabase.h>
40 #include <qwebelement.h>
41 #include <qwebframe.h>
42 #include <qwebhistory.h>
43 #include <qwebpage.h>
44 #include <qwebsecurityorigin.h>
45 #include <qwebview.h>
46 #include <qimagewriter.h>
47
48 class EventSpy : public QObject, public QList<QEvent::Type>
49 {
50     Q_OBJECT
51 public:
52     EventSpy(QObject* objectToSpy)
53     {
54         objectToSpy->installEventFilter(this);
55     }
56
57     virtual bool eventFilter(QObject* receiver, QEvent* event)
58     {
59         append(event->type());
60         return false;
61     }
62 };
63
64 class tst_QWebPage : public QObject
65 {
66     Q_OBJECT
67
68 public:
69     tst_QWebPage();
70     virtual ~tst_QWebPage();
71
72 public slots:
73     void init();
74     void cleanup();
75     void cleanupFiles();
76
77 private slots:
78     void initTestCase();
79     void cleanupTestCase();
80
81     void acceptNavigationRequest();
82     void geolocationRequestJS();
83     void loadFinished();
84     void acceptNavigationRequestWithNewWindow();
85     void userStyleSheet();
86     void modified();
87     void contextMenuCrash();
88     void database();
89     void createPluginWithPluginsEnabled();
90     void createPluginWithPluginsDisabled();
91     void destroyPlugin_data();
92     void destroyPlugin();
93     void createViewlessPlugin_data();
94     void createViewlessPlugin();
95     void graphicsWidgetPlugin();
96     void multiplePageGroupsAndLocalStorage();
97     void cursorMovements();
98     void textSelection();
99     void textEditing();
100     void backActionUpdate();
101     void frameAt();
102     void requestCache();
103     void loadCachedPage();
104     void protectBindingsRuntimeObjectsFromCollector();
105     void localURLSchemes();
106     void testOptionalJSObjects();
107     void testEnablePersistentStorage();
108     void consoleOutput();
109     void inputMethods_data();
110     void inputMethods();
111     void inputMethodsTextFormat_data();
112     void inputMethodsTextFormat();
113     void defaultTextEncoding();
114     void errorPageExtension();
115     void errorPageExtensionInIFrames();
116     void errorPageExtensionInFrameset();
117     void userAgentApplicationName();
118     void userAgentLocaleChange();
119
120     void viewModes();
121
122     void crashTests_LazyInitializationOfMainFrame();
123
124     void screenshot_data();
125     void screenshot();
126
127     void originatingObjectInNetworkRequests();
128     void testJSPrompt();
129     void showModalDialog();
130     void testStopScheduledPageRefresh();
131     void findText();
132     void supportedContentType();
133     void infiniteLoopJS();
134     void networkAccessManagerOnDifferentThread();
135     void navigatorCookieEnabled();
136
137 #ifdef Q_OS_MAC
138     void macCopyUnicodeToClipboard();
139 #endif
140     
141 private:
142     QWebView* m_view;
143     QWebPage* m_page;
144 };
145
146 tst_QWebPage::tst_QWebPage()
147 {
148 }
149
150 tst_QWebPage::~tst_QWebPage()
151 {
152 }
153
154 void tst_QWebPage::init()
155 {
156     m_view = new QWebView();
157     m_page = m_view->page();
158 }
159
160 void tst_QWebPage::cleanup()
161 {
162     delete m_view;
163 }
164
165 void tst_QWebPage::cleanupFiles()
166 {
167     QFile::remove("Databases.db");
168     QDir::current().rmdir("http_www.myexample.com_0");
169     QFile::remove("http_www.myexample.com_0.localstorage");
170 }
171
172 void tst_QWebPage::initTestCase()
173 {
174     cleanupFiles(); // In case there are old files from previous runs
175 }
176
177 void tst_QWebPage::cleanupTestCase()
178 {
179     cleanupFiles(); // Be nice
180 }
181
182 class NavigationRequestOverride : public QWebPage
183 {
184 public:
185     NavigationRequestOverride(QWebView* parent, bool initialValue) : QWebPage(parent), m_acceptNavigationRequest(initialValue) {}
186
187     bool m_acceptNavigationRequest;
188 protected:
189     virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, QWebPage::NavigationType type) {
190         Q_UNUSED(frame);
191         Q_UNUSED(request);
192         Q_UNUSED(type);
193
194         return m_acceptNavigationRequest;
195     }
196 };
197
198 void tst_QWebPage::acceptNavigationRequest()
199 {
200     QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
201
202     NavigationRequestOverride* newPage = new NavigationRequestOverride(m_view, false);
203     m_view->setPage(newPage);
204
205     m_view->setHtml(QString("<html><body><form name='tstform' action='data:text/html,foo'method='get'>"
206                             "<input type='text'><input type='submit'></form></body></html>"), QUrl());
207     QTRY_COMPARE(loadSpy.count(), 1);
208
209     m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
210
211     newPage->m_acceptNavigationRequest = true;
212     m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
213     QTRY_COMPARE(loadSpy.count(), 2);
214
215     QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("foo?"));
216
217     // Restore default page
218     m_view->setPage(0);
219 }
220
221 class JSTestPage : public QWebPage
222 {
223 Q_OBJECT
224 public:
225     JSTestPage(QObject* parent = 0)
226     : QWebPage(parent) {}
227
228 public slots:
229     bool shouldInterruptJavaScript() {
230         return true;
231     }
232     void requestPermission(QWebFrame* frame, QWebPage::Feature feature)
233     {
234         if (m_allowGeolocation)
235             setFeaturePermission(frame, feature, PermissionGrantedByUser);
236         else 
237             setFeaturePermission(frame, feature, PermissionDeniedByUser);
238     }
239
240 public:
241     void setGeolocationPermission(bool allow) 
242     {
243         m_allowGeolocation = allow;
244     }
245
246 private: 
247     bool m_allowGeolocation;
248 };
249
250 void tst_QWebPage::infiniteLoopJS()
251 {
252     JSTestPage* newPage = new JSTestPage(m_view);
253     m_view->setPage(newPage);
254     m_view->setHtml(QString("<html><body>test</body></html>"), QUrl());
255     m_view->page()->mainFrame()->evaluateJavaScript("var run = true;var a = 1;while(run){a++;}");
256     delete newPage;
257 }
258
259 void tst_QWebPage::geolocationRequestJS()
260 {
261     JSTestPage* newPage = new JSTestPage(m_view);
262     connect(newPage, SIGNAL(featurePermissionRequested(QWebFrame*, QWebPage::Feature)),
263             newPage, SLOT(requestPermission(QWebFrame*, QWebPage::Feature)));
264
265     newPage->setGeolocationPermission(false);
266     m_view->setPage(newPage);
267     m_view->setHtml(QString("<html><body>test</body></html>"), QUrl());
268     m_view->page()->mainFrame()->evaluateJavaScript("var errorCode = 0; function error(err) { errorCode = err.code; } function success(pos) { } navigator.geolocation.getCurrentPosition(success, error)");
269     QTest::qWait(2000);
270     QVariant empty = m_view->page()->mainFrame()->evaluateJavaScript("errorCode");
271
272     QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 0);
273
274     newPage->setGeolocationPermission(true);
275     m_view->page()->mainFrame()->evaluateJavaScript("errorCode = 0; navigator.geolocation.getCurrentPosition(success, error);");
276     empty = m_view->page()->mainFrame()->evaluateJavaScript("errorCode");
277
278     //http://dev.w3.org/geo/api/spec-source.html#position
279     //PositionError: const unsigned short PERMISSION_DENIED = 1;
280     QVERIFY(empty.type() == QVariant::Double && empty.toInt() != 1);
281     delete newPage;
282 }
283
284 void tst_QWebPage::loadFinished()
285 {
286     qRegisterMetaType<QWebFrame*>("QWebFrame*");
287     qRegisterMetaType<QNetworkRequest*>("QNetworkRequest*");
288     QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted()));
289     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
290
291     m_view->page()->mainFrame()->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
292                                            "<head><meta http-equiv='refresh' content='1'></head>foo \">"
293                                            "<frame src=\"data:text/html,bar\"></frameset>"));
294     QTRY_COMPARE(spyLoadFinished.count(), 1);
295
296     QTRY_VERIFY(spyLoadStarted.count() > 1);
297     QTRY_VERIFY(spyLoadFinished.count() > 1);
298
299     spyLoadFinished.clear();
300
301     m_view->page()->mainFrame()->load(QUrl("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
302                                            "foo \"><frame src=\"data:text/html,bar\"></frameset>"));
303     QTRY_COMPARE(spyLoadFinished.count(), 1);
304     QCOMPARE(spyLoadFinished.count(), 1);
305 }
306
307 class ConsolePage : public QWebPage
308 {
309 public:
310     ConsolePage(QObject* parent = 0) : QWebPage(parent) {}
311
312     virtual void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID)
313     {
314         messages.append(message);
315         lineNumbers.append(lineNumber);
316         sourceIDs.append(sourceID);
317     }
318
319     QStringList messages;
320     QList<int> lineNumbers;
321     QStringList sourceIDs;
322 };
323
324 void tst_QWebPage::consoleOutput()
325 {
326     ConsolePage page;
327     page.mainFrame()->evaluateJavaScript("this is not valid JavaScript");
328     QCOMPARE(page.messages.count(), 1);
329     QCOMPARE(page.lineNumbers.at(0), 1);
330 }
331
332 class TestPage : public QWebPage
333 {
334 public:
335     TestPage(QObject* parent = 0) : QWebPage(parent) {}
336
337     struct Navigation {
338         QPointer<QWebFrame> frame;
339         QNetworkRequest request;
340         NavigationType type;
341     };
342
343     QList<Navigation> navigations;
344     QList<QWebPage*> createdWindows;
345
346     virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, NavigationType type) {
347         Navigation n;
348         n.frame = frame;
349         n.request = request;
350         n.type = type;
351         navigations.append(n);
352         return true;
353     }
354
355     virtual QWebPage* createWindow(WebWindowType) {
356         QWebPage* page = new TestPage(this);
357         createdWindows.append(page);
358         return page;
359     }
360 };
361
362 void tst_QWebPage::acceptNavigationRequestWithNewWindow()
363 {
364     TestPage* page = new TestPage(m_view);
365     page->settings()->setAttribute(QWebSettings::LinksIncludedInFocusChain, true);
366     m_page = page;
367     m_view->setPage(m_page);
368
369     m_view->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
370     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
371
372     QFocusEvent fe(QEvent::FocusIn);
373     m_page->event(&fe);
374
375     QVERIFY(m_page->focusNextPrevChild(/*next*/ true));
376
377     QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
378     m_page->event(&keyEnter);
379
380     QCOMPARE(page->navigations.count(), 2);
381
382     TestPage::Navigation n = page->navigations.at(1);
383     QVERIFY(n.frame.isNull());
384     QCOMPARE(n.request.url().toString(), QString("data:text/html,Reached"));
385     QVERIFY(n.type == QWebPage::NavigationTypeLinkClicked);
386
387     QCOMPARE(page->createdWindows.count(), 1);
388 }
389
390 class TestNetworkManager : public QNetworkAccessManager
391 {
392 public:
393     TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {}
394
395     QList<QUrl> requestedUrls;
396     QList<QNetworkRequest> requests;
397
398 protected:
399     virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) {
400         requests.append(request);
401         requestedUrls.append(request.url());
402         return QNetworkAccessManager::createRequest(op, request, outgoingData);
403     }
404 };
405
406 void tst_QWebPage::userStyleSheet()
407 {
408     TestNetworkManager* networkManager = new TestNetworkManager(m_page);
409     m_page->setNetworkAccessManager(networkManager);
410     networkManager->requestedUrls.clear();
411
412     m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css;charset=utf-8;base64,"
413             + QByteArray("p { background-image: url('http://does.not/exist.png');}").toBase64()));
414     m_view->setHtml("<p>hello world</p>");
415     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
416
417     QVERIFY(networkManager->requestedUrls.count() >= 1);
418     QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png"));
419 }
420
421 void tst_QWebPage::viewModes()
422 {
423     m_view->setHtml("<body></body>");
424     m_page->setProperty("_q_viewMode", "minimized");
425
426     QVariant empty = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode)\")");
427     QVERIFY(empty.type() == QVariant::Bool && empty.toBool());
428
429     QVariant minimized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: minimized)\")");
430     QVERIFY(minimized.type() == QVariant::Bool && minimized.toBool());
431
432     QVariant maximized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: maximized)\")");
433     QVERIFY(maximized.type() == QVariant::Bool && !maximized.toBool());
434 }
435
436 void tst_QWebPage::modified()
437 {
438     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>blub"));
439     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
440
441     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah"));
442     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
443
444     QVERIFY(!m_page->isModified());
445
446 //    m_page->mainFrame()->evaluateJavaScript("alert(document.getElementById('foo'))");
447     m_page->mainFrame()->evaluateJavaScript("document.getElementById('foo').focus()");
448     m_page->mainFrame()->evaluateJavaScript("document.execCommand('InsertText', true, 'Test');");
449
450     QVERIFY(m_page->isModified());
451
452     m_page->mainFrame()->evaluateJavaScript("document.execCommand('Undo', true);");
453
454     QVERIFY(!m_page->isModified());
455
456     m_page->mainFrame()->evaluateJavaScript("document.execCommand('Redo', true);");
457
458     QVERIFY(m_page->isModified());
459
460     QVERIFY(m_page->history()->canGoBack());
461     QVERIFY(!m_page->history()->canGoForward());
462     QCOMPARE(m_page->history()->count(), 2);
463     QVERIFY(m_page->history()->backItem().isValid());
464     QVERIFY(!m_page->history()->forwardItem().isValid());
465
466     m_page->history()->back();
467     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
468
469     QVERIFY(!m_page->history()->canGoBack());
470     QVERIFY(m_page->history()->canGoForward());
471
472     QVERIFY(!m_page->isModified());
473
474     QVERIFY(m_page->history()->currentItemIndex() == 0);
475
476     m_page->history()->setMaximumItemCount(3);
477     QVERIFY(m_page->history()->maximumItemCount() == 3);
478
479     QVariant variant("string test");
480     m_page->history()->currentItem().setUserData(variant);
481     QVERIFY(m_page->history()->currentItem().userData().toString() == "string test");
482
483     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is second page"));
484     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is third page"));
485     QVERIFY(m_page->history()->count() == 2);
486     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fourth page"));
487     QVERIFY(m_page->history()->count() == 2);
488     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fifth page"));
489     QVERIFY(::waitForSignal(m_page, SIGNAL(saveFrameStateRequested(QWebFrame*,QWebHistoryItem*))));
490 }
491
492 void tst_QWebPage::contextMenuCrash()
493 {
494     QWebView view;
495     view.setHtml("<p>test");
496     view.page()->updatePositionDependentActions(QPoint(0, 0));
497     QMenu* contextMenu = 0;
498     foreach (QObject* child, view.children()) {
499         contextMenu = qobject_cast<QMenu*>(child);
500         if (contextMenu)
501             break;
502     }
503     QVERIFY(contextMenu);
504     delete contextMenu;
505 }
506
507 void tst_QWebPage::database()
508 {
509     QString path = QDir::currentPath();
510     m_page->settings()->setOfflineStoragePath(path);
511     QVERIFY(m_page->settings()->offlineStoragePath() == path);
512
513     QWebSettings::setOfflineStorageDefaultQuota(1024 * 1024);
514     QVERIFY(QWebSettings::offlineStorageDefaultQuota() == 1024 * 1024);
515
516     m_page->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
517     m_page->settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true);
518
519     QString dbFileName = path + "Databases.db";
520
521     if (QFile::exists(dbFileName))
522         QFile::remove(dbFileName);
523
524     qRegisterMetaType<QWebFrame*>("QWebFrame*");
525     QSignalSpy spy(m_page, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString)));
526     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"));
527     QTRY_COMPARE(spy.count(), 1);
528     m_page->mainFrame()->evaluateJavaScript("var db2; db2=openDatabase('testdb', '1.0', 'test database API', 50000);");
529     QTRY_COMPARE(spy.count(),1);
530
531     m_page->mainFrame()->evaluateJavaScript("localStorage.test='This is a test for local storage';");
532     m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
533
534     QVariant s1 = m_page->mainFrame()->evaluateJavaScript("localStorage.test");
535     QCOMPARE(s1.toString(), QString("This is a test for local storage"));
536
537     m_page->mainFrame()->evaluateJavaScript("sessionStorage.test='This is a test for session storage';");
538     m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
539     QVariant s2 = m_page->mainFrame()->evaluateJavaScript("sessionStorage.test");
540     QCOMPARE(s2.toString(), QString("This is a test for session storage"));
541
542     m_view->setHtml(QString("<html><head></head><body><div></div></body></html>"), QUrl("http://www.myexample.com"));
543     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) { });");
544     QTest::qWait(200);
545
546     // Remove all databases.
547     QWebSecurityOrigin origin = m_page->mainFrame()->securityOrigin();
548     QList<QWebDatabase> dbs = origin.databases();
549     for (int i = 0; i < dbs.count(); i++) {
550         QString fileName = dbs[i].fileName();
551         QVERIFY(QFile::exists(fileName));
552         QWebDatabase::removeDatabase(dbs[i]);
553         QVERIFY(!QFile::exists(fileName));
554     }
555     QVERIFY(!origin.databases().size());
556     // Remove removed test :-)
557     QWebDatabase::removeAllDatabases();
558     QVERIFY(!origin.databases().size());
559 }
560
561 class PluginPage : public QWebPage
562 {
563 public:
564     PluginPage(QObject *parent = 0)
565         : QWebPage(parent) {}
566
567     struct CallInfo
568     {
569         CallInfo(const QString &c, const QUrl &u,
570                  const QStringList &pn, const QStringList &pv,
571                  QObject *r)
572             : classid(c), url(u), paramNames(pn),
573               paramValues(pv), returnValue(r)
574             {}
575         QString classid;
576         QUrl url;
577         QStringList paramNames;
578         QStringList paramValues;
579         QObject *returnValue;
580     };
581
582     QList<CallInfo> calls;
583
584 protected:
585     virtual QObject *createPlugin(const QString &classid, const QUrl &url,
586                                   const QStringList &paramNames,
587                                   const QStringList &paramValues)
588     {
589         QObject *result = 0;
590         if (classid == "pushbutton")
591             result = new QPushButton();
592 #ifndef QT_NO_INPUTDIALOG
593         else if (classid == "lineedit")
594             result = new QLineEdit();
595 #endif
596         else if (classid == "graphicswidget")
597             result = new QGraphicsWidget();
598         if (result)
599             result->setObjectName(classid);
600         calls.append(CallInfo(classid, url, paramNames, paramValues, result));
601         return result;
602     }
603 };
604
605 static void createPlugin(QWebView *view)
606 {
607     QSignalSpy loadSpy(view, SIGNAL(loadFinished(bool)));
608
609     PluginPage* newPage = new PluginPage(view);
610     view->setPage(newPage);
611
612     // type has to be application/x-qt-plugin
613     view->setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='pushbutton' id='mybutton'/></body></html>"));
614     QTRY_COMPARE(loadSpy.count(), 1);
615     QCOMPARE(newPage->calls.count(), 0);
616
617     view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>"));
618     QTRY_COMPARE(loadSpy.count(), 2);
619     QCOMPARE(newPage->calls.count(), 1);
620     {
621         PluginPage::CallInfo ci = newPage->calls.takeFirst();
622         QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
623         QCOMPARE(ci.url, QUrl());
624         QCOMPARE(ci.paramNames.count(), 3);
625         QCOMPARE(ci.paramValues.count(), 3);
626         QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
627         QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
628         QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
629         QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton"));
630         QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
631         QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton"));
632         QVERIFY(ci.returnValue != 0);
633         QVERIFY(ci.returnValue->inherits("QPushButton"));
634     }
635     // test JS bindings
636     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mybutton').toString()").toString(),
637              QString::fromLatin1("[object HTMLObjectElement]"));
638     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.toString()").toString(),
639              QString::fromLatin1("[object HTMLObjectElement]"));
640     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.objectName").toString(),
641              QString::fromLatin1("string"));
642     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.objectName").toString(),
643              QString::fromLatin1("pushbutton"));
644     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.clicked").toString(),
645              QString::fromLatin1("function"));
646     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.clicked.toString()").toString(),
647              QString::fromLatin1("function clicked() {\n    [native code]\n}"));
648
649     view->setHtml(QString("<html><body><table>"
650                             "<tr><object type='application/x-qt-plugin' classid='lineedit' id='myedit'/></tr>"
651                             "<tr><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></tr>"
652                             "</table></body></html>"), QUrl("http://foo.bar.baz"));
653     QTRY_COMPARE(loadSpy.count(), 3);
654     QCOMPARE(newPage->calls.count(), 2);
655     {
656         PluginPage::CallInfo ci = newPage->calls.takeFirst();
657         QCOMPARE(ci.classid, QString::fromLatin1("lineedit"));
658         QCOMPARE(ci.url, QUrl());
659         QCOMPARE(ci.paramNames.count(), 3);
660         QCOMPARE(ci.paramValues.count(), 3);
661         QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
662         QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
663         QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
664         QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("lineedit"));
665         QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
666         QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("myedit"));
667         QVERIFY(ci.returnValue != 0);
668         QVERIFY(ci.returnValue->inherits("QLineEdit"));
669     }
670     {
671         PluginPage::CallInfo ci = newPage->calls.takeFirst();
672         QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
673         QCOMPARE(ci.url, QUrl());
674         QCOMPARE(ci.paramNames.count(), 3);
675         QCOMPARE(ci.paramValues.count(), 3);
676         QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
677         QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
678         QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
679         QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton"));
680         QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
681         QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton"));
682         QVERIFY(ci.returnValue != 0);
683         QVERIFY(ci.returnValue->inherits("QPushButton"));
684     }
685 }
686
687 void tst_QWebPage::graphicsWidgetPlugin()
688 {
689     m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
690     QGraphicsWebView webView;
691
692     QSignalSpy loadSpy(&webView, SIGNAL(loadFinished(bool)));
693
694     PluginPage* newPage = new PluginPage(&webView);
695     webView.setPage(newPage);
696
697     // type has to be application/x-qt-plugin
698     webView.setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='graphicswidget' id='mygraphicswidget'/></body></html>"));
699     QTRY_COMPARE(loadSpy.count(), 1);
700     QCOMPARE(newPage->calls.count(), 0);
701
702     webView.setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='graphicswidget' id='mygraphicswidget'/></body></html>"));
703     QTRY_COMPARE(loadSpy.count(), 2);
704     QCOMPARE(newPage->calls.count(), 1);
705     {
706         PluginPage::CallInfo ci = newPage->calls.takeFirst();
707         QCOMPARE(ci.classid, QString::fromLatin1("graphicswidget"));
708         QCOMPARE(ci.url, QUrl());
709         QCOMPARE(ci.paramNames.count(), 3);
710         QCOMPARE(ci.paramValues.count(), 3);
711         QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
712         QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
713         QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
714         QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("graphicswidget"));
715         QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
716         QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mygraphicswidget"));
717         QVERIFY(ci.returnValue);
718         QVERIFY(ci.returnValue->inherits("QGraphicsWidget"));
719     }
720     // test JS bindings
721     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mygraphicswidget').toString()").toString(),
722              QString::fromLatin1("[object HTMLObjectElement]"));
723     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.toString()").toString(),
724              QString::fromLatin1("[object HTMLObjectElement]"));
725     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mygraphicswidget.objectName").toString(),
726              QString::fromLatin1("string"));
727     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.objectName").toString(),
728              QString::fromLatin1("graphicswidget"));
729     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mygraphicswidget.geometryChanged").toString(),
730              QString::fromLatin1("function"));
731     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mygraphicswidget.geometryChanged.toString()").toString(),
732              QString::fromLatin1("function geometryChanged() {\n    [native code]\n}"));
733 }
734
735 void tst_QWebPage::createPluginWithPluginsEnabled()
736 {
737     m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
738     createPlugin(m_view);
739 }
740
741 void tst_QWebPage::createPluginWithPluginsDisabled()
742 {
743     // Qt Plugins should be loaded by QtWebKit even when PluginsEnabled is
744     // false. The client decides whether a Qt plugin is enabled or not when
745     // it decides whether or not to instantiate it.
746     m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, false);
747     createPlugin(m_view);
748 }
749
750 // Standard base class for template PluginTracerPage. In tests it is used as interface.
751 class PluginCounterPage : public QWebPage {
752 public:
753     int m_count;
754     QPointer<QObject> m_widget;
755     QObject* m_pluginParent;
756     PluginCounterPage(QObject* parent = 0)
757         : QWebPage(parent)
758         , m_count(0)
759         , m_widget(0)
760         , m_pluginParent(0)
761     {
762        settings()->setAttribute(QWebSettings::PluginsEnabled, true);
763     }
764     ~PluginCounterPage()
765     {
766         if (m_pluginParent)
767             m_pluginParent->deleteLater();
768     }
769 };
770
771 template<class T>
772 class PluginTracerPage : public PluginCounterPage {
773 public:
774     PluginTracerPage(QObject* parent = 0)
775         : PluginCounterPage(parent)
776     {
777         // this is a dummy parent object for the created plugin
778         m_pluginParent = new T;
779     }
780     virtual QObject* createPlugin(const QString&, const QUrl&, const QStringList&, const QStringList&)
781     {
782         m_count++;
783         m_widget = new T;
784         // need a cast to the specific type, as QObject::setParent cannot be called,
785         // because it is not virtual. Instead it is necesary to call QWidget::setParent,
786         // which also takes a QWidget* instead of a QObject*. Therefore we need to
787         // upcast to T*, which is a QWidget.
788         static_cast<T*>(m_widget.data())->setParent(static_cast<T*>(m_pluginParent));
789         return m_widget;
790     }
791 };
792
793 class PluginFactory {
794 public:
795     enum FactoredType {QWidgetType, QGraphicsWidgetType};
796     static PluginCounterPage* create(FactoredType type, QObject* parent = 0)
797     {
798         PluginCounterPage* result = 0;
799         switch (type) {
800         case QWidgetType:
801             result = new PluginTracerPage<QWidget>(parent);
802             break;
803         case QGraphicsWidgetType:
804             result = new PluginTracerPage<QGraphicsWidget>(parent);
805             break;
806         default: {/*Oops*/};
807         }
808         return result;
809     }
810
811     static void prepareTestData()
812     {
813         QTest::addColumn<int>("type");
814         QTest::newRow("QWidget") << (int)PluginFactory::QWidgetType;
815         QTest::newRow("QGraphicsWidget") << (int)PluginFactory::QGraphicsWidgetType;
816     }
817 };
818
819 void tst_QWebPage::destroyPlugin_data()
820 {
821     PluginFactory::prepareTestData();
822 }
823
824 void tst_QWebPage::destroyPlugin()
825 {
826     QFETCH(int, type);
827     PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type, m_view);
828     m_view->setPage(page);
829
830     // we create the plugin, so the widget should be constructed
831     QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
832     m_view->setHtml(content);
833     QVERIFY(page->m_widget);
834     QCOMPARE(page->m_count, 1);
835
836     // navigate away, the plugin widget should be destructed
837     m_view->setHtml("<html><body>Hi</body></html>");
838     QTestEventLoop::instance().enterLoop(1);
839     QVERIFY(!page->m_widget);
840 }
841
842 void tst_QWebPage::createViewlessPlugin_data()
843 {
844     PluginFactory::prepareTestData();
845 }
846
847 void tst_QWebPage::createViewlessPlugin()
848 {
849     QFETCH(int, type);
850     PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type);
851     QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
852     page->mainFrame()->setHtml(content);
853     QCOMPARE(page->m_count, 1);
854     QVERIFY(page->m_widget);
855     QVERIFY(page->m_pluginParent);
856     QVERIFY(page->m_widget->parent() == page->m_pluginParent);
857     delete page;
858
859 }
860
861 void tst_QWebPage::multiplePageGroupsAndLocalStorage()
862 {
863     QDir dir(QDir::currentPath());
864     dir.mkdir("path1");
865     dir.mkdir("path2");
866
867     QWebView view1;
868     QWebView view2;
869
870     view1.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
871     view1.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path1"));
872     DumpRenderTreeSupportQt::webPageSetGroupName(view1.page(), "group1");
873     view2.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);    
874     view2.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path2"));
875     DumpRenderTreeSupportQt::webPageSetGroupName(view2.page(), "group2");
876     QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view1.page()), QString("group1"));
877     QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view2.page()), QString("group2"));
878
879
880     view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
881     view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
882
883     view1.page()->mainFrame()->evaluateJavaScript("localStorage.test='value1';");
884     view2.page()->mainFrame()->evaluateJavaScript("localStorage.test='value2';");
885
886     view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
887     view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
888
889     QVariant s1 = view1.page()->mainFrame()->evaluateJavaScript("localStorage.test");
890     QCOMPARE(s1.toString(), QString("value1"));
891
892     QVariant s2 = view2.page()->mainFrame()->evaluateJavaScript("localStorage.test");
893     QCOMPARE(s2.toString(), QString("value2"));
894
895     QTest::qWait(1000);
896
897     QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path1/http_www.myexample.com_0.localstorage"));
898     QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path2/http_www.myexample.com_0.localstorage"));
899     dir.rmdir(QDir::toNativeSeparators("./path1"));
900     dir.rmdir(QDir::toNativeSeparators("./path2"));
901 }
902
903 class CursorTrackedPage : public QWebPage
904 {
905 public:
906
907     CursorTrackedPage(QWidget *parent = 0): QWebPage(parent) {
908         setViewportSize(QSize(1024, 768)); // big space
909     }
910
911     QString selectedText() {
912         return mainFrame()->evaluateJavaScript("window.getSelection().toString()").toString();
913     }
914
915     int selectionStartOffset() {
916         return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).startOffset").toInt();
917     }
918
919     int selectionEndOffset() {
920         return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).endOffset").toInt();
921     }
922
923     // true if start offset == end offset, i.e. no selected text
924     int isSelectionCollapsed() {
925         return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).collapsed").toBool();
926     }
927 };
928
929 void tst_QWebPage::cursorMovements()
930 {
931     CursorTrackedPage* page = new CursorTrackedPage;
932     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>");
933     page->mainFrame()->setHtml(content);
934
935     // this will select the first paragraph
936     QString script = "var range = document.createRange(); " \
937         "var node = document.getElementById(\"one\"); " \
938         "range.selectNode(node); " \
939         "getSelection().addRange(range);";
940     page->mainFrame()->evaluateJavaScript(script);
941     QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
942     QCOMPARE(page->selectedHtml().trimmed(), QString::fromLatin1("<span class=\"Apple-style-span\" style=\"border-collapse: separate; color: rgb(0, 0, 0); font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; \"><p id=\"one\">The quick brown fox</p></span>"));
943
944     // these actions must exist
945     QVERIFY(page->action(QWebPage::MoveToNextChar) != 0);
946     QVERIFY(page->action(QWebPage::MoveToPreviousChar) != 0);
947     QVERIFY(page->action(QWebPage::MoveToNextWord) != 0);
948     QVERIFY(page->action(QWebPage::MoveToPreviousWord) != 0);
949     QVERIFY(page->action(QWebPage::MoveToNextLine) != 0);
950     QVERIFY(page->action(QWebPage::MoveToPreviousLine) != 0);
951     QVERIFY(page->action(QWebPage::MoveToStartOfLine) != 0);
952     QVERIFY(page->action(QWebPage::MoveToEndOfLine) != 0);
953     QVERIFY(page->action(QWebPage::MoveToStartOfBlock) != 0);
954     QVERIFY(page->action(QWebPage::MoveToEndOfBlock) != 0);
955     QVERIFY(page->action(QWebPage::MoveToStartOfDocument) != 0);
956     QVERIFY(page->action(QWebPage::MoveToEndOfDocument) != 0);
957
958     // right now they are disabled because contentEditable is false
959     QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), false);
960     QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), false);
961     QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), false);
962     QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), false);
963     QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), false);
964     QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), false);
965     QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), false);
966     QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), false);
967     QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), false);
968     QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), false);
969     QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), false);
970     QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), false);
971
972     // make it editable before navigating the cursor
973     page->setContentEditable(true);
974
975     // here the actions are enabled after contentEditable is true
976     QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), true);
977     QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), true);
978     QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), true);
979     QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), true);
980     QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), true);
981     QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), true);
982     QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), true);
983     QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), true);
984     QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), true);
985     QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), true);
986     QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), true);
987     QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), true);
988
989     // cursor will be before the word "jump"
990     page->triggerAction(QWebPage::MoveToNextChar);
991     QVERIFY(page->isSelectionCollapsed());
992     QCOMPARE(page->selectionStartOffset(), 0);
993
994     // cursor will be between 'j' and 'u' in the word "jump"
995     page->triggerAction(QWebPage::MoveToNextChar);
996     QVERIFY(page->isSelectionCollapsed());
997     QCOMPARE(page->selectionStartOffset(), 1);
998
999     // cursor will be between 'u' and 'm' in the word "jump"
1000     page->triggerAction(QWebPage::MoveToNextChar);
1001     QVERIFY(page->isSelectionCollapsed());
1002     QCOMPARE(page->selectionStartOffset(), 2);
1003
1004     // cursor will be after the word "jump"
1005     page->triggerAction(QWebPage::MoveToNextWord);
1006     QVERIFY(page->isSelectionCollapsed());
1007     QCOMPARE(page->selectionStartOffset(), 5);
1008
1009     // cursor will be after the word "lazy"
1010     page->triggerAction(QWebPage::MoveToNextWord);
1011     page->triggerAction(QWebPage::MoveToNextWord);
1012     page->triggerAction(QWebPage::MoveToNextWord);
1013     QVERIFY(page->isSelectionCollapsed());
1014     QCOMPARE(page->selectionStartOffset(), 19);
1015
1016     // cursor will be between 'z' and 'y' in "lazy"
1017     page->triggerAction(QWebPage::MoveToPreviousChar);
1018     QVERIFY(page->isSelectionCollapsed());
1019     QCOMPARE(page->selectionStartOffset(), 18);
1020
1021     // cursor will be between 'a' and 'z' in "lazy"
1022     page->triggerAction(QWebPage::MoveToPreviousChar);
1023     QVERIFY(page->isSelectionCollapsed());
1024     QCOMPARE(page->selectionStartOffset(), 17);
1025
1026     // cursor will be before the word "lazy"
1027     page->triggerAction(QWebPage::MoveToPreviousWord);
1028     QVERIFY(page->isSelectionCollapsed());
1029     QCOMPARE(page->selectionStartOffset(), 15);
1030
1031     // cursor will be before the word "quick"
1032     page->triggerAction(QWebPage::MoveToPreviousWord);
1033     page->triggerAction(QWebPage::MoveToPreviousWord);
1034     page->triggerAction(QWebPage::MoveToPreviousWord);
1035     page->triggerAction(QWebPage::MoveToPreviousWord);
1036     page->triggerAction(QWebPage::MoveToPreviousWord);
1037     page->triggerAction(QWebPage::MoveToPreviousWord);
1038     QVERIFY(page->isSelectionCollapsed());
1039     QCOMPARE(page->selectionStartOffset(), 4);
1040
1041     // cursor will be between 'p' and 's' in the word "jumps"
1042     page->triggerAction(QWebPage::MoveToNextWord);
1043     page->triggerAction(QWebPage::MoveToNextWord);
1044     page->triggerAction(QWebPage::MoveToNextWord);
1045     page->triggerAction(QWebPage::MoveToNextChar);
1046     page->triggerAction(QWebPage::MoveToNextChar);
1047     page->triggerAction(QWebPage::MoveToNextChar);
1048     page->triggerAction(QWebPage::MoveToNextChar);
1049     page->triggerAction(QWebPage::MoveToNextChar);
1050     QVERIFY(page->isSelectionCollapsed());
1051     QCOMPARE(page->selectionStartOffset(), 4);
1052
1053     // cursor will be before the word "jumps"
1054     page->triggerAction(QWebPage::MoveToStartOfLine);
1055     QVERIFY(page->isSelectionCollapsed());
1056     QCOMPARE(page->selectionStartOffset(), 0);
1057
1058     // cursor will be after the word "dog"
1059     page->triggerAction(QWebPage::MoveToEndOfLine);
1060     QVERIFY(page->isSelectionCollapsed());
1061     QCOMPARE(page->selectionStartOffset(), 23);
1062
1063     // cursor will be between 'w' and 'n' in "brown"
1064     page->triggerAction(QWebPage::MoveToStartOfLine);
1065     page->triggerAction(QWebPage::MoveToPreviousWord);
1066     page->triggerAction(QWebPage::MoveToPreviousWord);
1067     page->triggerAction(QWebPage::MoveToNextChar);
1068     page->triggerAction(QWebPage::MoveToNextChar);
1069     page->triggerAction(QWebPage::MoveToNextChar);
1070     page->triggerAction(QWebPage::MoveToNextChar);
1071     QVERIFY(page->isSelectionCollapsed());
1072     QCOMPARE(page->selectionStartOffset(), 14);
1073
1074     // cursor will be after the word "fox"
1075     page->triggerAction(QWebPage::MoveToEndOfLine);
1076     QVERIFY(page->isSelectionCollapsed());
1077     QCOMPARE(page->selectionStartOffset(), 19);
1078
1079     // cursor will be before the word "The"
1080     page->triggerAction(QWebPage::MoveToStartOfDocument);
1081     QVERIFY(page->isSelectionCollapsed());
1082     QCOMPARE(page->selectionStartOffset(), 0);
1083
1084     // cursor will be after the word "you!"
1085     page->triggerAction(QWebPage::MoveToEndOfDocument);
1086     QVERIFY(page->isSelectionCollapsed());
1087     QCOMPARE(page->selectionStartOffset(), 12);
1088
1089     // cursor will be before the word "be"
1090     page->triggerAction(QWebPage::MoveToStartOfBlock);
1091     QVERIFY(page->isSelectionCollapsed());
1092     QCOMPARE(page->selectionStartOffset(), 0);
1093
1094     // cursor will be after the word "you!"
1095     page->triggerAction(QWebPage::MoveToEndOfBlock);
1096     QVERIFY(page->isSelectionCollapsed());
1097     QCOMPARE(page->selectionStartOffset(), 12);
1098
1099     // try to move before the document start
1100     page->triggerAction(QWebPage::MoveToStartOfDocument);
1101     page->triggerAction(QWebPage::MoveToPreviousChar);
1102     QVERIFY(page->isSelectionCollapsed());
1103     QCOMPARE(page->selectionStartOffset(), 0);
1104     page->triggerAction(QWebPage::MoveToStartOfDocument);
1105     page->triggerAction(QWebPage::MoveToPreviousWord);
1106     QVERIFY(page->isSelectionCollapsed());
1107     QCOMPARE(page->selectionStartOffset(), 0);
1108
1109     // try to move past the document end
1110     page->triggerAction(QWebPage::MoveToEndOfDocument);
1111     page->triggerAction(QWebPage::MoveToNextChar);
1112     QVERIFY(page->isSelectionCollapsed());
1113     QCOMPARE(page->selectionStartOffset(), 12);
1114     page->triggerAction(QWebPage::MoveToEndOfDocument);
1115     page->triggerAction(QWebPage::MoveToNextWord);
1116     QVERIFY(page->isSelectionCollapsed());
1117     QCOMPARE(page->selectionStartOffset(), 12);
1118
1119     delete page;
1120 }
1121
1122 void tst_QWebPage::textSelection()
1123 {
1124     CursorTrackedPage* page = new CursorTrackedPage;
1125     QString content("<html><body><p id=one>The quick brown fox</p>" \
1126         "<p id=two>jumps over the lazy dog</p>" \
1127         "<p>May the source<br/>be with you!</p></body></html>");
1128     page->mainFrame()->setHtml(content);
1129
1130     // these actions must exist
1131     QVERIFY(page->action(QWebPage::SelectAll) != 0);
1132     QVERIFY(page->action(QWebPage::SelectNextChar) != 0);
1133     QVERIFY(page->action(QWebPage::SelectPreviousChar) != 0);
1134     QVERIFY(page->action(QWebPage::SelectNextWord) != 0);
1135     QVERIFY(page->action(QWebPage::SelectPreviousWord) != 0);
1136     QVERIFY(page->action(QWebPage::SelectNextLine) != 0);
1137     QVERIFY(page->action(QWebPage::SelectPreviousLine) != 0);
1138     QVERIFY(page->action(QWebPage::SelectStartOfLine) != 0);
1139     QVERIFY(page->action(QWebPage::SelectEndOfLine) != 0);
1140     QVERIFY(page->action(QWebPage::SelectStartOfBlock) != 0);
1141     QVERIFY(page->action(QWebPage::SelectEndOfBlock) != 0);
1142     QVERIFY(page->action(QWebPage::SelectStartOfDocument) != 0);
1143     QVERIFY(page->action(QWebPage::SelectEndOfDocument) != 0);
1144
1145     // right now they are disabled because contentEditable is false and 
1146     // there isn't an existing selection to modify
1147     QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), false);
1148     QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), false);
1149     QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), false);
1150     QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), false);
1151     QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), false);
1152     QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), false);
1153     QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), false);
1154     QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), false);
1155     QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), false);
1156     QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), false);
1157     QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), false);
1158     QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), false);
1159
1160     // ..but SelectAll is awalys enabled
1161     QCOMPARE(page->action(QWebPage::SelectAll)->isEnabled(), true);
1162
1163     // Verify hasSelection returns false since there is no selection yet...
1164     QCOMPARE(page->hasSelection(), false);
1165
1166     // this will select the first paragraph
1167     QString selectScript = "var range = document.createRange(); " \
1168         "var node = document.getElementById(\"one\"); " \
1169         "range.selectNode(node); " \
1170         "getSelection().addRange(range);";
1171     page->mainFrame()->evaluateJavaScript(selectScript);
1172     QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
1173     QCOMPARE(page->selectedHtml().trimmed(), QString::fromLatin1("<span class=\"Apple-style-span\" style=\"border-collapse: separate; color: rgb(0, 0, 0); font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; \"><p id=\"one\">The quick brown fox</p></span>"));
1174
1175     // Make sure hasSelection returns true, since there is selected text now...
1176     QCOMPARE(page->hasSelection(), true);
1177
1178     // here the actions are enabled after a selection has been created
1179     QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true);
1180     QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true);
1181     QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true);
1182     QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true);
1183     QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true);
1184     QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true);
1185     QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true);
1186     QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true);
1187     QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true);
1188     QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true);
1189     QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true);
1190     QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true);
1191
1192     // make it editable before navigating the cursor
1193     page->setContentEditable(true);
1194
1195     // cursor will be before the word "The", this makes sure there is a charet
1196     page->triggerAction(QWebPage::MoveToStartOfDocument);
1197     QVERIFY(page->isSelectionCollapsed());
1198     QCOMPARE(page->selectionStartOffset(), 0);
1199
1200     // here the actions are enabled after contentEditable is true
1201     QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true);
1202     QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true);
1203     QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true);
1204     QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true);
1205     QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true);
1206     QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true);
1207     QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true);
1208     QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true);
1209     QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true);
1210     QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true);
1211     QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true);
1212     QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true);
1213
1214     delete page;
1215 }
1216
1217 void tst_QWebPage::textEditing()
1218 {
1219     CursorTrackedPage* page = new CursorTrackedPage;
1220     QString content("<html><body><p id=one>The quick brown fox</p>" \
1221         "<p id=two>jumps over the lazy dog</p>" \
1222         "<p>May the source<br/>be with you!</p></body></html>");
1223     page->mainFrame()->setHtml(content);
1224
1225     // these actions must exist
1226     QVERIFY(page->action(QWebPage::Cut) != 0);
1227     QVERIFY(page->action(QWebPage::Copy) != 0);
1228     QVERIFY(page->action(QWebPage::Paste) != 0);
1229     QVERIFY(page->action(QWebPage::DeleteStartOfWord) != 0);
1230     QVERIFY(page->action(QWebPage::DeleteEndOfWord) != 0);
1231     QVERIFY(page->action(QWebPage::SetTextDirectionDefault) != 0);
1232     QVERIFY(page->action(QWebPage::SetTextDirectionLeftToRight) != 0);
1233     QVERIFY(page->action(QWebPage::SetTextDirectionRightToLeft) != 0);
1234     QVERIFY(page->action(QWebPage::ToggleBold) != 0);
1235     QVERIFY(page->action(QWebPage::ToggleItalic) != 0);
1236     QVERIFY(page->action(QWebPage::ToggleUnderline) != 0);
1237     QVERIFY(page->action(QWebPage::InsertParagraphSeparator) != 0);
1238     QVERIFY(page->action(QWebPage::InsertLineSeparator) != 0);
1239     QVERIFY(page->action(QWebPage::PasteAndMatchStyle) != 0);
1240     QVERIFY(page->action(QWebPage::RemoveFormat) != 0);
1241     QVERIFY(page->action(QWebPage::ToggleStrikethrough) != 0);
1242     QVERIFY(page->action(QWebPage::ToggleSubscript) != 0);
1243     QVERIFY(page->action(QWebPage::ToggleSuperscript) != 0);
1244     QVERIFY(page->action(QWebPage::InsertUnorderedList) != 0);
1245     QVERIFY(page->action(QWebPage::InsertOrderedList) != 0);
1246     QVERIFY(page->action(QWebPage::Indent) != 0);
1247     QVERIFY(page->action(QWebPage::Outdent) != 0);
1248     QVERIFY(page->action(QWebPage::AlignCenter) != 0);
1249     QVERIFY(page->action(QWebPage::AlignJustified) != 0);
1250     QVERIFY(page->action(QWebPage::AlignLeft) != 0);
1251     QVERIFY(page->action(QWebPage::AlignRight) != 0);
1252
1253     // right now they are disabled because contentEditable is false
1254     QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false);
1255     QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), false);
1256     QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), false);
1257     QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), false);
1258     QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), false);
1259     QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), false);
1260     QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), false);
1261     QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), false);
1262     QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), false);
1263     QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), false);
1264     QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), false);
1265     QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), false);
1266     QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), false);
1267     QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false);
1268     QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), false);
1269     QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), false);
1270     QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), false);
1271     QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), false);
1272     QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), false);
1273     QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), false);
1274     QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), false);
1275     QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), false);
1276     QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), false);
1277     QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), false);
1278     QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), false);
1279
1280     // Select everything
1281     page->triggerAction(QWebPage::SelectAll);
1282
1283     // make sure it is enabled since there is a selection
1284     QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), true);
1285
1286     // make it editable before navigating the cursor
1287     page->setContentEditable(true);
1288
1289     // clear the selection
1290     page->triggerAction(QWebPage::MoveToStartOfDocument);
1291     QVERIFY(page->isSelectionCollapsed());
1292     QCOMPARE(page->selectionStartOffset(), 0);
1293
1294     // make sure it is disabled since there isn't a selection
1295     QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), false);
1296
1297     // here the actions are enabled after contentEditable is true
1298     QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), true);
1299     QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), true);
1300     QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), true);
1301     QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), true);
1302     QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), true);
1303     QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), true);
1304     QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), true);
1305     QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), true);
1306     QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), true);
1307     QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), true);
1308     QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), true);
1309     QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), true);
1310     QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), true);
1311     QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), true);
1312     QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), true);
1313     QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), true);
1314     QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), true);
1315     QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), true);
1316     QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), true);
1317     QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), true);
1318     QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), true);
1319     QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), true);
1320     QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), true);
1321     
1322     // make sure these are disabled since there isn't a selection
1323     QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false);
1324     QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false);
1325     
1326     // make sure everything is selected
1327     page->triggerAction(QWebPage::SelectAll);
1328     
1329     // this is only true if there is an editable selection
1330     QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), true);
1331     QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), true);
1332
1333     delete page;
1334 }
1335
1336 void tst_QWebPage::requestCache()
1337 {
1338     TestPage page;
1339     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
1340
1341     page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
1342     QTRY_COMPARE(loadSpy.count(), 1);
1343     QTRY_COMPARE(page.navigations.count(), 1);
1344
1345     page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me2</a>"));
1346     QTRY_COMPARE(loadSpy.count(), 2);
1347     QTRY_COMPARE(page.navigations.count(), 2);
1348
1349     page.triggerAction(QWebPage::Stop);
1350     QVERIFY(page.history()->canGoBack());
1351     page.triggerAction(QWebPage::Back);
1352
1353     QTRY_COMPARE(loadSpy.count(), 3);
1354     QTRY_COMPARE(page.navigations.count(), 3);
1355     QCOMPARE(page.navigations.at(0).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
1356              (int)QNetworkRequest::PreferNetwork);
1357     QCOMPARE(page.navigations.at(1).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
1358              (int)QNetworkRequest::PreferNetwork);
1359     QCOMPARE(page.navigations.at(2).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
1360              (int)QNetworkRequest::PreferCache);
1361 }
1362
1363 void tst_QWebPage::loadCachedPage()
1364 {
1365     TestPage page;
1366     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
1367     page.settings()->setMaximumPagesInCache(3);
1368
1369     page.mainFrame()->load(QUrl("data:text/html,This is first page"));
1370
1371     QTRY_COMPARE(loadSpy.count(), 1);
1372     QTRY_COMPARE(page.navigations.count(), 1);
1373
1374     QUrl firstPageUrl = page.mainFrame()->url();
1375     page.mainFrame()->load(QUrl("data:text/html,This is second page"));
1376
1377     QTRY_COMPARE(loadSpy.count(), 2);
1378     QTRY_COMPARE(page.navigations.count(), 2);
1379
1380     page.triggerAction(QWebPage::Stop);
1381     QVERIFY(page.history()->canGoBack());
1382
1383     QSignalSpy urlSpy(page.mainFrame(), SIGNAL(urlChanged(QUrl)));
1384     QVERIFY(urlSpy.isValid());
1385
1386     page.triggerAction(QWebPage::Back);
1387     ::waitForSignal(page.mainFrame(), SIGNAL(urlChanged(QUrl)));
1388     QCOMPARE(urlSpy.size(), 1);
1389
1390     QList<QVariant> arguments1 = urlSpy.takeFirst();
1391     QCOMPARE(arguments1.at(0).toUrl(), firstPageUrl);
1392
1393 }
1394 void tst_QWebPage::backActionUpdate()
1395 {
1396     QWebView view;
1397     QWebPage *page = view.page();
1398     QAction *action = page->action(QWebPage::Back);
1399     QVERIFY(!action->isEnabled());
1400     QSignalSpy loadSpy(page, SIGNAL(loadFinished(bool)));
1401     QUrl url = QUrl("qrc:///resources/index.html");
1402     page->mainFrame()->load(url);
1403     QTRY_COMPARE(loadSpy.count(), 1);
1404     QVERIFY(!action->isEnabled());
1405     QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(10, 10));
1406     QTRY_COMPARE(loadSpy.count(), 2);
1407
1408     QVERIFY(action->isEnabled());
1409 }
1410
1411 void frameAtHelper(QWebPage* webPage, QWebFrame* webFrame, QPoint framePosition)
1412 {
1413     if (!webFrame)
1414         return;
1415
1416     framePosition += QPoint(webFrame->pos());
1417     QList<QWebFrame*> children = webFrame->childFrames();
1418     for (int i = 0; i < children.size(); ++i) {
1419         if (children.at(i)->childFrames().size() > 0)
1420             frameAtHelper(webPage, children.at(i), framePosition);
1421
1422         QRect frameRect(children.at(i)->pos() + framePosition, children.at(i)->geometry().size());
1423         QVERIFY(children.at(i) == webPage->frameAt(frameRect.topLeft()));
1424     }
1425 }
1426
1427 void tst_QWebPage::frameAt()
1428 {
1429     QWebView webView;
1430     QWebPage* webPage = webView.page();
1431     QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool)));
1432     QUrl url = QUrl("qrc:///resources/iframe.html");
1433     webPage->mainFrame()->load(url);
1434     QTRY_COMPARE(loadSpy.count(), 1);
1435     frameAtHelper(webPage, webPage->mainFrame(), webPage->mainFrame()->pos());
1436 }
1437
1438 void tst_QWebPage::inputMethods_data()
1439 {
1440     QTest::addColumn<QString>("viewType");
1441     QTest::newRow("QWebView") << "QWebView";
1442     QTest::newRow("QGraphicsWebView") << "QGraphicsWebView";
1443 }
1444
1445 static Qt::InputMethodHints inputMethodHints(QObject* object)
1446 {
1447     if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
1448         return o->inputMethodHints();
1449     if (QWidget* w = qobject_cast<QWidget*>(object))
1450         return w->inputMethodHints();
1451     return Qt::InputMethodHints();
1452 }
1453
1454 static bool inputMethodEnabled(QObject* object)
1455 {
1456     if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
1457         return o->flags() & QGraphicsItem::ItemAcceptsInputMethod;
1458     if (QWidget* w = qobject_cast<QWidget*>(object))
1459         return w->testAttribute(Qt::WA_InputMethodEnabled);
1460     return false;
1461 }
1462
1463 static void clickOnPage(QWebPage* page, const QPoint& position)
1464 {
1465     QMouseEvent evpres(QEvent::MouseButtonPress, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1466     page->event(&evpres);
1467     QMouseEvent evrel(QEvent::MouseButtonRelease, position, Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1468     page->event(&evrel);
1469 }
1470
1471 void tst_QWebPage::inputMethods()
1472 {
1473     QFETCH(QString, viewType);
1474     QWebPage* page = new QWebPage;
1475     QObject* view = 0;
1476     QObject* container = 0;
1477     if (viewType == "QWebView") {
1478         QWebView* wv = new QWebView;
1479         wv->setPage(page);
1480         view = wv;
1481         container = view;
1482     } else if (viewType == "QGraphicsWebView") {
1483         QGraphicsWebView* wv = new QGraphicsWebView;
1484         wv->setPage(page);
1485         view = wv;
1486
1487         QGraphicsView* gv = new QGraphicsView;
1488         QGraphicsScene* scene = new QGraphicsScene(gv);
1489         gv->setScene(scene);
1490         scene->addItem(wv);
1491         wv->setGeometry(QRect(0, 0, 500, 500));
1492
1493         container = gv;
1494     } else
1495         QVERIFY2(false, "Unknown view type");
1496
1497     page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont");
1498     page->mainFrame()->setHtml("<html><body>" \
1499                                             "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/><br>" \
1500                                             "<input type='password'/>" \
1501                                             "</body></html>");
1502     page->mainFrame()->setFocus();
1503
1504     EventSpy viewEventSpy(container);
1505
1506     QWebElementCollection inputs = page->mainFrame()->documentElement().findAll("input");
1507     QPoint textInputCenter = inputs.at(0).geometry().center();
1508
1509     clickOnPage(page, textInputCenter);
1510
1511     // This part of the test checks if the SIP (Software Input Panel) is triggered,
1512     // which normally happens on mobile platforms, when a user input form receives
1513     // a mouse click.
1514     int  inputPanel = 0;
1515     if (viewType == "QWebView") {
1516         if (QWebView* wv = qobject_cast<QWebView*>(view))
1517             inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel);
1518     } else if (viewType == "QGraphicsWebView") {
1519         if (QGraphicsWebView* wv = qobject_cast<QGraphicsWebView*>(view))
1520             inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel);
1521     }
1522
1523     // For non-mobile platforms RequestSoftwareInputPanel event is not called
1524     // because there is no SIP (Software Input Panel) triggered. In the case of a
1525     // mobile platform, an input panel, e.g. virtual keyboard, is usually invoked
1526     // and the RequestSoftwareInputPanel event is called. For these two situations
1527     // this part of the test can verified as the checks below.
1528     if (inputPanel)
1529         QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1530     else
1531         QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1532     viewEventSpy.clear();
1533
1534     clickOnPage(page, textInputCenter);
1535     QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1536
1537     //ImMicroFocus
1538     QVariant variant = page->inputMethodQuery(Qt::ImMicroFocus);
1539     QRect focusRect = variant.toRect();
1540     QVERIFY(inputs.at(0).geometry().contains(variant.toRect().topLeft()));
1541
1542     //ImFont
1543     variant = page->inputMethodQuery(Qt::ImFont);
1544     QFont font = variant.value<QFont>();
1545     QCOMPARE(page->settings()->fontFamily(QWebSettings::SerifFont), font.family());
1546
1547     QList<QInputMethodEvent::Attribute> inputAttributes;
1548
1549     //Insert text.
1550     {
1551         QInputMethodEvent eventText("QtWebKit", inputAttributes);
1552         QSignalSpy signalSpy(page, SIGNAL(microFocusChanged()));
1553         page->event(&eventText);
1554         QCOMPARE(signalSpy.count(), 0);
1555     }
1556
1557     {
1558         QInputMethodEvent eventText("", inputAttributes);
1559         eventText.setCommitString(QString("QtWebKit"), 0, 0);
1560         page->event(&eventText);
1561     }
1562
1563     //ImMaximumTextLength
1564     variant = page->inputMethodQuery(Qt::ImMaximumTextLength);
1565     QCOMPARE(20, variant.toInt());
1566
1567     //Set selection
1568     inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant());
1569     QInputMethodEvent eventSelection("",inputAttributes);
1570     page->event(&eventSelection);
1571
1572     //ImAnchorPosition
1573     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1574     int anchorPosition =  variant.toInt();
1575     QCOMPARE(anchorPosition, 3);
1576
1577     //ImCursorPosition
1578     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1579     int cursorPosition =  variant.toInt();
1580     QCOMPARE(cursorPosition, 5);
1581
1582     //ImCurrentSelection
1583     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1584     QString selectionValue = variant.value<QString>();
1585     QCOMPARE(selectionValue, QString("eb"));
1586
1587     //Set selection with negative length
1588     inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 6, -5, QVariant());
1589     QInputMethodEvent eventSelection3("",inputAttributes);
1590     page->event(&eventSelection3);
1591
1592     //ImAnchorPosition
1593     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1594     anchorPosition =  variant.toInt();
1595     QCOMPARE(anchorPosition, 1);
1596
1597     //ImCursorPosition
1598     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1599     cursorPosition =  variant.toInt();
1600     QCOMPARE(cursorPosition, 6);
1601
1602     //ImCurrentSelection
1603     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1604     selectionValue = variant.value<QString>();
1605     QCOMPARE(selectionValue, QString("tWebK"));
1606
1607     //ImSurroundingText
1608     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1609     QString value = variant.value<QString>();
1610     QCOMPARE(value, QString("QtWebKit"));
1611
1612     {
1613         QList<QInputMethodEvent::Attribute> attributes;
1614         // Clear the selection, so the next test does not clear any contents.
1615         QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
1616         attributes.append(newSelection);
1617         QInputMethodEvent event("composition", attributes);
1618         page->event(&event);
1619     }
1620
1621     // A ongoing composition should not change the surrounding text before it is committed.
1622     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1623     value = variant.value<QString>();
1624     QCOMPARE(value, QString("QtWebKit"));
1625
1626     // Cancel current composition first
1627     inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant());
1628     QInputMethodEvent eventSelection4("", inputAttributes);
1629     page->event(&eventSelection4);
1630
1631     // START - Tests for Selection when the Editor is NOT in Composition mode
1632
1633     // LEFT to RIGHT selection
1634     // Deselect the selection by sending MouseButtonPress events
1635     // This moves the current cursor to the end of the text
1636     clickOnPage(page, textInputCenter);
1637
1638     {
1639         QList<QInputMethodEvent::Attribute> attributes;
1640         QInputMethodEvent event(QString(), attributes);
1641         event.setCommitString("XXX", 0, 0);
1642         page->event(&event);
1643         event.setCommitString(QString(), -2, 2); // Erase two characters.
1644         page->event(&event);
1645         event.setCommitString(QString(), -1, 1); // Erase one character.
1646         page->event(&event);
1647         variant = page->inputMethodQuery(Qt::ImSurroundingText);
1648         value = variant.value<QString>();
1649         QCOMPARE(value, QString("QtWebKit"));
1650     }
1651
1652     //Move to the start of the line
1653     page->triggerAction(QWebPage::MoveToStartOfLine);
1654
1655     QKeyEvent keyRightEventPress(QEvent::KeyPress, Qt::Key_Right, Qt::NoModifier);
1656     QKeyEvent keyRightEventRelease(QEvent::KeyRelease, Qt::Key_Right, Qt::NoModifier);
1657
1658     //Move 2 characters RIGHT
1659     for (int j = 0; j < 2; ++j) {
1660         page->event(&keyRightEventPress);
1661         page->event(&keyRightEventRelease);
1662     }
1663
1664     //Select to the end of the line
1665     page->triggerAction(QWebPage::SelectEndOfLine);
1666
1667     //ImAnchorPosition QtWebKit
1668     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1669     anchorPosition =  variant.toInt();
1670     QCOMPARE(anchorPosition, 2);
1671
1672     //ImCursorPosition
1673     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1674     cursorPosition =  variant.toInt();
1675     QCOMPARE(cursorPosition, 8);
1676
1677     //ImCurrentSelection
1678     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1679     selectionValue = variant.value<QString>();
1680     QCOMPARE(selectionValue, QString("WebKit"));
1681
1682     //RIGHT to LEFT selection
1683     //Deselect the selection (this moves the current cursor to the end of the text)
1684     clickOnPage(page, textInputCenter);
1685
1686     //ImAnchorPosition
1687     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1688     anchorPosition =  variant.toInt();
1689     QCOMPARE(anchorPosition, 8);
1690
1691     //ImCursorPosition
1692     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1693     cursorPosition =  variant.toInt();
1694     QCOMPARE(cursorPosition, 8);
1695
1696     //ImCurrentSelection
1697     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1698     selectionValue = variant.value<QString>();
1699     QCOMPARE(selectionValue, QString(""));
1700
1701     QKeyEvent keyLeftEventPress(QEvent::KeyPress, Qt::Key_Left, Qt::NoModifier);
1702     QKeyEvent keyLeftEventRelease(QEvent::KeyRelease, Qt::Key_Left, Qt::NoModifier);
1703
1704     //Move 2 characters LEFT
1705     for (int i = 0; i < 2; ++i) {
1706         page->event(&keyLeftEventPress);
1707         page->event(&keyLeftEventRelease);
1708     }
1709
1710     //Select to the start of the line
1711     page->triggerAction(QWebPage::SelectStartOfLine);
1712
1713     //ImAnchorPosition
1714     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1715     anchorPosition =  variant.toInt();
1716     QCOMPARE(anchorPosition, 6);
1717
1718     //ImCursorPosition
1719     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1720     cursorPosition =  variant.toInt();
1721     QCOMPARE(cursorPosition, 0);
1722
1723     //ImCurrentSelection
1724     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1725     selectionValue = variant.value<QString>();
1726     QCOMPARE(selectionValue, QString("QtWebK"));
1727
1728     //END - Tests for Selection when the Editor is not in Composition mode
1729
1730     //ImhHiddenText
1731     QPoint passwordInputCenter = inputs.at(1).geometry().center();
1732     clickOnPage(page, passwordInputCenter);
1733
1734     QVERIFY(inputMethodEnabled(view));
1735     QVERIFY(inputMethodHints(view) & Qt::ImhHiddenText);
1736
1737     clickOnPage(page, textInputCenter);
1738     QVERIFY(!(inputMethodHints(view) & Qt::ImhHiddenText));
1739
1740     page->mainFrame()->setHtml("<html><body><p>nothing to input here");
1741     viewEventSpy.clear();
1742
1743     QWebElement para = page->mainFrame()->findFirstElement("p");
1744     clickOnPage(page, para.geometry().center());
1745
1746     QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1747
1748     //START - Test for sending empty QInputMethodEvent
1749     page->mainFrame()->setHtml("<html><body>" \
1750                                             "<input type='text' id='input3' value='QtWebKit2'/>" \
1751                                             "</body></html>");
1752     page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input3'); inputEle.focus(); inputEle.select();");
1753
1754     //Send empty QInputMethodEvent
1755     QInputMethodEvent emptyEvent;
1756     page->event(&emptyEvent);
1757
1758     QString inputValue = page->mainFrame()->evaluateJavaScript("document.getElementById('input3').value").toString();
1759     QCOMPARE(inputValue, QString("QtWebKit2"));
1760     //END - Test for sending empty QInputMethodEvent
1761
1762     page->mainFrame()->setHtml("<html><body>" \
1763                                             "<input type='text' id='input4' value='QtWebKit inputMethod'/>" \
1764                                             "</body></html>");
1765     page->mainFrame()->evaluateJavaScript("var inputEle = document.getElementById('input4'); inputEle.focus(); inputEle.select();");
1766
1767     // Clear the selection, also cancel the ongoing composition if there is one.
1768     {
1769         QList<QInputMethodEvent::Attribute> attributes;
1770         QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
1771         attributes.append(newSelection);
1772         QInputMethodEvent event("", attributes);
1773         page->event(&event);
1774     }
1775
1776     // ImCurrentSelection
1777     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1778     selectionValue = variant.value<QString>();
1779     QCOMPARE(selectionValue, QString(""));
1780
1781     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1782     QString surroundingValue = variant.value<QString>();
1783     QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
1784
1785     // ImAnchorPosition
1786     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1787     anchorPosition =  variant.toInt();
1788     QCOMPARE(anchorPosition, 0);
1789
1790     // ImCursorPosition
1791     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1792     cursorPosition =  variant.toInt();
1793     QCOMPARE(cursorPosition, 0);
1794
1795     // 1. Insert a character to the begining of the line.
1796     // Send temporary text, which makes the editor has composition 'm'.
1797     {
1798         QList<QInputMethodEvent::Attribute> attributes;
1799         QInputMethodEvent event("m", attributes);
1800         page->event(&event);
1801     }
1802
1803     // ImCurrentSelection
1804     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1805     selectionValue = variant.value<QString>();
1806     QCOMPARE(selectionValue, QString(""));
1807
1808     // ImSurroundingText
1809     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1810     surroundingValue = variant.value<QString>();
1811     QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
1812
1813     // ImCursorPosition
1814     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1815     cursorPosition =  variant.toInt();
1816     QCOMPARE(cursorPosition, 0);
1817
1818     // ImAnchorPosition
1819     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1820     anchorPosition =  variant.toInt();
1821     QCOMPARE(anchorPosition, 0);
1822
1823     // Send temporary text, which makes the editor has composition 'n'.
1824     {
1825         QList<QInputMethodEvent::Attribute> attributes;
1826         QInputMethodEvent event("n", attributes);
1827         page->event(&event);
1828     }
1829
1830     // ImCurrentSelection
1831     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1832     selectionValue = variant.value<QString>();
1833     QCOMPARE(selectionValue, QString(""));
1834
1835     // ImSurroundingText
1836     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1837     surroundingValue = variant.value<QString>();
1838     QCOMPARE(surroundingValue, QString("QtWebKit inputMethod"));
1839
1840     // ImCursorPosition
1841     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1842     cursorPosition =  variant.toInt();
1843     QCOMPARE(cursorPosition, 0);
1844
1845     // ImAnchorPosition
1846     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1847     anchorPosition =  variant.toInt();
1848     QCOMPARE(anchorPosition, 0);
1849
1850     // Send commit text, which makes the editor conforms composition.
1851     {
1852         QList<QInputMethodEvent::Attribute> attributes;
1853         QInputMethodEvent event("", attributes);
1854         event.setCommitString("o");
1855         page->event(&event);
1856     }
1857
1858     // ImCurrentSelection
1859     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1860     selectionValue = variant.value<QString>();
1861     QCOMPARE(selectionValue, QString(""));
1862
1863     // ImSurroundingText
1864     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1865     surroundingValue = variant.value<QString>();
1866     QCOMPARE(surroundingValue, QString("oQtWebKit inputMethod"));
1867
1868     // ImCursorPosition
1869     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1870     cursorPosition =  variant.toInt();
1871     QCOMPARE(cursorPosition, 1);
1872
1873     // ImAnchorPosition
1874     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1875     anchorPosition =  variant.toInt();
1876     QCOMPARE(anchorPosition, 1);
1877
1878     // 2. insert a character to the middle of the line.
1879     // Send temporary text, which makes the editor has composition 'd'.
1880     {
1881         QList<QInputMethodEvent::Attribute> attributes;
1882         QInputMethodEvent event("d", 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("oQtWebKit inputMethod"));
1895
1896     // ImCursorPosition
1897     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1898     cursorPosition =  variant.toInt();
1899     QCOMPARE(cursorPosition, 1);
1900
1901     // ImAnchorPosition
1902     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1903     anchorPosition =  variant.toInt();
1904     QCOMPARE(anchorPosition, 1);
1905
1906     // Send commit text, which makes the editor conforms composition.
1907     {
1908         QList<QInputMethodEvent::Attribute> attributes;
1909         QInputMethodEvent event("", attributes);
1910         event.setCommitString("e");
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 inputMethod"));
1923
1924     // ImCursorPosition
1925     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1926     cursorPosition =  variant.toInt();
1927     QCOMPARE(cursorPosition, 2);
1928
1929     // ImAnchorPosition
1930     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1931     anchorPosition =  variant.toInt();
1932     QCOMPARE(anchorPosition, 2);
1933
1934     // 3. Insert a character to the end of the line.
1935     page->triggerAction(QWebPage::MoveToEndOfLine);
1936     
1937     // Send temporary text, which makes the editor has composition 't'.
1938     {
1939         QList<QInputMethodEvent::Attribute> attributes;
1940         QInputMethodEvent event("t", attributes);
1941         page->event(&event);
1942     }
1943
1944     // ImCurrentSelection
1945     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1946     selectionValue = variant.value<QString>();
1947     QCOMPARE(selectionValue, QString(""));
1948
1949     // ImSurroundingText
1950     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1951     surroundingValue = variant.value<QString>();
1952     QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethod"));
1953
1954     // ImCursorPosition
1955     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1956     cursorPosition =  variant.toInt();
1957     QCOMPARE(cursorPosition, 22);
1958
1959     // ImAnchorPosition
1960     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1961     anchorPosition =  variant.toInt();
1962     QCOMPARE(anchorPosition, 22);
1963
1964     // Send commit text, which makes the editor conforms composition.
1965     {
1966         QList<QInputMethodEvent::Attribute> attributes;
1967         QInputMethodEvent event("", attributes);
1968         event.setCommitString("t");
1969         page->event(&event);
1970     }
1971
1972     // ImCurrentSelection
1973     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1974     selectionValue = variant.value<QString>();
1975     QCOMPARE(selectionValue, QString(""));
1976
1977     // ImSurroundingText
1978     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1979     surroundingValue = variant.value<QString>();
1980     QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt"));
1981
1982     // ImCursorPosition
1983     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1984     cursorPosition =  variant.toInt();
1985     QCOMPARE(cursorPosition, 23);
1986
1987     // ImAnchorPosition
1988     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1989     anchorPosition =  variant.toInt();
1990     QCOMPARE(anchorPosition, 23);
1991
1992     // 4. Replace the selection.
1993     page->triggerAction(QWebPage::SelectPreviousWord);
1994
1995     // ImCurrentSelection
1996     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1997     selectionValue = variant.value<QString>();
1998     QCOMPARE(selectionValue, QString("inputMethodt"));
1999
2000     // ImSurroundingText
2001     variant = page->inputMethodQuery(Qt::ImSurroundingText);
2002     surroundingValue = variant.value<QString>();
2003     QCOMPARE(surroundingValue, QString("oeQtWebKit inputMethodt"));
2004
2005     // ImCursorPosition
2006     variant = page->inputMethodQuery(Qt::ImCursorPosition);
2007     cursorPosition =  variant.toInt();
2008     QCOMPARE(cursorPosition, 11);
2009
2010     // ImAnchorPosition
2011     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
2012     anchorPosition =  variant.toInt();
2013     QCOMPARE(anchorPosition, 23);
2014
2015     // Send temporary text, which makes the editor has composition 'w'.
2016     {
2017         QList<QInputMethodEvent::Attribute> attributes;
2018         QInputMethodEvent event("w", attributes);
2019         page->event(&event);
2020     }
2021
2022     // ImCurrentSelection
2023     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
2024     selectionValue = variant.value<QString>();
2025     QCOMPARE(selectionValue, QString(""));
2026
2027     // ImSurroundingText
2028     variant = page->inputMethodQuery(Qt::ImSurroundingText);
2029     surroundingValue = variant.value<QString>();
2030     QCOMPARE(surroundingValue, QString("oeQtWebKit "));
2031
2032     // ImCursorPosition
2033     variant = page->inputMethodQuery(Qt::ImCursorPosition);
2034     cursorPosition =  variant.toInt();
2035     QCOMPARE(cursorPosition, 11);
2036
2037     // ImAnchorPosition
2038     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
2039     anchorPosition =  variant.toInt();
2040     QCOMPARE(anchorPosition, 11);
2041
2042     // Send commit text, which makes the editor conforms composition.
2043     {
2044         QList<QInputMethodEvent::Attribute> attributes;
2045         QInputMethodEvent event("", attributes);
2046         event.setCommitString("2");
2047         page->event(&event);
2048     }
2049
2050     // ImCurrentSelection
2051     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
2052     selectionValue = variant.value<QString>();
2053     QCOMPARE(selectionValue, QString(""));
2054
2055     // ImSurroundingText
2056     variant = page->inputMethodQuery(Qt::ImSurroundingText);
2057     surroundingValue = variant.value<QString>();
2058     QCOMPARE(surroundingValue, QString("oeQtWebKit 2"));
2059
2060     // ImCursorPosition
2061     variant = page->inputMethodQuery(Qt::ImCursorPosition);
2062     cursorPosition =  variant.toInt();
2063     QCOMPARE(cursorPosition, 12);
2064
2065     // ImAnchorPosition
2066     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
2067     anchorPosition =  variant.toInt();
2068     QCOMPARE(anchorPosition, 12);
2069
2070     // Check sending RequestSoftwareInputPanel event
2071     page->mainFrame()->setHtml("<html><body>" \
2072                                             "<input type='text' id='input5' value='QtWebKit inputMethod'/>" \
2073                                             "<div id='btnDiv' onclick='i=document.getElementById(&quot;input5&quot;); i.focus();'>abc</div>"\
2074                                             "</body></html>");
2075     QWebElement inputElement = page->mainFrame()->findFirstElement("div");
2076     clickOnPage(page, inputElement.geometry().center());
2077
2078     QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
2079     delete container;
2080 }
2081
2082 void tst_QWebPage::inputMethodsTextFormat_data()
2083 {
2084     QTest::addColumn<QString>("string");
2085     QTest::addColumn<int>("start");
2086     QTest::addColumn<int>("length");
2087
2088     QTest::newRow("") << QString("") << 0 << 0;
2089     QTest::newRow("Q") << QString("Q") << 0 << 1;
2090     QTest::newRow("Qt") << QString("Qt") << 0 << 1;
2091     QTest::newRow("Qt") << QString("Qt") << 0 << 2;
2092     QTest::newRow("Qt") << QString("Qt") << 1 << 1;
2093     QTest::newRow("Qt ") << QString("Qt ") << 0 << 1;
2094     QTest::newRow("Qt ") << QString("Qt ") << 1 << 1;
2095     QTest::newRow("Qt ") << QString("Qt ") << 2 << 1;
2096     QTest::newRow("Qt ") << QString("Qt ") << 2 << -1;
2097     QTest::newRow("Qt ") << QString("Qt ") << -2 << 3;
2098     QTest::newRow("Qt ") << QString("Qt ") << 0 << 3;
2099     QTest::newRow("Qt by") << QString("Qt by") << 0 << 1;
2100     QTest::newRow("Qt by Nokia") << QString("Qt by Nokia") << 0 << 1;
2101 }
2102
2103
2104 void tst_QWebPage::inputMethodsTextFormat()
2105 {
2106     QWebPage* page = new QWebPage;
2107     QWebView* view = new QWebView;
2108     view->setPage(page);
2109     page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont");
2110     page->mainFrame()->setHtml("<html><body>" \
2111                                             "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/>");
2112     page->mainFrame()->evaluateJavaScript("document.getElementById('input1').focus()");
2113     page->mainFrame()->setFocus();
2114     view->show();
2115
2116     QFETCH(QString, string);
2117     QFETCH(int, start);
2118     QFETCH(int, length);
2119
2120     QList<QInputMethodEvent::Attribute> attrs;
2121     QTextCharFormat format;
2122     format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
2123     format.setUnderlineColor(Qt::red);
2124     attrs.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, length, format));
2125     QInputMethodEvent im(string, attrs);
2126     page->event(&im);
2127
2128     QTest::qWait(1000);
2129
2130     delete view;
2131 }
2132
2133 void tst_QWebPage::protectBindingsRuntimeObjectsFromCollector()
2134 {
2135     QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
2136
2137     PluginPage* newPage = new PluginPage(m_view);
2138     m_view->setPage(newPage);
2139
2140     m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
2141
2142     m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='lineedit' id='mylineedit'/></body></html>"));
2143     QTRY_COMPARE(loadSpy.count(), 1);
2144
2145     newPage->mainFrame()->evaluateJavaScript("function testme(text) { var lineedit = document.getElementById('mylineedit'); lineedit.setText(text); lineedit.selectAll(); }");
2146
2147     newPage->mainFrame()->evaluateJavaScript("testme('foo')");
2148
2149     DumpRenderTreeSupportQt::garbageCollectorCollect();
2150
2151     // don't crash!
2152     newPage->mainFrame()->evaluateJavaScript("testme('bar')");
2153 }
2154
2155 void tst_QWebPage::localURLSchemes()
2156 {
2157     int i = QWebSecurityOrigin::localSchemes().size();
2158
2159     QWebSecurityOrigin::removeLocalScheme("file");
2160     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2161     QWebSecurityOrigin::addLocalScheme("file");
2162     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2163
2164     QWebSecurityOrigin::removeLocalScheme("qrc");
2165     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i - 1);
2166     QWebSecurityOrigin::addLocalScheme("qrc");
2167     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2168
2169     QString myscheme = "myscheme";
2170     QWebSecurityOrigin::addLocalScheme(myscheme);
2171     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i + 1);
2172     QVERIFY(QWebSecurityOrigin::localSchemes().contains(myscheme));
2173     QWebSecurityOrigin::removeLocalScheme(myscheme);
2174     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2175     QWebSecurityOrigin::removeLocalScheme(myscheme);
2176     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
2177 }
2178
2179 static inline bool testFlag(QWebPage& webPage, QWebSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue)
2180 {
2181     webPage.settings()->setAttribute(settingAttribute, settingValue);
2182     return webPage.mainFrame()->evaluateJavaScript(QString("(window.%1 != undefined)").arg(jsObjectName)).toBool();
2183 }
2184
2185 void tst_QWebPage::testOptionalJSObjects()
2186 {
2187     // Once a feature is enabled and the JS object is accessed turning off the setting will not turn off
2188     // the visibility of the JS object any more. For this reason this test uses two QWebPage instances.
2189     // Part of the test is to make sure that the QWebPage instances do not interfere with each other so turning on
2190     // a feature for one instance will not turn it on for another.
2191
2192     QWebPage webPage1;
2193     QWebPage webPage2;
2194
2195     webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
2196     webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
2197
2198     QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
2199     QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
2200     QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", true),  true);
2201     QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
2202     QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
2203     QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), true);
2204
2205     QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false);
2206     QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", true),  true);
2207     QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false);
2208     QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", false), true);
2209 }
2210
2211 void tst_QWebPage::testEnablePersistentStorage()
2212 {
2213     QWebPage webPage;
2214
2215     // By default all persistent options should be disabled
2216     QCOMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), false);
2217     QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), false);
2218     QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), false);
2219     QVERIFY(webPage.settings()->iconDatabasePath().isEmpty());
2220
2221     QWebSettings::enablePersistentStorage();
2222
2223
2224     QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), true);
2225     QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), true);
2226     QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), true);
2227
2228     QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty());
2229     QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty());
2230     QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty());
2231 }
2232
2233 void tst_QWebPage::defaultTextEncoding()
2234 {
2235     QWebFrame* mainFrame = m_page->mainFrame();
2236
2237     QString defaultCharset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2238     QVERIFY(!defaultCharset.isEmpty());
2239     QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), defaultCharset);
2240
2241     m_page->settings()->setDefaultTextEncoding(QString("utf-8"));
2242     QString charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2243     QCOMPARE(charset, QString("utf-8"));
2244     QCOMPARE(m_page->settings()->defaultTextEncoding(), charset);
2245
2246     m_page->settings()->setDefaultTextEncoding(QString());
2247     charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2248     QVERIFY(!charset.isEmpty());
2249     QCOMPARE(charset, defaultCharset);
2250
2251     QWebSettings::globalSettings()->setDefaultTextEncoding(QString("utf-8"));
2252     charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
2253     QCOMPARE(charset, QString("utf-8"));
2254     QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), charset);
2255 }
2256
2257 class ErrorPage : public QWebPage
2258 {
2259 public:
2260
2261     ErrorPage(QWidget* parent = 0): QWebPage(parent)
2262     {
2263     }
2264
2265     virtual bool supportsExtension(Extension extension) const
2266     {
2267         return extension == ErrorPageExtension;
2268     }
2269
2270     virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output)
2271     {
2272         ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output);
2273
2274         errorPage->contentType = "text/html";
2275         errorPage->content = "error";
2276         return true;
2277     }
2278 };
2279
2280 void tst_QWebPage::errorPageExtension()
2281 {
2282     ErrorPage* page = new ErrorPage;
2283     m_view->setPage(page);
2284
2285     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
2286
2287     m_view->setUrl(QUrl("data:text/html,foo"));
2288     QTRY_COMPARE(spyLoadFinished.count(), 1);
2289
2290     page->mainFrame()->setUrl(QUrl("http://non.existent/url"));
2291     QTRY_COMPARE(spyLoadFinished.count(), 2);
2292     QCOMPARE(page->mainFrame()->toPlainText(), QString("error"));
2293     QCOMPARE(page->history()->count(), 2);
2294     QCOMPARE(page->history()->currentItem().url(), QUrl("http://non.existent/url"));
2295     QCOMPARE(page->history()->canGoBack(), true);
2296     QCOMPARE(page->history()->canGoForward(), false);
2297
2298     page->triggerAction(QWebPage::Back);
2299     QTRY_COMPARE(page->history()->canGoBack(), false);
2300     QTRY_COMPARE(page->history()->canGoForward(), true);
2301
2302     page->triggerAction(QWebPage::Forward);
2303     QTRY_COMPARE(page->history()->canGoBack(), true);
2304     QTRY_COMPARE(page->history()->canGoForward(), false);
2305
2306     page->triggerAction(QWebPage::Back);
2307     QTRY_COMPARE(page->history()->canGoBack(), false);
2308     QTRY_COMPARE(page->history()->canGoForward(), true);
2309     QTRY_COMPARE(page->history()->currentItem().url(), QUrl("data:text/html,foo"));
2310
2311     m_view->setPage(0);
2312 }
2313
2314 void tst_QWebPage::errorPageExtensionInIFrames()
2315 {
2316     ErrorPage* page = new ErrorPage;
2317     m_view->setPage(page);
2318
2319     m_view->page()->mainFrame()->load(QUrl(
2320         "data:text/html,"
2321         "<h1>h1</h1>"
2322         "<iframe src='data:text/html,<p/>p'></iframe>"
2323         "<iframe src='http://non.existent/url'></iframe>"));
2324     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
2325     QTRY_COMPARE(spyLoadFinished.count(), 1);
2326
2327     QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("error"));
2328
2329     m_view->setPage(0);
2330 }
2331
2332 void tst_QWebPage::errorPageExtensionInFrameset()
2333 {
2334     ErrorPage* page = new ErrorPage;
2335     m_view->setPage(page);
2336
2337     m_view->load(QUrl("qrc:///resources/index.html"));
2338
2339     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
2340     QTRY_COMPARE(spyLoadFinished.count(), 1);
2341     QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("error"));
2342
2343     m_view->setPage(0);
2344 }
2345
2346 class FriendlyWebPage : public QWebPage
2347 {
2348 public:
2349     friend class tst_QWebPage;
2350 };
2351
2352 void tst_QWebPage::userAgentApplicationName()
2353 {
2354     const QString oldApplicationName = QCoreApplication::applicationName();
2355     FriendlyWebPage page;
2356
2357     const QString applicationNameMarker = QString::fromUtf8("StrangeName\342\210\236");
2358     QCoreApplication::setApplicationName(applicationNameMarker);
2359     QVERIFY(page.userAgentForUrl(QUrl()).contains(applicationNameMarker));
2360
2361     QCoreApplication::setApplicationName(oldApplicationName);
2362 }
2363
2364 void tst_QWebPage::userAgentLocaleChange()
2365 {
2366     FriendlyWebPage page;
2367     m_view->setPage(&page);
2368
2369     const QString markerString = QString::fromLatin1(" nn-NO)");
2370
2371     if (page.userAgentForUrl(QUrl()).contains(markerString))
2372         QSKIP("marker string already present", SkipSingle);
2373
2374     m_view->setLocale(QLocale(QString::fromLatin1("nn_NO")));
2375     QVERIFY(page.userAgentForUrl(QUrl()).contains(markerString));
2376 }
2377
2378 void tst_QWebPage::crashTests_LazyInitializationOfMainFrame()
2379 {
2380     {
2381         QWebPage webPage;
2382     }
2383     {
2384         QWebPage webPage;
2385         webPage.selectedText();
2386     }
2387     {
2388         QWebPage webPage;
2389         webPage.selectedHtml();
2390     }
2391     {
2392         QWebPage webPage;
2393         webPage.triggerAction(QWebPage::Back, true);
2394     }
2395     {
2396         QWebPage webPage;
2397         QPoint pos(10,10);
2398         webPage.updatePositionDependentActions(pos);
2399     }
2400 }
2401
2402 static void takeScreenshot(QWebPage* page)
2403 {
2404     QWebFrame* mainFrame = page->mainFrame();
2405     page->setViewportSize(mainFrame->contentsSize());
2406     QImage image(page->viewportSize(), QImage::Format_ARGB32);
2407     QPainter painter(&image);
2408     mainFrame->render(&painter);
2409     painter.end();
2410 }
2411
2412 void tst_QWebPage::screenshot_data()
2413 {
2414     QTest::addColumn<QString>("html");
2415     QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>";
2416     QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>");
2417     QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode='transparent'></embed></body></html>");
2418 }
2419
2420 void tst_QWebPage::screenshot()
2421 {
2422     if (!QDir(TESTS_SOURCE_DIR).exists())
2423         QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
2424
2425     QDir::setCurrent(TESTS_SOURCE_DIR);
2426
2427     QFETCH(QString, html);
2428     QWebPage* page = new QWebPage;
2429     page->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
2430     QWebFrame* mainFrame = page->mainFrame();
2431     mainFrame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR));
2432     ::waitForSignal(mainFrame, SIGNAL(loadFinished(bool)), 2000);
2433
2434     // take screenshot without a view
2435     takeScreenshot(page);
2436
2437     QWebView* view = new QWebView;
2438     view->setPage(page);
2439
2440     // take screenshot when attached to a view
2441     takeScreenshot(page);
2442
2443     delete page;
2444     delete view;
2445
2446     QDir::setCurrent(QApplication::applicationDirPath());
2447 }
2448
2449 void tst_QWebPage::originatingObjectInNetworkRequests()
2450 {
2451     TestNetworkManager* networkManager = new TestNetworkManager(m_page);
2452     m_page->setNetworkAccessManager(networkManager);
2453     networkManager->requests.clear();
2454
2455     m_view->setHtml(QString("<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
2456                             "<head><meta http-equiv='refresh' content='1'></head>foo \">"
2457                             "<frame src=\"data:text/html,bar\"></frameset>"), QUrl());
2458     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
2459
2460     QCOMPARE(networkManager->requests.count(), 2);
2461
2462     QList<QWebFrame*> childFrames = m_page->mainFrame()->childFrames();
2463     QCOMPARE(childFrames.count(), 2);
2464
2465     for (int i = 0; i < 2; ++i)
2466         QVERIFY(qobject_cast<QWebFrame*>(networkManager->requests.at(i).originatingObject()) == childFrames.at(i));
2467 }
2468
2469 /**
2470  * Test fixups for https://bugs.webkit.org/show_bug.cgi?id=30914
2471  *
2472  * From JS we test the following conditions.
2473  *
2474  *   OK     + QString() => SUCCESS, empty string (but not null)
2475  *   OK     + "text"    => SUCCESS, "text"
2476  *   CANCEL + QString() => CANCEL, null string
2477  *   CANCEL + "text"    => CANCEL, null string
2478  */
2479 class JSPromptPage : public QWebPage {
2480     Q_OBJECT
2481 public:
2482     JSPromptPage()
2483     {}
2484
2485     bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result)
2486     {
2487         if (msg == QLatin1String("test1")) {
2488             *result = QString();
2489             return true;
2490         } else if (msg == QLatin1String("test2")) {
2491             *result = QLatin1String("text");
2492             return true;
2493         } else if (msg == QLatin1String("test3")) {
2494             *result = QString();
2495             return false;
2496         } else if (msg == QLatin1String("test4")) {
2497             *result = QLatin1String("text");
2498             return false;
2499         }
2500
2501         qFatal("Unknown msg.");
2502         return QWebPage::javaScriptPrompt(frame, msg, defaultValue, result);
2503     }
2504 };
2505
2506 void tst_QWebPage::testJSPrompt()
2507 {
2508     JSPromptPage page;
2509     bool res;
2510
2511     // OK + QString()
2512     res = page.mainFrame()->evaluateJavaScript(
2513             "var retval = prompt('test1');"
2514             "retval=='' && retval.length == 0;").toBool();
2515     QVERIFY(res);
2516
2517     // OK + "text"
2518     res = page.mainFrame()->evaluateJavaScript(
2519             "var retval = prompt('test2');"
2520             "retval=='text' && retval.length == 4;").toBool();
2521     QVERIFY(res);
2522
2523     // Cancel + QString()
2524     res = page.mainFrame()->evaluateJavaScript(
2525             "var retval = prompt('test3');"
2526             "retval===null;").toBool();
2527     QVERIFY(res);
2528
2529     // Cancel + "text"
2530     res = page.mainFrame()->evaluateJavaScript(
2531             "var retval = prompt('test4');"
2532             "retval===null;").toBool();
2533     QVERIFY(res);
2534 }
2535
2536 class TestModalPage : public QWebPage
2537 {
2538     Q_OBJECT
2539 public:
2540     TestModalPage(QObject* parent = 0) : QWebPage(parent) {
2541     }
2542     virtual QWebPage* createWindow(WebWindowType) {
2543         QWebPage* page = new TestModalPage();
2544         connect(page, SIGNAL(windowCloseRequested()), page, SLOT(deleteLater()));
2545         return page;
2546     }
2547 };
2548
2549 void tst_QWebPage::showModalDialog()
2550 {
2551     TestModalPage page;
2552     page.mainFrame()->setHtml(QString("<html></html>"));
2553     QString res = page.mainFrame()->evaluateJavaScript("window.showModalDialog('javascript:window.returnValue=dialogArguments; window.close();', 'This is a test');").toString();
2554     QCOMPARE(res, QString("This is a test"));
2555 }
2556
2557 void tst_QWebPage::testStopScheduledPageRefresh()
2558 {    
2559     // Without QWebPage::StopScheduledPageRefresh
2560     QWebPage page1;
2561     page1.setNetworkAccessManager(new TestNetworkManager(&page1));
2562     page1.mainFrame()->setHtml("<html><head>"
2563                                 "<meta http-equiv=\"refresh\"content=\"0;URL=qrc:///resources/index.html\">"
2564                                 "</head><body><h1>Page redirects immediately...</h1>"
2565                                 "</body></html>");
2566     QVERIFY(::waitForSignal(&page1, SIGNAL(loadFinished(bool))));
2567     QTest::qWait(500);
2568     QCOMPARE(page1.mainFrame()->url(), QUrl(QLatin1String("qrc:///resources/index.html")));
2569     
2570     // With QWebPage::StopScheduledPageRefresh
2571     QWebPage page2;
2572     page2.setNetworkAccessManager(new TestNetworkManager(&page2));
2573     page2.mainFrame()->setHtml("<html><head>"
2574                                "<meta http-equiv=\"refresh\"content=\"1;URL=qrc:///resources/index.html\">"
2575                                "</head><body><h1>Page redirect test with 1 sec timeout...</h1>"
2576                                "</body></html>");
2577     page2.triggerAction(QWebPage::StopScheduledPageRefresh);
2578     QTest::qWait(1500);
2579     QCOMPARE(page2.mainFrame()->url().toString(), QLatin1String("about:blank"));
2580 }
2581
2582 void tst_QWebPage::findText()
2583 {
2584     m_view->setHtml(QString("<html><head></head><body><div>foo bar</div></body></html>"));
2585     m_page->triggerAction(QWebPage::SelectAll);
2586     QVERIFY(!m_page->selectedText().isEmpty());
2587     QVERIFY(!m_page->selectedHtml().isEmpty());
2588     m_page->findText("");
2589     QVERIFY(m_page->selectedText().isEmpty());
2590     QVERIFY(m_page->selectedHtml().isEmpty());
2591     QStringList words = (QStringList() << "foo" << "bar");
2592     foreach (QString subString, words) {
2593         m_page->findText(subString, QWebPage::FindWrapsAroundDocument);
2594         QCOMPARE(m_page->selectedText(), subString);
2595         QCOMPARE(m_page->selectedHtml(), QString("<span class=\"Apple-style-span\" style=\"border-collapse: separate; color: rgb(0, 0, 0); font-family: Times; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: 2; text-align: -webkit-auto; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px; -webkit-border-horizontal-spacing: 0px; -webkit-border-vertical-spacing: 0px; -webkit-text-decorations-in-effect: none; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px; font-size: medium; \">%1</span>").arg(subString));
2596         m_page->findText("");
2597         QVERIFY(m_page->selectedText().isEmpty());
2598         QVERIFY(m_page->selectedHtml().isEmpty());
2599     }
2600 }
2601
2602 struct ImageExtensionMap {
2603     const char* extension;
2604     const char* mimeType;
2605 };
2606
2607 static const ImageExtensionMap extensionMap[] = {
2608     { "bmp", "image/bmp" },
2609     { "css", "text/css" },
2610     { "gif", "image/gif" },
2611     { "html", "text/html" },
2612     { "htm", "text/html" },
2613     { "ico", "image/x-icon" },
2614     { "jpeg", "image/jpeg" },
2615     { "jpg", "image/jpeg" },
2616     { "js", "application/x-javascript" },
2617     { "mng", "video/x-mng" },
2618     { "pbm", "image/x-portable-bitmap" },
2619     { "pgm", "image/x-portable-graymap" },
2620     { "pdf", "application/pdf" },
2621     { "png", "image/png" },
2622     { "ppm", "image/x-portable-pixmap" },
2623     { "rss", "application/rss+xml" },
2624     { "svg", "image/svg+xml" },
2625     { "text", "text/plain" },
2626     { "tif", "image/tiff" },
2627     { "tiff", "image/tiff" },
2628     { "txt", "text/plain" },
2629     { "xbm", "image/x-xbitmap" },
2630     { "xml", "text/xml" },
2631     { "xpm", "image/x-xpm" },
2632     { "xsl", "text/xsl" },
2633     { "xhtml", "application/xhtml+xml" },
2634     { "wml", "text/vnd.wap.wml" },
2635     { "wmlc", "application/vnd.wap.wmlc" },
2636     { 0, 0 }
2637 };
2638
2639 static QString getMimeTypeForExtension(const QString &ext)
2640 {
2641     const ImageExtensionMap *e = extensionMap;
2642     while (e->extension) {
2643         if (ext.compare(QLatin1String(e->extension), Qt::CaseInsensitive) == 0)
2644             return QLatin1String(e->mimeType);
2645         ++e;
2646     }
2647
2648     return QString();
2649 }
2650
2651 void tst_QWebPage::supportedContentType()
2652 {
2653    QStringList contentTypes;
2654
2655    // Add supported non image types...
2656    contentTypes << "text/html" << "text/xml" << "text/xsl" << "text/plain" << "text/"
2657                 << "application/xml" << "application/xhtml+xml" << "application/vnd.wap.xhtml+xml"
2658                 << "application/rss+xml" << "application/atom+xml" << "application/json";
2659
2660    // Add supported image types...
2661    Q_FOREACH(const QByteArray& imageType, QImageWriter::supportedImageFormats()) {
2662       const QString mimeType = getMimeTypeForExtension(imageType);
2663       if (!mimeType.isEmpty())
2664           contentTypes << mimeType;
2665    }
2666
2667    // Get the mime types supported by webkit...
2668    const QStringList supportedContentTypes = m_page->supportedContentTypes();
2669
2670    Q_FOREACH(const QString& mimeType, contentTypes)
2671       QVERIFY2(supportedContentTypes.contains(mimeType), QString("'%1' is not a supported content type!").arg(mimeType).toLatin1());
2672       
2673    Q_FOREACH(const QString& mimeType, contentTypes)
2674       QVERIFY2(m_page->supportsContentType(mimeType), QString("Cannot handle content types '%1'!").arg(mimeType).toLatin1());
2675 }
2676
2677 class QtNAMThread : public QThread {
2678 public:
2679     QtNAMThread()
2680     {
2681         m_qnamFuture.reportStarted();
2682     }
2683     ~QtNAMThread()
2684     {
2685         quit();
2686         wait();
2687     }
2688
2689     QFuture<QNetworkAccessManager*> networkAccessManager()
2690     {
2691         return m_qnamFuture.future();
2692     }
2693 protected:
2694     void run()
2695     {
2696         QNetworkAccessManager* qnam = new QNetworkAccessManager;
2697         m_qnamFuture.reportFinished(&qnam);
2698         exec();
2699         delete qnam;
2700     }
2701 private:
2702     QFutureInterface<QNetworkAccessManager*> m_qnamFuture;
2703 };
2704
2705 void tst_QWebPage::networkAccessManagerOnDifferentThread()
2706 {
2707     QtNAMThread qnamThread;
2708     qnamThread.start();
2709     m_page->setNetworkAccessManager(qnamThread.networkAccessManager());
2710     QSignalSpy loadSpy(m_page, SIGNAL(loadFinished(bool)));
2711     QUrl url = QUrl("qrc:///resources/index.html");
2712     m_page->mainFrame()->load(url);
2713     QTRY_COMPARE(loadSpy.count(), 1);
2714     QCOMPARE(m_page->mainFrame()->childFrames()[0]->url(), QUrl("qrc:///resources/frame_a.html"));
2715 }
2716
2717 void tst_QWebPage::navigatorCookieEnabled()
2718 {
2719     m_page->networkAccessManager()->setCookieJar(0);
2720     QVERIFY(!m_page->networkAccessManager()->cookieJar());
2721     QVERIFY(!m_page->mainFrame()->evaluateJavaScript("navigator.cookieEnabled").toBool());
2722
2723     m_page->networkAccessManager()->setCookieJar(new QNetworkCookieJar());
2724     QVERIFY(m_page->networkAccessManager()->cookieJar());
2725     QVERIFY(m_page->mainFrame()->evaluateJavaScript("navigator.cookieEnabled").toBool());
2726 }
2727
2728 #ifdef Q_OS_MAC
2729 void tst_QWebPage::macCopyUnicodeToClipboard()
2730 {
2731     QString unicodeText = QString::fromUtf8("αβγδεζηθικλμπ");
2732     m_page->mainFrame()->setHtml(QString("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /></head><body>%1</body></html>").arg(unicodeText));
2733     m_page->triggerAction(QWebPage::SelectAll);
2734     m_page->triggerAction(QWebPage::Copy);
2735
2736     QString clipboardData = QString::fromUtf8(QApplication::clipboard()->mimeData()->data(QLatin1String("text/html")));
2737
2738     QVERIFY(clipboardData.contains(QLatin1String("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />")));
2739     QVERIFY(clipboardData.contains(unicodeText));
2740 }
2741 #endif
2742
2743 QTEST_MAIN(tst_QWebPage)
2744 #include "tst_qwebpage.moc"