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