0bfe11dc76b617e06a129d611b4db5b2876a8b1d
[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 <QDir>
25 #include <QGraphicsWidget>
26 #include <QLineEdit>
27 #include <QMenu>
28 #include <QPushButton>
29 #include <QtTest/QtTest>
30 #include <QTextCharFormat>
31 #include <qgraphicsscene.h>
32 #include <qgraphicsview.h>
33 #include <qgraphicswebview.h>
34 #include <qnetworkrequest.h>
35 #include <qwebdatabase.h>
36 #include <qwebelement.h>
37 #include <qwebframe.h>
38 #include <qwebhistory.h>
39 #include <qwebpage.h>
40 #include <qwebsecurityorigin.h>
41 #include <qwebview.h>
42
43 class EventSpy : public QObject, public QList<QEvent::Type>
44 {
45     Q_OBJECT
46 public:
47     EventSpy(QObject* objectToSpy)
48     {
49         objectToSpy->installEventFilter(this);
50     }
51
52     virtual bool eventFilter(QObject* receiver, QEvent* event)
53     {
54         append(event->type());
55         return false;
56     }
57 };
58
59 class tst_QWebPage : public QObject
60 {
61     Q_OBJECT
62
63 public:
64     tst_QWebPage();
65     virtual ~tst_QWebPage();
66
67 public slots:
68     void init();
69     void cleanup();
70     void cleanupFiles();
71
72 private slots:
73     void initTestCase();
74     void cleanupTestCase();
75
76     void acceptNavigationRequest();
77     void infiniteLoopJS();
78     void loadFinished();
79     void acceptNavigationRequestWithNewWindow();
80     void userStyleSheet();
81     void modified();
82     void contextMenuCrash();
83     void database();
84     void createPluginWithPluginsEnabled();
85     void createPluginWithPluginsDisabled();
86     void destroyPlugin_data();
87     void destroyPlugin();
88     void createViewlessPlugin_data();
89     void createViewlessPlugin();
90     void multiplePageGroupsAndLocalStorage();
91     void cursorMovements();
92     void textSelection();
93     void textEditing();
94     void backActionUpdate();
95     void frameAt();
96     void requestCache();
97     void protectBindingsRuntimeObjectsFromCollector();
98     void localURLSchemes();
99     void testOptionalJSObjects();
100     void testEnablePersistentStorage();
101     void consoleOutput();
102     void inputMethods_data();
103     void inputMethods();
104     void inputMethodsTextFormat_data();
105     void inputMethodsTextFormat();
106     void defaultTextEncoding();
107     void errorPageExtension();
108     void errorPageExtensionInIFrames();
109     void errorPageExtensionInFrameset();
110
111     void wrt_viewModes();
112
113     void crashTests_LazyInitializationOfMainFrame();
114
115     void screenshot_data();
116     void screenshot();
117
118     void originatingObjectInNetworkRequests();
119     void testJSPrompt();
120     void showModalDialog();
121
122 private:
123     QWebView* m_view;
124     QWebPage* m_page;
125 };
126
127 tst_QWebPage::tst_QWebPage()
128 {
129 }
130
131 tst_QWebPage::~tst_QWebPage()
132 {
133 }
134
135 void tst_QWebPage::init()
136 {
137     m_view = new QWebView();
138     m_page = m_view->page();
139 }
140
141 void tst_QWebPage::cleanup()
142 {
143     delete m_view;
144 }
145
146 void tst_QWebPage::cleanupFiles()
147 {
148     QFile::remove("Databases.db");
149     QDir::current().rmdir("http_www.myexample.com_0");
150     QFile::remove("http_www.myexample.com_0.localstorage");
151 }
152
153 void tst_QWebPage::initTestCase()
154 {
155     cleanupFiles(); // In case there are old files from previous runs
156 }
157
158 void tst_QWebPage::cleanupTestCase()
159 {
160     cleanupFiles(); // Be nice
161 }
162
163 class NavigationRequestOverride : public QWebPage
164 {
165 public:
166     NavigationRequestOverride(QWebView* parent, bool initialValue) : QWebPage(parent), m_acceptNavigationRequest(initialValue) {}
167
168     bool m_acceptNavigationRequest;
169 protected:
170     virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, QWebPage::NavigationType type) {
171         Q_UNUSED(frame);
172         Q_UNUSED(request);
173         Q_UNUSED(type);
174
175         return m_acceptNavigationRequest;
176     }
177 };
178
179 void tst_QWebPage::acceptNavigationRequest()
180 {
181     QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
182
183     NavigationRequestOverride* newPage = new NavigationRequestOverride(m_view, false);
184     m_view->setPage(newPage);
185
186     m_view->setHtml(QString("<html><body><form name='tstform' action='data:text/html,foo'method='get'>"
187                             "<input type='text'><input type='submit'></form></body></html>"), QUrl());
188     QTRY_COMPARE(loadSpy.count(), 1);
189
190     m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
191
192     newPage->m_acceptNavigationRequest = true;
193     m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
194     QTRY_COMPARE(loadSpy.count(), 2);
195
196     QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("foo?"));
197
198     // Restore default page
199     m_view->setPage(0);
200 }
201
202 class JSTestPage : public QWebPage
203 {
204 Q_OBJECT
205 public:
206     JSTestPage(QObject* parent = 0)
207     : QWebPage(parent) {}
208
209 public slots:
210     bool shouldInterruptJavaScript() {
211         return true; 
212     }
213 };
214
215 void tst_QWebPage::infiniteLoopJS()
216 {
217     JSTestPage* newPage = new JSTestPage(m_view);
218     m_view->setPage(newPage);
219     m_view->setHtml(QString("<html><bodytest</body></html>"), QUrl());
220     m_view->page()->mainFrame()->evaluateJavaScript("var run = true;var a = 1;while(run){a++;}");
221 }
222
223 void tst_QWebPage::loadFinished()
224 {
225     qRegisterMetaType<QWebFrame*>("QWebFrame*");
226     qRegisterMetaType<QNetworkRequest*>("QNetworkRequest*");
227     QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted()));
228     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
229
230     m_view->setHtml(QString("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
231                             "<head><meta http-equiv='refresh' content='1'></head>foo \">"
232                             "<frame src=\"data:text/html,bar\"></frameset>"), QUrl());
233     QTRY_COMPARE(spyLoadFinished.count(), 1);
234
235     QTRY_VERIFY(spyLoadStarted.count() > 1);
236     QTRY_VERIFY(spyLoadFinished.count() > 1);
237
238     spyLoadFinished.clear();
239
240     m_view->setHtml(QString("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
241                             "foo \"><frame src=\"data:text/html,bar\"></frameset>"), QUrl());
242     QTRY_COMPARE(spyLoadFinished.count(), 1);
243     QCOMPARE(spyLoadFinished.count(), 1);
244 }
245
246 class ConsolePage : public QWebPage
247 {
248 public:
249     ConsolePage(QObject* parent = 0) : QWebPage(parent) {}
250
251     virtual void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID)
252     {
253         messages.append(message);
254         lineNumbers.append(lineNumber);
255         sourceIDs.append(sourceID);
256     }
257
258     QStringList messages;
259     QList<int> lineNumbers;
260     QStringList sourceIDs;
261 };
262
263 void tst_QWebPage::consoleOutput()
264 {
265     ConsolePage page;
266     page.mainFrame()->evaluateJavaScript("this is not valid JavaScript");
267     QCOMPARE(page.messages.count(), 1);
268     QCOMPARE(page.lineNumbers.at(0), 1);
269 }
270
271 class TestPage : public QWebPage
272 {
273 public:
274     TestPage(QObject* parent = 0) : QWebPage(parent) {}
275
276     struct Navigation {
277         QPointer<QWebFrame> frame;
278         QNetworkRequest request;
279         NavigationType type;
280     };
281
282     QList<Navigation> navigations;
283     QList<QWebPage*> createdWindows;
284
285     virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, NavigationType type) {
286         Navigation n;
287         n.frame = frame;
288         n.request = request;
289         n.type = type;
290         navigations.append(n);
291         return true;
292     }
293
294     virtual QWebPage* createWindow(WebWindowType) {
295         QWebPage* page = new TestPage(this);
296         createdWindows.append(page);
297         return page;
298     }
299 };
300
301 void tst_QWebPage::acceptNavigationRequestWithNewWindow()
302 {
303     TestPage* page = new TestPage(m_view);
304     page->settings()->setAttribute(QWebSettings::LinksIncludedInFocusChain, true);
305     m_page = page;
306     m_view->setPage(m_page);
307
308     m_view->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
309     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
310
311     QFocusEvent fe(QEvent::FocusIn);
312     m_page->event(&fe);
313
314     QVERIFY(m_page->focusNextPrevChild(/*next*/ true));
315
316     QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
317     m_page->event(&keyEnter);
318
319     QCOMPARE(page->navigations.count(), 2);
320
321     TestPage::Navigation n = page->navigations.at(1);
322     QVERIFY(n.frame.isNull());
323     QCOMPARE(n.request.url().toString(), QString("data:text/html,Reached"));
324     QVERIFY(n.type == QWebPage::NavigationTypeLinkClicked);
325
326     QCOMPARE(page->createdWindows.count(), 1);
327 }
328
329 class TestNetworkManager : public QNetworkAccessManager
330 {
331 public:
332     TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {}
333
334     QList<QUrl> requestedUrls;
335     QList<QNetworkRequest> requests;
336
337 protected:
338     virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) {
339         requests.append(request);
340         requestedUrls.append(request.url());
341         return QNetworkAccessManager::createRequest(op, request, outgoingData);
342     }
343 };
344
345 void tst_QWebPage::userStyleSheet()
346 {
347     TestNetworkManager* networkManager = new TestNetworkManager(m_page);
348     m_page->setNetworkAccessManager(networkManager);
349     networkManager->requestedUrls.clear();
350
351     m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css;charset=utf-8;base64,"
352             + QByteArray("p { background-image: url('http://does.not/exist.png');}").toBase64()));
353     m_view->setHtml("<p>hello world</p>");
354     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
355
356     QVERIFY(networkManager->requestedUrls.count() >= 1);
357     QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png"));
358 }
359
360 void tst_QWebPage::wrt_viewModes()
361 {
362     m_view->setHtml("<body></body>");
363     m_page->setProperty("wrt_viewMode", "minimized");
364
365     QVariant empty = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode)\")");
366     QVERIFY(empty.type() == QVariant::Bool && empty.toBool());
367
368     QVariant minimized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: minimized)\")");
369     QVERIFY(minimized.type() == QVariant::Bool && minimized.toBool());
370
371     QVariant maximized = m_page->mainFrame()->evaluateJavaScript("window.styleMedia.matchMedium(\"(-webkit-view-mode: maximized)\")");
372     QVERIFY(maximized.type() == QVariant::Bool && !maximized.toBool());
373 }
374
375 void tst_QWebPage::modified()
376 {
377     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>blub"));
378     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
379
380     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah"));
381     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
382
383     QVERIFY(!m_page->isModified());
384
385 //    m_page->mainFrame()->evaluateJavaScript("alert(document.getElementById('foo'))");
386     m_page->mainFrame()->evaluateJavaScript("document.getElementById('foo').focus()");
387     m_page->mainFrame()->evaluateJavaScript("document.execCommand('InsertText', true, 'Test');");
388
389     QVERIFY(m_page->isModified());
390
391     m_page->mainFrame()->evaluateJavaScript("document.execCommand('Undo', true);");
392
393     QVERIFY(!m_page->isModified());
394
395     m_page->mainFrame()->evaluateJavaScript("document.execCommand('Redo', true);");
396
397     QVERIFY(m_page->isModified());
398
399     QVERIFY(m_page->history()->canGoBack());
400     QVERIFY(!m_page->history()->canGoForward());
401     QCOMPARE(m_page->history()->count(), 2);
402     QVERIFY(m_page->history()->backItem().isValid());
403     QVERIFY(!m_page->history()->forwardItem().isValid());
404
405     m_page->history()->back();
406     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
407
408     QVERIFY(!m_page->history()->canGoBack());
409     QVERIFY(m_page->history()->canGoForward());
410
411     QVERIFY(!m_page->isModified());
412
413     QVERIFY(m_page->history()->currentItemIndex() == 0);
414
415     m_page->history()->setMaximumItemCount(3);
416     QVERIFY(m_page->history()->maximumItemCount() == 3);
417
418     QVariant variant("string test");
419     m_page->history()->currentItem().setUserData(variant);
420     QVERIFY(m_page->history()->currentItem().userData().toString() == "string test");
421
422     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is second page"));
423     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is third page"));
424     QVERIFY(m_page->history()->count() == 2);
425     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fourth page"));
426     QVERIFY(m_page->history()->count() == 2);
427     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fifth page"));
428     QVERIFY(::waitForSignal(m_page, SIGNAL(saveFrameStateRequested(QWebFrame*,QWebHistoryItem*))));
429 }
430
431 void tst_QWebPage::contextMenuCrash()
432 {
433     QWebView view;
434     view.setHtml("<p>test");
435     view.page()->updatePositionDependentActions(QPoint(0, 0));
436     QMenu* contextMenu = 0;
437     foreach (QObject* child, view.children()) {
438         contextMenu = qobject_cast<QMenu*>(child);
439         if (contextMenu)
440             break;
441     }
442     QVERIFY(contextMenu);
443     delete contextMenu;
444 }
445
446 void tst_QWebPage::database()
447 {
448     QString path = QDir::currentPath();
449     m_page->settings()->setOfflineStoragePath(path);
450     QVERIFY(m_page->settings()->offlineStoragePath() == path);
451
452     QWebSettings::setOfflineStorageDefaultQuota(1024 * 1024);
453     QVERIFY(QWebSettings::offlineStorageDefaultQuota() == 1024 * 1024);
454
455     m_page->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
456     m_page->settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true);
457
458     QString dbFileName = path + "Databases.db";
459
460     if (QFile::exists(dbFileName))
461         QFile::remove(dbFileName);
462
463     qRegisterMetaType<QWebFrame*>("QWebFrame*");
464     QSignalSpy spy(m_page, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString)));
465     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"));
466     QTRY_COMPARE(spy.count(), 1);
467     m_page->mainFrame()->evaluateJavaScript("var db2; db2=openDatabase('testdb', '1.0', 'test database API', 50000);");
468     QTRY_COMPARE(spy.count(),1);
469
470     m_page->mainFrame()->evaluateJavaScript("localStorage.test='This is a test for local storage';");
471     m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
472
473     QVariant s1 = m_page->mainFrame()->evaluateJavaScript("localStorage.test");
474     QCOMPARE(s1.toString(), QString("This is a test for local storage"));
475
476     m_page->mainFrame()->evaluateJavaScript("sessionStorage.test='This is a test for session storage';");
477     m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
478     QVariant s2 = m_page->mainFrame()->evaluateJavaScript("sessionStorage.test");
479     QCOMPARE(s2.toString(), QString("This is a test for session storage"));
480
481     m_view->setHtml(QString("<html><head></head><body><div></div></body></html>"), QUrl("http://www.myexample.com"));
482     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) { });");
483     QTest::qWait(200);
484
485     // Remove all databases.
486     QWebSecurityOrigin origin = m_page->mainFrame()->securityOrigin();
487     QList<QWebDatabase> dbs = origin.databases();
488     for (int i = 0; i < dbs.count(); i++) {
489         QString fileName = dbs[i].fileName();
490         QVERIFY(QFile::exists(fileName));
491         QWebDatabase::removeDatabase(dbs[i]);
492         QVERIFY(!QFile::exists(fileName));
493     }
494     QVERIFY(!origin.databases().size());
495     // Remove removed test :-)
496     QWebDatabase::removeAllDatabases();
497     QVERIFY(!origin.databases().size());
498 }
499
500 class PluginPage : public QWebPage
501 {
502 public:
503     PluginPage(QObject *parent = 0)
504         : QWebPage(parent) {}
505
506     struct CallInfo
507     {
508         CallInfo(const QString &c, const QUrl &u,
509                  const QStringList &pn, const QStringList &pv,
510                  QObject *r)
511             : classid(c), url(u), paramNames(pn),
512               paramValues(pv), returnValue(r)
513             {}
514         QString classid;
515         QUrl url;
516         QStringList paramNames;
517         QStringList paramValues;
518         QObject *returnValue;
519     };
520
521     QList<CallInfo> calls;
522
523 protected:
524     virtual QObject *createPlugin(const QString &classid, const QUrl &url,
525                                   const QStringList &paramNames,
526                                   const QStringList &paramValues)
527     {
528         QObject *result = 0;
529         if (classid == "pushbutton")
530             result = new QPushButton();
531         else if (classid == "lineedit")
532             result = new QLineEdit();
533         if (result)
534             result->setObjectName(classid);
535         calls.append(CallInfo(classid, url, paramNames, paramValues, result));
536         return result;
537     }
538 };
539
540 static void createPlugin(QWebView *view)
541 {
542     QSignalSpy loadSpy(view, SIGNAL(loadFinished(bool)));
543
544     PluginPage* newPage = new PluginPage(view);
545     view->setPage(newPage);
546
547     // type has to be application/x-qt-plugin
548     view->setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='pushbutton' id='mybutton'/></body></html>"));
549     QTRY_COMPARE(loadSpy.count(), 1);
550     QCOMPARE(newPage->calls.count(), 0);
551
552     view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>"));
553     QTRY_COMPARE(loadSpy.count(), 2);
554     QCOMPARE(newPage->calls.count(), 1);
555     {
556         PluginPage::CallInfo ci = newPage->calls.takeFirst();
557         QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
558         QCOMPARE(ci.url, QUrl());
559         QCOMPARE(ci.paramNames.count(), 3);
560         QCOMPARE(ci.paramValues.count(), 3);
561         QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
562         QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
563         QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
564         QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton"));
565         QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
566         QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton"));
567         QVERIFY(ci.returnValue != 0);
568         QVERIFY(ci.returnValue->inherits("QPushButton"));
569     }
570     // test JS bindings
571     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mybutton').toString()").toString(),
572              QString::fromLatin1("[object HTMLObjectElement]"));
573     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.toString()").toString(),
574              QString::fromLatin1("[object HTMLObjectElement]"));
575     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.objectName").toString(),
576              QString::fromLatin1("string"));
577     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.objectName").toString(),
578              QString::fromLatin1("pushbutton"));
579     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.clicked").toString(),
580              QString::fromLatin1("function"));
581     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.clicked.toString()").toString(),
582              QString::fromLatin1("function clicked() {\n    [native code]\n}"));
583
584     view->setHtml(QString("<html><body><table>"
585                             "<tr><object type='application/x-qt-plugin' classid='lineedit' id='myedit'/></tr>"
586                             "<tr><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></tr>"
587                             "</table></body></html>"), QUrl("http://foo.bar.baz"));
588     QTRY_COMPARE(loadSpy.count(), 3);
589     QCOMPARE(newPage->calls.count(), 2);
590     {
591         PluginPage::CallInfo ci = newPage->calls.takeFirst();
592         QCOMPARE(ci.classid, QString::fromLatin1("lineedit"));
593         QCOMPARE(ci.url, QUrl());
594         QCOMPARE(ci.paramNames.count(), 3);
595         QCOMPARE(ci.paramValues.count(), 3);
596         QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
597         QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
598         QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
599         QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("lineedit"));
600         QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
601         QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("myedit"));
602         QVERIFY(ci.returnValue != 0);
603         QVERIFY(ci.returnValue->inherits("QLineEdit"));
604     }
605     {
606         PluginPage::CallInfo ci = newPage->calls.takeFirst();
607         QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
608         QCOMPARE(ci.url, QUrl());
609         QCOMPARE(ci.paramNames.count(), 3);
610         QCOMPARE(ci.paramValues.count(), 3);
611         QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
612         QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
613         QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
614         QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton"));
615         QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
616         QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton"));
617         QVERIFY(ci.returnValue != 0);
618         QVERIFY(ci.returnValue->inherits("QPushButton"));
619     }
620 }
621
622 void tst_QWebPage::createPluginWithPluginsEnabled()
623 {
624     m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
625     createPlugin(m_view);
626 }
627
628 void tst_QWebPage::createPluginWithPluginsDisabled()
629 {
630     // Qt Plugins should be loaded by QtWebKit even when PluginsEnabled is
631     // false. The client decides whether a Qt plugin is enabled or not when
632     // it decides whether or not to instantiate it.
633     m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, false);
634     createPlugin(m_view);
635 }
636
637 // Standard base class for template PluginTracerPage. In tests it is used as interface.
638 class PluginCounterPage : public QWebPage {
639 public:
640     int m_count;
641     QPointer<QObject> m_widget;
642     QObject* m_pluginParent;
643     PluginCounterPage(QObject* parent = 0)
644         : QWebPage(parent)
645         , m_count(0)
646         , m_widget(0)
647         , m_pluginParent(0)
648     {
649        settings()->setAttribute(QWebSettings::PluginsEnabled, true);
650     }
651     ~PluginCounterPage()
652     {
653         if (m_pluginParent)
654             m_pluginParent->deleteLater();
655     }
656 };
657
658 template<class T>
659 class PluginTracerPage : public PluginCounterPage {
660 public:
661     PluginTracerPage(QObject* parent = 0)
662         : PluginCounterPage(parent)
663     {
664         // this is a dummy parent object for the created plugin
665         m_pluginParent = new T;
666     }
667     virtual QObject* createPlugin(const QString&, const QUrl&, const QStringList&, const QStringList&)
668     {
669         m_count++;
670         m_widget = new T;
671         // need a cast to the specific type, as QObject::setParent cannot be called,
672         // because it is not virtual. Instead it is necesary to call QWidget::setParent,
673         // which also takes a QWidget* instead of a QObject*. Therefore we need to
674         // upcast to T*, which is a QWidget.
675         static_cast<T*>(m_widget.data())->setParent(static_cast<T*>(m_pluginParent));
676         return m_widget;
677     }
678 };
679
680 class PluginFactory {
681 public:
682     enum FactoredType {QWidgetType, QGraphicsWidgetType};
683     static PluginCounterPage* create(FactoredType type, QObject* parent = 0)
684     {
685         PluginCounterPage* result = 0;
686         switch (type) {
687         case QWidgetType:
688             result = new PluginTracerPage<QWidget>(parent);
689             break;
690         case QGraphicsWidgetType:
691             result = new PluginTracerPage<QGraphicsWidget>(parent);
692             break;
693         default: {/*Oops*/};
694         }
695         return result;
696     }
697
698     static void prepareTestData()
699     {
700         QTest::addColumn<int>("type");
701         QTest::newRow("QWidget") << (int)PluginFactory::QWidgetType;
702         QTest::newRow("QGraphicsWidget") << (int)PluginFactory::QGraphicsWidgetType;
703     }
704 };
705
706 void tst_QWebPage::destroyPlugin_data()
707 {
708     PluginFactory::prepareTestData();
709 }
710
711 void tst_QWebPage::destroyPlugin()
712 {
713     QFETCH(int, type);
714     PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type, m_view);
715     m_view->setPage(page);
716
717     // we create the plugin, so the widget should be constructed
718     QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
719     m_view->setHtml(content);
720     QVERIFY(page->m_widget);
721     QCOMPARE(page->m_count, 1);
722
723     // navigate away, the plugin widget should be destructed
724     m_view->setHtml("<html><body>Hi</body></html>");
725     QTestEventLoop::instance().enterLoop(1);
726     QVERIFY(!page->m_widget);
727 }
728
729 void tst_QWebPage::createViewlessPlugin_data()
730 {
731     PluginFactory::prepareTestData();
732 }
733
734 void tst_QWebPage::createViewlessPlugin()
735 {
736     QFETCH(int, type);
737     PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type);
738     QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
739     page->mainFrame()->setHtml(content);
740     QCOMPARE(page->m_count, 1);
741     QVERIFY(page->m_widget);
742     QVERIFY(page->m_pluginParent);
743     QVERIFY(page->m_widget->parent() == page->m_pluginParent);
744     delete page;
745
746 }
747
748 void tst_QWebPage::multiplePageGroupsAndLocalStorage()
749 {
750     QDir dir(QDir::currentPath());
751     dir.mkdir("path1");
752     dir.mkdir("path2");
753
754     QWebView view1;
755     QWebView view2;
756
757     view1.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
758     view1.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path1"));
759     DumpRenderTreeSupportQt::webPageSetGroupName(view1.page(), "group1");
760     view2.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);    
761     view2.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path2"));
762     DumpRenderTreeSupportQt::webPageSetGroupName(view2.page(), "group2");
763     QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view1.page()), QString("group1"));
764     QCOMPARE(DumpRenderTreeSupportQt::webPageGroupName(view2.page()), QString("group2"));
765
766
767     view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
768     view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
769
770     view1.page()->mainFrame()->evaluateJavaScript("localStorage.test='value1';");
771     view2.page()->mainFrame()->evaluateJavaScript("localStorage.test='value2';");
772
773     view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
774     view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
775
776     QVariant s1 = view1.page()->mainFrame()->evaluateJavaScript("localStorage.test");
777     QCOMPARE(s1.toString(), QString("value1"));
778
779     QVariant s2 = view2.page()->mainFrame()->evaluateJavaScript("localStorage.test");
780     QCOMPARE(s2.toString(), QString("value2"));
781
782     QTest::qWait(1000);
783
784     QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path1/http_www.myexample.com_0.localstorage"));
785     QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path2/http_www.myexample.com_0.localstorage"));
786     dir.rmdir(QDir::toNativeSeparators("./path1"));
787     dir.rmdir(QDir::toNativeSeparators("./path2"));
788 }
789
790 class CursorTrackedPage : public QWebPage
791 {
792 public:
793
794     CursorTrackedPage(QWidget *parent = 0): QWebPage(parent) {
795         setViewportSize(QSize(1024, 768)); // big space
796     }
797
798     QString selectedText() {
799         return mainFrame()->evaluateJavaScript("window.getSelection().toString()").toString();
800     }
801
802     int selectionStartOffset() {
803         return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).startOffset").toInt();
804     }
805
806     int selectionEndOffset() {
807         return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).endOffset").toInt();
808     }
809
810     // true if start offset == end offset, i.e. no selected text
811     int isSelectionCollapsed() {
812         return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).collapsed").toBool();
813     }
814 };
815
816 void tst_QWebPage::cursorMovements()
817 {
818     CursorTrackedPage* page = new CursorTrackedPage;
819     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>");
820     page->mainFrame()->setHtml(content);
821
822     // this will select the first paragraph
823     QString script = "var range = document.createRange(); " \
824         "var node = document.getElementById(\"one\"); " \
825         "range.selectNode(node); " \
826         "getSelection().addRange(range);";
827     page->mainFrame()->evaluateJavaScript(script);
828     QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
829
830     // these actions must exist
831     QVERIFY(page->action(QWebPage::MoveToNextChar) != 0);
832     QVERIFY(page->action(QWebPage::MoveToPreviousChar) != 0);
833     QVERIFY(page->action(QWebPage::MoveToNextWord) != 0);
834     QVERIFY(page->action(QWebPage::MoveToPreviousWord) != 0);
835     QVERIFY(page->action(QWebPage::MoveToNextLine) != 0);
836     QVERIFY(page->action(QWebPage::MoveToPreviousLine) != 0);
837     QVERIFY(page->action(QWebPage::MoveToStartOfLine) != 0);
838     QVERIFY(page->action(QWebPage::MoveToEndOfLine) != 0);
839     QVERIFY(page->action(QWebPage::MoveToStartOfBlock) != 0);
840     QVERIFY(page->action(QWebPage::MoveToEndOfBlock) != 0);
841     QVERIFY(page->action(QWebPage::MoveToStartOfDocument) != 0);
842     QVERIFY(page->action(QWebPage::MoveToEndOfDocument) != 0);
843
844     // right now they are disabled because contentEditable is false
845     QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), false);
846     QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), false);
847     QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), false);
848     QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), false);
849     QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), false);
850     QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), false);
851     QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), false);
852     QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), false);
853     QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), false);
854     QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), false);
855     QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), false);
856     QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), false);
857
858     // make it editable before navigating the cursor
859     page->setContentEditable(true);
860
861     // here the actions are enabled after contentEditable is true
862     QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), true);
863     QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), true);
864     QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), true);
865     QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), true);
866     QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), true);
867     QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), true);
868     QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), true);
869     QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), true);
870     QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), true);
871     QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), true);
872     QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), true);
873     QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), true);
874
875     // cursor will be before the word "jump"
876     page->triggerAction(QWebPage::MoveToNextChar);
877     QVERIFY(page->isSelectionCollapsed());
878     QCOMPARE(page->selectionStartOffset(), 0);
879
880     // cursor will be between 'j' and 'u' in the word "jump"
881     page->triggerAction(QWebPage::MoveToNextChar);
882     QVERIFY(page->isSelectionCollapsed());
883     QCOMPARE(page->selectionStartOffset(), 1);
884
885     // cursor will be between 'u' and 'm' in the word "jump"
886     page->triggerAction(QWebPage::MoveToNextChar);
887     QVERIFY(page->isSelectionCollapsed());
888     QCOMPARE(page->selectionStartOffset(), 2);
889
890     // cursor will be after the word "jump"
891     page->triggerAction(QWebPage::MoveToNextWord);
892     QVERIFY(page->isSelectionCollapsed());
893     QCOMPARE(page->selectionStartOffset(), 5);
894
895     // cursor will be after the word "lazy"
896     page->triggerAction(QWebPage::MoveToNextWord);
897     page->triggerAction(QWebPage::MoveToNextWord);
898     page->triggerAction(QWebPage::MoveToNextWord);
899     QVERIFY(page->isSelectionCollapsed());
900     QCOMPARE(page->selectionStartOffset(), 19);
901
902     // cursor will be between 'z' and 'y' in "lazy"
903     page->triggerAction(QWebPage::MoveToPreviousChar);
904     QVERIFY(page->isSelectionCollapsed());
905     QCOMPARE(page->selectionStartOffset(), 18);
906
907     // cursor will be between 'a' and 'z' in "lazy"
908     page->triggerAction(QWebPage::MoveToPreviousChar);
909     QVERIFY(page->isSelectionCollapsed());
910     QCOMPARE(page->selectionStartOffset(), 17);
911
912     // cursor will be before the word "lazy"
913     page->triggerAction(QWebPage::MoveToPreviousWord);
914     QVERIFY(page->isSelectionCollapsed());
915     QCOMPARE(page->selectionStartOffset(), 15);
916
917     // cursor will be before the word "quick"
918     page->triggerAction(QWebPage::MoveToPreviousWord);
919     page->triggerAction(QWebPage::MoveToPreviousWord);
920     page->triggerAction(QWebPage::MoveToPreviousWord);
921     page->triggerAction(QWebPage::MoveToPreviousWord);
922     page->triggerAction(QWebPage::MoveToPreviousWord);
923     page->triggerAction(QWebPage::MoveToPreviousWord);
924     QVERIFY(page->isSelectionCollapsed());
925     QCOMPARE(page->selectionStartOffset(), 4);
926
927     // cursor will be between 'p' and 's' in the word "jumps"
928     page->triggerAction(QWebPage::MoveToNextWord);
929     page->triggerAction(QWebPage::MoveToNextWord);
930     page->triggerAction(QWebPage::MoveToNextWord);
931     page->triggerAction(QWebPage::MoveToNextChar);
932     page->triggerAction(QWebPage::MoveToNextChar);
933     page->triggerAction(QWebPage::MoveToNextChar);
934     page->triggerAction(QWebPage::MoveToNextChar);
935     page->triggerAction(QWebPage::MoveToNextChar);
936     QVERIFY(page->isSelectionCollapsed());
937     QCOMPARE(page->selectionStartOffset(), 4);
938
939     // cursor will be before the word "jumps"
940     page->triggerAction(QWebPage::MoveToStartOfLine);
941     QVERIFY(page->isSelectionCollapsed());
942     QCOMPARE(page->selectionStartOffset(), 0);
943
944     // cursor will be after the word "dog"
945     page->triggerAction(QWebPage::MoveToEndOfLine);
946     QVERIFY(page->isSelectionCollapsed());
947     QCOMPARE(page->selectionStartOffset(), 23);
948
949     // cursor will be between 'w' and 'n' in "brown"
950     page->triggerAction(QWebPage::MoveToStartOfLine);
951     page->triggerAction(QWebPage::MoveToPreviousWord);
952     page->triggerAction(QWebPage::MoveToPreviousWord);
953     page->triggerAction(QWebPage::MoveToNextChar);
954     page->triggerAction(QWebPage::MoveToNextChar);
955     page->triggerAction(QWebPage::MoveToNextChar);
956     page->triggerAction(QWebPage::MoveToNextChar);
957     QVERIFY(page->isSelectionCollapsed());
958     QCOMPARE(page->selectionStartOffset(), 14);
959
960     // cursor will be after the word "fox"
961     page->triggerAction(QWebPage::MoveToEndOfLine);
962     QVERIFY(page->isSelectionCollapsed());
963     QCOMPARE(page->selectionStartOffset(), 19);
964
965     // cursor will be before the word "The"
966     page->triggerAction(QWebPage::MoveToStartOfDocument);
967     QVERIFY(page->isSelectionCollapsed());
968     QCOMPARE(page->selectionStartOffset(), 0);
969
970     // cursor will be after the word "you!"
971     page->triggerAction(QWebPage::MoveToEndOfDocument);
972     QVERIFY(page->isSelectionCollapsed());
973     QCOMPARE(page->selectionStartOffset(), 12);
974
975     // cursor will be before the word "be"
976     page->triggerAction(QWebPage::MoveToStartOfBlock);
977     QVERIFY(page->isSelectionCollapsed());
978     QCOMPARE(page->selectionStartOffset(), 0);
979
980     // cursor will be after the word "you!"
981     page->triggerAction(QWebPage::MoveToEndOfBlock);
982     QVERIFY(page->isSelectionCollapsed());
983     QCOMPARE(page->selectionStartOffset(), 12);
984
985     // try to move before the document start
986     page->triggerAction(QWebPage::MoveToStartOfDocument);
987     page->triggerAction(QWebPage::MoveToPreviousChar);
988     QVERIFY(page->isSelectionCollapsed());
989     QCOMPARE(page->selectionStartOffset(), 0);
990     page->triggerAction(QWebPage::MoveToStartOfDocument);
991     page->triggerAction(QWebPage::MoveToPreviousWord);
992     QVERIFY(page->isSelectionCollapsed());
993     QCOMPARE(page->selectionStartOffset(), 0);
994
995     // try to move past the document end
996     page->triggerAction(QWebPage::MoveToEndOfDocument);
997     page->triggerAction(QWebPage::MoveToNextChar);
998     QVERIFY(page->isSelectionCollapsed());
999     QCOMPARE(page->selectionStartOffset(), 12);
1000     page->triggerAction(QWebPage::MoveToEndOfDocument);
1001     page->triggerAction(QWebPage::MoveToNextWord);
1002     QVERIFY(page->isSelectionCollapsed());
1003     QCOMPARE(page->selectionStartOffset(), 12);
1004
1005     delete page;
1006 }
1007
1008 void tst_QWebPage::textSelection()
1009 {
1010     CursorTrackedPage* page = new CursorTrackedPage;
1011     QString content("<html><body<p id=one>The quick brown fox</p>" \
1012         "<p id=two>jumps over the lazy dog</p>" \
1013         "<p>May the source<br/>be with you!</p></body></html>");
1014     page->mainFrame()->setHtml(content);
1015
1016     // these actions must exist
1017     QVERIFY(page->action(QWebPage::SelectAll) != 0);
1018     QVERIFY(page->action(QWebPage::SelectNextChar) != 0);
1019     QVERIFY(page->action(QWebPage::SelectPreviousChar) != 0);
1020     QVERIFY(page->action(QWebPage::SelectNextWord) != 0);
1021     QVERIFY(page->action(QWebPage::SelectPreviousWord) != 0);
1022     QVERIFY(page->action(QWebPage::SelectNextLine) != 0);
1023     QVERIFY(page->action(QWebPage::SelectPreviousLine) != 0);
1024     QVERIFY(page->action(QWebPage::SelectStartOfLine) != 0);
1025     QVERIFY(page->action(QWebPage::SelectEndOfLine) != 0);
1026     QVERIFY(page->action(QWebPage::SelectStartOfBlock) != 0);
1027     QVERIFY(page->action(QWebPage::SelectEndOfBlock) != 0);
1028     QVERIFY(page->action(QWebPage::SelectStartOfDocument) != 0);
1029     QVERIFY(page->action(QWebPage::SelectEndOfDocument) != 0);
1030
1031     // right now they are disabled because contentEditable is false and 
1032     // there isn't an existing selection to modify
1033     QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), false);
1034     QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), false);
1035     QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), false);
1036     QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), false);
1037     QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), false);
1038     QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), false);
1039     QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), false);
1040     QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), false);
1041     QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), false);
1042     QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), false);
1043     QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), false);
1044     QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), false);
1045
1046     // ..but SelectAll is awalys enabled
1047     QCOMPARE(page->action(QWebPage::SelectAll)->isEnabled(), true);
1048
1049     // this will select the first paragraph
1050     QString selectScript = "var range = document.createRange(); " \
1051         "var node = document.getElementById(\"one\"); " \
1052         "range.selectNode(node); " \
1053         "getSelection().addRange(range);";
1054     page->mainFrame()->evaluateJavaScript(selectScript);
1055     QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
1056
1057     // here the actions are enabled after a selection has been created
1058     QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true);
1059     QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true);
1060     QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true);
1061     QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true);
1062     QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true);
1063     QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true);
1064     QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true);
1065     QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true);
1066     QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true);
1067     QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true);
1068     QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true);
1069     QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true);
1070
1071     // make it editable before navigating the cursor
1072     page->setContentEditable(true);
1073
1074     // cursor will be before the word "The", this makes sure there is a charet
1075     page->triggerAction(QWebPage::MoveToStartOfDocument);
1076     QVERIFY(page->isSelectionCollapsed());
1077     QCOMPARE(page->selectionStartOffset(), 0);
1078
1079     // here the actions are enabled after contentEditable is true
1080     QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true);
1081     QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true);
1082     QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true);
1083     QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true);
1084     QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true);
1085     QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true);
1086     QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true);
1087     QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true);
1088     QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true);
1089     QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true);
1090     QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true);
1091     QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true);
1092
1093     delete page;
1094 }
1095
1096 void tst_QWebPage::textEditing()
1097 {
1098     CursorTrackedPage* page = new CursorTrackedPage;
1099     QString content("<html><body<p id=one>The quick brown fox</p>" \
1100         "<p id=two>jumps over the lazy dog</p>" \
1101         "<p>May the source<br/>be with you!</p></body></html>");
1102     page->mainFrame()->setHtml(content);
1103
1104     // these actions must exist
1105     QVERIFY(page->action(QWebPage::Cut) != 0);
1106     QVERIFY(page->action(QWebPage::Copy) != 0);
1107     QVERIFY(page->action(QWebPage::Paste) != 0);
1108     QVERIFY(page->action(QWebPage::DeleteStartOfWord) != 0);
1109     QVERIFY(page->action(QWebPage::DeleteEndOfWord) != 0);
1110     QVERIFY(page->action(QWebPage::SetTextDirectionDefault) != 0);
1111     QVERIFY(page->action(QWebPage::SetTextDirectionLeftToRight) != 0);
1112     QVERIFY(page->action(QWebPage::SetTextDirectionRightToLeft) != 0);
1113     QVERIFY(page->action(QWebPage::ToggleBold) != 0);
1114     QVERIFY(page->action(QWebPage::ToggleItalic) != 0);
1115     QVERIFY(page->action(QWebPage::ToggleUnderline) != 0);
1116     QVERIFY(page->action(QWebPage::InsertParagraphSeparator) != 0);
1117     QVERIFY(page->action(QWebPage::InsertLineSeparator) != 0);
1118     QVERIFY(page->action(QWebPage::PasteAndMatchStyle) != 0);
1119     QVERIFY(page->action(QWebPage::RemoveFormat) != 0);
1120     QVERIFY(page->action(QWebPage::ToggleStrikethrough) != 0);
1121     QVERIFY(page->action(QWebPage::ToggleSubscript) != 0);
1122     QVERIFY(page->action(QWebPage::ToggleSuperscript) != 0);
1123     QVERIFY(page->action(QWebPage::InsertUnorderedList) != 0);
1124     QVERIFY(page->action(QWebPage::InsertOrderedList) != 0);
1125     QVERIFY(page->action(QWebPage::Indent) != 0);
1126     QVERIFY(page->action(QWebPage::Outdent) != 0);
1127     QVERIFY(page->action(QWebPage::AlignCenter) != 0);
1128     QVERIFY(page->action(QWebPage::AlignJustified) != 0);
1129     QVERIFY(page->action(QWebPage::AlignLeft) != 0);
1130     QVERIFY(page->action(QWebPage::AlignRight) != 0);
1131
1132     // right now they are disabled because contentEditable is false
1133     QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false);
1134     QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), false);
1135     QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), false);
1136     QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), false);
1137     QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), false);
1138     QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), false);
1139     QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), false);
1140     QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), false);
1141     QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), false);
1142     QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), false);
1143     QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), false);
1144     QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), false);
1145     QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), false);
1146     QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false);
1147     QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), false);
1148     QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), false);
1149     QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), false);
1150     QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), false);
1151     QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), false);
1152     QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), false);
1153     QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), false);
1154     QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), false);
1155     QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), false);
1156     QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), false);
1157     QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), false);
1158
1159     // Select everything
1160     page->triggerAction(QWebPage::SelectAll);
1161
1162     // make sure it is enabled since there is a selection
1163     QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), true);
1164
1165     // make it editable before navigating the cursor
1166     page->setContentEditable(true);
1167
1168     // clear the selection
1169     page->triggerAction(QWebPage::MoveToStartOfDocument);
1170     QVERIFY(page->isSelectionCollapsed());
1171     QCOMPARE(page->selectionStartOffset(), 0);
1172
1173     // make sure it is disabled since there isn't a selection
1174     QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), false);
1175
1176     // here the actions are enabled after contentEditable is true
1177     QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), true);
1178     QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), true);
1179     QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), true);
1180     QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), true);
1181     QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), true);
1182     QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), true);
1183     QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), true);
1184     QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), true);
1185     QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), true);
1186     QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), true);
1187     QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), true);
1188     QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), true);
1189     QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), true);
1190     QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), true);
1191     QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), true);
1192     QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), true);
1193     QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), true);
1194     QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), true);
1195     QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), true);
1196     QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), true);
1197     QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), true);
1198     QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), true);
1199     QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), true);
1200     
1201     // make sure these are disabled since there isn't a selection
1202     QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false);
1203     QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false);
1204     
1205     // make sure everything is selected
1206     page->triggerAction(QWebPage::SelectAll);
1207     
1208     // this is only true if there is an editable selection
1209     QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), true);
1210     QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), true);
1211
1212     delete page;
1213 }
1214
1215 void tst_QWebPage::requestCache()
1216 {
1217     TestPage page;
1218     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
1219
1220     page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
1221     QTRY_COMPARE(loadSpy.count(), 1);
1222     QTRY_COMPARE(page.navigations.count(), 1);
1223
1224     page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me2</a>"));
1225     QTRY_COMPARE(loadSpy.count(), 2);
1226     QTRY_COMPARE(page.navigations.count(), 2);
1227
1228     page.triggerAction(QWebPage::Stop);
1229     QVERIFY(page.history()->canGoBack());
1230     page.triggerAction(QWebPage::Back);
1231
1232     QTRY_COMPARE(loadSpy.count(), 3);
1233     QTRY_COMPARE(page.navigations.count(), 3);
1234     QCOMPARE(page.navigations.at(0).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
1235              (int)QNetworkRequest::PreferNetwork);
1236     QCOMPARE(page.navigations.at(1).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
1237              (int)QNetworkRequest::PreferNetwork);
1238     QCOMPARE(page.navigations.at(2).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
1239              (int)QNetworkRequest::PreferCache);
1240 }
1241
1242 void tst_QWebPage::backActionUpdate()
1243 {
1244     QWebView view;
1245     QWebPage *page = view.page();
1246     QAction *action = page->action(QWebPage::Back);
1247     QVERIFY(!action->isEnabled());
1248     QSignalSpy loadSpy(page, SIGNAL(loadFinished(bool)));
1249     QUrl url = QUrl("qrc:///resources/index.html");
1250     page->mainFrame()->load(url);
1251     QTRY_COMPARE(loadSpy.count(), 1);
1252     QVERIFY(!action->isEnabled());
1253     QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(10, 10));
1254     QTRY_COMPARE(loadSpy.count(), 2);
1255
1256     QVERIFY(action->isEnabled());
1257 }
1258
1259 void frameAtHelper(QWebPage* webPage, QWebFrame* webFrame, QPoint framePosition)
1260 {
1261     if (!webFrame)
1262         return;
1263
1264     framePosition += QPoint(webFrame->pos());
1265     QList<QWebFrame*> children = webFrame->childFrames();
1266     for (int i = 0; i < children.size(); ++i) {
1267         if (children.at(i)->childFrames().size() > 0)
1268             frameAtHelper(webPage, children.at(i), framePosition);
1269
1270         QRect frameRect(children.at(i)->pos() + framePosition, children.at(i)->geometry().size());
1271         QVERIFY(children.at(i) == webPage->frameAt(frameRect.topLeft()));
1272     }
1273 }
1274
1275 void tst_QWebPage::frameAt()
1276 {
1277     QWebView webView;
1278     QWebPage* webPage = webView.page();
1279     QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool)));
1280     QUrl url = QUrl("qrc:///resources/iframe.html");
1281     webPage->mainFrame()->load(url);
1282     QTRY_COMPARE(loadSpy.count(), 1);
1283     frameAtHelper(webPage, webPage->mainFrame(), webPage->mainFrame()->pos());
1284 }
1285
1286 void tst_QWebPage::inputMethods_data()
1287 {
1288     QTest::addColumn<QString>("viewType");
1289     QTest::newRow("QWebView") << "QWebView";
1290 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
1291     QTest::newRow("QGraphicsWebView") << "QGraphicsWebView";
1292 #endif
1293 }
1294
1295 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
1296 static Qt::InputMethodHints inputMethodHints(QObject* object)
1297 {
1298     if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
1299         return o->inputMethodHints();
1300     if (QWidget* w = qobject_cast<QWidget*>(object))
1301         return w->inputMethodHints();
1302     return Qt::InputMethodHints();
1303 }
1304 #endif
1305
1306 static bool inputMethodEnabled(QObject* object)
1307 {
1308 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
1309     if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
1310         return o->flags() & QGraphicsItem::ItemAcceptsInputMethod;
1311 #endif
1312     if (QWidget* w = qobject_cast<QWidget*>(object))
1313         return w->testAttribute(Qt::WA_InputMethodEnabled);
1314     return false;
1315 }
1316
1317 void tst_QWebPage::inputMethods()
1318 {
1319     QFETCH(QString, viewType);
1320     QWebPage* page = new QWebPage;
1321     QObject* view = 0;
1322     QObject* container = 0;
1323     if (viewType == "QWebView") {
1324         QWebView* wv = new QWebView;
1325         wv->setPage(page);
1326         view = wv;
1327         container = view;
1328     }
1329 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
1330     else if (viewType == "QGraphicsWebView") {
1331         QGraphicsWebView* wv = new QGraphicsWebView;
1332         wv->setPage(page);
1333         view = wv;
1334
1335         QGraphicsView* gv = new QGraphicsView;
1336         QGraphicsScene* scene = new QGraphicsScene(gv);
1337         gv->setScene(scene);
1338         scene->addItem(wv);
1339         wv->setGeometry(QRect(0, 0, 500, 500));
1340
1341         container = gv;
1342     }
1343 #endif
1344     else
1345         QVERIFY2(false, "Unknown view type");
1346
1347     page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont");
1348     page->mainFrame()->setHtml("<html><body>" \
1349                                             "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/><br>" \
1350                                             "<input type='password'/>" \
1351                                             "</body></html>");
1352     page->mainFrame()->setFocus();
1353
1354     EventSpy viewEventSpy(container);
1355
1356     QWebElementCollection inputs = page->mainFrame()->documentElement().findAll("input");
1357
1358     QMouseEvent evpres(QEvent::MouseButtonPress, inputs.at(0).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1359     page->event(&evpres);
1360     QMouseEvent evrel(QEvent::MouseButtonRelease, inputs.at(0).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1361     page->event(&evrel);
1362
1363 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
1364     QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1365 #endif
1366     viewEventSpy.clear();
1367
1368     page->event(&evpres);
1369     page->event(&evrel);
1370
1371 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
1372     QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1373 #endif
1374
1375     //ImMicroFocus
1376     QVariant variant = page->inputMethodQuery(Qt::ImMicroFocus);
1377     QRect focusRect = variant.toRect();
1378     QVERIFY(inputs.at(0).geometry().contains(variant.toRect().topLeft()));
1379
1380     //ImFont
1381     variant = page->inputMethodQuery(Qt::ImFont);
1382     QFont font = variant.value<QFont>();
1383     QCOMPARE(page->settings()->fontFamily(QWebSettings::SerifFont), font.family());
1384
1385     QList<QInputMethodEvent::Attribute> inputAttributes;
1386
1387     //Insert text.
1388     {
1389         QInputMethodEvent eventText("QtWebKit", inputAttributes);
1390         QSignalSpy signalSpy(page, SIGNAL(microFocusChanged()));
1391         page->event(&eventText);
1392         QCOMPARE(signalSpy.count(), 0);
1393     }
1394
1395     {
1396         QInputMethodEvent eventText("", inputAttributes);
1397         eventText.setCommitString(QString("QtWebKit"), 0, 0);
1398         page->event(&eventText);
1399     }
1400
1401 #if QT_VERSION >= 0x040600
1402     //ImMaximumTextLength
1403     variant = page->inputMethodQuery(Qt::ImMaximumTextLength);
1404     QCOMPARE(20, variant.toInt());
1405
1406     //Set selection
1407     inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant());
1408     QInputMethodEvent eventSelection("",inputAttributes);
1409     page->event(&eventSelection);
1410
1411     //ImAnchorPosition
1412     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1413     int anchorPosition =  variant.toInt();
1414     QCOMPARE(anchorPosition, 3);
1415
1416     //ImCursorPosition
1417     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1418     int cursorPosition =  variant.toInt();
1419     QCOMPARE(cursorPosition, 5);
1420
1421     //ImCurrentSelection
1422     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1423     QString selectionValue = variant.value<QString>();
1424     QCOMPARE(selectionValue, QString("eb"));
1425
1426     //Cancel current composition first
1427     inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 0, 0, QVariant());
1428     QInputMethodEvent eventSelection2("",inputAttributes);
1429     page->event(&eventSelection2);
1430
1431     //Set selection with negative length
1432     inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 6, -5, QVariant());
1433     QInputMethodEvent eventSelection3("",inputAttributes);
1434     page->event(&eventSelection3);
1435
1436     //ImAnchorPosition
1437     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
1438     anchorPosition =  variant.toInt();
1439     QCOMPARE(anchorPosition, 1);
1440
1441     //ImCursorPosition
1442     variant = page->inputMethodQuery(Qt::ImCursorPosition);
1443     cursorPosition =  variant.toInt();
1444     QCOMPARE(cursorPosition, 6);
1445
1446     //ImCurrentSelection
1447     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
1448     selectionValue = variant.value<QString>();
1449     QCOMPARE(selectionValue, QString("tWebK"));
1450 #endif
1451
1452     //ImSurroundingText
1453     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1454     QString value = variant.value<QString>();
1455     QCOMPARE(value, QString("QtWebKit"));
1456
1457 #if QT_VERSION >= 0x040600
1458     {
1459         QList<QInputMethodEvent::Attribute> attributes;
1460         // Clear the selection, so the next test does not clear any contents.
1461         QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
1462         attributes.append(newSelection);
1463         QInputMethodEvent event("composition", attributes);
1464         page->event(&event);
1465     }
1466
1467     // A ongoing composition should not change the surrounding text before it is committed.
1468     variant = page->inputMethodQuery(Qt::ImSurroundingText);
1469     value = variant.value<QString>();
1470     QCOMPARE(value, QString("QtWebKit"));
1471 #endif
1472
1473     //ImhHiddenText
1474     QMouseEvent evpresPassword(QEvent::MouseButtonPress, inputs.at(1).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1475     page->event(&evpresPassword);
1476     QMouseEvent evrelPassword(QEvent::MouseButtonRelease, inputs.at(1).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1477     page->event(&evrelPassword);
1478
1479     QVERIFY(inputMethodEnabled(view));
1480 #if QT_VERSION >= 0x040600
1481     QVERIFY(inputMethodHints(view) & Qt::ImhHiddenText);
1482
1483     page->event(&evpres);
1484     page->event(&evrel);
1485     QVERIFY(!(inputMethodHints(view) & Qt::ImhHiddenText));
1486 #endif
1487
1488     page->mainFrame()->setHtml("<html><body><p>nothing to input here");
1489     viewEventSpy.clear();
1490
1491     QWebElement para = page->mainFrame()->findFirstElement("p");
1492     {
1493         QMouseEvent evpres(QEvent::MouseButtonPress, para.geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1494         page->event(&evpres);
1495         QMouseEvent evrel(QEvent::MouseButtonRelease, para.geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
1496         page->event(&evrel);
1497     }
1498
1499 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
1500     QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
1501 #endif
1502
1503     delete container;
1504 }
1505
1506 void tst_QWebPage::inputMethodsTextFormat_data()
1507 {
1508     QTest::addColumn<QString>("string");
1509     QTest::addColumn<int>("start");
1510     QTest::addColumn<int>("length");
1511
1512     QTest::newRow("") << QString("") << 0 << 0;
1513     QTest::newRow("Q") << QString("Q") << 0 << 1;
1514     QTest::newRow("Qt") << QString("Qt") << 0 << 1;
1515     QTest::newRow("Qt") << QString("Qt") << 0 << 2;
1516     QTest::newRow("Qt") << QString("Qt") << 1 << 1;
1517     QTest::newRow("Qt ") << QString("Qt ") << 0 << 1;
1518     QTest::newRow("Qt ") << QString("Qt ") << 1 << 1;
1519     QTest::newRow("Qt ") << QString("Qt ") << 2 << 1;
1520     QTest::newRow("Qt ") << QString("Qt ") << 2 << -1;
1521     QTest::newRow("Qt ") << QString("Qt ") << -2 << 3;
1522     QTest::newRow("Qt ") << QString("Qt ") << 0 << 3;
1523     QTest::newRow("Qt by") << QString("Qt by") << 0 << 1;
1524     QTest::newRow("Qt by Nokia") << QString("Qt by Nokia") << 0 << 1;
1525 }
1526
1527
1528 void tst_QWebPage::inputMethodsTextFormat()
1529 {
1530     QWebPage* page = new QWebPage;
1531     QWebView* view = new QWebView;
1532     view->setPage(page);
1533     page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont");
1534     page->mainFrame()->setHtml("<html><body>" \
1535                                             "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/>");
1536     page->mainFrame()->evaluateJavaScript("document.getElementById('input1').focus()");
1537     page->mainFrame()->setFocus();
1538     view->show();
1539
1540     QFETCH(QString, string);
1541     QFETCH(int, start);
1542     QFETCH(int, length);
1543
1544     QList<QInputMethodEvent::Attribute> attrs;
1545     QTextCharFormat format;
1546     format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
1547     format.setUnderlineColor(Qt::red);
1548     attrs.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, start, length, format));
1549     QInputMethodEvent im(string, attrs);
1550     page->event(&im);
1551
1552     QTest::qWait(1000);
1553
1554     delete view;
1555 }
1556
1557 void tst_QWebPage::protectBindingsRuntimeObjectsFromCollector()
1558 {
1559     QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
1560
1561     PluginPage* newPage = new PluginPage(m_view);
1562     m_view->setPage(newPage);
1563
1564     m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
1565
1566     m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='lineedit' id='mylineedit'/></body></html>"));
1567     QTRY_COMPARE(loadSpy.count(), 1);
1568
1569     newPage->mainFrame()->evaluateJavaScript("function testme(text) { var lineedit = document.getElementById('mylineedit'); lineedit.setText(text); lineedit.selectAll(); }");
1570
1571     newPage->mainFrame()->evaluateJavaScript("testme('foo')");
1572
1573     DumpRenderTreeSupportQt::garbageCollectorCollect();
1574
1575     // don't crash!
1576     newPage->mainFrame()->evaluateJavaScript("testme('bar')");
1577 }
1578
1579 void tst_QWebPage::localURLSchemes()
1580 {
1581     int i = QWebSecurityOrigin::localSchemes().size();
1582
1583     QWebSecurityOrigin::removeLocalScheme("file");
1584     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
1585     QWebSecurityOrigin::addLocalScheme("file");
1586     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
1587
1588     QWebSecurityOrigin::removeLocalScheme("qrc");
1589     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i - 1);
1590     QWebSecurityOrigin::addLocalScheme("qrc");
1591     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
1592
1593     QString myscheme = "myscheme";
1594     QWebSecurityOrigin::addLocalScheme(myscheme);
1595     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i + 1);
1596     QVERIFY(QWebSecurityOrigin::localSchemes().contains(myscheme));
1597     QWebSecurityOrigin::removeLocalScheme(myscheme);
1598     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
1599     QWebSecurityOrigin::removeLocalScheme(myscheme);
1600     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
1601 }
1602
1603 static inline bool testFlag(QWebPage& webPage, QWebSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue)
1604 {
1605     webPage.settings()->setAttribute(settingAttribute, settingValue);
1606     return webPage.mainFrame()->evaluateJavaScript(QString("(window.%1 != undefined)").arg(jsObjectName)).toBool();
1607 }
1608
1609 void tst_QWebPage::testOptionalJSObjects()
1610 {
1611     // Once a feature is enabled and the JS object is accessed turning off the setting will not turn off
1612     // the visibility of the JS object any more. For this reason this test uses two QWebPage instances.
1613     // Part of the test is to make sure that the QWebPage instances do not interfere with each other so turning on
1614     // a feature for one instance will not turn it on for another.
1615
1616     QWebPage webPage1;
1617     QWebPage webPage2;
1618
1619     webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
1620     webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
1621
1622     QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
1623     QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
1624     QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", true),  true);
1625     QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
1626     QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
1627     QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), true);
1628
1629     QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false);
1630     QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", true),  true);
1631     QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false);
1632     QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", false), true);
1633 }
1634
1635 void tst_QWebPage::testEnablePersistentStorage()
1636 {
1637     QWebPage webPage;
1638
1639     // By default all persistent options should be disabled
1640     QCOMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), false);
1641     QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), false);
1642     QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), false);
1643     QVERIFY(webPage.settings()->iconDatabasePath().isEmpty());
1644
1645     QWebSettings::enablePersistentStorage();
1646
1647
1648     QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), true);
1649     QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), true);
1650     QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), true);
1651
1652     QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty());
1653     QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty());
1654     QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty());
1655 }
1656
1657 void tst_QWebPage::defaultTextEncoding()
1658 {
1659     QWebFrame* mainFrame = m_page->mainFrame();
1660
1661     QString defaultCharset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
1662     QVERIFY(!defaultCharset.isEmpty());
1663     QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), defaultCharset);
1664
1665     m_page->settings()->setDefaultTextEncoding(QString("utf-8"));
1666     QString charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
1667     QCOMPARE(charset, QString("utf-8"));
1668     QCOMPARE(m_page->settings()->defaultTextEncoding(), charset);
1669
1670     m_page->settings()->setDefaultTextEncoding(QString());
1671     charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
1672     QVERIFY(!charset.isEmpty());
1673     QCOMPARE(charset, defaultCharset);
1674
1675     QWebSettings::globalSettings()->setDefaultTextEncoding(QString("utf-8"));
1676     charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
1677     QCOMPARE(charset, QString("utf-8"));
1678     QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), charset);
1679 }
1680
1681 class ErrorPage : public QWebPage
1682 {
1683 public:
1684
1685     ErrorPage(QWidget* parent = 0): QWebPage(parent)
1686     {
1687     }
1688
1689     virtual bool supportsExtension(Extension extension) const
1690     {
1691         return extension == ErrorPageExtension;
1692     }
1693
1694     virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output)
1695     {
1696         ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output);
1697
1698         errorPage->content = "data:text/html,error";
1699         return true;
1700     }
1701 };
1702
1703 void tst_QWebPage::errorPageExtension()
1704 {
1705     ErrorPage* page = new ErrorPage;
1706     m_view->setPage(page);
1707
1708     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
1709
1710     m_view->setUrl(QUrl("data:text/html,foo"));
1711     QTRY_COMPARE(spyLoadFinished.count(), 1);
1712
1713     page->mainFrame()->setUrl(QUrl("http://non.existent/url"));
1714     QTRY_COMPARE(spyLoadFinished.count(), 2);
1715     QCOMPARE(page->mainFrame()->toPlainText(), QString("data:text/html,error"));
1716     QCOMPARE(page->history()->count(), 2);
1717     QCOMPARE(page->history()->currentItem().url(), QUrl("http://non.existent/url"));
1718     QCOMPARE(page->history()->canGoBack(), true);
1719     QCOMPARE(page->history()->canGoForward(), false);
1720
1721     page->triggerAction(QWebPage::Back);
1722     QTRY_COMPARE(page->history()->canGoBack(), false);
1723     QTRY_COMPARE(page->history()->canGoForward(), true);
1724
1725     page->triggerAction(QWebPage::Forward);
1726     QTRY_COMPARE(page->history()->canGoBack(), true);
1727     QTRY_COMPARE(page->history()->canGoForward(), false);
1728
1729     page->triggerAction(QWebPage::Back);
1730     QTRY_COMPARE(page->history()->canGoBack(), false);
1731     QTRY_COMPARE(page->history()->canGoForward(), true);
1732     QTRY_COMPARE(page->history()->currentItem().url(), QUrl("data:text/html,foo"));
1733
1734     m_view->setPage(0);
1735 }
1736
1737 void tst_QWebPage::errorPageExtensionInIFrames()
1738 {
1739     ErrorPage* page = new ErrorPage;
1740     m_view->setPage(page);
1741
1742     m_view->setHtml(QString("data:text/html,"
1743                             "<h1>h1</h1>"
1744                             "<iframe src='data:text/html,<p/>p'></iframe>"
1745                             "<iframe src='non-existent.html'></iframe>"));
1746     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
1747     QTRY_COMPARE(spyLoadFinished.count(), 1);
1748
1749     QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("data:text/html,error"));
1750
1751     m_view->setPage(0);
1752 }
1753
1754 void tst_QWebPage::errorPageExtensionInFrameset()
1755 {
1756     ErrorPage* page = new ErrorPage;
1757     m_view->setPage(page);
1758
1759     m_view->load(QUrl("qrc:///resources/index.html"));
1760
1761     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
1762     QTRY_COMPARE(spyLoadFinished.count(), 1);
1763     QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("data:text/html,error"));
1764
1765     m_view->setPage(0);
1766 }
1767
1768 void tst_QWebPage::crashTests_LazyInitializationOfMainFrame()
1769 {
1770     {
1771         QWebPage webPage;
1772     }
1773     {
1774         QWebPage webPage;
1775         webPage.selectedText();
1776     }
1777     {
1778         QWebPage webPage;
1779         webPage.triggerAction(QWebPage::Back, true);
1780     }
1781     {
1782         QWebPage webPage;
1783         QPoint pos(10,10);
1784         webPage.updatePositionDependentActions(pos);
1785     }
1786 }
1787
1788 static void takeScreenshot(QWebPage* page)
1789 {
1790     QWebFrame* mainFrame = page->mainFrame();
1791     page->setViewportSize(mainFrame->contentsSize());
1792     QImage image(page->viewportSize(), QImage::Format_ARGB32);
1793     QPainter painter(&image);
1794     mainFrame->render(&painter);
1795     painter.end();
1796 }
1797
1798 void tst_QWebPage::screenshot_data()
1799 {
1800     QTest::addColumn<QString>("html");
1801     QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>";
1802     QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>");
1803     QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode='transparent'></embed></body></html>");
1804 }
1805
1806 void tst_QWebPage::screenshot()
1807 {
1808     if (!QDir(TESTS_SOURCE_DIR).exists())
1809         QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
1810
1811     QDir::setCurrent(TESTS_SOURCE_DIR);
1812
1813     QFETCH(QString, html);
1814     QWebPage* page = new QWebPage;
1815     page->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
1816     QWebFrame* mainFrame = page->mainFrame();
1817     mainFrame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR));
1818     ::waitForSignal(mainFrame, SIGNAL(loadFinished(bool)), 2000);
1819
1820     // take screenshot without a view
1821     takeScreenshot(page);
1822
1823     QWebView* view = new QWebView;
1824     view->setPage(page);
1825
1826     // take screenshot when attached to a view
1827     takeScreenshot(page);
1828
1829     delete page;
1830     delete view;
1831
1832     QDir::setCurrent(QApplication::applicationDirPath());
1833 }
1834
1835 void tst_QWebPage::originatingObjectInNetworkRequests()
1836 {
1837     TestNetworkManager* networkManager = new TestNetworkManager(m_page);
1838     m_page->setNetworkAccessManager(networkManager);
1839     networkManager->requests.clear();
1840
1841     m_view->setHtml(QString("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
1842                             "<head><meta http-equiv='refresh' content='1'></head>foo \">"
1843                             "<frame src=\"data:text/html,bar\"></frameset>"), QUrl());
1844     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
1845
1846     QCOMPARE(networkManager->requests.count(), 2);
1847
1848     QList<QWebFrame*> childFrames = m_page->mainFrame()->childFrames();
1849     QCOMPARE(childFrames.count(), 2);
1850
1851 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
1852     for (int i = 0; i < 2; ++i)
1853         QVERIFY(qobject_cast<QWebFrame*>(networkManager->requests.at(i).originatingObject()) == childFrames.at(i));
1854 #endif
1855 }
1856
1857 /**
1858  * Test fixups for https://bugs.webkit.org/show_bug.cgi?id=30914
1859  *
1860  * From JS we test the following conditions.
1861  *
1862  *   OK     + QString() => SUCCESS, empty string (but not null)
1863  *   OK     + "text"    => SUCCESS, "text"
1864  *   CANCEL + QString() => CANCEL, null string
1865  *   CANCEL + "text"    => CANCEL, null string
1866  */
1867 class JSPromptPage : public QWebPage {
1868     Q_OBJECT
1869 public:
1870     JSPromptPage()
1871     {}
1872
1873     bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result)
1874     {
1875         if (msg == QLatin1String("test1")) {
1876             *result = QString();
1877             return true;
1878         } else if (msg == QLatin1String("test2")) {
1879             *result = QLatin1String("text");
1880             return true;
1881         } else if (msg == QLatin1String("test3")) {
1882             *result = QString();
1883             return false;
1884         } else if (msg == QLatin1String("test4")) {
1885             *result = QLatin1String("text");
1886             return false;
1887         }
1888
1889         qFatal("Unknown msg.");
1890         return QWebPage::javaScriptPrompt(frame, msg, defaultValue, result);
1891     }
1892 };
1893
1894 void tst_QWebPage::testJSPrompt()
1895 {
1896     JSPromptPage page;
1897     bool res;
1898
1899     // OK + QString()
1900     res = page.mainFrame()->evaluateJavaScript(
1901             "var retval = prompt('test1');"
1902             "retval=='' && retval.length == 0;").toBool();
1903     QVERIFY(res);
1904
1905     // OK + "text"
1906     res = page.mainFrame()->evaluateJavaScript(
1907             "var retval = prompt('test2');"
1908             "retval=='text' && retval.length == 4;").toBool();
1909     QVERIFY(res);
1910
1911     // Cancel + QString()
1912     res = page.mainFrame()->evaluateJavaScript(
1913             "var retval = prompt('test3');"
1914             "retval===null;").toBool();
1915     QVERIFY(res);
1916
1917     // Cancel + "text"
1918     res = page.mainFrame()->evaluateJavaScript(
1919             "var retval = prompt('test4');"
1920             "retval===null;").toBool();
1921     QVERIFY(res);
1922 }
1923
1924 class TestModalPage : public QWebPage
1925 {
1926     Q_OBJECT
1927 public:
1928     TestModalPage(QObject* parent = 0) : QWebPage(parent) {
1929     }
1930     virtual QWebPage* createWindow(WebWindowType) {
1931         QWebPage* page = new TestModalPage();
1932         connect(page, SIGNAL(windowCloseRequested()), page, SLOT(deleteLater()));
1933         return page;
1934     }
1935 };
1936
1937 void tst_QWebPage::showModalDialog()
1938 {
1939     TestModalPage page;
1940     page.mainFrame()->setHtml(QString("<html></html>"));
1941     QString res = page.mainFrame()->evaluateJavaScript("window.showModalDialog('javascript:window.returnValue=dialogArguments; window.close();', 'This is a test');").toString();
1942     QCOMPARE(res, QString("This is a test"));
1943 }
1944
1945 QTEST_MAIN(tst_QWebPage)
1946 #include "tst_qwebpage.moc"