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