implement layoutTestController.dumpEditingCallbacks() correctly.
[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     m_controller = new LayoutTestController(this);
127     connect(m_controller, SIGNAL(done()), this, SLOT(dump()), Qt::QueuedConnection);
128
129     m_page = new WebPage(0, this);
130     m_page->resize(maxViewWidth, maxViewHeight);
131     m_page->mainFrame()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
132     m_page->mainFrame()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
133     connect(m_page, SIGNAL(titleChanged(const QString&)),
134             SLOT(titleChanged(const QString&)));
135
136     m_eventSender = new EventSender(m_page);
137
138     QObject::connect(this, SIGNAL(quit()), qApp, SLOT(quit()), Qt::QueuedConnection);
139 }
140
141 DumpRenderTree::~DumpRenderTree()
142 {
143     delete m_page;
144
145     delete m_stdin;
146     delete m_notifier;
147 }
148
149 void DumpRenderTree::open()
150 {
151     if (!m_stdin) {
152         m_stdin = new QFile;
153         m_stdin->open(stdin, QFile::ReadOnly);
154     }
155
156     if (!m_notifier) {
157         m_notifier = new QSocketNotifier(STDIN_FILENO, QSocketNotifier::Read);
158         connect(m_notifier, SIGNAL(activated(int)), this, SLOT(readStdin(int)));
159     }
160 }
161
162 void DumpRenderTree::open(const QUrl& url)
163 {
164     resetJSObjects();
165     m_page->open(url);
166 }
167
168 void DumpRenderTree::readStdin(int /* socket */)
169 {
170     // Read incoming data from stdin...
171     QByteArray line = m_stdin->readLine();
172     if (line.endsWith('\n'))
173         line.truncate(line.size()-1);
174     //fprintf(stderr, "\n    opening %s\n", line.constData());
175     if (line.isEmpty())
176         quit();
177     QFileInfo fi(line);
178     open(QUrl::fromLocalFile(fi.absoluteFilePath()));
179     fflush(stdout);
180 }
181
182 void DumpRenderTree::resetJSObjects()
183 {
184     m_controller->reset();
185     foreach(QWidget *widget, windows)
186         delete widget;
187     windows.clear();
188 }
189
190 void DumpRenderTree::initJSObjects()
191 {
192     QWebFrame *frame = qobject_cast<QWebFrame*>(sender());
193     Q_ASSERT(frame);
194     frame->addToJSWindowObject("layoutTestController", m_controller);
195     frame->addToJSWindowObject("eventSender", m_eventSender);
196 }
197
198
199 QString DumpRenderTree::dumpFramesAsText(QWebFrame* frame)
200 {
201     if (!frame)
202         return QString();
203
204     QString result;
205     QWebFrame *parent = qobject_cast<QWebFrame *>(frame->parent());
206     if (parent) {
207         result.append(QLatin1String("\n--------\nFrame: '"));
208         result.append(frame->name());
209         result.append(QLatin1String("'\n--------\n"));
210     }
211
212     result.append(frame->innerText());
213     result.append(QLatin1String("\n"));
214
215     if (m_controller->shouldDumpChildrenAsText()) {
216         QList<QWebFrame *> children = frame->childFrames();
217         for (int i = 0; i < children.size(); ++i)
218             result += dumpFramesAsText(children.at(i));
219     }
220
221     return result;
222 }
223
224 void DumpRenderTree::dump()
225 {
226     QWebFrame *frame = m_page->mainFrame();
227
228     //fprintf(stderr, "    Dumping\n");
229     if (!m_notifier) {
230         // Dump markup in single file mode...
231         QString markup = frame->markup();
232         fprintf(stdout, "Source:\n\n%s\n", markup.toUtf8().constData());
233     }
234
235     // Dump render text...
236     QString renderDump;
237     if (m_controller->shouldDumpAsText()) {
238         renderDump = dumpFramesAsText(frame);
239     } else {
240         renderDump = frame->renderTreeDump();
241     }
242     if (renderDump.isEmpty()) {
243         printf("ERROR: nil result from %s", m_controller->shouldDumpAsText() ? "[documentElement innerText]" : "[frame renderTreeAsExternalRepresentation]");
244     } else {
245         fprintf(stdout, "%s", renderDump.toUtf8().constData());
246     }
247
248     fprintf(stdout, "#EOF\n");
249
250     fflush(stdout);
251
252     if (!m_notifier) {
253         // Exit now in single file mode...
254         quit();
255     }
256 }
257
258 void DumpRenderTree::titleChanged(const QString &s)
259 {
260     if (m_controller->shouldDumpTitleChanges())
261         printf("TITLE CHANGED: %s\n", s.toUtf8().data());
262 }
263
264
265 QWebPage *DumpRenderTree::createWindow()
266 {
267     if (!m_controller->canOpenWindows())
268         return 0;
269     QWidget *container = new QWidget(0);
270     container->resize(0, 0);
271     container->move(-1, -1);
272     container->hide();
273     QWebPage *page = new WebPage(container, this);
274     windows.append(container);
275     return page;
276 }
277
278 int DumpRenderTree::windowCount() const
279 {
280     int count = 0;
281     foreach(QWidget *w, windows) {
282         if (w->children().count())
283             ++count;
284     }
285     return count + 1;
286 }
287
288 }