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