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