LayoutTests:
[WebKit-https.git] / WebCore / html / HTMLTextAreaElement.cpp
1 /*
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  *           (C) 2001 Dirk Mueller (mueller@kde.org)
7  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
8  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Library General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Library General Public License for more details.
19  *
20  * You should have received a copy of the GNU Library General Public License
21  * along with this library; see the file COPYING.LIB.  If not, write to
22  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23  * Boston, MA 02111-1307, USA.
24  *
25  */
26
27 #include "config.h"
28 #include "HTMLTextAreaElement.h"
29
30 #include "Document.h"
31 #include "Event.h"
32 #include "EventNames.h"
33 #include "FormDataList.h"
34 #include "Frame.h"
35 #include "HTMLNames.h"
36 #include "RenderTextControl.h"
37 #include "Text.h"
38 #include "RenderStyle.h"
39
40 namespace WebCore {
41
42 using namespace EventNames;
43 using namespace HTMLNames;
44
45 HTMLTextAreaElement::HTMLTextAreaElement(Document *doc, HTMLFormElement *f)
46     : HTMLGenericFormElement(textareaTag, doc, f)
47     , m_rows(2)
48     , m_cols(20)
49     , m_wrap(ta_Virtual)
50     , cachedSelStart(-1)
51     , cachedSelEnd(-1)
52 {
53     setValueMatchesRenderer();
54     document()->registerFormElementWithState(this);
55 }
56
57 HTMLTextAreaElement::~HTMLTextAreaElement()
58 {
59     document()->deregisterFormElementWithState(this);
60 }
61
62 const AtomicString& HTMLTextAreaElement::type() const
63 {
64     static const AtomicString textarea("textarea");
65     return textarea;
66 }
67
68 String HTMLTextAreaElement::stateValue() const
69 {
70     return value();
71 }
72
73 void HTMLTextAreaElement::restoreState(const String& state)
74 {
75     setDefaultValue(state);
76 }
77
78 int HTMLTextAreaElement::selectionStart()
79 {
80     if (renderer()) {
81         if (document()->focusNode() != this && cachedSelStart >= 0)
82             return cachedSelStart;
83         return static_cast<RenderTextControl *>(renderer())->selectionStart();
84     }
85     return 0;
86 }
87
88 int HTMLTextAreaElement::selectionEnd()
89 {
90     if (renderer()) {
91         if (document()->focusNode() != this && cachedSelEnd >= 0)
92             return cachedSelEnd;
93         return static_cast<RenderTextControl *>(renderer())->selectionEnd();
94     }
95     return 0;
96 }
97
98 void HTMLTextAreaElement::setSelectionStart(int start)
99 {
100     if (renderer())
101         static_cast<RenderTextControl*>(renderer())->setSelectionStart(start);
102 }
103
104 void HTMLTextAreaElement::setSelectionEnd(int end)
105 {
106     if (renderer())
107         static_cast<RenderTextControl*>(renderer())->setSelectionEnd(end);
108 }
109
110 void HTMLTextAreaElement::select()
111 {
112     if (renderer())
113         static_cast<RenderTextControl *>(renderer())->select();
114 }
115
116 void HTMLTextAreaElement::setSelectionRange(int start, int end)
117 {
118     if (renderer())
119         static_cast<RenderTextControl*>(renderer())->setSelectionRange(start, end);
120 }
121
122 void HTMLTextAreaElement::childrenChanged()
123 {
124     setValue(defaultValue());
125 }
126     
127 void HTMLTextAreaElement::parseMappedAttribute(MappedAttribute *attr)
128 {
129     if (attr->name() == rowsAttr) {
130         m_rows = !attr->isNull() ? attr->value().toInt() : 3;
131         if (renderer())
132             renderer()->setNeedsLayoutAndMinMaxRecalc();
133     } else if (attr->name() == colsAttr) {
134         m_cols = !attr->isNull() ? attr->value().toInt() : 60;
135         if (renderer())
136             renderer()->setNeedsLayoutAndMinMaxRecalc();
137     } else if (attr->name() == wrapAttr) {
138         // virtual / physical is Netscape extension of HTML 3.0, now deprecated
139         // soft/ hard / off is recommendation for HTML 4 extension by IE and NS 4
140         if (equalIgnoringCase(attr->value(), "virtual") || equalIgnoringCase(attr->value(), "soft"))
141             m_wrap = ta_Virtual;
142         else if (equalIgnoringCase(attr->value(), "physical") || equalIgnoringCase(attr->value(), "hard"))
143             m_wrap = ta_Physical;
144         else if (equalIgnoringCase(attr->value(), "on" ))
145             m_wrap = ta_Physical;
146         else if (equalIgnoringCase(attr->value(), "off"))
147             m_wrap = ta_NoWrap;
148         if (renderer())
149             renderer()->setNeedsLayoutAndMinMaxRecalc();
150     } else if (attr->name() == accesskeyAttr) {
151         // ignore for the moment
152     } else if (attr->name() == onfocusAttr)
153         setHTMLEventListener(focusEvent, attr);
154     else if (attr->name() == onblurAttr)
155         setHTMLEventListener(blurEvent, attr);
156     else if (attr->name() == onselectAttr)
157         setHTMLEventListener(selectEvent, attr);
158     else if (attr->name() == onchangeAttr)
159         setHTMLEventListener(changeEvent, attr);
160     else
161         HTMLGenericFormElement::parseMappedAttribute(attr);
162 }
163
164 RenderObject* HTMLTextAreaElement::createRenderer(RenderArena* arena, RenderStyle* style)
165 {
166     return new (arena) RenderTextControl(this, true);
167 }
168
169 bool HTMLTextAreaElement::appendFormData(FormDataList& encoding, bool)
170 {
171     if (name().isEmpty())
172         return false;
173         
174     bool hardWrap = renderer() && wrap() == ta_Physical;
175     String v = hardWrap ? static_cast<RenderTextControl*>(renderer())->textWithHardLineBreaks() : value();
176     encoding.appendData(name(), v);
177     return true;
178 }
179
180 void HTMLTextAreaElement::reset()
181 {
182     setValue(defaultValue());
183 }
184
185 bool HTMLTextAreaElement::isKeyboardFocusable(KeyboardEvent*) const
186 {
187     // If text areas can be focused, then they should always be keyboard focusable
188     return HTMLGenericFormElement::isFocusable();
189 }
190
191 bool HTMLTextAreaElement::isMouseFocusable() const
192 {
193     return HTMLGenericFormElement::isFocusable();
194 }
195
196 void HTMLTextAreaElement::focus()
197 {
198     Document* doc = document();
199     if (doc->focusNode() == this)
200         return;
201     doc->updateLayout();
202     if (!supportsFocus())
203         return;
204     doc->setFocusNode(this);
205     // FIXME: Should isFocusable do the updateLayout?
206     if (!isFocusable()) {
207         setNeedsFocusAppearanceUpdate(true);
208         return;
209     }
210     updateFocusAppearance();
211 }
212
213 void HTMLTextAreaElement::updateFocusAppearance()
214 {
215     ASSERT(renderer());
216     
217     if (cachedSelStart == -1) {
218         ASSERT(cachedSelEnd == -1);
219         // If this is the first focus, set a caret at the end of the text.  
220         // This matches other browsers' behavior.
221         int max = static_cast<RenderTextControl*>(renderer())->text().length();
222         setSelectionRange(max, max);
223     } else
224         // Restore the cached selection.  This matches other browsers' behavior.
225         setSelectionRange(cachedSelStart, cachedSelEnd); 
226
227     if (document() && document()->frame())
228         document()->frame()->revealSelection();
229 }
230
231 void HTMLTextAreaElement::defaultEventHandler(Event *evt)
232 {
233     if (renderer() && (evt->isMouseEvent() || evt->isDragEvent() || evt->isWheelEvent() || evt->type() == blurEvent))
234         static_cast<RenderTextControl*>(renderer())->forwardEvent(evt);
235
236     HTMLGenericFormElement::defaultEventHandler(evt);
237 }
238
239 void HTMLTextAreaElement::rendererWillBeDestroyed()
240 {
241     updateValue();
242 }
243
244 void HTMLTextAreaElement::updateValue() const
245 {
246     if (!valueMatchesRenderer()) {
247         ASSERT(renderer());
248         m_value = static_cast<RenderTextControl*>(renderer())->text();
249         setValueMatchesRenderer();
250     }
251 }
252
253 String HTMLTextAreaElement::value() const
254 {
255     updateValue();
256     return m_value;
257 }
258
259 void HTMLTextAreaElement::setValue(const String& value)
260 {
261     // Code elsewhere normalizes line endings added by the user via the keyboard or pasting.
262     // We must normalize line endings coming from JS.
263     DeprecatedString valueWithNormalizedLineEndings = value.deprecatedString();
264     valueWithNormalizedLineEndings.replace("\r\n", "\n");
265     valueWithNormalizedLineEndings.replace("\r", "\n");
266     
267     m_value = valueWithNormalizedLineEndings;
268     setValueMatchesRenderer();
269     if (renderer())
270         renderer()->updateFromElement();
271         
272     // Restore a caret at the starting point of the old selection.
273     // This matches Safari 2.0 behavior.
274     if (document()->focusNode() == this && cachedSelStart >= 0) {
275         ASSERT(cachedSelEnd >= 0);
276         setSelectionRange(cachedSelStart, cachedSelStart);
277     }
278     setChanged(true);
279 }
280
281 String HTMLTextAreaElement::defaultValue() const
282 {
283     String val = "";
284
285     // Since there may be comments, ignore nodes other than text nodes.
286     for (Node* n = firstChild(); n; n = n->nextSibling())
287         if (n->isTextNode())
288             val += static_cast<Text*>(n)->data();
289
290     // FIXME: We should only drop the first carriage return for the default
291     // value in the original source, not defaultValues set from JS. This code
292     // will do both.
293     if (val.length() >= 2 && val[0] == '\r' && val[1] == '\n')
294         val.remove(0, 2);
295     else if (val.length() >= 1 && (val[0] == '\r' || val[0] == '\n'))
296         val.remove(0, 1);
297
298     return val;
299 }
300
301 void HTMLTextAreaElement::setDefaultValue(const String& defaultValue)
302 {
303     // To preserve comments, remove all the text nodes, then add a single one.
304     Vector<RefPtr<Node> > textNodes;
305     for (Node* n = firstChild(); n; n = n->nextSibling())
306         if (n->isTextNode())
307             textNodes.append(n);
308     ExceptionCode ec = 0;
309     size_t size = textNodes.size();
310     for (size_t i = 0; i < size; ++i)
311         removeChild(textNodes[i].get(), ec);
312     insertBefore(document()->createTextNode(defaultValue), firstChild(), ec);
313     setValue(defaultValue);
314 }
315
316 void HTMLTextAreaElement::accessKeyAction(bool sendToAnyElement)
317 {
318     focus();
319 }
320
321 String HTMLTextAreaElement::accessKey() const
322 {
323     return getAttribute(accesskeyAttr);
324 }
325
326 void HTMLTextAreaElement::setAccessKey(const String& value)
327 {
328     setAttribute(accesskeyAttr, value);
329 }
330
331 void HTMLTextAreaElement::setCols(int cols)
332 {
333     setAttribute(colsAttr, String::number(cols));
334 }
335
336 void HTMLTextAreaElement::setRows(int rows)
337 {
338     setAttribute(rowsAttr, String::number(rows));
339 }
340
341 } // namespace