2009-03-27 Darin Adler <darin@apple.com>
[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, 2008 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., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "HTMLFormElement.h"
27
28 #include "CSSHelper.h"
29 #include "ChromeClient.h"
30 #include "Document.h"
31 #include "Event.h"
32 #include "EventNames.h"
33 #include "FileList.h"
34 #include "FileSystem.h"
35 #include "FormData.h"
36 #include "FormDataList.h"
37 #include "Frame.h"
38 #include "FrameLoader.h"
39 #include "HTMLDocument.h"
40 #include "HTMLFormCollection.h"
41 #include "HTMLImageElement.h"
42 #include "HTMLInputElement.h"
43 #include "HTMLNames.h"
44 #include "MIMETypeRegistry.h"
45 #include "Page.h"
46 #include "RenderTextControl.h"
47 #include <wtf/CurrentTime.h>
48 #include <wtf/RandomNumber.h>
49
50 #include <limits>
51
52 #if PLATFORM(WX)
53 #include <wx/defs.h>
54 #include <wx/filename.h>
55 #endif
56
57 #if PLATFORM(WIN_OS)
58 #include <shlwapi.h>
59 #endif
60
61 namespace WebCore {
62
63 using namespace HTMLNames;
64
65 static int64_t generateFormDataIdentifier()
66 {
67     // Initialize to the current time to reduce the likelihood of generating
68     // identifiers that overlap with those from past/future browser sessions.
69     static int64_t nextIdentifier = static_cast<int64_t>(currentTime() * 1000000.0);
70     return ++nextIdentifier;
71 }
72
73 HTMLFormElement::HTMLFormElement(const QualifiedName& tagName, Document* doc)
74     : HTMLElement(tagName, doc)
75     , m_elementAliases(0)
76     , collectionInfo(0)
77     , m_autocomplete(true)
78     , m_insubmit(false)
79     , m_doingsubmit(false)
80     , m_inreset(false)
81     , m_malformed(false)
82 {
83     ASSERT(hasTagName(formTag));
84 }
85
86 HTMLFormElement::~HTMLFormElement()
87 {
88     if (!m_autocomplete)
89         document()->unregisterForDocumentActivationCallbacks(this);
90
91     delete m_elementAliases;
92     delete collectionInfo;
93
94     for (unsigned i = 0; i < formElements.size(); ++i)
95         formElements[i]->formDestroyed();
96     for (unsigned i = 0; i < imgElements.size(); ++i)
97         imgElements[i]->m_form = 0;
98 }
99
100 bool HTMLFormElement::formWouldHaveSecureSubmission(const String& url)
101 {
102     return document()->completeURL(url).protocolIs("https");
103 }
104
105 void HTMLFormElement::attach()
106 {
107     HTMLElement::attach();
108 }
109
110 void HTMLFormElement::insertedIntoDocument()
111 {
112     if (document()->isHTMLDocument())
113         static_cast<HTMLDocument*>(document())->addNamedItem(m_name);
114
115     HTMLElement::insertedIntoDocument();
116 }
117
118 void HTMLFormElement::removedFromDocument()
119 {
120     if (document()->isHTMLDocument())
121         static_cast<HTMLDocument*>(document())->removeNamedItem(m_name);
122    
123     HTMLElement::removedFromDocument();
124 }
125
126 void HTMLFormElement::handleLocalEvents(Event* event, bool useCapture)
127 {
128     Node* targetNode = event->target()->toNode();
129     if (!useCapture && targetNode && targetNode != this && (event->type() == eventNames().submitEvent || event->type() == eventNames().resetEvent)) {
130         event->stopPropagation();
131         return;
132     }
133     HTMLElement::handleLocalEvents(event, useCapture);
134 }
135
136 unsigned HTMLFormElement::length() const
137 {
138     int len = 0;
139     for (unsigned i = 0; i < formElements.size(); ++i)
140         if (formElements[i]->isEnumeratable())
141             ++len;
142
143     return len;
144 }
145
146 Node* HTMLFormElement::item(unsigned index)
147 {
148     return elements()->item(index);
149 }
150
151 void HTMLFormElement::submitClick(Event* event)
152 {
153     bool submitFound = false;
154     for (unsigned i = 0; i < formElements.size(); ++i) {
155         if (formElements[i]->hasLocalName(inputTag)) {
156             HTMLInputElement* element = static_cast<HTMLInputElement*>(formElements[i]);
157             if (element->isSuccessfulSubmitButton() && element->renderer()) {
158                 submitFound = true;
159                 element->dispatchSimulatedClick(event);
160                 break;
161             }
162         }
163     }
164     if (!submitFound) // submit the form without a submit or image input
165         prepareSubmit(event);
166 }
167
168 TextEncoding HTMLFormElement::dataEncoding() const
169 {
170     if (isMailtoForm())
171         return UTF8Encoding();
172
173     return m_formDataBuilder.dataEncoding(document());
174 }
175
176 PassRefPtr<FormData> HTMLFormElement::createFormData(const CString& boundary)
177 {
178     Vector<char> encodedData;
179     TextEncoding encoding = dataEncoding().encodingForFormSubmission();
180
181     RefPtr<FormData> result = FormData::create();
182
183     for (unsigned i = 0; i < formElements.size(); ++i) {
184         HTMLFormControlElement* control = formElements[i];
185         FormDataList list(encoding);
186
187         if (!control->disabled() && control->appendFormData(list, m_formDataBuilder.isMultiPartForm())) {
188             size_t formDataListSize = list.list().size();
189             ASSERT(formDataListSize % 2 == 0);
190             for (size_t j = 0; j < formDataListSize; j += 2) {
191                 const FormDataList::Item& key = list.list()[j];
192                 const FormDataList::Item& value = list.list()[j + 1];
193                 if (!m_formDataBuilder.isMultiPartForm()) {
194                     // Omit the name "isindex" if it's the first form data element.
195                     // FIXME: Why is this a good rule? Is this obsolete now?
196                     if (encodedData.isEmpty() && key.data() == "isindex")
197                         FormDataBuilder::encodeStringAsFormData(encodedData, value.data());
198                     else
199                         m_formDataBuilder.addKeyValuePairAsFormData(encodedData, key.data(), value.data());
200                 } else {
201                     Vector<char> header;
202                     m_formDataBuilder.beginMultiPartHeader(header, boundary, key.data());
203
204                     bool shouldGenerateFile = false;
205                     // if the current type is FILE, then we also need to include the filename
206                     if (value.file()) {
207                         const String& path = value.file()->path();
208                         String fileName = value.file()->fileName();
209
210                         // Let the application specify a filename if it's going to generate a replacement file for the upload.
211                         if (!path.isEmpty()) {
212                             if (Page* page = document()->page()) {
213                                 String generatedFileName;
214                                 shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFileName);
215                                 if (shouldGenerateFile)
216                                     fileName = generatedFileName;
217                             }
218                         }
219
220                         // We have to include the filename=".." part in the header, even if the filename is empty
221                         m_formDataBuilder.addFilenameToMultiPartHeader(header, encoding, fileName);
222
223                         if (!fileName.isEmpty()) {
224                             // FIXME: The MIMETypeRegistry function's name makes it sound like it takes a path,
225                             // not just a basename. But filename is not the path. But note that it's not safe to
226                             // just use path instead since in the generated-file case it will not reflect the
227                             // MIME type of the generated file.
228                             String mimeType = MIMETypeRegistry::getMIMETypeForPath(fileName);
229                             if (!mimeType.isEmpty())
230                                 m_formDataBuilder.addContentTypeToMultiPartHeader(header, mimeType.latin1());
231                         }
232                     }
233
234                     m_formDataBuilder.finishMultiPartHeader(header);
235
236                     // Append body
237                     result->appendData(header.data(), header.size());
238                     if (size_t dataSize = value.data().length())
239                         result->appendData(value.data().data(), dataSize);
240                     else if (value.file() && !value.file()->path().isEmpty())
241                         result->appendFile(value.file()->path(), shouldGenerateFile);
242
243                     result->appendData("\r\n", 2);
244                 }
245             }
246         }
247     }
248
249     if (m_formDataBuilder.isMultiPartForm())
250         m_formDataBuilder.addBoundaryToMultiPartHeader(encodedData, boundary, true);
251
252     result->appendData(encodedData.data(), encodedData.size());
253
254     result->setIdentifier(generateFormDataIdentifier());
255     return result;
256 }
257
258 bool HTMLFormElement::isMailtoForm() const
259 {
260     return protocolIs(m_url, "mailto");
261 }
262
263 bool HTMLFormElement::prepareSubmit(Event* event)
264 {
265     Frame* frame = document()->frame();
266     if (m_insubmit || !frame)
267         return m_insubmit;
268
269     m_insubmit = true;
270     m_doingsubmit = false;
271
272     if (dispatchEventForType(eventNames().submitEvent, true, true) && !m_doingsubmit)
273         m_doingsubmit = true;
274
275     m_insubmit = false;
276
277     if (m_doingsubmit)
278         submit(event, true);
279
280     return m_doingsubmit;
281 }
282
283 static void transferMailtoPostFormDataToURL(RefPtr<FormData>& data, KURL& url, const String& encodingType)
284 {
285     String body = data->flattenToString();
286     data = FormData::create();
287
288     if (equalIgnoringCase(encodingType, "text/plain")) {
289         // Convention seems to be to decode, and s/&/\r\n/. Also, spaces are encoded as %20.
290         body = decodeURLEscapeSequences(body.replace('&', "\r\n").replace('+', ' ') + "\r\n");
291     }
292
293     Vector<char> bodyData;
294     bodyData.append("body=", 5);
295     FormDataBuilder::encodeStringAsFormData(bodyData, body.utf8());
296     body = String(bodyData.data(), bodyData.size()).replace('+', "%20");
297
298     String query = url.query();
299     if (!query.isEmpty())
300         query.append('&');
301     query.append(body);
302     url.setQuery(query);
303 }
304
305 void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool lockHistory, bool lockBackForwardList)
306 {
307     FrameView* view = document()->view();
308     Frame* frame = document()->frame();
309     if (!view || !frame)
310         return;
311
312     if (m_insubmit) {
313         m_doingsubmit = true;
314         return;
315     }
316
317     m_insubmit = true;
318
319     HTMLFormControlElement* firstSuccessfulSubmitButton = 0;
320     bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button?
321     
322     frame->loader()->clearRecordedFormValues();
323     frame->loader()->setFormAboutToBeSubmitted(this);
324     for (unsigned i = 0; i < formElements.size(); ++i) {
325         HTMLFormControlElement* control = formElements[i];
326         if (control->hasLocalName(inputTag)) {
327             HTMLInputElement* input = static_cast<HTMLInputElement*>(control);
328             if (input->isTextField()) {
329                 frame->loader()->recordFormValue(input->name(), input->value());
330                 if (input->isSearchField())
331                     input->addSearchResult();
332             }
333         }
334         if (needButtonActivation) {
335             if (control->isActivatedSubmit())
336                 needButtonActivation = false;
337             else if (firstSuccessfulSubmitButton == 0 && control->isSuccessfulSubmitButton())
338                 firstSuccessfulSubmitButton = control;
339         }
340     }
341
342     if (needButtonActivation && firstSuccessfulSubmitButton)
343         firstSuccessfulSubmitButton->setActivatedSubmit(true);
344     
345     if (m_url.isEmpty())
346         m_url = document()->url().string();
347
348     if (m_formDataBuilder.isPostMethod()) {
349         if (m_formDataBuilder.isMultiPartForm() && isMailtoForm()) {
350             setEnctype("application/x-www-form-urlencoded");
351             ASSERT(!m_formDataBuilder.isMultiPartForm());
352         }
353
354         if (!m_formDataBuilder.isMultiPartForm()) {
355             RefPtr<FormData> data = createFormData(CString());
356
357             if (isMailtoForm()) {
358                 // Convert the form data into a string that we put into the URL.
359                 KURL url = document()->completeURL(m_url);
360                 transferMailtoPostFormDataToURL(data, url, m_formDataBuilder.encodingType());
361                 m_url = url.string();
362             }
363
364             frame->loader()->submitForm("POST", m_url, data.release(), m_target, m_formDataBuilder.encodingType(), String(), event, lockHistory, lockBackForwardList);
365         } else {
366             Vector<char> boundary = m_formDataBuilder.generateUniqueBoundaryString();
367             frame->loader()->submitForm("POST", m_url, createFormData(boundary.data()), m_target, m_formDataBuilder.encodingType(), boundary.data(), event, lockHistory, lockBackForwardList);
368         }
369     } else {
370         m_formDataBuilder.setIsMultiPartForm(false);
371         frame->loader()->submitForm("GET", m_url, createFormData(CString()), m_target, String(), String(), event, lockHistory, lockBackForwardList);
372     }
373
374     if (needButtonActivation && firstSuccessfulSubmitButton)
375         firstSuccessfulSubmitButton->setActivatedSubmit(false);
376     
377     m_doingsubmit = m_insubmit = false;
378 }
379
380 void HTMLFormElement::reset()
381 {
382     Frame* frame = document()->frame();
383     if (m_inreset || !frame)
384         return;
385
386     m_inreset = true;
387
388     // ### DOM2 labels this event as not cancelable, however
389     // common browsers( sick! ) allow it be cancelled.
390     if ( !dispatchEventForType(eventNames().resetEvent,true, true) ) {
391         m_inreset = false;
392         return;
393     }
394
395     for (unsigned i = 0; i < formElements.size(); ++i)
396         formElements[i]->reset();
397
398     m_inreset = false;
399 }
400
401 void HTMLFormElement::parseMappedAttribute(MappedAttribute* attr)
402 {
403     if (attr->name() == actionAttr)
404         m_url = parseURL(attr->value());
405     else if (attr->name() == targetAttr)
406         m_target = attr->value();
407     else if (attr->name() == methodAttr)
408         m_formDataBuilder.parseMethodType(attr->value());
409     else if (attr->name() == enctypeAttr)
410         m_formDataBuilder.parseEncodingType(attr->value());
411     else if (attr->name() == accept_charsetAttr)
412         // space separated list of charsets the server
413         // accepts - see rfc2045
414         m_formDataBuilder.setAcceptCharset(attr->value());
415     else if (attr->name() == acceptAttr) {
416         // ignore this one for the moment...
417     } else if (attr->name() == autocompleteAttr) {
418         m_autocomplete = !equalIgnoringCase(attr->value(), "off");
419         if (!m_autocomplete)
420             document()->registerForDocumentActivationCallbacks(this);    
421         else
422             document()->unregisterForDocumentActivationCallbacks(this);
423     } else if (attr->name() == onsubmitAttr)
424         setInlineEventListenerForTypeAndAttribute(eventNames().submitEvent, attr);
425     else if (attr->name() == onresetAttr)
426         setInlineEventListenerForTypeAndAttribute(eventNames().resetEvent, attr);
427     else if (attr->name() == nameAttr) {
428         const AtomicString& newName = attr->value();
429         if (inDocument() && document()->isHTMLDocument()) {
430             HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
431             document->removeNamedItem(m_name);
432             document->addNamedItem(newName);
433         }
434         m_name = newName;
435     } else
436         HTMLElement::parseMappedAttribute(attr);
437 }
438
439 template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T* item)
440 {
441     size_t size = vec.size();
442     for (size_t i = 0; i != size; ++i)
443         if (vec[i] == item) {
444             vec.remove(i);
445             break;
446         }
447 }
448
449 unsigned HTMLFormElement::formElementIndex(HTMLFormControlElement* e)
450 {
451     // Check for the special case where this element is the very last thing in
452     // the form's tree of children; we don't want to walk the entire tree in that
453     // common case that occurs during parsing; instead we'll just return a value
454     // that says "add this form element to the end of the array".
455     if (e->traverseNextNode(this)) {
456         unsigned i = 0;
457         for (Node* node = this; node; node = node->traverseNextNode(this)) {
458             if (node == e)
459                 return i;
460             if (node->isHTMLElement()
461                     && static_cast<Element*>(node)->isFormControlElement()
462                     && static_cast<HTMLFormControlElement*>(node)->form() == this)
463                 ++i;
464         }
465     }
466     return formElements.size();
467 }
468
469 void HTMLFormElement::registerFormElement(HTMLFormControlElement* e)
470 {
471     document()->checkedRadioButtons().removeButton(e);
472     m_checkedRadioButtons.addButton(e);
473     formElements.insert(formElementIndex(e), e);
474 }
475
476 void HTMLFormElement::removeFormElement(HTMLFormControlElement* e)
477 {
478     m_checkedRadioButtons.removeButton(e);
479     removeFromVector(formElements, e);
480 }
481
482 bool HTMLFormElement::isURLAttribute(Attribute* attr) const
483 {
484     return attr->name() == actionAttr;
485 }
486
487 void HTMLFormElement::registerImgElement(HTMLImageElement* e)
488 {
489     imgElements.append(e);
490 }
491
492 void HTMLFormElement::removeImgElement(HTMLImageElement* e)
493 {
494     removeFromVector(imgElements, e);
495 }
496
497 PassRefPtr<HTMLCollection> HTMLFormElement::elements()
498 {
499     return HTMLFormCollection::create(this);
500 }
501
502 String HTMLFormElement::name() const
503 {
504     return getAttribute(nameAttr);
505 }
506
507 void HTMLFormElement::setName(const String &value)
508 {
509     setAttribute(nameAttr, value);
510 }
511
512 void HTMLFormElement::setAcceptCharset(const String &value)
513 {
514     setAttribute(accept_charsetAttr, value);
515 }
516
517 String HTMLFormElement::action() const
518 {
519     return getAttribute(actionAttr);
520 }
521
522 void HTMLFormElement::setAction(const String &value)
523 {
524     setAttribute(actionAttr, value);
525 }
526
527 void HTMLFormElement::setEnctype(const String &value)
528 {
529     setAttribute(enctypeAttr, value);
530 }
531
532 String HTMLFormElement::method() const
533 {
534     return getAttribute(methodAttr);
535 }
536
537 void HTMLFormElement::setMethod(const String &value)
538 {
539     setAttribute(methodAttr, value);
540 }
541
542 String HTMLFormElement::target() const
543 {
544     return getAttribute(targetAttr);
545 }
546
547 void HTMLFormElement::setTarget(const String &value)
548 {
549     setAttribute(targetAttr, value);
550 }
551
552 PassRefPtr<HTMLFormControlElement> HTMLFormElement::elementForAlias(const AtomicString& alias)
553 {
554     if (alias.isEmpty() || !m_elementAliases)
555         return 0;
556     return m_elementAliases->get(alias.impl());
557 }
558
559 void HTMLFormElement::addElementAlias(HTMLFormControlElement* element, const AtomicString& alias)
560 {
561     if (alias.isEmpty())
562         return;
563     if (!m_elementAliases)
564         m_elementAliases = new AliasMap;
565     m_elementAliases->set(alias.impl(), element);
566 }
567
568 void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems)
569 {
570     elements()->namedItems(name, namedItems);
571
572     // see if we have seen something with this name before
573     RefPtr<HTMLFormControlElement> aliasElem;
574     if (aliasElem = elementForAlias(name)) {
575         bool found = false;
576         for (unsigned n = 0; n < namedItems.size(); n++) {
577             if (namedItems[n] == aliasElem.get()) {
578                 found = true;
579                 break;
580             }
581         }
582         if (!found)
583             // we have seen it before but it is gone now. still, we need to return it.
584             namedItems.append(aliasElem.get());
585     }
586     // name has been accessed, remember it
587     if (namedItems.size() && aliasElem != namedItems.first())
588         addElementAlias(static_cast<HTMLFormControlElement*>(namedItems.first().get()), name);        
589 }
590
591 void HTMLFormElement::documentDidBecomeActive()
592 {
593     ASSERT(!m_autocomplete);
594     
595     for (unsigned i = 0; i < formElements.size(); ++i)
596         formElements[i]->reset();
597 }
598
599 void HTMLFormElement::willMoveToNewOwnerDocument()
600 {
601     if (!m_autocomplete)
602         document()->unregisterForDocumentActivationCallbacks(this);
603 }
604
605 void HTMLFormElement::didMoveToNewOwnerDocument()
606 {
607     if(m_autocomplete)
608         document()->registerForDocumentActivationCallbacks(this);
609 }
610
611 void HTMLFormElement::CheckedRadioButtons::addButton(HTMLFormControlElement* element)
612 {
613     // We only want to add radio buttons.
614     if (!element->isRadioButton())
615         return;
616
617     // Without a name, there is no group.
618     if (element->name().isEmpty())
619         return;
620
621     HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(element);
622
623     // We only track checked buttons.
624     if (!inputElement->checked())
625         return;
626
627     if (!m_nameToCheckedRadioButtonMap)
628         m_nameToCheckedRadioButtonMap.set(new NameToInputMap);
629
630     pair<NameToInputMap::iterator, bool> result = m_nameToCheckedRadioButtonMap->add(element->name().impl(), inputElement);
631     if (result.second)
632         return;
633     
634     HTMLInputElement* oldCheckedButton = result.first->second;
635     if (oldCheckedButton == inputElement)
636         return;
637
638     result.first->second = inputElement;
639     oldCheckedButton->setChecked(false);
640 }
641
642 HTMLInputElement* HTMLFormElement::CheckedRadioButtons::checkedButtonForGroup(const AtomicString& name) const
643 {
644     if (!m_nameToCheckedRadioButtonMap)
645         return 0;
646     
647     return m_nameToCheckedRadioButtonMap->get(name.impl());
648 }
649
650 void HTMLFormElement::CheckedRadioButtons::removeButton(HTMLFormControlElement* element)
651 {
652     if (element->name().isEmpty() || !m_nameToCheckedRadioButtonMap)
653         return;
654     
655     NameToInputMap::iterator it = m_nameToCheckedRadioButtonMap->find(element->name().impl());
656     if (it == m_nameToCheckedRadioButtonMap->end() || it->second != element)
657         return;
658     
659     InputElement* inputElement = toInputElement(element);
660     ASSERT_UNUSED(inputElement, inputElement);
661     ASSERT(inputElement->isChecked());
662     ASSERT(element->isRadioButton());
663
664     m_nameToCheckedRadioButtonMap->remove(it);
665     if (m_nameToCheckedRadioButtonMap->isEmpty())
666         m_nameToCheckedRadioButtonMap.clear();
667 }
668
669 } // namespace