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