2006-12-28 David Kilzer <ddkilzer@webkit.org>
[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
155         if (o.parent() && (o.parent()->style()->backgroundColor() != o.style()->backgroundColor()) &&
156             o.style()->backgroundColor().isValid() && o.style()->backgroundColor().rgb())
157             // Do not dump invalid or transparent backgrounds, since that is the default.
158             ts << " [bgcolor=" << o.style()->backgroundColor().name() << "]";
159         
160         if (o.parent() && (o.parent()->style()->textFillColor() != o.style()->textFillColor()) &&
161             o.style()->textFillColor().isValid() && o.style()->textFillColor().rgb())
162             ts << " [textFillColor=" << o.style()->textFillColor().name() << "]";
163
164         if (o.parent() && (o.parent()->style()->textStrokeColor() != o.style()->textStrokeColor()) &&
165             o.style()->textStrokeColor().isValid() && o.style()->textStrokeColor().rgb())
166             ts << " [textStrokeColor=" << o.style()->textStrokeColor().name() << "]";
167
168         if (o.parent() && (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth()) &&
169             o.style()->textStrokeWidth() > 0)
170             ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]";
171
172         if (o.borderTop() || o.borderRight() || o.borderBottom() || o.borderLeft()) {
173             ts << " [border:";
174
175             BorderValue prevBorder;
176             if (o.style()->borderTop() != prevBorder) {
177                 prevBorder = o.style()->borderTop();
178                 if (!o.borderTop())
179                     ts << " none";
180                 else {
181                     ts << " (" << o.borderTop() << "px ";
182                     printBorderStyle(ts, o, o.style()->borderTopStyle());
183                     Color col = o.style()->borderTopColor();
184                     if (!col.isValid())
185                         col = o.style()->color();
186                     ts << col.name() << ")";
187                 }
188             }
189
190             if (o.style()->borderRight() != prevBorder) {
191                 prevBorder = o.style()->borderRight();
192                 if (!o.borderRight())
193                     ts << " none";
194                 else {
195                     ts << " (" << o.borderRight() << "px ";
196                     printBorderStyle(ts, o, o.style()->borderRightStyle());
197                     Color col = o.style()->borderRightColor();
198                     if (!col.isValid())
199                         col = o.style()->color();
200                     ts << col.name() << ")";
201                 }
202             }
203
204             if (o.style()->borderBottom() != prevBorder) {
205                 prevBorder = o.style()->borderBottom();
206                 if (!o.borderBottom())
207                     ts << " none";
208                 else {
209                     ts << " (" << o.borderBottom() << "px ";
210                     printBorderStyle(ts, o, o.style()->borderBottomStyle());
211                     Color col = o.style()->borderBottomColor();
212                     if (!col.isValid())
213                         col = o.style()->color();
214                     ts << col.name() << ")";
215                 }
216             }
217
218             if (o.style()->borderLeft() != prevBorder) {
219                 prevBorder = o.style()->borderLeft();
220                 if (!o.borderLeft())
221                     ts << " none";
222                 else {
223                     ts << " (" << o.borderLeft() << "px ";
224                     printBorderStyle(ts, o, o.style()->borderLeftStyle());
225                     Color col = o.style()->borderLeftColor();
226                     if (!col.isValid())
227                         col = o.style()->color();
228                     ts << col.name() << ")";
229                 }
230             }
231
232             ts << "]";
233         }
234     }
235
236     if (o.isTableCell()) {
237         const RenderTableCell& c = static_cast<const RenderTableCell&>(o);
238         ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
239     }
240
241     return ts;
242 }
243
244 static String quoteAndEscapeNonPrintables(const String& s)
245 {
246     DeprecatedString result;
247     result += '"';
248     for (unsigned i = 0; i != s.length(); ++i) {
249         UChar c = s[i];
250         if (c == '\\')
251             result += "\\\\";
252         else if (c == '"')
253             result += "\\\"";
254         else if (c == '\n' || c == 0x00A0)
255             result += ' ';
256         else {
257             if (c >= 0x20 && c < 0x7F)
258                 result += c;
259             else {
260                 DeprecatedString hex;
261                 unsigned u = c;
262                 hex.format("\\x{%X}", u);
263                 result += hex;
264             }
265         }
266     }
267     result += '"';
268     return result;
269 }
270
271 static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
272 {
273     ts << "text run at (" << run.m_x << "," << run.m_y << ") width " << run.m_width;
274     if (run.m_reversed || run.m_dirOverride) {
275         ts << (run.m_reversed ? " RTL" : " LTR");
276         if (run.m_dirOverride)
277             ts << " override";
278     }
279     ts << ": "
280         << quoteAndEscapeNonPrintables(o.data().substring(run.m_start, run.m_len))
281         << "\n";
282 }
283
284 void write(TextStream& ts, const RenderObject& o, int indent)
285 {
286 #ifdef SVG_SUPPORT
287     // FIXME:  A hackish way to doing our own "virtual" dispatch
288     if (o.isRenderPath()) {
289         write(ts, static_cast<const RenderPath&>(o), indent);
290         return;
291     }
292     if (o.isKCanvasContainer()) {
293         write(ts, static_cast<const RenderSVGContainer&>(o), indent);
294         return;
295     }
296 #endif
297     writeIndent(ts, indent);
298
299     ts << o << "\n";
300
301     if (o.isText() && !o.isBR()) {
302         const RenderText& text = static_cast<const RenderText&>(o);
303         for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
304             writeIndent(ts, indent + 1);
305             writeTextRun(ts, text, *box);
306         }
307     }
308
309     for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) {
310         if (child->layer())
311             continue;
312         write(ts, *child, indent + 1);
313     }
314
315     if (o.isWidget()) {
316         Widget* widget = static_cast<const RenderWidget&>(o).widget();
317         if (widget && widget->isFrameView()) {
318             FrameView* view = static_cast<FrameView*>(widget);
319             RenderObject* root = view->frame()->renderer();
320             if (root) {
321                 view->layout();
322                 RenderLayer* l = root->layer();
323                 if (l)
324                     writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height()), indent + 1);
325             }
326         }
327     }
328 }
329
330 static void write(TextStream& ts, RenderLayer& l,
331                   const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect,
332                   int layerType = 0, int indent = 0)
333 {
334     writeIndent(ts, indent);
335
336     ts << "layer " << layerBounds;
337
338     if (!layerBounds.isEmpty()) {
339         if (!backgroundClipRect.contains(layerBounds))
340             ts << " backgroundClip " << backgroundClipRect;
341         if (!clipRect.contains(layerBounds))
342             ts << " clip " << clipRect;
343         if (!outlineClipRect.contains(layerBounds))
344             ts << " outlineClip " << outlineClipRect;
345     }
346
347     if (l.renderer()->hasOverflowClip()) {
348         if (l.scrollXOffset())
349             ts << " scrollX " << l.scrollXOffset();
350         if (l.scrollYOffset())
351             ts << " scrollY " << l.scrollYOffset();
352         if (l.renderer()->clientWidth() != l.scrollWidth())
353             ts << " scrollWidth " << l.scrollWidth();
354         if (l.renderer()->clientHeight() != l.scrollHeight())
355             ts << " scrollHeight " << l.scrollHeight();
356     }
357
358     if (layerType == -1)
359         ts << " layerType: background only";
360     else if (layerType == 1)
361         ts << " layerType: foreground only";
362
363     ts << "\n";
364
365     if (layerType != -1)
366         write(ts, *l.renderer(), indent + 1);
367 }
368
369 static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l,
370                         const IntRect& paintDirtyRect, int indent)
371 {
372     // Calculate the clip rects we should use.
373     IntRect layerBounds, damageRect, clipRectToApply, outlineRect;
374     l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect);
375
376     // Ensure our lists are up-to-date.
377     l->updateZOrderLists();
378     l->updateOverflowList();
379
380     bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect);
381     Vector<RenderLayer*>* negList = l->negZOrderList();
382     if (shouldPaint && negList && negList->size() > 0)
383         write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, -1, indent);
384
385     if (negList) {
386         for (unsigned i = 0; i != negList->size(); ++i)
387             writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, indent);
388     }
389
390     if (shouldPaint)
391         write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, negList && negList->size() > 0, indent);
392
393     Vector<RenderLayer*>* overflowList = l->overflowList();
394     if (overflowList) {
395         for (unsigned i = 0; i != overflowList->size(); ++i)
396             writeLayers(ts, rootLayer, overflowList->at(i), paintDirtyRect, indent);
397     }
398
399     Vector<RenderLayer*>* posList = l->posZOrderList();
400     if (posList) {
401         for (unsigned i = 0; i != posList->size(); ++i)
402             writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, indent);
403     }
404 }
405
406 static DeprecatedString nodePosition(Node* node)
407 {
408     DeprecatedString result;
409
410     Node* parent;
411     for (Node* n = node; n; n = parent) {
412         parent = n->parentNode();
413         if (!parent)
414             parent = n->shadowParentNode();
415         if (n != node)
416             result += " of ";
417         if (parent)
418             result += "child " + DeprecatedString::number(n->nodeIndex()) + " {" + getTagName(n) + "}";
419         else
420             result += "document";
421     }
422
423     return result;
424 }
425
426 static void writeSelection(TextStream& ts, const RenderObject* o)
427 {
428     Node* n = o->element();
429     if (!n || !n->isDocumentNode())
430         return;
431
432     Document* doc = static_cast<Document*>(n);
433     Frame* frame = doc->frame();
434     if (!frame)
435         return;
436
437     Selection selection = frame->selectionController()->selection();
438     if (selection.isCaret()) {
439         ts << "caret: position " << selection.start().offset() << " of " << nodePosition(selection.start().node());
440         if (selection.affinity() == UPSTREAM)
441             ts << " (upstream affinity)";
442         ts << "\n";
443     } else if (selection.isRange())
444         ts << "selection start: position " << selection.start().offset() << " of " << nodePosition(selection.start().node()) << "\n"
445            << "selection end:   position " << selection.end().offset() << " of " << nodePosition(selection.end().node()) << "\n";
446 }
447
448 DeprecatedString externalRepresentation(RenderObject* o)
449 {
450     JSEditor::setSupportsPasteCommand(true);
451
452     DeprecatedString s;
453     if (o) {
454         TextStream ts(&s);
455         ts.precision(2);
456 #ifdef SVG_SUPPORT
457         writeRenderResources(ts, o->document());
458 #endif
459         o->view()->frameView()->layout();
460         RenderLayer* l = o->layer();
461         if (l) {
462             writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height()));
463             writeSelection(ts, o);
464         }
465     }
466     return s;
467 }
468
469 } // namespace WebCore