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