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