OSDN Git Service

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