Reviewed by Mitz.
[WebKit-https.git] / WebCore / rendering / RenderTreeAsText.cpp
1 /*
2  * Copyright (C) 2004, 2006 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 "RenderTreeAsText.h"
28
29 #include "Document.h"
30 #include "Frame.h"
31 #include "FrameView.h"
32 #include "HTMLElement.h"
33 #include "HTMLNames.h"
34 #include "InlineTextBox.h"
35 #include "JSEditor.h"
36 #include "RenderBR.h"
37 #include "RenderTableCell.h"
38 #include "RenderView.h"
39 #include "RenderWidget.h"
40 #include "SelectionController.h"
41 #include <wtf/Vector.h>
42
43 #ifdef SVG_SUPPORT
44 #include "RenderSVGContainer.h"
45 #include "SVGRenderTreeAsText.h"
46 #endif
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51
52 static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0);
53
54 #ifndef SVG_SUPPORT
55 static TextStream &operator<<(TextStream& ts, const IntRect& r)
56 {
57     return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
58 }
59 #endif
60
61 static void writeIndent(TextStream& ts, int indent)
62 {
63     for (int i = 0; i != indent; ++i)
64         ts << "  ";
65 }
66
67 static void printBorderStyle(TextStream& ts, const RenderObject& o, const EBorderStyle borderStyle)
68 {
69     switch (borderStyle) {
70         case BNONE:
71             ts << "none";
72             break;
73         case BHIDDEN:
74             ts << "hidden";
75             break;
76         case INSET:
77             ts << "inset";
78             break;
79         case GROOVE:
80             ts << "groove";
81             break;
82         case RIDGE:
83             ts << "ridge";
84             break;
85         case OUTSET:
86             ts << "outset";
87             break;
88         case DOTTED:
89             ts << "dotted";
90             break;
91         case DASHED:
92             ts << "dashed";
93             break;
94         case SOLID:
95             ts << "solid";
96             break;
97         case DOUBLE:
98             ts << "double";
99             break;
100     }
101
102     ts << " ";
103 }
104
105 static DeprecatedString getTagName(Node* n)
106 {
107     if (n->isDocumentNode())
108         return "";
109     if (n->isCommentNode())
110         return "COMMENT";
111     return n->nodeName().deprecatedString();
112 }
113
114 static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
115 {
116     if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
117         return false;
118
119     const HTMLElement* elem = static_cast<const HTMLElement*>(node);
120     if (elem->getAttribute(classAttr) != "Apple-style-span")
121         return false;
122
123     if (!node->hasChildNodes())
124         return true;
125
126     CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl();
127     return (!inlineStyleDecl || inlineStyleDecl->length() == 0);
128 }
129
130 static TextStream &operator<<(TextStream& ts, const RenderObject& o)
131 {
132     ts << o.renderName();
133
134     if (o.style() && o.style()->zIndex())
135         ts << " zI: " << o.style()->zIndex();
136
137     if (o.element()) {
138         DeprecatedString tagName = getTagName(o.element());
139         if (!tagName.isEmpty()) {
140             ts << " {" << tagName << "}";
141             // flag empty or unstyled AppleStyleSpan because we never
142             // want to leave them in the DOM
143             if (isEmptyOrUnstyledAppleStyleSpan(o.element()))
144                 ts << " *empty or unstyled AppleStyleSpan*";
145         }
146     }
147
148     IntRect r(o.xPos(), o.yPos(), o.width(), o.height());
149     ts << " " << r;
150
151     if (!o.isText()) {
152         if (o.parent() && (o.parent()->style()->color() != o.style()->color()))
153             ts << " [color=" << o.style()->color().name() << "]";
154         if (o.parent() && (o.parent()->style()->backgroundColor() != o.style()->backgroundColor()) &&
155             o.style()->backgroundColor().isValid() && o.style()->backgroundColor().rgb())
156             // Do not dump invalid or transparent backgrounds, since that is the default.
157             ts << " [bgcolor=" << o.style()->backgroundColor().name() << "]";
158
159         if (o.borderTop() || o.borderRight() || o.borderBottom() || o.borderLeft()) {
160             ts << " [border:";
161
162             BorderValue prevBorder;
163             if (o.style()->borderTop() != prevBorder) {
164                 prevBorder = o.style()->borderTop();
165                 if (!o.borderTop())
166                     ts << " none";
167                 else {
168                     ts << " (" << o.borderTop() << "px ";
169                     printBorderStyle(ts, o, o.style()->borderTopStyle());
170                     Color col = o.style()->borderTopColor();
171                     if (!col.isValid())
172                         col = o.style()->color();
173                     ts << col.name() << ")";
174                 }
175             }
176
177             if (o.style()->borderRight() != prevBorder) {
178                 prevBorder = o.style()->borderRight();
179                 if (!o.borderRight())
180                     ts << " none";
181                 else {
182                     ts << " (" << o.borderRight() << "px ";
183                     printBorderStyle(ts, o, o.style()->borderRightStyle());
184                     Color col = o.style()->borderRightColor();
185                     if (!col.isValid())
186                         col = o.style()->color();
187                     ts << col.name() << ")";
188                 }
189             }
190
191             if (o.style()->borderBottom() != prevBorder) {
192                 prevBorder = o.style()->borderBottom();
193                 if (!o.borderBottom())
194                     ts << " none";
195                 else {
196                     ts << " (" << o.borderBottom() << "px ";
197                     printBorderStyle(ts, o, o.style()->borderBottomStyle());
198                     Color col = o.style()->borderBottomColor();
199                     if (!col.isValid())
200                         col = o.style()->color();
201                     ts << col.name() << ")";
202                 }
203             }
204
205             if (o.style()->borderLeft() != prevBorder) {
206                 prevBorder = o.style()->borderLeft();
207                 if (!o.borderLeft())
208                     ts << " none";
209                 else {
210                     ts << " (" << o.borderLeft() << "px ";
211                     printBorderStyle(ts, o, o.style()->borderLeftStyle());
212                     Color col = o.style()->borderLeftColor();
213                     if (!col.isValid())
214                         col = o.style()->color();
215                     ts << col.name() << ")";
216                 }
217             }
218
219             ts << "]";
220         }
221     }
222
223     if (o.isTableCell()) {
224         const RenderTableCell& c = static_cast<const RenderTableCell&>(o);
225         ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
226     }
227
228     return ts;
229 }
230
231 static String quoteAndEscapeNonPrintables(const String& s)
232 {
233     DeprecatedString result;
234     result += '"';
235     for (unsigned i = 0; i != s.length(); ++i) {
236         UChar c = s[i];
237         if (c == '\\')
238             result += "\\\\";
239         else if (c == '"')
240             result += "\\\"";
241         else if (c == '\n' || c == 0x00A0)
242             result += ' ';
243         else {
244             if (c >= 0x20 && c < 0x7F)
245                 result += c;
246             else {
247                 DeprecatedString hex;
248                 unsigned u = c;
249                 hex.format("\\x{%X}", u);
250                 result += hex;
251             }
252         }
253     }
254     result += '"';
255     return result;
256 }
257
258 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
259 {
260     ts << "text run at (" << run.m_x << "," << run.m_y << ") width " << run.m_width;
261     if (run.m_reversed || run.m_dirOverride) {
262         ts << (run.m_reversed ? " RTL" : " LTR");
263         if (run.m_dirOverride)
264             ts << " override";
265     }
266     ts << ": "
267         << quoteAndEscapeNonPrintables(o.data().substring(run.m_start, run.m_len))
268         << "\n";
269 }
270
271 void write(TextStream& ts, const RenderObject& o, int indent)
272 {
273 #ifdef SVG_SUPPORT
274     // FIXME:  A hackish way to doing our own "virtual" dispatch
275     if (o.isRenderPath()) {
276         write(ts, static_cast<const RenderPath&>(o), indent);
277         return;
278     }
279     if (o.isKCanvasContainer()) {
280         write(ts, static_cast<const RenderSVGContainer&>(o), indent);
281         return;
282     }
283 #endif
284     writeIndent(ts, indent);
285
286     ts << o << "\n";
287
288     if (o.isText() && !o.isBR()) {
289         const RenderText& text = static_cast<const RenderText&>(o);
290         for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
291             writeIndent(ts, indent + 1);
292             writeTextRun(ts, text, *box);
293         }
294     }
295
296     for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) {
297         if (child->layer())
298             continue;
299         write(ts, *child, indent + 1);
300     }
301
302     if (o.isWidget()) {
303         Widget* widget = static_cast<const RenderWidget&>(o).widget();
304         if (widget && widget->isFrameView()) {
305             FrameView* view = static_cast<FrameView*>(widget);
306             RenderObject* root = view->frame()->renderer();
307             if (root) {
308                 view->layout();
309                 RenderLayer* l = root->layer();
310                 if (l)
311                     writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height()), indent + 1);
312             }
313         }
314     }
315 }
316
317 static void write(TextStream& ts, RenderLayer& l,
318                   const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect,
319                   int layerType = 0, int indent = 0)
320 {
321     writeIndent(ts, indent);
322
323     ts << "layer " << layerBounds;
324
325     if (!layerBounds.isEmpty()) {
326         if (!backgroundClipRect.contains(layerBounds))
327             ts << " backgroundClip " << backgroundClipRect;
328         if (!clipRect.contains(layerBounds))
329             ts << " clip " << clipRect;
330         if (!outlineClipRect.contains(layerBounds))
331             ts << " outlineClip " << outlineClipRect;
332     }
333
334     if (l.renderer()->hasOverflowClip()) {
335         if (l.scrollXOffset())
336             ts << " scrollX " << l.scrollXOffset();
337         if (l.scrollYOffset())
338             ts << " scrollY " << l.scrollYOffset();
339         if (l.renderer()->clientWidth() != l.scrollWidth())
340             ts << " scrollWidth " << l.scrollWidth();
341         if (l.renderer()->clientHeight() != l.scrollHeight())
342             ts << " scrollHeight " << l.scrollHeight();
343     }
344
345     if (layerType == -1)
346         ts << " layerType: background only";
347     else if (layerType == 1)
348         ts << " layerType: foreground only";
349
350     ts << "\n";
351
352     if (layerType != -1)
353         write(ts, *l.renderer(), indent + 1);
354 }
355
356 static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l,
357                         const IntRect& paintDirtyRect, int indent)
358 {
359     // Calculate the clip rects we should use.
360     IntRect layerBounds, damageRect, clipRectToApply, outlineRect;
361     l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect);
362
363     // Ensure our lists are up-to-date.
364     l->updateZOrderLists();
365     l->updateOverflowList();
366
367     bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect);
368     Vector<RenderLayer*>* negList = l->negZOrderList();
369     if (shouldPaint && negList && negList->size() > 0)
370         write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, -1, indent);
371
372     if (negList) {
373         for (unsigned i = 0; i != negList->size(); ++i)
374             writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, indent);
375     }
376
377     if (shouldPaint)
378         write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, negList && negList->size() > 0, indent);
379
380     Vector<RenderLayer*>* overflowList = l->overflowList();
381     if (overflowList) {
382         for (unsigned i = 0; i != overflowList->size(); ++i)
383             writeLayers(ts, rootLayer, overflowList->at(i), paintDirtyRect, indent);
384     }
385
386     Vector<RenderLayer*>* posList = l->posZOrderList();
387     if (posList) {
388         for (unsigned i = 0; i != posList->size(); ++i)
389             writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, indent);
390     }
391 }
392
393 static DeprecatedString nodePosition(Node* node)
394 {
395     DeprecatedString result;
396
397     Node* parent;
398     for (Node* n = node; n; n = parent) {
399         parent = n->parentNode();
400         if (!parent)
401             parent = n->shadowParentNode();
402         if (n != node)
403             result += " of ";
404         if (parent)
405             result += "child " + DeprecatedString::number(n->nodeIndex()) + " {" + getTagName(n) + "}";
406         else
407             result += "document";
408     }
409
410     return result;
411 }
412
413 static void writeSelection(TextStream& ts, const RenderObject* o)
414 {
415     Node* n = o->element();
416     if (!n || !n->isDocumentNode())
417         return;
418
419     Document* doc = static_cast<Document*>(n);
420     Frame* frame = doc->frame();
421     if (!frame)
422         return;
423
424     Selection selection = frame->selectionController()->selection();
425     if (selection.isCaret()) {
426         ts << "caret: position " << selection.start().offset() << " of " << nodePosition(selection.start().node());
427         if (selection.affinity() == UPSTREAM)
428             ts << " (upstream affinity)";
429         ts << "\n";
430     } else if (selection.isRange())
431         ts << "selection start: position " << selection.start().offset() << " of " << nodePosition(selection.start().node()) << "\n"
432            << "selection end:   position " << selection.end().offset() << " of " << nodePosition(selection.end().node()) << "\n";
433 }
434
435 DeprecatedString externalRepresentation(RenderObject* o)
436 {
437     JSEditor::setSupportsPasteCommand(true);
438
439     DeprecatedString s;
440     if (o) {
441         TextStream ts(&s);
442 #ifdef SVG_SUPPORT
443         ts.precision(2);
444         writeRenderResources(ts, o->document());
445 #endif
446         o->view()->frameView()->layout();
447         RenderLayer* l = o->layer();
448         if (l) {
449             writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height()));
450             writeSelection(ts, o);
451         }
452     }
453     return s;
454 }
455
456 } // namespace WebCore