Commit e9c46e1b authored by justing's avatar justing

LayoutTests:

        Reviewed by darin
        
        <http://bugzilla.opendarwin.org/show_bug.cgi?id=6893> 
        REGRESSION: Major bug with TinyMCE, no value submitted from textarea
        <rdar://problem/3465857> value from textarea form field inside of hidden div isn't submitted
        <rdar://problem/3968059> Textarea with hard-wrap: pre-filled text doesn't get hard-wrapped

        * fast/forms/textarea-hard-linewrap-expected.txt: Added.
        * fast/forms/textarea-hard-linewrap.html: Added.
        * fast/forms/textarea-setvalue-submit-expected.txt: Added.
        * fast/forms/textarea-setvalue-submit.html: Added.

WebCore:

        Reviewed by darin
        
        <http://bugzilla.opendarwin.org/show_bug.cgi?id=6893> 
        REGRESSION: Major bug with TinyMCE, no value submitted from textarea
        <rdar://problem/3465857> value from textarea form field inside of hidden div isn't submitted
        <rdar://problem/3968059> Textarea with hard-wrap: pre-filled text doesn't get hard-wrapped
        
        Canonicalize line endings in textareas to avoid the workarounds that were a source of bugs,
        Also call textWithHardLineWraps inside appendFormData (and nowhere else) if wrap="hard".

        * khtml/html/HTMLTextAreaElementImpl.cpp:
        (WebCore::HTMLTextAreaElementImpl::HTMLTextAreaElementImpl):
        (WebCore::HTMLTextAreaElementImpl::select):
        (WebCore::HTMLTextAreaElementImpl::appendFormData):
        (WebCore::HTMLTextAreaElementImpl::rendererWillBeDestroyed):
        (WebCore::HTMLTextAreaElementImpl::updateValue):
        (WebCore::HTMLTextAreaElementImpl::value):
        (WebCore::HTMLTextAreaElementImpl::setValue):
        (WebCore::HTMLTextAreaElementImpl::defaultValue):
        * khtml/html/HTMLTextAreaElementImpl.h:
        (DOM::HTMLTextAreaElementImpl::invalidateValue):
        * kwq/KWQTextArea.h:
        * kwq/KWQTextArea.mm:
        (-[KWQTextAreaTextView textDidChange:]):
        (-[KWQTextAreaTextView text]):
        (-[KWQTextAreaTextView textWithHardLineBreaks]):
        (-[KWQTextAreaTextView setSelectedRange:]):
        (-[KWQTextAreaTextView selectedRange]):
        (-[KWQTextAreaTextView getCursorPositionAsIndex:inParagraph:]):
        (RangeOfParagraph):
        (-[KWQTextAreaTextView textView:shouldChangeTextInRange:replacementString:]):
        * rendering/render_form.cpp:
        (WebCore::RenderTextArea::destroy):
        (WebCore::RenderTextArea::updateFromElement):
        (WebCore::RenderTextArea::text):
        (WebCore::RenderTextArea::textWithHardLineBreaks):
        * rendering/render_form.h:



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@13051 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 4efd4763
2006-02-27 Justin Garcia <justin.garcia@apple.com>
Reviewed by darin
<http://bugzilla.opendarwin.org/show_bug.cgi?id=6893>
REGRESSION: Major bug with TinyMCE, no value submitted from textarea
<rdar://problem/3465857> value from textarea form field inside of hidden div isn't submitted
<rdar://problem/3968059> Textarea with hard-wrap: pre-filled text doesn't get hard-wrapped
* fast/forms/textarea-hard-linewrap-expected.txt: Added.
* fast/forms/textarea-hard-linewrap.html: Added.
* fast/forms/textarea-setvalue-submit-expected.txt: Added.
* fast/forms/textarea-setvalue-submit.html: Added.
2006-02-27 Mitz Pettel <opendarwin.org@mitzpettel.com>
Reviewed by Darin.
......
This tests that a textarea's default value is submitted with hard line wraps (carriage returns where line wraps appear in the textarea).
Due to a bug, the textarea is actually a bit wider than we specify via the cols attribute, so, the number and position of the carriage returns in the submitted data is not what you'd expect. The expected results for this testcase should be updated when http://bugzilla.opendarwin.org/show_bug.cgi?id=7505 is fixed.
Success
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
<title>Charsets and submitting forms</title>
</head>
<body>
<p>This tests that a textarea's default value is submitted with hard line wraps (carriage returns where line wraps appear in the textarea).</p>
<p>Due to a bug, the textarea is actually a bit wider than we specify via the cols attribute, so, the number and position of the carriage returns in the submitted data is not what you'd expect. The expected results for this testcase should be updated when <a href="http://bugzilla.opendarwin.org/show_bug.cgi?id=7505">http://bugzilla.opendarwin.org/show_bug.cgi?id=7505</a> is fixed.</p>
<form name="f" method="?" action="textarea-hard-linewrap.html">
<textarea id="textarea" name="textarea" wrap="hard" cols="5">123456789</textarea>
<input type="submit">
</form>
<script>
function foo() {
document.f.submit();
}
if (document.URL.indexOf('?') == -1) {
if (window.layoutTestController) {
window.layoutTestController.dumpAsText();
window.layoutTestController.waitUntilDone();
}
window.setTimeout(foo, 200);
} else {
var formData = document.URL.substring(document.URL.indexOf('?') + 1, document.URL.length);
if (formData == "textarea=1234567%0D%0A89")
document.write("Success");
else
document.write("Failure. The form data that should have been submitted: textarea=1234567%0D%0A89<br>This is what was actually submitted: " + formData);
if (window.layoutTestController)
window.layoutTestController.notifyDone();
}
</script>
</body>
</html>
\ No newline at end of file
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
<title>Charsets and submitting forms</title>
</head>
<body>
<form name="f" method="?" action="textarea-setvalue-submit.html">
<textarea id="textarea1" name="textarea1">default value</textarea>
<textarea id="textarea2" name="textarea2" style="display:none">default value</textarea>
</form>
<script>
if (document.URL.indexOf('?') == -1) {
if (window.layoutTestController) {
window.layoutTestController.dumpAsText();
window.layoutTestController.waitUntilDone();
}
var textarea1 = document.getElementById("textarea1");
textarea1.value = "new value";
var textarea2 = document.getElementById("textarea2");
textarea2.value = "new value";
document.f.submit();
} else {
var formData = document.URL.substring(document.URL.indexOf('?') + 1, document.URL.length);
var expected = "textarea1=new+value&textarea2=new+value";
if (formData == expected)
document.write("Success");
else
document.write("Failure. The value set in the textarea via javascript wasn't sent when the form was submitted. Expected: " + expected + ", Found: " + formData);
if (window.layoutTestController)
window.layoutTestController.notifyDone();
}
</script>
</body>
</html>
\ No newline at end of file
2006-02-27 Justin Garcia <justin.garcia@apple.com>
Reviewed by darin
<http://bugzilla.opendarwin.org/show_bug.cgi?id=6893>
REGRESSION: Major bug with TinyMCE, no value submitted from textarea
<rdar://problem/3465857> value from textarea form field inside of hidden div isn't submitted
<rdar://problem/3968059> Textarea with hard-wrap: pre-filled text doesn't get hard-wrapped
Canonicalize line endings in textareas to avoid the workarounds that were a source of bugs,
Also call textWithHardLineWraps inside appendFormData (and nowhere else) if wrap="hard".
* khtml/html/HTMLTextAreaElementImpl.cpp:
(WebCore::HTMLTextAreaElementImpl::HTMLTextAreaElementImpl):
(WebCore::HTMLTextAreaElementImpl::select):
(WebCore::HTMLTextAreaElementImpl::appendFormData):
(WebCore::HTMLTextAreaElementImpl::rendererWillBeDestroyed):
(WebCore::HTMLTextAreaElementImpl::updateValue):
(WebCore::HTMLTextAreaElementImpl::value):
(WebCore::HTMLTextAreaElementImpl::setValue):
(WebCore::HTMLTextAreaElementImpl::defaultValue):
* khtml/html/HTMLTextAreaElementImpl.h:
(DOM::HTMLTextAreaElementImpl::invalidateValue):
* kwq/KWQTextArea.h:
* kwq/KWQTextArea.mm:
(-[KWQTextAreaTextView textDidChange:]):
(-[KWQTextAreaTextView text]):
(-[KWQTextAreaTextView textWithHardLineBreaks]):
(-[KWQTextAreaTextView setSelectedRange:]):
(-[KWQTextAreaTextView selectedRange]):
(-[KWQTextAreaTextView getCursorPositionAsIndex:inParagraph:]):
(RangeOfParagraph):
(-[KWQTextAreaTextView textView:shouldChangeTextInRange:replacementString:]):
* rendering/render_form.cpp:
(WebCore::RenderTextArea::destroy):
(WebCore::RenderTextArea::updateFromElement):
(WebCore::RenderTextArea::text):
(WebCore::RenderTextArea::textWithHardLineBreaks):
* rendering/render_form.h:
2006-02-27 Eric Seidel <eseidel@apple.com>
Reviewed by darin.
......
......@@ -40,7 +40,8 @@ using namespace EventNames;
using namespace HTMLNames;
HTMLTextAreaElementImpl::HTMLTextAreaElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
: HTMLGenericFormElementImpl(textareaTag, doc, f), m_valueIsValid(false), m_valueMatchesRenderer(false)
: HTMLGenericFormElementImpl(textareaTag, doc, f)
, m_valueMatchesRenderer(true)
{
// DTD requires rows & cols be specified, but we will provide reasonable defaults
m_rows = 2;
......@@ -98,7 +99,7 @@ void HTMLTextAreaElementImpl::setSelectionEnd(int end)
static_cast<RenderTextArea *>(renderer())->setSelectionEnd(end);
}
void HTMLTextAreaElementImpl::select( )
void HTMLTextAreaElementImpl::select()
{
if (renderer())
static_cast<RenderTextArea*>(renderer())->select();
......@@ -159,8 +160,12 @@ RenderObject *HTMLTextAreaElementImpl::createRenderer(RenderArena *arena, Render
bool HTMLTextAreaElementImpl::appendFormData(FormDataList& encoding, bool)
{
if (name().isEmpty()) return false;
encoding.appendData(name(), value());
if (name().isEmpty())
return false;
bool hardWrap = renderer() && wrap() == ta_Physical;
String v = hardWrap ? static_cast<RenderTextArea*>(renderer())->textWithHardLineBreaks() : value();
encoding.appendData(name(), v);
return true;
}
......@@ -169,39 +174,41 @@ void HTMLTextAreaElementImpl::reset()
setValue(defaultValue());
}
void HTMLTextAreaElementImpl::rendererWillBeDestroyed()
{
updateValue();
}
void HTMLTextAreaElementImpl::updateValue()
{
if (!m_valueIsValid) {
if (renderer()) {
m_value = static_cast<RenderTextArea*>(renderer())->text().qstring();
m_valueMatchesRenderer = true;
} else {
m_value = defaultValue().qstring();
m_valueMatchesRenderer = false;
}
m_valueIsValid = true;
if (!m_valueMatchesRenderer) {
ASSERT(renderer());
m_value = static_cast<RenderTextArea*>(renderer())->text();
m_valueMatchesRenderer = true;
}
}
DOMString HTMLTextAreaElementImpl::value()
{
updateValue();
return m_value.isNull() ? DOMString("") : m_value;
return m_value;
}
void HTMLTextAreaElementImpl::setValue(const DOMString &value)
{
m_value = value.qstring();
m_valueMatchesRenderer = false;
m_valueIsValid = true;
QString string = value.qstring();
// KWQTextArea normalizes line endings added by the user via the keyboard or pasting.
// We must normalize line endings coming from JS.
string.replace("\r\n", "\n");
string.replace("\r", "\n");
m_value = String(string);
m_valueMatchesRenderer = true;
if (renderer())
renderer()->updateFromElement();
// FIXME: Force reload from renderer, as renderer may have normalized line endings.
m_valueIsValid = false;
setChanged(true);
}
DOMString HTMLTextAreaElementImpl::defaultValue()
{
DOMString val = "";
......@@ -211,13 +218,12 @@ DOMString HTMLTextAreaElementImpl::defaultValue()
if (n->isTextNode())
val += static_cast<TextImpl*>(n)->data();
if (val[0] == '\r' && val[1] == '\n') {
val = val.copy();
val.remove(0,2);
} else if (val[0] == '\r' || val[0] == '\n') {
val = val.copy();
val.remove(0,1);
}
// FIXME: We should only drop the first carriage return for the default
// value in the original source, not defaultValues set from JS.
if (val.length() >= 2 && val[0] == '\r' && val[1] == '\n')
val.remove(0, 2);
else if (val.length() >= 1 && (val[0] == '\r' || val[0] == '\n'))
val.remove(0, 1);
return val;
}
......@@ -250,19 +256,6 @@ void HTMLTextAreaElementImpl::accessKeyAction(bool sendToAnyElement)
focus();
}
void HTMLTextAreaElementImpl::attach()
{
m_valueIsValid = true;
HTMLGenericFormElementImpl::attach();
updateValue();
}
void HTMLTextAreaElementImpl::detach()
{
HTMLGenericFormElementImpl::detach();
m_valueMatchesRenderer = false;
}
DOMString HTMLTextAreaElementImpl::accessKey() const
{
return getAttribute(accesskeyAttr);
......
......@@ -74,20 +74,15 @@ public:
virtual void childrenChanged();
virtual void parseMappedAttribute(MappedAttributeImpl *attr);
virtual khtml::RenderObject *createRenderer(RenderArena *, khtml::RenderStyle *);
virtual void attach();
virtual void detach();
virtual bool appendFormData(FormDataList&, bool);
virtual void reset();
DOMString value();
void setValue(const DOMString &value);
DOMString defaultValue();
void setDefaultValue(const DOMString &value);
void invalidateValue() { m_valueIsValid = false; }
void updateValue();
bool valueMatchesRenderer() const { return m_valueMatchesRenderer; }
void setValueMatchesRenderer() { m_valueMatchesRenderer = true; }
void invalidateValue() { m_valueMatchesRenderer = false; }
void rendererWillBeDestroyed();
virtual bool isEditable();
......@@ -104,9 +99,11 @@ protected:
int m_rows;
int m_cols;
WrapMethod m_wrap;
QString m_value;
bool m_valueIsValid;
String m_value;
bool m_valueMatchesRenderer;
private:
void updateValue();
};
} //namespace
......
......@@ -41,6 +41,7 @@ class QTextEdit;
BOOL inInitWithFrame;
BOOL resizableByUser;
BOOL resizableByUserComputed;
BOOL normalizeLineEndings;
}
- initWithQTextEdit:(QTextEdit *)w;
......
......@@ -215,6 +215,18 @@ const float LargeNumberForText = 1.0e7;
- (void)textDidChange:(NSNotification *)notification
{
// Turn \r\ns into \ns so that a line ending is always a single character.
// Turn \rs into \ns to simplify code that finds paragraph boundaries.
if (normalizeLineEndings) {
NSMutableString *string = [[textView string] mutableCopy];
[string replaceOccurrencesOfString:@"\r\n" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
[string replaceOccurrencesOfString:@"\r" withString:@"\n" options:NSLiteralSearch range:NSMakeRange(0, [string length])];
[textView setString:string];
normalizeLineEndings = NO;
}
if (widget)
widget->textChanged();
......@@ -243,20 +255,7 @@ const float LargeNumberForText = 1.0e7;
- (NSString *)text
{
NSString *text = [textView string];
if ([text rangeOfString:@"\r" options:NSLiteralSearch].location != NSNotFound) {
NSMutableString *mutableText = [[text mutableCopy] autorelease];
[mutableText replaceOccurrencesOfString:@"\r\n" withString:@"\n"
options:NSLiteralSearch range:NSMakeRange(0, [mutableText length])];
[mutableText replaceOccurrencesOfString:@"\r" withString:@"\n"
options:NSLiteralSearch range:NSMakeRange(0, [mutableText length])];
text = mutableText;
}
return text;
return [textView string];
}
- (NSString *)textWithHardLineBreaks
......@@ -273,18 +272,13 @@ const float LargeNumberForText = 1.0e7;
NSRange lineRange = [layoutManager characterRangeForGlyphRange:lineGlyphRange actualGlyphRange:NULL];
if ([textWithHardLineBreaks length]) {
unichar lastCharacter = [textWithHardLineBreaks characterAtIndex:[textWithHardLineBreaks length] - 1];
if (lastCharacter != '\n' && lastCharacter != '\r') {
if (lastCharacter != '\n') {
[textWithHardLineBreaks appendString:@"\n"];
}
}
[textWithHardLineBreaks appendString:[text substringWithRange:lineRange]];
}
[textWithHardLineBreaks replaceOccurrencesOfString:@"\r\n" withString:@"\n"
options:NSLiteralSearch range:NSMakeRange(0, [textWithHardLineBreaks length])];
[textWithHardLineBreaks replaceOccurrencesOfString:@"\r" withString:@"\n"
options:NSLiteralSearch range:NSMakeRange(0, [textWithHardLineBreaks length])];
return textWithHardLineBreaks;
}
......@@ -295,62 +289,12 @@ const float LargeNumberForText = 1.0e7;
- (void)setSelectedRange:(NSRange)aRange
{
NSString *text = [textView string];
// Ok, the selection has to match up with the string returned by -text
// and since -text translates \r\n to \n, we have to modify our selection
// if a \r\n sequence is anywhere in or before the selection
unsigned count = 0;
NSRange foundRange, searchRange = NSMakeRange(0, aRange.location);
while (foundRange = [text rangeOfString:@"\r\n" options:NSLiteralSearch range:searchRange],
foundRange.location != NSNotFound) {
count++;
searchRange.location = NSMaxRange(foundRange);
if (searchRange.location >= aRange.location) break;
searchRange.length = aRange.location - searchRange.location;
}
aRange.location += count;
count = 0;
searchRange = NSMakeRange(aRange.location, aRange.length);
while (foundRange = [text rangeOfString:@"\r\n" options:NSLiteralSearch range:searchRange],
foundRange.location != NSNotFound) {
count++;
searchRange.location = NSMaxRange(foundRange);
if (searchRange.location >= NSMaxRange(aRange)) break;
searchRange.length = NSMaxRange(aRange) - searchRange.location;
}
aRange.length += count;
[textView setSelectedRange:aRange];
}
- (NSRange)selectedRange
{
NSRange aRange = [textView selectedRange];
if (aRange.location == NSNotFound) {
return aRange;
}
// Same issue as with -setSelectedRange: regarding \r\n sequences
unsigned count = 0;
NSRange foundRange, searchRange = NSMakeRange(0, aRange.location);
NSString *text = [textView string];
while (foundRange = [text rangeOfString:@"\r\n" options:NSLiteralSearch range:searchRange],
foundRange.location != NSNotFound) {
count++;
searchRange.location = NSMaxRange(foundRange);
if (searchRange.location >= aRange.location) break;
searchRange.length = aRange.location - searchRange.location;
}
aRange.location -= count;
count = 0;
searchRange = NSMakeRange(aRange.location, aRange.length);
while (foundRange = [text rangeOfString:@"\r\n" options:NSLiteralSearch range:searchRange],
foundRange.location != NSNotFound) {
count++;
searchRange.location = NSMaxRange(foundRange);
if (searchRange.location >= NSMaxRange(aRange)) break;
searchRange.length = NSMaxRange(aRange) - searchRange.location;
}
aRange.length -= count;
return aRange;
return [textView selectedRange];
}
- (BOOL)hasSelection
......@@ -460,7 +404,7 @@ const float LargeNumberForText = 1.0e7;
NSScanner *scanner = [NSScanner scannerWithString:text];
[scanner setCharactersToBeSkipped:nil];
NSCharacterSet *newlineSet = [NSCharacterSet characterSetWithCharactersInString:@"\r\n"];
NSCharacterSet *newlineSet = [NSCharacterSet characterSetWithCharactersInString:@"\n"];
while (true) {
[scanner scanUpToCharactersFromSet:newlineSet intoString:NULL];
......@@ -471,11 +415,7 @@ const float LargeNumberForText = 1.0e7;
paragraphSoFar++;
unichar c = [text characterAtIndex:[scanner scanLocation]];
[scanner setScanLocation:[scanner scanLocation]+1]; // skip over the found char
if (c == '\r') {
[scanner scanString:@"\n" intoString:NULL]; // get the entire crlf if it is one
}
paragraphStart = [scanner scanLocation];
}
......@@ -494,7 +434,7 @@ static NSRange RangeOfParagraph(NSString *text, int paragraph)
NSScanner *scanner = [NSScanner scannerWithString:text];
[scanner setCharactersToBeSkipped:nil];
NSCharacterSet *newlineSet = [NSCharacterSet characterSetWithCharactersInString:@"\r\n"];
NSCharacterSet *newlineSet = [NSCharacterSet characterSetWithCharactersInString:@"\n"];
while (true) {
[scanner scanUpToCharactersFromSet:newlineSet intoString:NULL];
......@@ -505,11 +445,7 @@ static NSRange RangeOfParagraph(NSString *text, int paragraph)
paragraphSoFar++;
unichar c = [text characterAtIndex:[scanner scanLocation]];
[scanner setScanLocation:[scanner scanLocation]+1]; // skip over the found char
if (c == '\r') {
[scanner scanString:@"\n" intoString:NULL]; // get the entire crlf if it is one
}
paragraphStart = [scanner scanLocation];
}
......@@ -855,6 +791,14 @@ static NSRange RangeOfParagraph(NSString *text, int paragraph)
return NO;
}
- (BOOL)textView:(NSTextView *)aTextView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString
{
if ([replacementString rangeOfCharacterFromSet:[NSCharacterSet characterSetWithCharactersInString:@"\r"]].location != NSNotFound)
normalizeLineEndings = YES;
return YES;
}
- (NSSize)sizeWithColumns:(int)numColumns rows:(int)numRows
{
// Must use font from _font field rather than from the text view's font method,
......
......@@ -972,7 +972,7 @@ RenderTextArea::RenderTextArea(HTMLTextAreaElementImpl *element)
void RenderTextArea::destroy()
{
element()->updateValue();
element()->rendererWillBeDestroyed();
RenderFormElement::destroy();
}
......@@ -1033,38 +1033,33 @@ void RenderTextArea::updateFromElement()
w->setReadOnly(e->readOnly());
w->setDisabled(e->disabled());
e->updateValue();
if (!e->valueMatchesRenderer()) {
DOMString widgetText = text();
DOMString text = e->value();
text.replace(QChar('\\'), backslashAsCurrencySymbol());
if (widgetText != text) {
int line, col;
w->getCursorPosition( &line, &col );
m_updating = true;
w->setText(text);
m_updating = false;
w->setCursorPosition( line, col );
}
e->setValueMatchesRenderer();
m_dirty = false;
String widgetText = text();
String text = e->value();
text.replace(QChar('\\'), backslashAsCurrencySymbol());
if (widgetText != text) {
int line, col;
w->getCursorPosition( &line, &col );
m_updating = true;
w->setText(text);
m_updating = false;
w->setCursorPosition( line, col );
}
m_dirty = false;
w->setColors(style()->backgroundColor(), style()->color());
RenderFormElement::updateFromElement();
}
DOMString RenderTextArea::text()
String RenderTextArea::text()
{
DOMString txt;
QTextEdit* w = static_cast<QTextEdit*>(m_widget);
if (element()->wrap() == HTMLTextAreaElementImpl::ta_Physical)
txt = w->textWithHardLineBreaks();
else
txt = w->text();
String txt = static_cast<QTextEdit*>(m_widget)->text();
return txt.replace(backslashAsCurrencySymbol(), QChar('\\'));
}
String RenderTextArea::textWithHardLineBreaks()
{
String txt = static_cast<QTextEdit*>(m_widget)->textWithHardLineBreaks();
return txt.replace(backslashAsCurrencySymbol(), QChar('\\'));
}
......
......@@ -291,7 +291,8 @@ public:
DOM::HTMLTextAreaElementImpl* element() const
{ return static_cast<DOM::HTMLTextAreaElementImpl*>(RenderObject::element()); }
DOM::DOMString text();
String text();
String textWithHardLineBreaks();
int selectionStart();
int selectionEnd();
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment