2 * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #include "KWQRenderTreeDebug.h"
28 #include "dom_docimpl.h"
29 #include "dom_position.h"
31 #include "jsediting.h"
32 #include "khtmlview.h"
33 #include "render_canvas.h"
34 #include "render_replaced.h"
35 #include "render_table.h"
36 #include "render_text.h"
37 #include "render_br.h"
38 #include "selection.h"
40 #include "KWQKHTMLPart.h"
41 #include "KWQTextStream.h"
43 using DOM::DocumentImpl;
48 using khtml::BorderValue;
49 using khtml::EBorderStyle;
50 using khtml::InlineTextBox;
51 using khtml::RenderLayer;
52 using khtml::RenderObject;
53 using khtml::RenderTableCell;
54 using khtml::RenderWidget;
55 using khtml::RenderText;
56 using khtml::RenderCanvas;
57 using khtml::RenderBR;
58 using khtml::Selection;
59 using khtml::transparentColor;
61 static void writeLayers(QTextStream &ts, const RenderLayer* rootLayer, RenderLayer* l,
62 const QRect& paintDirtyRect, int indent=0);
64 static QTextStream &operator<<(QTextStream &ts, const QRect &r)
66 return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
69 static void writeIndent(QTextStream &ts, int indent)
71 for (int i = 0; i != indent; ++i) {
76 static void printBorderStyle(QTextStream &ts, const RenderObject &o, const EBorderStyle borderStyle)
78 switch (borderStyle) {
114 static QString getTagName(NodeImpl *n)
116 if (n->isDocumentNode())
118 if (n->id() <= ID_LAST_TAG)
119 return getTagName(n->id()).string();
120 return n->nodeName().string();
123 static QTextStream &operator<<(QTextStream &ts, const RenderObject &o)
125 ts << o.renderName();
127 if (o.style() && o.style()->zIndex()) {
128 ts << " zI: " << o.style()->zIndex();
132 QString tagName = getTagName(o.element());
133 if (!tagName.isEmpty()) {
134 ts << " {" << tagName << "}";
138 // FIXME: Will remove this <br> code once all layout tests pass. Until then, we can't really change
139 // all the results easily.
140 bool usePositions = true;
142 const RenderBR* br = static_cast<const RenderBR*>(&o);
143 usePositions = (br->firstTextBox() && br->firstTextBox()->isText());
145 QRect r(usePositions ? o.xPos() : 0, usePositions ? o.yPos() : 0, o.width(), o.height());
149 if (o.parent() && (o.parent()->style()->color() != o.style()->color()))
150 ts << " [color=" << o.style()->color().name() << "]";
151 if (o.parent() && (o.parent()->style()->backgroundColor() != o.style()->backgroundColor()) &&
152 o.style()->backgroundColor().isValid() &&
153 o.style()->backgroundColor().rgb() != khtml::transparentColor)
154 // Do not dump invalid or transparent backgrounds, since that is the default.
155 ts << " [bgcolor=" << o.style()->backgroundColor().name() << "]";
157 if (o.borderTop() || o.borderRight() || o.borderBottom() || o.borderLeft()) {
160 BorderValue prevBorder;
161 if (o.style()->borderTop() != prevBorder) {
162 prevBorder = o.style()->borderTop();
166 ts << " (" << o.borderTop() << "px ";
167 printBorderStyle(ts, o, o.style()->borderTopStyle());
168 QColor col = o.style()->borderTopColor();
169 if (!col.isValid()) col = o.style()->color();
170 ts << col.name() << ")";
174 if (o.style()->borderRight() != prevBorder) {
175 prevBorder = o.style()->borderRight();
176 if (!o.borderRight())
179 ts << " (" << o.borderRight() << "px ";
180 printBorderStyle(ts, o, o.style()->borderRightStyle());
181 QColor col = o.style()->borderRightColor();
182 if (!col.isValid()) col = o.style()->color();
183 ts << col.name() << ")";
187 if (o.style()->borderBottom() != prevBorder) {
188 prevBorder = o.style()->borderBottom();
189 if (!o.borderBottom())
192 ts << " (" << o.borderBottom() << "px ";
193 printBorderStyle(ts, o, o.style()->borderBottomStyle());
194 QColor col = o.style()->borderBottomColor();
195 if (!col.isValid()) col = o.style()->color();
196 ts << col.name() << ")";
200 if (o.style()->borderLeft() != prevBorder) {
201 prevBorder = o.style()->borderLeft();
205 ts << " (" << o.borderLeft() << "px ";
206 printBorderStyle(ts, o, o.style()->borderLeftStyle());
207 QColor col = o.style()->borderLeftColor();
208 if (!col.isValid()) col = o.style()->color();
209 ts << col.name() << ")";
217 if (o.isTableCell()) {
218 const RenderTableCell &c = static_cast<const RenderTableCell &>(o);
219 ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
225 static QString quoteAndEscapeNonPrintables(const QString &s)
229 for (uint i = 0; i != s.length(); ++i) {
233 } else if (c == '"') {
235 } else if (c == '\n' || c.unicode() == 0x00A0) {
238 ushort u = c.unicode();
239 if (u >= 0x20 && u < 0x7F) {
243 hex.sprintf("\\x{%X}", u);
252 static void writeTextRun(QTextStream &ts, const RenderText &o, const InlineTextBox &run)
254 ts << "text run at (" << run.m_x << "," << run.m_y << ") width " << run.m_width << ": "
255 << quoteAndEscapeNonPrintables(o.data().string().mid(run.m_start, run.m_len))
259 static void write(QTextStream &ts, const RenderObject &o, int indent = 0)
261 writeIndent(ts, indent);
265 if (o.isText() && !o.isBR()) {
266 const RenderText &text = static_cast<const RenderText &>(o);
267 for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
268 writeIndent(ts, indent+1);
269 writeTextRun(ts, text, *box);
273 for (RenderObject *child = o.firstChild(); child; child = child->nextSibling()) {
274 if (child->layer()) {
277 write(ts, *child, indent + 1);
281 QWidget *widget = static_cast<const RenderWidget &>(o).widget();
282 if (widget && widget->inherits("KHTMLView")) {
283 KHTMLView *view = static_cast<KHTMLView *>(widget);
284 RenderObject *root = KWQ(view->part())->renderer();
287 RenderLayer* l = root->layer();
289 writeLayers(ts, l, l, QRect(l->xPos(), l->yPos(), l->width(), l->height()), indent+1);
295 static void write(QTextStream &ts, RenderLayer &l,
296 const QRect& layerBounds, const QRect& backgroundClipRect, const QRect& clipRect,
297 int layerType = 0, int indent = 0)
299 writeIndent(ts, indent);
302 ts << " " << layerBounds;
304 if (layerBounds != layerBounds.intersect(backgroundClipRect))
305 ts << " backgroundClip " << backgroundClipRect;
306 if (layerBounds != layerBounds.intersect(clipRect))
307 ts << " clip " << clipRect;
309 if (l.renderer()->hasOverflowClip()) {
310 if (l.scrollXOffset())
311 ts << " scrollX " << l.scrollXOffset();
312 if (l.scrollYOffset())
313 ts << " scrollY " << l.scrollYOffset();
314 if (l.renderer()->clientWidth() != l.scrollWidth())
315 ts << " scrollWidth " << l.scrollWidth();
316 if (l.renderer()->clientHeight() != l.scrollHeight())
317 ts << " scrollHeight " << l.scrollHeight();
321 ts << " layerType: background only";
322 else if (layerType == 1)
323 ts << " layerType: foreground only";
328 write(ts, *l.renderer(), indent + 1);
331 static void writeLayers(QTextStream &ts, const RenderLayer* rootLayer, RenderLayer* l,
332 const QRect& paintDirtyRect, int indent)
334 // Calculate the clip rects we should use.
335 QRect layerBounds, damageRect, clipRectToApply;
336 l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply);
338 // Ensure our z-order lists are up-to-date.
339 l->updateZOrderLists();
341 bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect);
342 QPtrVector<RenderLayer>* negList = l->negZOrderList();
343 if (shouldPaint && negList && negList->count() > 0)
344 write(ts, *l, layerBounds, damageRect, clipRectToApply, -1, indent);
347 for (unsigned i = 0; i != negList->count(); ++i)
348 writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, indent);
352 write(ts, *l, layerBounds, damageRect, clipRectToApply, negList && negList->count() > 0, indent);
354 QPtrVector<RenderLayer>* posList = l->posZOrderList();
356 for (unsigned i = 0; i != posList->count(); ++i)
357 writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, indent);
361 static QString nodePositionRelativeToRoot(NodeImpl *node, NodeImpl *root)
367 NodeImpl *p = n->parentNode();
368 if (!p || n == root) {
369 result += " of root {" + getTagName(n) + "}";
375 for (NodeImpl *search = p->firstChild(); search != n; search = search->nextSibling())
377 result += "child " + QString::number(count) + " {" + getTagName(n) + "}";
384 static void writeSelection(QTextStream &ts, const RenderObject *o)
386 NodeImpl *n = o->element();
387 if (!n || !n->isDocumentNode())
390 DocumentImpl *doc = static_cast<DocumentImpl *>(n);
394 Selection selection = doc->part()->selection();
395 if (selection.isNone())
398 if (!selection.start().node()->isContentEditable() || !selection.end().node()->isContentEditable())
401 Position startPosition = selection.start();
402 Position endPosition = selection.end();
404 QString startNodeTagName(getTagName(startPosition.node()));
405 QString endNodeTagName(getTagName(endPosition.node()));
407 NodeImpl *rootNode = doc->getElementById("root");
409 if (selection.isCaret()) {
410 Position upstream = startPosition.upstream(DOM::StayInBlock);
411 Position downstream = startPosition.downstream(DOM::StayInBlock);
412 QString positionString = nodePositionRelativeToRoot(startPosition.node(), rootNode);
413 QString upstreamString = nodePositionRelativeToRoot(upstream.node(), rootNode);
414 QString downstreamString = nodePositionRelativeToRoot(downstream.node(), rootNode);
415 ts << "selection is CARET:\n" <<
416 "start: position " << startPosition.offset() << " of " << positionString << "\n"
417 "upstream: position " << upstream.offset() << " of " << upstreamString << "\n"
418 "downstream: position " << downstream.offset() << " of " << downstreamString << "\n";
420 else if (selection.isRange()) {
421 QString startString = nodePositionRelativeToRoot(startPosition.node(), rootNode);
422 Position upstreamStart = startPosition.upstream(DOM::StayInBlock);
423 QString upstreamStartString = nodePositionRelativeToRoot(upstreamStart.node(), rootNode);
424 Position downstreamStart = startPosition.downstream(DOM::StayInBlock);
425 QString downstreamStartString = nodePositionRelativeToRoot(downstreamStart.node(), rootNode);
426 QString endString = nodePositionRelativeToRoot(endPosition.node(), rootNode);
427 Position upstreamEnd = endPosition.upstream(DOM::StayInBlock);
428 QString upstreamEndString = nodePositionRelativeToRoot(upstreamEnd.node(), rootNode);
429 Position downstreamEnd = endPosition.downstream(DOM::StayInBlock);
430 QString downstreamEndString = nodePositionRelativeToRoot(downstreamEnd.node(), rootNode);
431 ts << "selection is RANGE:\n" <<
432 "start: position " << startPosition.offset() << " of " << startString << "\n" <<
433 "upstream: position " << upstreamStart.offset() << " of " << upstreamStartString << "\n"
434 "downstream: position " << downstreamStart.offset() << " of " << downstreamStartString << "\n"
435 "end: position " << endPosition.offset() << " of " << endString << "\n"
436 "upstream: position " << upstreamEnd.offset() << " of " << upstreamEndString << "\n"
437 "downstream: position " << downstreamEnd.offset() << " of " << downstreamEndString << "\n";
441 static bool debuggingRenderTreeFlag = false;
443 bool debuggingRenderTree()
445 return debuggingRenderTreeFlag;
448 QString externalRepresentation(RenderObject *o)
450 debuggingRenderTreeFlag = true;
451 JSEditor::setSupportsPasteCommand(true);
457 // FIXME: Hiding the vertical scrollbar is a total hack to preserve the
458 // layout test results until I can figure out what the heck is going on. -dwh
459 o->canvas()->view()->setVScrollBarMode(QScrollView::AlwaysOff);
460 o->canvas()->view()->layout();
461 RenderLayer* l = o->layer();
463 writeLayers(ts, l, l, QRect(l->xPos(), l->yPos(), l->width(), l->height()));
464 writeSelection(ts, o);