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