e4d088b82400d3316d3b4f63fe02aa60f5c29c79
[WebKit-https.git] / WebCore / kwq / KWQRenderTreeDebug.cpp
1 /*
2  * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "KWQRenderTreeDebug.h"
28
29 #include "DocumentImpl.h"
30 #include "dom_position.h"
31 #include "jsediting.h"
32 #include "FrameView.h"
33 #include "render_canvas.h"
34 #include "render_replaced.h"
35 #include "RenderTableCell.h"
36 #include "InlineTextBox.h"
37 #include "render_br.h"
38 #include "SelectionController.h"
39
40 #if SVG_SUPPORT
41 #include "KCanvasTreeDebug.h"
42 #include "KCanvasItem.h"
43 #include "KCanvasContainer.h"
44 #endif
45
46 #include "Frame.h"
47 #include "KWQTextStream.h"
48 #include <kxmlcore/Vector.h>
49
50 using namespace DOM;
51 using namespace khtml;
52
53 static void writeLayers(QTextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0);
54
55 #if !SVG_SUPPORT
56 static QTextStream &operator<<(QTextStream &ts, const IntRect &r)
57 {
58     return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
59 }
60 #endif
61
62 static void writeIndent(QTextStream &ts, int indent)
63 {
64     for (int i = 0; i != indent; ++i) {
65         ts << "  ";
66     }
67 }
68
69 static void printBorderStyle(QTextStream &ts, const RenderObject &o, const EBorderStyle borderStyle)
70 {
71     switch (borderStyle) {
72         case khtml::BNONE:
73             ts << "none";
74             break;
75         case khtml::BHIDDEN:
76             ts << "hidden";
77             break;
78         case khtml::INSET:
79             ts << "inset";
80             break;
81         case khtml::GROOVE:
82             ts << "groove";
83             break;
84         case khtml::RIDGE:
85             ts << "ridge";
86             break;
87         case khtml::OUTSET:
88             ts << "outset";
89             break;
90         case khtml::DOTTED:
91             ts << "dotted";
92             break;
93         case khtml::DASHED:
94             ts << "dashed";
95             break;
96         case khtml::SOLID:
97             ts << "solid";
98             break;
99         case khtml::DOUBLE:
100             ts << "double";
101             break;
102     }
103     
104     ts << " ";
105 }
106
107 static QString getTagName(NodeImpl *n)
108 {
109     if (n->isDocumentNode())
110         return "";
111     if (n->isTextNode())
112         return "TEXT"; // FIXME: Remove once the layout tests are ready to change.
113     if (n->isCommentNode())
114         return "COMMENT";
115     if (n->isHTMLElement())
116         return n->nodeName().upper().qstring(); // FIXME: We want to dump the real DOM name, not an uppercase name.
117     return n->nodeName().qstring(); 
118 }
119
120 static QTextStream &operator<<(QTextStream &ts, const RenderObject &o)
121 {
122     ts << o.renderName();
123     
124     if (o.style() && o.style()->zIndex()) {
125         ts << " zI: " << o.style()->zIndex();
126     }
127     
128     if (o.element()) {
129         QString tagName = getTagName(o.element());
130         if (!tagName.isEmpty()) {
131             ts << " {" << tagName << "}";
132         }
133     }
134     
135     // FIXME: Will remove this <br> code once all layout tests pass.  Until then, we can't really change
136     // all the results easily.
137     bool usePositions = true;
138     if (o.isBR()) {
139         const RenderBR* br = static_cast<const RenderBR*>(&o);
140         usePositions = (br->firstTextBox() && br->firstTextBox()->isText());
141     }
142     IntRect r(usePositions ? o.xPos() : 0, usePositions ? o.yPos() : 0, o.width(), o.height());
143     ts << " " << r;
144     
145     if (!o.isText()) {
146         if (o.parent() && (o.parent()->style()->color() != o.style()->color()))
147             ts << " [color=" << o.style()->color().name() << "]";
148         if (o.parent() && (o.parent()->style()->backgroundColor() != o.style()->backgroundColor()) &&
149             o.style()->backgroundColor().isValid() && o.style()->backgroundColor().rgb())
150             // Do not dump invalid or transparent backgrounds, since that is the default.
151             ts << " [bgcolor=" << o.style()->backgroundColor().name() << "]";
152     
153         if (o.borderTop() || o.borderRight() || o.borderBottom() || o.borderLeft()) {
154             ts << " [border:";
155             
156             BorderValue prevBorder;
157             if (o.style()->borderTop() != prevBorder) {
158                 prevBorder = o.style()->borderTop();
159                 if (!o.borderTop())
160                     ts << " none";
161                 else {
162                     ts << " (" << o.borderTop() << "px ";
163                     printBorderStyle(ts, o, o.style()->borderTopStyle());
164                     Color col = o.style()->borderTopColor();
165                     if (!col.isValid()) col = o.style()->color();
166                     ts << col.name() << ")";
167                 }
168             }
169             
170             if (o.style()->borderRight() != prevBorder) {
171                 prevBorder = o.style()->borderRight();
172                 if (!o.borderRight())
173                     ts << " none";
174                 else {
175                     ts << " (" << o.borderRight() << "px ";
176                     printBorderStyle(ts, o, o.style()->borderRightStyle());
177                     Color col = o.style()->borderRightColor();
178                     if (!col.isValid()) col = o.style()->color();
179                     ts << col.name() << ")";
180                 }
181             }
182             
183             if (o.style()->borderBottom() != prevBorder) {
184                 prevBorder = o.style()->borderBottom();
185                 if (!o.borderBottom())
186                     ts << " none";
187                 else {
188                     ts << " (" << o.borderBottom() << "px ";
189                     printBorderStyle(ts, o, o.style()->borderBottomStyle());
190                     Color col = o.style()->borderBottomColor();
191                     if (!col.isValid()) col = o.style()->color();
192                     ts << col.name() << ")";
193                 }
194             }
195             
196             if (o.style()->borderLeft() != prevBorder) {
197                 prevBorder = o.style()->borderLeft();
198                 if (!o.borderLeft())
199                     ts << " none";
200                 else {                    
201                     ts << " (" << o.borderLeft() << "px ";
202                     printBorderStyle(ts, o, o.style()->borderLeftStyle());
203                     Color col = o.style()->borderLeftColor();
204                     if (!col.isValid()) col = o.style()->color();
205                     ts << col.name() << ")";
206                 }
207             }
208             
209             ts << "]";
210         }
211     }
212     
213     if (o.isTableCell()) {
214         const RenderTableCell &c = static_cast<const RenderTableCell &>(o);
215         ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
216     }
217
218     return ts;
219 }
220
221 static QString quoteAndEscapeNonPrintables(const QString &s)
222 {
223     QString result;
224     result += '"';
225     for (uint i = 0; i != s.length(); ++i) {
226         QChar c = s.at(i);
227         if (c == '\\') {
228             result += "\\\\";
229         } else if (c == '"') {
230             result += "\\\"";
231         } else if (c == '\n' || c.unicode() == 0x00A0) {
232             result += ' ';
233         } else {
234             ushort u = c.unicode();
235             if (u >= 0x20 && u < 0x7F) {
236                 result += c;
237             } else {
238                 QString hex;
239                 hex.sprintf("\\x{%X}", u);
240                 result += hex;
241             }
242         }
243     }
244     result += '"';
245     return result;
246 }
247
248 static void writeTextRun(QTextStream &ts, const RenderText &o, const InlineTextBox &run)
249 {
250     ts << "text run at (" << run.m_x << "," << run.m_y << ") width " << run.m_width;
251     if (run.m_reversed || run.m_dirOverride) {
252         ts << (run.m_reversed ? " RTL" : " LTR");
253         if (run.m_dirOverride)
254             ts << " override";
255     }
256     ts << ": "
257         << quoteAndEscapeNonPrintables(o.data().qstring().mid(run.m_start, run.m_len))
258         << "\n"; 
259 }
260
261 void write(QTextStream &ts, const RenderObject &o, int indent)
262 {
263 #if SVG_SUPPORT
264     // FIXME:  A hackish way to doing our own "virtual" dispatch
265     if (o.isRenderPath()) {
266         write(ts, static_cast<const RenderPath &>(o), indent);
267         return;
268     }
269     if (o.isKCanvasContainer()) {
270         write(ts, static_cast<const KCanvasContainer &>(o), indent);
271         return;
272     }
273 #endif
274     writeIndent(ts, indent);
275     
276     ts << o << "\n";
277     
278     if (o.isText() && !o.isBR()) {
279         const RenderText &text = static_cast<const RenderText &>(o);
280         for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
281             writeIndent(ts, indent+1);
282             writeTextRun(ts, text, *box);
283         }
284     }
285
286     for (RenderObject *child = o.firstChild(); child; child = child->nextSibling()) {
287         if (child->layer()) {
288             continue;
289         }
290         write(ts, *child, indent + 1);
291     }
292     
293     if (o.isWidget()) {
294         QWidget *widget = static_cast<const RenderWidget &>(o).widget();
295         if (widget && widget->inherits("FrameView")) {
296             FrameView *view = static_cast<FrameView *>(widget);
297             RenderObject *root = view->frame()->renderer();
298             if (root) {
299                 view->layout();
300                 RenderLayer* l = root->layer();
301                 if (l)
302                     writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height()), indent+1);
303             }
304         }
305     }
306 }
307
308 static void write(QTextStream &ts, RenderLayer &l,
309                   const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect,
310                   int layerType = 0, int indent = 0)
311 {
312     writeIndent(ts, indent);
313     
314     ts << "layer";
315     ts << " " << layerBounds;
316
317     if (layerBounds != layerBounds.intersect(backgroundClipRect))
318         ts << " backgroundClip " << backgroundClipRect;
319     if (layerBounds != layerBounds.intersect(clipRect))
320         ts << " clip " << clipRect;
321     if (layerBounds != layerBounds.intersect(outlineClipRect))
322         ts << " outlineClip " << outlineClipRect;
323
324     if (l.renderer()->hasOverflowClip()) {
325         if (l.scrollXOffset())
326             ts << " scrollX " << l.scrollXOffset();
327         if (l.scrollYOffset())
328             ts << " scrollY " << l.scrollYOffset();
329         if (l.renderer()->clientWidth() != l.scrollWidth())
330             ts << " scrollWidth " << l.scrollWidth();
331         if (l.renderer()->clientHeight() != l.scrollHeight())
332             ts << " scrollHeight " << l.scrollHeight();
333     }
334
335     if (layerType == -1)
336         ts << " layerType: background only";
337     else if (layerType == 1)
338         ts << " layerType: foreground only";
339     
340     ts << "\n";
341
342     if (layerType != -1)
343         write(ts, *l.renderer(), indent + 1);
344 }
345     
346 static void writeLayers(QTextStream &ts, const RenderLayer* rootLayer, RenderLayer* l,
347                         const IntRect& paintDirtyRect, int indent)
348 {
349     // Calculate the clip rects we should use.
350     IntRect layerBounds, damageRect, clipRectToApply, outlineRect;
351     l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect);
352     
353     // Ensure our z-order lists are up-to-date.
354     l->updateZOrderLists();
355
356     bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect);
357     Vector<RenderLayer*>* negList = l->negZOrderList();
358     if (shouldPaint && negList && negList->size() > 0)
359         write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, -1, indent);
360
361     if (negList) {
362         for (unsigned i = 0; i != negList->size(); ++i)
363             writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, indent);
364     }
365
366     if (shouldPaint)
367         write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, negList && negList->size() > 0, indent);
368
369     Vector<RenderLayer*>* posList = l->posZOrderList();
370     if (posList) {
371         for (unsigned i = 0; i != posList->size(); ++i)
372             writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, indent);
373     }
374 }
375
376 static QString nodePosition(NodeImpl *node)
377 {
378     QString result;
379
380     NodeImpl *p;
381     for (NodeImpl *n = node; n; n = p) {
382         p = n->parentNode();
383         if (n != node)
384             result += " of ";
385         if (p)
386             result += "child " + QString::number(n->nodeIndex()) + " {" + getTagName(n) + "}";
387         else
388             result += "document";
389     }
390
391     return result;
392 }
393
394 static void writeSelection(QTextStream &ts, const RenderObject *o)
395 {
396     NodeImpl *n = o->element();
397     if (!n || !n->isDocumentNode())
398         return;
399
400     DocumentImpl *doc = static_cast<DocumentImpl *>(n);
401     Frame *frame = doc->frame();
402     if (!frame)
403         return;
404
405     SelectionController selection = frame->selection();
406     if (selection.isCaret()) {
407         ts << "caret: position " << selection.start().offset() << " of " << nodePosition(selection.start().node());
408         if (selection.affinity() == UPSTREAM)
409             ts << " (upstream affinity)";
410         ts << "\n"; 
411     } else if (selection.isRange()) {
412         ts << "selection start: position " << selection.start().offset() << " of " << nodePosition(selection.start().node()) << "\n"
413            << "selection end:   position " << selection.end().offset() << " of " << nodePosition(selection.end().node()) << "\n"; 
414     }
415 }
416
417 static bool debuggingRenderTreeFlag = false;
418
419 bool debuggingRenderTree()
420 {
421     return debuggingRenderTreeFlag;
422 }
423
424 QString externalRepresentation(RenderObject *o)
425 {
426     debuggingRenderTreeFlag = true;
427     JSEditor::setSupportsPasteCommand(true);
428
429     QString s;
430     {
431         QTextStream ts(&s);
432 #if SVG_SUPPORT
433         ts.precision(2);
434         writeRenderResources(ts, o->document());
435 #endif
436         if (o) {
437             o->canvas()->view()->layout();
438             RenderLayer* l = o->layer();
439             if (l) {
440                 writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height()));
441                 writeSelection(ts, o);
442             }
443         }
444     }
445     return s;
446 }