WebCore:
[WebKit-https.git] / WebCore / html / HTMLFormElement.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 "HTMLFormElement.h"
29
30 #include "CString.h"
31 #include "Event.h"
32 #include "EventNames.h"
33 #include "FormData.h"
34 #include "FormDataList.h"
35 #include "Frame.h"
36 #include "FrameLoader.h"
37 #include "HTMLDocument.h"
38 #include "HTMLFormCollection.h"
39 #include "HTMLImageElement.h"
40 #include "HTMLInputElement.h"
41 #include "HTMLNames.h"
42 #include "csshelper.h"
43 #include "RenderLineEdit.h"
44
45 namespace WebCore {
46
47 using namespace EventNames;
48 using namespace HTMLNames;
49
50 HTMLFormElement::HTMLFormElement(Document* doc)
51     : HTMLElement(formTag, doc)
52 {
53     collectionInfo = 0;
54     m_post = false;
55     m_multipart = false;
56     m_autocomplete = true;
57     m_insubmit = false;
58     m_doingsubmit = false;
59     m_inreset = false;
60     m_enctype = "application/x-www-form-urlencoded";
61     m_boundary = "----------0xKhTmLbOuNdArY";
62     m_malformed = false;
63     m_preserveAcrossRemove = false;
64 }
65
66 HTMLFormElement::~HTMLFormElement()
67 {
68     delete collectionInfo;
69     
70     for (unsigned i = 0; i < formElements.size(); ++i)
71         formElements[i]->m_form = 0;
72     for (unsigned i = 0; i < imgElements.size(); ++i)
73         imgElements[i]->m_form = 0;
74 }
75
76 bool HTMLFormElement::formWouldHaveSecureSubmission(const String &url)
77 {
78     if (url.isNull()) {
79         return false;
80     }
81     return document()->completeURL(url.deprecatedString()).startsWith("https:", false);
82 }
83
84 void HTMLFormElement::attach()
85 {
86     HTMLElement::attach();
87
88     // note we don't deal with calling secureFormRemoved() on detach, because the timing
89     // was such that it cleared our state too early
90     if (formWouldHaveSecureSubmission(m_url))
91         document()->secureFormAdded();
92 }
93
94 void HTMLFormElement::insertedIntoDocument()
95 {
96     if (document()->isHTMLDocument()) {
97         HTMLDocument *doc = static_cast<HTMLDocument *>(document());
98         doc->addNamedItem(oldNameAttr);
99     }
100
101     HTMLElement::insertedIntoDocument();
102 }
103
104 void HTMLFormElement::removedFromDocument()
105 {
106     if (document()->isHTMLDocument()) {
107         HTMLDocument *doc = static_cast<HTMLDocument *>(document());
108         doc->removeNamedItem(oldNameAttr);
109     }
110    
111     HTMLElement::removedFromDocument();
112 }
113
114 unsigned HTMLFormElement::length() const
115 {
116     int len = 0;
117     for (unsigned i = 0; i < formElements.size(); ++i)
118         if (formElements[i]->isEnumeratable())
119             ++len;
120
121     return len;
122 }
123
124 Node* HTMLFormElement::item(unsigned index)
125 {
126     return elements()->item(index);
127 }
128
129 void HTMLFormElement::submitClick(Event* event)
130 {
131     bool submitFound = false;
132     for (unsigned i = 0; i < formElements.size(); ++i) {
133         if (formElements[i]->hasLocalName(inputTag)) {
134             HTMLInputElement *element = static_cast<HTMLInputElement *>(formElements[i]);
135             if (element->isSuccessfulSubmitButton() && element->renderer()) {
136                 submitFound = true;
137                 element->dispatchSimulatedClick(event);
138                 break;
139             }
140         }
141     }
142     if (!submitFound) // submit the form without a submit or image input
143         prepareSubmit(event);
144 }
145
146 static DeprecatedCString encodeCString(const CString& cstr)
147 {
148     DeprecatedCString e = cstr.deprecatedCString();
149
150     // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
151     // same safe characters as Netscape for compatibility
152     static const char *safe = "-._*";
153     int elen = e.length();
154     DeprecatedCString encoded((elen + e.contains('\n')) * 3 + 1);
155     int enclen = 0;
156
157     for (int pos = 0; pos < elen; pos++) {
158         unsigned char c = e[pos];
159
160         if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || strchr(safe, c))
161             encoded[enclen++] = c;
162         else if (c == ' ')
163             encoded[enclen++] = '+';
164         else if (c == '\n' || (c == '\r' && e[pos + 1] != '\n')) {
165             encoded[enclen++] = '%';
166             encoded[enclen++] = '0';
167             encoded[enclen++] = 'D';
168             encoded[enclen++] = '%';
169             encoded[enclen++] = '0';
170             encoded[enclen++] = 'A';
171         } else if (c != '\r') {
172             encoded[enclen++] = '%';
173             unsigned int h = c / 16;
174             h += (h > 9) ? ('A' - 10) : '0';
175             encoded[enclen++] = h;
176
177             unsigned int l = c % 16;
178             l += (l > 9) ? ('A' - 10) : '0';
179             encoded[enclen++] = l;
180         }
181     }
182     encoded[enclen++] = '\0';
183     encoded.truncate(enclen);
184
185     return encoded;
186 }
187
188 PassRefPtr<FormData> HTMLFormElement::formData() const
189 {
190     DeprecatedCString enc_string = ""; // used for non-multipart data
191
192     String str = m_acceptcharset;
193     str.replace(',', ' ');
194     Vector<String> charsets = str.split(' ');
195     TextEncoding encoding;
196     Frame* frame = document()->frame();
197     Vector<String>::const_iterator end = charsets.end();
198     for (Vector<String>::const_iterator it = charsets.begin(); it != end; ++it)
199         if ((encoding = TextEncoding(*it)).isValid())
200             break;
201     if (!encoding.isValid()) {
202         if (frame)
203             encoding = frame->loader()->encoding();
204         else
205             encoding = Latin1Encoding();
206     }
207
208     RefPtr<FormData> result = new FormData;
209     
210     for (unsigned i = 0; i < formElements.size(); ++i) {
211         HTMLGenericFormElement* current = formElements[i];
212         FormDataList lst(encoding);
213
214         if (!current->disabled() && current->appendFormData(lst, m_multipart)) {
215             size_t ln = lst.list().size();
216             for (size_t j = 0; j < ln; ++j) {
217                 const FormDataListItem& item = lst.list()[j];
218                 if (!m_multipart) {
219                     // handle ISINDEX / <input name=isindex> special
220                     // but only if its the first entry
221                     if (enc_string.isEmpty() && item.m_data == "isindex") {
222                         enc_string += encodeCString(lst.list()[j + 1].m_data);
223                         ++j;
224                     } else {
225                         if (!enc_string.isEmpty())
226                             enc_string += '&';
227
228                         enc_string += encodeCString(item.m_data);
229                         enc_string += "=";
230                         enc_string += encodeCString(lst.list()[j + 1].m_data);
231                         ++j;
232                     }
233                 }
234                 else
235                 {
236                     DeprecatedCString hstr("--");
237                     hstr += m_boundary.deprecatedString().latin1();
238                     hstr += "\r\n";
239                     hstr += "Content-Disposition: form-data; name=\"";
240                     hstr += item.m_data.data();
241                     hstr += "\"";
242
243                     // if the current type is FILE, then we also need to
244                     // include the filename
245                     if (current->hasLocalName(inputTag) &&
246                         static_cast<HTMLInputElement*>(current)->inputType() == HTMLInputElement::FILE) {
247                         String path = static_cast<HTMLInputElement*>(current)->value();
248
249                         // FIXME: This won't work if the filename includes a " mark,
250                         // or control characters like CR or LF. This also does strange
251                         // things if the filename includes characters you can't encode
252                         // in the website's character set.
253                         hstr += "; filename=\"";
254                         int start = path.reverseFind('/') + 1;
255                         int length = path.length() - start;
256                         hstr += encoding.encode(reinterpret_cast<const UChar*>(path.characters() + start), length, true);
257                         hstr += "\"";
258
259                         if (!static_cast<HTMLInputElement*>(current)->value().isEmpty()) {
260                             DeprecatedString mimeType = frame ? frame->mimeTypeForFileName(path).deprecatedString() : DeprecatedString();
261                             if (!mimeType.isEmpty()) {
262                                 hstr += "\r\nContent-Type: ";
263                                 hstr += mimeType.ascii();
264                             }
265                         }
266                     }
267
268                     hstr += "\r\n\r\n";
269
270                     // append body
271                     result->appendData(hstr.data(), hstr.length());
272                     const FormDataListItem& item = lst.list()[j + 1];
273                     if (size_t dataSize = item.m_data.length())
274                         result->appendData(item.m_data, dataSize);
275                     else if (!item.m_path.isEmpty())
276                         result->appendFile(item.m_path);
277                     result->appendData("\r\n", 2);
278
279                     ++j;
280                 }
281             }
282         }
283     }
284
285
286     if (m_multipart)
287         enc_string = ("--" + m_boundary.deprecatedString() + "--\r\n").ascii();
288
289     result->appendData(enc_string.data(), enc_string.length());
290     return result;
291 }
292
293 void HTMLFormElement::parseEnctype(const String& type)
294 {
295     if(type.contains("multipart", false) || type.contains("form-data", false)) {
296         m_enctype = "multipart/form-data";
297         m_multipart = true;
298     } else if (type.contains("text", false) || type.contains("plain", false)) {
299         m_enctype = "text/plain";
300         m_multipart = false;
301     } else {
302         m_enctype = "application/x-www-form-urlencoded";
303         m_multipart = false;
304     }
305 }
306
307 void HTMLFormElement::setBoundary( const String& bound )
308 {
309     m_boundary = bound;
310 }
311
312 bool HTMLFormElement::prepareSubmit(Event* event)
313 {
314     Frame* frame = document()->frame();
315     if (m_insubmit || !frame)
316         return m_insubmit;
317
318     m_insubmit = true;
319     m_doingsubmit = false;
320
321     if (dispatchHTMLEvent(submitEvent, true, true) && !m_doingsubmit)
322         m_doingsubmit = true;
323
324     m_insubmit = false;
325
326     if (m_doingsubmit)
327         submit(event, true);
328
329     return m_doingsubmit;
330 }
331
332 void HTMLFormElement::submit()
333 {
334     submit(0, false);
335 }
336
337 void HTMLFormElement::submit(Event* event, bool activateSubmitButton)
338 {
339     FrameView *view = document()->view();
340     Frame *frame = document()->frame();
341     if (!view || !frame)
342         return;
343
344     if (m_insubmit) {
345         m_doingsubmit = true;
346         return;
347     }
348
349     m_insubmit = true;
350
351     HTMLGenericFormElement* firstSuccessfulSubmitButton = 0;
352     bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button?
353     
354     frame->loader()->clearRecordedFormValues();
355     for (unsigned i = 0; i < formElements.size(); ++i) {
356         HTMLGenericFormElement* current = formElements[i];
357         if (current->hasLocalName(inputTag)) {
358             HTMLInputElement* input = static_cast<HTMLInputElement*>(current);
359             if (input->isTextField()) {
360                 frame->loader()->recordFormValue(input->name(), input->value(), this);
361                 if (input->renderer() && input->inputType() == HTMLInputElement::SEARCH)
362                     static_cast<RenderLineEdit*>(input->renderer())->addSearchResult();
363             }
364         }
365         if (needButtonActivation) {
366             if (current->isActivatedSubmit())
367                 needButtonActivation = false;
368             else if (firstSuccessfulSubmitButton == 0 && current->isSuccessfulSubmitButton())
369                 firstSuccessfulSubmitButton = current;
370         }
371     }
372
373     if (needButtonActivation && firstSuccessfulSubmitButton)
374         firstSuccessfulSubmitButton->setActivatedSubmit(true);
375
376     if (!m_post)
377         m_multipart = false;
378     
379     if (m_post)
380         frame->loader()->submitForm("POST", m_url, formData(), m_target, enctype(), boundary(), event);
381     else
382         frame->loader()->submitForm("GET", m_url, formData(), m_target, String(), String(), event);
383
384     if (needButtonActivation && firstSuccessfulSubmitButton)
385         firstSuccessfulSubmitButton->setActivatedSubmit(false);
386     
387     m_doingsubmit = m_insubmit = false;
388 }
389
390 void HTMLFormElement::reset()
391 {
392     Frame *frame = document()->frame();
393     if (m_inreset || !frame)
394         return;
395
396     m_inreset = true;
397
398     // ### DOM2 labels this event as not cancelable, however
399     // common browsers( sick! ) allow it be cancelled.
400     if ( !dispatchHTMLEvent(resetEvent,true, true) ) {
401         m_inreset = false;
402         return;
403     }
404
405     for (unsigned i = 0; i < formElements.size(); ++i)
406         formElements[i]->reset();
407
408     m_inreset = false;
409 }
410
411 void HTMLFormElement::parseMappedAttribute(MappedAttribute *attr)
412 {
413     if (attr->name() == actionAttr) {
414         bool oldURLWasSecure = formWouldHaveSecureSubmission(m_url);
415         m_url = parseURL(attr->value());
416         bool newURLIsSecure = formWouldHaveSecureSubmission(m_url);
417
418         if (m_attached && (oldURLWasSecure != newURLIsSecure))
419             if (newURLIsSecure)
420                 document()->secureFormAdded();
421             else
422                 document()->secureFormRemoved();
423     }
424     else if (attr->name() == targetAttr)
425         m_target = attr->value();
426     else if (attr->name() == methodAttr) {
427         if (equalIgnoringCase(attr->value(), "post"))
428             m_post = true;
429         else if (equalIgnoringCase(attr->value(), "get"))
430             m_post = false;
431     } else if (attr->name() == enctypeAttr)
432         parseEnctype(attr->value());
433     else if (attr->name() == accept_charsetAttr)
434         // space separated list of charsets the server
435         // accepts - see rfc2045
436         m_acceptcharset = attr->value();
437     else if (attr->name() == acceptAttr) {
438         // ignore this one for the moment...
439     } else if (attr->name() == autocompleteAttr)
440         m_autocomplete = !equalIgnoringCase(attr->value(), "off");
441     else if (attr->name() == onsubmitAttr)
442         setHTMLEventListener(submitEvent, attr);
443     else if (attr->name() == onresetAttr)
444         setHTMLEventListener(resetEvent, attr);
445     else if (attr->name() == nameAttr) {
446         String newNameAttr = attr->value();
447         if (inDocument() && document()->isHTMLDocument()) {
448             HTMLDocument *doc = static_cast<HTMLDocument *>(document());
449             doc->removeNamedItem(oldNameAttr);
450             doc->addNamedItem(newNameAttr);
451         }
452         oldNameAttr = newNameAttr;
453     } else
454         HTMLElement::parseMappedAttribute(attr);
455 }
456
457 template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T* item)
458 {
459     size_t size = vec.size();
460     for (size_t i = 0; i != size; ++i)
461         if (vec[i] == item) {
462             vec.remove(i);
463             break;
464         }
465 }
466
467 unsigned HTMLFormElement::formElementIndex(HTMLGenericFormElement *e)
468 {
469     // Check for the special case where this element is the very last thing in
470     // the form's tree of children; we don't want to walk the entire tree in that
471     // common case that occurs during parsing; instead we'll just return a value
472     // that says "add this form element to the end of the array".
473     if (e->traverseNextNode(this)) {
474         unsigned i = 0;
475         for (Node *node = this; node; node = node->traverseNextNode(this)) {
476             if (node == e)
477                 return i;
478             if (node->isHTMLElement()
479                     && static_cast<HTMLElement *>(node)->isGenericFormElement()
480                     && static_cast<HTMLGenericFormElement *>(node)->form() == this)
481                 ++i;
482         }
483     }
484     return formElements.size();
485 }
486
487 void HTMLFormElement::registerFormElement(HTMLGenericFormElement* e)
488 {
489     Document* doc = document();
490     if (e->isRadioButton() && !e->name().isEmpty()) {
491         HTMLGenericFormElement* currentCheckedRadio = doc->checkedRadioButtonForGroup(e->name().impl(), 0);
492         if (currentCheckedRadio == e)
493             doc->removeRadioButtonGroup(e->name().impl(), 0);
494         if (e->isChecked())
495             doc->radioButtonChecked(static_cast<HTMLInputElement*>(e), this);
496     }
497     formElements.insert(formElementIndex(e), e);
498 }
499
500 void HTMLFormElement::removeFormElement(HTMLGenericFormElement* e)
501 {
502     if (!e->name().isEmpty()) {
503         HTMLGenericFormElement* currentCheckedRadio = document()->checkedRadioButtonForGroup(e->name().impl(), this);
504         if (currentCheckedRadio == e)
505             document()->removeRadioButtonGroup(e->name().impl(), this);
506     }
507     removeFromVector(formElements, e);
508 }
509
510 bool HTMLFormElement::isURLAttribute(Attribute *attr) const
511 {
512     return attr->name() == actionAttr;
513 }
514
515 void HTMLFormElement::registerImgElement(HTMLImageElement *e)
516 {
517     imgElements.append(e);
518 }
519
520 void HTMLFormElement::removeImgElement(HTMLImageElement *e)
521 {
522     removeFromVector(imgElements, e);
523 }
524
525 PassRefPtr<HTMLCollection> HTMLFormElement::elements()
526 {
527     return new HTMLFormCollection(this);
528 }
529
530 String HTMLFormElement::name() const
531 {
532     return getAttribute(nameAttr);
533 }
534
535 void HTMLFormElement::setName(const String &value)
536 {
537     setAttribute(nameAttr, value);
538 }
539
540 String HTMLFormElement::acceptCharset() const
541 {
542     return getAttribute(accept_charsetAttr);
543 }
544
545 void HTMLFormElement::setAcceptCharset(const String &value)
546 {
547     setAttribute(accept_charsetAttr, value);
548 }
549
550 String HTMLFormElement::action() const
551 {
552     return getAttribute(actionAttr);
553 }
554
555 void HTMLFormElement::setAction(const String &value)
556 {
557     setAttribute(actionAttr, value);
558 }
559
560 void HTMLFormElement::setEnctype(const String &value)
561 {
562     setAttribute(enctypeAttr, value);
563 }
564
565 String HTMLFormElement::method() const
566 {
567     return getAttribute(methodAttr);
568 }
569
570 void HTMLFormElement::setMethod(const String &value)
571 {
572     setAttribute(methodAttr, value);
573 }
574
575 String HTMLFormElement::target() const
576 {
577     return getAttribute(targetAttr);
578 }
579
580 void HTMLFormElement::setTarget(const String &value)
581 {
582     setAttribute(targetAttr, value);
583 }
584     
585 } // namespace