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