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