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