3c972f6dc83ce3135eb81dca78e40f0237ec7562
[WebKit-https.git] / WebKitTools / DumpRenderTree / qt / DumpRenderTree.cpp
1 /*
2  * Copyright (C) 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org>
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
30 #include "DumpRenderTree.h"
31 #include "jsobjects.h"
32
33 #include <QDir>
34 #include <QFile>
35 #include <QTimer>
36 #include <QBoxLayout>
37 #include <QScrollArea>
38 #include <QApplication>
39 #include <QUrl>
40
41 #include <qwebpage.h>
42 #include <qwebframe.h>
43 #include <qwebsettings.h>
44
45 #include <unistd.h>
46 #include <qdebug.h>
47 extern void qt_dump_editing_callbacks(bool b);
48 extern void qt_dump_set_accepts_editing(bool b);
49
50
51 namespace WebCore {
52
53 // Choose some default values.
54 const unsigned int maxViewWidth = 800;
55 const unsigned int maxViewHeight = 600;
56
57 class WebFrame : public QWebFrame {
58 public:
59     WebFrame(QWebPage *parent, QWebFrameData *frameData)
60         : QWebFrame(parent, frameData) {}
61     WebFrame(QWebFrame *parent, QWebFrameData *frameData)
62         : QWebFrame(parent, frameData) {}
63 };
64
65 class WebPage : public QWebPage {
66 public:
67     WebPage(QWidget *parent, DumpRenderTree *drt);
68
69     QWebFrame *createFrame(QWebFrame *parentFrame, QWebFrameData *frameData);
70     QWebPage *createWindow();
71
72     void javaScriptAlert(QWebFrame *frame, const QString& message);
73     void javaScriptConsoleMessage(const QString& message, unsigned int lineNumber, const QString& sourceID);
74
75 private:
76     DumpRenderTree *m_drt;
77 };
78
79 WebPage::WebPage(QWidget *parent, DumpRenderTree *drt)
80     : QWebPage(parent), m_drt(drt)
81 {
82     QWebSettings s = settings();
83     s.setAttribute(QWebSettings::JavascriptCanOpenWindows, true);
84     setSettings(s);
85 }
86
87 QWebFrame *WebPage::createFrame(QWebFrame *parentFrame, QWebFrameData *frameData)
88 {
89     if (parentFrame) {
90         WebFrame *f = new WebFrame(parentFrame, frameData);
91         connect(f, SIGNAL(cleared()), m_drt, SLOT(initJSObjects()));
92         connect(f, SIGNAL(provisionalLoad()),
93                 m_drt->layoutTestController(), SLOT(provisionalLoad()));
94         return f;
95     }
96     WebFrame *f = new WebFrame(this, frameData);
97
98     connect(f, SIGNAL(cleared()), m_drt, SLOT(initJSObjects()));
99     connect(f, SIGNAL(provisionalLoad()),
100             m_drt->layoutTestController(), SLOT(provisionalLoad()));
101     connect(f, SIGNAL(loadDone(bool)),
102             m_drt->layoutTestController(), SLOT(maybeDump(bool)));
103
104     return f;
105 }
106
107 QWebPage *WebPage::createWindow()
108 {
109     return m_drt->createWindow();
110 }
111
112 void WebPage::javaScriptAlert(QWebFrame *frame, const QString& message)
113 {
114     fprintf(stdout, "ALERT: %s\n", message.toUtf8().constData());
115 }
116
117 void WebPage::javaScriptConsoleMessage(const QString& message, unsigned int lineNumber, const QString&)
118 {
119     fprintf (stdout, "CONSOLE MESSAGE: line %d: %s\n", lineNumber, message.toUtf8().constData());
120 }
121
122 DumpRenderTree::DumpRenderTree()
123     : m_stdin(0)
124     , m_notifier(0)
125 {
126     qt_dump_editing_callbacks(true);
127     m_controller = new LayoutTestController(this);
128     connect(m_controller, SIGNAL(done()), this, SLOT(dump()), Qt::QueuedConnection);
129
130     m_page = new WebPage(0, this);
131     m_page->resize(maxViewWidth, maxViewHeight);
132     m_page->mainFrame()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
133     m_page->mainFrame()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
134     connect(m_page, SIGNAL(titleChanged(const QString&)),
135             SLOT(titleChanged(const QString&)));
136
137     m_eventSender = new EventSender(m_page);
138
139     QObject::connect(this, SIGNAL(quit()), qApp, SLOT(quit()), Qt::QueuedConnection);
140 }
141
142 DumpRenderTree::~DumpRenderTree()
143 {
144     delete m_page;
145
146     delete m_stdin;
147     delete m_notifier;
148 }
149
150 void DumpRenderTree::open()
151 {
152     if (!m_stdin) {
153         m_stdin = new QFile;
154         m_stdin->open(stdin, QFile::ReadOnly);
155     }
156
157     if (!m_notifier) {
158         m_notifier = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read);
159         connect(m_notifier, SIGNAL(activated(int)), this, SLOT(readStdin(int)));
160     }
161 }
162
163 void DumpRenderTree::open(const QUrl& url)
164 {
165     resetJSObjects();
166     m_page->open(url);
167 }
168
169 void DumpRenderTree::readStdin(int /* socket */)
170 {
171     // Read incoming data from stdin...
172     QByteArray line = m_stdin->readLine();
173     if (line.endsWith('\n'))
174         line.truncate(line.size()-1);
175     //fprintf(stderr, "\n    opening %s\n", line.constData());
176     if (line.isEmpty())
177         quit();
178     QFileInfo fi(line);
179     open(QUrl::fromLocalFile(fi.absoluteFilePath()));
180     fflush(stdout);
181 }
182
183 void DumpRenderTree::resetJSObjects()
184 {
185     m_controller->reset();
186     foreach(QWidget *widget, windows)
187         delete widget;
188     windows.clear();
189 }
190
191 void DumpRenderTree::initJSObjects()
192 {
193     QWebFrame *frame = qobject_cast<QWebFrame*>(sender());
194     Q_ASSERT(frame);
195     frame->addToJSWindowObject("layoutTestController", m_controller);
196     frame->addToJSWindowObject("eventSender", m_eventSender);
197 }
198
199
200 QString DumpRenderTree::dumpFramesAsText(QWebFrame* frame)
201 {
202     if (!frame)
203         return QString();
204
205     QString result;
206     QWebFrame *parent = qobject_cast<QWebFrame *>(frame->parent());
207     if (parent) {
208         result.append(QLatin1String("\n--------\nFrame: '"));
209         result.append(frame->name());
210         result.append(QLatin1String("'\n--------\n"));
211     }
212
213     result.append(frame->innerText());
214     result.append(QLatin1String("\n"));
215
216     if (m_controller->shouldDumpChildrenAsText()) {
217         QList<QWebFrame *> children = frame->childFrames();
218         for (int i = 0; i < children.size(); ++i)
219             result += dumpFramesAsText(children.at(i));
220     }
221
222     return result;
223 }
224
225 void DumpRenderTree::dump()
226 {
227     QWebFrame *frame = m_page->mainFrame();
228
229     //fprintf(stderr, "    Dumping\n");
230     if (!m_notifier) {
231         // Dump markup in single file mode...
232         QString markup = frame->markup();
233         fprintf(stdout, "Source:\n\n%s\n", markup.toUtf8().constData());
234     }
235
236     // Dump render text...
237     QString renderDump;
238     if (m_controller->shouldDumpAsText()) {
239         renderDump = dumpFramesAsText(frame);
240     } else {
241         renderDump = frame->renderTreeDump();
242     }
243     if (renderDump.isEmpty()) {
244         printf("ERROR: nil result from %s", m_controller->shouldDumpAsText() ? "[documentElement innerText]" : "[frame renderTreeAsExternalRepresentation]");
245     } else {
246         fprintf(stdout, "%s", renderDump.toUtf8().constData());
247     }
248
249     fprintf(stdout, "#EOF\n");
250
251     fflush(stdout);
252
253     if (!m_notifier) {
254         // Exit now in single file mode...
255         quit();
256     }
257 }
258
259 void DumpRenderTree::titleChanged(const QString &s)
260 {
261     if (m_controller->shouldDumpTitleChanges())
262         printf("TITLE CHANGED: %s\n", s.toUtf8().data());
263 }
264
265
266 QWebPage *DumpRenderTree::createWindow()
267 {
268     if (!m_controller->canOpenWindows())
269         return 0;
270     QWidget *container = new QWidget(0);
271     container->resize(0, 0);
272     container->move(-1, -1);
273     container->hide();
274     QWebPage *page = new WebPage(container, this);
275     windows.append(container);
276     return page;
277 }
278
279 int DumpRenderTree::windowCount() const
280 {
281     int count = 0;
282     foreach(QWidget *w, windows) {
283         if (w->children().count())
284             ++count;
285     }
286     return count + 1;
287 }
288
289 }