• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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