<marquee> support

David Hyatt hyatt at apple.com
Wed Oct 29 01:00:58 CET 2003


I feel dirty.  Evil.  Bad.  Nevertheless, here it is in all its hideous 
glory.

-------------- next part --------------
Index: ChangeLog
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/ChangeLog,v
retrieving revision 1.2145
diff -u -p -r1.2145 ChangeLog
--- ChangeLog	2003/10/29 08:16:34	1.2145
+++ ChangeLog	2003/10/29 08:55:19
@@ -1,5 +1,128 @@
 2003-10-29  David Hyatt  <hyatt at apple.com>
 
+	Implement support for <marquee>.  This includes support for the CSS3 specification (although modified a fair
+	bit to actually be compatible with WinIE's version and to correct obvious errors in the draft).  All of the
+	marquee behaviors (slide, scroll, and alternate) are supported.
+
+	This patch also fixes the following bugs that were discovered while testing marquee:
+	(1) An error in the computation of scrollWidth and scrollHeight.  
+	(2) The ability to ask for the leftmostPosition as well as the rightmostPosition of content and to
+	be able to ask for the positions of children (via left/right/lowest) without including the parent in the
+	computation.
+	(3) An optimization to RenderFlow's rightmost/lowestPosition functions to avoid examining text elements since
+	they are always fully accounted for by either overflow or by inline flow boxes.
+	(4) A fix for RTL rendering.  A block with direction:RTL was being placed on the right, when in reality only
+	its children should be placed on the right.
+	(5) A fix to prevent inline blocks and tables from computing auto margins (making them just like floats in this
+	regard).
+	
+        Reviewed by darin
+
+        * khtml/css/cssparser.cpp:
+        (validUnit):
+        (CSSParser::parseValue):
+        * khtml/css/cssproperties.c:
+        (hash_prop):
+        (findProp):
+        * khtml/css/cssproperties.h:
+        * khtml/css/cssproperties.in:
+        * khtml/css/cssstyleselector.cpp:
+        (khtml::convertToLength):
+        (khtml::CSSStyleSelector::applyRule):
+        * khtml/css/cssvalues.c:
+        (hash_val):
+        (findValue):
+        * khtml/css/cssvalues.h:
+        * khtml/css/cssvalues.in:
+        * khtml/css/html4.css:
+        * khtml/html/html_blockimpl.cpp:
+        (HTMLMarqueeElementImpl::HTMLMarqueeElementImpl):
+        (HTMLMarqueeElementImpl::id):
+        (HTMLMarqueeElementImpl::parseAttribute):
+        * khtml/html/html_blockimpl.h:
+        (DOM::HTMLMarqueeElementImpl::minimumDelay):
+        * khtml/html/htmlparser.cpp:
+        (KHTMLParser::getElement):
+        * khtml/misc/htmlattrs.c:
+        (hash_attr):
+        (findAttr):
+        * khtml/misc/htmlattrs.h:
+        * khtml/misc/htmlattrs.in:
+        * khtml/rendering/bidi.cpp:
+        (khtml::RenderBlock::findNextLineBreak):
+        * khtml/rendering/render_block.cpp:
+        (khtml::RenderBlock::lowestPosition):
+        (khtml::RenderBlock::rightmostPosition):
+        (khtml::RenderBlock::leftmostPosition):
+        * khtml/rendering/render_block.h:
+        * khtml/rendering/render_box.cpp:
+        (RenderBox::setStyle):
+        (RenderBox::calcWidth):
+        (RenderBox::calcHorizontalMargins):
+        (RenderBox::lowestPosition):
+        (RenderBox::rightmostPosition):
+        (RenderBox::leftmostPosition):
+        * khtml/rendering/render_box.h:
+        * khtml/rendering/render_flow.cpp:
+        (RenderFlow::lowestPosition):
+        (RenderFlow::rightmostPosition):
+        (RenderFlow::leftmostPosition):
+        * khtml/rendering/render_flow.h:
+        * khtml/rendering/render_layer.cpp:
+        (RenderLayer::RenderLayer):
+        (RenderLayer::~RenderLayer):
+        (RenderLayer::updateLayerPositions):
+        (RenderLayer::scrollToOffset):
+        (RenderLayer::computeScrollDimensions):
+        (RenderLayer::styleChanged):
+        (RenderLayer::stopMarquees):
+        (m_whiteSpace):
+        (Marquee::marqueeSpeed):
+        (Marquee::direction):
+        (Marquee::isHorizontal):
+        (Marquee::computePosition):
+        (Marquee::start):
+        (Marquee::stop):
+        (Marquee::updateMarqueePosition):
+        (Marquee::updateMarqueeStyle):
+        (Marquee::timerEvent):
+        * khtml/rendering/render_layer.h:
+        (khtml::):
+        (khtml::RenderLayer::marquee):
+        * khtml/rendering/render_object.cpp:
+        (RenderObject::isHTMLMarquee):
+        (RenderObject::sizesToMaxWidth):
+        * khtml/rendering/render_object.h:
+        (khtml::RenderObject::lowestPosition):
+        (khtml::RenderObject::rightmostPosition):
+        (khtml::RenderObject::leftmostPosition):
+        * khtml/rendering/render_style.cpp:
+        (StyleMarqueeData::StyleMarqueeData):
+        (StyleMarqueeData::operator==):
+        (opacity):
+        (marquee):
+        (StyleCSS3NonInheritedData::operator==):
+        (RenderStyle::RenderStyle):
+        * khtml/rendering/render_style.h:
+        (khtml::):
+        (khtml::StyleMarqueeData::operator!=):
+        (khtml::RenderStyle::marqueeIncrement):
+        (khtml::RenderStyle::marqueeSpeed):
+        (khtml::RenderStyle::marqueeLoopCount):
+        (khtml::RenderStyle::marqueeBehavior):
+        (khtml::RenderStyle::marqueeDirection):
+        (khtml::RenderStyle::setMarqueeIncrement):
+        (khtml::RenderStyle::setMarqueeSpeed):
+        (khtml::RenderStyle::setMarqueeDirection):
+        (khtml::RenderStyle::setMarqueeBehavior):
+        (khtml::RenderStyle::setMarqueeLoopCount):
+        * khtml/xml/dom_docimpl.cpp:
+        (DocumentImpl::createHTMLElement):
+        * kwq/KWQKHTMLPart.mm:
+        (KWQKHTMLPart::clearTimers):
+
+2003-10-29  David Hyatt  <hyatt at apple.com>
+
 	Make <button> be an inline-block.
 	
         * khtml/css/html4.css:
Index: khtml/css/cssparser.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/css/cssparser.cpp,v
retrieving revision 1.44
diff -u -p -r1.44 khtml/css/cssparser.cpp
--- khtml/css/cssparser.cpp	2003/10/18 21:30:55	1.44
+++ khtml/css/cssparser.cpp	2003/10/29 08:55:22
@@ -378,11 +378,13 @@ static bool validUnit( Value *value, int
     case CSSPrimitiveValue::CSS_PC:
 	b = (unitflags & FLength);
 	break;
+    case CSSPrimitiveValue::CSS_MS:
+    case CSSPrimitiveValue::CSS_S:
+        b = (unitflags & FTime);
+        break;
     case CSSPrimitiveValue::CSS_DEG:
     case CSSPrimitiveValue::CSS_RAD:
     case CSSPrimitiveValue::CSS_GRAD:
-    case CSSPrimitiveValue::CSS_MS:
-    case CSSPrimitiveValue::CSS_S:
     case CSSPrimitiveValue::CSS_HZ:
     case CSSPrimitiveValue::CSS_KHZ:
     case CSSPrimitiveValue::CSS_DIMENSION:
@@ -505,7 +507,8 @@ bool CSSParser::parseValue( int propId, 
 	break;
 
     case CSS_PROP_OVERFLOW:             // visible | hidden | scroll | auto | inherit
-	if ( id == CSS_VAL_VISIBLE || id == CSS_VAL_HIDDEN || id == CSS_VAL_SCROLL || id == CSS_VAL_AUTO )
+	if (id == CSS_VAL_VISIBLE || id == CSS_VAL_HIDDEN || id == CSS_VAL_SCROLL || id == CSS_VAL_AUTO ||
+            id == CSS_VAL_MARQUEE)
 	    valid_primitive = true;
 	break;
 
@@ -1029,6 +1032,42 @@ bool CSSParser::parseValue( int propId, 
         valid_primitive = validUnit(value, FInteger|FNonNeg, true);
         break;
     
+    case CSS_PROP__KHTML_MARQUEE: {
+        const int properties[5] = { CSS_PROP__KHTML_MARQUEE_DIRECTION, CSS_PROP__KHTML_MARQUEE_INCREMENT,
+                                    CSS_PROP__KHTML_MARQUEE_REPETITION,
+                                    CSS_PROP__KHTML_MARQUEE_STYLE, CSS_PROP__KHTML_MARQUEE_SPEED };
+        return parseShortHand(properties, 5, important);
+    }
+    case CSS_PROP__KHTML_MARQUEE_DIRECTION:
+        if (id == CSS_VAL_FORWARDS || id == CSS_VAL_BACKWARDS || id == CSS_VAL_AHEAD ||
+            id == CSS_VAL_REVERSE || id == CSS_VAL_LEFT || id == CSS_VAL_RIGHT || id == CSS_VAL_DOWN ||
+            id == CSS_VAL_UP || id == CSS_VAL_AUTO)
+            valid_primitive = true;
+        break;
+    case CSS_PROP__KHTML_MARQUEE_INCREMENT:
+        if (id == CSS_VAL_SMALL || id == CSS_VAL_LARGE || id == CSS_VAL_MEDIUM)
+            valid_primitive = true;
+        else
+            valid_primitive = validUnit(value, FLength|FPercent, strict&(!nonCSSHint));
+        break;
+    case CSS_PROP__KHTML_MARQUEE_STYLE:
+        if (id == CSS_VAL_NONE || id == CSS_VAL_SLIDE || id == CSS_VAL_SCROLL || id == CSS_VAL_ALTERNATE)
+            valid_primitive = true;
+        break;
+    case CSS_PROP__KHTML_MARQUEE_REPETITION:
+        if (id == CSS_VAL_INFINITE)
+            valid_primitive = true;
+        else
+            valid_primitive = validUnit(value, FInteger|FNonNeg, strict&(!nonCSSHint));
+        break;
+    case CSS_PROP__KHTML_MARQUEE_SPEED:
+        if (id == CSS_VAL_NORMAL || id == CSS_VAL_SLOW || id == CSS_VAL_FAST)
+            valid_primitive = true;
+        else
+            valid_primitive = validUnit(value, FTime|FInteger|FNonNeg, strict&(!nonCSSHint));
+        break;
+    // End of CSS3 properties
+        
 	/* shorthand properties */
     case CSS_PROP_BACKGROUND:
     	// ['background-color' || 'background-image' ||'background-repeat' ||
Index: khtml/css/cssproperties.in
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/css/cssproperties.in,v
retrieving revision 1.8
diff -u -p -r1.8 khtml/css/cssproperties.in
--- khtml/css/cssproperties.in	2003/10/18 21:30:55	1.8
+++ khtml/css/cssproperties.in	2003/10/29 08:55:23
@@ -81,6 +81,12 @@ margin-right
 margin-bottom
 margin-left
 marker-offset
+-khtml-marquee
+-khtml-marquee-direction
+-khtml-marquee-increment
+-khtml-marquee-repetition
+-khtml-marquee-speed
+-khtml-marquee-style
 max-height
 max-width
 min-height
Index: khtml/css/cssstyleselector.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/css/cssstyleselector.cpp,v
retrieving revision 1.102
diff -u -p -r1.102 khtml/css/cssstyleselector.cpp
--- khtml/css/cssstyleselector.cpp	2003/10/21 22:14:37	1.102
+++ khtml/css/cssstyleselector.cpp	2003/10/29 08:55:27
@@ -2006,6 +2006,8 @@ void CSSStyleSelector::applyRule( int id
             o = OSCROLL; break;
         case CSS_VAL_AUTO:
             o = OAUTO; break;
+        case CSS_VAL_MARQUEE:
+            o = OMARQUEE; break;
         default:
             return;
         }
@@ -3387,7 +3389,145 @@ void CSSStyleSelector::applyRule( int id
         if (!primitiveValue || primitiveValue->primitiveType() != CSSPrimitiveValue::CSS_NUMBER)
             return; // Error case.
         style->setBoxOrdinalGroup((unsigned int)(primitiveValue->getFloatValue(CSSPrimitiveValue::CSS_NUMBER)));
-        return;      
+        return;
+    case CSS_PROP__KHTML_MARQUEE:
+        if (value->cssValueType() != CSSValue::CSS_INHERIT || !parentNode) return;
+        style->setMarqueeDirection(parentStyle->marqueeDirection());
+        style->setMarqueeIncrement(parentStyle->marqueeIncrement());
+        style->setMarqueeSpeed(parentStyle->marqueeSpeed());
+        style->setMarqueeLoopCount(parentStyle->marqueeLoopCount());
+        style->setMarqueeBehavior(parentStyle->marqueeBehavior());
+        break;
+    case CSS_PROP__KHTML_MARQUEE_REPETITION: {
+        if (value->cssValueType() == CSSValue::CSS_INHERIT) {
+            if(!parentNode) return;
+            style->setMarqueeLoopCount(parentStyle->marqueeLoopCount());
+            return;
+        }
+        if (!primitiveValue) return;
+        if (primitiveValue->getIdent() == CSS_VAL_INFINITE)
+            style->setMarqueeLoopCount(-1); // -1 means repeat forever.
+        else if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_NUMBER)
+            style->setMarqueeLoopCount((int)(primitiveValue->getFloatValue(CSSPrimitiveValue::CSS_NUMBER)));
+        break;
+    }
+    case CSS_PROP__KHTML_MARQUEE_SPEED: {
+        if (value->cssValueType() == CSSValue::CSS_INHERIT) {
+            if(!parentNode) return;
+            style->setMarqueeSpeed(parentStyle->marqueeSpeed());
+            return;
+        }        
+        if (!primitiveValue) return;
+        if (primitiveValue->getIdent()) {
+            switch (primitiveValue->getIdent())
+            {
+                case CSS_VAL_SLOW:
+                    style->setMarqueeSpeed(500); // 500 msec.
+                    break;
+                case CSS_VAL_NORMAL:
+                    style->setMarqueeSpeed(85); // 85msec. The WinIE default.
+                    break;
+                case CSS_VAL_FAST:
+                    style->setMarqueeSpeed(10); // 10msec. Super fast.
+                    break;
+            }
+        }
+        else if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_S)
+            style->setMarqueeSpeed(int(1000*primitiveValue->getFloatValue(CSSPrimitiveValue::CSS_S)));
+        else if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_MS)
+            style->setMarqueeSpeed(int(primitiveValue->getFloatValue(CSSPrimitiveValue::CSS_MS)));
+        else if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_NUMBER) // For scrollamount support.
+            style->setMarqueeSpeed(int(primitiveValue->getFloatValue(CSSPrimitiveValue::CSS_NUMBER)));
+        break;
+    }
+    case CSS_PROP__KHTML_MARQUEE_INCREMENT: {
+        if (value->cssValueType() == CSSValue::CSS_INHERIT) {
+            if(!parentNode) return;
+            style->setMarqueeIncrement(parentStyle->marqueeIncrement());
+            return;
+        }        
+        if (!primitiveValue) return;
+        if (primitiveValue->getIdent()) {
+            switch (primitiveValue->getIdent())
+            {
+                case CSS_VAL_SMALL:
+                    style->setMarqueeIncrement(Length(1, Fixed)); // 1px.
+                    break;
+                case CSS_VAL_NORMAL:
+                    style->setMarqueeIncrement(Length(6, Fixed)); // 6px. The WinIE default.
+                    break;
+                case CSS_VAL_LARGE:
+                    style->setMarqueeIncrement(Length(36, Fixed)); // 36px.
+                    break;
+            }
+        }
+        else {
+            bool ok = true;
+            Length l = convertToLength(primitiveValue, style, paintDeviceMetrics, &ok);
+            if (ok)
+                style->setMarqueeIncrement(l);
+        }
+        break;
+    }
+    case CSS_PROP__KHTML_MARQUEE_STYLE: {
+        if (value->cssValueType() == CSSValue::CSS_INHERIT) {
+            if(!parentNode) return;
+            style->setMarqueeBehavior(parentStyle->marqueeBehavior());
+            return;
+        }        
+        if (!primitiveValue || !primitiveValue->getIdent()) return;
+        switch (primitiveValue->getIdent())
+        {
+            case CSS_VAL_NONE:
+                style->setMarqueeBehavior(MNONE);
+                break;
+            case CSS_VAL_SCROLL:
+                style->setMarqueeBehavior(MSCROLL);
+                break;
+            case CSS_VAL_SLIDE:
+                style->setMarqueeBehavior(MSLIDE);
+                break;
+            case CSS_VAL_ALTERNATE:
+                style->setMarqueeBehavior(MALTERNATE);
+                break;
+        }
+        break;
+    }
+    case CSS_PROP__KHTML_MARQUEE_DIRECTION: {
+        if (value->cssValueType() == CSSValue::CSS_INHERIT) {
+            if(!parentNode) return;
+            style->setMarqueeDirection(parentStyle->marqueeDirection());
+            return;
+        }        
+        if (!primitiveValue || !primitiveValue->getIdent()) return;
+        switch (primitiveValue->getIdent())
+        {
+            case CSS_VAL_FORWARDS:
+                style->setMarqueeDirection(MFORWARD);
+                break;
+            case CSS_VAL_BACKWARDS:
+                style->setMarqueeDirection(MBACKWARD);
+                break;
+            case CSS_VAL_AUTO:
+                style->setMarqueeDirection(MAUTO);
+                break;
+            case CSS_VAL_AHEAD:
+            case CSS_VAL_UP: // We don't support vertical languages, so AHEAD just maps to UP.
+                style->setMarqueeDirection(MUP);
+                break;
+            case CSS_VAL_REVERSE:
+            case CSS_VAL_DOWN: // REVERSE just maps to DOWN, since we don't do vertical text.
+                style->setMarqueeDirection(MDOWN);
+                break;
+            case CSS_VAL_LEFT:
+                style->setMarqueeDirection(MLEFT);
+                break;
+            case CSS_VAL_RIGHT:
+                style->setMarqueeDirection(MRIGHT);
+                break;
+        }
+        break;
+    }
     default:
         return;
     }
Index: khtml/css/cssvalues.in
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/css/cssvalues.in,v
retrieving revision 1.11
diff -u -p -r1.11 khtml/css/cssvalues.in
--- khtml/css/cssvalues.in	2003/10/17 22:32:13	1.11
+++ khtml/css/cssvalues.in	2003/10/29 08:55:29
@@ -298,6 +298,7 @@ level
 line-through
 loud
 lower
+marquee
 mix
 no-close-quote
 no-open-quote
@@ -412,4 +413,29 @@ block-axis
 # CSS_PROP_BOX_LINES
 single
 multiple
+
+# CSS_PROP_MARQUEE_DIRECTION
+forwards
+backwards
+ahead
+# reverse
+# left
+# right
+up
+down
+# auto
+
+# CSS_PROP_MARQUEE_SPEED
+slow
+# normal
+fast
+
+# CSS_PROP_MARQUEE_REPETITION
+infinite
+
+# CSS_PROP_MARQUEE_STYLE
+# none
+slide
+# scroll
+alternate
 
Index: khtml/css/html4.css
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/css/html4.css,v
retrieving revision 1.49
diff -u -p -r1.49 khtml/css/html4.css
--- khtml/css/html4.css	2003/10/29 08:16:34	1.49
+++ khtml/css/html4.css	2003/10/29 08:55:29
@@ -61,6 +61,11 @@ layer {
 	display: block;
 }
 
+marquee {
+    display: inline-block;
+    overflow: marquee;
+}
+
 address {
 	display: block;
 }
@@ -489,10 +494,6 @@ nobr {
 
 wbr {
         white-space: normal;
-}
-
-marquee {
-        display: none;
 }
 
 /* noscript is handled internally, as it depends on the html settings */
Index: khtml/html/html_blockimpl.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/html/html_blockimpl.cpp,v
retrieving revision 1.10
diff -u -p -r1.10 khtml/html/html_blockimpl.cpp
--- khtml/html/html_blockimpl.cpp	2003/07/24 22:07:45	1.10
+++ khtml/html/html_blockimpl.cpp	2003/10/29 08:55:31
@@ -233,6 +233,106 @@ void HTMLPreElementImpl::setWidth( long 
     // ###
 }
 
+// -------------------------------------------------------------------------
+
+ // WinIE uses 60ms as the minimum delay by default.
+const int defaultMinimumDelay = 60;
+
+HTMLMarqueeElementImpl::HTMLMarqueeElementImpl(DocumentPtr *doc)
+: HTMLElementImpl(doc),
+  m_minimumDelay(defaultMinimumDelay)
+{
+}
+
+NodeImpl::Id HTMLMarqueeElementImpl::id() const
+{
+    return ID_MARQUEE;
+}
+
+void HTMLMarqueeElementImpl::parseAttribute(AttributeImpl *attr)
+{
+    switch(attr->id())
+    {
+        case ATTR_WIDTH:
+            if (!attr->value().isEmpty())
+                addCSSLength(CSS_PROP_WIDTH, attr->value());
+            else
+                removeCSSProperty(CSS_PROP_WIDTH);
+            break;
+        case ATTR_HEIGHT:
+            if (!attr->value().isEmpty())
+                addCSSLength(CSS_PROP_HEIGHT, attr->value());
+            else
+                removeCSSProperty(CSS_PROP_HEIGHT);
+            break;
+        case ATTR_BGCOLOR:
+            if (!attr->value().isEmpty())
+                addHTMLColor(CSS_PROP_BACKGROUND_COLOR, attr->value());
+            else
+                removeCSSProperty(CSS_PROP_BACKGROUND_COLOR);
+            break;
+        case ATTR_VSPACE:
+            if (!attr->value().isEmpty()) {
+                addCSSLength(CSS_PROP_MARGIN_TOP, attr->value());
+                addCSSLength(CSS_PROP_MARGIN_BOTTOM, attr->value());
+            }
+            else {
+                removeCSSProperty(CSS_PROP_MARGIN_TOP);
+                removeCSSProperty(CSS_PROP_MARGIN_BOTTOM);
+            }
+            break;
+        case ATTR_HSPACE:
+            if (!attr->value().isEmpty()) {
+                addCSSLength(CSS_PROP_MARGIN_LEFT, attr->value());
+                addCSSLength(CSS_PROP_MARGIN_RIGHT, attr->value());
+            }
+            else {
+                removeCSSProperty(CSS_PROP_MARGIN_LEFT);
+                removeCSSProperty(CSS_PROP_MARGIN_RIGHT);
+            }
+            break;
+        case ATTR_SCROLLAMOUNT:
+            if (!attr->value().isEmpty())
+                addCSSLength(CSS_PROP__KHTML_MARQUEE_INCREMENT, attr->value());
+            else
+                removeCSSProperty(CSS_PROP__KHTML_MARQUEE_INCREMENT);
+            break;
+        case ATTR_SCROLLDELAY:
+            if (!attr->value().isEmpty())
+                addCSSLength(CSS_PROP__KHTML_MARQUEE_SPEED, attr->value());
+            else
+                removeCSSProperty(CSS_PROP__KHTML_MARQUEE_SPEED);
+            break;
+        case ATTR_LOOP:
+            if (!attr->value().isEmpty()) {
+                if (attr->value() == "-1" || strcasecmp(attr->value(), "infinite") == 0)
+                    addCSSProperty(CSS_PROP__KHTML_MARQUEE_REPETITION, CSS_VAL_INFINITE);
+                else
+                    addCSSLength(CSS_PROP__KHTML_MARQUEE_REPETITION, attr->value());
+            }
+            else
+                removeCSSProperty(CSS_PROP__KHTML_MARQUEE_REPETITION);
+            break;
+        case ATTR_BEHAVIOR:
+            if (!attr->value().isEmpty())
+                addCSSProperty(CSS_PROP__KHTML_MARQUEE_STYLE, attr->value());
+            else
+                removeCSSProperty(CSS_PROP__KHTML_MARQUEE_STYLE);
+            break;
+        case ATTR_DIRECTION:
+            if (!attr->value().isEmpty())
+                addCSSProperty(CSS_PROP__KHTML_MARQUEE_DIRECTION, attr->value());
+            else
+                removeCSSProperty(CSS_PROP__KHTML_MARQUEE_DIRECTION);
+            break;
+        case ATTR_TRUESPEED:
+            m_minimumDelay = attr->val() ? 0 : defaultMinimumDelay;
+            break;
+        default:
+            HTMLElementImpl::parseAttribute(attr);
+    }
+}
+
 // ------------------------------------------------------------------------
 
 HTMLLayerElementImpl::HTMLLayerElementImpl(DocumentPtr *doc)
Index: khtml/html/html_blockimpl.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/html/html_blockimpl.h,v
retrieving revision 1.4
diff -u -p -r1.4 khtml/html/html_blockimpl.h
--- khtml/html/html_blockimpl.h	2002/12/05 01:20:08	1.4
+++ khtml/html/html_blockimpl.h	2003/10/29 08:55:31
@@ -112,6 +112,22 @@ public:
 
 // -------------------------------------------------------------------------
 
+class HTMLMarqueeElementImpl : public HTMLElementImpl
+{
+public:
+    HTMLMarqueeElementImpl(DocumentPtr *doc);
+
+    virtual NodeImpl::Id id() const;
+    virtual void parseAttribute(AttributeImpl *token);
+
+    int minimumDelay() const { return m_minimumDelay; }
+    
+private:
+    int m_minimumDelay;
+};
+
+// -------------------------------------------------------------------------
+
 class HTMLLayerElementImpl : public HTMLDivElementImpl
 {
 public:
Index: khtml/html/htmlparser.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/html/htmlparser.cpp,v
retrieving revision 1.59
diff -u -p -r1.59 khtml/html/htmlparser.cpp
--- khtml/html/htmlparser.cpp	2003/10/06 20:28:50	1.59
+++ khtml/html/htmlparser.cpp	2003/10/29 08:55:33
@@ -1077,7 +1077,7 @@ NodeImpl *KHTMLParser::getElement(Token*
         return 0;
         break;
     case ID_MARQUEE:
-        n = new HTMLGenericElementImpl(document, t->id);
+        n = new HTMLMarqueeElementImpl(document);
         break;
 // text
     case ID_TEXT:
Index: khtml/misc/htmlattrs.in
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/misc/htmlattrs.in,v
retrieving revision 1.4
diff -u -p -r1.4 khtml/misc/htmlattrs.in
--- khtml/misc/htmlattrs.in	2003/10/14 15:36:10	1.4
+++ khtml/misc/htmlattrs.in	2003/10/29 08:55:34
@@ -10,6 +10,7 @@ archive
 autocomplete
 axis
 background
+behavior
 bgcolor
 bgproperties
 border
@@ -40,6 +41,7 @@ datetime
 declare
 defer
 dir
+direction
 disabled
 enctype
 face
@@ -63,6 +65,7 @@ left
 leftmargin
 link
 longdesc
+loop
 marginheight
 marginwidth
 maxlength
@@ -115,6 +118,8 @@ rowspan
 rules
 scheme
 scope
+scrollamount
+scrolldelay
 scrolling
 selected
 shape
@@ -131,6 +136,7 @@ text
 title
 top
 topmargin
+truespeed
 type
 unknown
 usemap
Index: khtml/rendering/bidi.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/bidi.cpp,v
retrieving revision 1.77
diff -u -p -r1.77 khtml/rendering/bidi.cpp
--- khtml/rendering/bidi.cpp	2003/10/17 22:32:13	1.77
+++ khtml/rendering/bidi.cpp	2003/10/29 08:55:36
@@ -1577,7 +1577,23 @@ BidiIterator RenderBlock::findNextLineBr
             tmpW += o->marginLeft()+o->borderLeft()+o->paddingLeft()+
                     o->marginRight()+o->borderRight()+o->paddingRight();
         } else if ( o->isReplaced() ) {
-            if (o->style()->whiteSpace() == NORMAL || last->style()->whiteSpace() == NORMAL) {
+            EWhiteSpace currWS = o->style()->whiteSpace();
+            EWhiteSpace lastWS = last->style()->whiteSpace();
+            
+            // WinIE marquees have different whitespace characteristics by default when viewed from
+            // the outside vs. the inside.  Text inside is NOWRAP, and so we altered the marquee's
+            // style to reflect this, but we now have to get back to the original whitespace value
+            // for the marquee when checking for line breaking.
+            if (o->isHTMLMarquee() && o->layer() && o->layer()->marquee())
+                currWS = o->layer()->marquee()->whiteSpace();
+            if (last->isHTMLMarquee() && last->layer() && last->layer()->marquee())
+                lastWS = last->layer()->marquee()->whiteSpace();
+            
+            // Break on replaced elements if either has normal white-space.
+            // FIXME: This does not match WinIE, Opera, and Mozilla.  They treat replaced elements
+            // like characters in a word, and require spaces between the replaced elements in order
+            // to break.
+            if (currWS == NORMAL || lastWS == NORMAL) {
                 w += tmpW;
                 tmpW = 0;
                 lBreak.obj = o;
Index: khtml/rendering/render_block.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_block.cpp,v
retrieving revision 1.70
diff -u -p -r1.70 khtml/rendering/render_block.cpp
--- khtml/rendering/render_block.cpp	2003/10/23 22:23:25	1.70
+++ khtml/rendering/render_block.cpp	2003/10/29 08:55:40
@@ -1586,12 +1586,12 @@ RenderBlock::floatBottom() const
 }
 
 int
-RenderBlock::lowestPosition(bool includeOverflowInterior) const
+RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
 {
-    int bottom = RenderFlow::lowestPosition(includeOverflowInterior);
+    int bottom = RenderFlow::lowestPosition(includeOverflowInterior, includeSelf);
     if (!includeOverflowInterior && style()->hidesOverflow())
         return bottom;
-    if (m_overflowHeight > bottom)
+    if (includeSelf && m_overflowHeight > bottom)
         bottom = m_overflowHeight;
     
     if (m_floatingObjects) {
@@ -1600,7 +1600,7 @@ RenderBlock::lowestPosition(bool include
         for ( ; (r = it.current()); ++it ) {
             if (!r->noPaint) {
                 int lp = r->startY + r->node->lowestPosition(false);
-                bottom = QMAX(bottom, lp);
+                bottom = kMax(bottom, lp);
             }
         }
     }
@@ -1612,19 +1612,24 @@ RenderBlock::lowestPosition(bool include
         QPtrListIterator<RenderObject> it(*m_positionedObjects);
         for ( ; (r = it.current()); ++it ) {
             int lp = r->yPos() + r->lowestPosition(false);
-            bottom = QMAX(bottom, lp);
+            bottom = kMax(bottom, lp);
         }
     }
 
+    if (!includeSelf && lastLineBox()) {
+        int lp = lastLineBox()->yPos() + lastLineBox()->height();
+        bottom = kMax(bottom, lp);
+    }
+    
     return bottom;
 }
 
-int RenderBlock::rightmostPosition(bool includeOverflowInterior) const
+int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
 {
-    int right = RenderFlow::rightmostPosition(includeOverflowInterior);
+    int right = RenderFlow::rightmostPosition(includeOverflowInterior, includeSelf);
     if (!includeOverflowInterior && style()->hidesOverflow())
         return right;
-    if (m_overflowWidth > right)
+    if (includeSelf && m_overflowWidth > right)
         right = m_overflowWidth;
     
     if (m_floatingObjects) {
@@ -1633,7 +1638,7 @@ int RenderBlock::rightmostPosition(bool 
         for ( ; (r = it.current()); ++it ) {
             if (!r->noPaint) {
                 int rp = r->left + r->node->rightmostPosition(false);
-           	right = QMAX(right, rp);
+           	right = kMax(right, rp);
             }
         }
     }
@@ -1643,13 +1648,55 @@ int RenderBlock::rightmostPosition(bool 
         QPtrListIterator<RenderObject> it(*m_positionedObjects);
         for ( ; (r = it.current()); ++it ) {
             int rp = r->xPos() + r->rightmostPosition(false);
-            right = QMAX(right, rp);
+            right = kMax(right, rp);
         }
     }
 
+    if (!includeSelf && firstLineBox()) {
+        for (InlineRunBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) {
+            int rp = currBox->xPos() + currBox->width();
+            right = kMax(right, rp);
+        }
+    }
+    
     return right;
 }
 
+int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+    int left = RenderFlow::leftmostPosition(includeOverflowInterior, includeSelf);
+    if (!includeOverflowInterior && style()->hidesOverflow())
+        return left;
+    
+    // FIXME: Check left overflow when we eventually support it.
+    
+    if (m_floatingObjects) {
+        FloatingObject* r;
+        QPtrListIterator<FloatingObject> it(*m_floatingObjects);
+        for ( ; (r = it.current()); ++it ) {
+            if (!r->noPaint) {
+                int lp = r->left + r->node->leftmostPosition(false);
+                left = kMin(left, lp);
+            }
+        }
+    }
+    
+    if (m_positionedObjects && !isCanvas()) {
+        RenderObject* r;
+        QPtrListIterator<RenderObject> it(*m_positionedObjects);
+        for ( ; (r = it.current()); ++it ) {
+            int lp = r->xPos() + r->leftmostPosition(false);
+            left = kMin(left, lp);
+        }
+    }
+    
+    if (!includeSelf && firstLineBox()) {
+        for (InlineRunBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox())
+            left = kMin(left, (int)currBox->xPos());
+    }
+    
+    return left;
+}
 
 int
 RenderBlock::leftBottom()
Index: khtml/rendering/render_block.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_block.h,v
retrieving revision 1.27
diff -u -p -r1.27 khtml/rendering/render_block.h
--- khtml/rendering/render_block.h	2003/10/23 22:23:25	1.27
+++ khtml/rendering/render_block.h	2003/10/29 08:55:40
@@ -151,8 +151,9 @@ public:
     inline int rightBottom();
 
     virtual unsigned short lineWidth(int y) const;
-    virtual int lowestPosition(bool includeOverflowInterior=true) const;
-    virtual int rightmostPosition(bool includeOverflowInterior=true) const;
+    virtual int lowestPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+    virtual int rightmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+    virtual int leftmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
 
     int rightOffset() const;
     int rightRelOffset(int y, int fixedOffset, bool applyTextIndent = true,
Index: khtml/rendering/render_box.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_box.cpp,v
retrieving revision 1.89
diff -u -p -r1.89 khtml/rendering/render_box.cpp
--- khtml/rendering/render_box.cpp	2003/10/22 02:22:21	1.89
+++ khtml/rendering/render_box.cpp	2003/10/29 08:55:42
@@ -108,6 +108,9 @@ void RenderBox::setStyle(RenderStyle *_s
         m_layer = 0;
     }
 
+    if (m_layer)
+        m_layer->styleChanged();
+    
     // Set the text color if we're the body.
     if (isBody())
         element()->getDocument()->setTextColor(_style->color());
@@ -753,7 +756,7 @@ void RenderBox::calcWidth()
         if (cw && cw != m_width + m_marginLeft + m_marginRight && !isFloating() && !isInline() &&
             !cb->isFlexibleBox())
         {
-            if (style()->direction()==LTR)
+            if (cb->style()->direction()==LTR)
                 m_marginRight = cw - m_width - m_marginLeft;
             else
                 m_marginLeft = cw - m_width - m_marginRight;
@@ -802,7 +805,7 @@ int RenderBox::calcWidthUsing(WidthType 
 
 void RenderBox::calcHorizontalMargins(const Length& ml, const Length& mr, int cw)
 {
-    if (isFloating())
+    if (isFloating() || isInline()) // Inline blocks/tables and floats don't have their margins increased.
     {
         m_marginLeft = ml.minWidth(cw);
         m_marginRight = mr.minWidth(cw);
@@ -1364,14 +1367,19 @@ void RenderBox::calcAbsoluteVertical()
 }
 
 
-int RenderBox::lowestPosition(bool includeOverflowInterior) const
+int RenderBox::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
 {
-    return m_height;
+    return includeSelf ? m_height : 0;
 }
 
-int RenderBox::rightmostPosition(bool includeOverflowInterior) const
+int RenderBox::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
 {
-    return m_width;
+    return includeSelf ? m_width : 0;
+}
+
+int RenderBox::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+    return includeSelf ? 0 : m_width;
 }
 
 #undef DEBUG_LAYOUT
Index: khtml/rendering/render_box.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_box.h,v
retrieving revision 1.31
diff -u -p -r1.31 khtml/rendering/render_box.h
--- khtml/rendering/render_box.h	2003/10/09 00:45:35	1.31
+++ khtml/rendering/render_box.h	2003/10/29 08:55:42
@@ -84,8 +84,9 @@ public:
 
     virtual void position(InlineBox* box, int from, int len, bool reverse);
     
-    virtual int lowestPosition(bool includeOverflowInterior=true) const;
-    virtual int rightmostPosition(bool includeOverflowInterior=true) const;
+    virtual int lowestPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+    virtual int rightmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+    virtual int leftmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
 
     virtual QRect getAbsoluteRepaintRect();
     virtual void computeAbsoluteRepaintRect(QRect& r, bool f=false);
Index: khtml/rendering/render_flow.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_flow.cpp,v
retrieving revision 1.112
diff -u -p -r1.112 khtml/rendering/render_flow.cpp
--- khtml/rendering/render_flow.cpp	2003/10/07 04:43:23	1.112
+++ khtml/rendering/render_flow.cpp	2003/10/29 08:55:43
@@ -268,9 +268,9 @@ QRect RenderFlow::getAbsoluteRepaintRect
 }
 
 int
-RenderFlow::lowestPosition(bool includeOverflowInterior) const
+RenderFlow::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
 {
-    int bottom = RenderBox::lowestPosition(includeOverflowInterior);
+    int bottom = RenderBox::lowestPosition(includeOverflowInterior, includeSelf);
     if (!includeOverflowInterior && style()->hidesOverflow())
         return bottom;
 
@@ -279,18 +279,18 @@ RenderFlow::lowestPosition(bool includeO
     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
     // the abs div.
     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
-        if (!c->isFloatingOrPositioned()) {
+        if (!c->isFloatingOrPositioned() && !c->isText()) {
             int lp = c->yPos() + c->lowestPosition(false);
-            bottom = QMAX(bottom, lp);
+            bottom = kMax(bottom, lp);
         }
     }
     
     return bottom;
 }
 
-int RenderFlow::rightmostPosition(bool includeOverflowInterior) const
+int RenderFlow::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
 {
-    int right = RenderBox::rightmostPosition(includeOverflowInterior);
+    int right = RenderBox::rightmostPosition(includeOverflowInterior, includeSelf);
     if (!includeOverflowInterior && style()->hidesOverflow())
         return right;
 
@@ -299,12 +299,32 @@ int RenderFlow::rightmostPosition(bool i
     // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
     // the abs div.
     for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
-        if (!c->isFloatingOrPositioned()) {
+        if (!c->isFloatingOrPositioned() && !c->isText()) {
             int rp = c->xPos() + c->rightmostPosition(false);
-            right = QMAX(right, rp);
+            right = kMax(right, rp);
         }
     }
     
     return right;
+}
+
+int RenderFlow::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
+{
+    int left = RenderBox::leftmostPosition(includeOverflowInterior, includeSelf);
+    if (!includeOverflowInterior && style()->hidesOverflow())
+        return left;
+    
+    // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids.
+    // For now, we have to descend into all the children, since we may have a huge abs div inside
+    // a tiny rel div buried somewhere deep in our child tree.  In this case we have to get to
+    // the abs div.
+    for (RenderObject *c = firstChild(); c; c = c->nextSibling()) {
+        if (!c->isFloatingOrPositioned() && !c->isText()) {
+            int lp = c->xPos() + c->leftmostPosition(false);
+            left = kMin(left, lp);
+        }
+    }
+    
+    return left;
 }
 
Index: khtml/rendering/render_flow.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_flow.h,v
retrieving revision 1.45
diff -u -p -r1.45 khtml/rendering/render_flow.h
--- khtml/rendering/render_flow.h	2003/10/01 20:58:55	1.45
+++ khtml/rendering/render_flow.h	2003/10/29 08:55:43
@@ -71,8 +71,9 @@ public:
 
     virtual QRect getAbsoluteRepaintRect();
     
-    virtual int lowestPosition(bool includeOverflowInterior=true) const;
-    virtual int rightmostPosition(bool includeOverflowInterior=true) const;
+    virtual int lowestPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+    virtual int rightmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
+    virtual int leftmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const;
     
 protected:
     // An inline can be split with blocks occurring in between the inline content.
Index: khtml/rendering/render_layer.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_layer.cpp,v
retrieving revision 1.54
diff -u -p -r1.54 khtml/rendering/render_layer.cpp
--- khtml/rendering/render_layer.cpp	2003/10/07 20:15:59	1.54
+++ khtml/rendering/render_layer.cpp	2003/10/29 08:55:45
@@ -48,6 +48,8 @@
 #include "render_canvas.h"
 #include "render_arena.h"
 #include "xml/dom_docimpl.h"
+#include "misc/htmltags.h"
+#include "html/html_blockimpl.h"
 
 #include <qscrollbar.h>
 #include <qptrvector.h>
@@ -94,7 +96,8 @@ m_vBar( 0 ),
 m_scrollMediator( 0 ),
 m_posZOrderList( 0 ),
 m_negZOrderList( 0 ),
-m_zOrderListsDirty( true )
+m_zOrderListsDirty( true ),
+m_marquee( 0 )
 {
 }
 
@@ -102,12 +105,12 @@ RenderLayer::~RenderLayer()
 {
     // Child layers will be deleted by their corresponding render objects, so
     // our destructor doesn't have to do anything.
-    m_parent = m_previous = m_next = m_first = m_last = 0;
     delete m_hBar;
     delete m_vBar;
     delete m_scrollMediator;
     delete m_posZOrderList;
     delete m_negZOrderList;
+    delete m_marquee;
 }
 
 #ifdef INCREMENTAL_REPAINTING
@@ -147,7 +150,7 @@ void RenderLayer::updateLayerPositions()
         QRect layerBounds = QRect(x,y,width(),height());
         positionScrollbars(layerBounds);
     }
-    
+
     // FIXME: Child object could override visibility.
     if (checkForRepaint && (m_object->style()->visibility() == VISIBLE))
         m_object->repaintAfterLayoutIfNeeded(m_repaintRect, m_fullRepaintRect);
@@ -159,6 +162,10 @@ void RenderLayer::updateLayerPositions()
 #else
         child->updateLayerPositions();
 #endif
+        
+    // With all our children positioned, now update our marquee if we need to.
+    if (m_marquee)
+        m_marquee->updateMarqueePosition();
 }
 
 void RenderLayer::updateLayerPosition()
@@ -421,19 +428,21 @@ RenderLayer::subtractScrollOffset(int& x
 }
 
 void
-RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars)
+RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint)
 {
-    if (x < 0) x = 0;
-    if (y < 0) y = 0;
-
-    // Call the scrollWidth/Height functions so that the dimensions will be computed if they need
-    // to be (for overflow:hidden blocks).
-    int maxX = scrollWidth() - m_object->clientWidth();
-    int maxY = scrollHeight() - m_object->clientHeight();
+    if (renderer()->style()->overflow() != OMARQUEE) {
+        if (x < 0) x = 0;
+        if (y < 0) y = 0;
+    
+        // Call the scrollWidth/Height functions so that the dimensions will be computed if they need
+        // to be (for overflow:hidden blocks).
+        int maxX = scrollWidth() - m_object->clientWidth();
+        int maxY = scrollHeight() - m_object->clientHeight();
+        
+        if (x > maxX) x = maxX;
+        if (y > maxY) y = maxY;
+    }
     
-    if (x > maxX) x = maxX;
-    if (y > maxY) y = maxY;
-
     // FIXME: Eventually, we will want to perform a blit.  For now never
     // blit, since the check for blitting is going to be very
     // complicated (since it will involve testing whether our layer
@@ -445,7 +454,8 @@ RenderLayer::scrollToOffset(int x, int y
     // FIXME: Fire the onscroll DOM event.
     
     // Just schedule a full repaint of our object.
-    m_object->repaint(true);
+    if (repaint)
+        m_object->repaint(true);
     
     if (updateScrollbars) {
         if (m_hBar)
@@ -583,23 +593,20 @@ int RenderLayer::scrollHeight()
 void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar)
 {
     m_scrollDimensionsDirty = false;
-    int rightPos = m_object->rightmostPosition();
-    int bottomPos = m_object->lowestPosition();
+
+    int rightPos = m_object->rightmostPosition(true, false) - m_object->borderLeft();
+    int bottomPos = m_object->lowestPosition(true, false) - m_object->borderTop();
 
     int clientWidth = m_object->clientWidth();
     int clientHeight = m_object->clientHeight();
-    m_scrollWidth = clientWidth;
-    m_scrollHeight = clientHeight;
 
-    if (rightPos - m_object->borderLeft() > m_scrollWidth)
-        m_scrollWidth = rightPos - m_object->borderLeft();
-    if (bottomPos - m_object->borderTop() > m_scrollHeight)
-        m_scrollHeight = bottomPos - m_object->borderTop();
+    m_scrollWidth = kMax(rightPos, clientWidth);
+    m_scrollHeight = kMax(bottomPos, clientHeight);
 
     if (needHBar)
-        *needHBar = rightPos > m_object->overflowWidth(false);
+        *needHBar = rightPos > clientWidth;
     if (needVBar)
-        *needVBar = bottomPos > m_object->overflowHeight(false);
+        *needVBar = bottomPos > clientHeight;
 }
 
 void
@@ -1179,3 +1186,254 @@ void RenderLayer::collectLayers(QPtrVect
             child->collectLayers(posBuffer, negBuffer);
     }
 }
+
+void RenderLayer::styleChanged()
+{
+    if (m_object->style()->overflow() == OMARQUEE && m_object->style()->marqueeBehavior() != MNONE) {
+        if (!m_marquee)
+            m_marquee = new Marquee(this);
+        m_marquee->updateMarqueeStyle();
+    }
+    else if (m_marquee) {
+        delete m_marquee;
+        m_marquee = 0;
+    }
+}
+
+void RenderLayer::stopMarquees()
+{
+    if (m_marquee)
+        m_marquee->stop();
+    
+    for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling())
+        curr->stopMarquees();
+}
+
+// --------------------------------------------------------------------------
+// Marquee implementation
+
+Marquee::Marquee(RenderLayer* l)
+:m_layer(l), m_currentLoop(0), m_timerId(0), m_start(0), m_end(0), m_speed(0), m_reset(false),
+ m_whiteSpace(NORMAL)
+{
+}
+
+int Marquee::marqueeSpeed() const
+{
+    int result = m_layer->renderer()->style()->marqueeSpeed();
+    DOM::NodeImpl* elt = m_layer->renderer()->element();
+    if (elt && elt->id() == ID_MARQUEE) {
+        HTMLMarqueeElementImpl* marqueeElt = static_cast<HTMLMarqueeElementImpl*>(elt);
+        result = kMax(result, marqueeElt->minimumDelay());
+    }
+    return result;
+}
+
+EMarqueeDirection Marquee::direction() const
+{
+    // FIXME: Support the CSS3 "auto" value for determining the direction of the marquee.
+    // For now just map MAUTO to MBACKWARD
+    EMarqueeDirection result = m_layer->renderer()->style()->marqueeDirection();
+    EDirection dir =  m_layer->renderer()->style()->direction();
+    if (result == MAUTO)
+        result = MBACKWARD;
+    if (result == MFORWARD)
+        result = (dir == LTR) ? MRIGHT : MLEFT;
+    if (result == MBACKWARD)
+        result = (dir == LTR) ? MLEFT : MRIGHT;
+    
+    // Now we have the real direction.  Next we check to see if the increment is negative.
+    // If so, then we reverse the direction.
+    Length increment = m_layer->renderer()->style()->marqueeIncrement();
+    if (increment.value < 0)
+        result = static_cast<EMarqueeDirection>(-result);
+    
+    return result;
+}
+
+bool Marquee::isHorizontal() const
+{
+    return direction() == MLEFT || direction() == MRIGHT;
+}
+
+int Marquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge)
+{
+    RenderObject* o = m_layer->renderer();
+    RenderStyle* s = o->style();
+    if (isHorizontal()) {
+        bool ltr = s->direction() == LTR;
+        int clientWidth = o->clientWidth();
+        int contentWidth = ltr ? o->rightmostPosition(true, false) : o->leftmostPosition(true, false);
+        if (ltr)
+            contentWidth += (o->paddingRight() - o->borderLeft());
+        else {
+            contentWidth = o->width() - contentWidth;
+            contentWidth += (o->paddingLeft() - o->borderRight());
+        }
+        if (dir == MRIGHT) {
+            if (stopAtContentEdge)
+                return kMax(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
+            else
+                return ltr ? contentWidth : clientWidth;
+        }
+        else {
+            if (stopAtContentEdge)
+                return kMin(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth));
+            else
+                return ltr ? -clientWidth : -contentWidth;
+        }
+    }
+    else {
+        int contentHeight = m_layer->renderer()->lowestPosition(true, false) - 
+                            m_layer->renderer()->borderTop() + m_layer->renderer()->paddingBottom();
+        int clientHeight = m_layer->renderer()->clientHeight();
+        if (dir == MUP) {
+            if (stopAtContentEdge)
+                 return kMin(contentHeight - clientHeight, 0);
+            else
+                return -clientHeight;
+        }
+        else {
+            if (stopAtContentEdge)
+                return kMax(contentHeight - clientHeight, 0);
+            else 
+                return contentHeight;
+        }
+    }    
+}
+
+void Marquee::start()
+{
+    if (m_timerId)
+        return;
+    
+    if (isHorizontal())
+        m_layer->scrollToOffset(m_start, 0, false, false);
+    else
+        m_layer->scrollToOffset(0, m_start, false, false);
+
+    m_timerId = startTimer(speed());
+}
+
+void Marquee::stop()
+{
+    m_currentLoop = 0;
+    m_reset = false;
+    
+    if (m_timerId) {
+        killTimer(m_timerId);
+        m_timerId = 0;
+    }
+}
+
+void Marquee::updateMarqueePosition()
+{
+    bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
+    if (activate) {
+        EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior();
+        m_start = computePosition(direction(), behavior == MALTERNATE);
+        m_end = computePosition(reverseDirection(), behavior == MALTERNATE || behavior == MSLIDE);
+        start();
+    }
+}
+
+void Marquee::updateMarqueeStyle()
+{
+    RenderStyle* s = m_layer->renderer()->style();
+    
+    m_totalLoops = s->marqueeLoopCount();
+    m_whiteSpace = s->whiteSpace();
+    
+    if (m_layer->renderer()->isHTMLMarquee()) {
+        // Hack for WinIE.  In WinIE, a value of 0 or lower for the loop count for SLIDE means to only do
+        // one loop.
+        if (m_totalLoops <= 0 && s->marqueeBehavior() == MSLIDE)
+            m_totalLoops = 1;
+        
+        // Hack alert: Set the white-space value to nowrap for horizontal marquees with inline children, thus ensuring
+        // all the text ends up on one line by default.  Limit this hack to the <marquee> element to emulate
+        // WinIE's behavior.  Someone using CSS3 can use white-space: nowrap on their own to get this effect.
+        // Second hack alert: Set the text-align back to auto.  WinIE completely ignores text-align on the
+        // marquee element.
+        // FIXME: Bring these up with the CSS WG.
+        if (isHorizontal() && m_layer->renderer()->childrenInline()) {
+            s->setWhiteSpace(NOWRAP);
+            s->setTextAlign(TAAUTO);
+        }
+    }
+    
+    if (speed() != marqueeSpeed()) {
+        m_speed = marqueeSpeed();
+        if (m_timerId) {
+            killTimer(m_timerId);
+            m_timerId = startTimer(speed());
+        }
+    }
+    
+    // Check the loop count to see if we should now stop.
+    bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops);
+    if (activate && !m_timerId)
+        m_layer->renderer()->setNeedsLayout(true);
+    else if (!activate && m_timerId) {
+        // Destroy the timer.
+        killTimer(m_timerId);
+        m_timerId = 0;
+    }
+}
+
+void Marquee::timerEvent(QTimerEvent* evt)
+{
+    if (m_layer->renderer()->needsLayout())
+        return;
+    
+    if (m_reset) {
+        m_reset = false;
+        if (isHorizontal())
+            m_layer->scrollToXOffset(m_start);
+        else
+            m_layer->scrollToYOffset(m_start);
+        return;
+    }
+    
+    RenderStyle* s = m_layer->renderer()->style();
+    
+    int endPoint = m_end;
+    int range = m_end - m_start;
+    int newPos;
+    if (range == 0)
+        newPos = m_end;
+    else {  
+        bool addIncrement = direction() == MUP || direction() == MLEFT;
+        if (s->marqueeBehavior() == MALTERNATE && m_currentLoop % 2) {
+            // We're going in the reverse direction.
+            endPoint = m_start;
+            range = -range;
+            addIncrement = !addIncrement;
+        }
+        bool positive = range > 0;
+        int clientSize = (isHorizontal() ? m_layer->renderer()->clientWidth() : m_layer->renderer()->clientHeight());
+        int increment = abs(m_layer->renderer()->style()->marqueeIncrement().width(clientSize));
+        newPos = (isHorizontal() ? m_layer->scrollXOffset() : m_layer->scrollYOffset()) + 
+            (addIncrement ? increment : -increment);
+        if (positive)
+            newPos = kMin(newPos, endPoint);
+        else
+            newPos = kMax(newPos, endPoint);
+    }
+        
+    if (newPos == endPoint) {
+        m_currentLoop++;
+        if (m_totalLoops > 0 && m_currentLoop >= m_totalLoops) {
+            killTimer(m_timerId);
+            m_timerId = 0;
+        }
+        else if (s->marqueeBehavior() != MALTERNATE)
+            m_reset = true;
+    }
+    
+    if (isHorizontal())
+        m_layer->scrollToXOffset(newPos);
+    else
+        m_layer->scrollToYOffset(newPos);
+}
+
Index: khtml/rendering/render_layer.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_layer.h,v
retrieving revision 1.30
diff -u -p -r1.30 khtml/rendering/render_layer.h
--- khtml/rendering/render_layer.h	2003/10/07 20:15:59	1.30
+++ khtml/rendering/render_layer.h	2003/10/29 08:55:45
@@ -77,7 +77,44 @@ public:
 private:
     RenderLayer* m_layer;
 };
+
+// This class handles the auto-scrolling of layers with overflow: marquee.
+class Marquee: public QObject
+{
+    Q_OBJECT
+    
+public:
+    Marquee(RenderLayer* l);
+
+    void timerEvent(QTimerEvent*);
+
+    int speed() const { return m_speed; }
+    int marqueeSpeed() const;
+    EMarqueeDirection direction() const;
+    EMarqueeDirection reverseDirection() const { return static_cast<EMarqueeDirection>(-direction()); }
+    bool isHorizontal() const;
+    EWhiteSpace whiteSpace() { return m_whiteSpace; }
+    
+    int computePosition(EMarqueeDirection dir, bool stopAtClientEdge);
     
+    void start();
+    void stop();
+    
+    void updateMarqueeStyle();
+    void updateMarqueePosition();
+    
+private:
+    RenderLayer* m_layer;
+    int m_currentLoop;
+    int m_totalLoops;
+    int m_timerId;
+    int m_start;
+    int m_end;
+    int m_speed;
+    bool m_reset;
+    EWhiteSpace m_whiteSpace : 2;
+};
+
 class RenderLayer
 {
 public:
@@ -102,6 +139,11 @@ public:
     void removeOnlyThisLayer();
     void insertOnlyThisLayer();
 
+    void styleChanged();
+    
+    Marquee* marquee() const { return m_marquee; }
+    void stopMarquees();
+    
 #if APPLE_CHANGES
     bool isTransparent();
     RenderLayer* transparentAncestor();
@@ -134,7 +176,7 @@ public:
     void subtractScrollOffset(int& x, int& y);
     short scrollXOffset() { return m_scrollX; }
     int scrollYOffset() { return m_scrollY; }
-    void scrollToOffset(int x, int y, bool updateScrollbars = true);
+    void scrollToOffset(int x, int y, bool updateScrollbars = true, bool repaint = true);
     void scrollToXOffset(int x) { scrollToOffset(x, m_scrollY); }
     void scrollToYOffset(int y) { scrollToOffset(m_scrollX, y); }
     void setHasHorizontalScrollbar(bool hasScrollbar);
@@ -278,6 +320,8 @@ protected:   
     QPtrVector<RenderLayer>* m_posZOrderList;
     QPtrVector<RenderLayer>* m_negZOrderList;
     bool m_zOrderListsDirty;
+    
+    Marquee* m_marquee; // Used by layers with overflow:marquee
 };
 
 }; // namespace
Index: khtml/rendering/render_object.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_object.cpp,v
retrieving revision 1.107
diff -u -p -r1.107 khtml/rendering/render_object.cpp
--- khtml/rendering/render_object.cpp	2003/10/23 22:23:25	1.107
+++ khtml/rendering/render_object.cpp	2003/10/29 08:55:47
@@ -170,6 +170,11 @@ bool RenderObject::isBody() const
     return element() && element()->renderer() == this && element()->id() == ID_BODY;
 }
 
+bool RenderObject::isHTMLMarquee() const
+{
+    return element() && element()->renderer() == this && element()->id() == ID_MARQUEE;
+}
+
 bool RenderObject::canHaveChildren() const
 {
     return false;
@@ -560,10 +565,21 @@ int RenderObject::containingBlockHeight(
 
 bool RenderObject::sizesToMaxWidth() const
 {
-    if (isFloating() || isCompact() || isInlineBlockOrInlineTable() ||
+    // Marquees in WinIE are like a mixture of blocks and inline-blocks.  They size as though they're blocks,
+    // but they allow text to sit on the same line as the marquee.
+    if (isFloating() || isCompact() || 
+        (isInlineBlockOrInlineTable() && !isHTMLMarquee()) ||
         (element() && (element()->id() == ID_BUTTON || element()->id() == ID_LEGEND)))
         return true;
-
+    
+    // Children of a horizontal marquee do not fill the container by default.
+    // FIXME: Need to deal with MAUTO value properly.  It could be vertical.
+    if (parent()->style()->overflow() == OMARQUEE) {
+        EMarqueeDirection dir = parent()->style()->marqueeDirection();
+        if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT)
+            return true;
+    }
+    
     // Flexible horizontal boxes lay out children at their maxwidths.  Also vertical boxes
     // that don't stretch their kids lay out their children at their maxwidths.
     if (parent()->isFlexibleBox() &&
Index: khtml/rendering/render_object.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_object.h,v
retrieving revision 1.88
diff -u -p -r1.88 khtml/rendering/render_object.h
--- khtml/rendering/render_object.h	2003/10/23 22:23:25	1.88
+++ khtml/rendering/render_object.h	2003/10/29 08:55:48
@@ -235,6 +235,8 @@ public:
     virtual bool isFrameSet() const { return false; }
     virtual bool isApplet() const { return false; }
 
+    bool isHTMLMarquee() const;
+    
     bool isAnonymous() const { return m_isAnonymous; }
     void setIsAnonymous(bool b) { m_isAnonymous = b; }
     
@@ -638,14 +640,11 @@ public:
     virtual void setSelectionState(SelectionState) {}
 
     virtual void cursorPos(int /*offset*/, int &/*_x*/, int &/*_y*/, int &/*height*/);
-
-    virtual int lowestPosition(bool includeOverflowInterior=true) const {return 0;}
-
-    virtual int rightmostPosition(bool includeOverflowInterior=true) const {return 0;}
 
-    // recursively invalidate current layout
-    // unused: void invalidateLayout();
-
+    virtual int lowestPosition(bool includeOverflowInterior=true, bool includeSelf=true) const { return 0; }
+    virtual int rightmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const { return 0; }
+    virtual int leftmostPosition(bool includeOverflowInterior=true, bool includeSelf=true) const { return 0; }
+    
     virtual void calcVerticalMargins() {}
     void removeFromObjectLists();
 
Index: khtml/rendering/render_style.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_style.cpp,v
retrieving revision 1.31
diff -u -p -r1.31 khtml/rendering/render_style.cpp
--- khtml/rendering/render_style.cpp	2003/10/23 22:23:25	1.31
+++ khtml/rendering/render_style.cpp	2003/10/29 08:55:49
@@ -129,6 +129,22 @@ bool StyleBackgroundData::operator==(con
 	outline == o.outline;
 }
 
+StyleMarqueeData::StyleMarqueeData()
+{
+    increment.type = Fixed;
+    increment.value = 6; // 6 pixels is the WinIE default.
+    speed = 85; // 85msec is the WinIE default.
+    direction = MAUTO; // Direction is auto-determined by overflow by default.
+    behavior = MSCROLL; // Scrolling marquee is the default.
+    loops = -1; // Loop forever by default. Matches WinIE.
+}
+
+bool StyleMarqueeData::operator==(const StyleMarqueeData& o) const
+{
+    return (increment == o.increment && speed == o.speed && direction == o.direction &&
+            behavior == o.behavior && loops == o.loops);
+}
+
 StyleFlexibleBoxData::StyleFlexibleBoxData()
 : Shared<StyleFlexibleBoxData>()
 {
@@ -166,17 +182,16 @@ bool StyleFlexibleBoxData::operator==(co
 StyleCSS3NonInheritedData::StyleCSS3NonInheritedData()
 :Shared<StyleCSS3NonInheritedData>(), opacity(1.0f)
 {
-    
 }
 
 StyleCSS3NonInheritedData::StyleCSS3NonInheritedData(const StyleCSS3NonInheritedData& o)
-:Shared<StyleCSS3NonInheritedData>(), opacity(o.opacity), flexibleBox(o.flexibleBox)
+:Shared<StyleCSS3NonInheritedData>(), opacity(o.opacity), flexibleBox(o.flexibleBox), marquee(o.marquee)
 {
 }
 
 bool StyleCSS3NonInheritedData::operator==(const StyleCSS3NonInheritedData& o) const
 {
-    return opacity == o.opacity && flexibleBox == o.flexibleBox;
+    return opacity == o.opacity && flexibleBox == o.flexibleBox && marquee == o.marquee;
 }
 
 StyleCSS3InheritedData::StyleCSS3InheritedData()
@@ -273,6 +288,7 @@ RenderStyle::RenderStyle(bool)
     surround.init();
     css3NonInheritedData.init();
     css3NonInheritedData.access()->flexibleBox.init();
+    css3NonInheritedData.access()->marquee.init();
     css3InheritedData.init();
     inherited.init();
 
Index: khtml/rendering/render_style.h
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/rendering/render_style.h,v
retrieving revision 1.34
diff -u -p -r1.34 khtml/rendering/render_style.h
--- khtml/rendering/render_style.h	2003/10/23 22:23:25	1.34
+++ khtml/rendering/render_style.h	2003/10/29 08:55:51
@@ -323,7 +323,7 @@ public:
 // Random visual rendering model attributes. Not inherited.
 
 enum EOverflow {
-    OVISIBLE, OHIDDEN, OSCROLL, OAUTO
+    OVISIBLE, OHIDDEN, OSCROLL, OAUTO, OMARQUEE
 };
 
 enum EVerticalAlign {
@@ -404,6 +404,31 @@ public:
 };
 
 //------------------------------------------------
+// CSS3 Marquee Properties
+
+enum EMarqueeBehavior { MNONE, MSCROLL, MSLIDE, MALTERNATE };
+enum EMarqueeDirection { MAUTO = 0, MLEFT = 1, MRIGHT = -1, MUP = 2, MDOWN = -2, MFORWARD = 3, MBACKWARD = -3 };
+
+class StyleMarqueeData : public Shared<StyleMarqueeData>
+{
+public:
+    StyleMarqueeData();
+    
+    bool operator==(const StyleMarqueeData& o) const;
+    bool operator!=(const StyleMarqueeData& o) const {
+        return !(*this == o);
+    }
+    
+    Length increment;
+    int speed;
+    
+    int loops; // -1 means infinite.
+    
+    EMarqueeBehavior behavior : 2;
+    EMarqueeDirection direction : 3;
+};
+    
+//------------------------------------------------
 // CSS3 Flexible Box Properties
 
 enum EBoxAlignment { BSTRETCH, BSTART, BCENTER, BEND, BJUSTIFY, BBASELINE };
@@ -471,6 +496,7 @@ public:
     
     float opacity;         // Whether or not we're transparent.
     DataRef<StyleFlexibleBoxData> flexibleBox; // Flexible box properties 
+    DataRef<StyleMarqueeData> marquee; // Marquee properties
 };
 
 // This struct is for rarely used inherited CSS3 properties.  By grouping them together,
@@ -949,6 +975,11 @@ public:
     EBoxOrient boxOrient() { return css3NonInheritedData->flexibleBox->orient; }
     EBoxAlignment boxPack() { return css3NonInheritedData->flexibleBox->pack; }
     int boxFlexedHeight() { return css3NonInheritedData->flexibleBox->flexed_height; }
+    Length marqueeIncrement() { return css3NonInheritedData->marquee->increment; }
+    int marqueeSpeed() { return css3NonInheritedData->marquee->speed; }
+    int marqueeLoopCount() { return css3NonInheritedData->marquee->loops; }
+    EMarqueeBehavior marqueeBehavior() { return css3NonInheritedData->marquee->behavior; }
+    EMarqueeDirection marqueeDirection() { return css3NonInheritedData->marquee->direction; }
     // End CSS3 Getters
 
 // attribute setter methods
@@ -1106,6 +1137,11 @@ public:
     void setBoxOrient(EBoxOrient o) { SET_VAR(css3NonInheritedData.access()->flexibleBox, orient, o); }
     void setBoxPack(EBoxAlignment p) { SET_VAR(css3NonInheritedData.access()->flexibleBox, pack, p); }
     void setBoxFlexedHeight(int h) { SET_VAR(css3NonInheritedData.access()->flexibleBox, flexed_height, h); }
+    void setMarqueeIncrement(const Length& f) { SET_VAR(css3NonInheritedData.access()->marquee, increment, f); }
+    void setMarqueeSpeed(int f) { SET_VAR(css3NonInheritedData.access()->marquee, speed, f); }
+    void setMarqueeDirection(EMarqueeDirection d) { SET_VAR(css3NonInheritedData.access()->marquee, direction, d); }
+    void setMarqueeBehavior(EMarqueeBehavior b) { SET_VAR(css3NonInheritedData.access()->marquee, behavior, b); }
+    void setMarqueeLoopCount(int i) { SET_VAR(css3NonInheritedData.access()->marquee, loops, i); }
     // End CSS3 Setters
     
     QPalette palette() const { return visual->palette; }
Index: khtml/xml/dom_docimpl.cpp
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/khtml/xml/dom_docimpl.cpp,v
retrieving revision 1.83
diff -u -p -r1.83 khtml/xml/dom_docimpl.cpp
--- khtml/xml/dom_docimpl.cpp	2003/10/20 17:28:10	1.83
+++ khtml/xml/dom_docimpl.cpp	2003/10/29 08:55:53
@@ -850,6 +850,10 @@ ElementImpl *DocumentImpl::createHTMLEle
         n = new HTMLGenericElementImpl(docPtr(), id);
         break;
 
+    case ID_MARQUEE:
+        n = new HTMLMarqueeElementImpl(docPtr());
+        break;
+        
     case ID_BDO:
         break;
 
Index: kwq/KWQKHTMLPart.mm
===================================================================
RCS file: /local/home/cvs/Labyrinth/WebCore/kwq/KWQKHTMLPart.mm,v
retrieving revision 1.418
diff -u -p -r1.418 kwq/KWQKHTMLPart.mm
--- kwq/KWQKHTMLPart.mm	2003/10/29 07:22:09	1.418
+++ kwq/KWQKHTMLPart.mm	2003/10/29 08:55:57
@@ -1726,8 +1726,14 @@ void KWQKHTMLPart::khtmlMouseReleaseEven
 
 void KWQKHTMLPart::clearTimers(KHTMLView *view)
 {
-    if (view)
+    if (view) {
         view->unscheduleRelayout();
+        if (view->part()) {
+            DocumentImpl* document = view->part()->xmlDocImpl();
+            if (document && document->renderer() && document->renderer()->layer())
+                document->renderer()->layer()->stopMarquees();
+        }
+    }
 }
 
 void KWQKHTMLPart::clearTimers()
-------------- next part --------------


dave


More information about the Khtml-devel mailing list