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