Home | History | Annotate | Download | only in qt
      1 /*
      2  * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
      3  * Copyright (C) 2009 Torch Mobile Inc. http://www.torchmobile.com/
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *
      9  * 1.  Redistributions of source code must retain the above copyright
     10  *     notice, this list of conditions and the following disclaimer.
     11  * 2.  Redistributions in binary form must reproduce the above copyright
     12  *     notice, this list of conditions and the following disclaimer in the
     13  *     documentation and/or other materials provided with the distribution.
     14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     15  *     its contributors may be used to endorse or promote products derived
     16  *     from this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28  */
     29 #include "config.h"
     30 #include "LayoutTestControllerQt.h"
     31 
     32 #include "DumpRenderTreeQt.h"
     33 #include "WorkQueue.h"
     34 #include "WorkQueueItemQt.h"
     35 #include <QDir>
     36 #include <QLocale>
     37 #include <qwebsettings.h>
     38 
     39 extern void qt_dump_editing_callbacks(bool b);
     40 extern void qt_dump_frame_loader(bool b);
     41 extern void qt_dump_resource_load_callbacks(bool b);
     42 extern void qt_drt_setFrameSetFlatteningEnabled(QWebPage*, bool);
     43 extern void qt_drt_setJavaScriptProfilingEnabled(QWebFrame*, bool enabled);
     44 extern bool qt_drt_pauseAnimation(QWebFrame*, const QString& name, double time, const QString& elementId);
     45 extern bool qt_drt_pauseTransitionOfProperty(QWebFrame*, const QString& name, double time, const QString& elementId);
     46 extern bool qt_drt_pauseSVGAnimation(QWebFrame*, const QString& animationId, double time, const QString& elementId);
     47 extern int qt_drt_numberOfActiveAnimations(QWebFrame*);
     48 extern void qt_drt_setDomainRelaxationForbiddenForURLScheme(bool forbidden, const QString& scheme);
     49 
     50 extern void qt_drt_whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains);
     51 extern QString qt_drt_counterValueForElementById(QWebFrame* qFrame, const QString& id);
     52 extern int qt_drt_workerThreadCount();
     53 extern int qt_drt_pageNumberForElementById(QWebFrame* qFrame, const QString& id, float width, float height);
     54 
     55 LayoutTestController::LayoutTestController(WebCore::DumpRenderTree* drt)
     56     : QObject()
     57     , m_drt(drt)
     58 {
     59     reset();
     60 }
     61 
     62 void LayoutTestController::reset()
     63 {
     64     m_hasDumped = false;
     65     m_loadFinished = false;
     66     m_textDump = false;
     67     m_dumpBackForwardList = false;
     68     m_dumpChildrenAsText = false;
     69     m_canOpenWindows = false;
     70     m_waitForDone = false;
     71     m_dumpTitleChanges = false;
     72     m_dumpDatabaseCallbacks = false;
     73     m_dumpStatusCallbacks = false;
     74     m_timeoutTimer.stop();
     75     m_topLoadingFrame = 0;
     76     m_waitForPolicy = false;
     77     m_handleErrorPages = false;
     78     m_webHistory = 0;
     79     m_globalFlag = false;
     80     qt_dump_editing_callbacks(false);
     81     qt_dump_frame_loader(false);
     82     qt_dump_resource_load_callbacks(false);
     83     emit hidePage();
     84 }
     85 
     86 void LayoutTestController::processWork()
     87 {
     88     // qDebug() << ">>>processWork";
     89 
     90     // if we didn't start a new load, then we finished all the commands, so we're ready to dump state
     91     if (WorkQueue::shared()->processWork() && !shouldWaitUntilDone()) {
     92         emit done();
     93         m_hasDumped = true;
     94     }
     95 }
     96 
     97 // Called on loadFinished on WebPage
     98 void LayoutTestController::maybeDump(bool success)
     99 {
    100 
    101     // This can happen on any of the http/tests/security/window-events-*.html tests, where the test opens
    102     // a new window, calls the unload and load event handlers on the window's page, and then immediately
    103     // issues a notifyDone. Needs investigation.
    104     if (!m_topLoadingFrame)
    105         return;
    106 
    107     // It is possible that we get called by windows created from the main page that have finished
    108     // loading, so we don't ASSERT here. At the moment we do not gather results from such windows,
    109     // but may need to in future.
    110     if (sender() != m_topLoadingFrame->page())
    111         return;
    112 
    113     m_loadFinished = true;
    114     // as the function is called on loadFinished, the test might
    115     // already have dumped and thus no longer be active, thus
    116     // bail out here.
    117     if (m_hasDumped)
    118         return;
    119 
    120     WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test
    121     if (WorkQueue::shared()->count())
    122         QTimer::singleShot(0, this, SLOT(processWork()));
    123     else if (!shouldWaitUntilDone()) {
    124         if (success)
    125             emit done();
    126         m_hasDumped = true;
    127     }
    128 }
    129 
    130 void LayoutTestController::waitUntilDone()
    131 {
    132     //qDebug() << ">>>>waitForDone";
    133     m_waitForDone = true;
    134     m_timeoutTimer.start(15000, this);
    135 }
    136 
    137 QString LayoutTestController::counterValueForElementById(const QString& id)
    138 {
    139     return qt_drt_counterValueForElementById(m_drt->webPage()->mainFrame(), id);
    140 }
    141 
    142 int LayoutTestController::webHistoryItemCount()
    143 {
    144     if (!m_webHistory)
    145         return -1;
    146 
    147     // Subtract one here as our QWebHistory::count() includes the actual page,
    148     // which is not considered in the DRT tests.
    149     return m_webHistory->count() - 1;
    150 }
    151 
    152 void LayoutTestController::keepWebHistory()
    153 {
    154     m_webHistory = m_drt->webPage()->history();
    155 }
    156 
    157 void LayoutTestController::notifyDone()
    158 {
    159     qDebug() << ">>>>notifyDone";
    160 
    161     if (!m_timeoutTimer.isActive())
    162         return;
    163 
    164     m_timeoutTimer.stop();
    165     m_waitForDone = false;
    166 
    167     // If the page has not finished loading (i.e. loadFinished() has not been emitted) then
    168     // content created by the likes of document.write() JS methods will not be available yet.
    169     // When the page has finished loading, maybeDump above will dump the results now that we have
    170     // just set shouldWaitUntilDone to false.
    171     if (!m_loadFinished)
    172         return;
    173 
    174     emit done();
    175 
    176     // FIXME: investigate why always resetting these result in timeouts
    177     m_hasDumped = true;
    178     m_waitForPolicy = false;
    179 }
    180 
    181 int LayoutTestController::windowCount()
    182 {
    183     return m_drt->windowCount();
    184 }
    185 
    186 void LayoutTestController::display()
    187 {
    188     emit showPage();
    189 }
    190 
    191 void LayoutTestController::clearBackForwardList()
    192 {
    193     m_drt->webPage()->history()->clear();
    194 }
    195 
    196 QString LayoutTestController::pathToLocalResource(const QString& url)
    197 {
    198     // Function introduced in r28690.
    199     return QDir::toNativeSeparators(url);
    200 }
    201 
    202 void LayoutTestController::dumpEditingCallbacks()
    203 {
    204     qDebug() << ">>>dumpEditingCallbacks";
    205     qt_dump_editing_callbacks(true);
    206 }
    207 
    208 void LayoutTestController::dumpFrameLoadCallbacks()
    209 {
    210     qt_dump_frame_loader(true);
    211 }
    212 
    213 void LayoutTestController::dumpResourceLoadCallbacks()
    214 {
    215     qt_dump_resource_load_callbacks(true);
    216 }
    217 
    218 void LayoutTestController::queueBackNavigation(int howFarBackward)
    219 {
    220     //qDebug() << ">>>queueBackNavigation" << howFarBackward;
    221     WorkQueue::shared()->queue(new BackItem(howFarBackward, m_drt->webPage()));
    222 }
    223 
    224 void LayoutTestController::queueForwardNavigation(int howFarForward)
    225 {
    226     //qDebug() << ">>>queueForwardNavigation" << howFarForward;
    227     WorkQueue::shared()->queue(new ForwardItem(howFarForward, m_drt->webPage()));
    228 }
    229 
    230 void LayoutTestController::queueLoad(const QString& url, const QString& target)
    231 {
    232     //qDebug() << ">>>queueLoad" << url << target;
    233     QUrl mainResourceUrl = m_drt->webPage()->mainFrame()->url();
    234     QString absoluteUrl = mainResourceUrl.resolved(QUrl(url)).toEncoded();
    235     WorkQueue::shared()->queue(new LoadItem(absoluteUrl, target, m_drt->webPage()));
    236 }
    237 
    238 void LayoutTestController::queueReload()
    239 {
    240     //qDebug() << ">>>queueReload";
    241     WorkQueue::shared()->queue(new ReloadItem(m_drt->webPage()));
    242 }
    243 
    244 void LayoutTestController::queueLoadingScript(const QString& script)
    245 {
    246     //qDebug() << ">>>queueLoadingScript" << script;
    247     WorkQueue::shared()->queue(new LoadingScriptItem(script, m_drt->webPage()));
    248 }
    249 
    250 void LayoutTestController::queueNonLoadingScript(const QString& script)
    251 {
    252     //qDebug() << ">>>queueNonLoadingScript" << script;
    253     WorkQueue::shared()->queue(new NonLoadingScriptItem(script, m_drt->webPage()));
    254 }
    255 
    256 void LayoutTestController::provisionalLoad()
    257 {
    258     QWebFrame* frame = qobject_cast<QWebFrame*>(sender());
    259     if (!m_topLoadingFrame && !m_hasDumped)
    260         m_topLoadingFrame = frame;
    261 }
    262 
    263 void LayoutTestController::timerEvent(QTimerEvent *ev)
    264 {
    265     if (ev->timerId() == m_timeoutTimer.timerId()) {
    266         const char* message = "FAIL: Timed out waiting for notifyDone to be called\n";
    267         fprintf(stderr, "%s", message);
    268         fprintf(stdout, "%s", message);
    269         notifyDone();
    270     } else
    271         QObject::timerEvent(ev);
    272 }
    273 
    274 QString LayoutTestController::encodeHostName(const QString& host)
    275 {
    276     QString encoded = QString::fromLatin1(QUrl::toAce(host + QLatin1String(".no")));
    277     encoded.truncate(encoded.length() - 3); // strip .no
    278     return encoded;
    279 }
    280 
    281 QString LayoutTestController::decodeHostName(const QString& host)
    282 {
    283     QString decoded = QUrl::fromAce(host.toLatin1() + QByteArray(".no"));
    284     decoded.truncate(decoded.length() - 3);
    285     return decoded;
    286 }
    287 
    288 void LayoutTestController::showWebInspector()
    289 {
    290     m_drt->webPage()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
    291     m_drt->webPage()->webInspector()->show();
    292 }
    293 
    294 void LayoutTestController::hideWebInspector()
    295 {
    296     m_drt->webPage()->webInspector()->hide();
    297 }
    298 
    299 void LayoutTestController::setFrameSetFlatteningEnabled(bool enabled)
    300 {
    301     qt_drt_setFrameSetFlatteningEnabled(m_drt->webPage(), enabled);
    302 }
    303 
    304 void LayoutTestController::setAllowUniversalAccessFromFileURLs(bool enabled)
    305 {
    306     m_drt->webPage()->settings()->setAttribute(QWebSettings::LocalContentCanAccessRemoteUrls, enabled);
    307 }
    308 
    309 void LayoutTestController::setJavaScriptProfilingEnabled(bool enable)
    310 {
    311     m_topLoadingFrame->page()->settings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
    312     qt_drt_setJavaScriptProfilingEnabled(m_topLoadingFrame, enable);
    313 }
    314 
    315 void LayoutTestController::setFixedContentsSize(int width, int height)
    316 {
    317     m_topLoadingFrame->page()->setPreferredContentsSize(QSize(width, height));
    318 }
    319 
    320 void LayoutTestController::setPrivateBrowsingEnabled(bool enable)
    321 {
    322     m_drt->webPage()->settings()->setAttribute(QWebSettings::PrivateBrowsingEnabled, enable);
    323 }
    324 
    325 void LayoutTestController::setPopupBlockingEnabled(bool enable)
    326 {
    327     m_drt->webPage()->settings()->setAttribute(QWebSettings::JavascriptCanOpenWindows, !enable);
    328 }
    329 
    330 void LayoutTestController::setPOSIXLocale(const QString& locale)
    331 {
    332     QLocale qlocale(locale);
    333     QLocale::setDefault(qlocale);
    334 }
    335 
    336 void LayoutTestController::setWindowIsKey(bool isKey)
    337 {
    338     m_drt->switchFocus(isKey);
    339 }
    340 
    341 void LayoutTestController::setMainFrameIsFirstResponder(bool isFirst)
    342 {
    343     //FIXME: only need this for the moment: https://bugs.webkit.org/show_bug.cgi?id=32990
    344 }
    345 
    346 void LayoutTestController::setXSSAuditorEnabled(bool enable)
    347 {
    348     // Set XSSAuditorEnabled globally so that windows created by the test inherit it too.
    349     // resetSettings() will call this to reset the page and global setting to false again.
    350     // Needed by http/tests/security/xssAuditor/link-opens-new-window.html
    351     QWebSettings* globalSettings = QWebSettings::globalSettings();
    352     globalSettings->setAttribute(QWebSettings::XSSAuditorEnabled, enable);
    353     m_drt->webPage()->settings()->setAttribute(QWebSettings::XSSAuditorEnabled, enable);
    354 }
    355 
    356 bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(const QString& animationName,
    357                                                                double time,
    358                                                                const QString& elementId)
    359 {
    360     QWebFrame* frame = m_drt->webPage()->mainFrame();
    361     Q_ASSERT(frame);
    362     return qt_drt_pauseAnimation(frame, animationName, time, elementId);
    363 }
    364 
    365 bool LayoutTestController::pauseTransitionAtTimeOnElementWithId(const QString& propertyName,
    366                                                                 double time,
    367                                                                 const QString& elementId)
    368 {
    369     QWebFrame* frame = m_drt->webPage()->mainFrame();
    370     Q_ASSERT(frame);
    371     return qt_drt_pauseTransitionOfProperty(frame, propertyName, time, elementId);
    372 }
    373 
    374 bool LayoutTestController::sampleSVGAnimationForElementAtTime(const QString& animationId,
    375                                                               double time,
    376                                                               const QString& elementId)
    377 {
    378     QWebFrame* frame = m_drt->webPage()->mainFrame();
    379     Q_ASSERT(frame);
    380     return qt_drt_pauseSVGAnimation(frame, animationId, time, elementId);
    381 }
    382 
    383 unsigned LayoutTestController::numberOfActiveAnimations() const
    384 {
    385     QWebFrame* frame = m_drt->webPage()->mainFrame();
    386     Q_ASSERT(frame);
    387     return qt_drt_numberOfActiveAnimations(frame);
    388 }
    389 
    390 void LayoutTestController::disableImageLoading()
    391 {
    392     // FIXME: Implement for testing fix for https://bugs.webkit.org/show_bug.cgi?id=27896
    393     // Also need to make sure image loading is re-enabled for each new test.
    394 }
    395 
    396 void LayoutTestController::dispatchPendingLoadRequests()
    397 {
    398     // FIXME: Implement for testing fix for 6727495
    399 }
    400 
    401 void LayoutTestController::setDatabaseQuota(int size)
    402 {
    403     if (!m_topLoadingFrame)
    404         return;
    405     m_topLoadingFrame->securityOrigin().setDatabaseQuota(size);
    406 }
    407 
    408 void LayoutTestController::clearAllDatabases()
    409 {
    410     QWebDatabase::removeAllDatabases();
    411 }
    412 
    413 void LayoutTestController::whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains)
    414 {
    415     qt_drt_whiteListAccessFromOrigin(sourceOrigin, destinationProtocol, destinationHost, allowDestinationSubdomains);
    416 }
    417 
    418 void LayoutTestController::waitForPolicyDelegate()
    419 {
    420     m_waitForPolicy = true;
    421     waitUntilDone();
    422 }
    423 
    424 void LayoutTestController::overridePreference(const QString& name, const QVariant& value)
    425 {
    426     QWebSettings* settings = m_topLoadingFrame->page()->settings();
    427 
    428     if (name == "WebKitJavaScriptEnabled")
    429         settings->setAttribute(QWebSettings::JavascriptEnabled, value.toBool());
    430     else if (name == "WebKitTabToLinksPreferenceKey")
    431         settings->setAttribute(QWebSettings::LinksIncludedInFocusChain, value.toBool());
    432     else if (name == "WebKitOfflineWebApplicationCacheEnabled")
    433         settings->setAttribute(QWebSettings::OfflineWebApplicationCacheEnabled, value.toBool());
    434     else if (name == "WebKitDefaultFontSize")
    435         settings->setFontSize(QWebSettings::DefaultFontSize, value.toInt());
    436     else if (name == "WebKitUsesPageCachePreferenceKey")
    437         QWebSettings::setMaximumPagesInCache(value.toInt());
    438     else
    439         printf("ERROR: LayoutTestController::overridePreference() does not support the '%s' preference\n",
    440             name.toLatin1().data());
    441 }
    442 
    443 void LayoutTestController::setUserStyleSheetLocation(const QString& url)
    444 {
    445     m_userStyleSheetLocation = QUrl(url);
    446 }
    447 
    448 void LayoutTestController::setUserStyleSheetEnabled(bool enabled)
    449 {
    450     if (enabled)
    451         m_drt->webPage()->settings()->setUserStyleSheetUrl(m_userStyleSheetLocation);
    452     else
    453         m_drt->webPage()->settings()->setUserStyleSheetUrl(QUrl());
    454 }
    455 
    456 void LayoutTestController::setDomainRelaxationForbiddenForURLScheme(bool forbidden, const QString& scheme)
    457 {
    458     qt_drt_setDomainRelaxationForbiddenForURLScheme(forbidden, scheme);
    459 }
    460 
    461 int LayoutTestController::workerThreadCount()
    462 {
    463     return qt_drt_workerThreadCount();
    464 }
    465 
    466 int LayoutTestController::pageNumberForElementById(const QString& id, float width, float height)
    467 {
    468     // If no size specified, webpage viewport size is used
    469     if (!width && !height) {
    470         width = m_drt->webPage()->viewportSize().width();
    471         height = m_drt->webPage()->viewportSize().height();
    472     }
    473 
    474     return qt_drt_pageNumberForElementById(m_drt->webPage()->mainFrame(), id, width, height);
    475 }
    476