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 <QDir>
     24 #include <QGraphicsWidget>
     25 #include <QLineEdit>
     26 #include <QMenu>
     27 #include <QPushButton>
     28 #include <QtTest/QtTest>
     29 #include <qgraphicsscene.h>
     30 #include <qgraphicsview.h>
     31 #include <qgraphicswebview.h>
     32 #include <qnetworkrequest.h>
     33 #include <qwebdatabase.h>
     34 #include <qwebelement.h>
     35 #include <qwebframe.h>
     36 #include <qwebhistory.h>
     37 #include <qwebpage.h>
     38 #include <qwebsecurityorigin.h>
     39 #include <qwebview.h>
     40 
     41 class EventSpy : public QObject, public QList<QEvent::Type>
     42 {
     43     Q_OBJECT
     44 public:
     45     EventSpy(QObject* objectToSpy)
     46     {
     47         objectToSpy->installEventFilter(this);
     48     }
     49 
     50     virtual bool eventFilter(QObject* receiver, QEvent* event)
     51     {
     52         append(event->type());
     53         return false;
     54     }
     55 };
     56 
     57 class tst_QWebPage : public QObject
     58 {
     59     Q_OBJECT
     60 
     61 public:
     62     tst_QWebPage();
     63     virtual ~tst_QWebPage();
     64 
     65 public slots:
     66     void init();
     67     void cleanup();
     68     void cleanupFiles();
     69 
     70 private slots:
     71     void initTestCase();
     72     void cleanupTestCase();
     73 
     74     void acceptNavigationRequest();
     75     void infiniteLoopJS();
     76     void loadFinished();
     77     void acceptNavigationRequestWithNewWindow();
     78     void userStyleSheet();
     79     void modified();
     80     void contextMenuCrash();
     81     void database();
     82     void createPlugin();
     83     void destroyPlugin_data();
     84     void destroyPlugin();
     85     void createViewlessPlugin_data();
     86     void createViewlessPlugin();
     87     void multiplePageGroupsAndLocalStorage();
     88     void cursorMovements();
     89     void textSelection();
     90     void textEditing();
     91     void backActionUpdate();
     92     void frameAt();
     93     void requestCache();
     94     void protectBindingsRuntimeObjectsFromCollector();
     95     void localURLSchemes();
     96     void testOptionalJSObjects();
     97     void testEnablePersistentStorage();
     98     void consoleOutput();
     99     void inputMethods_data();
    100     void inputMethods();
    101     void defaultTextEncoding();
    102     void errorPageExtension();
    103     void errorPageExtensionInIFrames();
    104     void errorPageExtensionInFrameset();
    105 
    106     void crashTests_LazyInitializationOfMainFrame();
    107 
    108     void screenshot_data();
    109     void screenshot();
    110 
    111     void originatingObjectInNetworkRequests();
    112     void testJSPrompt();
    113     void showModalDialog();
    114 
    115 private:
    116     QWebView* m_view;
    117     QWebPage* m_page;
    118 };
    119 
    120 tst_QWebPage::tst_QWebPage()
    121 {
    122 }
    123 
    124 tst_QWebPage::~tst_QWebPage()
    125 {
    126 }
    127 
    128 void tst_QWebPage::init()
    129 {
    130     m_view = new QWebView();
    131     m_page = m_view->page();
    132 }
    133 
    134 void tst_QWebPage::cleanup()
    135 {
    136     delete m_view;
    137 }
    138 
    139 void tst_QWebPage::cleanupFiles()
    140 {
    141     QFile::remove("Databases.db");
    142     QDir::current().rmdir("http_www.myexample.com_0");
    143     QFile::remove("http_www.myexample.com_0.localstorage");
    144 }
    145 
    146 void tst_QWebPage::initTestCase()
    147 {
    148     cleanupFiles(); // In case there are old files from previous runs
    149 }
    150 
    151 void tst_QWebPage::cleanupTestCase()
    152 {
    153     cleanupFiles(); // Be nice
    154 }
    155 
    156 class NavigationRequestOverride : public QWebPage
    157 {
    158 public:
    159     NavigationRequestOverride(QWebView* parent, bool initialValue) : QWebPage(parent), m_acceptNavigationRequest(initialValue) {}
    160 
    161     bool m_acceptNavigationRequest;
    162 protected:
    163     virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, QWebPage::NavigationType type) {
    164         Q_UNUSED(frame);
    165         Q_UNUSED(request);
    166         Q_UNUSED(type);
    167 
    168         return m_acceptNavigationRequest;
    169     }
    170 };
    171 
    172 void tst_QWebPage::acceptNavigationRequest()
    173 {
    174     QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
    175 
    176     NavigationRequestOverride* newPage = new NavigationRequestOverride(m_view, false);
    177     m_view->setPage(newPage);
    178 
    179     m_view->setHtml(QString("<html><body><form name='tstform' action='data:text/html,foo'method='get'>"
    180                             "<input type='text'><input type='submit'></form></body></html>"), QUrl());
    181     QTRY_COMPARE(loadSpy.count(), 1);
    182 
    183     m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
    184 
    185     newPage->m_acceptNavigationRequest = true;
    186     m_view->page()->mainFrame()->evaluateJavaScript("tstform.submit();");
    187     QTRY_COMPARE(loadSpy.count(), 2);
    188 
    189     QCOMPARE(m_view->page()->mainFrame()->toPlainText(), QString("foo?"));
    190 
    191     // Restore default page
    192     m_view->setPage(0);
    193 }
    194 
    195 class JSTestPage : public QWebPage
    196 {
    197 Q_OBJECT
    198 public:
    199     JSTestPage(QObject* parent = 0)
    200     : QWebPage(parent) {}
    201 
    202 public slots:
    203     bool shouldInterruptJavaScript() {
    204         return true;
    205     }
    206 };
    207 
    208 void tst_QWebPage::infiniteLoopJS()
    209 {
    210     JSTestPage* newPage = new JSTestPage(m_view);
    211     m_view->setPage(newPage);
    212     m_view->setHtml(QString("<html><bodytest</body></html>"), QUrl());
    213     m_view->page()->mainFrame()->evaluateJavaScript("var run = true;var a = 1;while(run){a++;}");
    214 }
    215 
    216 void tst_QWebPage::loadFinished()
    217 {
    218     qRegisterMetaType<QWebFrame*>("QWebFrame*");
    219     qRegisterMetaType<QNetworkRequest*>("QNetworkRequest*");
    220     QSignalSpy spyLoadStarted(m_view, SIGNAL(loadStarted()));
    221     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
    222 
    223     m_view->setHtml(QString("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
    224                             "<head><meta http-equiv='refresh' content='1'></head>foo \">"
    225                             "<frame src=\"data:text/html,bar\"></frameset>"), QUrl());
    226     QTRY_COMPARE(spyLoadFinished.count(), 1);
    227 
    228     QTRY_VERIFY(spyLoadStarted.count() > 1);
    229     QTRY_VERIFY(spyLoadFinished.count() > 1);
    230 
    231     spyLoadFinished.clear();
    232 
    233     m_view->setHtml(QString("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
    234                             "foo \"><frame src=\"data:text/html,bar\"></frameset>"), QUrl());
    235     QTRY_COMPARE(spyLoadFinished.count(), 1);
    236     QCOMPARE(spyLoadFinished.count(), 1);
    237 }
    238 
    239 class ConsolePage : public QWebPage
    240 {
    241 public:
    242     ConsolePage(QObject* parent = 0) : QWebPage(parent) {}
    243 
    244     virtual void javaScriptConsoleMessage(const QString& message, int lineNumber, const QString& sourceID)
    245     {
    246         messages.append(message);
    247         lineNumbers.append(lineNumber);
    248         sourceIDs.append(sourceID);
    249     }
    250 
    251     QStringList messages;
    252     QList<int> lineNumbers;
    253     QStringList sourceIDs;
    254 };
    255 
    256 void tst_QWebPage::consoleOutput()
    257 {
    258     ConsolePage page;
    259     page.mainFrame()->evaluateJavaScript("this is not valid JavaScript");
    260     QCOMPARE(page.messages.count(), 1);
    261     QCOMPARE(page.lineNumbers.at(0), 1);
    262 }
    263 
    264 class TestPage : public QWebPage
    265 {
    266 public:
    267     TestPage(QObject* parent = 0) : QWebPage(parent) {}
    268 
    269     struct Navigation {
    270         QPointer<QWebFrame> frame;
    271         QNetworkRequest request;
    272         NavigationType type;
    273     };
    274 
    275     QList<Navigation> navigations;
    276     QList<QWebPage*> createdWindows;
    277 
    278     virtual bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest &request, NavigationType type) {
    279         Navigation n;
    280         n.frame = frame;
    281         n.request = request;
    282         n.type = type;
    283         navigations.append(n);
    284         return true;
    285     }
    286 
    287     virtual QWebPage* createWindow(WebWindowType) {
    288         QWebPage* page = new TestPage(this);
    289         createdWindows.append(page);
    290         return page;
    291     }
    292 };
    293 
    294 void tst_QWebPage::acceptNavigationRequestWithNewWindow()
    295 {
    296     TestPage* page = new TestPage(m_view);
    297     page->settings()->setAttribute(QWebSettings::LinksIncludedInFocusChain, true);
    298     m_page = page;
    299     m_view->setPage(m_page);
    300 
    301     m_view->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
    302     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
    303 
    304     QFocusEvent fe(QEvent::FocusIn);
    305     m_page->event(&fe);
    306 
    307     QVERIFY(m_page->focusNextPrevChild(/*next*/ true));
    308 
    309     QKeyEvent keyEnter(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier);
    310     m_page->event(&keyEnter);
    311 
    312     QCOMPARE(page->navigations.count(), 2);
    313 
    314     TestPage::Navigation n = page->navigations.at(1);
    315     QVERIFY(n.frame.isNull());
    316     QCOMPARE(n.request.url().toString(), QString("data:text/html,Reached"));
    317     QVERIFY(n.type == QWebPage::NavigationTypeLinkClicked);
    318 
    319     QCOMPARE(page->createdWindows.count(), 1);
    320 }
    321 
    322 class TestNetworkManager : public QNetworkAccessManager
    323 {
    324 public:
    325     TestNetworkManager(QObject* parent) : QNetworkAccessManager(parent) {}
    326 
    327     QList<QUrl> requestedUrls;
    328     QList<QNetworkRequest> requests;
    329 
    330 protected:
    331     virtual QNetworkReply* createRequest(Operation op, const QNetworkRequest &request, QIODevice* outgoingData) {
    332         requests.append(request);
    333         requestedUrls.append(request.url());
    334         return QNetworkAccessManager::createRequest(op, request, outgoingData);
    335     }
    336 };
    337 
    338 void tst_QWebPage::userStyleSheet()
    339 {
    340     TestNetworkManager* networkManager = new TestNetworkManager(m_page);
    341     m_page->setNetworkAccessManager(networkManager);
    342     networkManager->requestedUrls.clear();
    343 
    344     m_page->settings()->setUserStyleSheetUrl(QUrl("data:text/css;charset=utf-8;base64,"
    345             + QByteArray("p { background-image: url('http://does.not/exist.png');}").toBase64()));
    346     m_view->setHtml("<p>hello world</p>");
    347     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
    348 
    349     QVERIFY(networkManager->requestedUrls.count() >= 1);
    350     QCOMPARE(networkManager->requestedUrls.at(0), QUrl("http://does.not/exist.png"));
    351 }
    352 
    353 void tst_QWebPage::modified()
    354 {
    355     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>blub"));
    356     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
    357 
    358     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body id=foo contenteditable>blah"));
    359     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
    360 
    361     QVERIFY(!m_page->isModified());
    362 
    363 //    m_page->mainFrame()->evaluateJavaScript("alert(document.getElementById('foo'))");
    364     m_page->mainFrame()->evaluateJavaScript("document.getElementById('foo').focus()");
    365     m_page->mainFrame()->evaluateJavaScript("document.execCommand('InsertText', true, 'Test');");
    366 
    367     QVERIFY(m_page->isModified());
    368 
    369     m_page->mainFrame()->evaluateJavaScript("document.execCommand('Undo', true);");
    370 
    371     QVERIFY(!m_page->isModified());
    372 
    373     m_page->mainFrame()->evaluateJavaScript("document.execCommand('Redo', true);");
    374 
    375     QVERIFY(m_page->isModified());
    376 
    377     QVERIFY(m_page->history()->canGoBack());
    378     QVERIFY(!m_page->history()->canGoForward());
    379     QCOMPARE(m_page->history()->count(), 2);
    380     QVERIFY(m_page->history()->backItem().isValid());
    381     QVERIFY(!m_page->history()->forwardItem().isValid());
    382 
    383     m_page->history()->back();
    384     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
    385 
    386     QVERIFY(!m_page->history()->canGoBack());
    387     QVERIFY(m_page->history()->canGoForward());
    388 
    389     QVERIFY(!m_page->isModified());
    390 
    391     QVERIFY(m_page->history()->currentItemIndex() == 0);
    392 
    393     m_page->history()->setMaximumItemCount(3);
    394     QVERIFY(m_page->history()->maximumItemCount() == 3);
    395 
    396     QVariant variant("string test");
    397     m_page->history()->currentItem().setUserData(variant);
    398     QVERIFY(m_page->history()->currentItem().userData().toString() == "string test");
    399 
    400     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is second page"));
    401     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is third page"));
    402     QVERIFY(m_page->history()->count() == 2);
    403     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fourth page"));
    404     QVERIFY(m_page->history()->count() == 2);
    405     m_page->mainFrame()->setUrl(QUrl("data:text/html,<body>This is fifth page"));
    406     QVERIFY(::waitForSignal(m_page, SIGNAL(saveFrameStateRequested(QWebFrame*,QWebHistoryItem*))));
    407 }
    408 
    409 void tst_QWebPage::contextMenuCrash()
    410 {
    411     QWebView view;
    412     view.setHtml("<p>test");
    413     view.page()->updatePositionDependentActions(QPoint(0, 0));
    414     QMenu* contextMenu = 0;
    415     foreach (QObject* child, view.children()) {
    416         contextMenu = qobject_cast<QMenu*>(child);
    417         if (contextMenu)
    418             break;
    419     }
    420     QVERIFY(contextMenu);
    421     delete contextMenu;
    422 }
    423 
    424 void tst_QWebPage::database()
    425 {
    426     QString path = QDir::currentPath();
    427     m_page->settings()->setOfflineStoragePath(path);
    428     QVERIFY(m_page->settings()->offlineStoragePath() == path);
    429 
    430     QWebSettings::setOfflineStorageDefaultQuota(1024 * 1024);
    431     QVERIFY(QWebSettings::offlineStorageDefaultQuota() == 1024 * 1024);
    432 
    433     m_page->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
    434     m_page->settings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true);
    435 
    436     QString dbFileName = path + "Databases.db";
    437 
    438     if (QFile::exists(dbFileName))
    439         QFile::remove(dbFileName);
    440 
    441     qRegisterMetaType<QWebFrame*>("QWebFrame*");
    442     QSignalSpy spy(m_page, SIGNAL(databaseQuotaExceeded(QWebFrame*,QString)));
    443     m_view->setHtml(QString("<html><head><script>var db; db=openDatabase('testdb', '1.0', 'test database API', 50000); </script></head><body><div></div></body></html>"), QUrl("http://www.myexample.com"));
    444     QTRY_COMPARE(spy.count(), 1);
    445     m_page->mainFrame()->evaluateJavaScript("var db2; db2=openDatabase('testdb', '1.0', 'test database API', 50000);");
    446     QTRY_COMPARE(spy.count(),1);
    447 
    448     m_page->mainFrame()->evaluateJavaScript("localStorage.test='This is a test for local storage';");
    449     m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
    450 
    451     QVariant s1 = m_page->mainFrame()->evaluateJavaScript("localStorage.test");
    452     QCOMPARE(s1.toString(), QString("This is a test for local storage"));
    453 
    454     m_page->mainFrame()->evaluateJavaScript("sessionStorage.test='This is a test for session storage';");
    455     m_view->setHtml(QString("<html><body id='b'>text</body></html>"), QUrl("http://www.myexample.com"));
    456     QVariant s2 = m_page->mainFrame()->evaluateJavaScript("sessionStorage.test");
    457     QCOMPARE(s2.toString(), QString("This is a test for session storage"));
    458 
    459     m_view->setHtml(QString("<html><head></head><body><div></div></body></html>"), QUrl("http://www.myexample.com"));
    460     m_page->mainFrame()->evaluateJavaScript("var db3; db3=openDatabase('testdb', '1.0', 'test database API', 50000);db3.transaction(function(tx) { tx.executeSql('CREATE TABLE IF NOT EXISTS Test (text TEXT)', []); }, function(tx, result) { }, function(tx, error) { });");
    461     QTest::qWait(200);
    462 
    463     // Remove all databases.
    464     QWebSecurityOrigin origin = m_page->mainFrame()->securityOrigin();
    465     QList<QWebDatabase> dbs = origin.databases();
    466     for (int i = 0; i < dbs.count(); i++) {
    467         QString fileName = dbs[i].fileName();
    468         QVERIFY(QFile::exists(fileName));
    469         QWebDatabase::removeDatabase(dbs[i]);
    470         QVERIFY(!QFile::exists(fileName));
    471     }
    472     QVERIFY(!origin.databases().size());
    473     // Remove removed test :-)
    474     QWebDatabase::removeAllDatabases();
    475     QVERIFY(!origin.databases().size());
    476 }
    477 
    478 class PluginPage : public QWebPage
    479 {
    480 public:
    481     PluginPage(QObject *parent = 0)
    482         : QWebPage(parent) {}
    483 
    484     struct CallInfo
    485     {
    486         CallInfo(const QString &c, const QUrl &u,
    487                  const QStringList &pn, const QStringList &pv,
    488                  QObject *r)
    489             : classid(c), url(u), paramNames(pn),
    490               paramValues(pv), returnValue(r)
    491             {}
    492         QString classid;
    493         QUrl url;
    494         QStringList paramNames;
    495         QStringList paramValues;
    496         QObject *returnValue;
    497     };
    498 
    499     QList<CallInfo> calls;
    500 
    501 protected:
    502     virtual QObject *createPlugin(const QString &classid, const QUrl &url,
    503                                   const QStringList &paramNames,
    504                                   const QStringList &paramValues)
    505     {
    506         QObject *result = 0;
    507         if (classid == "pushbutton")
    508             result = new QPushButton();
    509         else if (classid == "lineedit")
    510             result = new QLineEdit();
    511         if (result)
    512             result->setObjectName(classid);
    513         calls.append(CallInfo(classid, url, paramNames, paramValues, result));
    514         return result;
    515     }
    516 };
    517 
    518 void tst_QWebPage::createPlugin()
    519 {
    520     QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
    521 
    522     PluginPage* newPage = new PluginPage(m_view);
    523     m_view->setPage(newPage);
    524 
    525     // plugins not enabled by default, so the plugin shouldn't be loaded
    526     m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>"));
    527     QTRY_COMPARE(loadSpy.count(), 1);
    528     QCOMPARE(newPage->calls.count(), 0);
    529 
    530     m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
    531 
    532     // type has to be application/x-qt-plugin
    533     m_view->setHtml(QString("<html><body><object type='application/x-foobarbaz' classid='pushbutton' id='mybutton'/></body></html>"));
    534     QTRY_COMPARE(loadSpy.count(), 2);
    535     QCOMPARE(newPage->calls.count(), 0);
    536 
    537     m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>"));
    538     QTRY_COMPARE(loadSpy.count(), 3);
    539     QCOMPARE(newPage->calls.count(), 1);
    540     {
    541         PluginPage::CallInfo ci = newPage->calls.takeFirst();
    542         QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
    543         QCOMPARE(ci.url, QUrl());
    544         QCOMPARE(ci.paramNames.count(), 3);
    545         QCOMPARE(ci.paramValues.count(), 3);
    546         QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
    547         QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
    548         QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
    549         QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton"));
    550         QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
    551         QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton"));
    552         QVERIFY(ci.returnValue != 0);
    553         QVERIFY(ci.returnValue->inherits("QPushButton"));
    554     }
    555     // test JS bindings
    556     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("document.getElementById('mybutton').toString()").toString(),
    557              QString::fromLatin1("[object HTMLObjectElement]"));
    558     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.toString()").toString(),
    559              QString::fromLatin1("[object HTMLObjectElement]"));
    560     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.objectName").toString(),
    561              QString::fromLatin1("string"));
    562     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.objectName").toString(),
    563              QString::fromLatin1("pushbutton"));
    564     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("typeof mybutton.clicked").toString(),
    565              QString::fromLatin1("function"));
    566     QCOMPARE(newPage->mainFrame()->evaluateJavaScript("mybutton.clicked.toString()").toString(),
    567              QString::fromLatin1("function clicked() {\n    [native code]\n}"));
    568 
    569     m_view->setHtml(QString("<html><body><table>"
    570                             "<tr><object type='application/x-qt-plugin' classid='lineedit' id='myedit'/></tr>"
    571                             "<tr><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></tr>"
    572                             "</table></body></html>"), QUrl("http://foo.bar.baz"));
    573     QTRY_COMPARE(loadSpy.count(), 4);
    574     QCOMPARE(newPage->calls.count(), 2);
    575     {
    576         PluginPage::CallInfo ci = newPage->calls.takeFirst();
    577         QCOMPARE(ci.classid, QString::fromLatin1("lineedit"));
    578         QCOMPARE(ci.url, QUrl());
    579         QCOMPARE(ci.paramNames.count(), 3);
    580         QCOMPARE(ci.paramValues.count(), 3);
    581         QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
    582         QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
    583         QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
    584         QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("lineedit"));
    585         QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
    586         QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("myedit"));
    587         QVERIFY(ci.returnValue != 0);
    588         QVERIFY(ci.returnValue->inherits("QLineEdit"));
    589     }
    590     {
    591         PluginPage::CallInfo ci = newPage->calls.takeFirst();
    592         QCOMPARE(ci.classid, QString::fromLatin1("pushbutton"));
    593         QCOMPARE(ci.url, QUrl());
    594         QCOMPARE(ci.paramNames.count(), 3);
    595         QCOMPARE(ci.paramValues.count(), 3);
    596         QCOMPARE(ci.paramNames.at(0), QString::fromLatin1("type"));
    597         QCOMPARE(ci.paramValues.at(0), QString::fromLatin1("application/x-qt-plugin"));
    598         QCOMPARE(ci.paramNames.at(1), QString::fromLatin1("classid"));
    599         QCOMPARE(ci.paramValues.at(1), QString::fromLatin1("pushbutton"));
    600         QCOMPARE(ci.paramNames.at(2), QString::fromLatin1("id"));
    601         QCOMPARE(ci.paramValues.at(2), QString::fromLatin1("mybutton"));
    602         QVERIFY(ci.returnValue != 0);
    603         QVERIFY(ci.returnValue->inherits("QPushButton"));
    604     }
    605 
    606     m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, false);
    607 
    608     m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='pushbutton' id='mybutton'/></body></html>"));
    609     QTRY_COMPARE(loadSpy.count(), 5);
    610     QCOMPARE(newPage->calls.count(), 0);
    611 }
    612 
    613 
    614 // Standard base class for template PluginTracerPage. In tests it is used as interface.
    615 class PluginCounterPage : public QWebPage {
    616 public:
    617     int m_count;
    618     QPointer<QObject> m_widget;
    619     QObject* m_pluginParent;
    620     PluginCounterPage(QObject* parent = 0)
    621         : QWebPage(parent)
    622         , m_count(0)
    623         , m_widget(0)
    624         , m_pluginParent(0)
    625     {
    626        settings()->setAttribute(QWebSettings::PluginsEnabled, true);
    627     }
    628     ~PluginCounterPage()
    629     {
    630         if (m_pluginParent)
    631             m_pluginParent->deleteLater();
    632     }
    633 };
    634 
    635 template<class T>
    636 class PluginTracerPage : public PluginCounterPage {
    637 public:
    638     PluginTracerPage(QObject* parent = 0)
    639         : PluginCounterPage(parent)
    640     {
    641         // this is a dummy parent object for the created plugin
    642         m_pluginParent = new T;
    643     }
    644     virtual QObject* createPlugin(const QString&, const QUrl&, const QStringList&, const QStringList&)
    645     {
    646         m_count++;
    647         m_widget = new T;
    648         // need a cast to the specific type, as QObject::setParent cannot be called,
    649         // because it is not virtual. Instead it is necesary to call QWidget::setParent,
    650         // which also takes a QWidget* instead of a QObject*. Therefore we need to
    651         // upcast to T*, which is a QWidget.
    652         static_cast<T*>(m_widget.data())->setParent(static_cast<T*>(m_pluginParent));
    653         return m_widget;
    654     }
    655 };
    656 
    657 class PluginFactory {
    658 public:
    659     enum FactoredType {QWidgetType, QGraphicsWidgetType};
    660     static PluginCounterPage* create(FactoredType type, QObject* parent = 0)
    661     {
    662         PluginCounterPage* result = 0;
    663         switch (type) {
    664         case QWidgetType:
    665             result = new PluginTracerPage<QWidget>(parent);
    666             break;
    667         case QGraphicsWidgetType:
    668             result = new PluginTracerPage<QGraphicsWidget>(parent);
    669             break;
    670         default: {/*Oops*/};
    671         }
    672         return result;
    673     }
    674 
    675     static void prepareTestData()
    676     {
    677         QTest::addColumn<int>("type");
    678         QTest::newRow("QWidget") << (int)PluginFactory::QWidgetType;
    679         QTest::newRow("QGraphicsWidget") << (int)PluginFactory::QGraphicsWidgetType;
    680     }
    681 };
    682 
    683 void tst_QWebPage::destroyPlugin_data()
    684 {
    685     PluginFactory::prepareTestData();
    686 }
    687 
    688 void tst_QWebPage::destroyPlugin()
    689 {
    690     QFETCH(int, type);
    691     PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type, m_view);
    692     m_view->setPage(page);
    693 
    694     // we create the plugin, so the widget should be constructed
    695     QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
    696     m_view->setHtml(content);
    697     QVERIFY(page->m_widget);
    698     QCOMPARE(page->m_count, 1);
    699 
    700     // navigate away, the plugin widget should be destructed
    701     m_view->setHtml("<html><body>Hi</body></html>");
    702     QTestEventLoop::instance().enterLoop(1);
    703     QVERIFY(!page->m_widget);
    704 }
    705 
    706 void tst_QWebPage::createViewlessPlugin_data()
    707 {
    708     PluginFactory::prepareTestData();
    709 }
    710 
    711 void tst_QWebPage::createViewlessPlugin()
    712 {
    713     QFETCH(int, type);
    714     PluginCounterPage* page = PluginFactory::create((PluginFactory::FactoredType)type);
    715     QString content("<html><body><object type=\"application/x-qt-plugin\" classid=\"QProgressBar\"></object></body></html>");
    716     page->mainFrame()->setHtml(content);
    717     QCOMPARE(page->m_count, 1);
    718     QVERIFY(page->m_widget);
    719     QVERIFY(page->m_pluginParent);
    720     QVERIFY(page->m_widget->parent() == page->m_pluginParent);
    721     delete page;
    722 
    723 }
    724 
    725 // import private API
    726 void QWEBKIT_EXPORT qt_webpage_setGroupName(QWebPage* page, const QString& groupName);
    727 QString QWEBKIT_EXPORT qt_webpage_groupName(QWebPage* page);
    728 
    729 void tst_QWebPage::multiplePageGroupsAndLocalStorage()
    730 {
    731     QDir dir(QDir::currentPath());
    732     dir.mkdir("path1");
    733     dir.mkdir("path2");
    734 
    735     QWebView view1;
    736     QWebView view2;
    737 
    738     view1.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
    739     view1.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path1"));
    740     qt_webpage_setGroupName(view1.page(), "group1");
    741     view2.page()->settings()->setAttribute(QWebSettings::LocalStorageEnabled, true);
    742     view2.page()->settings()->setLocalStoragePath(QDir::toNativeSeparators(QDir::currentPath() + "/path2"));
    743     qt_webpage_setGroupName(view2.page(), "group2");
    744     QCOMPARE(qt_webpage_groupName(view1.page()), QString("group1"));
    745     QCOMPARE(qt_webpage_groupName(view2.page()), QString("group2"));
    746 
    747 
    748     view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
    749     view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
    750 
    751     view1.page()->mainFrame()->evaluateJavaScript("localStorage.test='value1';");
    752     view2.page()->mainFrame()->evaluateJavaScript("localStorage.test='value2';");
    753 
    754     view1.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
    755     view2.setHtml(QString("<html><body> </body></html>"), QUrl("http://www.myexample.com"));
    756 
    757     QVariant s1 = view1.page()->mainFrame()->evaluateJavaScript("localStorage.test");
    758     QCOMPARE(s1.toString(), QString("value1"));
    759 
    760     QVariant s2 = view2.page()->mainFrame()->evaluateJavaScript("localStorage.test");
    761     QCOMPARE(s2.toString(), QString("value2"));
    762 
    763     QTest::qWait(1000);
    764 
    765     QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path1/http_www.myexample.com_0.localstorage"));
    766     QFile::remove(QDir::toNativeSeparators(QDir::currentPath() + "/path2/http_www.myexample.com_0.localstorage"));
    767     dir.rmdir(QDir::toNativeSeparators("./path1"));
    768     dir.rmdir(QDir::toNativeSeparators("./path2"));
    769 }
    770 
    771 class CursorTrackedPage : public QWebPage
    772 {
    773 public:
    774 
    775     CursorTrackedPage(QWidget *parent = 0): QWebPage(parent) {
    776         setViewportSize(QSize(1024, 768)); // big space
    777     }
    778 
    779     QString selectedText() {
    780         return mainFrame()->evaluateJavaScript("window.getSelection().toString()").toString();
    781     }
    782 
    783     int selectionStartOffset() {
    784         return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).startOffset").toInt();
    785     }
    786 
    787     int selectionEndOffset() {
    788         return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).endOffset").toInt();
    789     }
    790 
    791     // true if start offset == end offset, i.e. no selected text
    792     int isSelectionCollapsed() {
    793         return mainFrame()->evaluateJavaScript("window.getSelection().getRangeAt(0).collapsed").toBool();
    794     }
    795 };
    796 
    797 void tst_QWebPage::cursorMovements()
    798 {
    799     CursorTrackedPage* page = new CursorTrackedPage;
    800     QString content("<html><body<p id=one>The quick brown fox</p><p id=two>jumps over the lazy dog</p><p>May the source<br/>be with you!</p></body></html>");
    801     page->mainFrame()->setHtml(content);
    802 
    803     // this will select the first paragraph
    804     QString script = "var range = document.createRange(); " \
    805         "var node = document.getElementById(\"one\"); " \
    806         "range.selectNode(node); " \
    807         "getSelection().addRange(range);";
    808     page->mainFrame()->evaluateJavaScript(script);
    809     QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
    810 
    811     // these actions must exist
    812     QVERIFY(page->action(QWebPage::MoveToNextChar) != 0);
    813     QVERIFY(page->action(QWebPage::MoveToPreviousChar) != 0);
    814     QVERIFY(page->action(QWebPage::MoveToNextWord) != 0);
    815     QVERIFY(page->action(QWebPage::MoveToPreviousWord) != 0);
    816     QVERIFY(page->action(QWebPage::MoveToNextLine) != 0);
    817     QVERIFY(page->action(QWebPage::MoveToPreviousLine) != 0);
    818     QVERIFY(page->action(QWebPage::MoveToStartOfLine) != 0);
    819     QVERIFY(page->action(QWebPage::MoveToEndOfLine) != 0);
    820     QVERIFY(page->action(QWebPage::MoveToStartOfBlock) != 0);
    821     QVERIFY(page->action(QWebPage::MoveToEndOfBlock) != 0);
    822     QVERIFY(page->action(QWebPage::MoveToStartOfDocument) != 0);
    823     QVERIFY(page->action(QWebPage::MoveToEndOfDocument) != 0);
    824 
    825     // right now they are disabled because contentEditable is false
    826     QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), false);
    827     QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), false);
    828     QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), false);
    829     QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), false);
    830     QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), false);
    831     QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), false);
    832     QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), false);
    833     QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), false);
    834     QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), false);
    835     QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), false);
    836     QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), false);
    837     QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), false);
    838 
    839     // make it editable before navigating the cursor
    840     page->setContentEditable(true);
    841 
    842     // here the actions are enabled after contentEditable is true
    843     QCOMPARE(page->action(QWebPage::MoveToNextChar)->isEnabled(), true);
    844     QCOMPARE(page->action(QWebPage::MoveToPreviousChar)->isEnabled(), true);
    845     QCOMPARE(page->action(QWebPage::MoveToNextWord)->isEnabled(), true);
    846     QCOMPARE(page->action(QWebPage::MoveToPreviousWord)->isEnabled(), true);
    847     QCOMPARE(page->action(QWebPage::MoveToNextLine)->isEnabled(), true);
    848     QCOMPARE(page->action(QWebPage::MoveToPreviousLine)->isEnabled(), true);
    849     QCOMPARE(page->action(QWebPage::MoveToStartOfLine)->isEnabled(), true);
    850     QCOMPARE(page->action(QWebPage::MoveToEndOfLine)->isEnabled(), true);
    851     QCOMPARE(page->action(QWebPage::MoveToStartOfBlock)->isEnabled(), true);
    852     QCOMPARE(page->action(QWebPage::MoveToEndOfBlock)->isEnabled(), true);
    853     QCOMPARE(page->action(QWebPage::MoveToStartOfDocument)->isEnabled(), true);
    854     QCOMPARE(page->action(QWebPage::MoveToEndOfDocument)->isEnabled(), true);
    855 
    856     // cursor will be before the word "jump"
    857     page->triggerAction(QWebPage::MoveToNextChar);
    858     QVERIFY(page->isSelectionCollapsed());
    859     QCOMPARE(page->selectionStartOffset(), 0);
    860 
    861     // cursor will be between 'j' and 'u' in the word "jump"
    862     page->triggerAction(QWebPage::MoveToNextChar);
    863     QVERIFY(page->isSelectionCollapsed());
    864     QCOMPARE(page->selectionStartOffset(), 1);
    865 
    866     // cursor will be between 'u' and 'm' in the word "jump"
    867     page->triggerAction(QWebPage::MoveToNextChar);
    868     QVERIFY(page->isSelectionCollapsed());
    869     QCOMPARE(page->selectionStartOffset(), 2);
    870 
    871     // cursor will be after the word "jump"
    872     page->triggerAction(QWebPage::MoveToNextWord);
    873     QVERIFY(page->isSelectionCollapsed());
    874     QCOMPARE(page->selectionStartOffset(), 5);
    875 
    876     // cursor will be after the word "lazy"
    877     page->triggerAction(QWebPage::MoveToNextWord);
    878     page->triggerAction(QWebPage::MoveToNextWord);
    879     page->triggerAction(QWebPage::MoveToNextWord);
    880     QVERIFY(page->isSelectionCollapsed());
    881     QCOMPARE(page->selectionStartOffset(), 19);
    882 
    883     // cursor will be between 'z' and 'y' in "lazy"
    884     page->triggerAction(QWebPage::MoveToPreviousChar);
    885     QVERIFY(page->isSelectionCollapsed());
    886     QCOMPARE(page->selectionStartOffset(), 18);
    887 
    888     // cursor will be between 'a' and 'z' in "lazy"
    889     page->triggerAction(QWebPage::MoveToPreviousChar);
    890     QVERIFY(page->isSelectionCollapsed());
    891     QCOMPARE(page->selectionStartOffset(), 17);
    892 
    893     // cursor will be before the word "lazy"
    894     page->triggerAction(QWebPage::MoveToPreviousWord);
    895     QVERIFY(page->isSelectionCollapsed());
    896     QCOMPARE(page->selectionStartOffset(), 15);
    897 
    898     // cursor will be before the word "quick"
    899     page->triggerAction(QWebPage::MoveToPreviousWord);
    900     page->triggerAction(QWebPage::MoveToPreviousWord);
    901     page->triggerAction(QWebPage::MoveToPreviousWord);
    902     page->triggerAction(QWebPage::MoveToPreviousWord);
    903     page->triggerAction(QWebPage::MoveToPreviousWord);
    904     page->triggerAction(QWebPage::MoveToPreviousWord);
    905     QVERIFY(page->isSelectionCollapsed());
    906     QCOMPARE(page->selectionStartOffset(), 4);
    907 
    908     // cursor will be between 'p' and 's' in the word "jumps"
    909     page->triggerAction(QWebPage::MoveToNextWord);
    910     page->triggerAction(QWebPage::MoveToNextWord);
    911     page->triggerAction(QWebPage::MoveToNextWord);
    912     page->triggerAction(QWebPage::MoveToNextChar);
    913     page->triggerAction(QWebPage::MoveToNextChar);
    914     page->triggerAction(QWebPage::MoveToNextChar);
    915     page->triggerAction(QWebPage::MoveToNextChar);
    916     page->triggerAction(QWebPage::MoveToNextChar);
    917     QVERIFY(page->isSelectionCollapsed());
    918     QCOMPARE(page->selectionStartOffset(), 4);
    919 
    920     // cursor will be before the word "jumps"
    921     page->triggerAction(QWebPage::MoveToStartOfLine);
    922     QVERIFY(page->isSelectionCollapsed());
    923     QCOMPARE(page->selectionStartOffset(), 0);
    924 
    925     // cursor will be after the word "dog"
    926     page->triggerAction(QWebPage::MoveToEndOfLine);
    927     QVERIFY(page->isSelectionCollapsed());
    928     QCOMPARE(page->selectionStartOffset(), 23);
    929 
    930     // cursor will be between 'w' and 'n' in "brown"
    931     page->triggerAction(QWebPage::MoveToStartOfLine);
    932     page->triggerAction(QWebPage::MoveToPreviousWord);
    933     page->triggerAction(QWebPage::MoveToPreviousWord);
    934     page->triggerAction(QWebPage::MoveToNextChar);
    935     page->triggerAction(QWebPage::MoveToNextChar);
    936     page->triggerAction(QWebPage::MoveToNextChar);
    937     page->triggerAction(QWebPage::MoveToNextChar);
    938     QVERIFY(page->isSelectionCollapsed());
    939     QCOMPARE(page->selectionStartOffset(), 14);
    940 
    941     // cursor will be after the word "fox"
    942     page->triggerAction(QWebPage::MoveToEndOfLine);
    943     QVERIFY(page->isSelectionCollapsed());
    944     QCOMPARE(page->selectionStartOffset(), 19);
    945 
    946     // cursor will be before the word "The"
    947     page->triggerAction(QWebPage::MoveToStartOfDocument);
    948     QVERIFY(page->isSelectionCollapsed());
    949     QCOMPARE(page->selectionStartOffset(), 0);
    950 
    951     // cursor will be after the word "you!"
    952     page->triggerAction(QWebPage::MoveToEndOfDocument);
    953     QVERIFY(page->isSelectionCollapsed());
    954     QCOMPARE(page->selectionStartOffset(), 12);
    955 
    956     // cursor will be before the word "be"
    957     page->triggerAction(QWebPage::MoveToStartOfBlock);
    958     QVERIFY(page->isSelectionCollapsed());
    959     QCOMPARE(page->selectionStartOffset(), 0);
    960 
    961     // cursor will be after the word "you!"
    962     page->triggerAction(QWebPage::MoveToEndOfBlock);
    963     QVERIFY(page->isSelectionCollapsed());
    964     QCOMPARE(page->selectionStartOffset(), 12);
    965 
    966     // try to move before the document start
    967     page->triggerAction(QWebPage::MoveToStartOfDocument);
    968     page->triggerAction(QWebPage::MoveToPreviousChar);
    969     QVERIFY(page->isSelectionCollapsed());
    970     QCOMPARE(page->selectionStartOffset(), 0);
    971     page->triggerAction(QWebPage::MoveToStartOfDocument);
    972     page->triggerAction(QWebPage::MoveToPreviousWord);
    973     QVERIFY(page->isSelectionCollapsed());
    974     QCOMPARE(page->selectionStartOffset(), 0);
    975 
    976     // try to move past the document end
    977     page->triggerAction(QWebPage::MoveToEndOfDocument);
    978     page->triggerAction(QWebPage::MoveToNextChar);
    979     QVERIFY(page->isSelectionCollapsed());
    980     QCOMPARE(page->selectionStartOffset(), 12);
    981     page->triggerAction(QWebPage::MoveToEndOfDocument);
    982     page->triggerAction(QWebPage::MoveToNextWord);
    983     QVERIFY(page->isSelectionCollapsed());
    984     QCOMPARE(page->selectionStartOffset(), 12);
    985 
    986     delete page;
    987 }
    988 
    989 void tst_QWebPage::textSelection()
    990 {
    991     CursorTrackedPage* page = new CursorTrackedPage;
    992     QString content("<html><body<p id=one>The quick brown fox</p>" \
    993         "<p id=two>jumps over the lazy dog</p>" \
    994         "<p>May the source<br/>be with you!</p></body></html>");
    995     page->mainFrame()->setHtml(content);
    996 
    997     // these actions must exist
    998     QVERIFY(page->action(QWebPage::SelectAll) != 0);
    999     QVERIFY(page->action(QWebPage::SelectNextChar) != 0);
   1000     QVERIFY(page->action(QWebPage::SelectPreviousChar) != 0);
   1001     QVERIFY(page->action(QWebPage::SelectNextWord) != 0);
   1002     QVERIFY(page->action(QWebPage::SelectPreviousWord) != 0);
   1003     QVERIFY(page->action(QWebPage::SelectNextLine) != 0);
   1004     QVERIFY(page->action(QWebPage::SelectPreviousLine) != 0);
   1005     QVERIFY(page->action(QWebPage::SelectStartOfLine) != 0);
   1006     QVERIFY(page->action(QWebPage::SelectEndOfLine) != 0);
   1007     QVERIFY(page->action(QWebPage::SelectStartOfBlock) != 0);
   1008     QVERIFY(page->action(QWebPage::SelectEndOfBlock) != 0);
   1009     QVERIFY(page->action(QWebPage::SelectStartOfDocument) != 0);
   1010     QVERIFY(page->action(QWebPage::SelectEndOfDocument) != 0);
   1011 
   1012     // right now they are disabled because contentEditable is false and
   1013     // there isn't an existing selection to modify
   1014     QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), false);
   1015     QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), false);
   1016     QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), false);
   1017     QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), false);
   1018     QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), false);
   1019     QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), false);
   1020     QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), false);
   1021     QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), false);
   1022     QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), false);
   1023     QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), false);
   1024     QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), false);
   1025     QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), false);
   1026 
   1027     // ..but SelectAll is awalys enabled
   1028     QCOMPARE(page->action(QWebPage::SelectAll)->isEnabled(), true);
   1029 
   1030     // this will select the first paragraph
   1031     QString selectScript = "var range = document.createRange(); " \
   1032         "var node = document.getElementById(\"one\"); " \
   1033         "range.selectNode(node); " \
   1034         "getSelection().addRange(range);";
   1035     page->mainFrame()->evaluateJavaScript(selectScript);
   1036     QCOMPARE(page->selectedText().trimmed(), QString::fromLatin1("The quick brown fox"));
   1037 
   1038     // here the actions are enabled after a selection has been created
   1039     QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true);
   1040     QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true);
   1041     QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true);
   1042     QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true);
   1043     QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true);
   1044     QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true);
   1045     QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true);
   1046     QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true);
   1047     QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true);
   1048     QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true);
   1049     QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true);
   1050     QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true);
   1051 
   1052     // make it editable before navigating the cursor
   1053     page->setContentEditable(true);
   1054 
   1055     // cursor will be before the word "The", this makes sure there is a charet
   1056     page->triggerAction(QWebPage::MoveToStartOfDocument);
   1057     QVERIFY(page->isSelectionCollapsed());
   1058     QCOMPARE(page->selectionStartOffset(), 0);
   1059 
   1060     // here the actions are enabled after contentEditable is true
   1061     QCOMPARE(page->action(QWebPage::SelectNextChar)->isEnabled(), true);
   1062     QCOMPARE(page->action(QWebPage::SelectPreviousChar)->isEnabled(), true);
   1063     QCOMPARE(page->action(QWebPage::SelectNextWord)->isEnabled(), true);
   1064     QCOMPARE(page->action(QWebPage::SelectPreviousWord)->isEnabled(), true);
   1065     QCOMPARE(page->action(QWebPage::SelectNextLine)->isEnabled(), true);
   1066     QCOMPARE(page->action(QWebPage::SelectPreviousLine)->isEnabled(), true);
   1067     QCOMPARE(page->action(QWebPage::SelectStartOfLine)->isEnabled(), true);
   1068     QCOMPARE(page->action(QWebPage::SelectEndOfLine)->isEnabled(), true);
   1069     QCOMPARE(page->action(QWebPage::SelectStartOfBlock)->isEnabled(), true);
   1070     QCOMPARE(page->action(QWebPage::SelectEndOfBlock)->isEnabled(), true);
   1071     QCOMPARE(page->action(QWebPage::SelectStartOfDocument)->isEnabled(), true);
   1072     QCOMPARE(page->action(QWebPage::SelectEndOfDocument)->isEnabled(), true);
   1073 
   1074     delete page;
   1075 }
   1076 
   1077 void tst_QWebPage::textEditing()
   1078 {
   1079     CursorTrackedPage* page = new CursorTrackedPage;
   1080     QString content("<html><body<p id=one>The quick brown fox</p>" \
   1081         "<p id=two>jumps over the lazy dog</p>" \
   1082         "<p>May the source<br/>be with you!</p></body></html>");
   1083     page->mainFrame()->setHtml(content);
   1084 
   1085     // these actions must exist
   1086     QVERIFY(page->action(QWebPage::Cut) != 0);
   1087     QVERIFY(page->action(QWebPage::Copy) != 0);
   1088     QVERIFY(page->action(QWebPage::Paste) != 0);
   1089     QVERIFY(page->action(QWebPage::DeleteStartOfWord) != 0);
   1090     QVERIFY(page->action(QWebPage::DeleteEndOfWord) != 0);
   1091     QVERIFY(page->action(QWebPage::SetTextDirectionDefault) != 0);
   1092     QVERIFY(page->action(QWebPage::SetTextDirectionLeftToRight) != 0);
   1093     QVERIFY(page->action(QWebPage::SetTextDirectionRightToLeft) != 0);
   1094     QVERIFY(page->action(QWebPage::ToggleBold) != 0);
   1095     QVERIFY(page->action(QWebPage::ToggleItalic) != 0);
   1096     QVERIFY(page->action(QWebPage::ToggleUnderline) != 0);
   1097     QVERIFY(page->action(QWebPage::InsertParagraphSeparator) != 0);
   1098     QVERIFY(page->action(QWebPage::InsertLineSeparator) != 0);
   1099     QVERIFY(page->action(QWebPage::PasteAndMatchStyle) != 0);
   1100     QVERIFY(page->action(QWebPage::RemoveFormat) != 0);
   1101     QVERIFY(page->action(QWebPage::ToggleStrikethrough) != 0);
   1102     QVERIFY(page->action(QWebPage::ToggleSubscript) != 0);
   1103     QVERIFY(page->action(QWebPage::ToggleSuperscript) != 0);
   1104     QVERIFY(page->action(QWebPage::InsertUnorderedList) != 0);
   1105     QVERIFY(page->action(QWebPage::InsertOrderedList) != 0);
   1106     QVERIFY(page->action(QWebPage::Indent) != 0);
   1107     QVERIFY(page->action(QWebPage::Outdent) != 0);
   1108     QVERIFY(page->action(QWebPage::AlignCenter) != 0);
   1109     QVERIFY(page->action(QWebPage::AlignJustified) != 0);
   1110     QVERIFY(page->action(QWebPage::AlignLeft) != 0);
   1111     QVERIFY(page->action(QWebPage::AlignRight) != 0);
   1112 
   1113     // right now they are disabled because contentEditable is false
   1114     QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false);
   1115     QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), false);
   1116     QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), false);
   1117     QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), false);
   1118     QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), false);
   1119     QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), false);
   1120     QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), false);
   1121     QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), false);
   1122     QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), false);
   1123     QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), false);
   1124     QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), false);
   1125     QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), false);
   1126     QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), false);
   1127     QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false);
   1128     QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), false);
   1129     QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), false);
   1130     QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), false);
   1131     QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), false);
   1132     QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), false);
   1133     QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), false);
   1134     QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), false);
   1135     QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), false);
   1136     QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), false);
   1137     QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), false);
   1138     QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), false);
   1139 
   1140     // Select everything
   1141     page->triggerAction(QWebPage::SelectAll);
   1142 
   1143     // make sure it is enabled since there is a selection
   1144     QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), true);
   1145 
   1146     // make it editable before navigating the cursor
   1147     page->setContentEditable(true);
   1148 
   1149     // clear the selection
   1150     page->triggerAction(QWebPage::MoveToStartOfDocument);
   1151     QVERIFY(page->isSelectionCollapsed());
   1152     QCOMPARE(page->selectionStartOffset(), 0);
   1153 
   1154     // make sure it is disabled since there isn't a selection
   1155     QCOMPARE(page->action(QWebPage::Copy)->isEnabled(), false);
   1156 
   1157     // here the actions are enabled after contentEditable is true
   1158     QCOMPARE(page->action(QWebPage::Paste)->isEnabled(), true);
   1159     QCOMPARE(page->action(QWebPage::DeleteStartOfWord)->isEnabled(), true);
   1160     QCOMPARE(page->action(QWebPage::DeleteEndOfWord)->isEnabled(), true);
   1161     QCOMPARE(page->action(QWebPage::SetTextDirectionDefault)->isEnabled(), true);
   1162     QCOMPARE(page->action(QWebPage::SetTextDirectionLeftToRight)->isEnabled(), true);
   1163     QCOMPARE(page->action(QWebPage::SetTextDirectionRightToLeft)->isEnabled(), true);
   1164     QCOMPARE(page->action(QWebPage::ToggleBold)->isEnabled(), true);
   1165     QCOMPARE(page->action(QWebPage::ToggleItalic)->isEnabled(), true);
   1166     QCOMPARE(page->action(QWebPage::ToggleUnderline)->isEnabled(), true);
   1167     QCOMPARE(page->action(QWebPage::InsertParagraphSeparator)->isEnabled(), true);
   1168     QCOMPARE(page->action(QWebPage::InsertLineSeparator)->isEnabled(), true);
   1169     QCOMPARE(page->action(QWebPage::PasteAndMatchStyle)->isEnabled(), true);
   1170     QCOMPARE(page->action(QWebPage::ToggleStrikethrough)->isEnabled(), true);
   1171     QCOMPARE(page->action(QWebPage::ToggleSubscript)->isEnabled(), true);
   1172     QCOMPARE(page->action(QWebPage::ToggleSuperscript)->isEnabled(), true);
   1173     QCOMPARE(page->action(QWebPage::InsertUnorderedList)->isEnabled(), true);
   1174     QCOMPARE(page->action(QWebPage::InsertOrderedList)->isEnabled(), true);
   1175     QCOMPARE(page->action(QWebPage::Indent)->isEnabled(), true);
   1176     QCOMPARE(page->action(QWebPage::Outdent)->isEnabled(), true);
   1177     QCOMPARE(page->action(QWebPage::AlignCenter)->isEnabled(), true);
   1178     QCOMPARE(page->action(QWebPage::AlignJustified)->isEnabled(), true);
   1179     QCOMPARE(page->action(QWebPage::AlignLeft)->isEnabled(), true);
   1180     QCOMPARE(page->action(QWebPage::AlignRight)->isEnabled(), true);
   1181 
   1182     // make sure these are disabled since there isn't a selection
   1183     QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), false);
   1184     QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), false);
   1185 
   1186     // make sure everything is selected
   1187     page->triggerAction(QWebPage::SelectAll);
   1188 
   1189     // this is only true if there is an editable selection
   1190     QCOMPARE(page->action(QWebPage::Cut)->isEnabled(), true);
   1191     QCOMPARE(page->action(QWebPage::RemoveFormat)->isEnabled(), true);
   1192 
   1193     delete page;
   1194 }
   1195 
   1196 void tst_QWebPage::requestCache()
   1197 {
   1198     TestPage page;
   1199     QSignalSpy loadSpy(&page, SIGNAL(loadFinished(bool)));
   1200 
   1201     page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me</a>"));
   1202     QTRY_COMPARE(loadSpy.count(), 1);
   1203     QTRY_COMPARE(page.navigations.count(), 1);
   1204 
   1205     page.mainFrame()->setUrl(QString("data:text/html,<a href=\"data:text/html,Reached\" target=\"_blank\">Click me2</a>"));
   1206     QTRY_COMPARE(loadSpy.count(), 2);
   1207     QTRY_COMPARE(page.navigations.count(), 2);
   1208 
   1209     page.triggerAction(QWebPage::Stop);
   1210     QVERIFY(page.history()->canGoBack());
   1211     page.triggerAction(QWebPage::Back);
   1212 
   1213     QTRY_COMPARE(loadSpy.count(), 3);
   1214     QTRY_COMPARE(page.navigations.count(), 3);
   1215     QCOMPARE(page.navigations.at(0).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
   1216              (int)QNetworkRequest::PreferNetwork);
   1217     QCOMPARE(page.navigations.at(1).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
   1218              (int)QNetworkRequest::PreferNetwork);
   1219     QCOMPARE(page.navigations.at(2).request.attribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork).toInt(),
   1220              (int)QNetworkRequest::PreferCache);
   1221 }
   1222 
   1223 void tst_QWebPage::backActionUpdate()
   1224 {
   1225     QWebView view;
   1226     QWebPage *page = view.page();
   1227     QAction *action = page->action(QWebPage::Back);
   1228     QVERIFY(!action->isEnabled());
   1229     QSignalSpy loadSpy(page, SIGNAL(loadFinished(bool)));
   1230     QUrl url = QUrl("qrc:///resources/index.html");
   1231     page->mainFrame()->load(url);
   1232     QTRY_COMPARE(loadSpy.count(), 1);
   1233     QVERIFY(!action->isEnabled());
   1234     QTest::mouseClick(&view, Qt::LeftButton, 0, QPoint(10, 10));
   1235     QTRY_COMPARE(loadSpy.count(), 2);
   1236 
   1237     QVERIFY(action->isEnabled());
   1238 }
   1239 
   1240 void frameAtHelper(QWebPage* webPage, QWebFrame* webFrame, QPoint framePosition)
   1241 {
   1242     if (!webFrame)
   1243         return;
   1244 
   1245     framePosition += QPoint(webFrame->pos());
   1246     QList<QWebFrame*> children = webFrame->childFrames();
   1247     for (int i = 0; i < children.size(); ++i) {
   1248         if (children.at(i)->childFrames().size() > 0)
   1249             frameAtHelper(webPage, children.at(i), framePosition);
   1250 
   1251         QRect frameRect(children.at(i)->pos() + framePosition, children.at(i)->geometry().size());
   1252         QVERIFY(children.at(i) == webPage->frameAt(frameRect.topLeft()));
   1253     }
   1254 }
   1255 
   1256 void tst_QWebPage::frameAt()
   1257 {
   1258     QWebView webView;
   1259     QWebPage* webPage = webView.page();
   1260     QSignalSpy loadSpy(webPage, SIGNAL(loadFinished(bool)));
   1261     QUrl url = QUrl("qrc:///resources/iframe.html");
   1262     webPage->mainFrame()->load(url);
   1263     QTRY_COMPARE(loadSpy.count(), 1);
   1264     frameAtHelper(webPage, webPage->mainFrame(), webPage->mainFrame()->pos());
   1265 }
   1266 
   1267 void tst_QWebPage::inputMethods_data()
   1268 {
   1269     QTest::addColumn<QString>("viewType");
   1270     QTest::newRow("QWebView") << "QWebView";
   1271 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
   1272     QTest::newRow("QGraphicsWebView") << "QGraphicsWebView";
   1273 #endif
   1274 }
   1275 
   1276 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
   1277 static Qt::InputMethodHints inputMethodHints(QObject* object)
   1278 {
   1279     if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
   1280         return o->inputMethodHints();
   1281     if (QWidget* w = qobject_cast<QWidget*>(object))
   1282         return w->inputMethodHints();
   1283     return Qt::InputMethodHints();
   1284 }
   1285 #endif
   1286 
   1287 static bool inputMethodEnabled(QObject* object)
   1288 {
   1289 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
   1290     if (QGraphicsObject* o = qobject_cast<QGraphicsObject*>(object))
   1291         return o->flags() & QGraphicsItem::ItemAcceptsInputMethod;
   1292 #endif
   1293     if (QWidget* w = qobject_cast<QWidget*>(object))
   1294         return w->testAttribute(Qt::WA_InputMethodEnabled);
   1295     return false;
   1296 }
   1297 
   1298 void tst_QWebPage::inputMethods()
   1299 {
   1300     QFETCH(QString, viewType);
   1301     QWebPage* page = new QWebPage;
   1302     QObject* view = 0;
   1303     QObject* container = 0;
   1304     if (viewType == "QWebView") {
   1305         QWebView* wv = new QWebView;
   1306         wv->setPage(page);
   1307         view = wv;
   1308         container = view;
   1309     }
   1310 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
   1311     else if (viewType == "QGraphicsWebView") {
   1312         QGraphicsWebView* wv = new QGraphicsWebView;
   1313         wv->setPage(page);
   1314         view = wv;
   1315 
   1316         QGraphicsView* gv = new QGraphicsView;
   1317         QGraphicsScene* scene = new QGraphicsScene(gv);
   1318         gv->setScene(scene);
   1319         scene->addItem(wv);
   1320         wv->setGeometry(QRect(0, 0, 500, 500));
   1321 
   1322         container = gv;
   1323     }
   1324 #endif
   1325     else
   1326         QVERIFY2(false, "Unknown view type");
   1327 
   1328     page->settings()->setFontFamily(QWebSettings::SerifFont, "FooSerifFont");
   1329     page->mainFrame()->setHtml("<html><body>" \
   1330                                             "<input type='text' id='input1' style='font-family: serif' value='' maxlength='20'/><br>" \
   1331                                             "<input type='password'/>" \
   1332                                             "</body></html>");
   1333     page->mainFrame()->setFocus();
   1334 
   1335     EventSpy viewEventSpy(container);
   1336 
   1337     QWebElementCollection inputs = page->mainFrame()->documentElement().findAll("input");
   1338 
   1339     QMouseEvent evpres(QEvent::MouseButtonPress, inputs.at(0).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
   1340     page->event(&evpres);
   1341     QMouseEvent evrel(QEvent::MouseButtonRelease, inputs.at(0).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
   1342     page->event(&evrel);
   1343 
   1344 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
   1345     QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
   1346 #endif
   1347     viewEventSpy.clear();
   1348 
   1349     page->event(&evpres);
   1350     page->event(&evrel);
   1351 
   1352 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
   1353     QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
   1354 #endif
   1355 
   1356     //ImMicroFocus
   1357     QVariant variant = page->inputMethodQuery(Qt::ImMicroFocus);
   1358     QRect focusRect = variant.toRect();
   1359     QVERIFY(inputs.at(0).geometry().contains(variant.toRect().topLeft()));
   1360 
   1361     //ImFont
   1362     variant = page->inputMethodQuery(Qt::ImFont);
   1363     QFont font = variant.value<QFont>();
   1364     QCOMPARE(page->settings()->fontFamily(QWebSettings::SerifFont), font.family());
   1365 
   1366     QList<QInputMethodEvent::Attribute> inputAttributes;
   1367 
   1368     //Insert text.
   1369     {
   1370         QInputMethodEvent eventText("QtWebKit", inputAttributes);
   1371         QSignalSpy signalSpy(page, SIGNAL(microFocusChanged()));
   1372         page->event(&eventText);
   1373         QCOMPARE(signalSpy.count(), 0);
   1374     }
   1375 
   1376     {
   1377         QInputMethodEvent eventText("", inputAttributes);
   1378         eventText.setCommitString(QString("QtWebKit"), 0, 0);
   1379         page->event(&eventText);
   1380     }
   1381 
   1382 #if QT_VERSION >= 0x040600
   1383     //ImMaximumTextLength
   1384     variant = page->inputMethodQuery(Qt::ImMaximumTextLength);
   1385     QCOMPARE(20, variant.toInt());
   1386 
   1387     //Set selection
   1388     inputAttributes << QInputMethodEvent::Attribute(QInputMethodEvent::Selection, 3, 2, QVariant());
   1389     QInputMethodEvent eventSelection("",inputAttributes);
   1390     page->event(&eventSelection);
   1391 
   1392     //ImAnchorPosition
   1393     variant = page->inputMethodQuery(Qt::ImAnchorPosition);
   1394     int anchorPosition =  variant.toInt();
   1395     QCOMPARE(anchorPosition, 3);
   1396 
   1397     //ImCursorPosition
   1398     variant = page->inputMethodQuery(Qt::ImCursorPosition);
   1399     int cursorPosition =  variant.toInt();
   1400     QCOMPARE(cursorPosition, 5);
   1401 
   1402     //ImCurrentSelection
   1403     variant = page->inputMethodQuery(Qt::ImCurrentSelection);
   1404     QString selectionValue = variant.value<QString>();
   1405     QCOMPARE(selectionValue, QString("eb"));
   1406 #endif
   1407 
   1408     //ImSurroundingText
   1409     variant = page->inputMethodQuery(Qt::ImSurroundingText);
   1410     QString value = variant.value<QString>();
   1411     QCOMPARE(value, QString("QtWebKit"));
   1412 
   1413 #if QT_VERSION >= 0x040600
   1414     {
   1415         QList<QInputMethodEvent::Attribute> attributes;
   1416         // Clear the selection, so the next test does not clear any contents.
   1417         QInputMethodEvent::Attribute newSelection(QInputMethodEvent::Selection, 0, 0, QVariant());
   1418         attributes.append(newSelection);
   1419         QInputMethodEvent event("composition", attributes);
   1420         page->event(&event);
   1421     }
   1422 
   1423     // A ongoing composition should not change the surrounding text before it is committed.
   1424     variant = page->inputMethodQuery(Qt::ImSurroundingText);
   1425     value = variant.value<QString>();
   1426     QCOMPARE(value, QString("QtWebKit"));
   1427 #endif
   1428 
   1429     //ImhHiddenText
   1430     QMouseEvent evpresPassword(QEvent::MouseButtonPress, inputs.at(1).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
   1431     page->event(&evpresPassword);
   1432     QMouseEvent evrelPassword(QEvent::MouseButtonRelease, inputs.at(1).geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
   1433     page->event(&evrelPassword);
   1434 
   1435     QVERIFY(inputMethodEnabled(view));
   1436 #if QT_VERSION >= 0x040600
   1437     QVERIFY(inputMethodHints(view) & Qt::ImhHiddenText);
   1438 
   1439     page->event(&evpres);
   1440     page->event(&evrel);
   1441     QVERIFY(!(inputMethodHints(view) & Qt::ImhHiddenText));
   1442 #endif
   1443 
   1444     page->mainFrame()->setHtml("<html><body><p>nothing to input here");
   1445     viewEventSpy.clear();
   1446 
   1447     QWebElement para = page->mainFrame()->findFirstElement("p");
   1448     {
   1449         QMouseEvent evpres(QEvent::MouseButtonPress, para.geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
   1450         page->event(&evpres);
   1451         QMouseEvent evrel(QEvent::MouseButtonRelease, para.geometry().center(), Qt::LeftButton, Qt::NoButton, Qt::NoModifier);
   1452         page->event(&evrel);
   1453     }
   1454 
   1455 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
   1456     QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel));
   1457 #endif
   1458 
   1459     delete container;
   1460 }
   1461 
   1462 // import a little DRT helper function to trigger the garbage collector
   1463 void QWEBKIT_EXPORT qt_drt_garbageCollector_collect();
   1464 
   1465 void tst_QWebPage::protectBindingsRuntimeObjectsFromCollector()
   1466 {
   1467     QSignalSpy loadSpy(m_view, SIGNAL(loadFinished(bool)));
   1468 
   1469     PluginPage* newPage = new PluginPage(m_view);
   1470     m_view->setPage(newPage);
   1471 
   1472     m_view->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
   1473 
   1474     m_view->setHtml(QString("<html><body><object type='application/x-qt-plugin' classid='lineedit' id='mylineedit'/></body></html>"));
   1475     QTRY_COMPARE(loadSpy.count(), 1);
   1476 
   1477     newPage->mainFrame()->evaluateJavaScript("function testme(text) { var lineedit = document.getElementById('mylineedit'); lineedit.setText(text); lineedit.selectAll(); }");
   1478 
   1479     newPage->mainFrame()->evaluateJavaScript("testme('foo')");
   1480 
   1481     qt_drt_garbageCollector_collect();
   1482 
   1483     // don't crash!
   1484     newPage->mainFrame()->evaluateJavaScript("testme('bar')");
   1485 }
   1486 
   1487 void tst_QWebPage::localURLSchemes()
   1488 {
   1489     int i = QWebSecurityOrigin::localSchemes().size();
   1490 
   1491     QWebSecurityOrigin::removeLocalScheme("file");
   1492     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
   1493     QWebSecurityOrigin::addLocalScheme("file");
   1494     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
   1495 
   1496     QWebSecurityOrigin::removeLocalScheme("qrc");
   1497     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i - 1);
   1498     QWebSecurityOrigin::addLocalScheme("qrc");
   1499     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
   1500 
   1501     QString myscheme = "myscheme";
   1502     QWebSecurityOrigin::addLocalScheme(myscheme);
   1503     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i + 1);
   1504     QVERIFY(QWebSecurityOrigin::localSchemes().contains(myscheme));
   1505     QWebSecurityOrigin::removeLocalScheme(myscheme);
   1506     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
   1507     QWebSecurityOrigin::removeLocalScheme(myscheme);
   1508     QTRY_COMPARE(QWebSecurityOrigin::localSchemes().size(), i);
   1509 }
   1510 
   1511 static inline bool testFlag(QWebPage& webPage, QWebSettings::WebAttribute settingAttribute, const QString& jsObjectName, bool settingValue)
   1512 {
   1513     webPage.settings()->setAttribute(settingAttribute, settingValue);
   1514     return webPage.mainFrame()->evaluateJavaScript(QString("(window.%1 != undefined)").arg(jsObjectName)).toBool();
   1515 }
   1516 
   1517 void tst_QWebPage::testOptionalJSObjects()
   1518 {
   1519     // Once a feature is enabled and the JS object is accessed turning off the setting will not turn off
   1520     // the visibility of the JS object any more. For this reason this test uses two QWebPage instances.
   1521     // Part of the test is to make sure that the QWebPage instances do not interfere with each other so turning on
   1522     // a feature for one instance will not turn it on for another.
   1523 
   1524     QWebPage webPage1;
   1525     QWebPage webPage2;
   1526 
   1527     webPage1.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
   1528     webPage2.currentFrame()->setHtml(QString("<html><body>test</body></html>"), QUrl());
   1529 
   1530     QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
   1531     QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
   1532     QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", true),  true);
   1533     QEXPECT_FAIL("","Feature enabled/disabled checking problem. Look at bugs.webkit.org/show_bug.cgi?id=29867", Continue);
   1534     QCOMPARE(testFlag(webPage1, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), false);
   1535     QCOMPARE(testFlag(webPage2, QWebSettings::OfflineWebApplicationCacheEnabled, "applicationCache", false), true);
   1536 
   1537     QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false);
   1538     QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", true),  true);
   1539     QCOMPARE(testFlag(webPage1, QWebSettings::LocalStorageEnabled, "localStorage", false), false);
   1540     QCOMPARE(testFlag(webPage2, QWebSettings::LocalStorageEnabled, "localStorage", false), true);
   1541 }
   1542 
   1543 void tst_QWebPage::testEnablePersistentStorage()
   1544 {
   1545     QWebPage webPage;
   1546 
   1547     // By default all persistent options should be disabled
   1548     QCOMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), false);
   1549     QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), false);
   1550     QCOMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), false);
   1551     QVERIFY(webPage.settings()->iconDatabasePath().isEmpty());
   1552 
   1553     QWebSettings::enablePersistentStorage();
   1554 
   1555 
   1556     QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::LocalStorageEnabled), true);
   1557     QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineStorageDatabaseEnabled), true);
   1558     QTRY_COMPARE(webPage.settings()->testAttribute(QWebSettings::OfflineWebApplicationCacheEnabled), true);
   1559 
   1560     QTRY_VERIFY(!webPage.settings()->offlineStoragePath().isEmpty());
   1561     QTRY_VERIFY(!webPage.settings()->offlineWebApplicationCachePath().isEmpty());
   1562     QTRY_VERIFY(!webPage.settings()->iconDatabasePath().isEmpty());
   1563 }
   1564 
   1565 void tst_QWebPage::defaultTextEncoding()
   1566 {
   1567     QWebFrame* mainFrame = m_page->mainFrame();
   1568 
   1569     QString defaultCharset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
   1570     QVERIFY(!defaultCharset.isEmpty());
   1571     QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), defaultCharset);
   1572 
   1573     m_page->settings()->setDefaultTextEncoding(QString("utf-8"));
   1574     QString charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
   1575     QCOMPARE(charset, QString("utf-8"));
   1576     QCOMPARE(m_page->settings()->defaultTextEncoding(), charset);
   1577 
   1578     m_page->settings()->setDefaultTextEncoding(QString());
   1579     charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
   1580     QVERIFY(!charset.isEmpty());
   1581     QCOMPARE(charset, defaultCharset);
   1582 
   1583     QWebSettings::globalSettings()->setDefaultTextEncoding(QString("utf-8"));
   1584     charset = mainFrame->evaluateJavaScript("document.defaultCharset").toString();
   1585     QCOMPARE(charset, QString("utf-8"));
   1586     QCOMPARE(QWebSettings::globalSettings()->defaultTextEncoding(), charset);
   1587 }
   1588 
   1589 class ErrorPage : public QWebPage
   1590 {
   1591 public:
   1592 
   1593     ErrorPage(QWidget* parent = 0): QWebPage(parent)
   1594     {
   1595     }
   1596 
   1597     virtual bool supportsExtension(Extension extension) const
   1598     {
   1599         return extension == ErrorPageExtension;
   1600     }
   1601 
   1602     virtual bool extension(Extension, const ExtensionOption* option, ExtensionReturn* output)
   1603     {
   1604         ErrorPageExtensionReturn* errorPage = static_cast<ErrorPageExtensionReturn*>(output);
   1605 
   1606         errorPage->content = "data:text/html,error";
   1607         return true;
   1608     }
   1609 };
   1610 
   1611 void tst_QWebPage::errorPageExtension()
   1612 {
   1613     ErrorPage* page = new ErrorPage;
   1614     m_view->setPage(page);
   1615 
   1616     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
   1617 
   1618     m_view->setUrl(QUrl("data:text/html,foo"));
   1619     QTRY_COMPARE(spyLoadFinished.count(), 1);
   1620 
   1621     page->mainFrame()->setUrl(QUrl("http://non.existent/url"));
   1622     QTRY_COMPARE(spyLoadFinished.count(), 2);
   1623     QCOMPARE(page->mainFrame()->toPlainText(), QString("data:text/html,error"));
   1624     QCOMPARE(page->history()->count(), 2);
   1625     QCOMPARE(page->history()->currentItem().url(), QUrl("http://non.existent/url"));
   1626     QCOMPARE(page->history()->canGoBack(), true);
   1627     QCOMPARE(page->history()->canGoForward(), false);
   1628 
   1629     page->triggerAction(QWebPage::Back);
   1630     QTRY_COMPARE(page->history()->canGoBack(), false);
   1631     QTRY_COMPARE(page->history()->canGoForward(), true);
   1632 
   1633     page->triggerAction(QWebPage::Forward);
   1634     QTRY_COMPARE(page->history()->canGoBack(), true);
   1635     QTRY_COMPARE(page->history()->canGoForward(), false);
   1636 
   1637     page->triggerAction(QWebPage::Back);
   1638     QTRY_COMPARE(page->history()->canGoBack(), false);
   1639     QTRY_COMPARE(page->history()->canGoForward(), true);
   1640     QTRY_COMPARE(page->history()->currentItem().url(), QUrl("data:text/html,foo"));
   1641 
   1642     m_view->setPage(0);
   1643 }
   1644 
   1645 void tst_QWebPage::errorPageExtensionInIFrames()
   1646 {
   1647     ErrorPage* page = new ErrorPage;
   1648     m_view->setPage(page);
   1649 
   1650     m_view->setHtml(QString("data:text/html,"
   1651                             "<h1>h1</h1>"
   1652                             "<iframe src='data:text/html,<p/>p'></iframe>"
   1653                             "<iframe src='non-existent.html'></iframe>"));
   1654     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
   1655     QTRY_COMPARE(spyLoadFinished.count(), 1);
   1656 
   1657     QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("data:text/html,error"));
   1658 
   1659     m_view->setPage(0);
   1660 }
   1661 
   1662 void tst_QWebPage::errorPageExtensionInFrameset()
   1663 {
   1664     ErrorPage* page = new ErrorPage;
   1665     m_view->setPage(page);
   1666 
   1667     m_view->load(QUrl("qrc:///resources/index.html"));
   1668 
   1669     QSignalSpy spyLoadFinished(m_view, SIGNAL(loadFinished(bool)));
   1670     QTRY_COMPARE(spyLoadFinished.count(), 1);
   1671     QCOMPARE(page->mainFrame()->childFrames()[1]->toPlainText(), QString("data:text/html,error"));
   1672 
   1673     m_view->setPage(0);
   1674 }
   1675 
   1676 void tst_QWebPage::crashTests_LazyInitializationOfMainFrame()
   1677 {
   1678     {
   1679         QWebPage webPage;
   1680     }
   1681     {
   1682         QWebPage webPage;
   1683         webPage.selectedText();
   1684     }
   1685     {
   1686         QWebPage webPage;
   1687         webPage.triggerAction(QWebPage::Back, true);
   1688     }
   1689     {
   1690         QWebPage webPage;
   1691         QPoint pos(10,10);
   1692         webPage.updatePositionDependentActions(pos);
   1693     }
   1694 }
   1695 
   1696 static void takeScreenshot(QWebPage* page)
   1697 {
   1698     QWebFrame* mainFrame = page->mainFrame();
   1699     page->setViewportSize(mainFrame->contentsSize());
   1700     QImage image(page->viewportSize(), QImage::Format_ARGB32);
   1701     QPainter painter(&image);
   1702     mainFrame->render(&painter);
   1703     painter.end();
   1704 }
   1705 
   1706 void tst_QWebPage::screenshot_data()
   1707 {
   1708     QTest::addColumn<QString>("html");
   1709     QTest::newRow("WithoutPlugin") << "<html><body id='b'>text</body></html>";
   1710     QTest::newRow("WindowedPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf'></embed></body></html>");
   1711     QTest::newRow("WindowlessPlugin") << QString("<html><body id='b'>text<embed src='resources/test.swf' wmode='transparent'></embed></body></html>");
   1712 }
   1713 
   1714 void tst_QWebPage::screenshot()
   1715 {
   1716     if (!QDir(TESTS_SOURCE_DIR).exists())
   1717         QSKIP(QString("This test requires access to resources found in '%1'").arg(TESTS_SOURCE_DIR).toLatin1().constData(), SkipAll);
   1718 
   1719     QDir::setCurrent(TESTS_SOURCE_DIR);
   1720 
   1721     QFETCH(QString, html);
   1722     QWebPage* page = new QWebPage;
   1723     page->settings()->setAttribute(QWebSettings::PluginsEnabled, true);
   1724     QWebFrame* mainFrame = page->mainFrame();
   1725     mainFrame->setHtml(html, QUrl::fromLocalFile(TESTS_SOURCE_DIR));
   1726     ::waitForSignal(mainFrame, SIGNAL(loadFinished(bool)), 2000);
   1727 
   1728     // take screenshot without a view
   1729     takeScreenshot(page);
   1730 
   1731     QWebView* view = new QWebView;
   1732     view->setPage(page);
   1733 
   1734     // take screenshot when attached to a view
   1735     takeScreenshot(page);
   1736 
   1737     delete page;
   1738     delete view;
   1739 
   1740     QDir::setCurrent(QApplication::applicationDirPath());
   1741 }
   1742 
   1743 void tst_QWebPage::originatingObjectInNetworkRequests()
   1744 {
   1745     TestNetworkManager* networkManager = new TestNetworkManager(m_page);
   1746     m_page->setNetworkAccessManager(networkManager);
   1747     networkManager->requests.clear();
   1748 
   1749     m_view->setHtml(QString("data:text/html,<frameset cols=\"25%,75%\"><frame src=\"data:text/html,"
   1750                             "<head><meta http-equiv='refresh' content='1'></head>foo \">"
   1751                             "<frame src=\"data:text/html,bar\"></frameset>"), QUrl());
   1752     QVERIFY(::waitForSignal(m_view, SIGNAL(loadFinished(bool))));
   1753 
   1754     QCOMPARE(networkManager->requests.count(), 2);
   1755 
   1756     QList<QWebFrame*> childFrames = m_page->mainFrame()->childFrames();
   1757     QCOMPARE(childFrames.count(), 2);
   1758 
   1759 #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0)
   1760     for (int i = 0; i < 2; ++i)
   1761         QVERIFY(qobject_cast<QWebFrame*>(networkManager->requests.at(i).originatingObject()) == childFrames.at(i));
   1762 #endif
   1763 }
   1764 
   1765 /**
   1766  * Test fixups for https://bugs.webkit.org/show_bug.cgi?id=30914
   1767  *
   1768  * From JS we test the following conditions.
   1769  *
   1770  *   OK     + QString() => SUCCESS, empty string (but not null)
   1771  *   OK     + "text"    => SUCCESS, "text"
   1772  *   CANCEL + QString() => CANCEL, null string
   1773  *   CANCEL + "text"    => CANCEL, null string
   1774  */
   1775 class JSPromptPage : public QWebPage {
   1776     Q_OBJECT
   1777 public:
   1778     JSPromptPage()
   1779     {}
   1780 
   1781     bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result)
   1782     {
   1783         if (msg == QLatin1String("test1")) {
   1784             *result = QString();
   1785             return true;
   1786         } else if (msg == QLatin1String("test2")) {
   1787             *result = QLatin1String("text");
   1788             return true;
   1789         } else if (msg == QLatin1String("test3")) {
   1790             *result = QString();
   1791             return false;
   1792         } else if (msg == QLatin1String("test4")) {
   1793             *result = QLatin1String("text");
   1794             return false;
   1795         }
   1796 
   1797         qFatal("Unknown msg.");
   1798         return QWebPage::javaScriptPrompt(frame, msg, defaultValue, result);
   1799     }
   1800 };
   1801 
   1802 void tst_QWebPage::testJSPrompt()
   1803 {
   1804     JSPromptPage page;
   1805     bool res;
   1806 
   1807     // OK + QString()
   1808     res = page.mainFrame()->evaluateJavaScript(
   1809             "var retval = prompt('test1');"
   1810             "retval=='' && retval.length == 0;").toBool();
   1811     QVERIFY(res);
   1812 
   1813     // OK + "text"
   1814     res = page.mainFrame()->evaluateJavaScript(
   1815             "var retval = prompt('test2');"
   1816             "retval=='text' && retval.length == 4;").toBool();
   1817     QVERIFY(res);
   1818 
   1819     // Cancel + QString()
   1820     res = page.mainFrame()->evaluateJavaScript(
   1821             "var retval = prompt('test3');"
   1822             "retval===null;").toBool();
   1823     QVERIFY(res);
   1824 
   1825     // Cancel + "text"
   1826     res = page.mainFrame()->evaluateJavaScript(
   1827             "var retval = prompt('test4');"
   1828             "retval===null;").toBool();
   1829     QVERIFY(res);
   1830 }
   1831 
   1832 class TestModalPage : public QWebPage
   1833 {
   1834     Q_OBJECT
   1835 public:
   1836     TestModalPage(QObject* parent = 0) : QWebPage(parent) {
   1837     }
   1838     virtual QWebPage* createWindow(WebWindowType) {
   1839         QWebPage* page = new TestModalPage();
   1840         connect(page, SIGNAL(windowCloseRequested()), page, SLOT(deleteLater()));
   1841         return page;
   1842     }
   1843 };
   1844 
   1845 void tst_QWebPage::showModalDialog()
   1846 {
   1847     TestModalPage page;
   1848     page.mainFrame()->setHtml(QString("<html></html>"));
   1849     QString res = page.mainFrame()->evaluateJavaScript("window.showModalDialog('javascript:window.returnValue=dialogArguments; window.close();', 'This is a test');").toString();
   1850     QCOMPARE(res, QString("This is a test"));
   1851 }
   1852 
   1853 QTEST_MAIN(tst_QWebPage)
   1854 #include "tst_qwebpage.moc"
   1855