7396cf4db3015e33c4d2d80d7aa22ecdab7a308e
[WebKit-https.git] / WebCore / html / HTMLFormElement.cpp
1 /*
2  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
4  *           (C) 2001 Dirk Mueller (mueller@kde.org)
5  * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
6  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "HTMLFormElement.h"
27
28 #include "Base64.h"
29 #include "CString.h"
30 #include "csshelper.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 "MimeTypeRegistry.h"
43 #include "RenderTextControl.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_malformed = false;
62     m_preserveAcrossRemove = false;
63 }
64
65 HTMLFormElement::~HTMLFormElement()
66 {
67     delete collectionInfo;
68     
69     for (unsigned i = 0; i < formElements.size(); ++i)
70         formElements[i]->formDestroyed();
71     for (unsigned i = 0; i < imgElements.size(); ++i)
72         imgElements[i]->m_form = 0;
73 }
74
75 bool HTMLFormElement::formWouldHaveSecureSubmission(const String &url)
76 {
77     if (url.isNull()) {
78         return false;
79     }
80     return document()->completeURL(url.deprecatedString()).startsWith("https:", false);
81 }
82
83 void HTMLFormElement::attach()
84 {
85     HTMLElement::attach();
86
87     // note we don't deal with calling secureFormRemoved() on detach, because the timing
88     // was such that it cleared our state too early
89     if (formWouldHaveSecureSubmission(m_url))
90         document()->secureFormAdded();
91 }
92
93 void HTMLFormElement::insertedIntoDocument()
94 {
95     if (document()->isHTMLDocument()) {
96         HTMLDocument *doc = static_cast<HTMLDocument *>(document());
97         doc->addNamedItem(oldNameAttr);
98     }
99
100     HTMLElement::insertedIntoDocument();
101 }
102
103 void HTMLFormElement::removedFromDocument()
104 {
105     if (document()->isHTMLDocument()) {
106         HTMLDocument *doc = static_cast<HTMLDocument *>(document());
107         doc->removeNamedItem(oldNameAttr);
108     }
109    
110     HTMLElement::removedFromDocument();
111 }
112
113 void HTMLFormElement::handleLocalEvents(Event* event, bool useCapture)
114 {
115     EventTargetNode* targetNode = event->target()->toNode();
116     if (!useCapture && targetNode && targetNode != this && (event->type() == submitEvent || event->type() == resetEvent)) {
117         event->stopPropagation();
118         return;
119     }
120     HTMLElement::handleLocalEvents(event, useCapture);
121 }
122
123 unsigned HTMLFormElement::length() const
124 {
125     int len = 0;
126     for (unsigned i = 0; i < formElements.size(); ++i)
127         if (formElements[i]->isEnumeratable())
128             ++len;
129
130     return len;
131 }
132
133 Node* HTMLFormElement::item(unsigned index)
134 {
135     return elements()->item(index);
136 }
137
138 void HTMLFormElement::submitClick(Event* event)
139 {
140     bool submitFound = false;
141     for (unsigned i = 0; i < formElements.size(); ++i) {
142         if (formElements[i]->hasLocalName(inputTag)) {
143             HTMLInputElement *element = static_cast<HTMLInputElement *>(formElements[i]);
144             if (element->isSuccessfulSubmitButton() && element->renderer()) {
145                 submitFound = true;
146                 element->dispatchSimulatedClick(event);
147                 break;
148             }
149         }
150     }
151     if (!submitFound) // submit the form without a submit or image input
152         prepareSubmit(event);
153 }
154
155 static DeprecatedCString encodeCString(const CString& cstr)
156 {
157     DeprecatedCString e = cstr.deprecatedCString();
158
159     // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
160     // same safe characters as Netscape for compatibility
161     static const char *safe = "-._*";
162     int elen = e.length();
163     DeprecatedCString encoded((elen + e.contains('\n')) * 3 + 1);
164     int enclen = 0;
165
166     for (int pos = 0; pos < elen; pos++) {
167         unsigned char c = e[pos];
168
169         if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || strchr(safe, c))
170             encoded[enclen++] = c;
171         else if (c == ' ')
172             encoded[enclen++] = '+';
173         else if (c == '\n' || (c == '\r' && e[pos + 1] != '\n')) {
174             encoded[enclen++] = '%';
175             encoded[enclen++] = '0';
176             encoded[enclen++] = 'D';
177             encoded[enclen++] = '%';
178             encoded[enclen++] = '0';
179             encoded[enclen++] = 'A';
180         } else if (c != '\r') {
181             encoded[enclen++] = '%';
182             unsigned int h = c / 16;
183             h += (h > 9) ? ('A' - 10) : '0';
184             encoded[enclen++] = h;
185
186             unsigned int l = c % 16;
187             l += (l > 9) ? ('A' - 10) : '0';
188             encoded[enclen++] = l;
189         }
190     }
191     encoded[enclen++] = '\0';
192     encoded.truncate(enclen);
193
194     return encoded;
195 }
196
197 static int randomNumber()
198 {
199     static bool randomSeeded = false;
200
201 #if PLATFORM(DARWIN)
202     if (!randomSeeded) {
203         srandomdev();
204         randomSeeded = true;
205     }
206     return random();
207 #else
208     if (!randomSeeded) {
209         srand(static_cast<unsigned>(time(0)));
210         randomSeeded = true;
211     }
212     return rand();
213 #endif
214 }
215
216 PassRefPtr<FormData> HTMLFormElement::formData(const char* boundary) const
217 {
218     DeprecatedCString enc_string = "";
219
220     String str = m_acceptcharset;
221     str.replace(',', ' ');
222     Vector<String> charsets = str.split(' ');
223     TextEncoding encoding;
224     Frame* frame = document()->frame();
225     Vector<String>::const_iterator end = charsets.end();
226     for (Vector<String>::const_iterator it = charsets.begin(); it != end; ++it)
227         if ((encoding = TextEncoding(*it)).isValid())
228             break;
229     if (!encoding.isValid()) {
230         if (frame)
231             encoding = frame->loader()->encoding();
232         else
233             encoding = Latin1Encoding();
234     }
235
236     RefPtr<FormData> result = new FormData;
237     
238     for (unsigned i = 0; i < formElements.size(); ++i) {
239         HTMLGenericFormElement* current = formElements[i];
240         FormDataList lst(encoding);
241
242         if (!current->disabled() && current->appendFormData(lst, m_multipart)) {
243             size_t ln = lst.list().size();
244             for (size_t j = 0; j < ln; ++j) {
245                 const FormDataListItem& item = lst.list()[j];
246                 if (!m_multipart) {
247                     // handle ISINDEX / <input name=isindex> special
248                     // but only if its the first entry
249                     if (enc_string.isEmpty() && item.m_data == "isindex") {
250                         enc_string += encodeCString(lst.list()[j + 1].m_data);
251                         ++j;
252                     } else {
253                         if (!enc_string.isEmpty())
254                             enc_string += '&';
255
256                         enc_string += encodeCString(item.m_data);
257                         enc_string += "=";
258                         enc_string += encodeCString(lst.list()[j + 1].m_data);
259                         ++j;
260                     }
261                 }
262                 else
263                 {
264                     DeprecatedCString hstr("--");
265                     hstr += boundary;
266                     hstr += "\r\n";
267                     hstr += "Content-Disposition: form-data; name=\"";
268                     hstr += item.m_data.data();
269                     hstr += "\"";
270
271                     // if the current type is FILE, then we also need to
272                     // include the filename
273                     if (current->hasLocalName(inputTag) &&
274                         static_cast<HTMLInputElement*>(current)->inputType() == HTMLInputElement::FILE) {
275                         String path = static_cast<HTMLInputElement*>(current)->value();
276
277                         // FIXME: This won't work if the filename includes a " mark,
278                         // or control characters like CR or LF. This also does strange
279                         // things if the filename includes characters you can't encode
280                         // in the website's character set.
281                         hstr += "; filename=\"";
282                         int start = path.reverseFind('/') + 1;
283                         int length = path.length() - start;
284                         hstr += encoding.encode(reinterpret_cast<const UChar*>(path.characters() + start), length, true);
285                         hstr += "\"";
286
287                         if (!static_cast<HTMLInputElement*>(current)->value().isEmpty()) {
288                             DeprecatedString mimeType = MimeTypeRegistry::getMIMETypeForPath(path).deprecatedString();
289                             if (!mimeType.isEmpty()) {
290                                 hstr += "\r\nContent-Type: ";
291                                 hstr += mimeType.ascii();
292                             }
293                         }
294                     }
295
296                     hstr += "\r\n\r\n";
297
298                     // append body
299                     result->appendData(hstr.data(), hstr.length());
300                     const FormDataListItem& item = lst.list()[j + 1];
301                     if (size_t dataSize = item.m_data.length())
302                         result->appendData(item.m_data, dataSize);
303                     else if (!item.m_path.isEmpty())
304                         result->appendFile(item.m_path);
305                     result->appendData("\r\n", 2);
306
307                     ++j;
308                 }
309             }
310         }
311     }
312
313
314     if (m_multipart) {
315         enc_string = "--";
316         enc_string += boundary;
317         enc_string += "--\r\n";
318     }
319
320     result->appendData(enc_string.data(), enc_string.length());
321     return result;
322 }
323
324 void HTMLFormElement::parseEnctype(const String& type)
325 {
326     if(type.contains("multipart", false) || type.contains("form-data", false)) {
327         m_enctype = "multipart/form-data";
328         m_multipart = true;
329     } else if (type.contains("text", false) || type.contains("plain", false)) {
330         m_enctype = "text/plain";
331         m_multipart = false;
332     } else {
333         m_enctype = "application/x-www-form-urlencoded";
334         m_multipart = false;
335     }
336 }
337
338 bool HTMLFormElement::prepareSubmit(Event* event)
339 {
340     Frame* frame = document()->frame();
341     if (m_insubmit || !frame)
342         return m_insubmit;
343
344     m_insubmit = true;
345     m_doingsubmit = false;
346
347     if (dispatchHTMLEvent(submitEvent, true, true) && !m_doingsubmit)
348         m_doingsubmit = true;
349
350     m_insubmit = false;
351
352     if (m_doingsubmit)
353         submit(event, true);
354
355     return m_doingsubmit;
356 }
357
358 void HTMLFormElement::submit()
359 {
360     submit(0, false);
361 }
362
363 // Returns a 0-terminated C string in the vector.
364 static void getUniqueBoundaryString(Vector<char>& boundary)
365 {
366     // Start with an informative prefix.
367     const char boundaryPrefix[] = "----WebKitFormBoundary";
368     boundary.append(boundaryPrefix, strlen(boundaryPrefix));
369
370     // Append 16 characters of base 64 randomness.
371     Vector<char> randomBytes;
372     for (int i = 0; i < 3; ++i) {
373         int randomness = randomNumber();
374         randomBytes.append(static_cast<char>(randomness >> 24));
375         randomBytes.append(static_cast<char>(randomness >> 16));
376         randomBytes.append(static_cast<char>(randomness >> 8));
377         randomBytes.append(static_cast<char>(randomness));
378     }
379     Vector<char> randomBase64;
380     base64Encode(randomBytes, randomBase64);
381     boundary.append(randomBase64);
382
383     // Add a 0 at the end so we can use this as a C-style string.
384     boundary.append(0);
385 }
386
387 void HTMLFormElement::submit(Event* event, bool activateSubmitButton)
388 {
389     FrameView *view = document()->view();
390     Frame *frame = document()->frame();
391     if (!view || !frame)
392         return;
393
394     if (m_insubmit) {
395         m_doingsubmit = true;
396         return;
397     }
398
399     m_insubmit = true;
400
401     HTMLGenericFormElement* firstSuccessfulSubmitButton = 0;
402     bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button?
403     
404     frame->loader()->clearRecordedFormValues();
405     for (unsigned i = 0; i < formElements.size(); ++i) {
406         HTMLGenericFormElement* current = formElements[i];
407         if (current->hasLocalName(inputTag)) {
408             HTMLInputElement* input = static_cast<HTMLInputElement*>(current);
409             if (input->isTextField()) {
410                 frame->loader()->recordFormValue(input->name(), input->value(), this);
411                 if (input->isSearchField())
412                     input->addSearchResult();
413             }
414         }
415         if (needButtonActivation) {
416             if (current->isActivatedSubmit())
417                 needButtonActivation = false;
418             else if (firstSuccessfulSubmitButton == 0 && current->isSuccessfulSubmitButton())
419                 firstSuccessfulSubmitButton = current;
420         }
421     }
422
423     if (needButtonActivation && firstSuccessfulSubmitButton)
424         firstSuccessfulSubmitButton->setActivatedSubmit(true);
425
426     if (m_post) {
427         if (!m_multipart)
428             frame->loader()->submitForm("POST", m_url, formData(0), m_target, enctype(), String(), event);
429         else {
430             Vector<char> boundary;
431             getUniqueBoundaryString(boundary);
432             frame->loader()->submitForm("POST", m_url, formData(boundary.data()), m_target, enctype(), boundary.data(), event);
433         }
434     } else {
435         m_multipart = false;
436         frame->loader()->submitForm("GET", m_url, formData(0), m_target, String(), String(), event);
437     }
438
439     if (needButtonActivation && firstSuccessfulSubmitButton)
440         firstSuccessfulSubmitButton->setActivatedSubmit(false);
441     
442     m_doingsubmit = m_insubmit = false;
443 }
444
445 void HTMLFormElement::reset()
446 {
447     Frame *frame = document()->frame();
448     if (m_inreset || !frame)
449         return;
450
451     m_inreset = true;
452
453     // ### DOM2 labels this event as not cancelable, however
454     // common browsers( sick! ) allow it be cancelled.
455     if ( !dispatchHTMLEvent(resetEvent,true, true) ) {
456         m_inreset = false;
457         return;
458     }
459
460     for (unsigned i = 0; i < formElements.size(); ++i)
461         formElements[i]->reset();
462
463     m_inreset = false;
464 }
465
466 void HTMLFormElement::parseMappedAttribute(MappedAttribute *attr)
467 {
468     if (attr->name() == actionAttr) {
469         bool oldURLWasSecure = formWouldHaveSecureSubmission(m_url);
470         m_url = parseURL(attr->value());
471         bool newURLIsSecure = formWouldHaveSecureSubmission(m_url);
472
473         if (m_attached && (oldURLWasSecure != newURLIsSecure))
474             if (newURLIsSecure)
475                 document()->secureFormAdded();
476             else
477                 document()->secureFormRemoved();
478     }
479     else if (attr->name() == targetAttr)
480         m_target = attr->value();
481     else if (attr->name() == methodAttr) {
482         if (equalIgnoringCase(attr->value(), "post"))
483             m_post = true;
484         else if (equalIgnoringCase(attr->value(), "get"))
485             m_post = false;
486     } else if (attr->name() == enctypeAttr)
487         parseEnctype(attr->value());
488     else if (attr->name() == accept_charsetAttr)
489         // space separated list of charsets the server
490         // accepts - see rfc2045
491         m_acceptcharset = attr->value();
492     else if (attr->name() == acceptAttr) {
493         // ignore this one for the moment...
494     } else if (attr->name() == autocompleteAttr)
495         m_autocomplete = !equalIgnoringCase(attr->value(), "off");
496     else if (attr->name() == onsubmitAttr)
497         setHTMLEventListener(submitEvent, attr);
498     else if (attr->name() == onresetAttr)
499         setHTMLEventListener(resetEvent, attr);
500     else if (attr->name() == nameAttr) {
501         String newNameAttr = attr->value();
502         if (inDocument() && document()->isHTMLDocument()) {
503             HTMLDocument *doc = static_cast<HTMLDocument *>(document());
504             doc->removeNamedItem(oldNameAttr);
505             doc->addNamedItem(newNameAttr);
506         }
507         oldNameAttr = newNameAttr;
508     } else
509         HTMLElement::parseMappedAttribute(attr);
510 }
511
512 template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T* item)
513 {
514     size_t size = vec.size();
515     for (size_t i = 0; i != size; ++i)
516         if (vec[i] == item) {
517             vec.remove(i);
518             break;
519         }
520 }
521
522 unsigned HTMLFormElement::formElementIndex(HTMLGenericFormElement *e)
523 {
524     // Check for the special case where this element is the very last thing in
525     // the form's tree of children; we don't want to walk the entire tree in that
526     // common case that occurs during parsing; instead we'll just return a value
527     // that says "add this form element to the end of the array".
528     if (e->traverseNextNode(this)) {
529         unsigned i = 0;
530         for (Node *node = this; node; node = node->traverseNextNode(this)) {
531             if (node == e)
532                 return i;
533             if (node->isHTMLElement()
534                     && static_cast<HTMLElement *>(node)->isGenericFormElement()
535                     && static_cast<HTMLGenericFormElement *>(node)->form() == this)
536                 ++i;
537         }
538     }
539     return formElements.size();
540 }
541
542 void HTMLFormElement::registerFormElement(HTMLGenericFormElement* e)
543 {
544     Document* doc = document();
545     if (e->isRadioButton() && !e->name().isEmpty()) {
546         HTMLGenericFormElement* currentCheckedRadio = doc->checkedRadioButtonForGroup(e->name().impl(), 0);
547         if (currentCheckedRadio == e)
548             doc->removeRadioButtonGroup(e->name().impl(), 0);
549         if (e->isChecked())
550             doc->radioButtonChecked(static_cast<HTMLInputElement*>(e), this);
551     }
552     formElements.insert(formElementIndex(e), e);
553     doc->incDOMTreeVersion();
554 }
555
556 void HTMLFormElement::removeFormElement(HTMLGenericFormElement* e)
557 {
558     if (!e->name().isEmpty()) {
559         HTMLGenericFormElement* currentCheckedRadio = document()->checkedRadioButtonForGroup(e->name().impl(), this);
560         if (currentCheckedRadio == e)
561             document()->removeRadioButtonGroup(e->name().impl(), this);
562     }
563     removeFromVector(formElements, e);
564     document()->incDOMTreeVersion();
565 }
566
567 bool HTMLFormElement::isURLAttribute(Attribute *attr) const
568 {
569     return attr->name() == actionAttr;
570 }
571
572 void HTMLFormElement::registerImgElement(HTMLImageElement *e)
573 {
574     imgElements.append(e);
575 }
576
577 void HTMLFormElement::removeImgElement(HTMLImageElement *e)
578 {
579     removeFromVector(imgElements, e);
580 }
581
582 PassRefPtr<HTMLCollection> HTMLFormElement::elements()
583 {
584     return new HTMLFormCollection(this);
585 }
586
587 String HTMLFormElement::name() const
588 {
589     return getAttribute(nameAttr);
590 }
591
592 void HTMLFormElement::setName(const String &value)
593 {
594     setAttribute(nameAttr, value);
595 }
596
597 String HTMLFormElement::acceptCharset() const
598 {
599     return getAttribute(accept_charsetAttr);
600 }
601
602 void HTMLFormElement::setAcceptCharset(const String &value)
603 {
604     setAttribute(accept_charsetAttr, value);
605 }
606
607 String HTMLFormElement::action() const
608 {
609     return getAttribute(actionAttr);
610 }
611
612 void HTMLFormElement::setAction(const String &value)
613 {
614     setAttribute(actionAttr, value);
615 }
616
617 void HTMLFormElement::setEnctype(const String &value)
618 {
619     setAttribute(enctypeAttr, value);
620 }
621
622 String HTMLFormElement::method() const
623 {
624     return getAttribute(methodAttr);
625 }
626
627 void HTMLFormElement::setMethod(const String &value)
628 {
629     setAttribute(methodAttr, value);
630 }
631
632 String HTMLFormElement::target() const
633 {
634     return getAttribute(targetAttr);
635 }
636
637 void HTMLFormElement::setTarget(const String &value)
638 {
639     setAttribute(targetAttr, value);
640 }
641     
642 } // namespace