LayoutTests:
[WebKit-https.git] / WebCore / rendering / RenderTreeAsText.cpp
index 36dcbae..4bb9277 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2004, 2006 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2004, 2006, 2007 Apple Computer, Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -20,7 +20,7 @@
  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "config.h"
 
 #include "Document.h"
 #include "Frame.h"
+#include "FrameView.h"
+#include "HTMLElement.h"
+#include "HTMLNames.h"
 #include "InlineTextBox.h"
 #include "JSEditor.h"
 #include "RenderBR.h"
-#include "RenderCanvas.h"
+#include "RenderListMarker.h"
 #include "RenderTableCell.h"
+#include "RenderView.h"
+#include "RenderWidget.h"
 #include "SelectionController.h"
-#include "render_replaced.h"
 #include <wtf/Vector.h>
 
-#if SVG_SUPPORT
-#include "KCanvasTreeDebug.h"
-#include "KCanvasContainer.h"
+#ifdef SVG_SUPPORT
+#include "RenderSVGContainer.h"
+#include "SVGRenderTreeAsText.h"
 #endif
 
-using namespace WebCore;
+namespace WebCore {
 
-static void writeLayers(QTextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0);
+using namespace HTMLNames;
 
-#if !SVG_SUPPORT
-static QTextStream &operator<<(QTextStream &ts, const IntRect &r)
+static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0);
+
+#ifndef SVG_SUPPORT
+static TextStream &operator<<(TextStream& ts, const IntRect& r)
 {
     return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
 }
 #endif
 
-static void writeIndent(QTextStream &ts, int indent)
+static void writeIndent(TextStream& ts, int indent)
 {
-    for (int i = 0; i != indent; ++i) {
+    for (int i = 0; i != indent; ++i)
         ts << "  ";
-    }
 }
 
-static void printBorderStyle(QTextStream &ts, const RenderObject &o, const EBorderStyle borderStyle)
+static void printBorderStyle(TextStream& ts, const RenderObject& o, const EBorderStyle borderStyle)
 {
     switch (borderStyle) {
-        case WebCore::BNONE:
+        case BNONE:
             ts << "none";
             break;
-        case WebCore::BHIDDEN:
+        case BHIDDEN:
             ts << "hidden";
             break;
-        case WebCore::INSET:
+        case INSET:
             ts << "inset";
             break;
-        case WebCore::GROOVE:
+        case GROOVE:
             ts << "groove";
             break;
-        case WebCore::RIDGE:
+        case RIDGE:
             ts << "ridge";
             break;
-        case WebCore::OUTSET:
+        case OUTSET:
             ts << "outset";
             break;
-        case WebCore::DOTTED:
+        case DOTTED:
             ts << "dotted";
             break;
-        case WebCore::DASHED:
+        case DASHED:
             ts << "dashed";
             break;
-        case WebCore::SOLID:
+        case SOLID:
             ts << "solid";
             break;
-        case WebCore::DOUBLE:
+        case DOUBLE:
             ts << "double";
             break;
     }
-    
+
     ts << " ";
 }
 
-static DeprecatedString getTagName(Node *n)
+static String getTagName(Node* n)
 {
     if (n->isDocumentNode())
         return "";
     if (n->isCommentNode())
         return "COMMENT";
-    return n->nodeName().deprecatedString(); 
+    return n->nodeName();
+}
+
+static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node)
+{
+    if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag))
+        return false;
+
+    const HTMLElement* elem = static_cast<const HTMLElement*>(node);
+    if (elem->getAttribute(classAttr) != "Apple-style-span")
+        return false;
+
+    if (!node->hasChildNodes())
+        return true;
+
+    CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl();
+    return (!inlineStyleDecl || inlineStyleDecl->length() == 0);
+}
+
+static String quoteAndEscapeNonPrintables(const String& s)
+{
+    Vector<UChar> result;
+    result.append('"');
+    for (unsigned i = 0; i != s.length(); ++i) {
+        UChar c = s[i];
+        if (c == '\\') {
+            result.append('\\');
+            result.append('\\');
+        } else if (c == '"') {
+            result.append('\\');
+            result.append('"');
+        } else if (c == '\n' || c == 0x00A0)
+            result.append(' ');
+        else {
+            if (c >= 0x20 && c < 0x7F)
+                result.append(c);
+            else {
+                unsigned u = c;
+                String hex = String::format("\\x{%X}", u);
+                unsigned len = hex.length();
+                for (unsigned i = 0; i < len; ++i)
+                    result.append(hex[i]);
+            }
+        }
+    }
+    result.append('"');
+    return String::adopt(result);
 }
 
-static QTextStream &operator<<(QTextStream &ts, const RenderObject &o)
+static TextStream &operator<<(TextStream& ts, const RenderObject& o)
 {
     ts << o.renderName();
-    
-    if (o.style() && o.style()->zIndex()) {
+
+    if (o.style() && o.style()->zIndex())
         ts << " zI: " << o.style()->zIndex();
-    }
-    
+
     if (o.element()) {
-        DeprecatedString tagName = getTagName(o.element());
+        String tagName = getTagName(o.element());
         if (!tagName.isEmpty()) {
             ts << " {" << tagName << "}";
+            // flag empty or unstyled AppleStyleSpan because we never
+            // want to leave them in the DOM
+            if (isEmptyOrUnstyledAppleStyleSpan(o.element()))
+                ts << " *empty or unstyled AppleStyleSpan*";
         }
     }
-    
+
     IntRect r(o.xPos(), o.yPos(), o.width(), o.height());
     ts << " " << r;
-    
-    if (!o.isText()) {
+
+    if (!(o.isText() && !o.isBR())) {
         if (o.parent() && (o.parent()->style()->color() != o.style()->color()))
             ts << " [color=" << o.style()->color().name() << "]";
+
         if (o.parent() && (o.parent()->style()->backgroundColor() != o.style()->backgroundColor()) &&
             o.style()->backgroundColor().isValid() && o.style()->backgroundColor().rgb())
             // Do not dump invalid or transparent backgrounds, since that is the default.
             ts << " [bgcolor=" << o.style()->backgroundColor().name() << "]";
-    
+        
+        if (o.parent() && (o.parent()->style()->textFillColor() != o.style()->textFillColor()) &&
+            o.style()->textFillColor().isValid() && o.style()->textFillColor().rgb())
+            ts << " [textFillColor=" << o.style()->textFillColor().name() << "]";
+
+        if (o.parent() && (o.parent()->style()->textStrokeColor() != o.style()->textStrokeColor()) &&
+            o.style()->textStrokeColor().isValid() && o.style()->textStrokeColor().rgb())
+            ts << " [textStrokeColor=" << o.style()->textStrokeColor().name() << "]";
+
+        if (o.parent() && (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth()) &&
+            o.style()->textStrokeWidth() > 0)
+            ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]";
+
         if (o.borderTop() || o.borderRight() || o.borderBottom() || o.borderLeft()) {
             ts << " [border:";
-            
+
             BorderValue prevBorder;
             if (o.style()->borderTop() != prevBorder) {
                 prevBorder = o.style()->borderTop();
@@ -145,11 +212,12 @@ static QTextStream &operator<<(QTextStream &ts, const RenderObject &o)
                     ts << " (" << o.borderTop() << "px ";
                     printBorderStyle(ts, o, o.style()->borderTopStyle());
                     Color col = o.style()->borderTopColor();
-                    if (!col.isValid()) col = o.style()->color();
+                    if (!col.isValid())
+                        col = o.style()->color();
                     ts << col.name() << ")";
                 }
             }
-            
+
             if (o.style()->borderRight() != prevBorder) {
                 prevBorder = o.style()->borderRight();
                 if (!o.borderRight())
@@ -158,11 +226,12 @@ static QTextStream &operator<<(QTextStream &ts, const RenderObject &o)
                     ts << " (" << o.borderRight() << "px ";
                     printBorderStyle(ts, o, o.style()->borderRightStyle());
                     Color col = o.style()->borderRightColor();
-                    if (!col.isValid()) col = o.style()->color();
+                    if (!col.isValid())
+                        col = o.style()->color();
                     ts << col.name() << ")";
                 }
             }
-            
+
             if (o.style()->borderBottom() != prevBorder) {
                 prevBorder = o.style()->borderBottom();
                 if (!o.borderBottom())
@@ -171,64 +240,63 @@ static QTextStream &operator<<(QTextStream &ts, const RenderObject &o)
                     ts << " (" << o.borderBottom() << "px ";
                     printBorderStyle(ts, o, o.style()->borderBottomStyle());
                     Color col = o.style()->borderBottomColor();
-                    if (!col.isValid()) col = o.style()->color();
+                    if (!col.isValid())
+                        col = o.style()->color();
                     ts << col.name() << ")";
                 }
             }
-            
+
             if (o.style()->borderLeft() != prevBorder) {
                 prevBorder = o.style()->borderLeft();
                 if (!o.borderLeft())
                     ts << " none";
-                else {                    
+                else {
                     ts << " (" << o.borderLeft() << "px ";
                     printBorderStyle(ts, o, o.style()->borderLeftStyle());
                     Color col = o.style()->borderLeftColor();
-                    if (!col.isValid()) col = o.style()->color();
+                    if (!col.isValid())
+                        col = o.style()->color();
                     ts << col.name() << ")";
                 }
             }
-            
+
             ts << "]";
         }
     }
-    
+
     if (o.isTableCell()) {
         const RenderTableCell& c = static_cast<const RenderTableCell&>(o);
         ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]";
     }
 
-    return ts;
-}
-
-static String quoteAndEscapeNonPrintables(const String& s)
-{
-    DeprecatedString result;
-    result += '"';
-    for (unsigned i = 0; i != s.length(); ++i) {
-        UChar c = s[i];
-        if (c == '\\')
-            result += "\\\\";
-        else if (c == '"')
-            result += "\\\"";
-        else if (c == '\n' || c == 0x00A0)
-            result += ' ';
-        else {
-            if (c >= 0x20 && c < 0x7F)
-                result += c;
+    if (o.isListMarker()) {
+        String text = static_cast<const RenderListMarker&>(o).text();
+        if (!text.isEmpty()) {
+            if (text.length() != 1)
+                text = quoteAndEscapeNonPrintables(text);
             else {
-                DeprecatedString hex;
-                unsigned u = c;
-                hex.sprintf("\\x{%X}", u);
-                result += hex;
+                switch (text[0]) {
+                    case 0x2022:
+                        text = "bullet";
+                        break;
+                    case 0x25A0:
+                        text = "black square";
+                        break;
+                    case 0x25E6:
+                        text = "white bullet";
+                        break;
+                    default:
+                        text = quoteAndEscapeNonPrintables(text);
+                }
             }
+            ts << ": " << text;
         }
     }
-    result += '"';
-    return result;
+
+    return ts;
 }
 
-static void writeTextRun(QTextStream& ts, const RenderText& o, const InlineTextBox& run)
+static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run)
 {
     ts << "text run at (" << run.m_x << "," << run.m_y << ") width " << run.m_width;
     if (run.m_reversed || run.m_dirOverride) {
@@ -238,30 +306,30 @@ static void writeTextRun(QTextStream& ts, const RenderText& o, const InlineTextB
     }
     ts << ": "
         << quoteAndEscapeNonPrintables(o.data().substring(run.m_start, run.m_len))
-        << "\n"; 
+        << "\n";
 }
 
-void write(QTextStream &ts, const RenderObject &o, int indent)
+void write(TextStream& ts, const RenderObject& o, int indent)
 {
-#if SVG_SUPPORT
-    // FIXME:  A hackish way to doing our own "virtual" dispatch
+#ifdef SVG_SUPPORT
     if (o.isRenderPath()) {
         write(ts, static_cast<const RenderPath&>(o), indent);
         return;
     }
-    if (o.isKCanvasContainer()) {
-        write(ts, static_cast<const KCanvasContainer&>(o), indent);
+    if (o.isSVGContainer()) {
+        write(ts, static_cast<const RenderSVGContainer&>(o), indent);
         return;
     }
 #endif
+
     writeIndent(ts, indent);
-    
+
     ts << o << "\n";
-    
+
     if (o.isText() && !o.isBR()) {
         const RenderText& text = static_cast<const RenderText&>(o);
         for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) {
-            writeIndent(ts, indent+1);
+            writeIndent(ts, indent + 1);
             writeTextRun(ts, text, *box);
         }
     }
@@ -271,7 +339,7 @@ void write(QTextStream &ts, const RenderObject &o, int indent)
             continue;
         write(ts, *child, indent + 1);
     }
-    
+
     if (o.isWidget()) {
         Widget* widget = static_cast<const RenderWidget&>(o).widget();
         if (widget && widget->isFrameView()) {
@@ -281,20 +349,19 @@ void write(QTextStream &ts, const RenderObject &o, int indent)
                 view->layout();
                 RenderLayer* l = root->layer();
                 if (l)
-                    writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height()), indent+1);
+                    writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height()), indent + 1);
             }
         }
     }
 }
 
-static void write(QTextStream &ts, RenderLayer &l,
+static void write(TextStream& ts, RenderLayer& l,
                   const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect,
                   int layerType = 0, int indent = 0)
 {
     writeIndent(ts, indent);
-    
-    ts << "layer";
-    ts << " " << layerBounds;
+
+    ts << "layer " << layerBounds;
 
     if (!layerBounds.isEmpty()) {
         if (!backgroundClipRect.contains(layerBounds))
@@ -320,24 +387,24 @@ static void write(QTextStream &ts, RenderLayer &l,
         ts << " layerType: background only";
     else if (layerType == 1)
         ts << " layerType: foreground only";
-    
+
     ts << "\n";
 
     if (layerType != -1)
         write(ts, *l.renderer(), indent + 1);
 }
-    
-static void writeLayers(QTextStream &ts, const RenderLayer* rootLayer, RenderLayer* l,
+
+static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l,
                         const IntRect& paintDirtyRect, int indent)
 {
     // Calculate the clip rects we should use.
     IntRect layerBounds, damageRect, clipRectToApply, outlineRect;
     l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect);
-    
+
     // Ensure our lists are up-to-date.
     l->updateZOrderLists();
     l->updateOverflowList();
-    
+
     bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect);
     Vector<RenderLayer*>* negList = l->negZOrderList();
     if (shouldPaint && negList && negList->size() > 0)
@@ -364,19 +431,19 @@ static void writeLayers(QTextStream &ts, const RenderLayer* rootLayer, RenderLay
     }
 }
 
-static DeprecatedString nodePosition(Node *node)
+static String nodePosition(Node* node)
 {
-    DeprecatedString result;
+    String result;
 
-    Node *p;
-    for (Node *n = node; n; n = p) {
-        p = n->parentNode();
-        if (!p)
-            p = n->shadowParentNode();
+    Node* parent;
+    for (Node* n = node; n; n = parent) {
+        parent = n->parentNode();
+        if (!parent)
+            parent = n->shadowParentNode();
         if (n != node)
             result += " of ";
-        if (p)
-            result += "child " + DeprecatedString::number(n->nodeIndex()) + " {" + getTagName(n) + "}";
+        if (parent)
+            result += "child " + String::number(n->nodeIndex()) + " {" + getTagName(n) + "}";
         else
             result += "document";
     }
@@ -384,27 +451,26 @@ static DeprecatedString nodePosition(Node *node)
     return result;
 }
 
-static void writeSelection(QTextStream &ts, const RenderObject *o)
+static void writeSelection(TextStream& ts, const RenderObject* o)
 {
-    Node *n = o->element();
+    Noden = o->element();
     if (!n || !n->isDocumentNode())
         return;
 
-    Document *doc = static_cast<Document*>(n);
-    Frame *frame = doc->frame();
+    Documentdoc = static_cast<Document*>(n);
+    Frameframe = doc->frame();
     if (!frame)
         return;
 
-    SelectionController selection = frame->selection();
+    Selection selection = frame->selectionController()->selection();
     if (selection.isCaret()) {
         ts << "caret: position " << selection.start().offset() << " of " << nodePosition(selection.start().node());
         if (selection.affinity() == UPSTREAM)
             ts << " (upstream affinity)";
-        ts << "\n"; 
-    } else if (selection.isRange()) {
+        ts << "\n";
+    } else if (selection.isRange())
         ts << "selection start: position " << selection.start().offset() << " of " << nodePosition(selection.start().node()) << "\n"
-           << "selection end:   position " << selection.end().offset() << " of " << nodePosition(selection.end().node()) << "\n"; 
-    }
+           << "selection end:   position " << selection.end().offset() << " of " << nodePosition(selection.end().node()) << "\n";
 }
 
 DeprecatedString externalRepresentation(RenderObject* o)
@@ -413,12 +479,12 @@ DeprecatedString externalRepresentation(RenderObject* o)
 
     DeprecatedString s;
     if (o) {
-        QTextStream ts(&s);
-#if SVG_SUPPORT
+        TextStream ts(&s);
         ts.precision(2);
+#ifdef SVG_SUPPORT
         writeRenderResources(ts, o->document());
 #endif
-        o->canvas()->view()->layout();
+        o->view()->frameView()->layout();
         RenderLayer* l = o->layer();
         if (l) {
             writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height()));
@@ -427,3 +493,5 @@ DeprecatedString externalRepresentation(RenderObject* o)
     }
     return s;
 }
+
+} // namespace WebCore