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 ¶mNames,
504 const QStringList ¶mValues)
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