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