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"
30 #include "EventNames.h"
32 #include "FormDataList.h"
34 #include "FrameLoader.h"
35 #include "HTMLDocument.h"
36 #include "HTMLFormCollection.h"
37 #include "HTMLImageElement.h"
38 #include "HTMLInputElement.h"
39 #include "HTMLNames.h"
40 #include "MIMETypeRegistry.h"
41 #include "RenderTextControl.h"
44 #include <QtCore/QFileInfo>
49 #include <wx/filename.h>
58 using namespace EventNames;
59 using namespace HTMLNames;
61 static const char hexDigits[17] = "0123456789ABCDEF";
63 HTMLFormElement::HTMLFormElement(Document* doc)
64 : HTMLElement(formTag, doc)
67 , m_enctype("application/x-www-form-urlencoded")
70 , m_autocomplete(true)
72 , m_doingsubmit(false)
78 HTMLFormElement::~HTMLFormElement()
80 delete m_elementAliases;
81 delete collectionInfo;
83 for (unsigned i = 0; i < formElements.size(); ++i)
84 formElements[i]->formDestroyed();
85 for (unsigned i = 0; i < imgElements.size(); ++i)
86 imgElements[i]->m_form = 0;
89 bool HTMLFormElement::formWouldHaveSecureSubmission(const String& url)
91 return document()->completeURL(url).protocolIs("https");
94 void HTMLFormElement::attach()
96 HTMLElement::attach();
99 void HTMLFormElement::insertedIntoDocument()
101 if (document()->isHTMLDocument())
102 static_cast<HTMLDocument*>(document())->addNamedItem(m_name);
104 HTMLElement::insertedIntoDocument();
107 void HTMLFormElement::removedFromDocument()
109 if (document()->isHTMLDocument())
110 static_cast<HTMLDocument*>(document())->removeNamedItem(m_name);
112 HTMLElement::removedFromDocument();
115 void HTMLFormElement::handleLocalEvents(Event* event, bool useCapture)
117 EventTargetNode* targetNode = event->target()->toNode();
118 if (!useCapture && targetNode && targetNode != this && (event->type() == submitEvent || event->type() == resetEvent)) {
119 event->stopPropagation();
122 HTMLElement::handleLocalEvents(event, useCapture);
125 unsigned HTMLFormElement::length() const
128 for (unsigned i = 0; i < formElements.size(); ++i)
129 if (formElements[i]->isEnumeratable())
135 Node* HTMLFormElement::item(unsigned index)
137 return elements()->item(index);
140 void HTMLFormElement::submitClick(Event* event)
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()) {
148 element->dispatchSimulatedClick(event);
153 if (!submitFound) // submit the form without a submit or image input
154 prepareSubmit(event);
157 static void appendString(Vector<char>& buffer, const char* string)
159 buffer.append(string, strlen(string));
162 static void appendString(Vector<char>& buffer, const CString& string)
164 buffer.append(string.data(), string.length());
167 static void appendEncodedString(Vector<char>& buffer, const CString& string)
169 // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
170 int length = string.length();
171 for (int i = 0; i < length; i++) {
172 unsigned char c = string.data()[i];
174 // Same safe characters as Netscape for compatibility.
175 static const char safe[] = "-._*";
176 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || strchr(safe, c))
180 else if (c == '\n' || (c == '\r' && (i + 1 >= length || string.data()[i + 1] != '\n')))
181 appendString(buffer, "%0D%0A");
182 else if (c != '\r') {
184 buffer.append(hexDigits[c >> 4]);
185 buffer.append(hexDigits[c & 0xF]);
190 // FIXME: Move to platform directory?
191 static int randomNumber()
193 static bool randomSeeded = false;
203 srand(static_cast<unsigned>(time(0)));
210 // FIXME: Move to platform directory?
211 // Warning: this helper doesn't currently have a reliable cross-platform behavior in
212 // certain edge cases (see basename(3) specification for examples).
213 // Consider this if it ever needs to become a general purpose method.
214 static String pathGetFilename(const String& path)
217 return QFileInfo(path).fileName();
219 return wxFileName(path).GetFullName();
220 #elif PLATFORM(WIN_OS)
222 return String(::PathFindFileName(copy.charactersWithNullTermination()));
224 return path.substring(path.reverseFind('/') + 1);
228 TextEncoding HTMLFormElement::dataEncoding() const
231 return UTF8Encoding();
233 TextEncoding encoding;
234 String str = m_acceptcharset;
235 str.replace(',', ' ');
236 Vector<String> charsets;
237 str.split(' ', charsets);
238 Vector<String>::const_iterator end = charsets.end();
239 for (Vector<String>::const_iterator it = charsets.begin(); it != end; ++it)
240 if ((encoding = TextEncoding(*it)).isValid())
242 if (Frame* frame = document()->frame())
243 return frame->loader()->encoding();
244 return Latin1Encoding();
247 PassRefPtr<FormData> HTMLFormElement::formData(const char* boundary) const
249 Vector<char> encodedData;
250 TextEncoding encoding = dataEncoding();
252 RefPtr<FormData> result = FormData::create();
254 for (unsigned i = 0; i < formElements.size(); ++i) {
255 HTMLGenericFormElement* control = formElements[i];
256 FormDataList list(encoding);
258 if (!control->disabled() && control->appendFormData(list, m_multipart)) {
259 size_t ln = list.list().size();
260 for (size_t j = 0; j < ln; ++j) {
261 const FormDataListItem& item = list.list()[j];
263 // Omit the name "isindex" if it's the first form data element.
264 // FIXME: Why is this a good rule? Is this obsolete now?
265 if (encodedData.isEmpty() && item.m_data == "isindex")
266 appendEncodedString(encodedData, list.list()[++j].m_data);
268 if (!encodedData.isEmpty())
269 encodedData.append('&');
270 appendEncodedString(encodedData, item.m_data);
271 encodedData.append('=');
272 appendEncodedString(encodedData, list.list()[++j].m_data);
276 appendString(header, "--");
277 appendString(header, boundary);
278 appendString(header, "\r\n");
279 appendString(header, "Content-Disposition: form-data; name=\"");
280 header.append(item.m_data.data(), item.m_data.length());
283 // if the current type is FILE, then we also need to
284 // include the filename
285 if (control->hasLocalName(inputTag)
286 && static_cast<HTMLInputElement*>(control)->inputType() == HTMLInputElement::FILE) {
287 String path = static_cast<HTMLInputElement*>(control)->value();
288 String filename = pathGetFilename(path);
290 // FIXME: This won't work if the filename includes a " mark,
291 // or control characters like CR or LF. This also does strange
292 // things if the filename includes characters you can't encode
293 // in the website's character set.
294 appendString(header, "; filename=\"");
295 appendString(header, encoding.encode(filename.characters(), filename.length(), QuestionMarksForUnencodables));
298 if (!path.isEmpty()) {
299 String mimeType = MIMETypeRegistry::getMIMETypeForPath(path);
300 if (!mimeType.isEmpty()) {
301 appendString(header, "\r\nContent-Type: ");
302 appendString(header, mimeType.latin1());
307 appendString(header, "\r\n\r\n");
310 result->appendData(header.data(), header.size());
311 const FormDataListItem& item = list.list()[j + 1];
312 if (size_t dataSize = item.m_data.length())
313 result->appendData(item.m_data.data(), dataSize);
314 else if (!item.m_path.isEmpty())
315 result->appendFile(item.m_path);
316 result->appendData("\r\n", 2);
326 appendString(encodedData, "--");
327 appendString(encodedData, boundary);
328 appendString(encodedData, "--\r\n");
331 result->appendData(encodedData.data(), encodedData.size());
335 void HTMLFormElement::parseEnctype(const String& type)
337 if(type.contains("multipart", false) || type.contains("form-data", false)) {
338 m_enctype = "multipart/form-data";
340 } else if (type.contains("text", false) || type.contains("plain", false)) {
341 m_enctype = "text/plain";
344 m_enctype = "application/x-www-form-urlencoded";
349 bool HTMLFormElement::isMailtoForm() const
351 return protocolIs(m_url, "mailto");
354 bool HTMLFormElement::prepareSubmit(Event* event)
356 Frame* frame = document()->frame();
357 if (m_insubmit || !frame)
361 m_doingsubmit = false;
363 if (dispatchHTMLEvent(submitEvent, true, true) && !m_doingsubmit)
364 m_doingsubmit = true;
371 return m_doingsubmit;
374 void HTMLFormElement::submit()
379 // Returns a 0-terminated C string in the vector.
380 static void getUniqueBoundaryString(Vector<char>& boundary)
382 // The RFC 2046 spec says the AlphaNumeric characters plus the following characters
383 // are legal for boundaries: '()+_,-./:=?
384 // However the following characters, though legal, cause some sites to fail:
386 // http://bugs.webkit.org/show_bug.cgi?id=13352
387 static const char AlphaNumericEncMap[64] =
389 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
390 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
391 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
392 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
393 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
394 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
395 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33,
396 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x41
397 // FIXME <rdar://problem/5252577> gmail does not accept legal characters in the form boundary
398 // As stated above, some legal characters cause, sites to fail. Specifically
399 // the / character which was the last character in the above array. I have
400 // replaced the last character with another character already in the array
401 // (notice the first and last values are both 0x41, A). Instead of picking
402 // another unique legal character for boundary strings that, because it has
403 // never been tested, may or may not break other sites, I simply
404 // replaced / with A. This means A is twice as likely to occur in our boundary
405 // strings than any other character but I think this is fine for the time being.
406 // The FIXME here is about restoring the / character once the aforementioned
407 // radar has been resolved.
410 // Start with an informative prefix.
411 const char boundaryPrefix[] = "----WebKitFormBoundary";
412 boundary.append(boundaryPrefix, strlen(boundaryPrefix));
414 // Append 16 random 7bit ascii AlphaNumeric characters.
415 Vector<char> randomBytes;
417 for (int i = 0; i < 4; ++i) {
418 int randomness = randomNumber();
419 randomBytes.append(AlphaNumericEncMap[(randomness >> 24) & 0x3F]);
420 randomBytes.append(AlphaNumericEncMap[(randomness >> 16) & 0x3F]);
421 randomBytes.append(AlphaNumericEncMap[(randomness >> 8) & 0x3F]);
422 randomBytes.append(AlphaNumericEncMap[randomness & 0x3F]);
425 boundary.append(randomBytes);
426 boundary.append(0); // Add a 0 at the end so we can use this as a C-style string.
429 void HTMLFormElement::submit(Event* event, bool activateSubmitButton)
431 FrameView* view = document()->view();
432 Frame* frame = document()->frame();
437 m_doingsubmit = true;
443 HTMLGenericFormElement* firstSuccessfulSubmitButton = 0;
444 bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button?
446 frame->loader()->clearRecordedFormValues();
447 for (unsigned i = 0; i < formElements.size(); ++i) {
448 HTMLGenericFormElement* control = formElements[i];
449 if (control->hasLocalName(inputTag)) {
450 HTMLInputElement* input = static_cast<HTMLInputElement*>(control);
451 if (input->isTextField()) {
452 frame->loader()->recordFormValue(input->name(), input->value(), this);
453 if (input->isSearchField())
454 input->addSearchResult();
457 if (needButtonActivation) {
458 if (control->isActivatedSubmit())
459 needButtonActivation = false;
460 else if (firstSuccessfulSubmitButton == 0 && control->isSuccessfulSubmitButton())
461 firstSuccessfulSubmitButton = control;
465 if (needButtonActivation && firstSuccessfulSubmitButton)
466 firstSuccessfulSubmitButton->setActivatedSubmit(true);
469 m_url = document()->url().string();
472 if (m_multipart && isMailtoForm()) {
473 setEnctype("application/x-www-form-urlencoded");
478 RefPtr<FormData> data = formData(0);
479 if (isMailtoForm()) {
480 String body = data->flattenToString();
481 if (equalIgnoringCase(enctype(), "text/plain")) {
482 // Convention seems to be to decode, and s/&/\r\n/. Also, spaces are encoded as %20.
483 body = decodeURLEscapeSequences(body.replace('&', "\r\n").replace('+', ' ') + "\r\n");
485 Vector<char> bodyData;
486 appendString(bodyData, "body=");
487 appendEncodedString(bodyData, body.utf8());
488 data = FormData::create(String(bodyData.data(), bodyData.size()).replace('+', "%20").latin1());
490 frame->loader()->submitForm("POST", m_url, data, m_target, enctype(), String(), event);
492 Vector<char> boundary;
493 getUniqueBoundaryString(boundary);
494 frame->loader()->submitForm("POST", m_url, formData(boundary.data()), m_target, enctype(), boundary.data(), event);
498 frame->loader()->submitForm("GET", m_url, formData(0), m_target, String(), String(), event);
501 if (needButtonActivation && firstSuccessfulSubmitButton)
502 firstSuccessfulSubmitButton->setActivatedSubmit(false);
504 m_doingsubmit = m_insubmit = false;
507 void HTMLFormElement::reset()
509 Frame* frame = document()->frame();
510 if (m_inreset || !frame)
515 // ### DOM2 labels this event as not cancelable, however
516 // common browsers( sick! ) allow it be cancelled.
517 if ( !dispatchHTMLEvent(resetEvent,true, true) ) {
522 for (unsigned i = 0; i < formElements.size(); ++i)
523 formElements[i]->reset();
528 void HTMLFormElement::parseMappedAttribute(MappedAttribute* attr)
530 if (attr->name() == actionAttr)
531 m_url = parseURL(attr->value());
532 else if (attr->name() == targetAttr)
533 m_target = attr->value();
534 else if (attr->name() == methodAttr) {
535 if (equalIgnoringCase(attr->value(), "post"))
537 else if (equalIgnoringCase(attr->value(), "get"))
539 } else if (attr->name() == enctypeAttr)
540 parseEnctype(attr->value());
541 else if (attr->name() == accept_charsetAttr)
542 // space separated list of charsets the server
543 // accepts - see rfc2045
544 m_acceptcharset = attr->value();
545 else if (attr->name() == acceptAttr) {
546 // ignore this one for the moment...
547 } else if (attr->name() == autocompleteAttr)
548 m_autocomplete = !equalIgnoringCase(attr->value(), "off");
549 else if (attr->name() == onsubmitAttr)
550 setHTMLEventListener(submitEvent, attr);
551 else if (attr->name() == onresetAttr)
552 setHTMLEventListener(resetEvent, attr);
553 else if (attr->name() == nameAttr) {
554 const AtomicString& newName = attr->value();
555 if (inDocument() && document()->isHTMLDocument()) {
556 HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
557 document->removeNamedItem(m_name);
558 document->addNamedItem(newName);
562 HTMLElement::parseMappedAttribute(attr);
565 template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T* item)
567 size_t size = vec.size();
568 for (size_t i = 0; i != size; ++i)
569 if (vec[i] == item) {
575 unsigned HTMLFormElement::formElementIndex(HTMLGenericFormElement* e)
577 // Check for the special case where this element is the very last thing in
578 // the form's tree of children; we don't want to walk the entire tree in that
579 // common case that occurs during parsing; instead we'll just return a value
580 // that says "add this form element to the end of the array".
581 if (e->traverseNextNode(this)) {
583 for (Node* node = this; node; node = node->traverseNextNode(this)) {
586 if (node->isHTMLElement()
587 && static_cast<HTMLElement*>(node)->isGenericFormElement()
588 && static_cast<HTMLGenericFormElement*>(node)->form() == this)
592 return formElements.size();
595 void HTMLFormElement::registerFormElement(HTMLGenericFormElement* e)
597 document()->checkedRadioButtons().removeButton(e);
598 m_checkedRadioButtons.addButton(e);
599 formElements.insert(formElementIndex(e), e);
602 void HTMLFormElement::removeFormElement(HTMLGenericFormElement* e)
604 m_checkedRadioButtons.removeButton(e);
605 removeFromVector(formElements, e);
608 bool HTMLFormElement::isURLAttribute(Attribute* attr) const
610 return attr->name() == actionAttr;
613 void HTMLFormElement::registerImgElement(HTMLImageElement* e)
615 imgElements.append(e);
618 void HTMLFormElement::removeImgElement(HTMLImageElement* e)
620 removeFromVector(imgElements, e);
623 PassRefPtr<HTMLCollection> HTMLFormElement::elements()
625 return new HTMLFormCollection(this);
628 String HTMLFormElement::name() const
630 return getAttribute(nameAttr);
633 void HTMLFormElement::setName(const String &value)
635 setAttribute(nameAttr, value);
638 String HTMLFormElement::acceptCharset() const
640 return getAttribute(accept_charsetAttr);
643 void HTMLFormElement::setAcceptCharset(const String &value)
645 setAttribute(accept_charsetAttr, value);
648 String HTMLFormElement::action() const
650 return getAttribute(actionAttr);
653 void HTMLFormElement::setAction(const String &value)
655 setAttribute(actionAttr, value);
658 void HTMLFormElement::setEnctype(const String &value)
660 setAttribute(enctypeAttr, value);
663 String HTMLFormElement::method() const
665 return getAttribute(methodAttr);
668 void HTMLFormElement::setMethod(const String &value)
670 setAttribute(methodAttr, value);
673 String HTMLFormElement::target() const
675 return getAttribute(targetAttr);
678 void HTMLFormElement::setTarget(const String &value)
680 setAttribute(targetAttr, value);
683 PassRefPtr<HTMLGenericFormElement> HTMLFormElement::elementForAlias(const AtomicString& alias)
685 if (alias.isEmpty() || !m_elementAliases)
687 return m_elementAliases->get(alias.impl());
690 void HTMLFormElement::addElementAlias(HTMLGenericFormElement* element, const AtomicString& alias)
694 if (!m_elementAliases)
695 m_elementAliases = new AliasMap;
696 m_elementAliases->set(alias.impl(), element);
699 void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems)
701 elements()->namedItems(name, namedItems);
703 // see if we have seen something with this name before
704 RefPtr<HTMLGenericFormElement> aliasElem;
705 if (aliasElem = elementForAlias(name)) {
707 for (unsigned n = 0; n < namedItems.size(); n++) {
708 if (namedItems[n] == aliasElem.get()) {
714 // we have seen it before but it is gone now. still, we need to return it.
715 namedItems.append(aliasElem.get());
717 // name has been accessed, remember it
718 if (namedItems.size() && aliasElem != namedItems.first())
719 addElementAlias(static_cast<HTMLGenericFormElement*>(namedItems.first().get()), name);
722 void HTMLFormElement::CheckedRadioButtons::addButton(HTMLGenericFormElement* element)
724 // We only want to add radio buttons.
725 if (!element->isRadioButton())
728 // Without a name, there is no group.
729 if (element->name().isEmpty())
732 HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(element);
734 // We only track checked buttons.
735 if (!inputElement->checked())
738 if (!m_nameToCheckedRadioButtonMap)
739 m_nameToCheckedRadioButtonMap.set(new NameToInputMap);
741 pair<NameToInputMap::iterator, bool> result = m_nameToCheckedRadioButtonMap->add(element->name().impl(), inputElement);
745 HTMLInputElement* oldCheckedButton = result.first->second;
746 if (oldCheckedButton == inputElement)
749 result.first->second = inputElement;
750 oldCheckedButton->setChecked(false);
753 HTMLInputElement* HTMLFormElement::CheckedRadioButtons::checkedButtonForGroup(const AtomicString& name) const
755 if (!m_nameToCheckedRadioButtonMap)
758 return m_nameToCheckedRadioButtonMap->get(name.impl());
761 void HTMLFormElement::CheckedRadioButtons::removeButton(HTMLGenericFormElement* element)
763 if (element->name().isEmpty() || !m_nameToCheckedRadioButtonMap)
766 NameToInputMap::iterator it = m_nameToCheckedRadioButtonMap->find(element->name().impl());
767 if (it == m_nameToCheckedRadioButtonMap->end() || it->second != element)
770 ASSERT(element->isRadioButton());
771 ASSERT(element->isChecked());
773 m_nameToCheckedRadioButtonMap->remove(it);
774 if (m_nameToCheckedRadioButtonMap->isEmpty())
775 m_nameToCheckedRadioButtonMap.clear();