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