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)
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.
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.
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.
26 #include "HTMLFormElement.h"
28 #include "CSSHelper.h"
29 #include "ChromeClient.h"
32 #include "EventNames.h"
34 #include "FileSystem.h"
36 #include "FormDataList.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"
46 #include "RenderTextControl.h"
47 #include <wtf/CurrentTime.h>
48 #include <wtf/RandomNumber.h>
54 #include <wx/filename.h>
63 using namespace HTMLNames;
65 static int64_t generateFormDataIdentifier()
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;
73 HTMLFormElement::HTMLFormElement(const QualifiedName& tagName, Document* doc)
74 : HTMLElement(tagName, doc)
77 , m_autocomplete(true)
79 , m_doingsubmit(false)
83 ASSERT(hasTagName(formTag));
86 HTMLFormElement::~HTMLFormElement()
89 document()->unregisterForDocumentActivationCallbacks(this);
91 delete m_elementAliases;
92 delete collectionInfo;
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;
100 bool HTMLFormElement::formWouldHaveSecureSubmission(const String& url)
102 return document()->completeURL(url).protocolIs("https");
105 void HTMLFormElement::attach()
107 HTMLElement::attach();
110 void HTMLFormElement::insertedIntoDocument()
112 if (document()->isHTMLDocument())
113 static_cast<HTMLDocument*>(document())->addNamedItem(m_name);
115 HTMLElement::insertedIntoDocument();
118 void HTMLFormElement::removedFromDocument()
120 if (document()->isHTMLDocument())
121 static_cast<HTMLDocument*>(document())->removeNamedItem(m_name);
123 HTMLElement::removedFromDocument();
126 void HTMLFormElement::handleLocalEvents(Event* event, bool useCapture)
128 Node* targetNode = event->target()->toNode();
129 if (!useCapture && targetNode && targetNode != this && (event->type() == eventNames().submitEvent || event->type() == eventNames().resetEvent)) {
130 event->stopPropagation();
133 HTMLElement::handleLocalEvents(event, useCapture);
136 unsigned HTMLFormElement::length() const
139 for (unsigned i = 0; i < formElements.size(); ++i)
140 if (formElements[i]->isEnumeratable())
146 Node* HTMLFormElement::item(unsigned index)
148 return elements()->item(index);
151 void HTMLFormElement::submitClick(Event* event)
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()) {
159 element->dispatchSimulatedClick(event);
164 if (!submitFound) // submit the form without a submit or image input
165 prepareSubmit(event);
168 TextEncoding HTMLFormElement::dataEncoding() const
171 return UTF8Encoding();
173 return m_formDataBuilder.dataEncoding(document());
176 PassRefPtr<FormData> HTMLFormElement::createFormData(const CString& boundary)
178 Vector<char> encodedData;
179 TextEncoding encoding = dataEncoding().encodingForFormSubmission();
181 RefPtr<FormData> result = FormData::create();
183 for (unsigned i = 0; i < formElements.size(); ++i) {
184 HTMLFormControlElement* control = formElements[i];
185 FormDataList list(encoding);
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());
199 m_formDataBuilder.addKeyValuePairAsFormData(encodedData, key.data(), value.data());
202 m_formDataBuilder.beginMultiPartHeader(header, boundary, key.data());
204 bool shouldGenerateFile = false;
205 // if the current type is FILE, then we also need to include the filename
207 const String& path = value.file()->path();
208 String fileName = value.file()->fileName();
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;
220 // We have to include the filename=".." part in the header, even if the filename is empty
221 m_formDataBuilder.addFilenameToMultiPartHeader(header, encoding, fileName);
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());
234 m_formDataBuilder.finishMultiPartHeader(header);
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);
243 result->appendData("\r\n", 2);
249 if (m_formDataBuilder.isMultiPartForm())
250 m_formDataBuilder.addBoundaryToMultiPartHeader(encodedData, boundary, true);
252 result->appendData(encodedData.data(), encodedData.size());
254 result->setIdentifier(generateFormDataIdentifier());
258 bool HTMLFormElement::isMailtoForm() const
260 return protocolIs(m_url, "mailto");
263 bool HTMLFormElement::prepareSubmit(Event* event)
265 Frame* frame = document()->frame();
266 if (m_insubmit || !frame)
270 m_doingsubmit = false;
272 if (dispatchEventForType(eventNames().submitEvent, true, true) && !m_doingsubmit)
273 m_doingsubmit = true;
280 return m_doingsubmit;
283 static void transferMailtoPostFormDataToURL(RefPtr<FormData>& data, KURL& url, const String& encodingType)
285 String body = data->flattenToString();
286 data = FormData::create();
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");
293 Vector<char> bodyData;
294 bodyData.append("body=", 5);
295 FormDataBuilder::encodeStringAsFormData(bodyData, body.utf8());
296 body = String(bodyData.data(), bodyData.size()).replace('+', "%20");
298 String query = url.query();
299 if (!query.isEmpty())
305 void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool lockHistory, bool lockBackForwardList)
307 FrameView* view = document()->view();
308 Frame* frame = document()->frame();
313 m_doingsubmit = true;
319 HTMLFormControlElement* firstSuccessfulSubmitButton = 0;
320 bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button?
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();
334 if (needButtonActivation) {
335 if (control->isActivatedSubmit())
336 needButtonActivation = false;
337 else if (firstSuccessfulSubmitButton == 0 && control->isSuccessfulSubmitButton())
338 firstSuccessfulSubmitButton = control;
342 if (needButtonActivation && firstSuccessfulSubmitButton)
343 firstSuccessfulSubmitButton->setActivatedSubmit(true);
346 m_url = document()->url().string();
348 if (m_formDataBuilder.isPostMethod()) {
349 if (m_formDataBuilder.isMultiPartForm() && isMailtoForm()) {
350 setEnctype("application/x-www-form-urlencoded");
351 ASSERT(!m_formDataBuilder.isMultiPartForm());
354 if (!m_formDataBuilder.isMultiPartForm()) {
355 RefPtr<FormData> data = createFormData(CString());
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();
364 frame->loader()->submitForm("POST", m_url, data.release(), m_target, m_formDataBuilder.encodingType(), String(), event, lockHistory, lockBackForwardList);
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);
370 m_formDataBuilder.setIsMultiPartForm(false);
371 frame->loader()->submitForm("GET", m_url, createFormData(CString()), m_target, String(), String(), event, lockHistory, lockBackForwardList);
374 if (needButtonActivation && firstSuccessfulSubmitButton)
375 firstSuccessfulSubmitButton->setActivatedSubmit(false);
377 m_doingsubmit = m_insubmit = false;
380 void HTMLFormElement::reset()
382 Frame* frame = document()->frame();
383 if (m_inreset || !frame)
388 // ### DOM2 labels this event as not cancelable, however
389 // common browsers( sick! ) allow it be cancelled.
390 if ( !dispatchEventForType(eventNames().resetEvent,true, true) ) {
395 for (unsigned i = 0; i < formElements.size(); ++i)
396 formElements[i]->reset();
401 void HTMLFormElement::parseMappedAttribute(MappedAttribute* attr)
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");
420 document()->registerForDocumentActivationCallbacks(this);
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);
436 HTMLElement::parseMappedAttribute(attr);
439 template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T* item)
441 size_t size = vec.size();
442 for (size_t i = 0; i != size; ++i)
443 if (vec[i] == item) {
449 unsigned HTMLFormElement::formElementIndex(HTMLFormControlElement* e)
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)) {
457 for (Node* node = this; node; node = node->traverseNextNode(this)) {
460 if (node->isHTMLElement()
461 && static_cast<Element*>(node)->isFormControlElement()
462 && static_cast<HTMLFormControlElement*>(node)->form() == this)
466 return formElements.size();
469 void HTMLFormElement::registerFormElement(HTMLFormControlElement* e)
471 document()->checkedRadioButtons().removeButton(e);
472 m_checkedRadioButtons.addButton(e);
473 formElements.insert(formElementIndex(e), e);
476 void HTMLFormElement::removeFormElement(HTMLFormControlElement* e)
478 m_checkedRadioButtons.removeButton(e);
479 removeFromVector(formElements, e);
482 bool HTMLFormElement::isURLAttribute(Attribute* attr) const
484 return attr->name() == actionAttr;
487 void HTMLFormElement::registerImgElement(HTMLImageElement* e)
489 imgElements.append(e);
492 void HTMLFormElement::removeImgElement(HTMLImageElement* e)
494 removeFromVector(imgElements, e);
497 PassRefPtr<HTMLCollection> HTMLFormElement::elements()
499 return HTMLFormCollection::create(this);
502 String HTMLFormElement::name() const
504 return getAttribute(nameAttr);
507 void HTMLFormElement::setName(const String &value)
509 setAttribute(nameAttr, value);
512 void HTMLFormElement::setAcceptCharset(const String &value)
514 setAttribute(accept_charsetAttr, value);
517 String HTMLFormElement::action() const
519 return getAttribute(actionAttr);
522 void HTMLFormElement::setAction(const String &value)
524 setAttribute(actionAttr, value);
527 void HTMLFormElement::setEnctype(const String &value)
529 setAttribute(enctypeAttr, value);
532 String HTMLFormElement::method() const
534 return getAttribute(methodAttr);
537 void HTMLFormElement::setMethod(const String &value)
539 setAttribute(methodAttr, value);
542 String HTMLFormElement::target() const
544 return getAttribute(targetAttr);
547 void HTMLFormElement::setTarget(const String &value)
549 setAttribute(targetAttr, value);
552 PassRefPtr<HTMLFormControlElement> HTMLFormElement::elementForAlias(const AtomicString& alias)
554 if (alias.isEmpty() || !m_elementAliases)
556 return m_elementAliases->get(alias.impl());
559 void HTMLFormElement::addElementAlias(HTMLFormControlElement* element, const AtomicString& alias)
563 if (!m_elementAliases)
564 m_elementAliases = new AliasMap;
565 m_elementAliases->set(alias.impl(), element);
568 void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems)
570 elements()->namedItems(name, namedItems);
572 // see if we have seen something with this name before
573 RefPtr<HTMLFormControlElement> aliasElem;
574 if (aliasElem = elementForAlias(name)) {
576 for (unsigned n = 0; n < namedItems.size(); n++) {
577 if (namedItems[n] == aliasElem.get()) {
583 // we have seen it before but it is gone now. still, we need to return it.
584 namedItems.append(aliasElem.get());
586 // name has been accessed, remember it
587 if (namedItems.size() && aliasElem != namedItems.first())
588 addElementAlias(static_cast<HTMLFormControlElement*>(namedItems.first().get()), name);
591 void HTMLFormElement::documentDidBecomeActive()
593 ASSERT(!m_autocomplete);
595 for (unsigned i = 0; i < formElements.size(); ++i)
596 formElements[i]->reset();
599 void HTMLFormElement::willMoveToNewOwnerDocument()
602 document()->unregisterForDocumentActivationCallbacks(this);
605 void HTMLFormElement::didMoveToNewOwnerDocument()
608 document()->registerForDocumentActivationCallbacks(this);
611 void HTMLFormElement::CheckedRadioButtons::addButton(HTMLFormControlElement* element)
613 // We only want to add radio buttons.
614 if (!element->isRadioButton())
617 // Without a name, there is no group.
618 if (element->name().isEmpty())
621 HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(element);
623 // We only track checked buttons.
624 if (!inputElement->checked())
627 if (!m_nameToCheckedRadioButtonMap)
628 m_nameToCheckedRadioButtonMap.set(new NameToInputMap);
630 pair<NameToInputMap::iterator, bool> result = m_nameToCheckedRadioButtonMap->add(element->name().impl(), inputElement);
634 HTMLInputElement* oldCheckedButton = result.first->second;
635 if (oldCheckedButton == inputElement)
638 result.first->second = inputElement;
639 oldCheckedButton->setChecked(false);
642 HTMLInputElement* HTMLFormElement::CheckedRadioButtons::checkedButtonForGroup(const AtomicString& name) const
644 if (!m_nameToCheckedRadioButtonMap)
647 return m_nameToCheckedRadioButtonMap->get(name.impl());
650 void HTMLFormElement::CheckedRadioButtons::removeButton(HTMLFormControlElement* element)
652 if (element->name().isEmpty() || !m_nameToCheckedRadioButtonMap)
655 NameToInputMap::iterator it = m_nameToCheckedRadioButtonMap->find(element->name().impl());
656 if (it == m_nameToCheckedRadioButtonMap->end() || it->second != element)
659 InputElement* inputElement = toInputElement(element);
660 ASSERT_UNUSED(inputElement, inputElement);
661 ASSERT(inputElement->isChecked());
662 ASSERT(element->isRadioButton());
664 m_nameToCheckedRadioButtonMap->remove(it);
665 if (m_nameToCheckedRadioButtonMap->isEmpty())
666 m_nameToCheckedRadioButtonMap.clear();