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