diff options
Diffstat (limited to 'WebKitTools/DumpRenderTree/qt')
12 files changed, 568 insertions, 123 deletions
diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro index 81e929b..ad42bdd 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTree.pro @@ -17,29 +17,32 @@ INCLUDEPATH += ../../../JavaScriptCore/ForwardingHeaders INCLUDEPATH += $$BASEDIR DESTDIR = ../../../bin -CONFIG += link_pkgconfig -PKGCONFIG += fontconfig +!win32 { + CONFIG += link_pkgconfig + PKGCONFIG += fontconfig +} -QT = core gui network +QT = core gui network testlib macx: QT += xml HEADERS = $$BASEDIR/WorkQueue.h \ - WorkQueueItem.h \ - DumpRenderTree.h \ + DumpRenderTreeQt.h \ EventSenderQt.h \ TextInputControllerQt.h \ + WorkQueueItemQt.h \ LayoutTestControllerQt.h \ - jsobjects.h \ + GCControllerQt.h \ testplugin.h -SOURCES = $$BASEDIR/WorkQueue.cpp \ - DumpRenderTree.cpp \ +SOURCES = ../../../JavaScriptCore/wtf/Assertions.cpp \ + $$BASEDIR/WorkQueue.cpp \ + DumpRenderTreeQt.cpp \ EventSenderQt.cpp \ TextInputControllerQt.cpp \ WorkQueueItemQt.cpp \ LayoutTestControllerQt.cpp \ - main.cpp \ - jsobjects.cpp \ - testplugin.cpp + GCControllerQt.cpp \ + testplugin.cpp \ + main.cpp unix:!mac { QMAKE_RPATHDIR = $$OUTPUT_DIR/lib $$QMAKE_RPATHDIR diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.cpp b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp index 6d466bf..9916f24 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.cpp +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.cpp @@ -31,11 +31,11 @@ #include "config.h" -#include "DumpRenderTree.h" +#include "DumpRenderTreeQt.h" #include "EventSenderQt.h" +#include "GCControllerQt.h" #include "LayoutTestControllerQt.h" #include "TextInputControllerQt.h" -#include "jsobjects.h" #include "testplugin.h" #include "WorkQueue.h" @@ -48,6 +48,7 @@ #include <QFileInfo> #include <QFocusEvent> #include <QFontDatabase> +#include <QLocale> #include <QNetworkAccessManager> #include <QNetworkReply> #include <QNetworkRequest> @@ -66,7 +67,10 @@ #include <limits.h> +#ifndef Q_OS_WIN #include <unistd.h> +#endif + #include <qdebug.h> extern void qt_drt_run(bool b); @@ -86,13 +90,13 @@ const unsigned int maxViewHeight = 600; NetworkAccessManager::NetworkAccessManager(QObject* parent) : QNetworkAccessManager(parent) { -#ifndef QT_NO_SSL +#ifndef QT_NO_OPENSSL connect(this, SIGNAL(sslErrors(QNetworkReply*, const QList<QSslError>&)), this, SLOT(sslErrorsEncountered(QNetworkReply*, const QList<QSslError>&))); #endif } -#ifndef QT_NO_SSL +#ifndef QT_NO_OPENSSL void NetworkAccessManager::sslErrorsEncountered(QNetworkReply* reply, const QList<QSslError>& errors) { if (reply->url().host() == "127.0.0.1" || reply->url().host() == "localhost") { @@ -158,7 +162,6 @@ void WebPage::resetSettings() { // After each layout test, reset the settings that may have been changed by // layoutTestController.overridePreference() or similar. - settings()->resetFontSize(QWebSettings::DefaultFontSize); settings()->resetAttribute(QWebSettings::JavascriptCanOpenWindows); settings()->resetAttribute(QWebSettings::JavascriptEnabled); @@ -166,7 +169,12 @@ void WebPage::resetSettings() settings()->resetAttribute(QWebSettings::LinksIncludedInFocusChain); settings()->resetAttribute(QWebSettings::OfflineWebApplicationCacheEnabled); settings()->resetAttribute(QWebSettings::LocalContentCanAccessRemoteUrls); + + // globalSettings must be reset explicitly. + m_drt->layoutTestController()->setXSSAuditorEnabled(false); + QWebSettings::setMaximumPagesInCache(0); // reset to default + settings()->setUserStyleSheetUrl(QUrl()); // reset to default } QWebPage *WebPage::createWindow(QWebPage::WebWindowType) @@ -305,8 +313,8 @@ QObject* WebPage::createPlugin(const QString& classId, const QUrl& url, const QS DumpRenderTree::DumpRenderTree() : m_dumpPixels(false) , m_stdin(0) - , m_notifier(0) , m_enableTextOutput(false) + , m_singleFileMode(false) { qt_drt_overwritePluginDirectories(); QWebSettings::enablePersistentStorage(); @@ -317,9 +325,12 @@ DumpRenderTree::DumpRenderTree() m_page = new WebPage(m_mainView, this); m_mainView->setPage(m_page); - // create out controllers. This has to be done before connectFrame, + // create our controllers. This has to be done before connectFrame, // as it exports there to the JavaScript DOM window. m_controller = new LayoutTestController(this); + connect(m_controller, SIGNAL(showPage()), this, SLOT(showPage())); + connect(m_controller, SIGNAL(hidePage()), this, SLOT(hidePage())); + connect(m_controller, SIGNAL(done()), this, SLOT(dump())); m_eventSender = new EventSender(m_page); m_textInputController = new TextInputController(m_page); @@ -332,6 +343,11 @@ DumpRenderTree::DumpRenderTree() connect(m_page, SIGNAL(loadFinished(bool)), m_controller, SLOT(maybeDump(bool))); + // We need to connect to loadStarted() because notifyDone should only + // dump results itself when the last page loaded in the test has finished loading. + connect(m_page, SIGNAL(loadStarted()), + m_controller, SLOT(resetLoadFinished())); + connect(m_page, SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested())); connect(m_page->mainFrame(), SIGNAL(titleChanged(const QString&)), SLOT(titleChanged(const QString&))); @@ -350,20 +366,6 @@ DumpRenderTree::~DumpRenderTree() { delete m_mainView; delete m_stdin; - delete m_notifier; -} - -void DumpRenderTree::open() -{ - if (!m_stdin) { - m_stdin = new QFile; - m_stdin->open(stdin, QFile::ReadOnly); - } - - if (!m_notifier) { - m_notifier = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read); - connect(m_notifier, SIGNAL(activated(int)), this, SLOT(readStdin(int))); - } } static void clearHistory(QWebPage* page) @@ -438,6 +440,7 @@ void DumpRenderTree::open(const QUrl& aurl) QFocusEvent ev(QEvent::FocusIn); m_page->event(&ev); + QWebSettings::clearMemoryCaches(); QFontDatabase::removeAllApplicationFonts(); #if defined(Q_WS_X11) initializeFonts(); @@ -448,20 +451,59 @@ void DumpRenderTree::open(const QUrl& aurl) m_page->mainFrame()->load(url); } -void DumpRenderTree::readStdin(int /* socket */) +void DumpRenderTree::readLine() +{ + if (!m_stdin) { + m_stdin = new QFile; + m_stdin->open(stdin, QFile::ReadOnly); + + if (!m_stdin->isReadable()) { + emit quit(); + return; + } + } + + QByteArray line = m_stdin->readLine().trimmed(); + + if (line.isEmpty()) { + emit quit(); + return; + } + + processLine(QString::fromLocal8Bit(line.constData(), line.length())); +} + +void DumpRenderTree::processLine(const QString &input) { - // Read incoming data from stdin... - QByteArray line = m_stdin->readLine(); - if (line.endsWith('\n')) - line.truncate(line.size()-1); - //fprintf(stderr, "\n opening %s\n", line.constData()); - if (line.isEmpty()) - quit(); + QString line = input; - if (line.startsWith("http:") || line.startsWith("https:")) + if (line.startsWith(QLatin1String("http:")) + || line.startsWith(QLatin1String("https:")) + || line.startsWith(QLatin1String("file:"))) { open(QUrl(line)); - else { + } else { QFileInfo fi(line); + + if (!fi.exists()) { + QDir currentDir = QDir::currentPath(); + + // Try to be smart about where the test is located + if (currentDir.dirName() == QLatin1String("LayoutTests")) + fi = QFileInfo(currentDir, line.replace(QRegExp(".*?LayoutTests/(.*)"), "\\1")); + else if (!line.contains(QLatin1String("LayoutTests"))) + fi = QFileInfo(currentDir, line.prepend(QLatin1String("LayoutTests/"))); + + if (!fi.exists()) { + if (isSingleFileMode()) + emit quit(); + else + emit ready(); + + return; + } + + } + open(QUrl::fromLocalFile(fi.absoluteFilePath())); } @@ -490,6 +532,18 @@ void DumpRenderTree::initJSObjects() frame->addToJavaScriptWindowObject(QLatin1String("GCController"), m_gcController); } +void DumpRenderTree::showPage() +{ + m_mainView->show(); + // we need a paint event but cannot process all the events + QPixmap pixmap(m_mainView->size()); + m_mainView->render(&pixmap); +} + +void DumpRenderTree::hidePage() +{ + m_mainView->hide(); +} QString DumpRenderTree::dumpFramesAsText(QWebFrame* frame) { @@ -602,11 +656,12 @@ static const char *methodNameStringForFailedTest(LayoutTestController *controlle void DumpRenderTree::dump() { + // Prevent any further frame load callbacks from appearing after we dump the result. + qt_dump_frame_loader(false); + QWebFrame *mainFrame = m_page->mainFrame(); - //fprintf(stderr, " Dumping\n"); - if (!m_notifier) { - // Dump markup in single file mode... + if (isSingleFileMode()) { QString markup = mainFrame->toHtml(); fprintf(stdout, "Source:\n\n%s\n", markup.toUtf8().constData()); } @@ -684,8 +739,10 @@ void DumpRenderTree::dump() fflush(stdout); fflush(stderr); - if (!m_notifier) - quit(); // Exit now in single file mode... + if (isSingleFileMode()) + emit quit(); + else + emit ready(); } void DumpRenderTree::titleChanged(const QString &s) @@ -741,15 +798,30 @@ QWebPage *DumpRenderTree::createWindow() connect(page, SIGNAL(frameCreated(QWebFrame*)), this, SLOT(connectFrame(QWebFrame*))); connectFrame(page->mainFrame()); connect(page, SIGNAL(loadFinished(bool)), m_controller, SLOT(maybeDump(bool))); + connect(page, SIGNAL(windowCloseRequested()), this, SLOT(windowCloseRequested())); return page; } +void DumpRenderTree::windowCloseRequested() +{ + QWebPage* page = qobject_cast<QWebPage*>(sender()); + QObject* container = page->parent(); + windows.removeAll(container); + container->deleteLater(); +} + int DumpRenderTree::windowCount() const { // include the main view in the count return windows.count() + 1; } +void DumpRenderTree::switchFocus(bool focused) +{ + QFocusEvent event((focused) ? QEvent::FocusIn : QEvent::FocusOut, Qt::ActiveWindowFocusReason); + QApplication::sendEvent(m_mainView, &event); +} + #if defined(Q_WS_X11) void DumpRenderTree::initializeFonts() { diff --git a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.h b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h index ab229fe..00d7ae4 100644 --- a/WebKitTools/DumpRenderTree/qt/DumpRenderTree.h +++ b/WebKitTools/DumpRenderTree/qt/DumpRenderTreeQt.h @@ -27,8 +27,8 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef DUMPRENDERTREE_H -#define DUMPRENDERTREE_H +#ifndef DumpRenderTreeQt_h +#define DumpRenderTreeQt_h #include <QList> #include <QNetworkAccessManager> @@ -36,7 +36,7 @@ #include <QTextStream> #include <QSocketNotifier> -#ifndef QT_NO_SSL +#ifndef QT_NO_OPENSSL #include <QSslError> #endif @@ -68,15 +68,15 @@ public: DumpRenderTree(); virtual ~DumpRenderTree(); - // Initialize in multi-file mode, used by run-webkit-tests. - void open(); - // Initialize in single-file mode. void open(const QUrl& url); void setTextOutputEnabled(bool enable) { m_enableTextOutput = enable; } bool isTextOutputEnabled() { return m_enableTextOutput; } + void setSingleFileMode(bool flag) { m_singleFileMode = flag; } + bool isSingleFileMode() { return m_singleFileMode; } + void setDumpPixels(bool); void closeRemainingWindows(); @@ -89,6 +89,8 @@ public: QWebPage *createWindow(); int windowCount() const; + void switchFocus(bool focused); + WebPage *webPage() const { return m_page; } @@ -98,15 +100,24 @@ public: public Q_SLOTS: void initJSObjects(); - void readStdin(int); + + void readLine(); + void processLine(const QString&); + void dump(); void titleChanged(const QString &s); void connectFrame(QWebFrame *frame); void dumpDatabaseQuota(QWebFrame* frame, const QString& dbName); void statusBarMessage(const QString& message); + void windowCloseRequested(); Q_SIGNALS: void quit(); + void ready(); + +private Q_SLOTS: + void showPage(); + void hidePage(); private: QString dumpFramesAsText(QWebFrame* frame); @@ -124,10 +135,10 @@ private: GCController* m_gcController; QFile *m_stdin; - QSocketNotifier* m_notifier; QList<QObject*> windows; bool m_enableTextOutput; + bool m_singleFileMode; }; class NetworkAccessManager : public QNetworkAccessManager { @@ -136,7 +147,7 @@ public: NetworkAccessManager(QObject* parent); private slots: -#ifndef QT_NO_SSL +#ifndef QT_NO_OPENSSL void sslErrorsEncountered(QNetworkReply*, const QList<QSslError>&); #endif }; diff --git a/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp b/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp index a0da273..73a9934 100644 --- a/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp +++ b/WebKitTools/DumpRenderTree/qt/EventSenderQt.cpp @@ -31,6 +31,8 @@ //#include <QtDebug> +#include <QtTest/QtTest> + #define KEYCODE_DEL 127 #define KEYCODE_BACKSPACE 8 #define KEYCODE_LEFTARROW 0xf702 @@ -38,11 +40,34 @@ #define KEYCODE_UPARROW 0xf700 #define KEYCODE_DOWNARROW 0xf701 +// Ports like Gtk and Windows expose a different approach for their zooming +// API if compared to Qt: they have specific methods for zooming in and out, +// as well as a settable zoom factor, while Qt has only a 'setZoomValue' method. +// Hence Qt DRT adopts a fixed zoom-factor (1.2) for compatibility. +#define ZOOM_STEP 1.2 + +#define DRT_MESSAGE_DONE (QEvent::User + 1) + +struct DRTEventQueue { + QEvent* m_event; + int m_delay; +}; + +static DRTEventQueue eventQueue[1024]; +static unsigned endOfQueue; +static unsigned startOfQueue; EventSender::EventSender(QWebPage* parent) : QObject(parent) { m_page = parent; + m_mouseButtonPressed = false; + m_drag = false; + memset(eventQueue, 0, sizeof(eventQueue)); + endOfQueue = 0; + startOfQueue = 0; + m_eventLoop = 0; + m_page->view()->installEventFilter(this); } void EventSender::mouseDown(int button) @@ -67,9 +92,11 @@ void EventSender::mouseDown(int button) break; } + m_mouseButtons |= mouseButton; + // qDebug() << "EventSender::mouseDown" << frame; - QMouseEvent event(QEvent::MouseButtonPress, m_mousePos, mouseButton, mouseButton, Qt::NoModifier); - QApplication::sendEvent(m_page, &event); + QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonPress, m_mousePos, m_mousePos, mouseButton, m_mouseButtons, Qt::NoModifier); + sendOrQueueEvent(event); } void EventSender::mouseUp(int button) @@ -94,26 +121,28 @@ void EventSender::mouseUp(int button) break; } + m_mouseButtons &= ~mouseButton; + // qDebug() << "EventSender::mouseUp" << frame; - QMouseEvent event(QEvent::MouseButtonRelease, m_mousePos, mouseButton, mouseButton, Qt::NoModifier); - QApplication::sendEvent(m_page, &event); + QMouseEvent* event = new QMouseEvent(QEvent::MouseButtonRelease, m_mousePos, m_mousePos, mouseButton, m_mouseButtons, Qt::NoModifier); + sendOrQueueEvent(event); } void EventSender::mouseMoveTo(int x, int y) { // qDebug() << "EventSender::mouseMoveTo" << x << y; m_mousePos = QPoint(x, y); - QMouseEvent event(QEvent::MouseMove, m_mousePos, Qt::NoButton, Qt::NoButton, Qt::NoModifier); - QApplication::sendEvent(m_page, &event); + QMouseEvent* event = new QMouseEvent(QEvent::MouseMove, m_mousePos, m_mousePos, Qt::NoButton, m_mouseButtons, Qt::NoModifier); + sendOrQueueEvent(event); } void EventSender::leapForward(int ms) { - m_timeLeap += ms; + eventQueue[endOfQueue].m_delay = ms; //qDebug() << "EventSender::leapForward" << ms; } -void EventSender::keyDown(const QString& string, const QStringList& modifiers) +void EventSender::keyDown(const QString& string, const QStringList& modifiers, unsigned int location) { QString s = string; Qt::KeyboardModifiers modifs = 0; @@ -128,12 +157,16 @@ void EventSender::keyDown(const QString& string, const QStringList& modifiers) else if (m == "metaKey") modifs |= Qt::MetaModifier; } + if (location == 3) + modifs |= Qt::KeypadModifier; int code = 0; if (string.length() == 1) { code = string.unicode()->unicode(); //qDebug() << ">>>>>>>>> keyDown" << code << (char)code; // map special keycodes used by the tests to something that works for Qt/X11 - if (code == '\t') { + if (code == '\r') { + code = Qt::Key_Return; + } else if (code == '\t') { code = Qt::Key_Tab; if (modifs == Qt::ShiftModifier) code = Qt::Key_Backtab; @@ -251,6 +284,131 @@ void EventSender::scheduleAsynchronousClick() QApplication::postEvent(m_page, event2); } +void EventSender::addTouchPoint(int x, int y) +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) + int id = m_touchPoints.count(); + QTouchEvent::TouchPoint point(id); + m_touchPoints.append(point); + updateTouchPoint(id, x, y); + m_touchPoints[id].setState(Qt::TouchPointPressed); +#endif +} + +void EventSender::updateTouchPoint(int index, int x, int y) +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) + if (index < 0 || index >= m_touchPoints.count()) + return; + + QTouchEvent::TouchPoint &p = m_touchPoints[index]; + p.setPos(QPointF(x, y)); + p.setState(Qt::TouchPointMoved); +#endif +} + +void EventSender::setTouchModifier(const QString &modifier, bool enable) +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) + Qt::KeyboardModifier mod = Qt::NoModifier; + if (!modifier.compare(QLatin1String("shift"), Qt::CaseInsensitive)) + mod = Qt::ShiftModifier; + else if (!modifier.compare(QLatin1String("alt"), Qt::CaseInsensitive)) + mod = Qt::AltModifier; + else if (!modifier.compare(QLatin1String("meta"), Qt::CaseInsensitive)) + mod = Qt::MetaModifier; + else if (!modifier.compare(QLatin1String("ctrl"), Qt::CaseInsensitive)) + mod = Qt::ControlModifier; + + if (enable) + m_touchModifiers |= mod; + else + m_touchModifiers &= ~mod; +#endif +} + +void EventSender::touchStart() +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) + if (!m_touchActive) { + sendTouchEvent(QEvent::TouchBegin); + m_touchActive = true; + } else + sendTouchEvent(QEvent::TouchUpdate); +#endif +} + +void EventSender::touchMove() +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) + sendTouchEvent(QEvent::TouchUpdate); +#endif +} + +void EventSender::touchEnd() +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) + for (int i = 0; i < m_touchPoints.count(); ++i) + if (m_touchPoints[i].state() != Qt::TouchPointReleased) { + sendTouchEvent(QEvent::TouchUpdate); + return; + } + sendTouchEvent(QEvent::TouchEnd); + m_touchActive = false; +#endif +} + +void EventSender::clearTouchPoints() +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) + m_touchPoints.clear(); + m_touchModifiers = Qt::KeyboardModifiers(); + m_touchActive = false; +#endif +} + +void EventSender::releaseTouchPoint(int index) +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) + if (index < 0 || index >= m_touchPoints.count()) + return; + + m_touchPoints[index].setState(Qt::TouchPointReleased); +#endif +} + +void EventSender::sendTouchEvent(QEvent::Type type) +{ +#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) + QTouchEvent event(type, QTouchEvent::TouchScreen, m_touchModifiers); + event.setTouchPoints(m_touchPoints); + QApplication::sendEvent(m_page, &event); + QList<QTouchEvent::TouchPoint>::Iterator it = m_touchPoints.begin(); + while (it != m_touchPoints.end()) { + if (it->state() == Qt::TouchPointReleased) + it = m_touchPoints.erase(it); + else { + it->setState(Qt::TouchPointStationary); + ++it; + } + } +#endif +} + +void EventSender::zoomPageIn() +{ + QWebFrame* frame = m_page->mainFrame(); + if (frame) + frame->setZoomFactor(frame->zoomFactor() * ZOOM_STEP); +} + +void EventSender::zoomPageOut() +{ + QWebFrame* frame = m_page->mainFrame(); + if (frame) + frame->setZoomFactor(frame->zoomFactor() / ZOOM_STEP); +} + QWebFrame* EventSender::frameUnderMouse() const { QWebFrame* frame = m_page->mainFrame(); @@ -267,3 +425,76 @@ redo: return frame; return 0; } + +void EventSender::sendOrQueueEvent(QEvent* event) +{ + // Mouse move events are queued if + // 1. A previous event was queued. + // 2. A delay was set-up by leapForward(). + // 3. A call to mouseMoveTo while the mouse button is pressed could initiate a drag operation, and that does not return until mouseUp is processed. + // To be safe and avoid a deadlock, this event is queued. + if (endOfQueue == startOfQueue && !eventQueue[endOfQueue].m_delay && (!(m_mouseButtonPressed && (m_eventLoop && event->type() == QEvent::MouseButtonRelease)))) { + QApplication::sendEvent(m_page->view(), event); + delete event; + return; + } + eventQueue[endOfQueue++].m_event = event; + eventQueue[endOfQueue].m_delay = 0; + replaySavedEvents(event->type() != QEvent::MouseMove); +} + +void EventSender::replaySavedEvents(bool flush) +{ + if (startOfQueue < endOfQueue) { + // First send all the events that are ready to be sent + while (!eventQueue[startOfQueue].m_delay && startOfQueue < endOfQueue) { + QEvent* ev = eventQueue[startOfQueue++].m_event; + QApplication::postEvent(m_page->view(), ev); // ev deleted by the system + } + if (startOfQueue == endOfQueue) { + // Reset the queue + startOfQueue = 0; + endOfQueue = 0; + } else { + QTest::qWait(eventQueue[startOfQueue].m_delay); + eventQueue[startOfQueue].m_delay = 0; + } + } + if (!flush) + return; + + // Send a marker event, it will tell us when it is safe to exit the new event loop + QEvent* drtEvent = new QEvent((QEvent::Type)DRT_MESSAGE_DONE); + QApplication::postEvent(m_page->view(), drtEvent); + + // Start an event loop for async handling of Drag & Drop + m_eventLoop = new QEventLoop; + m_eventLoop->exec(); + delete m_eventLoop; + m_eventLoop = 0; +} + +bool EventSender::eventFilter(QObject* watched, QEvent* event) +{ + if (watched != m_page->view()) + return false; + switch (event->type()) { + case QEvent::Leave: + return true; + case QEvent::MouseButtonPress: + m_mouseButtonPressed = true; + break; + case QEvent::MouseMove: + if (m_mouseButtonPressed) + m_drag = true; + break; + case QEvent::MouseButtonRelease: + m_mouseButtonPressed = false; + m_drag = false; + break; + case DRT_MESSAGE_DONE: + m_eventLoop->exit(); + return true; + } + return false; +} diff --git a/WebKitTools/DumpRenderTree/qt/EventSenderQt.h b/WebKitTools/DumpRenderTree/qt/EventSenderQt.h index fd74455..f6cfc7c 100644 --- a/WebKitTools/DumpRenderTree/qt/EventSenderQt.h +++ b/WebKitTools/DumpRenderTree/qt/EventSenderQt.h @@ -31,6 +31,7 @@ #include <QApplication> #include <QEvent> +#include <QEventLoop> #include <QMouseEvent> #include <QObject> #include <QPoint> @@ -40,25 +41,52 @@ #include <qwebpage.h> #include <qwebframe.h> +#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) +#include <QTouchEvent> +#endif + class EventSender : public QObject { Q_OBJECT public: EventSender(QWebPage* parent); + virtual bool eventFilter(QObject* watched, QEvent* event); public slots: void mouseDown(int button = 0); void mouseUp(int button = 0); void mouseMoveTo(int x, int y); void leapForward(int ms); - void keyDown(const QString& string, const QStringList& modifiers = QStringList()); + void keyDown(const QString& string, const QStringList& modifiers = QStringList(), unsigned int location = 0); void clearKillRing() {} void contextClick(); void scheduleAsynchronousClick(); + void addTouchPoint(int x, int y); + void updateTouchPoint(int index, int x, int y); + void setTouchModifier(const QString &modifier, bool enable); + void touchStart(); + void touchMove(); + void touchEnd(); + void zoomPageIn(); + void zoomPageOut(); + void clearTouchPoints(); + void releaseTouchPoint(int index); private: + void sendTouchEvent(QEvent::Type); + void sendOrQueueEvent(QEvent*); + void replaySavedEvents(bool flush); QPoint m_mousePos; + Qt::MouseButtons m_mouseButtons; QWebPage* m_page; int m_timeLeap; + bool m_mouseButtonPressed; + bool m_drag; + QEventLoop* m_eventLoop; QWebFrame* frameUnderMouse() const; +#if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) + QList<QTouchEvent::TouchPoint> m_touchPoints; + Qt::KeyboardModifiers m_touchModifiers; + bool m_touchActive; +#endif }; #endif // EventSenderQt_h diff --git a/WebKitTools/DumpRenderTree/qt/jsobjects.cpp b/WebKitTools/DumpRenderTree/qt/GCControllerQt.cpp index af331f0..9cc3aa7 100644 --- a/WebKitTools/DumpRenderTree/qt/jsobjects.cpp +++ b/WebKitTools/DumpRenderTree/qt/GCControllerQt.cpp @@ -28,20 +28,20 @@ */ #include "config.h" -#include "jsobjects.h" +#include "GCControllerQt.h" #include <qwebpage.h> -GCController::GCController(QWebPage* parent) - : QObject(parent) -{ -} - extern int qt_drt_javaScriptObjectsCount(); extern void qt_drt_garbageCollector_collect(); extern void qt_drt_garbageCollector_collectOnAlternateThread(bool waitUntilDone); +GCController::GCController(QWebPage* parent) + : QObject(parent) +{ +} + void GCController::collect() const { qt_drt_garbageCollector_collect(); diff --git a/WebKitTools/DumpRenderTree/qt/jsobjects.h b/WebKitTools/DumpRenderTree/qt/GCControllerQt.h index 3ee566b..8f5b432 100644 --- a/WebKitTools/DumpRenderTree/qt/jsobjects.h +++ b/WebKitTools/DumpRenderTree/qt/GCControllerQt.h @@ -26,8 +26,8 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef JSOBJECTS_H -#define JSOBJECTS_H +#ifndef GCControllerQt_h +#define GCControllerQt_h #include <QObject> diff --git a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp index 0ea5632..9430ec7 100644 --- a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp +++ b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.cpp @@ -29,10 +29,12 @@ #include "config.h" #include "LayoutTestControllerQt.h" -#include "DumpRenderTree.h" +#include "DumpRenderTreeQt.h" #include "WorkQueue.h" -#include "WorkQueueItem.h" +#include "WorkQueueItemQt.h" #include <QDir> +#include <QLocale> +#include <qwebsettings.h> extern void qt_dump_editing_callbacks(bool b); extern void qt_dump_resource_load_callbacks(bool b); @@ -41,9 +43,11 @@ extern bool qt_drt_pauseAnimation(QWebFrame*, const QString& name, double time, extern bool qt_drt_pauseTransitionOfProperty(QWebFrame*, const QString& name, double time, const QString& elementId); extern bool qt_drt_pauseSVGAnimation(QWebFrame*, const QString& animationId, double time, const QString& elementId); extern int qt_drt_numberOfActiveAnimations(QWebFrame*); +extern void qt_drt_setDomainRelaxationForbiddenForURLScheme(bool forbidden, const QString& scheme); extern void qt_drt_whiteListAccessFromOrigin(const QString& sourceOrigin, const QString& destinationProtocol, const QString& destinationHost, bool allowDestinationSubdomains); extern QString qt_drt_counterValueForElementById(QWebFrame* qFrame, const QString& id); +extern int qt_drt_workerThreadCount(); LayoutTestController::LayoutTestController(WebCore::DumpRenderTree* drt) : QObject() @@ -54,7 +58,8 @@ LayoutTestController::LayoutTestController(WebCore::DumpRenderTree* drt) void LayoutTestController::reset() { - m_isLoading = true; + m_hasDumped = false; + m_loadFinished = false; m_textDump = false; m_dumpBackForwardList = false; m_dumpChildrenAsText = false; @@ -67,8 +72,10 @@ void LayoutTestController::reset() m_topLoadingFrame = 0; m_waitForPolicy = false; m_handleErrorPages = false; + m_webHistory = 0; qt_dump_editing_callbacks(false); qt_dump_resource_load_callbacks(false); + emit hidePage(); } void LayoutTestController::processWork() @@ -78,32 +85,40 @@ void LayoutTestController::processWork() // if we didn't start a new load, then we finished all the commands, so we're ready to dump state if (WorkQueue::shared()->processWork() && !shouldWaitUntilDone()) { emit done(); - m_isLoading = false; + m_hasDumped = true; } } -// Called on loadFinished on mainFrame. +// Called on loadFinished on WebPage void LayoutTestController::maybeDump(bool success) { - Q_ASSERT(sender() == m_topLoadingFrame->page()); + // This can happen on any of the http/tests/security/window-events-*.html tests, where the test opens + // a new window, calls the unload and load event handlers on the window's page, and then immediately + // issues a notifyDone. Needs investigation. + if (!m_topLoadingFrame) + return; + + // It is possible that we get called by windows created from the main page that have finished + // loading, so we don't ASSERT here. At the moment we do not gather results from such windows, + // but may need to in future. + if (sender() != m_topLoadingFrame->page()) + return; + + m_loadFinished = true; // as the function is called on loadFinished, the test might // already have dumped and thus no longer be active, thus // bail out here. - if (!m_isLoading) + if (m_hasDumped) return; - m_topLoadingFrame = 0; WorkQueue::shared()->setFrozen(true); // first complete load freezes the queue for the rest of this test - - if (!shouldWaitUntilDone()) { - if (WorkQueue::shared()->count()) - QTimer::singleShot(0, this, SLOT(processWork())); - else { - if (success) - emit done(); - m_isLoading = false; - } + if (WorkQueue::shared()->count()) + QTimer::singleShot(0, this, SLOT(processWork())); + else if (!shouldWaitUntilDone()) { + if (success) + emit done(); + m_hasDumped = true; } } @@ -119,9 +134,19 @@ QString LayoutTestController::counterValueForElementById(const QString& id) return qt_drt_counterValueForElementById(m_drt->webPage()->mainFrame(), id); } +int LayoutTestController::webHistoryItemCount() +{ + if (!m_webHistory) + return -1; + + // Subtract one here as our QWebHistory::count() includes the actual page, + // which is not considered in the DRT tests. + return m_webHistory->count() - 1; +} + void LayoutTestController::keepWebHistory() { - // FIXME: implement + m_webHistory = m_drt->webPage()->history(); } void LayoutTestController::notifyDone() @@ -132,11 +157,19 @@ void LayoutTestController::notifyDone() return; m_timeoutTimer.stop(); + m_waitForDone = false; + + // If the page has not finished loading (i.e. loadFinished() has not been emitted) then + // content created by the likes of document.write() JS methods will not be available yet. + // When the page has finished loading, maybeDump above will dump the results now that we have + // just set shouldWaitUntilDone to false. + if (!m_loadFinished) + return; + emit done(); // FIXME: investigate why always resetting these result in timeouts - m_isLoading = false; - m_waitForDone = false; + m_hasDumped = true; m_waitForPolicy = false; } @@ -145,6 +178,11 @@ int LayoutTestController::windowCount() return m_drt->windowCount(); } +void LayoutTestController::display() +{ + emit showPage(); +} + void LayoutTestController::clearBackForwardList() { m_drt->webPage()->history()->clear(); @@ -153,7 +191,7 @@ void LayoutTestController::clearBackForwardList() QString LayoutTestController::pathToLocalResource(const QString& url) { // Function introduced in r28690. - return QLatin1String("file://") + QUrl(url).toLocalFile(); + return QDir::toNativeSeparators(url); } void LayoutTestController::dumpEditingCallbacks() @@ -208,7 +246,7 @@ void LayoutTestController::queueNonLoadingScript(const QString& script) void LayoutTestController::provisionalLoad() { QWebFrame* frame = qobject_cast<QWebFrame*>(sender()); - if (!m_topLoadingFrame && m_isLoading) + if (!m_topLoadingFrame && !m_hasDumped) m_topLoadingFrame = frame; } @@ -280,6 +318,26 @@ void LayoutTestController::setPOSIXLocale(const QString& locale) QLocale::setDefault(qlocale); } +void LayoutTestController::setWindowIsKey(bool isKey) +{ + m_drt->switchFocus(isKey); +} + +void LayoutTestController::setMainFrameIsFirstResponder(bool isFirst) +{ + //FIXME: only need this for the moment: https://bugs.webkit.org/show_bug.cgi?id=32990 +} + +void LayoutTestController::setXSSAuditorEnabled(bool enable) +{ + // Set XSSAuditorEnabled globally so that windows created by the test inherit it too. + // resetSettings() will call this to reset the page and global setting to false again. + // Needed by http/tests/security/xssAuditor/link-opens-new-window.html + QWebSettings* globalSettings = QWebSettings::globalSettings(); + globalSettings->setAttribute(QWebSettings::XSSAuditorEnabled, enable); + m_drt->webPage()->settings()->setAttribute(QWebSettings::XSSAuditorEnabled, enable); +} + bool LayoutTestController::pauseAnimationAtTimeOnElementWithId(const QString& animationName, double time, const QString& elementId) @@ -363,3 +421,26 @@ void LayoutTestController::overridePreference(const QString& name, const QVarian else if (name == "WebKitUsesPageCachePreferenceKey") QWebSettings::setMaximumPagesInCache(value.toInt()); } + +void LayoutTestController::setUserStyleSheetLocation(const QString& url) +{ + m_userStyleSheetLocation = QUrl(url); +} + +void LayoutTestController::setUserStyleSheetEnabled(bool enabled) +{ + if (enabled) + m_drt->webPage()->settings()->setUserStyleSheetUrl(m_userStyleSheetLocation); + else + m_drt->webPage()->settings()->setUserStyleSheetUrl(QUrl()); +} + +void LayoutTestController::setDomainRelaxationForbiddenForURLScheme(bool forbidden, const QString& scheme) +{ + qt_drt_setDomainRelaxationForbiddenForURLScheme(forbidden, scheme); +} + +int LayoutTestController::workerThreadCount() +{ + return qt_drt_workerThreadCount(); +} diff --git a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h index ac8681f..64b3a15 100644 --- a/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h +++ b/WebKitTools/DumpRenderTree/qt/LayoutTestControllerQt.h @@ -51,12 +51,11 @@ namespace WebCore { } class LayoutTestController : public QObject { Q_OBJECT + Q_PROPERTY(int webHistoryItemCount READ webHistoryItemCount) + Q_PROPERTY(int workerThreadCount READ workerThreadCount) public: LayoutTestController(WebCore::DumpRenderTree* drt); - bool isLoading() const { return m_isLoading; } - void setLoading(bool loading) { m_isLoading = loading; } - bool shouldDumpAsText() const { return m_textDump; } bool shouldDumpBackForwardList() const { return m_dumpBackForwardList; } bool shouldDumpChildrenAsText() const { return m_dumpChildrenAsText; } @@ -76,6 +75,9 @@ protected: signals: void done(); + void showPage(); + void hidePage(); + public slots: void maybeDump(bool ok); void dumpAsText() { m_textDump = true; } @@ -85,6 +87,7 @@ public slots: void setCanOpenWindows() { m_canOpenWindows = true; } void waitUntilDone(); QString counterValueForElementById(const QString& id); + int webHistoryItemCount(); void keepWebHistory(); void notifyDone(); void dumpBackForwardList() { m_dumpBackForwardList = true; } @@ -100,7 +103,7 @@ public slots: void provisionalLoad(); void setCloseRemainingWindowsWhenComplete(bool = false) {} int windowCount(); - void display() {} + void display(); void clearBackForwardList(); QString pathToLocalResource(const QString& url); void dumpTitleChanges() { m_dumpTitleChanges = true; } @@ -115,6 +118,10 @@ public slots: void setPrivateBrowsingEnabled(bool enable); void setPopupBlockingEnabled(bool enable); void setPOSIXLocale(const QString& locale); + void resetLoadFinished() { m_loadFinished = false; } + void setWindowIsKey(bool isKey); + void setMainFrameIsFirstResponder(bool isFirst); + void setXSSAuditorEnabled(bool enable); bool pauseAnimationAtTimeOnElementWithId(const QString& animationName, double time, const QString& elementId); bool pauseTransitionAtTimeOnElementWithId(const QString& propertyName, double time, const QString& elementId); @@ -132,12 +139,16 @@ public slots: void waitForPolicyDelegate(); void overridePreference(const QString& name, const QVariant& value); + void setUserStyleSheetLocation(const QString& url); + void setUserStyleSheetEnabled(bool enabled); + void setDomainRelaxationForbiddenForURLScheme(bool forbidden, const QString& scheme); + int workerThreadCount(); private slots: void processWork(); private: - bool m_isLoading; + bool m_hasDumped; bool m_textDump; bool m_dumpBackForwardList; bool m_dumpChildrenAsText; @@ -148,10 +159,13 @@ private: bool m_dumpStatusCallbacks; bool m_waitForPolicy; bool m_handleErrorPages; + bool m_loadFinished; + QUrl m_userStyleSheetLocation; QBasicTimer m_timeoutTimer; QWebFrame* m_topLoadingFrame; WebCore::DumpRenderTree* m_drt; + QWebHistory* m_webHistory; }; #endif // LayoutTestControllerQt_h diff --git a/WebKitTools/DumpRenderTree/qt/WorkQueueItemQt.cpp b/WebKitTools/DumpRenderTree/qt/WorkQueueItemQt.cpp index 807a6a4..067e6aa 100644 --- a/WebKitTools/DumpRenderTree/qt/WorkQueueItemQt.cpp +++ b/WebKitTools/DumpRenderTree/qt/WorkQueueItemQt.cpp @@ -27,7 +27,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" -#include "WorkQueueItem.h" +#include "WorkQueueItemQt.h" QWebFrame* findFrameNamed(const QString& frameName, QWebFrame* frame) { diff --git a/WebKitTools/DumpRenderTree/qt/WorkQueueItem.h b/WebKitTools/DumpRenderTree/qt/WorkQueueItemQt.h index d534493..94da5e1 100644 --- a/WebKitTools/DumpRenderTree/qt/WorkQueueItem.h +++ b/WebKitTools/DumpRenderTree/qt/WorkQueueItemQt.h @@ -7,13 +7,13 @@ * are met: * * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. + * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. + * documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. + * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -27,8 +27,8 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef WorkQueueItem_h -#define WorkQueueItem_h +#ifndef WorkQueueItemQt_h +#define WorkQueueItemQt_h #include <QPointer> #include <QString> @@ -140,4 +140,4 @@ public: } }; -#endif // !defined(WorkQueueItem_h) +#endif // !defined(WorkQueueItemQt_h) diff --git a/WebKitTools/DumpRenderTree/qt/main.cpp b/WebKitTools/DumpRenderTree/qt/main.cpp index 719315f..69d3c23 100644 --- a/WebKitTools/DumpRenderTree/qt/main.cpp +++ b/WebKitTools/DumpRenderTree/qt/main.cpp @@ -27,7 +27,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "DumpRenderTree.h" +#include "DumpRenderTreeQt.h" #include <wtf/AlwaysInline.h> @@ -40,6 +40,7 @@ #include <qwebsettings.h> #include <qwebdatabase.h> #include <qdesktopservices.h> +#include <qtimer.h> #include <qwindowsstyle.h> #ifdef Q_WS_X11 @@ -89,12 +90,14 @@ QString get_backtrace() { return s; } +#ifndef Q_OS_WIN static NO_RETURN void crashHandler(int sig) { fprintf(stderr, "%s\n", strsignal(sig)); fprintf(stderr, "%s\n", get_backtrace().toLatin1().constData()); exit(128 + sig); } +#endif int main(int argc, char* argv[]) { @@ -121,6 +124,7 @@ int main(int argc, char* argv[]) QX11Info::setAppDpiX(0, 96); #endif +#ifndef Q_OS_WIN signal(SIGILL, crashHandler); /* 4: illegal instruction (not reset when caught) */ signal(SIGTRAP, crashHandler); /* 5: trace trap (not reset when caught) */ signal(SIGFPE, crashHandler); /* 8: floating point exception */ @@ -130,41 +134,42 @@ int main(int argc, char* argv[]) signal(SIGPIPE, crashHandler); /* 13: write on a pipe with no reader */ signal(SIGXCPU, crashHandler); /* 24: exceeded CPU time limit */ signal(SIGXFSZ, crashHandler); /* 25: exceeded file size limit */ +#endif QStringList args = app.arguments(); if (args.count() < 2) { - qDebug() << "Usage: DumpRenderTree [-v] filename"; + qDebug() << "Usage: DumpRenderTree [-v|--pixel-tests] filename"; exit(0); } - // supress debug output from Qt if not started with -v + // Suppress debug output from Qt if not started with -v if (!args.contains(QLatin1String("-v"))) qInstallMsgHandler(messageHandler); WebCore::DumpRenderTree dumper; - if (args.contains("--pixel-tests")) + if (args.contains(QLatin1String("--pixel-tests"))) dumper.setDumpPixels(true); QString dbDir = QDesktopServices::storageLocation(QDesktopServices::DataLocation) + QDir::separator() + "qtwebkitdrt"; QWebSettings::setOfflineStoragePath(dbDir); QWebDatabase::removeAllDatabases(); - if (args.last() == QLatin1String("-")) { - dumper.open(); + if (args.contains(QLatin1String("-"))) { + QObject::connect(&dumper, SIGNAL(ready()), &dumper, SLOT(readLine()), Qt::QueuedConnection); + QTimer::singleShot(0, &dumper, SLOT(readLine())); } else { - if (!args.last().startsWith("/") - && !args.last().startsWith("file:") - && !args.last().startsWith("http:") - && !args.last().startsWith("https:")) { - QString path = QDir::currentPath(); - if (!path.endsWith('/')) - path.append('/'); - args.last().prepend(path); + dumper.setSingleFileMode(true); + for (int i = 1; i < args.size(); ++i) { + if (!args.at(i).startsWith('-')) { + dumper.processLine(args.at(i)); + break; + } } - dumper.open(QUrl(args.last())); } + return app.exec(); + #ifdef Q_WS_X11 FcConfigSetCurrent(0); #endif |