2008-04-24 Adele Peterson <adele@apple.com>
[WebKit-https.git] / WebCore / html / HTMLFormElement.cpp
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 "Event.h"
30 #include "EventNames.h"
31 #include "FileSystem.h"
32 #include "FormData.h"
33 #include "FormDataList.h"
34 #include "Frame.h"
35 #include "FrameLoader.h"
36 #include "HTMLDocument.h"
37 #include "HTMLFormCollection.h"
38 #include "HTMLImageElement.h"
39 #include "HTMLInputElement.h"
40 #include "HTMLNames.h"
41 #include "MIMETypeRegistry.h"
42 #include "RenderTextControl.h"
43
44 #if PLATFORM(QT)
45 #include <QtCore/QFileInfo>
46 #endif
47
48 #if PLATFORM(WX)
49 #include <wx/defs.h>
50 #include <wx/filename.h>
51 #endif
52
53 #if PLATFORM(WIN_OS)
54 #include <shlwapi.h>
55 #endif
56
57 namespace WebCore {
58
59 using namespace EventNames;
60 using namespace HTMLNames;
61
62 static const char hexDigits[17] = "0123456789ABCDEF";
63
64 HTMLFormElement::HTMLFormElement(Document* doc)
65     : HTMLElement(formTag, doc)
66     , m_elementAliases(0)
67     , collectionInfo(0)
68     , m_enctype("application/x-www-form-urlencoded")
69     , m_post(false)
70     , m_multipart(false)
71     , m_autocomplete(true)
72     , m_insubmit(false)
73     , m_doingsubmit(false)
74     , m_inreset(false)
75     , m_malformed(false)
76 {
77 }
78
79 HTMLFormElement::~HTMLFormElement()
80 {
81     delete m_elementAliases;
82     delete collectionInfo;
83     
84     for (unsigned i = 0; i < formElements.size(); ++i)
85         formElements[i]->formDestroyed();
86     for (unsigned i = 0; i < imgElements.size(); ++i)
87         imgElements[i]->m_form = 0;
88 }
89
90 bool HTMLFormElement::formWouldHaveSecureSubmission(const String& url)
91 {
92     return document()->completeURL(url).protocolIs("https");
93 }
94
95 void HTMLFormElement::attach()
96 {
97     HTMLElement::attach();
98 }
99
100 void HTMLFormElement::insertedIntoDocument()
101 {
102     if (document()->isHTMLDocument())
103         static_cast<HTMLDocument*>(document())->addNamedItem(m_name);
104
105     HTMLElement::insertedIntoDocument();
106 }
107
108 void HTMLFormElement::removedFromDocument()
109 {
110     if (document()->isHTMLDocument())
111         static_cast<HTMLDocument*>(document())->removeNamedItem(m_name);
112    
113     HTMLElement::removedFromDocument();
114 }
115
116 void HTMLFormElement::handleLocalEvents(Event* event, bool useCapture)
117 {
118     EventTargetNode* targetNode = event->target()->toNode();
119     if (!useCapture && targetNode && targetNode != this && (event->type() == submitEvent || event->type() == resetEvent)) {
120         event->stopPropagation();
121         return;
122     }
123     HTMLElement::handleLocalEvents(event, useCapture);
124 }
125
126 unsigned HTMLFormElement::length() const
127 {
128     int len = 0;
129     for (unsigned i = 0; i < formElements.size(); ++i)
130         if (formElements[i]->isEnumeratable())
131             ++len;
132
133     return len;
134 }
135
136 Node* HTMLFormElement::item(unsigned index)
137 {
138     return elements()->item(index);
139 }
140
141 void HTMLFormElement::submitClick(Event* event)
142 {
143     bool submitFound = false;
144     for (unsigned i = 0; i < formElements.size(); ++i) {
145         if (formElements[i]->hasLocalName(inputTag)) {
146             HTMLInputElement* element = static_cast<HTMLInputElement*>(formElements[i]);
147             if (element->isSuccessfulSubmitButton() && element->renderer()) {
148                 submitFound = true;
149                 element->dispatchSimulatedClick(event);
150                 break;
151             }
152         }
153     }
154     if (!submitFound) // submit the form without a submit or image input
155         prepareSubmit(event);
156 }
157
158 static void appendString(Vector<char>& buffer, const char* string)
159 {
160     buffer.append(string, strlen(string));
161 }
162
163 static void appendString(Vector<char>& buffer, const CString& string)
164 {
165     buffer.append(string.data(), string.length());
166 }
167
168 static void appendEncodedString(Vector<char>& buffer, const CString& string)
169 {
170     // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
171     int length = string.length();
172     for (int i = 0; i < length; i++) {
173         unsigned char c = string.data()[i];
174
175         // Same safe characters as Netscape for compatibility.
176         static const char safe[] = "-._*";
177         if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || strchr(safe, c))
178             buffer.append(c);
179         else if (c == ' ')
180             buffer.append('+');
181         else if (c == '\n' || (c == '\r' && (i + 1 >= length || string.data()[i + 1] != '\n')))
182             appendString(buffer, "%0D%0A");
183         else if (c != '\r') {
184             buffer.append('%');
185             buffer.append(hexDigits[c >> 4]);
186             buffer.append(hexDigits[c & 0xF]);
187         }
188     }
189 }
190
191 // FIXME: Move to platform directory?
192 static int randomNumber()
193 {
194     static bool randomSeeded = false;
195
196 #if PLATFORM(DARWIN)
197     if (!randomSeeded) {
198         srandomdev();
199         randomSeeded = true;
200     }
201     return random();
202 #else
203     if (!randomSeeded) {
204         srand(static_cast<unsigned>(time(0)));
205         randomSeeded = true;
206     }
207     return rand();
208 #endif
209 }
210
211 TextEncoding HTMLFormElement::dataEncoding() const
212 {
213     if (isMailtoForm())
214         return UTF8Encoding();
215
216     TextEncoding encoding;
217     String str = m_acceptcharset;
218     str.replace(',', ' ');
219     Vector<String> charsets;
220     str.split(' ', charsets);
221     Vector<String>::const_iterator end = charsets.end();
222     for (Vector<String>::const_iterator it = charsets.begin(); it != end; ++it)
223         if ((encoding = TextEncoding(*it)).isValid())
224             return encoding;
225     if (Frame* frame = document()->frame())
226         return frame->loader()->encoding();
227     return Latin1Encoding();
228 }
229
230 PassRefPtr<FormData> HTMLFormElement::formData(const char* boundary) const
231 {
232     Vector<char> encodedData;
233     TextEncoding encoding = dataEncoding();
234
235     RefPtr<FormData> result = FormData::create();
236     
237     for (unsigned i = 0; i < formElements.size(); ++i) {
238         HTMLGenericFormElement* control = formElements[i];
239         FormDataList list(encoding);
240
241         if (!control->disabled() && control->appendFormData(list, m_multipart)) {
242             size_t ln = list.list().size();
243             for (size_t j = 0; j < ln; ++j) {
244                 const FormDataListItem& item = list.list()[j];
245                 if (!m_multipart) {
246                     // Omit the name "isindex" if it's the first form data element.
247                     // FIXME: Why is this a good rule? Is this obsolete now?
248                     if (encodedData.isEmpty() && item.m_data == "isindex")
249                         appendEncodedString(encodedData, list.list()[++j].m_data);
250                     else {
251                         if (!encodedData.isEmpty())
252                             encodedData.append('&');
253                         appendEncodedString(encodedData, item.m_data);
254                         encodedData.append('=');
255                         appendEncodedString(encodedData, list.list()[++j].m_data);
256                     }
257                 } else {
258                     Vector<char> header;
259                     appendString(header, "--");
260                     appendString(header, boundary);
261                     appendString(header, "\r\n");
262                     appendString(header, "Content-Disposition: form-data; name=\"");
263                     header.append(item.m_data.data(), item.m_data.length());
264                     header.append('"');
265
266                     // if the current type is FILE, then we also need to
267                     // include the filename
268                     if (control->hasLocalName(inputTag)
269                             && static_cast<HTMLInputElement*>(control)->inputType() == HTMLInputElement::FILE) {
270                         String path = static_cast<HTMLInputElement*>(control)->value();
271                         String filename = pathGetFileName(path);
272
273                         // FIXME: This won't work if the filename includes a " mark,
274                         // or control characters like CR or LF. This also does strange
275                         // things if the filename includes characters you can't encode
276                         // in the website's character set.
277                         appendString(header, "; filename=\"");
278                         appendString(header, encoding.encode(filename.characters(), filename.length(), QuestionMarksForUnencodables));
279                         header.append('"');
280
281                         if (!path.isEmpty()) {
282                             String mimeType = MIMETypeRegistry::getMIMETypeForPath(path);
283                             if (!mimeType.isEmpty()) {
284                                 appendString(header, "\r\nContent-Type: ");
285                                 appendString(header, mimeType.latin1());
286                             }
287                         }
288                     }
289
290                     appendString(header, "\r\n\r\n");
291
292                     // append body
293                     result->appendData(header.data(), header.size());
294                     const FormDataListItem& item = list.list()[j + 1];
295                     if (size_t dataSize = item.m_data.length())
296                         result->appendData(item.m_data.data(), dataSize);
297                     else if (!item.m_path.isEmpty())
298                         result->appendFile(item.m_path);
299                     result->appendData("\r\n", 2);
300
301                     ++j;
302                 }
303             }
304         }
305     }
306
307
308     if (m_multipart) {
309         appendString(encodedData, "--");
310         appendString(encodedData, boundary);
311         appendString(encodedData, "--\r\n");
312     }
313
314     result->appendData(encodedData.data(), encodedData.size());
315     return result;
316 }
317
318 void HTMLFormElement::parseEnctype(const String& type)
319 {
320     if(type.contains("multipart", false) || type.contains("form-data", false)) {
321         m_enctype = "multipart/form-data";
322         m_multipart = true;
323     } else if (type.contains("text", false) || type.contains("plain", false)) {
324         m_enctype = "text/plain";
325         m_multipart = false;
326     } else {
327         m_enctype = "application/x-www-form-urlencoded";
328         m_multipart = false;
329     }
330 }
331
332 bool HTMLFormElement::isMailtoForm() const
333 {
334     return protocolIs(m_url, "mailto");
335 }
336
337 bool HTMLFormElement::prepareSubmit(Event* event)
338 {
339     Frame* frame = document()->frame();
340     if (m_insubmit || !frame)
341         return m_insubmit;
342
343     m_insubmit = true;
344     m_doingsubmit = false;
345
346     if (dispatchHTMLEvent(submitEvent, true, true) && !m_doingsubmit)
347         m_doingsubmit = true;
348
349     m_insubmit = false;
350
351     if (m_doingsubmit)
352         submit(event, true);
353
354     return m_doingsubmit;
355 }
356
357 void HTMLFormElement::submit()
358 {
359     submit(0, false);
360 }
361
362 // Returns a 0-terminated C string in the vector.
363 static void getUniqueBoundaryString(Vector<char>& boundary)
364 {
365     // The RFC 2046 spec says the AlphaNumeric characters plus the following characters
366     // are legal for boundaries:  '()+_,-./:=?
367     // However the following characters, though legal, cause some sites to fail:
368     // (),./:=
369     // http://bugs.webkit.org/show_bug.cgi?id=13352
370     static const char AlphaNumericEncMap[64] =
371     {
372       0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
373       0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50,
374       0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
375       0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66,
376       0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E,
377       0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
378       0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33,
379       0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x41
380       // FIXME <rdar://problem/5252577> gmail does not accept legal characters in the form boundary
381       // As stated above, some legal characters cause, sites to fail. Specifically
382       // the / character which was the last character in the above array. I have
383       // replaced the last character with another character already in the array
384       // (notice the first and last values are both 0x41, A). Instead of picking
385       // another unique legal character for boundary strings that, because it has
386       // never been tested, may or may not break other sites, I simply
387       // replaced / with A.  This means A is twice as likely to occur in our boundary
388       // strings than any other character but I think this is fine for the time being.
389       // The FIXME here is about restoring the / character once the aforementioned
390       // radar has been resolved.
391     };
392
393     // Start with an informative prefix.
394     const char boundaryPrefix[] = "----WebKitFormBoundary";
395     boundary.append(boundaryPrefix, strlen(boundaryPrefix));
396
397     // Append 16 random 7bit ascii AlphaNumeric characters.
398     Vector<char> randomBytes;
399
400     for (int i = 0; i < 4; ++i) {
401         int randomness = randomNumber();
402         randomBytes.append(AlphaNumericEncMap[(randomness >> 24) & 0x3F]);
403         randomBytes.append(AlphaNumericEncMap[(randomness >> 16) & 0x3F]);
404         randomBytes.append(AlphaNumericEncMap[(randomness >> 8) & 0x3F]);
405         randomBytes.append(AlphaNumericEncMap[randomness & 0x3F]);
406     }
407
408     boundary.append(randomBytes);
409     boundary.append(0); // Add a 0 at the end so we can use this as a C-style string.
410 }
411
412 void HTMLFormElement::submit(Event* event, bool activateSubmitButton)
413 {
414     FrameView* view = document()->view();
415     Frame* frame = document()->frame();
416     if (!view || !frame)
417         return;
418
419     if (m_insubmit) {
420         m_doingsubmit = true;
421         return;
422     }
423
424     m_insubmit = true;
425
426     HTMLGenericFormElement* firstSuccessfulSubmitButton = 0;
427     bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button?
428     
429     frame->loader()->clearRecordedFormValues();
430     for (unsigned i = 0; i < formElements.size(); ++i) {
431         HTMLGenericFormElement* control = formElements[i];
432         if (control->hasLocalName(inputTag)) {
433             HTMLInputElement* input = static_cast<HTMLInputElement*>(control);
434             if (input->isTextField()) {
435                 frame->loader()->recordFormValue(input->name(), input->value(), this);
436                 if (input->isSearchField())
437                     input->addSearchResult();
438             }
439         }
440         if (needButtonActivation) {
441             if (control->isActivatedSubmit())
442                 needButtonActivation = false;
443             else if (firstSuccessfulSubmitButton == 0 && control->isSuccessfulSubmitButton())
444                 firstSuccessfulSubmitButton = control;
445         }
446     }
447
448     if (needButtonActivation && firstSuccessfulSubmitButton)
449         firstSuccessfulSubmitButton->setActivatedSubmit(true);
450     
451     if (!m_url)
452         m_url = document()->url().string();
453
454     if (m_post) {
455         if (m_multipart && isMailtoForm()) {
456             setEnctype("application/x-www-form-urlencoded");
457             m_multipart = false;
458         }
459
460         if (!m_multipart) {
461             RefPtr<FormData> data = formData(0);
462             if (isMailtoForm()) {
463                 String body = data->flattenToString();
464                 if (equalIgnoringCase(enctype(), "text/plain")) {
465                     // Convention seems to be to decode, and s/&/\r\n/. Also, spaces are encoded as %20.
466                     body = decodeURLEscapeSequences(body.replace('&', "\r\n").replace('+', ' ') + "\r\n");
467                 }
468                 Vector<char> bodyData;
469                 appendString(bodyData, "body=");
470                 appendEncodedString(bodyData, body.utf8());
471                 data = FormData::create(String(bodyData.data(), bodyData.size()).replace('+', "%20").latin1());
472             }
473             frame->loader()->submitForm("POST", m_url, data, m_target, enctype(), String(), event);
474         } else {
475             Vector<char> boundary;
476             getUniqueBoundaryString(boundary);
477             frame->loader()->submitForm("POST", m_url, formData(boundary.data()), m_target, enctype(), boundary.data(), event);
478         }
479     } else {
480         m_multipart = false;
481         frame->loader()->submitForm("GET", m_url, formData(0), m_target, String(), String(), event);
482     }
483
484     if (needButtonActivation && firstSuccessfulSubmitButton)
485         firstSuccessfulSubmitButton->setActivatedSubmit(false);
486     
487     m_doingsubmit = m_insubmit = false;
488 }
489
490 void HTMLFormElement::reset()
491 {
492     Frame* frame = document()->frame();
493     if (m_inreset || !frame)
494         return;
495
496     m_inreset = true;
497
498     // ### DOM2 labels this event as not cancelable, however
499     // common browsers( sick! ) allow it be cancelled.
500     if ( !dispatchHTMLEvent(resetEvent,true, true) ) {
501         m_inreset = false;
502         return;
503     }
504
505     for (unsigned i = 0; i < formElements.size(); ++i)
506         formElements[i]->reset();
507
508     m_inreset = false;
509 }
510
511 void HTMLFormElement::parseMappedAttribute(MappedAttribute* attr)
512 {
513     if (attr->name() == actionAttr)
514         m_url = parseURL(attr->value());
515     else if (attr->name() == targetAttr)
516         m_target = attr->value();
517     else if (attr->name() == methodAttr) {
518         if (equalIgnoringCase(attr->value(), "post"))
519             m_post = true;
520         else if (equalIgnoringCase(attr->value(), "get"))
521             m_post = false;
522     } else if (attr->name() == enctypeAttr)
523         parseEnctype(attr->value());
524     else if (attr->name() == accept_charsetAttr)
525         // space separated list of charsets the server
526         // accepts - see rfc2045
527         m_acceptcharset = attr->value();
528     else if (attr->name() == acceptAttr) {
529         // ignore this one for the moment...
530     } else if (attr->name() == autocompleteAttr)
531         m_autocomplete = !equalIgnoringCase(attr->value(), "off");
532     else if (attr->name() == onsubmitAttr)
533         setHTMLEventListener(submitEvent, attr);
534     else if (attr->name() == onresetAttr)
535         setHTMLEventListener(resetEvent, attr);
536     else if (attr->name() == nameAttr) {
537         const AtomicString& newName = attr->value();
538         if (inDocument() && document()->isHTMLDocument()) {
539             HTMLDocument* document = static_cast<HTMLDocument*>(this->document());
540             document->removeNamedItem(m_name);
541             document->addNamedItem(newName);
542         }
543         m_name = newName;
544     } else
545         HTMLElement::parseMappedAttribute(attr);
546 }
547
548 template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T* item)
549 {
550     size_t size = vec.size();
551     for (size_t i = 0; i != size; ++i)
552         if (vec[i] == item) {
553             vec.remove(i);
554             break;
555         }
556 }
557
558 unsigned HTMLFormElement::formElementIndex(HTMLGenericFormElement* e)
559 {
560     // Check for the special case where this element is the very last thing in
561     // the form's tree of children; we don't want to walk the entire tree in that
562     // common case that occurs during parsing; instead we'll just return a value
563     // that says "add this form element to the end of the array".
564     if (e->traverseNextNode(this)) {
565         unsigned i = 0;
566         for (Node* node = this; node; node = node->traverseNextNode(this)) {
567             if (node == e)
568                 return i;
569             if (node->isHTMLElement()
570                     && static_cast<HTMLElement*>(node)->isGenericFormElement()
571                     && static_cast<HTMLGenericFormElement*>(node)->form() == this)
572                 ++i;
573         }
574     }
575     return formElements.size();
576 }
577
578 void HTMLFormElement::registerFormElement(HTMLGenericFormElement* e)
579 {
580     document()->checkedRadioButtons().removeButton(e);
581     m_checkedRadioButtons.addButton(e);
582     formElements.insert(formElementIndex(e), e);
583 }
584
585 void HTMLFormElement::removeFormElement(HTMLGenericFormElement* e)
586 {
587     m_checkedRadioButtons.removeButton(e);
588     removeFromVector(formElements, e);
589 }
590
591 bool HTMLFormElement::isURLAttribute(Attribute* attr) const
592 {
593     return attr->name() == actionAttr;
594 }
595
596 void HTMLFormElement::registerImgElement(HTMLImageElement* e)
597 {
598     imgElements.append(e);
599 }
600
601 void HTMLFormElement::removeImgElement(HTMLImageElement* e)
602 {
603     removeFromVector(imgElements, e);
604 }
605
606 PassRefPtr<HTMLCollection> HTMLFormElement::elements()
607 {
608     return new HTMLFormCollection(this);
609 }
610
611 String HTMLFormElement::name() const
612 {
613     return getAttribute(nameAttr);
614 }
615
616 void HTMLFormElement::setName(const String &value)
617 {
618     setAttribute(nameAttr, value);
619 }
620
621 String HTMLFormElement::acceptCharset() const
622 {
623     return getAttribute(accept_charsetAttr);
624 }
625
626 void HTMLFormElement::setAcceptCharset(const String &value)
627 {
628     setAttribute(accept_charsetAttr, value);
629 }
630
631 String HTMLFormElement::action() const
632 {
633     return getAttribute(actionAttr);
634 }
635
636 void HTMLFormElement::setAction(const String &value)
637 {
638     setAttribute(actionAttr, value);
639 }
640
641 void HTMLFormElement::setEnctype(const String &value)
642 {
643     setAttribute(enctypeAttr, value);
644 }
645
646 String HTMLFormElement::method() const
647 {
648     return getAttribute(methodAttr);
649 }
650
651 void HTMLFormElement::setMethod(const String &value)
652 {
653     setAttribute(methodAttr, value);
654 }
655
656 String HTMLFormElement::target() const
657 {
658     return getAttribute(targetAttr);
659 }
660
661 void HTMLFormElement::setTarget(const String &value)
662 {
663     setAttribute(targetAttr, value);
664 }
665
666 PassRefPtr<HTMLGenericFormElement> HTMLFormElement::elementForAlias(const AtomicString& alias)
667 {
668     if (alias.isEmpty() || !m_elementAliases)
669         return 0;
670     return m_elementAliases->get(alias.impl());
671 }
672
673 void HTMLFormElement::addElementAlias(HTMLGenericFormElement* element, const AtomicString& alias)
674 {
675     if (alias.isEmpty())
676         return;
677     if (!m_elementAliases)
678         m_elementAliases = new AliasMap;
679     m_elementAliases->set(alias.impl(), element);
680 }
681
682 void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems)
683 {
684     elements()->namedItems(name, namedItems);
685
686     // see if we have seen something with this name before
687     RefPtr<HTMLGenericFormElement> aliasElem;
688     if (aliasElem = elementForAlias(name)) {
689         bool found = false;
690         for (unsigned n = 0; n < namedItems.size(); n++) {
691             if (namedItems[n] == aliasElem.get()) {
692                 found = true;
693                 break;
694             }
695         }
696         if (!found)
697             // we have seen it before but it is gone now. still, we need to return it.
698             namedItems.append(aliasElem.get());
699     }
700     // name has been accessed, remember it
701     if (namedItems.size() && aliasElem != namedItems.first())
702         addElementAlias(static_cast<HTMLGenericFormElement*>(namedItems.first().get()), name);        
703 }
704
705 void HTMLFormElement::CheckedRadioButtons::addButton(HTMLGenericFormElement* element)
706 {
707     // We only want to add radio buttons.
708     if (!element->isRadioButton())
709         return;
710
711     // Without a name, there is no group.
712     if (element->name().isEmpty())
713         return;
714
715     HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(element);
716
717     // We only track checked buttons.
718     if (!inputElement->checked())
719         return;
720
721     if (!m_nameToCheckedRadioButtonMap)
722         m_nameToCheckedRadioButtonMap.set(new NameToInputMap);
723
724     pair<NameToInputMap::iterator, bool> result = m_nameToCheckedRadioButtonMap->add(element->name().impl(), inputElement);
725     if (result.second)
726         return;
727     
728     HTMLInputElement* oldCheckedButton = result.first->second;
729     if (oldCheckedButton == inputElement)
730         return;
731
732     result.first->second = inputElement;
733     oldCheckedButton->setChecked(false);
734 }
735
736 HTMLInputElement* HTMLFormElement::CheckedRadioButtons::checkedButtonForGroup(const AtomicString& name) const
737 {
738     if (!m_nameToCheckedRadioButtonMap)
739         return 0;
740     
741     return m_nameToCheckedRadioButtonMap->get(name.impl());
742 }
743
744 void HTMLFormElement::CheckedRadioButtons::removeButton(HTMLGenericFormElement* element)
745 {
746     if (element->name().isEmpty() || !m_nameToCheckedRadioButtonMap)
747         return;
748     
749     NameToInputMap::iterator it = m_nameToCheckedRadioButtonMap->find(element->name().impl());
750     if (it == m_nameToCheckedRadioButtonMap->end() || it->second != element)
751         return;
752     
753     ASSERT(element->isRadioButton());
754     ASSERT(element->isChecked());
755     
756     m_nameToCheckedRadioButtonMap->remove(it);
757     if (m_nameToCheckedRadioButtonMap->isEmpty())
758         m_nameToCheckedRadioButtonMap.clear();
759 }
760
761 } // namespace