Rubber-stamped by Brady Eidson.
[webbrowser.git] / WebCore / html / HTMLFormElement.cpp
blob0433c82e9c650f3c1fc351726cd6a1bcec16ee6d
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, 2009 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.
25 #include "config.h"
26 #include "HTMLFormElement.h"
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 "FormState.h"
38 #include "Frame.h"
39 #include "FrameLoader.h"
40 #include "HTMLDocument.h"
41 #include "HTMLFormCollection.h"
42 #include "HTMLImageElement.h"
43 #include "HTMLInputElement.h"
44 #include "HTMLNames.h"
45 #include "ScriptEventListener.h"
46 #include "MIMETypeRegistry.h"
47 #include "MappedAttribute.h"
48 #include "Page.h"
49 #include "RenderTextControl.h"
50 #include "ValidityState.h"
51 #include <limits>
52 #include <wtf/CurrentTime.h>
53 #include <wtf/RandomNumber.h>
55 #if PLATFORM(WX)
56 #include <wx/defs.h>
57 #include <wx/filename.h>
58 #endif
60 using namespace std;
62 namespace WebCore {
64 using namespace HTMLNames;
66 static int64_t generateFormDataIdentifier()
68 // Initialize to the current time to reduce the likelihood of generating
69 // identifiers that overlap with those from past/future browser sessions.
70 static int64_t nextIdentifier = static_cast<int64_t>(currentTime() * 1000000.0);
71 return ++nextIdentifier;
74 HTMLFormElement::HTMLFormElement(const QualifiedName& tagName, Document* doc)
75 : HTMLElement(tagName, doc)
76 , m_elementAliases(0)
77 , collectionInfo(0)
78 , m_autocomplete(true)
79 , m_insubmit(false)
80 , m_doingsubmit(false)
81 , m_inreset(false)
82 , m_malformed(false)
83 , m_demoted(false)
85 ASSERT(hasTagName(formTag));
88 HTMLFormElement::~HTMLFormElement()
90 if (!m_autocomplete)
91 document()->unregisterForDocumentActivationCallbacks(this);
93 delete m_elementAliases;
94 delete collectionInfo;
96 for (unsigned i = 0; i < formElements.size(); ++i)
97 formElements[i]->formDestroyed();
98 for (unsigned i = 0; i < imgElements.size(); ++i)
99 imgElements[i]->m_form = 0;
102 bool HTMLFormElement::formWouldHaveSecureSubmission(const String& url)
104 return document()->completeURL(url).protocolIs("https");
107 void HTMLFormElement::attach()
109 HTMLElement::attach();
112 bool HTMLFormElement::rendererIsNeeded(RenderStyle* style)
114 if (!isDemoted())
115 return HTMLElement::rendererIsNeeded(style);
117 Node* node = parentNode();
118 RenderObject* parentRenderer = node->renderer();
119 bool parentIsTableElementPart = (parentRenderer->isTable() && node->hasTagName(tableTag))
120 || (parentRenderer->isTableRow() && node->hasTagName(trTag))
121 || (parentRenderer->isTableSection() && node->hasTagName(tbodyTag))
122 || (parentRenderer->isTableCol() && node->hasTagName(colTag))
123 || (parentRenderer->isTableCell() && node->hasTagName(trTag));
125 if (!parentIsTableElementPart)
126 return true;
128 EDisplay display = style->display();
129 bool formIsTablePart = display == TABLE || display == INLINE_TABLE || display == TABLE_ROW_GROUP
130 || display == TABLE_HEADER_GROUP || display == TABLE_FOOTER_GROUP || display == TABLE_ROW
131 || display == TABLE_COLUMN_GROUP || display == TABLE_COLUMN || display == TABLE_CELL
132 || display == TABLE_CAPTION;
134 return formIsTablePart;
137 void HTMLFormElement::insertedIntoDocument()
139 if (document()->isHTMLDocument())
140 static_cast<HTMLDocument*>(document())->addNamedItem(m_name);
142 HTMLElement::insertedIntoDocument();
145 void HTMLFormElement::removedFromDocument()
147 if (document()->isHTMLDocument())
148 static_cast<HTMLDocument*>(document())->removeNamedItem(m_name);
150 HTMLElement::removedFromDocument();
153 void HTMLFormElement::handleLocalEvents(Event* event)
155 Node* targetNode = event->target()->toNode();
156 if (event->eventPhase() != Event::CAPTURING_PHASE && targetNode && targetNode != this && (event->type() == eventNames().submitEvent || event->type() == eventNames().resetEvent)) {
157 event->stopPropagation();
158 return;
160 HTMLElement::handleLocalEvents(event);
163 unsigned HTMLFormElement::length() const
165 int len = 0;
166 for (unsigned i = 0; i < formElements.size(); ++i)
167 if (formElements[i]->isEnumeratable())
168 ++len;
170 return len;
173 Node* HTMLFormElement::item(unsigned index)
175 return elements()->item(index);
178 void HTMLFormElement::submitClick(Event* event)
180 bool submitFound = false;
181 for (unsigned i = 0; i < formElements.size(); ++i) {
182 if (formElements[i]->hasLocalName(inputTag)) {
183 HTMLInputElement* element = static_cast<HTMLInputElement*>(formElements[i]);
184 if (element->isSuccessfulSubmitButton() && element->renderer()) {
185 submitFound = true;
186 element->dispatchSimulatedClick(event);
187 break;
191 if (!submitFound) // submit the form without a submit or image input
192 prepareSubmit(event);
195 TextEncoding HTMLFormElement::dataEncoding() const
197 if (isMailtoForm())
198 return UTF8Encoding();
200 return m_formDataBuilder.dataEncoding(document());
203 PassRefPtr<FormData> HTMLFormElement::createFormData(const CString& boundary)
205 Vector<char> encodedData;
206 TextEncoding encoding = dataEncoding().encodingForFormSubmission();
208 RefPtr<FormData> result = FormData::create();
210 for (unsigned i = 0; i < formElements.size(); ++i) {
211 HTMLFormControlElement* control = formElements[i];
212 FormDataList list(encoding);
214 if (!control->disabled() && control->appendFormData(list, m_formDataBuilder.isMultiPartForm())) {
215 size_t formDataListSize = list.list().size();
216 ASSERT(formDataListSize % 2 == 0);
217 for (size_t j = 0; j < formDataListSize; j += 2) {
218 const FormDataList::Item& key = list.list()[j];
219 const FormDataList::Item& value = list.list()[j + 1];
220 if (!m_formDataBuilder.isMultiPartForm()) {
221 // Omit the name "isindex" if it's the first form data element.
222 // FIXME: Why is this a good rule? Is this obsolete now?
223 if (encodedData.isEmpty() && key.data() == "isindex")
224 FormDataBuilder::encodeStringAsFormData(encodedData, value.data());
225 else
226 m_formDataBuilder.addKeyValuePairAsFormData(encodedData, key.data(), value.data());
227 } else {
228 Vector<char> header;
229 m_formDataBuilder.beginMultiPartHeader(header, boundary, key.data());
231 bool shouldGenerateFile = false;
232 // if the current type is FILE, then we also need to include the filename
233 if (value.file()) {
234 const String& path = value.file()->path();
235 String fileName = value.file()->fileName();
237 // Let the application specify a filename if it's going to generate a replacement file for the upload.
238 if (!path.isEmpty()) {
239 if (Page* page = document()->page()) {
240 String generatedFileName;
241 shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFileName);
242 if (shouldGenerateFile)
243 fileName = generatedFileName;
247 // We have to include the filename=".." part in the header, even if the filename is empty
248 m_formDataBuilder.addFilenameToMultiPartHeader(header, encoding, fileName);
250 if (!fileName.isEmpty()) {
251 // FIXME: The MIMETypeRegistry function's name makes it sound like it takes a path,
252 // not just a basename. But filename is not the path. But note that it's not safe to
253 // just use path instead since in the generated-file case it will not reflect the
254 // MIME type of the generated file.
255 String mimeType = MIMETypeRegistry::getMIMETypeForPath(fileName);
256 if (!mimeType.isEmpty())
257 m_formDataBuilder.addContentTypeToMultiPartHeader(header, mimeType.latin1());
261 m_formDataBuilder.finishMultiPartHeader(header);
263 // Append body
264 result->appendData(header.data(), header.size());
265 if (size_t dataSize = value.data().length())
266 result->appendData(value.data().data(), dataSize);
267 else if (value.file() && !value.file()->path().isEmpty())
268 result->appendFile(value.file()->path(), shouldGenerateFile);
270 result->appendData("\r\n", 2);
276 if (m_formDataBuilder.isMultiPartForm())
277 m_formDataBuilder.addBoundaryToMultiPartHeader(encodedData, boundary, true);
279 result->appendData(encodedData.data(), encodedData.size());
281 result->setIdentifier(generateFormDataIdentifier());
282 return result;
285 bool HTMLFormElement::isMailtoForm() const
287 return protocolIs(m_url, "mailto");
290 bool HTMLFormElement::prepareSubmit(Event* event)
292 Frame* frame = document()->frame();
293 if (m_insubmit || !frame)
294 return m_insubmit;
296 m_insubmit = true;
297 m_doingsubmit = false;
299 if (dispatchEvent(Event::create(eventNames().submitEvent, true, true)) && !m_doingsubmit)
300 m_doingsubmit = true;
302 m_insubmit = false;
304 if (m_doingsubmit)
305 submit(event, true, false, NotSubmittedByJavaScript);
307 return m_doingsubmit;
310 static void transferMailtoPostFormDataToURL(RefPtr<FormData>& data, KURL& url, const String& encodingType)
312 String body = data->flattenToString();
313 data = FormData::create();
315 if (equalIgnoringCase(encodingType, "text/plain")) {
316 // Convention seems to be to decode, and s/&/\r\n/. Also, spaces are encoded as %20.
317 body = decodeURLEscapeSequences(body.replace('&', "\r\n").replace('+', ' ') + "\r\n");
320 Vector<char> bodyData;
321 bodyData.append("body=", 5);
322 FormDataBuilder::encodeStringAsFormData(bodyData, body.utf8());
323 body = String(bodyData.data(), bodyData.size()).replace('+', "%20");
325 String query = url.query();
326 if (!query.isEmpty())
327 query.append('&');
328 query.append(body);
329 url.setQuery(query);
332 void HTMLFormElement::submit(Frame* javaScriptActiveFrame)
334 if (javaScriptActiveFrame)
335 submit(0, false, !javaScriptActiveFrame->script()->anyPageIsProcessingUserGesture(), SubmittedByJavaScript);
336 else
337 submit(0, false, false, NotSubmittedByJavaScript);
340 void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool lockHistory, FormSubmissionTrigger formSubmissionTrigger)
342 FrameView* view = document()->view();
343 Frame* frame = document()->frame();
344 if (!view || !frame)
345 return;
347 if (m_insubmit) {
348 m_doingsubmit = true;
349 return;
352 m_insubmit = true;
354 HTMLFormControlElement* firstSuccessfulSubmitButton = 0;
355 bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button?
357 Vector<pair<String, String> > formValues;
359 for (unsigned i = 0; i < formElements.size(); ++i) {
360 HTMLFormControlElement* control = formElements[i];
361 if (control->hasLocalName(inputTag)) {
362 HTMLInputElement* input = static_cast<HTMLInputElement*>(control);
363 if (input->isTextField()) {
364 formValues.append(pair<String, String>(input->name(), input->value()));
365 if (input->isSearchField())
366 input->addSearchResult();
369 if (needButtonActivation) {
370 if (control->isActivatedSubmit())
371 needButtonActivation = false;
372 else if (firstSuccessfulSubmitButton == 0 && control->isSuccessfulSubmitButton())
373 firstSuccessfulSubmitButton = control;
377 RefPtr<FormState> formState = FormState::create(this, formValues, frame, formSubmissionTrigger);
379 if (needButtonActivation && firstSuccessfulSubmitButton)
380 firstSuccessfulSubmitButton->setActivatedSubmit(true);
382 if (m_url.isEmpty())
383 m_url = document()->url().string();
385 if (m_formDataBuilder.isPostMethod()) {
386 if (m_formDataBuilder.isMultiPartForm() && isMailtoForm()) {
387 setEnctype("application/x-www-form-urlencoded");
388 ASSERT(!m_formDataBuilder.isMultiPartForm());
391 if (!m_formDataBuilder.isMultiPartForm()) {
392 RefPtr<FormData> data = createFormData(CString());
394 if (isMailtoForm()) {
395 // Convert the form data into a string that we put into the URL.
396 KURL url = document()->completeURL(m_url);
397 transferMailtoPostFormDataToURL(data, url, m_formDataBuilder.encodingType());
398 m_url = url.string();
401 frame->loader()->submitForm("POST", m_url, data.release(), m_target, m_formDataBuilder.encodingType(), String(), lockHistory, event, formState.release());
402 } else {
403 Vector<char> boundary = m_formDataBuilder.generateUniqueBoundaryString();
404 frame->loader()->submitForm("POST", m_url, createFormData(boundary.data()), m_target, m_formDataBuilder.encodingType(), boundary.data(), lockHistory, event, formState.release());
406 } else {
407 m_formDataBuilder.setIsMultiPartForm(false);
408 frame->loader()->submitForm("GET", m_url, createFormData(CString()), m_target, String(), String(), lockHistory, event, formState.release());
411 if (needButtonActivation && firstSuccessfulSubmitButton)
412 firstSuccessfulSubmitButton->setActivatedSubmit(false);
414 m_doingsubmit = m_insubmit = false;
417 void HTMLFormElement::reset()
419 Frame* frame = document()->frame();
420 if (m_inreset || !frame)
421 return;
423 m_inreset = true;
425 // ### DOM2 labels this event as not cancelable, however
426 // common browsers( sick! ) allow it be cancelled.
427 if (!dispatchEvent(Event::create(eventNames().resetEvent, true, true))) {
428 m_inreset = false;
429 return;
432 for (unsigned i = 0; i < formElements.size(); ++i)
433 formElements[i]->reset();
435 m_inreset = false;
438 void HTMLFormElement::parseMappedAttribute(MappedAttribute* attr)
440 if (attr->name() == actionAttr)
441 m_url = deprecatedParseURL(attr->value());
442 else if (attr->name() == targetAttr)
443 m_target = attr->value();
444 else if (attr->name() == methodAttr)
445 m_formDataBuilder.parseMethodType(attr->value());
446 else if (attr->name() == enctypeAttr)
447 m_formDataBuilder.parseEncodingType(attr->value());
448 else if (attr->name() == accept_charsetAttr)
449 // space separated list of charsets the server
450 // accepts - see rfc2045
451 m_formDataBuilder.setAcceptCharset(attr->value());
452 else if (attr->name() == acceptAttr) {
453 // ignore this one for the moment...
454 } else if (attr->name() == autocompleteAttr) {
455 m_autocomplete = !equalIgnoringCase(attr->value(), "off");
456 if (!m_autocomplete)
457 document()->registerForDocumentActivationCallbacks(this);
458 else
459 document()->unregisterForDocumentActivationCallbacks(this);
460 } else if (attr->name() == onsubmitAttr)
461 setAttributeEventListener(eventNames().submitEvent, createAttributeEventListener(this, attr));
462 else if (attr->name() == onresetAttr)
463 setAttributeEventListener(eventNames().resetEvent, createAttributeEventListener(this, attr));
464 else if (attr->name() == nameAttr) {
465 const AtomicString& newName = attr->value();
466 if (inDocument() && document()->isHTMLDocument()) {
467 HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
468 document->removeNamedItem(m_name);
469 document->addNamedItem(newName);
471 m_name = newName;
472 } else
473 HTMLElement::parseMappedAttribute(attr);
476 template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T* item)
478 size_t size = vec.size();
479 for (size_t i = 0; i != size; ++i)
480 if (vec[i] == item) {
481 vec.remove(i);
482 break;
486 unsigned HTMLFormElement::formElementIndex(HTMLFormControlElement* e)
488 // Check for the special case where this element is the very last thing in
489 // the form's tree of children; we don't want to walk the entire tree in that
490 // common case that occurs during parsing; instead we'll just return a value
491 // that says "add this form element to the end of the array".
492 if (e->traverseNextNode(this)) {
493 unsigned i = 0;
494 for (Node* node = this; node; node = node->traverseNextNode(this)) {
495 if (node == e)
496 return i;
497 if (node->isHTMLElement()
498 && static_cast<Element*>(node)->isFormControlElement()
499 && static_cast<HTMLFormControlElement*>(node)->form() == this)
500 ++i;
503 return formElements.size();
506 void HTMLFormElement::registerFormElement(HTMLFormControlElement* e)
508 document()->checkedRadioButtons().removeButton(e);
509 m_checkedRadioButtons.addButton(e);
510 formElements.insert(formElementIndex(e), e);
513 void HTMLFormElement::removeFormElement(HTMLFormControlElement* e)
515 m_checkedRadioButtons.removeButton(e);
516 removeFromVector(formElements, e);
519 bool HTMLFormElement::isURLAttribute(Attribute* attr) const
521 return attr->name() == actionAttr;
524 void HTMLFormElement::registerImgElement(HTMLImageElement* e)
526 imgElements.append(e);
529 void HTMLFormElement::removeImgElement(HTMLImageElement* e)
531 removeFromVector(imgElements, e);
534 PassRefPtr<HTMLCollection> HTMLFormElement::elements()
536 return HTMLFormCollection::create(this);
539 String HTMLFormElement::name() const
541 return getAttribute(nameAttr);
544 void HTMLFormElement::setName(const String &value)
546 setAttribute(nameAttr, value);
549 bool HTMLFormElement::noValidate() const
551 return !getAttribute(novalidateAttr).isNull();
554 void HTMLFormElement::setNoValidate(bool novalidate)
556 setAttribute(novalidateAttr, novalidate ? "" : 0);
559 void HTMLFormElement::setAcceptCharset(const String &value)
561 setAttribute(accept_charsetAttr, value);
564 String HTMLFormElement::action() const
566 return getAttribute(actionAttr);
569 void HTMLFormElement::setAction(const String &value)
571 setAttribute(actionAttr, value);
574 void HTMLFormElement::setEnctype(const String &value)
576 setAttribute(enctypeAttr, value);
579 String HTMLFormElement::method() const
581 return getAttribute(methodAttr);
584 void HTMLFormElement::setMethod(const String &value)
586 setAttribute(methodAttr, value);
589 String HTMLFormElement::target() const
591 return getAttribute(targetAttr);
594 void HTMLFormElement::setTarget(const String &value)
596 setAttribute(targetAttr, value);
599 HTMLFormControlElement* HTMLFormElement::defaultButton() const
601 for (unsigned i = 0; i < formElements.size(); ++i) {
602 HTMLFormControlElement* control = formElements[i];
603 if (control->isSuccessfulSubmitButton())
604 return control;
607 return 0;
610 bool HTMLFormElement::checkValidity()
612 // TODO: Check for unhandled invalid controls, see #27452 for tips.
614 bool hasOnlyValidControls = true;
615 for (unsigned i = 0; i < formElements.size(); ++i) {
616 HTMLFormControlElement* control = formElements[i];
617 if (!control->checkValidity())
618 hasOnlyValidControls = false;
621 return hasOnlyValidControls;
624 PassRefPtr<HTMLFormControlElement> HTMLFormElement::elementForAlias(const AtomicString& alias)
626 if (alias.isEmpty() || !m_elementAliases)
627 return 0;
628 return m_elementAliases->get(alias.impl());
631 void HTMLFormElement::addElementAlias(HTMLFormControlElement* element, const AtomicString& alias)
633 if (alias.isEmpty())
634 return;
635 if (!m_elementAliases)
636 m_elementAliases = new AliasMap;
637 m_elementAliases->set(alias.impl(), element);
640 void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems)
642 elements()->namedItems(name, namedItems);
644 // see if we have seen something with this name before
645 RefPtr<HTMLFormControlElement> aliasElem;
646 if (aliasElem = elementForAlias(name)) {
647 bool found = false;
648 for (unsigned n = 0; n < namedItems.size(); n++) {
649 if (namedItems[n] == aliasElem.get()) {
650 found = true;
651 break;
654 if (!found)
655 // we have seen it before but it is gone now. still, we need to return it.
656 namedItems.append(aliasElem.get());
658 // name has been accessed, remember it
659 if (namedItems.size() && aliasElem != namedItems.first())
660 addElementAlias(static_cast<HTMLFormControlElement*>(namedItems.first().get()), name);
663 void HTMLFormElement::documentDidBecomeActive()
665 ASSERT(!m_autocomplete);
667 for (unsigned i = 0; i < formElements.size(); ++i)
668 formElements[i]->reset();
671 void HTMLFormElement::willMoveToNewOwnerDocument()
673 if (!m_autocomplete)
674 document()->unregisterForDocumentActivationCallbacks(this);
675 HTMLElement::willMoveToNewOwnerDocument();
678 void HTMLFormElement::didMoveToNewOwnerDocument()
680 if (!m_autocomplete)
681 document()->registerForDocumentActivationCallbacks(this);
682 HTMLElement::didMoveToNewOwnerDocument();
685 } // namespace