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