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.
26 #include "HTMLFormElement.h"
28 #include "CSSHelper.h"
29 #include "ChromeClient.h"
32 #include "EventNames.h"
34 #include "FileSystem.h"
36 #include "FormDataList.h"
37 #include "FormState.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"
49 #include "RenderTextControl.h"
50 #include "ValidityState.h"
52 #include <wtf/CurrentTime.h>
53 #include <wtf/RandomNumber.h>
57 #include <wx/filename.h>
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
)
78 , m_autocomplete(true)
80 , m_doingsubmit(false)
85 ASSERT(hasTagName(formTag
));
88 HTMLFormElement::~HTMLFormElement()
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
)
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
)
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();
160 HTMLElement::handleLocalEvents(event
);
163 unsigned HTMLFormElement::length() const
166 for (unsigned i
= 0; i
< formElements
.size(); ++i
)
167 if (formElements
[i
]->isEnumeratable())
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()) {
186 element
->dispatchSimulatedClick(event
);
191 if (!submitFound
) // submit the form without a submit or image input
192 prepareSubmit(event
);
195 TextEncoding
HTMLFormElement::dataEncoding() const
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());
226 m_formDataBuilder
.addKeyValuePairAsFormData(encodedData
, key
.data(), value
.data());
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
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
);
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());
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
)
297 m_doingsubmit
= false;
299 if (dispatchEvent(Event::create(eventNames().submitEvent
, true, true)) && !m_doingsubmit
)
300 m_doingsubmit
= true;
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())
332 void HTMLFormElement::submit(Frame
* javaScriptActiveFrame
)
334 if (javaScriptActiveFrame
)
335 submit(0, false, !javaScriptActiveFrame
->script()->anyPageIsProcessingUserGesture(), SubmittedByJavaScript
);
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();
348 m_doingsubmit
= 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);
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());
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());
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
)
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))) {
432 for (unsigned i
= 0; i
< formElements
.size(); ++i
)
433 formElements
[i
]->reset();
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");
457 document()->registerForDocumentActivationCallbacks(this);
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
);
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
) {
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)) {
494 for (Node
* node
= this; node
; node
= node
->traverseNextNode(this)) {
497 if (node
->isHTMLElement()
498 && static_cast<Element
*>(node
)->isFormControlElement()
499 && static_cast<HTMLFormControlElement
*>(node
)->form() == this)
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())
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
)
628 return m_elementAliases
->get(alias
.impl());
631 void HTMLFormElement::addElementAlias(HTMLFormControlElement
* element
, const AtomicString
& alias
)
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
)) {
648 for (unsigned n
= 0; n
< namedItems
.size(); n
++) {
649 if (namedItems
[n
] == aliasElem
.get()) {
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()
674 document()->unregisterForDocumentActivationCallbacks(this);
675 HTMLElement::willMoveToNewOwnerDocument();
678 void HTMLFormElement::didMoveToNewOwnerDocument()
681 document()->registerForDocumentActivationCallbacks(this);
682 HTMLElement::didMoveToNewOwnerDocument();