LayoutTests:
authoradele <adele@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 26 May 2006 21:13:22 +0000 (21:13 +0000)
committeradele <adele@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 26 May 2006 21:13:22 +0000 (21:13 +0000)
        Reviewed by Hyatt.

        Added test for new textarea implementation.  Tests
        wrap attributes and form submission.

        * fast/forms/textarea-appearance-wrap-expected.txt: Added.
        * fast/forms/textarea-appearance-wrap.html: Added.

WebCore:

        Reviewed by Hyatt.

        Added support for wrap=hard for new textarea implementation.

        Tests: fast/forms/textarea-appearance-wrap.html

        * dom/Range.h: Added version of toString that will convert BRs to newlines.
        * dom/Range.cpp: (WebCore::Range::toString):
        * editing/ReplaceSelectionCommand.cpp: (WebCore::ReplacementFragment::ReplacementFragment):
          When in plain-text mode, and a white-space mode that doesn't collapse whitespace, create a fragment with one text node.
        * editing/visible_units.cpp:
        (WebCore::previousLinePosition): Subtract scroll offset so the absolute position for the containing block is correct.
        (WebCore::nextLinePosition): ditto.
        * rendering/RenderText.cpp:
        (WebCore::RenderText::positionForCoordinates): If the position is equal to the left edge of the box,
        make the affinity downstream so the position doesn't jump back to the previous line.
        (WebCore::RenderText::atLineWrap): The logic was reversed here in a recent change.
        If the box is not at a line break, then its at a line wrap.
        (WebCore::RenderText::caretRect): Only go to the next text box if its at a line wrap and the
         affinity is also downstream.  If its upstream, then the correct box is on the current line.
        (WebCore::RenderText::inlineBox): ditto.
        * rendering/RenderTextField.cpp:
        (WebCore::RenderTextField::updateFromElement): multi line controls don't need to check
         valueMatchesRenderer before updating the renderer.  For textareas, the renderer should always try to update.
         This matches our old textarea behavior.
        (WebCore::RenderTextField::text): Pass true to textContent so it converts BRs to newlines.
        (WebCore::RenderTextField::textWithHardLineBreaks): Iterate through the RootLineBoxes to find the soft wraps and replace them with newlines.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@14616 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/forms/textarea-appearance-wrap-expected.txt [new file with mode: 0644]
LayoutTests/fast/forms/textarea-appearance-wrap.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/dom/Range.cpp
WebCore/dom/Range.h
WebCore/editing/ReplaceSelectionCommand.cpp
WebCore/editing/visible_units.cpp
WebCore/rendering/RenderText.cpp
WebCore/rendering/RenderTextField.cpp

index 5eea9c735c59797350ac5693ed55101163df55b4..50124b0947a004e7787b0b35bae4e857176918b1 100644 (file)
@@ -1,3 +1,13 @@
+2006-05-26  Adele Peterson  <adele@apple.com>
+
+        Reviewed by Hyatt.
+
+        Added test for new textarea implementation.  Tests
+        wrap attributes and form submission.
+
+        * fast/forms/textarea-appearance-wrap-expected.txt: Added.
+        * fast/forms/textarea-appearance-wrap.html: Added.
+
 2006-05-26  Anders Carlsson  <acarlsson@apple.com>
 
         Reviewed by Geoff.
diff --git a/LayoutTests/fast/forms/textarea-appearance-wrap-expected.txt b/LayoutTests/fast/forms/textarea-appearance-wrap-expected.txt
new file mode 100644 (file)
index 0000000..9d81370
--- /dev/null
@@ -0,0 +1,26 @@
+This tests that textarea wrap attributes work correctly when submitting a form.
+
+
+
+
+
+
+wrap=hard : Success
+
+
+wrap=soft : Success
+
+
+wrap=off : Success
+
+
+wrap=hard rtl text: Success
+
+
+wrap=hard mixed rtl and ltr text: Success
+
+
+wrap=hard visibility:hidden: Success
+
+
+
diff --git a/LayoutTests/fast/forms/textarea-appearance-wrap.html b/LayoutTests/fast/forms/textarea-appearance-wrap.html
new file mode 100644 (file)
index 0000000..9d4256b
--- /dev/null
@@ -0,0 +1,98 @@
+<html>
+<body>
+This tests that textarea wrap attributes work correctly when submitting a form.
+<form action="?" name=f>
+ <textarea name=ta_WrapHard cols=5 style="-webkit-appearance:textarea" wrap=hard>123456789</textarea><br>
+ <textarea name=ta_WrapSoft cols=5 style="-webkit-appearance:textarea" wrap=soft>123456789</textarea><br>
+ <textarea name=ta_WrapOff cols=5 style="-webkit-appearance:textarea" wrap=off>123456789</textarea><br>
+ <textarea name=ta_WrapHardRTL cols=5 style="-webkit-appearance:textarea; direction:rtl" wrap=hard>&#1488;&#1489;&#1490;&#1488;&#1489;&#1490;&#1488;&#1489;&#1490;&#1488;&#1489;&#1490;</textarea><br>
+ <textarea name=ta_WrapHardBidi cols=5 style="-webkit-appearance:textarea; direction:ltr" wrap=hard>abc&#1488;&#1489;&#1490;&#1488;&#1489;&#1490;abc</textarea><br>
+ <textarea name=ta_WrapHardHidden cols=5 style="-webkit-appearance:textarea; visibility:hidden" wrap=hard>123456789</textarea><br>
+
+</form>
+<div id="res">
+</div>
+<script>
+function log(msg) {
+    document.getElementById('res').innerHTML = document.getElementById('res').innerHTML + msg + "<br>";
+}
+    
+if (window.layoutTestController) {
+    layoutTestController.dumpAsText();
+    layoutTestController.waitUntilDone();
+}
+
+if (document.URL.substring(0, 4) == "file") {
+
+    if (document.URL.indexOf('?') == -1) {
+        document.f.submit();
+    } else {
+        var urlString = document.URL.substring(document.URL.indexOf('?')+1, document.URL.length);
+        var index1 = 0;
+        var index2 = urlString.indexOf('ta_WrapSoft');
+        var index3 = urlString.indexOf('ta_WrapOff');
+        var index4 = urlString.indexOf('ta_WrapHardRTL');
+        var index5 = urlString.indexOf('ta_WrapHardBidi');
+        var index6 = urlString.indexOf('ta_WrapHardHidden');
+        var index7 = document.URL.length;
+        var expectedResult;
+        var result;
+        
+        // wrap = hard
+        result = unescape(urlString.substring(index1, index2 - 1));
+        expectedResult = unescape("ta_WrapHard=1234567%0D%0A89");
+        if (result == expectedResult)
+            log("<p>wrap=hard : Success</p>");
+        else
+            log("<p>wrap=hard : Failure<br>" + result + " != " + expectedResult + "</p>");
+        
+        // wrap = soft
+        result = unescape(urlString.substring(index2, index3 - 1));
+        expectedResult = unescape("ta_WrapSoft=123456789");
+        if (result == expectedResult)
+            log("<p>wrap=soft : Success</p>");
+        else
+            log("<p>wrap=soft : Failure<br>" + result + " != " + expectedResult + "</p>");
+    
+        // wrap = off
+        result = unescape(urlString.substring(index3, index4 - 1));
+        expectedResult = unescape("ta_WrapOff=123456789");
+        if (result == expectedResult)
+            log("<p>wrap=off : Success</p>");
+        else
+            log("<p>wrap=off : Failure<br>" + result + " != " + expectedResult + "</p>");
+    
+        // wrap = hard - RTL text
+        result = unescape(urlString.substring(index4, index5 - 1));
+        expectedResult = unescape("ta_WrapHardRTL=&#1488;&#1489;&#1490;&#1488;&#1489;&#1490;&#1488;&#1489;%0D%0A&#1490;&#1488;&#1489;&#1490;");
+        if (result == expectedResult)
+            log("<p>wrap=hard rtl text: Success</p>");
+        else
+            log("<p>wrap=hard rtl text : Failure<br>" + result + " != " + expectedResult + "</p>");
+
+        // wrap = hard, mixed rtl and ltr text
+        result = unescape(urlString.substring(index5, index6 - 1));
+        expectedResult = unescape("ta_WrapHardBidi=abc&#1488;&#1489;&#1490;&#1488;&#1489;%0D%0A&#1490;abc");
+        if (result == expectedResult)
+            log("<p>wrap=hard mixed rtl and ltr text: Success</p>");
+        else
+            log("<p>wrap=hard mixed rtl and ltr text : Failure<br>" + result + " != " + expectedResult + "</p>");
+
+        // wrap = hard, visibility:hidden
+        result = unescape(urlString.substring(index6, index7 - 1));
+        expectedResult = unescape("ta_WrapHardHidden=1234567%0D%0A89");
+        if (result == expectedResult)
+            log("<p>wrap=hard visibility:hidden: Success</p>");
+        else
+            log("<p>wrap=hard visibility:hidden: Failure<br>" + result + " != " + expectedResult + "</p>");
+
+        if (window.layoutTestController)
+            layoutTestController.notifyDone();
+    }
+
+} else {
+    document.write("<p>This test doesn't work directly from bugzilla, please save it to a local file first.</p>");
+}
+</script>
+</body>
+</html>
index b2c0b74c8f34ad1cc7a0d55e2540885f2a43080e..f8f5fc98f3d1ef4d582d0125c043d74ed289225f 100644 (file)
@@ -1,3 +1,33 @@
+2006-05-26  Adele Peterson  <adele@apple.com>
+
+        Reviewed by Hyatt.
+
+        Added support for wrap=hard for new textarea implementation.
+
+        Tests: fast/forms/textarea-appearance-wrap.html
+
+        * dom/Range.h: Added version of toString that will convert BRs to newlines.
+        * dom/Range.cpp: (WebCore::Range::toString):
+        * editing/ReplaceSelectionCommand.cpp: (WebCore::ReplacementFragment::ReplacementFragment): 
+          When in plain-text mode, and a white-space mode that doesn't collapse whitespace, create a fragment with one text node.
+        * editing/visible_units.cpp:
+        (WebCore::previousLinePosition): Subtract scroll offset so the absolute position for the containing block is correct.
+        (WebCore::nextLinePosition): ditto.
+        * rendering/RenderText.cpp:
+        (WebCore::RenderText::positionForCoordinates): If the position is equal to the left edge of the box,
+        make the affinity downstream so the position doesn't jump back to the previous line.
+        (WebCore::RenderText::atLineWrap): The logic was reversed here in a recent change.
+        If the box is not at a line break, then its at a line wrap.
+        (WebCore::RenderText::caretRect): Only go to the next text box if its at a line wrap and the
+         affinity is also downstream.  If its upstream, then the correct box is on the current line.
+        (WebCore::RenderText::inlineBox): ditto.
+        * rendering/RenderTextField.cpp:
+        (WebCore::RenderTextField::updateFromElement): multi line controls don't need to check
+         valueMatchesRenderer before updating the renderer.  For textareas, the renderer should always try to update.
+         This matches our old textarea behavior.
+        (WebCore::RenderTextField::text): Pass true to textContent so it converts BRs to newlines.
+        (WebCore::RenderTextField::textWithHardLineBreaks): Iterate through the RootLineBoxes to find the soft wraps and replace them with newlines.
+
 2006-05-26  Anders Carlsson  <acarlsson@apple.com>
 
         Reviewed by Geoff.
index 755f8085b9ecaa712964e13c92833ef907d2a05f..0c2ee6a85ca0928aa348f4ee889d59db12d1660b 100644 (file)
@@ -30,6 +30,7 @@
 #include "DocumentFragment.h"
 #include "ExceptionCode.h"
 #include "HTMLElement.h"
+#include "HTMLNames.h"
 #include "ProcessingInstruction.h"
 #include "RenderBlock.h"
 #include "TextIterator.h"
@@ -38,6 +39,8 @@
 
 namespace WebCore {
 
+using namespace HTMLNames;
+
 Range::Range(Document* ownerDocument)
     : m_ownerDocument(ownerDocument)
     , m_startContainer(ownerDocument)
@@ -802,6 +805,11 @@ void Range::insertNode(PassRefPtr<Node> newNode, ExceptionCode& ec)
 }
 
 String Range::toString(ExceptionCode& ec) const
+{
+    return toString(false, ec);
+}
+
+String Range::toString(bool convertBRsToNewlines, ExceptionCode& ec) const
 {
     if (m_detached) {
         ec = INVALID_STATE_ERR;
@@ -819,6 +827,8 @@ String Range::toString(ExceptionCode& ec) const
                 str.remove(0, m_startOffset);
             text += str;
         }
+        if (n->hasTagName(brTag) && convertBRsToNewlines)
+            text += "\n";
     }
     return text;
 }
index 5b306fc75cbe64bedd6951de1783175a103f439e..e17711816c2799120f62ca68edf68e9456fcb7f7 100644 (file)
@@ -73,6 +73,8 @@ public:
     PassRefPtr<DocumentFragment> cloneContents(ExceptionCode&);
     void insertNode(PassRefPtr<Node>, ExceptionCode&);
     String toString(ExceptionCode&) const;
+    String toString(bool convertBRsToNewlines, ExceptionCode&) const;
+
     String toHTML() const;
     String text() const;
 
index 1205f28c0086529562edc87e4952b1e9004e92fb..e9604e1b90b4de04728b9700d54276649c2850f0 100644 (file)
@@ -88,7 +88,14 @@ ReplacementFragment::ReplacementFragment(Document *document, DocumentFragment *f
     editableRoot->dispatchEvent(evt, ec, true);
     ASSERT(ec == 0);
     if (text != evt->text() || !editableRoot->isContentRichlyEditable()) {
-        m_fragment = createFragmentFromText(document, evt->text().deprecatedString());
+        // If the root is in plain-text mode, and white-space shouldn't be collapsed, then contruct a fragment that contains one text node
+        if (!editableRoot->isContentRichlyEditable() && editableRoot->renderer() && !editableRoot->renderer()->style()->collapseWhiteSpace()) {
+            RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
+            fragment->appendChild(document->createTextNode(evt->text()), ec);
+            ASSERT(ec == 0);
+            m_fragment = fragment;
+        } else
+            m_fragment = createFragmentFromText(document, evt->text().deprecatedString());
         firstChild = m_fragment->firstChild();
         lastChild = m_fragment->firstChild();
         
index 4306368435051e060fe9c30bad9347457bc073b7..1094277a82c9e7f094a8083d0cfd8c8cd5306e00 100644 (file)
@@ -414,6 +414,8 @@ VisiblePosition previousLinePosition(const VisiblePosition &visiblePosition, int
     if (root) {
         int absx, absy;
         containingBlock->absolutePositionForContent(absx, absy);
+        if (containingBlock->hasOverflowClip())
+            containingBlock->layer()->subtractScrollOffset(absx, absy);
         RenderObject *renderer = root->closestLeafChildForXPos(x, absx)->object();
         return renderer->positionForCoordinates(x, absy + root->topOverflow());
     }
@@ -477,6 +479,8 @@ VisiblePosition nextLinePosition(const VisiblePosition &visiblePosition, int x)
     if (root) {
         int absx, absy;
         containingBlock->absolutePositionForContent(absx, absy);
+        if (containingBlock->hasOverflowClip())
+            containingBlock->layer()->subtractScrollOffset(absx, absy);
         RenderObject *renderer = root->closestLeafChildForXPos(x, absx)->object();
         return renderer->positionForCoordinates(x, absy + root->topOverflow());
     }    
index a4073b9dbb4d355cf615b005540aa65bdfeb78ca..2e8c2fef8673d8d0d010f7c27edde97650488ddf 100644 (file)
@@ -279,6 +279,11 @@ VisiblePosition RenderText::positionForCoordinates(int _x, int _y)
         if (_y >= absy + box->root()->topOverflow() && _y < absy + box->root()->bottomOverflow()) {
             offset = box->offsetForPosition(_x - absx);
 
+            if (_x == absx + box->m_x)
+                // the x coordinate is equal to the left edge of this box
+                // the affinity must be downstream so the position doesn't jump back to the previous line
+                return VisiblePosition(element(), offset + box->m_start, DOWNSTREAM);
+
             if (_x < absx + box->m_x + box->m_width)
                 // and the x coordinate is to the left of the right edge of this box
                 // check to see if position goes in this box
@@ -352,7 +357,7 @@ static RenderObject *lastRendererOnPrevLine(InlineBox *box)
 bool RenderText::atLineWrap(InlineTextBox *box, int offset)
 {
     if (box->nextTextBox() && !box->nextOnLine() && offset == box->m_start + box->m_len) {
-        if (!style()->preserveNewline() || box->isLineBreak())
+        if (!style()->preserveNewline() || !box->isLineBreak())
             return true;
     }
     
@@ -369,7 +374,7 @@ IntRect RenderText::caretRect(int offset, EAffinity affinity, int *extraWidthToE
     for (box = firstTextBox(); box; box = box->nextTextBox()) {
         if ((offset >= box->m_start) && (offset <= box->m_start + box->m_len)) {
             // Check if downstream affinity would make us move to the next line.
-            if (atLineWrap(box, offset)) {
+            if (atLineWrap(box, offset) && affinity == DOWNSTREAM) {
                 // Use the next text box
                 box = box->nextTextBox();
                 offset = box->m_start;
@@ -1103,7 +1108,7 @@ InlineBox *RenderText::inlineBox(int offset, EAffinity affinity)
 {
     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
         if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
-            if (atLineWrap(box, offset))
+            if (atLineWrap(box, offset) && affinity == DOWNSTREAM)
                 return box->nextTextBox();
             return box;
         }
index 2e3cadffd4708e974aeba1bb0f69b419f10e041c..0827f677a9e51351ff82023a63a081cd0799c58f 100644 (file)
@@ -33,6 +33,7 @@
 #include "dom2_eventsimpl.h"
 #include <math.h>
 #include "RenderTheme.h"
+#include "visible_units.h"
 
 namespace WebCore {
 
@@ -140,7 +141,7 @@ void RenderTextField::updateFromElement()
             value = static_cast<HTMLTextAreaElement*>(element)->value().copy();    
         else
             value = static_cast<HTMLInputElement*>(element)->value().copy();        
-        if (!element->valueMatchesRenderer()) {
+        if (!element->valueMatchesRenderer() || m_multiLine) {
             String oldText = text();
             if (value.isNull())
                 value = "";
@@ -245,15 +246,46 @@ void RenderTextField::subtreeHasChanged()
 String RenderTextField::text()
 {
     if (m_div)
-        return m_div->textContent().replace(backslashAsCurrencySymbol(), '\\');
+        return m_div->textContent(true).replace('\\', backslashAsCurrencySymbol());
     return String();
 }
 
 String RenderTextField::textWithHardLineBreaks()
 {
-    String txt = text();
-    // FIXME: add in line breaks in places where we wrap
-    return txt;
+    String s("");
+    
+    if (!m_div)
+        return s;
+
+    document()->updateLayout();
+
+    ASSERT(m_div->firstChild());
+    InlineBox* box = m_div->firstChild()->renderer()->inlineBox(0, DOWNSTREAM);
+    if (!box)
+        return s;
+    
+    ExceptionCode ec = 0;
+    RefPtr<Range> range = new Range(document());
+    range->selectNodeContents(m_div.get(), ec);
+    for (RootInlineBox* line = box->root(); line; line = line->nextRootBox()) {
+        // If we're at a soft wrap, then insert the hard line break here
+        if (!line->endsWithBreak() && line->nextRootBox()) {
+            // Update range so it ends before this wrap
+            ASSERT(line->lineBreakObj());
+            range->setEnd(line->lineBreakObj()->node(), line->lineBreakPos(), ec);
+
+            s.append(range->toString(true, ec));
+            s.append("\n");
+
+            // Update range so it starts after this wrap
+            range->setEnd(m_div.get(), 1, ec);
+            range->setStart(line->lineBreakObj()->node(), line->lineBreakPos(), ec);
+        }
+    }
+    s.append(range->toString(true, ec));
+    ASSERT(ec == 0);
+
+    return s.replace('\\', backslashAsCurrencySymbol());
 }
 
 void RenderTextField::calcHeight()