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