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