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