2c12dc3966a35c05ff57c8c194c209504206143b
[WebKit-https.git] / WebCore / khtml / html / html_formimpl.cpp
1 /*
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  *           (C) 2001 Dirk Mueller (mueller@kde.org)
7  * Copyright (C) 2004 Apple Computer, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  *
24  */
25
26 #undef FORMS_DEBUG
27 //#define FORMS_DEBUG
28
29 #include "html/html_formimpl.h"
30
31 #include "khtmlview.h"
32 #include "khtml_part.h"
33 #include "html/html_documentimpl.h"
34 #include "html_imageimpl.h"
35 #include "khtml_settings.h"
36 #include "misc/htmlhashes.h"
37 #include "misc/formdata.h"
38
39 #include "css/cssstyleselector.h"
40 #include "css/cssproperties.h"
41 #include "css/csshelper.h"
42 #include "xml/dom_textimpl.h"
43 #include "xml/dom2_eventsimpl.h"
44 #include "khtml_ext.h"
45
46 #include "rendering/render_form.h"
47
48 #include <kcharsets.h>
49 #include <kglobal.h>
50 #include <kdebug.h>
51 #include <kmimetype.h>
52 #include <kmessagebox.h>
53 #include <klocale.h>
54 #include <netaccess.h>
55 #include <kfileitem.h>
56 #include <qfile.h>
57 #include <qtextcodec.h>
58
59 // for keygen
60 #include <qstring.h>
61 #include <ksslkeygen.h>
62
63 #include <assert.h>
64
65 using namespace khtml;
66
67 namespace DOM {
68
69 class FormDataList {
70 public:
71     FormDataList(QTextCodec *);
72
73     void appendData(const DOMString &key, const DOMString &value)
74         { appendString(key.string()); appendString(value.string()); }
75     void appendData(const DOMString &key, const QString &value)
76         { appendString(key.string()); appendString(value); }
77     void appendData(const DOMString &key, const QCString &value)
78         { appendString(key.string()); appendString(value); }
79     void appendData(const DOMString &key, int value)
80         { appendString(key.string()); appendString(QString::number(value)); }
81     void appendFile(const DOMString &key, const DOMString &filename);
82
83     // Temporary API.
84     QValueListConstIterator<QCString> begin() const;
85     QValueListConstIterator<QCString> end() const;
86
87 private:
88     void appendString(const QCString &s);
89     void appendString(const QString &s);
90
91     QTextCodec *m_codec;
92     QValueList<QCString> m_strings;
93 };
94
95 HTMLFormElementImpl::HTMLFormElementImpl(DocumentPtr *doc)
96     : HTMLElementImpl(doc)
97 {
98     m_post = false;
99     m_multipart = false;
100     m_autocomplete = true;
101     m_insubmit = false;
102     m_doingsubmit = false;
103     m_inreset = false;
104     m_enctype = "application/x-www-form-urlencoded";
105     m_boundary = "----------0xKhTmLbOuNdArY";
106     m_acceptcharset = "UNKNOWN";
107     m_malformed = false;
108 }
109
110 HTMLFormElementImpl::~HTMLFormElementImpl()
111 {
112     for (unsigned i = 0; i < formElements.count(); ++i)
113         formElements[i]->m_form = 0;
114     for (unsigned i = 0; i < dormantFormElements.count(); ++i)
115         dormantFormElements[i]->m_form = 0;
116     for (unsigned i = 0; i < imgElements.count(); ++i)
117         imgElements[i]->m_form = 0;
118 }
119
120 NodeImpl::Id HTMLFormElementImpl::id() const
121 {
122     return ID_FORM;
123 }
124
125 #if APPLE_CHANGES
126
127 bool HTMLFormElementImpl::formWouldHaveSecureSubmission(const DOMString &url)
128 {
129     if (url.isNull()) {
130         return false;
131     }
132     return getDocument()->completeURL(url.string()).startsWith("https:", false);
133 }
134
135 #endif
136
137 void HTMLFormElementImpl::attach()
138 {
139     HTMLElementImpl::attach();
140
141     if (getDocument()->isHTMLDocument()) {
142         HTMLDocumentImpl *document = static_cast<HTMLDocumentImpl *>(getDocument());
143         document->addNamedImageOrForm(oldNameAttr);
144         document->addNamedImageOrForm(oldIdAttr);
145     }
146
147 #if APPLE_CHANGES
148     // note we don't deal with calling secureFormRemoved() on detach, because the timing
149     // was such that it cleared our state too early
150     if (formWouldHaveSecureSubmission(m_url))
151         getDocument()->secureFormAdded();
152 #endif
153 }
154
155 void HTMLFormElementImpl::detach()
156 {
157     if (getDocument()->isHTMLDocument()) {
158         HTMLDocumentImpl *document = static_cast<HTMLDocumentImpl *>(getDocument());
159         document->removeNamedImageOrForm(oldNameAttr);
160         document->removeNamedImageOrForm(oldIdAttr);
161     }
162
163     HTMLElementImpl::detach();
164 }
165
166 long HTMLFormElementImpl::length() const
167 {
168     int len = 0;
169     for (unsigned i = 0; i < formElements.count(); ++i)
170         if (formElements[i]->isEnumeratable())
171             ++len;
172
173     return len;
174 }
175
176 #if APPLE_CHANGES
177
178 void HTMLFormElementImpl::submitClick()
179 {
180     bool submitFound = false;
181     for (unsigned i = 0; i < formElements.count(); ++i) {
182         if (formElements[i]->id() == ID_INPUT) {
183             HTMLInputElementImpl *element = static_cast<HTMLInputElementImpl *>(formElements[i]);
184             if (element->isSuccessfulSubmitButton() && element->renderer()) {
185                 submitFound = true;
186                 element->click();
187                 break;
188             }
189         }
190     }
191     if (!submitFound) // submit the form without a submit or image input
192         prepareSubmit();
193 }
194
195 #endif // APPLE_CHANGES
196
197 static QCString encodeCString(const QCString& e)
198 {
199     // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
200     // safe characters like NS handles them for compatibility
201     static const char *safe = "-._*";
202     int elen = e.length();
203     QCString encoded(( elen+e.contains( '\n' ) )*3+1);
204     int enclen = 0;
205
206     //QCString orig(e.data(), e.size());
207
208     for(int pos = 0; pos < elen; pos++) {
209         unsigned char c = e[pos];
210
211         if ( (( c >= 'A') && ( c <= 'Z')) ||
212              (( c >= 'a') && ( c <= 'z')) ||
213              (( c >= '0') && ( c <= '9')) ||
214              (strchr(safe, c))
215             )
216             encoded[enclen++] = c;
217         else if ( c == ' ' )
218             encoded[enclen++] = '+';
219         else if ( c == '\n' || ( c == '\r' && e[pos+1] != '\n' ) )
220         {
221             encoded[enclen++] = '%';
222             encoded[enclen++] = '0';
223             encoded[enclen++] = 'D';
224             encoded[enclen++] = '%';
225             encoded[enclen++] = '0';
226             encoded[enclen++] = 'A';
227         }
228         else if ( c != '\r' )
229         {
230             encoded[enclen++] = '%';
231             unsigned int h = c / 16;
232             h += (h > 9) ? ('A' - 10) : '0';
233             encoded[enclen++] = h;
234
235             unsigned int l = c % 16;
236             l += (l > 9) ? ('A' - 10) : '0';
237             encoded[enclen++] = l;
238         }
239     }
240     encoded[enclen++] = '\0';
241     encoded.truncate(enclen);
242
243     return encoded;
244 }
245
246 // Change plain CR and plain LF to CRLF pairs.
247 static QCString fixLineBreaks(const QCString &s)
248 {
249     // Compute the length.
250     unsigned newLen = 0;
251     const char *p = s.data();
252     while (char c = *p++) {
253         if (c == '\r') {
254             // Safe to look ahead because of trailing '\0'.
255             if (*p != '\n') {
256                 // Turn CR into CRLF.
257                 newLen += 2;
258             }
259         } else if (c == '\n') {
260             // Turn LF into CRLF.
261             newLen += 2;
262         } else {
263             // Leave other characters alone.
264             newLen += 1;
265         }
266     }
267     if (newLen == s.length()) {
268         return s;
269     }
270     
271     // Make a copy of the string.
272     p = s.data();
273     QCString result(newLen + 1);
274     char *q = result.data();
275     while (char c = *p++) {
276         if (c == '\r') {
277             // Safe to look ahead because of trailing '\0'.
278             if (*p != '\n') {
279                 // Turn CR into CRLF.
280                 *q++ = '\r';
281                 *q++ = '\n';
282             }
283         } else if (c == '\n') {
284             // Turn LF into CRLF.
285             *q++ = '\r';
286             *q++ = '\n';
287         } else {
288             // Leave other characters alone.
289             *q++ = c;
290         }
291     }
292     return result;
293 }
294
295 #if !APPLE_CHANGES
296
297 void HTMLFormElementImpl::i18nData()
298 {
299     QString foo1 = i18n( "You're about to send data to the Internet "
300                          "via an unencrypted connection. It might be possible "
301                          "for others to see this information.\n"
302                          "Do you want to continue?");
303     QString foo2 = i18n("KDE Web browser");
304     QString foo3 = i18n("When you send a password unencrypted to the Internet, "
305                         "it might be possible for others to capture it as plain text.\n"
306                         "Do you want to continue?");
307     QString foo5 = i18n("Your data submission is redirected to "
308                         "an insecure site. The data is sent unencrypted.\n"
309                         "Do you want to continue?");
310     QString foo6 = i18n("The page contents expired. You can repost the form"
311                         "data by using <a href=\"javascript:go(0);\">Reload</a>");
312 }
313
314 #endif
315
316 bool HTMLFormElementImpl::formData(FormData &form_data) const
317 {
318 #ifdef FORMS_DEBUG
319     kdDebug( 6030 ) << "form: formData()" << endl;
320 #endif
321
322     QCString enc_string = ""; // used for non-multipart data
323
324     // find out the QTextcodec to use
325     QString str = m_acceptcharset.string();
326     str.replace(',', ' ');
327     QStringList charsets = QStringList::split(' ', str);
328     QTextCodec* codec = 0;
329     KHTMLPart *part = getDocument()->part();
330     for ( QStringList::Iterator it = charsets.begin(); it != charsets.end(); ++it )
331     {
332         QString enc = (*it);
333         if(enc.contains("UNKNOWN"))
334         {
335             // use standard document encoding
336             enc = "ISO-8859-1";
337             if (part)
338                 enc = part->encoding();
339         }
340         if((codec = KGlobal::charsets()->codecForName(enc.latin1())))
341             break;
342     }
343
344     if(!codec)
345         codec = QTextCodec::codecForLocale();
346
347 #if !APPLE_CHANGES
348     QStringList fileUploads;
349 #endif
350
351     for (unsigned i = 0; i < formElements.count(); ++i) {
352         HTMLGenericFormElementImpl* current = formElements[i];
353         FormDataList lst(codec);
354
355         if (!current->disabled() && current->appendFormData(lst, m_multipart))
356         {
357             //kdDebug(6030) << "adding name " << current->name().string() << endl;
358             for(QValueListConstIterator<QCString> it = lst.begin(); it != lst.end(); ++it )
359             {
360                 if (!m_multipart)
361                 {
362                     // handle ISINDEX / <input name=isindex> special
363                     // but only if its the first entry
364                     if ( enc_string.isEmpty() && *it == "isindex" ) {
365                         ++it;
366                         enc_string += encodeCString( *it );
367                     }
368                     else {
369                         if(!enc_string.isEmpty())
370                             enc_string += '&';
371
372                         enc_string += encodeCString(*it);
373                         enc_string += "=";
374                         ++it;
375                         enc_string += encodeCString(*it);
376                     }
377                 }
378                 else
379                 {
380                     QCString hstr("--");
381                     hstr += m_boundary.string().latin1();
382                     hstr += "\r\n";
383                     hstr += "Content-Disposition: form-data; name=\"";
384                     hstr += (*it).data();
385                     hstr += "\"";
386
387                     // if the current type is FILE, then we also need to
388                     // include the filename
389                     if (current->nodeType() == Node::ELEMENT_NODE && current->id() == ID_INPUT &&
390                         static_cast<HTMLInputElementImpl*>(current)->inputType() == HTMLInputElementImpl::FILE)
391                     {
392                         QString path = static_cast<HTMLInputElementImpl*>(current)->value().string();
393 #if !APPLE_CHANGES
394                         if (path.length()) fileUploads << path;
395 #endif
396
397                         // FIXME: This won't work if the filename includes a " mark,
398                         // or control characters like CR or LF. This also does strange
399                         // things if the filename includes characters you can't encode
400                         // in the website's character set.
401                         hstr += "; filename=\"";
402                         hstr += codec->fromUnicode(path.mid(path.findRev('/') + 1));
403                         hstr += "\"";
404
405                         if(!static_cast<HTMLInputElementImpl*>(current)->value().isEmpty())
406                         {
407 #if APPLE_CHANGES
408                             QString mimeType = part ? KWQ(part)->mimeTypeForFileName(path) : QString();
409 #else
410                             KMimeType::Ptr ptr = KMimeType::findByURL(KURL(path));
411                             QString mimeType = ptr->name();
412 #endif
413                             if (!mimeType.isEmpty()) {
414                                 hstr += "\r\nContent-Type: ";
415                                 hstr += mimeType.ascii();
416                             }
417                         }
418                     }
419
420                     hstr += "\r\n\r\n";
421                     ++it;
422
423                     // append body
424                     form_data.appendData(hstr.data(), hstr.length());
425                     form_data.appendData(*it, (*it).size() - 1);
426                     form_data.appendData("\r\n", 2);
427                 }
428             }
429         }
430     }
431
432 #if !APPLE_CHANGES
433     if (fileUploads.count()) {
434         int result = KMessageBox::warningContinueCancelList( 0,
435                                                              i18n("You're about to transfer the following files from "
436                                                                   "your local computer to the Internet.\n"
437                                                                   "Do you really want to continue?"),
438                                                              fileUploads);
439
440
441         if (result == KMessageBox::Cancel) {
442             return false;
443         }
444     }
445 #endif
446
447     if (m_multipart)
448         enc_string = ("--" + m_boundary.string() + "--\r\n").ascii();
449
450     form_data.appendData(enc_string.data(), enc_string.length());
451     return true;
452 }
453
454 void HTMLFormElementImpl::setEnctype( const DOMString& type )
455 {
456     if(type.string().find("multipart", 0, false) != -1 || type.string().find("form-data", 0, false) != -1)
457     {
458         m_enctype = "multipart/form-data";
459         m_multipart = true;
460         m_post = true;
461     } else if (type.string().find("text", 0, false) != -1 || type.string().find("plain", 0, false) != -1)
462     {
463         m_enctype = "text/plain";
464         m_multipart = false;
465     }
466     else
467     {
468         m_enctype = "application/x-www-form-urlencoded";
469         m_multipart = false;
470     }
471 }
472
473 void HTMLFormElementImpl::setBoundary( const DOMString& bound )
474 {
475     m_boundary = bound;
476 }
477
478 bool HTMLFormElementImpl::prepareSubmit()
479 {
480     KHTMLPart *part = getDocument()->part();
481     if(m_insubmit || !part || part->onlyLocalReferences())
482         return m_insubmit;
483
484     m_insubmit = true;
485     m_doingsubmit = false;
486
487     if ( dispatchHTMLEvent(EventImpl::SUBMIT_EVENT,false,true) && !m_doingsubmit )
488         m_doingsubmit = true;
489
490     m_insubmit = false;
491
492     if ( m_doingsubmit )
493         submit(true);
494
495     return m_doingsubmit;
496 }
497
498 void HTMLFormElementImpl::submit( bool activateSubmitButton )
499 {
500     KHTMLView *view = getDocument()->view();
501     KHTMLPart *part = getDocument()->part();
502     if (!view || !part) {
503         return;
504     }
505
506     if ( m_insubmit ) {
507         m_doingsubmit = true;
508         return;
509     }
510
511     m_insubmit = true;
512
513 #ifdef FORMS_DEBUG
514     kdDebug( 6030 ) << "submitting!" << endl;
515 #endif
516
517     HTMLGenericFormElementImpl* firstSuccessfulSubmitButton = 0;
518     bool needButtonActivation = activateSubmitButton;   // do we need to activate a submit button?
519     
520 #if APPLE_CHANGES
521     KWQ(part)->clearRecordedFormValues();
522 #endif
523     for (unsigned i = 0; i < formElements.count(); ++i) {
524         HTMLGenericFormElementImpl* current = formElements[i];
525 #if APPLE_CHANGES
526         // Our app needs to get form values for password fields for doing password autocomplete,
527         // so we are more lenient in pushing values, and let the app decide what to save when.
528         if (current->id() == ID_INPUT) {
529             HTMLInputElementImpl *input = static_cast<HTMLInputElementImpl*>(current);
530             if (input->inputType() == HTMLInputElementImpl::TEXT
531                 || input->inputType() ==  HTMLInputElementImpl::PASSWORD
532                 || input->inputType() == HTMLInputElementImpl::SEARCH)
533             {
534                 KWQ(part)->recordFormValue(input->name().string(), input->value().string(), this);
535                 if (input->renderer() && input->inputType() == HTMLInputElementImpl::SEARCH)
536                     static_cast<RenderLineEdit*>(input->renderer())->addSearchResult();
537             }
538         }
539 #else
540         if (current->id() == ID_INPUT &&
541             static_cast<HTMLInputElementImpl*>(current)->inputType() == HTMLInputElementImpl::TEXT &&
542             static_cast<HTMLInputElementImpl*>(current)->autoComplete() )
543         {
544             HTMLInputElementImpl *input = static_cast<HTMLInputElementImpl *>(current);
545             view->addFormCompletionItem(input->name().string(), input->value().string());
546         }
547 #endif
548
549         if (needButtonActivation) {
550             if (current->isActivatedSubmit()) {
551                 needButtonActivation = false;
552             } else if (firstSuccessfulSubmitButton == 0 && current->isSuccessfulSubmitButton()) {
553                 firstSuccessfulSubmitButton = current;
554             }
555         }
556     }
557
558     if (needButtonActivation && firstSuccessfulSubmitButton) {
559         firstSuccessfulSubmitButton->setActivatedSubmit(true);
560     }
561
562     FormData form_data;
563     if (formData(form_data)) {
564         if(m_post) {
565             part->submitForm( "post", m_url.string(), form_data,
566                                       m_target.string(),
567                                       enctype().string(),
568                                       boundary().string() );
569         }
570         else {
571             part->submitForm( "get", m_url.string(), form_data,
572                                       m_target.string() );
573         }
574     }
575
576     if (needButtonActivation && firstSuccessfulSubmitButton) {
577         firstSuccessfulSubmitButton->setActivatedSubmit(false);
578     }
579     
580     m_doingsubmit = m_insubmit = false;
581 }
582
583 void HTMLFormElementImpl::reset(  )
584 {
585     KHTMLPart *part = getDocument()->part();
586     if(m_inreset || !part) return;
587
588     m_inreset = true;
589
590 #ifdef FORMS_DEBUG
591     kdDebug( 6030 ) << "reset pressed!" << endl;
592 #endif
593
594     // ### DOM2 labels this event as not cancelable, however
595     // common browsers( sick! ) allow it be cancelled.
596     if ( !dispatchHTMLEvent(EventImpl::RESET_EVENT,true, true) ) {
597         m_inreset = false;
598         return;
599     }
600
601     for (unsigned i = 0; i < formElements.count(); ++i)
602         formElements[i]->reset();
603
604     m_inreset = false;
605 }
606
607 void HTMLFormElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
608 {
609     switch(attr->id())
610     {
611     case ATTR_ACTION:
612 #if APPLE_CHANGES
613         {
614         bool oldURLWasSecure = formWouldHaveSecureSubmission(m_url);
615 #endif
616         m_url = khtml::parseURL(attr->value());
617 #if APPLE_CHANGES
618         bool newURLIsSecure = formWouldHaveSecureSubmission(m_url);
619
620         if (m_attached && (oldURLWasSecure != newURLIsSecure))
621             if (newURLIsSecure)
622                 getDocument()->secureFormAdded();
623             else
624                 getDocument()->secureFormRemoved();
625         }
626 #endif
627         break;
628     case ATTR_TARGET:
629         m_target = attr->value();
630         break;
631     case ATTR_METHOD:
632         if ( strcasecmp( attr->value(), "post" ) == 0 )
633             m_post = true;
634         else if ( strcasecmp( attr->value(), "get" ) == 0 )
635             m_post = false;
636         break;
637     case ATTR_ENCTYPE:
638         setEnctype( attr->value() );
639         break;
640     case ATTR_ACCEPT_CHARSET:
641         // space separated list of charsets the server
642         // accepts - see rfc2045
643         m_acceptcharset = attr->value();
644         break;
645     case ATTR_ACCEPT:
646         // ignore this one for the moment...
647         break;
648     case ATTR_AUTOCOMPLETE:
649         m_autocomplete = strcasecmp( attr->value(), "off" );
650         break;
651     case ATTR_ONSUBMIT:
652         setHTMLEventListener(EventImpl::SUBMIT_EVENT,
653             getDocument()->createHTMLEventListener(attr->value().string()));
654         break;
655     case ATTR_ONRESET:
656         setHTMLEventListener(EventImpl::RESET_EVENT,
657             getDocument()->createHTMLEventListener(attr->value().string()));
658         break;
659     case ATTR_NAME:
660         {
661             QString newNameAttr = attr->value().string();
662             if (attached() && getDocument()->isHTMLDocument()) {
663                 HTMLDocumentImpl *document = static_cast<HTMLDocumentImpl *>(getDocument());
664                 document->removeNamedImageOrForm(oldNameAttr);
665                 document->addNamedImageOrForm(newNameAttr);
666             }
667             oldNameAttr = newNameAttr;
668         }
669         break;
670     case ATTR_ID:
671         {
672             QString newIdAttr = attr->value().string();
673             if (attached() && getDocument()->isHTMLDocument()) {
674                 HTMLDocumentImpl *document = static_cast<HTMLDocumentImpl *>(getDocument());
675                 document->removeNamedImageOrForm(oldIdAttr);
676                 document->addNamedImageOrForm(newIdAttr);
677             }
678             oldIdAttr = newIdAttr;
679         }
680         // fall through
681     default:
682         HTMLElementImpl::parseHTMLAttribute(attr);
683     }
684 }
685
686 void HTMLFormElementImpl::radioClicked( HTMLGenericFormElementImpl *caller )
687 {
688     for (unsigned i = 0; i < formElements.count(); ++i) {
689         HTMLGenericFormElementImpl *current = formElements[i];
690         if (current->id() == ID_INPUT &&
691             static_cast<HTMLInputElementImpl*>(current)->inputType() == HTMLInputElementImpl::RADIO &&
692             current != caller && current->form() == caller->form() && current->name() == caller->name()) {
693             static_cast<HTMLInputElementImpl*>(current)->setChecked(false);
694         }
695     }
696 }
697
698 template<class T> static void appendToVector(QPtrVector<T> &vec, T *item)
699 {
700     unsigned size = vec.size();
701     unsigned count = vec.count();
702     if (size == count)
703         vec.resize(size == 0 ? 8 : (int)(size * 1.5));
704     vec.insert(count, item);
705 }
706
707 template<class T> static void removeFromVector(QPtrVector<T> &vec, T *item)
708 {
709     int pos = vec.findRef(item);
710     int count = vec.count();
711
712     printf("item: 0x%x; pos: %d; vec[0]: 0x%x\n", (unsigned)item, pos, count ? (unsigned)vec[0] : 0);
713
714     if (pos < 0)
715         return;
716
717     for (int i = pos; i < count - 1; i++) {
718         vec.insert(i, vec[i+1]);
719     }
720     vec.remove(count - 1);
721 }
722
723 void HTMLFormElementImpl::registerFormElement(HTMLGenericFormElementImpl *e)
724 {
725     appendToVector(formElements, e);
726     removeFromVector(dormantFormElements, e);
727 }
728
729 void HTMLFormElementImpl::removeFormElement(HTMLGenericFormElementImpl *e)
730 {
731     removeFromVector(formElements, e);
732     removeFromVector(dormantFormElements, e);
733 }
734
735 void HTMLFormElementImpl::makeFormElementDormant(HTMLGenericFormElementImpl *e)
736 {
737     appendToVector(dormantFormElements, e);
738     removeFromVector(formElements, e);
739 }
740
741 bool HTMLFormElementImpl::isURLAttribute(AttributeImpl *attr) const
742 {
743     return attr->id() == ATTR_ACTION;
744 }
745
746 void HTMLFormElementImpl::registerImgElement(HTMLImageElementImpl *e)
747 {
748     appendToVector(imgElements, e);
749 }
750
751 void HTMLFormElementImpl::removeImgElement(HTMLImageElementImpl *e)
752 {
753     removeFromVector(imgElements, e);
754 }
755
756 // -------------------------------------------------------------------------
757
758 HTMLGenericFormElementImpl::HTMLGenericFormElementImpl(DocumentPtr *doc, HTMLFormElementImpl *f)
759     : HTMLElementImpl(doc)
760 {
761     m_disabled = m_readOnly = false;
762     m_name = 0;
763     m_dormant = false;
764
765     if (f)
766         m_form = f;
767     else
768         m_form = getForm();
769     if (m_form)
770         m_form->registerFormElement(this);
771 }
772
773 HTMLGenericFormElementImpl::~HTMLGenericFormElementImpl()
774 {
775     if (m_form)
776         m_form->removeFormElement(this);
777     if (m_name) m_name->deref();
778 }
779
780 void HTMLGenericFormElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
781 {
782     switch(attr->id())
783     {
784     case ATTR_NAME:
785         break;
786     case ATTR_DISABLED:
787         setDisabled( !attr->isNull() );
788         break;
789     case ATTR_READONLY:
790     {
791         bool m_oldreadOnly = m_readOnly;
792         m_readOnly = !attr->isNull();
793         if (m_oldreadOnly != m_readOnly) setChanged();
794         break;
795     }
796     default:
797         HTMLElementImpl::parseHTMLAttribute(attr);
798     }
799 }
800
801 void HTMLGenericFormElementImpl::attach()
802 {
803     assert(!attached());
804
805     // FIXME: This handles the case of a new form element being created by
806     // JavaScript and inserted inside a form. What it does not handle is
807     // a form element being moved from inside a form to outside, or from one
808     // inside one form to another. The reason this other case is hard to fix
809     // is that during parsing, we may have been passed a form that we are not
810     // inside, DOM-tree-wise. If so, it's hard for us to know when we should
811     // be removed from that form's element list.
812     if (!m_form) {
813         m_form = getForm();
814         if (m_form)
815             m_form->registerFormElement(this);
816     }
817
818     HTMLElementImpl::attach();
819
820     // The call to updateFromElement() needs to go after the call through
821     // to the base class's attach() because that can sometimes do a close
822     // on the renderer.
823     if (m_render) {
824         m_render->updateFromElement();
825     
826         // Delayed attachment in order to prevent FOUC can result in an object being
827         // programmatically focused before it has a render object.  If we have been focused
828         // (i.e., if we are the focusNode) then go ahead and focus our corresponding native widget.
829         // (Attach/detach can also happen as a result of display type changes, e.g., making a widget
830         // block instead of inline, and focus should be restored in that case as well.)
831         if (getDocument()->focusNode() == this && m_render->isWidget() && 
832             static_cast<RenderWidget*>(renderer())->widget())
833             static_cast<RenderWidget*>(renderer())->widget()->setFocus();
834     }
835 }
836
837 void HTMLGenericFormElementImpl::insertedIntoDocument()
838 {
839     if (m_form && m_dormant)
840         m_form->registerFormElement(this);
841
842     m_dormant = false;
843    
844     HTMLElementImpl::insertedIntoDocument();
845 }
846
847 void HTMLGenericFormElementImpl::removedFromDocument()
848 {
849     if (m_form)
850         m_form->makeFormElementDormant(this);
851
852     m_dormant = true;
853    
854     HTMLElementImpl::removedFromDocument();
855 }
856
857 HTMLFormElementImpl *HTMLGenericFormElementImpl::getForm() const
858 {
859     NodeImpl *p = parentNode();
860     while(p)
861     {
862         if( p->id() == ID_FORM )
863             return static_cast<HTMLFormElementImpl *>(p);
864         p = p->parentNode();
865     }
866 #ifdef FORMS_DEBUG
867     kdDebug( 6030 ) << "couldn't find form!" << endl;
868 #endif
869     return 0;
870 }
871
872 DOMString HTMLGenericFormElementImpl::name() const
873 {
874     if (m_name) return m_name;
875
876 // ###
877 //     DOMString n = getDocument()->htmlMode() != DocumentImpl::XHtml ?
878 //                   getAttribute(ATTR_NAME) : getAttribute(ATTR_ID);
879     DOMString n = getAttribute(ATTR_NAME);
880     if (n.isNull())
881         return new DOMStringImpl("");
882
883     return n;
884 }
885
886 void HTMLGenericFormElementImpl::setName(const DOMString& name)
887 {
888     if (m_name) m_name->deref();
889     m_name = name.implementation();
890     if (m_name) m_name->ref();
891 }
892
893 void HTMLGenericFormElementImpl::onSelect()
894 {
895     // ### make this work with new form events architecture
896     dispatchHTMLEvent(EventImpl::SELECT_EVENT,true,false);
897 }
898
899 void HTMLGenericFormElementImpl::onChange()
900 {
901     // ### make this work with new form events architecture
902     dispatchHTMLEvent(EventImpl::CHANGE_EVENT,true,false);
903 }
904
905 bool HTMLGenericFormElementImpl::disabled() const
906 {
907     return m_disabled;
908 }
909
910 void HTMLGenericFormElementImpl::setDisabled( bool _disabled )
911 {
912     if ( m_disabled != _disabled ) {
913         m_disabled = _disabled;
914         setChanged();
915     }
916 }
917
918 void HTMLGenericFormElementImpl::recalcStyle( StyleChange ch )
919 {
920     //bool changed = changed();
921     HTMLElementImpl::recalcStyle( ch );
922
923     if (m_render /*&& changed*/)
924         m_render->updateFromElement();
925 }
926
927 bool HTMLGenericFormElementImpl::isFocusable() const
928 {
929     if (!m_render || (m_render->style() && m_render->style()->visibility() != VISIBLE) || m_render->width() == 0 || m_render->height() == 0)
930         return false;
931     return true;
932 }
933
934 bool HTMLGenericFormElementImpl::isKeyboardFocusable() const
935 {
936     if (isFocusable()) {
937         if (m_render->isWidget()) {
938             return static_cast<RenderWidget*>(m_render)->widget() &&
939                 (static_cast<RenderWidget*>(m_render)->widget()->focusPolicy() & QWidget::TabFocus);
940         }
941         if (getDocument()->part())
942             return getDocument()->part()->tabsToAllControls();
943     }
944     return false;
945 }
946
947 bool HTMLGenericFormElementImpl::isMouseFocusable() const
948 {
949     if (isFocusable()) {
950         if (m_render->isWidget()) {
951             return static_cast<RenderWidget*>(m_render)->widget() &&
952                 (static_cast<RenderWidget*>(m_render)->widget()->focusPolicy() & QWidget::ClickFocus);
953         }
954 #if APPLE_CHANGES
955         // For <input type=image> and <button>, we will assume no mouse focusability.  This is
956         // consistent with OS X behavior for buttons.
957         return false;
958 #else
959         return true;
960 #endif
961     }
962     return false;
963 }
964
965 void HTMLGenericFormElementImpl::defaultEventHandler(EventImpl *evt)
966 {
967     if (evt->target()==this)
968     {
969         // Report focus in/out changes to the browser extension (editable widgets only)
970         KHTMLPart *part = getDocument()->part();
971         if (evt->id()==EventImpl::DOMFOCUSIN_EVENT && isEditable() && part && m_render && m_render->isWidget()) {
972             KHTMLPartBrowserExtension *ext = static_cast<KHTMLPartBrowserExtension *>(part->browserExtension());
973             QWidget *widget = static_cast<RenderWidget*>(m_render)->widget();
974             if (ext)
975                 ext->editableWidgetFocused(widget);
976         }
977
978 #if APPLE_CHANGES
979         // We don't want this default key event handling, we'll count on
980         // Cocoa event dispatch if the event doesn't get blocked.
981 #else
982         if (evt->id()==EventImpl::KEYDOWN_EVENT ||
983             evt->id()==EventImpl::KEYUP_EVENT)
984         {
985             KeyboardEventImpl * k = static_cast<KeyboardEventImpl *>(evt);
986             if (k->keyVal() == QChar('\n').unicode() && m_render && m_render->isWidget() && k->qKeyEvent)
987                 QApplication::sendEvent(static_cast<RenderWidget *>(m_render)->widget(), k->qKeyEvent);
988         }
989 #endif
990
991         if (evt->id()==EventImpl::DOMFOCUSOUT_EVENT && isEditable() && part && m_render && m_render->isWidget()) {
992             KHTMLPartBrowserExtension *ext = static_cast<KHTMLPartBrowserExtension *>(part->browserExtension());
993             QWidget *widget = static_cast<RenderWidget*>(m_render)->widget();
994             if (ext)
995                 ext->editableWidgetBlurred(widget);
996
997             // ### Don't count popup as a valid reason for losing the focus (example: opening the options of a select
998             // combobox shouldn't emit onblur)
999         }
1000     }
1001     HTMLElementImpl::defaultEventHandler(evt);
1002 }
1003
1004 bool HTMLGenericFormElementImpl::isEditable()
1005 {
1006     return false;
1007 }
1008
1009 // Special chars used to encode form state strings.
1010 // We pick chars that are unlikely to be used in an HTML attr, so we rarely have to really encode.
1011 const char stateSeparator = '&';
1012 const char stateEscape = '<';
1013 static const char stateSeparatorMarker[] = "<A";
1014 static const char stateEscapeMarker[] = "<<";
1015
1016 // Encode an element name so we can put it in a state string without colliding
1017 // with our separator char.
1018 static QString encodedElementName(QString str)
1019 {
1020     int sepLoc = str.find(stateSeparator);
1021     int escLoc = str.find(stateSeparator);
1022     if (sepLoc >= 0 || escLoc >= 0) {
1023         QString newStr = str;
1024         //   replace "<" with "<<"
1025         while (escLoc >= 0) {
1026             newStr.replace(escLoc, 1, stateEscapeMarker);
1027             escLoc = str.find(stateSeparator, escLoc+1);
1028         }
1029         //   replace "&" with "<A"
1030         while (sepLoc >= 0) {
1031             newStr.replace(sepLoc, 1, stateSeparatorMarker);
1032             sepLoc = str.find(stateSeparator, sepLoc+1);
1033         }
1034         return newStr;
1035     } else {
1036         return str;
1037     }
1038 }
1039
1040 QString HTMLGenericFormElementImpl::state( )
1041 {
1042     // Build a string that contains ElementName&ElementType&
1043     return encodedElementName(name().string()) + stateSeparator + type().string() + stateSeparator;
1044 }
1045
1046 QString HTMLGenericFormElementImpl::findMatchingState(QStringList &states)
1047 {
1048     QString encName = encodedElementName(name().string());
1049     QString typeStr = type().string();
1050     for (QStringList::Iterator it = states.begin(); it != states.end(); ++it) {
1051         QString state = *it;
1052         int sep1 = state.find(stateSeparator);
1053         int sep2 = state.find(stateSeparator, sep1+1);
1054         assert(sep1 >= 0);
1055         assert(sep2 >= 0);
1056
1057         QString nameAndType = state.left(sep2);
1058         if (encName.length() + typeStr.length() + 1 == (uint)sep2
1059             && nameAndType.startsWith(encName)
1060             && nameAndType.endsWith(typeStr))
1061         {
1062             states.remove(it);
1063             return state.mid(sep2+1);
1064         }
1065     }
1066     return QString::null;
1067 }
1068
1069 // -------------------------------------------------------------------------
1070
1071 HTMLButtonElementImpl::HTMLButtonElementImpl(DocumentPtr *doc, HTMLFormElementImpl *f)
1072     : HTMLGenericFormElementImpl(doc, f)
1073 {
1074     m_type = SUBMIT;
1075     m_dirty = true;
1076     m_activeSubmit = false;
1077 }
1078
1079 HTMLButtonElementImpl::~HTMLButtonElementImpl()
1080 {
1081 }
1082
1083 NodeImpl::Id HTMLButtonElementImpl::id() const
1084 {
1085     return ID_BUTTON;
1086 }
1087
1088 DOMString HTMLButtonElementImpl::type() const
1089 {
1090     return getAttribute(ATTR_TYPE);
1091 }
1092
1093 void HTMLButtonElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
1094 {
1095     switch(attr->id())
1096     {
1097     case ATTR_TYPE:
1098         if ( strcasecmp( attr->value(), "submit" ) == 0 )
1099             m_type = SUBMIT;
1100         else if ( strcasecmp( attr->value(), "reset" ) == 0 )
1101             m_type = RESET;
1102         else if ( strcasecmp( attr->value(), "button" ) == 0 )
1103             m_type = BUTTON;
1104         break;
1105     case ATTR_VALUE:
1106         m_value = attr->value();
1107         m_currValue = m_value;
1108         break;
1109     case ATTR_ACCESSKEY:
1110         break;
1111     case ATTR_ONFOCUS:
1112         setHTMLEventListener(EventImpl::FOCUS_EVENT,
1113             getDocument()->createHTMLEventListener(attr->value().string()));
1114         break;
1115     case ATTR_ONBLUR:
1116         setHTMLEventListener(EventImpl::BLUR_EVENT,
1117             getDocument()->createHTMLEventListener(attr->value().string()));
1118         break;
1119     default:
1120         HTMLGenericFormElementImpl::parseHTMLAttribute(attr);
1121     }
1122 }
1123
1124 void HTMLButtonElementImpl::defaultEventHandler(EventImpl *evt)
1125 {
1126     if (m_type != BUTTON && (evt->id() == EventImpl::DOMACTIVATE_EVENT)) {
1127
1128         if(m_form && m_type == SUBMIT) {
1129             m_activeSubmit = true;
1130             m_form->prepareSubmit();
1131             m_activeSubmit = false; // in case we were canceled
1132         }
1133         if(m_form && m_type == RESET) m_form->reset();
1134     }
1135     HTMLGenericFormElementImpl::defaultEventHandler(evt);
1136 }
1137
1138 bool HTMLButtonElementImpl::isSuccessfulSubmitButton() const
1139 {
1140     // HTML spec says that buttons must have names
1141     // to be considered successful. However, other browsers
1142     // do not impose this constraint. Therefore, we behave
1143     // differently and can use different buttons than the 
1144     // author intended. 
1145     // Remove the name constraint for now.
1146     // Was: m_type == SUBMIT && !m_disabled && !name().isEmpty()
1147     return m_type == SUBMIT && !m_disabled;
1148 }
1149
1150 bool HTMLButtonElementImpl::isActivatedSubmit() const
1151 {
1152     return m_activeSubmit;
1153 }
1154
1155 void HTMLButtonElementImpl::setActivatedSubmit(bool flag)
1156 {
1157     m_activeSubmit = flag;
1158 }
1159
1160 bool HTMLButtonElementImpl::appendFormData(FormDataList& encoding, bool /*multipart*/)
1161 {
1162     if (m_type != SUBMIT || name().isEmpty() || !m_activeSubmit)
1163         return false;
1164     encoding.appendData(name(), m_currValue);
1165     return true;
1166 }
1167
1168 void HTMLButtonElementImpl::click()
1169 {
1170 #if APPLE_CHANGES
1171     QWidget *widget;
1172     if (renderer() && (widget = static_cast<RenderWidget *>(renderer())->widget())) {
1173         // using this method gives us nice Cocoa user interface feedback
1174         static_cast<QButton *>(widget)->click();
1175     }
1176     else
1177 #endif
1178         HTMLGenericFormElementImpl::click();
1179 }
1180
1181 void HTMLButtonElementImpl::accessKeyAction()
1182 {   
1183     click();
1184 }
1185
1186 // -------------------------------------------------------------------------
1187
1188 HTMLFieldSetElementImpl::HTMLFieldSetElementImpl(DocumentPtr *doc, HTMLFormElementImpl *f)
1189    : HTMLGenericFormElementImpl(doc, f)
1190 {
1191 }
1192
1193 HTMLFieldSetElementImpl::~HTMLFieldSetElementImpl()
1194 {
1195 }
1196
1197 bool HTMLFieldSetElementImpl::isFocusable() const
1198 {
1199     return false;
1200 }
1201
1202 NodeImpl::Id HTMLFieldSetElementImpl::id() const
1203 {
1204     return ID_FIELDSET;
1205 }
1206
1207 DOMString HTMLFieldSetElementImpl::type() const
1208 {
1209     return "fieldset";
1210 }
1211
1212 RenderObject* HTMLFieldSetElementImpl::createRenderer(RenderArena* arena, RenderStyle* style)
1213 {
1214     return new (arena) RenderFieldset(this);
1215 }
1216
1217 // -------------------------------------------------------------------------
1218
1219 HTMLInputElementImpl::HTMLInputElementImpl(DocumentPtr *doc, HTMLFormElementImpl *f)
1220     : HTMLGenericFormElementImpl(doc, f), m_imageLoader(0)
1221 {
1222     m_type = TEXT;
1223     m_maxLen = -1;
1224     m_size = 20;
1225     m_checked = false;
1226     m_defaultChecked = false;
1227     m_useDefaultChecked = true;
1228     
1229     m_haveType = false;
1230     m_activeSubmit = false;
1231     m_autocomplete = true;
1232     m_inited = false;
1233
1234     xPos = 0;
1235     yPos = 0;
1236
1237 #if APPLE_CHANGES
1238     m_maxResults = 0;
1239 #endif
1240
1241     if ( m_form )
1242         m_autocomplete = f->autoComplete();
1243 }
1244
1245 HTMLInputElementImpl::~HTMLInputElementImpl()
1246 {
1247     if (getDocument()) getDocument()->deregisterMaintainsState(this);
1248     delete m_imageLoader;
1249 }
1250
1251 NodeImpl::Id HTMLInputElementImpl::id() const
1252 {
1253     return ID_INPUT;
1254 }
1255
1256 void HTMLInputElementImpl::setType(const DOMString& t)
1257 {
1258     typeEnum newType;
1259     
1260     if ( strcasecmp( t, "password" ) == 0 )
1261         newType = PASSWORD;
1262     else if ( strcasecmp( t, "checkbox" ) == 0 )
1263         newType = CHECKBOX;
1264     else if ( strcasecmp( t, "radio" ) == 0 )
1265         newType = RADIO;
1266     else if ( strcasecmp( t, "submit" ) == 0 )
1267         newType = SUBMIT;
1268     else if ( strcasecmp( t, "reset" ) == 0 )
1269         newType = RESET;
1270     else if ( strcasecmp( t, "file" ) == 0 )
1271         newType = FILE;
1272     else if ( strcasecmp( t, "hidden" ) == 0 )
1273         newType = HIDDEN;
1274     else if ( strcasecmp( t, "image" ) == 0 )
1275         newType = IMAGE;
1276     else if ( strcasecmp( t, "button" ) == 0 )
1277         newType = BUTTON;
1278     else if ( strcasecmp( t, "khtml_isindex" ) == 0 )
1279         newType = ISINDEX;
1280 #if APPLE_CHANGES
1281     else if ( strcasecmp( t, "search" ) == 0 )
1282         newType = SEARCH;
1283     else if ( strcasecmp( t, "range" ) == 0 )
1284         newType = RANGE;
1285 #endif
1286     else
1287         newType = TEXT;
1288
1289     // ### IMPORTANT: Don't allow the type to be changed to FILE after the first
1290     // type change, otherwise a JavaScript programmer would be able to set a text
1291     // field's value to something like /etc/passwd and then change it to a file field.
1292     if (m_type != newType) {
1293         if (newType == FILE && m_haveType) {
1294             // Set the attribute back to the old value.
1295             // Useful in case we were called from inside parseHTMLAttribute.
1296             setAttribute(ATTR_TYPE, type());
1297         } else {
1298             bool wasAttached = m_attached;
1299             if (wasAttached)
1300                 detach();
1301             bool didStoreValue = storesValueSeparateFromAttribute();
1302             m_type = newType;
1303             bool willStoreValue = storesValueSeparateFromAttribute();
1304             if (didStoreValue && !willStoreValue && !m_value.isNull()) {
1305                 setAttribute(ATTR_VALUE, m_value);
1306                 m_value = DOMString();
1307             }
1308             if (!didStoreValue && willStoreValue) {
1309                 m_value = getAttribute(ATTR_VALUE);
1310             }
1311             if (wasAttached)
1312                 attach();
1313         }
1314     }
1315     m_haveType = true;
1316 }
1317
1318 DOMString HTMLInputElementImpl::type() const
1319 {
1320     // needs to be lowercase according to DOM spec
1321     switch (m_type) {
1322     case TEXT: return "text";
1323     case PASSWORD: return "password";
1324     case CHECKBOX: return "checkbox";
1325     case RADIO: return "radio";
1326     case SUBMIT: return "submit";
1327     case RESET: return "reset";
1328     case FILE: return "file";
1329     case HIDDEN: return "hidden";
1330     case IMAGE: return "image";
1331     case BUTTON: return "button";
1332 #if APPLE_CHANGES
1333     case SEARCH: return "search";
1334     case RANGE: return "range";
1335 #endif
1336     case ISINDEX: return "";
1337     }
1338     return "";
1339 }
1340
1341 QString HTMLInputElementImpl::state( )
1342 {
1343     assert(m_type != PASSWORD);         // should never save/restore password fields
1344
1345     QString state = HTMLGenericFormElementImpl::state();
1346     switch (m_type) {
1347     case CHECKBOX:
1348     case RADIO:
1349         return state + (checked() ? "on" : "off");
1350     default:
1351         return state + value().string()+'.'; // Make sure the string is not empty!
1352     }
1353 }
1354
1355 void HTMLInputElementImpl::restoreState(QStringList &states)
1356 {
1357     assert(m_type != PASSWORD);         // should never save/restore password fields
1358     
1359     QString state = HTMLGenericFormElementImpl::findMatchingState(states);
1360     if (state.isNull()) return;
1361
1362     switch (m_type) {
1363     case CHECKBOX:
1364     case RADIO:
1365         setChecked((state == "on"));
1366         break;
1367     default:
1368         setValue(DOMString(state.left(state.length()-1)));
1369         break;
1370     }
1371 }
1372
1373 void HTMLInputElementImpl::select(  )
1374 {
1375     if(!m_render) return;
1376
1377     if (m_type == TEXT || m_type == PASSWORD)
1378         static_cast<RenderLineEdit*>(m_render)->select();
1379     else if (m_type == FILE)
1380         static_cast<RenderFileButton*>(m_render)->select();
1381 }
1382
1383 void HTMLInputElementImpl::click()
1384 {
1385     switch (inputType()) {
1386         case HIDDEN:
1387             // a no-op for this type
1388             break;
1389         case CHECKBOX:
1390         case RADIO:
1391         case SUBMIT:
1392         case RESET:
1393         case BUTTON: 
1394 #if APPLE_CHANGES
1395         {
1396             QWidget *widget;
1397             if (renderer() && (widget = static_cast<RenderWidget *>(renderer())->widget())) {
1398                 // using this method gives us nice Cocoa user interface feedback
1399                 static_cast<QButton *>(widget)->click();
1400                 break;
1401             }
1402         }
1403 #endif
1404             HTMLGenericFormElementImpl::click();
1405             break;
1406         case FILE:
1407 #if APPLE_CHANGES
1408             if (renderer()) {
1409                 static_cast<RenderFileButton *>(renderer())->click();
1410                 break;
1411             }
1412 #endif
1413             HTMLGenericFormElementImpl::click();
1414             break;
1415         case IMAGE:
1416         case ISINDEX:
1417         case PASSWORD:
1418 #if APPLE_CHANGES
1419         case SEARCH:
1420         case RANGE:
1421 #endif
1422         case TEXT:
1423             HTMLGenericFormElementImpl::click();
1424             break;
1425     }
1426 }
1427
1428 void HTMLInputElementImpl::accessKeyAction()
1429 {
1430     switch (inputType()) {
1431         case HIDDEN:
1432             // a no-op for this type
1433             break;
1434         case TEXT:
1435         case PASSWORD:
1436 #if APPLE_CHANGES
1437         case SEARCH:
1438 #endif
1439         case ISINDEX:
1440             focus();
1441             break;
1442         case CHECKBOX:
1443         case RADIO:
1444         case SUBMIT:
1445         case RESET:
1446         case IMAGE:
1447         case BUTTON:
1448         case FILE:
1449 #if APPLE_CHANGES
1450         case RANGE:
1451 #endif
1452             // focus and click
1453             focus();
1454             click();
1455             break;
1456     }
1457 }
1458
1459 bool HTMLInputElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
1460 {
1461     switch (attr) {
1462         case ATTR_WIDTH:
1463         case ATTR_HEIGHT:
1464         case ATTR_VSPACE:
1465         case ATTR_HSPACE:
1466             result = eUniversal;
1467             return false;
1468         case ATTR_ALIGN:
1469             result = eReplaced; // Share with <img> since the alignment behavior is the same.
1470             return false;
1471         default:
1472             break;
1473     }
1474     
1475     return HTMLElementImpl::mapToEntry(attr, result);
1476 }
1477
1478 void HTMLInputElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
1479 {
1480     switch(attr->id())
1481     {
1482     case ATTR_AUTOCOMPLETE:
1483         m_autocomplete = strcasecmp( attr->value(), "off" );
1484         break;
1485     case ATTR_TYPE:
1486         setType(attr->value());
1487         if (m_type != IMAGE && m_imageLoader) {
1488             delete m_imageLoader;
1489             m_imageLoader = 0;
1490         }
1491         break;
1492     case ATTR_VALUE:
1493         // We only need to setChanged if the form is looking at the default value right now.
1494         if (m_value.isNull())
1495             setChanged();
1496         break;
1497     case ATTR_CHECKED:
1498         m_defaultChecked = !attr->isNull();
1499         if (m_useDefaultChecked) {
1500             setChecked(m_defaultChecked);
1501             m_useDefaultChecked = true;
1502         }
1503         break;
1504     case ATTR_MAXLENGTH:
1505         m_maxLen = !attr->isNull() ? attr->value().toInt() : -1;
1506         setChanged();
1507         break;
1508     case ATTR_SIZE:
1509         m_size = !attr->isNull() ? attr->value().toInt() : 20;
1510         break;
1511     case ATTR_ALT:
1512         if (m_render && m_type == IMAGE)
1513             static_cast<RenderImage*>(m_render)->updateAltText();
1514         break;
1515     case ATTR_SRC:
1516         if (m_render && m_type == IMAGE) {
1517             if (!m_imageLoader)
1518                 m_imageLoader = new HTMLImageLoader(this);
1519             m_imageLoader->updateFromElement();
1520         }
1521         break;
1522     case ATTR_USEMAP:
1523     case ATTR_ACCESSKEY:
1524         // ### ignore for the moment
1525         break;
1526     case ATTR_VSPACE:
1527         addCSSLength(attr, CSS_PROP_MARGIN_TOP, attr->value());
1528         addCSSLength(attr, CSS_PROP_MARGIN_BOTTOM, attr->value());
1529         break;
1530     case ATTR_HSPACE:
1531         addCSSLength(attr, CSS_PROP_MARGIN_LEFT, attr->value());
1532         addCSSLength(attr, CSS_PROP_MARGIN_RIGHT, attr->value());
1533         break;        
1534     case ATTR_ALIGN:
1535         addHTMLAlignment(attr);
1536         break;
1537     case ATTR_WIDTH:
1538         addCSSLength(attr, CSS_PROP_WIDTH, attr->value() );
1539         break;
1540     case ATTR_HEIGHT:
1541         addCSSLength(attr, CSS_PROP_HEIGHT, attr->value() );
1542         break;
1543     case ATTR_ONFOCUS:
1544         setHTMLEventListener(EventImpl::FOCUS_EVENT,
1545             getDocument()->createHTMLEventListener(attr->value().string()));
1546         break;
1547     case ATTR_ONBLUR:
1548         setHTMLEventListener(EventImpl::BLUR_EVENT,
1549             getDocument()->createHTMLEventListener(attr->value().string()));
1550         break;
1551     case ATTR_ONSELECT:
1552         setHTMLEventListener(EventImpl::SELECT_EVENT,
1553             getDocument()->createHTMLEventListener(attr->value().string()));
1554         break;
1555     case ATTR_ONCHANGE:
1556         setHTMLEventListener(EventImpl::CHANGE_EVENT,
1557             getDocument()->createHTMLEventListener(attr->value().string()));
1558         break;
1559     case ATTR_ONINPUT:
1560         setHTMLEventListener(EventImpl::INPUT_EVENT,
1561                              getDocument()->createHTMLEventListener(attr->value().string()));
1562         break;
1563 #if APPLE_CHANGES
1564     // Search field and slider attributes all just cause updateFromElement to be called through style
1565     // recalcing.
1566     case ATTR_ONSEARCH:
1567         setHTMLEventListener(EventImpl::SEARCH_EVENT,
1568                              getDocument()->createHTMLEventListener(attr->value().string()));
1569         break;
1570     case ATTR_RESULTS:
1571         m_maxResults = !attr->isNull() ? attr->value().toInt() : 0;
1572         /* Fall through */
1573     case ATTR_AUTOSAVE:
1574     case ATTR_INCREMENTAL:
1575     case ATTR_PLACEHOLDER:
1576     case ATTR_MIN:
1577     case ATTR_MAX:
1578     case ATTR_PRECISION:
1579         setChanged();
1580         break;
1581 #endif
1582     default:
1583         HTMLGenericFormElementImpl::parseHTMLAttribute(attr);
1584     }
1585 }
1586
1587 bool HTMLInputElementImpl::rendererIsNeeded(RenderStyle *style)
1588 {
1589     switch(m_type)
1590     {
1591     case TEXT:
1592     case PASSWORD:
1593 #if APPLE_CHANGES
1594     case SEARCH:
1595     case RANGE:
1596 #endif
1597     case ISINDEX:
1598     case CHECKBOX:
1599     case RADIO:
1600     case SUBMIT:
1601     case IMAGE:
1602     case RESET:
1603     case FILE:
1604     case BUTTON:   return HTMLGenericFormElementImpl::rendererIsNeeded(style);
1605     case HIDDEN:   return false;
1606     }
1607     assert(false);
1608     return false;
1609 }
1610
1611 RenderObject *HTMLInputElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
1612 {
1613     switch(m_type)
1614     {
1615     case TEXT:
1616     case PASSWORD:
1617 #if APPLE_CHANGES
1618     case SEARCH:
1619 #endif
1620     case ISINDEX:  return new (arena) RenderLineEdit(this);
1621     case CHECKBOX: return new (arena) RenderCheckBox(this);
1622     case RADIO:    return new (arena) RenderRadioButton(this);
1623     case SUBMIT:   return new (arena) RenderSubmitButton(this);
1624     case IMAGE:    return new (arena) RenderImageButton(this);
1625     case RESET:    return new (arena) RenderResetButton(this);
1626     case FILE:     return new (arena) RenderFileButton(this);
1627     case BUTTON:   return new (arena) RenderPushButton(this);
1628 #if APPLE_CHANGES
1629     case RANGE:    return new (arena) RenderSlider(this);
1630 #endif
1631     case HIDDEN:   break;
1632     }
1633     assert(false);
1634     return 0;
1635 }
1636
1637 void HTMLInputElementImpl::attach()
1638 {
1639     if (!m_inited) {
1640         if (!m_haveType)
1641             setType(getAttribute(ATTR_TYPE));
1642
1643         // FIXME: This needs to be dynamic, doesn't it, since someone could set this
1644         // after attachment?
1645         DOMString val = getAttribute(ATTR_VALUE);
1646         if ((uint) m_type <= ISINDEX && !val.isEmpty()) {
1647             // remove newline stuff..
1648             QString nvalue;
1649             for (unsigned int i = 0; i < val.length(); ++i)
1650                 if (val[i] >= ' ')
1651                     nvalue += val[i];
1652
1653             if (val.length() != nvalue.length())
1654                 setAttribute(ATTR_VALUE, nvalue);
1655         }
1656
1657         m_defaultChecked = (!getAttribute(ATTR_CHECKED).isNull());
1658         
1659         m_inited = true;
1660     }
1661
1662     // Disallow the width attribute on inputs other than HIDDEN and IMAGE.
1663     // Dumb Web sites will try to set the width as an attribute on form controls that aren't
1664     // images or hidden.
1665     if (hasMappedAttributes() && m_type != HIDDEN && m_type != IMAGE && !getAttribute(ATTR_WIDTH).isEmpty()) {
1666         int excCode;
1667         removeAttribute(ATTR_WIDTH, excCode);
1668     }
1669
1670     HTMLGenericFormElementImpl::attach();
1671
1672     if (m_type == IMAGE) {
1673         if (!m_imageLoader)
1674             m_imageLoader = new HTMLImageLoader(this);
1675         m_imageLoader->updateFromElement();
1676         if (renderer()) {
1677             RenderImage* imageObj = static_cast<RenderImage*>(renderer());
1678             imageObj->setImage(m_imageLoader->image());    
1679         }
1680     }
1681
1682 #if APPLE_CHANGES
1683     // note we don't deal with calling passwordFieldRemoved() on detach, because the timing
1684     // was such that it cleared our state too early
1685     if (m_type == PASSWORD)
1686         getDocument()->passwordFieldAdded();
1687 #endif
1688 }
1689
1690 DOMString HTMLInputElementImpl::altText() const
1691 {
1692     // http://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
1693     // also heavily discussed by Hixie on bugzilla
1694     // note this is intentionally different to HTMLImageElementImpl::altText()
1695     DOMString alt = getAttribute( ATTR_ALT );
1696     // fall back to title attribute
1697     if ( alt.isNull() )
1698         alt = getAttribute( ATTR_TITLE );
1699     if ( alt.isNull() )
1700         alt = getAttribute( ATTR_VALUE );
1701     if ( alt.isEmpty() )
1702 #if APPLE_CHANGES
1703         alt = inputElementAltText();
1704 #else
1705         alt = i18n( "Submit" );
1706 #endif
1707
1708     return alt;
1709 }
1710
1711 bool HTMLInputElementImpl::isSuccessfulSubmitButton() const
1712 {
1713     // HTML spec says that buttons must have names
1714     // to be considered successful. However, other browsers
1715     // do not impose this constraint. Therefore, we behave
1716     // differently and can use different buttons than the 
1717     // author intended. 
1718     // Was: (m_type == SUBMIT && !name().isEmpty())
1719     return !m_disabled && (m_type == IMAGE || m_type == SUBMIT);
1720 }
1721
1722 bool HTMLInputElementImpl::isActivatedSubmit() const
1723 {
1724     return m_activeSubmit;
1725 }
1726
1727 void HTMLInputElementImpl::setActivatedSubmit(bool flag)
1728 {
1729     m_activeSubmit = flag;
1730 }
1731
1732 bool HTMLInputElementImpl::appendFormData(FormDataList &encoding, bool multipart)
1733 {
1734     // image generates its own names
1735     if (name().isEmpty() && m_type != IMAGE) return false;
1736
1737     switch (m_type) {
1738         case HIDDEN:
1739         case TEXT:
1740 #if APPLE_CHANGES
1741         case SEARCH:
1742         case RANGE:
1743 #endif
1744         case PASSWORD:
1745             // always successful
1746             encoding.appendData(name(), value());
1747             return true;
1748
1749         case CHECKBOX:
1750         case RADIO:
1751             if (checked()) {
1752                 encoding.appendData(name(), value());
1753                 return true;
1754             }
1755             break;
1756
1757         case BUTTON:
1758         case RESET:
1759             // those buttons are never successful
1760             return false;
1761
1762         case IMAGE:
1763             if (m_activeSubmit)
1764             {
1765                 encoding.appendData(name().isEmpty() ? QString::fromLatin1("x") : (name().string() + ".x"), clickX());
1766                 encoding.appendData(name().isEmpty() ? QString::fromLatin1("y") : (name().string() + ".y"), clickY());
1767                 return true;
1768             }
1769             break;
1770
1771         case SUBMIT:
1772             if (m_activeSubmit)
1773             {
1774                 QString enc_str = value().string();
1775                 if (enc_str.isEmpty())
1776                     enc_str = static_cast<RenderSubmitButton*>(m_render)->defaultLabel();
1777                 if (!enc_str.isEmpty()) {
1778                     encoding.appendData(name(), enc_str);
1779                     return true;
1780                 }
1781             }
1782             break;
1783
1784         case FILE:
1785         {
1786             // can't submit file on GET
1787             // don't submit if display: none or display: hidden
1788             if(!multipart || !renderer() || renderer()->style()->visibility() != khtml::VISIBLE)
1789                 return false;
1790
1791             // if no filename at all is entered, return successful, however empty
1792             // null would be more logical but netscape posts an empty file. argh.
1793             if (value().isEmpty()) {
1794                 encoding.appendData(name(), QString(""));
1795                 return true;
1796             }
1797
1798             KURL fileurl("file:///");
1799             fileurl.setPath(value().string());
1800             KIO::UDSEntry filestat;
1801
1802             if (!KIO::NetAccess::stat(fileurl, filestat)) {
1803 #if APPLE_CHANGES
1804                 // FIXME: Figure out how to report this error.
1805 #else
1806                 KMessageBox::sorry(0L, i18n("Error fetching file for submission:\n%1").arg(KIO::NetAccess::lastErrorString()));
1807 #endif
1808                 return false;
1809             }
1810
1811             KFileItem fileitem(filestat, fileurl, true, false);
1812             if (fileitem.isDir()) {
1813                 return false;
1814             }
1815
1816             QString local;
1817             if ( KIO::NetAccess::download(fileurl, local) )
1818             {
1819                 QFile file(local);
1820                 if (file.open(IO_ReadOnly))
1821                 {
1822                     QCString filearray(file.size()+1);
1823                     int readbytes = file.readBlock( filearray.data(), file.size());
1824                     if ( readbytes >= 0 )
1825                         filearray[readbytes] = '\0';
1826                     file.close();
1827
1828                     encoding.appendData(name(), filearray);
1829                     KIO::NetAccess::removeTempFile( local );
1830
1831                     return true;
1832                 }
1833                 return false;
1834             }
1835             else {
1836 #if APPLE_CHANGES
1837                 // FIXME: Figure out how to report this error.
1838 #else
1839                 KMessageBox::sorry(0L, i18n("Error fetching file for submission:\n%1").arg(KIO::NetAccess::lastErrorString()));
1840 #endif
1841                 return false;
1842             }
1843             break;
1844         }
1845         case ISINDEX:
1846             encoding.appendData(name(), value());
1847             return true;
1848     }
1849     return false;
1850 }
1851
1852 void HTMLInputElementImpl::reset()
1853 {
1854     if (storesValueSeparateFromAttribute())
1855         setValue(DOMString());
1856     setChecked(m_defaultChecked);
1857     m_useDefaultChecked = true;
1858 }
1859
1860 void HTMLInputElementImpl::setChecked(bool _checked)
1861 {
1862     if (checked() == _checked) return;
1863
1864     if (m_form && m_type == RADIO && _checked && !name().isEmpty())
1865         m_form->radioClicked(this);
1866
1867     m_useDefaultChecked = false;
1868     m_checked = _checked;
1869     setChanged();
1870 }
1871
1872
1873 DOMString HTMLInputElementImpl::value() const
1874 {
1875     DOMString value = m_value;
1876
1877     // It's important *not* to fall back to the value attribute for file inputs,
1878     // because that would allow a malicious web page to upload files by setting the
1879     // value attribute in markup.
1880     if (value.isNull() && m_type != FILE)
1881         value = getAttribute(ATTR_VALUE);
1882
1883     // If no attribute exists, then just use "on" or "" based off the checked() state of the control.
1884     if (value.isNull() && (m_type == CHECKBOX || m_type == RADIO))
1885         return DOMString(checked() ? "on" : "");
1886
1887     return value;
1888 }
1889
1890
1891 void HTMLInputElementImpl::setValue(const DOMString &value)
1892 {
1893     if (m_type == FILE) return;
1894
1895     if (storesValueSeparateFromAttribute()) {
1896         m_value = value;
1897         setChanged();
1898     } else {
1899         setAttribute(ATTR_VALUE, value);
1900     }
1901 }
1902
1903 bool HTMLInputElementImpl::storesValueSeparateFromAttribute() const
1904 {
1905     switch (m_type) {
1906         case BUTTON:
1907         case CHECKBOX:
1908         case FILE:
1909         case HIDDEN:
1910         case IMAGE:
1911         case RADIO:
1912 #if APPLE_CHANGES
1913         case RANGE:
1914 #endif
1915         case RESET:
1916         case SUBMIT:
1917             return false;
1918         case ISINDEX:
1919         case PASSWORD:
1920 #if APPLE_CHANGES
1921         case SEARCH:
1922 #endif
1923         case TEXT:
1924             return true;
1925     }
1926     return false;
1927 }
1928
1929 void HTMLInputElementImpl::blur()
1930 {
1931     if(getDocument()->focusNode() == this)
1932         getDocument()->setFocusNode(0);
1933 }
1934
1935 void HTMLInputElementImpl::focus()
1936 {
1937     getDocument()->setFocusNode(this);
1938 }
1939
1940 void HTMLInputElementImpl::defaultEventHandler(EventImpl *evt)
1941 {
1942     if (evt->isMouseEvent() &&
1943         ( evt->id() == EventImpl::KHTML_CLICK_EVENT || evt->id() == EventImpl::KHTML_DBLCLICK_EVENT ) &&
1944         m_type == IMAGE
1945         && m_render) {
1946         // record the mouse position for when we get the DOMActivate event
1947         MouseEventImpl *me = static_cast<MouseEventImpl*>(evt);
1948         int offsetX, offsetY;
1949         m_render->absolutePosition(offsetX,offsetY);
1950         xPos = me->clientX()-offsetX;
1951         yPos = me->clientY()-offsetY;
1952
1953         me->setDefaultHandled();
1954     }
1955
1956     // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means
1957     // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks
1958     // on the element, or presses enter while it is the active element. Javacsript code wishing to activate the element
1959     // must dispatch a DOMActivate event - a click event will not do the job.
1960     if ((evt->id() == EventImpl::DOMACTIVATE_EVENT) &&
1961         (m_type == IMAGE || m_type == SUBMIT || m_type == RESET)){
1962
1963         if (!m_form || !m_render)
1964             return;
1965
1966         if (m_type == RESET) {
1967             m_form->reset();
1968         }
1969         else {
1970             m_activeSubmit = true;
1971             if (!m_form->prepareSubmit()) {
1972                 xPos = 0;
1973                 yPos = 0;
1974             }
1975             m_activeSubmit = false;
1976         }
1977     }
1978
1979 #if APPLE_CHANGES
1980     // Use key press event here since sending simulated mouse events
1981     // on key down blocks the proper sending of the key press event.
1982     if (evt->id() == EventImpl::KEYPRESS_EVENT && evt->isKeyboardEvent()) {
1983         DOMString key = static_cast<KeyboardEventImpl *>(evt)->keyIdentifier();
1984         switch (m_type) {
1985             case BUTTON:
1986             case CHECKBOX:
1987             case FILE:
1988             case IMAGE:
1989             case RADIO:
1990             case RESET:
1991             case SUBMIT:
1992                 // Simulate mouse click for enter or spacebar for these types of elements.
1993                 // The AppKit already does this for spacebar for some, but not all, of them.
1994                 if (key == "U+000020" || key == "Enter") {
1995                     click();
1996                     evt->setDefaultHandled();
1997                 }
1998                 break;
1999             case HIDDEN:
2000             case ISINDEX:
2001             case PASSWORD:
2002             case RANGE:
2003             case SEARCH:
2004             case TEXT:
2005                 // Simulate mouse click on the default form button for enter for these types of elements.
2006                 if (key == "Enter" && m_form) {
2007                     m_form->submitClick();
2008                     evt->setDefaultHandled();
2009                 }
2010                 break;
2011         }
2012     }
2013 #endif
2014
2015     HTMLGenericFormElementImpl::defaultEventHandler(evt);
2016 }
2017
2018 bool HTMLInputElementImpl::isEditable()
2019 {
2020     return ((m_type == TEXT) || (m_type == PASSWORD) ||
2021             (m_type == SEARCH) || (m_type == ISINDEX) || (m_type == FILE));
2022 }
2023
2024 bool HTMLInputElementImpl::isURLAttribute(AttributeImpl *attr) const
2025 {
2026     return (attr->id() == ATTR_SRC);
2027 }
2028
2029 // -------------------------------------------------------------------------
2030
2031 HTMLLabelElementImpl::HTMLLabelElementImpl(DocumentPtr *doc)
2032     : HTMLElementImpl(doc)
2033 {
2034 }
2035
2036 HTMLLabelElementImpl::~HTMLLabelElementImpl()
2037 {
2038 }
2039
2040 bool HTMLLabelElementImpl::isFocusable() const
2041 {
2042     return false;
2043 }
2044
2045 NodeImpl::Id HTMLLabelElementImpl::id() const
2046 {
2047     return ID_LABEL;
2048 }
2049
2050 void HTMLLabelElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
2051 {
2052     switch(attr->id())
2053     {
2054     case ATTR_ONFOCUS:
2055         setHTMLEventListener(EventImpl::FOCUS_EVENT,
2056             getDocument()->createHTMLEventListener(attr->value().string()));
2057         break;
2058     case ATTR_ONBLUR:
2059         setHTMLEventListener(EventImpl::BLUR_EVENT,
2060             getDocument()->createHTMLEventListener(attr->value().string()));
2061         break;
2062     default:
2063         HTMLElementImpl::parseHTMLAttribute(attr);
2064     }
2065 }
2066
2067 ElementImpl *HTMLLabelElementImpl::formElement()
2068 {
2069     DOMString formElementId = getAttribute(ATTR_FOR);
2070     if (formElementId.isNull()) {
2071         // Search children of the label element for a form element.
2072         NodeImpl *node = this;
2073         while ((node = node->traverseNextNode(this))) {
2074             if (node->isHTMLElement()) {
2075                 HTMLElementImpl *element = static_cast<HTMLElementImpl *>(node);
2076                 if (element->isGenericFormElement()) {
2077                     return element;
2078                 }
2079             }
2080         }
2081         return 0;
2082     }
2083     if (formElementId.isEmpty())
2084         return 0;
2085     return getDocument()->getElementById(formElementId);
2086 }
2087
2088 void HTMLLabelElementImpl::accessKeyAction()
2089 {
2090     ElementImpl *element = formElement();
2091     if (element)
2092         element->accessKeyAction();
2093 }
2094
2095 // -------------------------------------------------------------------------
2096
2097 HTMLLegendElementImpl::HTMLLegendElementImpl(DocumentPtr *doc, HTMLFormElementImpl *f)
2098 : HTMLGenericFormElementImpl(doc, f)
2099 {
2100 }
2101
2102 HTMLLegendElementImpl::~HTMLLegendElementImpl()
2103 {
2104 }
2105
2106 bool HTMLLegendElementImpl::isFocusable() const
2107 {
2108     return false;
2109 }
2110
2111 NodeImpl::Id HTMLLegendElementImpl::id() const
2112 {
2113     return ID_LEGEND;
2114 }
2115
2116 RenderObject* HTMLLegendElementImpl::createRenderer(RenderArena* arena, RenderStyle* style)
2117 {
2118     return new (arena) RenderLegend(this);
2119 }
2120
2121 DOMString HTMLLegendElementImpl::type() const
2122 {
2123     return "legend";
2124 }
2125
2126 // -------------------------------------------------------------------------
2127
2128 HTMLSelectElementImpl::HTMLSelectElementImpl(DocumentPtr *doc, HTMLFormElementImpl *f)
2129     : HTMLGenericFormElementImpl(doc, f), m_options(0)
2130 {
2131     m_multiple = false;
2132     m_recalcListItems = false;
2133     // 0 means invalid (i.e. not set)
2134     m_size = 0;
2135     m_minwidth = 0;
2136 }
2137
2138 HTMLSelectElementImpl::~HTMLSelectElementImpl()
2139 {
2140     if (getDocument()) getDocument()->deregisterMaintainsState(this);
2141     if (m_options) {
2142         m_options->detach();
2143         m_options->deref();
2144     }
2145 }
2146
2147 NodeImpl::Id HTMLSelectElementImpl::id() const
2148 {
2149     return ID_SELECT;
2150 }
2151
2152 void HTMLSelectElementImpl::recalcStyle( StyleChange ch )
2153 {
2154     if (hasChangedChild() && m_render) {
2155         static_cast<khtml::RenderSelect*>(m_render)->setOptionsChanged(true);
2156     }
2157
2158     HTMLGenericFormElementImpl::recalcStyle( ch );
2159 }
2160
2161
2162 DOMString HTMLSelectElementImpl::type() const
2163 {
2164     return (m_multiple ? "select-multiple" : "select-one");
2165 }
2166
2167 long HTMLSelectElementImpl::selectedIndex() const
2168 {
2169     // return the number of the first option selected
2170     uint o = 0;
2171     QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2172     for (unsigned int i = 0; i < items.size(); i++) {
2173         if (items[i]->id() == ID_OPTION) {
2174             if (static_cast<HTMLOptionElementImpl*>(items[i])->selected())
2175                 return o;
2176             o++;
2177         }
2178     }
2179     Q_ASSERT(m_multiple);
2180     return -1;
2181 }
2182
2183 void HTMLSelectElementImpl::setSelectedIndex( long  index )
2184 {
2185     // deselect all other options and select only the new one
2186     QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2187     int listIndex;
2188     for (listIndex = 0; listIndex < int(items.size()); listIndex++) {
2189         if (items[listIndex]->id() == ID_OPTION)
2190             static_cast<HTMLOptionElementImpl*>(items[listIndex])->setSelected(false);
2191     }
2192     listIndex = optionToListIndex(index);
2193     if (listIndex >= 0)
2194         static_cast<HTMLOptionElementImpl*>(items[listIndex])->setSelected(true);
2195
2196     setChanged(true);
2197 }
2198
2199 long HTMLSelectElementImpl::length() const
2200 {
2201     int len = 0;
2202     uint i;
2203     QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2204     for (i = 0; i < items.size(); i++) {
2205         if (items[i]->id() == ID_OPTION)
2206             len++;
2207     }
2208     return len;
2209 }
2210
2211 void HTMLSelectElementImpl::add( HTMLElementImpl *element, HTMLElementImpl *before )
2212 {
2213     if (!element || element->id() != ID_OPTION)
2214         return;
2215
2216     int exceptioncode = 0;
2217     insertBefore(element, before, exceptioncode);
2218     if (!exceptioncode)
2219         setRecalcListItems();
2220 }
2221
2222 void HTMLSelectElementImpl::remove( long index )
2223 {
2224     int exceptioncode = 0;
2225     int listIndex = optionToListIndex(index);
2226
2227     QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2228     if(listIndex < 0 || index >= int(items.size()))
2229         return; // ### what should we do ? remove the last item?
2230
2231     removeChild(items[listIndex], exceptioncode);
2232     if( !exceptioncode )
2233         setRecalcListItems();
2234 }
2235
2236 void HTMLSelectElementImpl::blur()
2237 {
2238     if(getDocument()->focusNode() == this)
2239         getDocument()->setFocusNode(0);
2240 }
2241
2242 void HTMLSelectElementImpl::focus()
2243 {
2244     getDocument()->setFocusNode(this);
2245 }
2246
2247 DOMString HTMLSelectElementImpl::value( )
2248 {
2249     uint i;
2250     QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2251     for (i = 0; i < items.size(); i++) {
2252         if ( items[i]->id() == ID_OPTION
2253             && static_cast<HTMLOptionElementImpl*>(items[i])->selected())
2254             return static_cast<HTMLOptionElementImpl*>(items[i])->value();
2255     }
2256     return DOMString("");
2257 }
2258
2259 void HTMLSelectElementImpl::setValue(DOMStringImpl* value)
2260 {
2261     // find the option with value() matching the given parameter
2262     // and make it the current selection.
2263     QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2264     for (unsigned i = 0; i < items.size(); i++)
2265         if (items[i]->id() == ID_OPTION && static_cast<HTMLOptionElementImpl*>(items[i])->value() == value) {
2266             static_cast<HTMLOptionElementImpl*>(items[i])->setSelected(true);
2267             return;
2268         }
2269 }
2270
2271 QString HTMLSelectElementImpl::state( )
2272 {
2273 #if !APPLE_CHANGES
2274     QString state;
2275 #endif
2276     QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2277
2278     int l = items.count();
2279
2280 #if APPLE_CHANGES
2281     QChar stateChars[l];
2282     
2283     for(int i = 0; i < l; i++)
2284         if(items[i]->id() == ID_OPTION && static_cast<HTMLOptionElementImpl*>(items[i])->selected())
2285             stateChars[i] = 'X';
2286         else
2287             stateChars[i] = '.';
2288     QString state(stateChars, l);
2289 #else /* APPLE_CHANGES not defined */
2290     state.fill('.', l);
2291     for(int i = 0; i < l; i++)
2292         if(items[i]->id() == ID_OPTION && static_cast<HTMLOptionElementImpl*>(items[i])->selected())
2293             state[i] = 'X';
2294 #endif /* APPLE_CHANGES not defined */
2295
2296     return HTMLGenericFormElementImpl::state() + state;
2297 }
2298
2299 void HTMLSelectElementImpl::restoreState(QStringList &_states)
2300 {
2301     QString _state = HTMLGenericFormElementImpl::findMatchingState(_states);
2302     if (_state.isNull()) return;
2303
2304     recalcListItems();
2305
2306     QString state = _state;
2307     if(!state.isEmpty() && !state.contains('X') && !m_multiple) {
2308         qWarning("should not happen in restoreState!");
2309 #if APPLE_CHANGES
2310         // KWQString doesn't support this operation. Should never get here anyway.
2311         //state[0] = 'X';
2312 #else
2313         state[0] = 'X';
2314 #endif
2315     }
2316
2317     QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2318
2319     int l = items.count();
2320     for(int i = 0; i < l; i++) {
2321         if(items[i]->id() == ID_OPTION) {
2322             HTMLOptionElementImpl* oe = static_cast<HTMLOptionElementImpl*>(items[i]);
2323             oe->setSelected(state[i] == 'X');
2324         }
2325     }
2326     setChanged(true);
2327 }
2328
2329 NodeImpl *HTMLSelectElementImpl::insertBefore ( NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode )
2330 {
2331     NodeImpl *result = HTMLGenericFormElementImpl::insertBefore(newChild,refChild, exceptioncode );
2332     if (!exceptioncode)
2333         setRecalcListItems();
2334     return result;
2335 }
2336
2337 NodeImpl *HTMLSelectElementImpl::replaceChild ( NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode )
2338 {
2339     NodeImpl *result = HTMLGenericFormElementImpl::replaceChild(newChild,oldChild, exceptioncode);
2340     if( !exceptioncode )
2341         setRecalcListItems();
2342     return result;
2343 }
2344
2345 NodeImpl *HTMLSelectElementImpl::removeChild ( NodeImpl *oldChild, int &exceptioncode )
2346 {
2347     NodeImpl *result = HTMLGenericFormElementImpl::removeChild(oldChild, exceptioncode);
2348     if( !exceptioncode )
2349         setRecalcListItems();
2350     return result;
2351 }
2352
2353 NodeImpl *HTMLSelectElementImpl::appendChild ( NodeImpl *newChild, int &exceptioncode )
2354 {
2355     NodeImpl *result = HTMLGenericFormElementImpl::appendChild(newChild, exceptioncode);
2356     if( !exceptioncode )
2357         setRecalcListItems();
2358     setChanged(true);
2359     return result;
2360 }
2361
2362 NodeImpl* HTMLSelectElementImpl::addChild(NodeImpl* newChild)
2363 {
2364     setRecalcListItems();
2365     return HTMLGenericFormElementImpl::addChild(newChild);
2366 }
2367
2368 void HTMLSelectElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
2369 {
2370     switch(attr->id())
2371     {
2372     case ATTR_SIZE:
2373         m_size = QMAX( attr->value().toInt(), 1 );
2374         break;
2375     case ATTR_WIDTH:
2376         m_minwidth = QMAX( attr->value().toInt(), 0 );
2377         break;
2378     case ATTR_MULTIPLE:
2379         m_multiple = (!attr->isNull());
2380         break;
2381     case ATTR_ACCESSKEY:
2382         // ### ignore for the moment
2383         break;
2384     case ATTR_ONFOCUS:
2385         setHTMLEventListener(EventImpl::FOCUS_EVENT,
2386             getDocument()->createHTMLEventListener(attr->value().string()));
2387         break;
2388     case ATTR_ONBLUR:
2389         setHTMLEventListener(EventImpl::BLUR_EVENT,
2390             getDocument()->createHTMLEventListener(attr->value().string()));
2391         break;
2392     case ATTR_ONCHANGE:
2393         setHTMLEventListener(EventImpl::CHANGE_EVENT,
2394             getDocument()->createHTMLEventListener(attr->value().string()));
2395         break;
2396     default:
2397         HTMLGenericFormElementImpl::parseHTMLAttribute(attr);
2398     }
2399 }
2400
2401 RenderObject *HTMLSelectElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
2402 {
2403     return new (arena) RenderSelect(this);
2404 }
2405
2406 bool HTMLSelectElementImpl::appendFormData(FormDataList& encoded_values, bool)
2407 {
2408     bool successful = false;
2409     QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2410
2411     uint i;
2412     for (i = 0; i < items.size(); i++) {
2413         if (items[i]->id() == ID_OPTION) {
2414             HTMLOptionElementImpl *option = static_cast<HTMLOptionElementImpl*>(items[i]);
2415             if (option->selected()) {
2416                 encoded_values.appendData(name(), option->value());
2417                 successful = true;
2418             }
2419         }
2420     }
2421
2422     // ### this case should not happen. make sure that we select the first option
2423     // in any case. otherwise we have no consistency with the DOM interface. FIXME!
2424     // we return the first one if it was a combobox select
2425     if (!successful && !m_multiple && m_size <= 1 && items.size() &&
2426         (items[0]->id() == ID_OPTION) ) {
2427         HTMLOptionElementImpl *option = static_cast<HTMLOptionElementImpl*>(items[0]);
2428         if (option->value().isNull())
2429             encoded_values.appendData(name(), option->text().string().stripWhiteSpace());
2430         else
2431             encoded_values.appendData(name(), option->value());
2432         successful = true;
2433     }
2434
2435     return successful;
2436 }
2437
2438 int HTMLSelectElementImpl::optionToListIndex(int optionIndex) const
2439 {
2440     QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2441     if (optionIndex < 0 || optionIndex >= int(items.size()))
2442         return -1;
2443
2444     int listIndex = 0;
2445     int optionIndex2 = 0;
2446     for (;
2447          optionIndex2 < int(items.size()) && optionIndex2 <= optionIndex;
2448          listIndex++) { // not a typo!
2449         if (items[listIndex]->id() == ID_OPTION)
2450             optionIndex2++;
2451     }
2452     listIndex--;
2453     return listIndex;
2454 }
2455
2456 int HTMLSelectElementImpl::listToOptionIndex(int listIndex) const
2457 {
2458     QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2459     if (listIndex < 0 || listIndex >= int(items.size()) ||
2460         items[listIndex]->id() != ID_OPTION)
2461         return -1;
2462
2463     int optionIndex = 0; // actual index of option not counting OPTGROUP entries that may be in list
2464     int i;
2465     for (i = 0; i < listIndex; i++)
2466         if (items[i]->id() == ID_OPTION)
2467             optionIndex++;
2468     return optionIndex;
2469 }
2470
2471 HTMLOptionsCollectionImpl *HTMLSelectElementImpl::options()
2472 {
2473     if (!m_options) {
2474         m_options = new HTMLOptionsCollectionImpl(this);
2475         m_options->ref();
2476     }
2477     return m_options;
2478 }
2479
2480 void HTMLSelectElementImpl::recalcListItems()
2481 {
2482     NodeImpl* current = firstChild();
2483     m_listItems.resize(0);
2484     HTMLOptionElementImpl* foundSelected = 0;
2485     while(current) {
2486         if (current->id() == ID_OPTGROUP && current->firstChild()) {
2487             // ### what if optgroup contains just comments? don't want one of no options in it...
2488             m_listItems.resize(m_listItems.size()+1);
2489             m_listItems[m_listItems.size()-1] = static_cast<HTMLGenericFormElementImpl*>(current);
2490             current = current->firstChild();
2491         }
2492         if (current->id() == ID_OPTION) {
2493             m_listItems.resize(m_listItems.size()+1);
2494             m_listItems[m_listItems.size()-1] = static_cast<HTMLGenericFormElementImpl*>(current);
2495             if (!foundSelected && !m_multiple && m_size <= 1) {
2496                 foundSelected = static_cast<HTMLOptionElementImpl*>(current);
2497                 foundSelected->m_selected = true;
2498             }
2499             else if (foundSelected && !m_multiple && static_cast<HTMLOptionElementImpl*>(current)->selected()) {
2500                 foundSelected->m_selected = false;
2501                 foundSelected = static_cast<HTMLOptionElementImpl*>(current);
2502             }
2503         }
2504         NodeImpl *parent = current->parentNode();
2505         current = current->nextSibling();
2506         if (!current) {
2507             if (parent != this)
2508                 current = parent->nextSibling();
2509         }
2510     }
2511     m_recalcListItems = false;
2512 }
2513
2514 void HTMLSelectElementImpl::childrenChanged()
2515 {
2516     setRecalcListItems();
2517
2518     HTMLGenericFormElementImpl::childrenChanged();
2519 }
2520
2521 void HTMLSelectElementImpl::setRecalcListItems()
2522 {
2523     m_recalcListItems = true;
2524     if (m_render)
2525         static_cast<khtml::RenderSelect*>(m_render)->setOptionsChanged(true);
2526     setChanged();
2527 }
2528
2529 void HTMLSelectElementImpl::reset()
2530 {
2531     QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2532     uint i;
2533     for (i = 0; i < items.size(); i++) {
2534         if (items[i]->id() == ID_OPTION) {
2535             HTMLOptionElementImpl *option = static_cast<HTMLOptionElementImpl*>(items[i]);
2536             bool selected = (!option->getAttribute(ATTR_SELECTED).isNull());
2537             option->setSelected(selected);
2538         }
2539     }
2540     if ( m_render )
2541         static_cast<RenderSelect*>(m_render)->setSelectionChanged(true);
2542     setChanged( true );
2543 }
2544
2545 void HTMLSelectElementImpl::notifyOptionSelected(HTMLOptionElementImpl *selectedOption, bool selected)
2546 {
2547     if (selected && !m_multiple) {
2548         // deselect all other options
2549         QMemArray<HTMLGenericFormElementImpl*> items = listItems();
2550         uint i;
2551         for (i = 0; i < items.size(); i++) {
2552             if (items[i]->id() == ID_OPTION)
2553                 static_cast<HTMLOptionElementImpl*>(items[i])->m_selected = (items[i] == selectedOption);
2554         }
2555     }
2556     if (m_render)
2557         static_cast<RenderSelect*>(m_render)->setSelectionChanged(true);
2558
2559     setChanged(true);
2560 }
2561
2562 #if APPLE_CHANGES
2563
2564 void HTMLSelectElementImpl::defaultEventHandler(EventImpl *evt)
2565 {
2566     // Use key press event here since sending simulated mouse events
2567     // on key down blocks the proper sending of the key press event.
2568     if (evt->id() == EventImpl::KEYPRESS_EVENT) {
2569     
2570         if (!m_form || !m_render || !evt->isKeyboardEvent())
2571             return;
2572         
2573         DOMString key = static_cast<KeyboardEventImpl *>(evt)->keyIdentifier();
2574         
2575         if (key == "Enter") {
2576             m_form->submitClick();
2577             evt->setDefaultHandled();
2578         }
2579     }
2580     HTMLGenericFormElementImpl::defaultEventHandler(evt);
2581 }
2582
2583 #endif // APPLE_CHANGES
2584
2585 void HTMLSelectElementImpl::accessKeyAction()
2586 {
2587     focus();
2588 }
2589
2590 // -------------------------------------------------------------------------
2591
2592 HTMLKeygenElementImpl::HTMLKeygenElementImpl(DocumentPtr* doc, HTMLFormElementImpl* f)
2593     : HTMLSelectElementImpl(doc, f)
2594 {
2595     QStringList keys = KSSLKeyGen::supportedKeySizes();
2596     for (QStringList::Iterator i = keys.begin(); i != keys.end(); ++i) {
2597         HTMLOptionElementImpl* o = new HTMLOptionElementImpl(doc, form());
2598         addChild(o);
2599         o->addChild(new TextImpl(doc, DOMString(*i)));
2600     }
2601 }
2602
2603 NodeImpl::Id HTMLKeygenElementImpl::id() const
2604 {
2605     return ID_KEYGEN;
2606 }
2607
2608 DOMString HTMLKeygenElementImpl::type() const
2609 {
2610     return "keygen";
2611 }
2612
2613 void HTMLKeygenElementImpl::parseHTMLAttribute(HTMLAttributeImpl* attr)
2614 {
2615     switch(attr->id())
2616     {
2617     case ATTR_CHALLENGE:
2618         m_challenge = attr->value();
2619         break;
2620     case ATTR_KEYTYPE:
2621         m_keyType = attr->value();
2622         break;
2623     default:
2624         // skip HTMLSelectElementImpl parsing!
2625         HTMLGenericFormElementImpl::parseHTMLAttribute(attr);
2626     }
2627 }
2628
2629 bool HTMLKeygenElementImpl::appendFormData(FormDataList& encoded_values, bool)
2630 {
2631 #if APPLE_CHANGES
2632     // Only RSA is supported at this time.
2633     if (!m_keyType.isNull() && strcasecmp(m_keyType, "rsa")) {
2634         return false;
2635     }
2636     QString value = KSSLKeyGen::signedPublicKeyAndChallengeString(selectedIndex(), m_challenge.string(), getDocument()->part()->baseURL());
2637     if (value.isNull()) {
2638         return false;
2639     }
2640     encoded_values.appendData(name(), value.utf8());
2641     return true;
2642 #else
2643     bool successful = false;
2644
2645     // pop up the fancy certificate creation dialog here
2646     KSSLKeyGen *kg = new KSSLKeyGen(static_cast<RenderWidget *>(m_render)->widget(), "Key Generator", true);
2647
2648     kg->setKeySize(0);
2649     successful = (QDialog::Accepted == kg->exec());
2650
2651     delete kg;
2652
2653     encoded_values.appendData(name(), "deadbeef");
2654     
2655     return successful;
2656 #endif
2657 }
2658
2659 // -------------------------------------------------------------------------
2660
2661 HTMLOptGroupElementImpl::HTMLOptGroupElementImpl(DocumentPtr *doc, HTMLFormElementImpl *f)
2662     : HTMLGenericFormElementImpl(doc, f)
2663 {
2664 }
2665
2666 HTMLOptGroupElementImpl::~HTMLOptGroupElementImpl()
2667 {
2668 }
2669
2670 bool HTMLOptGroupElementImpl::isFocusable() const
2671 {
2672     return false;
2673 }
2674
2675 NodeImpl::Id HTMLOptGroupElementImpl::id() const
2676 {
2677     return ID_OPTGROUP;
2678 }
2679
2680 DOMString HTMLOptGroupElementImpl::type() const
2681 {
2682     return "optgroup";
2683 }
2684
2685 NodeImpl *HTMLOptGroupElementImpl::insertBefore ( NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode )
2686 {
2687     NodeImpl *result = HTMLGenericFormElementImpl::insertBefore(newChild,refChild, exceptioncode);
2688     if ( !exceptioncode )
2689         recalcSelectOptions();
2690     return result;
2691 }
2692
2693 NodeImpl *HTMLOptGroupElementImpl::replaceChild ( NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode )
2694 {
2695     NodeImpl *result = HTMLGenericFormElementImpl::replaceChild(newChild,oldChild, exceptioncode);
2696     if(!exceptioncode)
2697         recalcSelectOptions();
2698     return result;
2699 }
2700
2701 NodeImpl *HTMLOptGroupElementImpl::removeChild ( NodeImpl *oldChild, int &exceptioncode )
2702 {
2703     NodeImpl *result = HTMLGenericFormElementImpl::removeChild(oldChild, exceptioncode);
2704     if( !exceptioncode )
2705         recalcSelectOptions();
2706     return result;
2707 }
2708
2709 NodeImpl *HTMLOptGroupElementImpl::appendChild ( NodeImpl *newChild, int &exceptioncode )
2710 {
2711     NodeImpl *result = HTMLGenericFormElementImpl::appendChild(newChild, exceptioncode);
2712     if( !exceptioncode )
2713         recalcSelectOptions();
2714     return result;
2715 }
2716
2717 NodeImpl* HTMLOptGroupElementImpl::addChild(NodeImpl* newChild)
2718 {
2719     recalcSelectOptions();
2720
2721     return HTMLGenericFormElementImpl::addChild(newChild);
2722 }
2723
2724 void HTMLOptGroupElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
2725 {
2726     HTMLGenericFormElementImpl::parseHTMLAttribute(attr);
2727     recalcSelectOptions();
2728 }
2729
2730 void HTMLOptGroupElementImpl::recalcSelectOptions()
2731 {
2732     NodeImpl *select = parentNode();
2733     while (select && select->id() != ID_SELECT)
2734         select = select->parentNode();
2735     if (select)
2736         static_cast<HTMLSelectElementImpl*>(select)->setRecalcListItems();
2737 }
2738
2739 // -------------------------------------------------------------------------
2740
2741 HTMLOptionElementImpl::HTMLOptionElementImpl(DocumentPtr *doc, HTMLFormElementImpl *f)
2742     : HTMLGenericFormElementImpl(doc, f)
2743 {
2744     m_selected = false;
2745 }
2746
2747 bool HTMLOptionElementImpl::isFocusable() const
2748 {
2749     return false;
2750 }
2751
2752 NodeImpl::Id HTMLOptionElementImpl::id() const
2753 {
2754     return ID_OPTION;
2755 }
2756
2757 DOMString HTMLOptionElementImpl::type() const
2758 {
2759     return "option";
2760 }
2761
2762 DOMString HTMLOptionElementImpl::text() const
2763 {
2764     DOMString label;
2765     // WinIE does not use the label attribute, so as a quirk, we ignore it.
2766     if (getDocument() && !getDocument()->inCompatMode())
2767         label = getAttribute(ATTR_LABEL);
2768     if (label.isEmpty() && firstChild() && firstChild()->nodeType() == Node::TEXT_NODE) {
2769         if (firstChild()->nextSibling()) {
2770             DOMString ret = "";
2771             NodeImpl *n = firstChild();
2772             for (; n; n = n->nextSibling()) {
2773                 if (n->nodeType() == Node::TEXT_NODE ||
2774                     n->nodeType() == Node::CDATA_SECTION_NODE)
2775                     ret += n->nodeValue();
2776             }
2777             return ret;
2778         }
2779         else
2780             return firstChild()->nodeValue();
2781     }
2782     else
2783         return label;
2784 }
2785
2786 long HTMLOptionElementImpl::index() const
2787 {
2788     // Let's do this dynamically. Might be a bit slow, but we're sure
2789     // we won't forget to update a member variable in some cases...
2790     QMemArray<HTMLGenericFormElementImpl*> items = getSelect()->listItems();
2791     int l = items.count();
2792     int optionIndex = 0;
2793     for(int i = 0; i < l; i++) {
2794         if(items[i]->id() == ID_OPTION)
2795         {
2796             if (static_cast<HTMLOptionElementImpl*>(items[i]) == this)
2797                 return optionIndex;
2798             optionIndex++;
2799         }
2800     }
2801     kdWarning() << "HTMLOptionElementImpl::index(): option not found!" << endl;
2802     return 0;
2803 }
2804
2805 void HTMLOptionElementImpl::setIndex( long  )
2806 {
2807     kdWarning() << "Unimplemented HTMLOptionElementImpl::setIndex(long) called" << endl;
2808     // ###
2809 }
2810
2811 void HTMLOptionElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
2812 {
2813     switch(attr->id())
2814     {
2815     case ATTR_SELECTED:
2816         m_selected = (!attr->isNull());
2817         break;
2818     case ATTR_VALUE:
2819         m_value = attr->value();
2820         break;
2821     default:
2822         HTMLGenericFormElementImpl::parseHTMLAttribute(attr);
2823     }
2824 }
2825
2826 DOMString HTMLOptionElementImpl::value() const
2827 {
2828     if ( !m_value.isNull() )
2829         return m_value;
2830     // Use the text if the value wasn't set.
2831     return text().string().stripWhiteSpace();
2832 }
2833
2834 void HTMLOptionElementImpl::setValue(DOMStringImpl* value)
2835 {
2836     setAttribute(ATTR_VALUE, value);
2837 }
2838
2839 void HTMLOptionElementImpl::setSelected(bool _selected)
2840 {
2841     if(m_selected == _selected)
2842         return;
2843     m_selected = _selected;
2844     HTMLSelectElementImpl *select = getSelect();
2845     if (select)
2846         select->notifyOptionSelected(this,_selected);
2847 }
2848
2849 void HTMLOptionElementImpl::childrenChanged()
2850 {
2851    HTMLSelectElementImpl *select = getSelect();
2852    if (select)
2853        select->childrenChanged();
2854 }
2855
2856 HTMLSelectElementImpl *HTMLOptionElementImpl::getSelect() const
2857 {
2858     NodeImpl *select = parentNode();
2859     while (select && select->id() != ID_SELECT)
2860         select = select->parentNode();
2861     return static_cast<HTMLSelectElementImpl*>(select);
2862 }
2863
2864 // -------------------------------------------------------------------------
2865
2866 HTMLTextAreaElementImpl::HTMLTextAreaElementImpl(DocumentPtr *doc, HTMLFormElementImpl *f)
2867     : HTMLGenericFormElementImpl(doc, f)
2868 {
2869     // DTD requires rows & cols be specified, but we will provide reasonable defaults
2870     m_rows = 2;
2871     m_cols = 20;
2872     m_wrap = ta_Virtual;
2873     m_dirtyvalue = true;
2874 }
2875
2876 HTMLTextAreaElementImpl::~HTMLTextAreaElementImpl()
2877 {
2878     if (getDocument()) getDocument()->deregisterMaintainsState(this);
2879 }
2880
2881 NodeImpl::Id HTMLTextAreaElementImpl::id() const
2882 {
2883     return ID_TEXTAREA;
2884 }
2885
2886 DOMString HTMLTextAreaElementImpl::type() const
2887 {
2888     return "textarea";
2889 }
2890
2891 QString HTMLTextAreaElementImpl::state( )
2892 {
2893     // Make sure the string is not empty!
2894     return HTMLGenericFormElementImpl::state() + value().string()+'.';
2895 }
2896
2897 void HTMLTextAreaElementImpl::restoreState(QStringList &states)
2898 {
2899     QString state = HTMLGenericFormElementImpl::findMatchingState(states);
2900     if (state.isNull()) return;
2901     setDefaultValue(state.left(state.length()-1));
2902     // the close() in the rendertree will take care of transferring defaultvalue to 'value'
2903 }
2904
2905 void HTMLTextAreaElementImpl::select(  )
2906 {
2907     if (m_render)
2908         static_cast<RenderTextArea*>(m_render)->select();
2909     onSelect();
2910 }
2911
2912 void HTMLTextAreaElementImpl::childrenChanged()
2913 {
2914     setValue(defaultValue());
2915 }
2916     
2917 void HTMLTextAreaElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
2918 {
2919     switch(attr->id())
2920     {
2921     case ATTR_ROWS:
2922         m_rows = !attr->isNull() ? attr->value().toInt() : 3;
2923         if (renderer())
2924             renderer()->setNeedsLayoutAndMinMaxRecalc();
2925         break;
2926     case ATTR_COLS:
2927         m_cols = !attr->isNull() ? attr->value().toInt() : 60;
2928         if (renderer())
2929             renderer()->setNeedsLayoutAndMinMaxRecalc();
2930         break;
2931     case ATTR_WRAP:
2932         // virtual / physical is Netscape extension of HTML 3.0, now deprecated
2933         // soft/ hard / off is recommendation for HTML 4 extension by IE and NS 4
2934         if ( strcasecmp( attr->value(), "virtual" ) == 0  || strcasecmp( attr->value(), "soft") == 0)
2935             m_wrap = ta_Virtual;
2936         else if ( strcasecmp ( attr->value(), "physical" ) == 0 || strcasecmp( attr->value(), "hard") == 0)
2937             m_wrap = ta_Physical;
2938         else if(strcasecmp( attr->value(), "on" ) == 0)
2939             m_wrap = ta_Physical;
2940         else if(strcasecmp( attr->value(), "off") == 0)
2941             m_wrap = ta_NoWrap;
2942         if (renderer())
2943             renderer()->setNeedsLayoutAndMinMaxRecalc();
2944         break;
2945     case ATTR_ACCESSKEY:
2946         // ignore for the moment
2947         break;
2948     case ATTR_ONFOCUS:
2949         setHTMLEventListener(EventImpl::FOCUS_EVENT,
2950             getDocument()->createHTMLEventListener(attr->value().string()));
2951         break;
2952     case ATTR_ONBLUR:
2953         setHTMLEventListener(EventImpl::BLUR_EVENT,
2954             getDocument()->createHTMLEventListener(attr->value().string()));
2955         break;
2956     case ATTR_ONSELECT:
2957         setHTMLEventListener(EventImpl::SELECT_EVENT,
2958             getDocument()->createHTMLEventListener(attr->value().string()));
2959         break;
2960     case ATTR_ONCHANGE:
2961         setHTMLEventListener(EventImpl::CHANGE_EVENT,
2962             getDocument()->createHTMLEventListener(attr->value().string()));
2963         break;
2964     default:
2965         HTMLGenericFormElementImpl::parseHTMLAttribute(attr);
2966     }
2967 }
2968
2969 RenderObject *HTMLTextAreaElementImpl::createRenderer(RenderArena *arena, RenderStyle *style)
2970 {
2971     return new (arena) RenderTextArea(this);
2972 }
2973
2974 bool HTMLTextAreaElementImpl::appendFormData(FormDataList& encoding, bool)
2975 {
2976     if (name().isEmpty()) return false;
2977     encoding.appendData(name(), value());
2978     return true;
2979 }
2980
2981 void HTMLTextAreaElementImpl::reset()
2982 {
2983     setValue(defaultValue());
2984 }
2985
2986 DOMString HTMLTextAreaElementImpl::value()
2987 {
2988     if ( m_dirtyvalue) {
2989         if ( m_render )
2990             m_value = static_cast<RenderTextArea*>( m_render )->text();
2991         else
2992             m_value = defaultValue().string();
2993         m_dirtyvalue = false;
2994     }
2995
2996     if ( m_value.isNull() ) return "";
2997
2998     return m_value;
2999 }
3000
3001 void HTMLTextAreaElementImpl::setValue(DOMString _value)
3002 {
3003     m_value = _value.string();
3004     m_dirtyvalue = false;
3005     setChanged(true);
3006 }
3007
3008
3009 DOMString HTMLTextAreaElementImpl::defaultValue()
3010 {
3011     DOMString val = "";
3012     // there may be comments - just grab the text nodes
3013     NodeImpl *n;
3014     for (n = firstChild(); n; n = n->nextSibling())
3015         if (n->isTextNode())
3016             val += static_cast<TextImpl*>(n)->data();
3017     if (val[0] == '\r' && val[1] == '\n') {
3018         val = val.copy();
3019         val.remove(0,2);
3020     }
3021     else if (val[0] == '\r' || val[0] == '\n') {
3022         val = val.copy();
3023         val.remove(0,1);
3024     }
3025
3026     return val;
3027 }
3028
3029 void HTMLTextAreaElementImpl::setDefaultValue(DOMString _defaultValue)
3030 {
3031     // there may be comments - remove all the text nodes and replace them with one
3032     QPtrList<NodeImpl> toRemove;
3033     NodeImpl *n;
3034     for (n = firstChild(); n; n = n->nextSibling())
3035         if (n->isTextNode())
3036             toRemove.append(n);
3037     QPtrListIterator<NodeImpl> it(toRemove);
3038     int exceptioncode = 0;
3039     for (; it.current(); ++it) {
3040         removeChild(it.current(), exceptioncode);
3041     }
3042     insertBefore(getDocument()->createTextNode(_defaultValue),firstChild(), exceptioncode);
3043     setValue(_defaultValue);
3044 }
3045
3046 void HTMLTextAreaElementImpl::blur()
3047 {
3048     if(getDocument()->focusNode() == this)
3049         getDocument()->setFocusNode(0);
3050 }
3051
3052 void HTMLTextAreaElementImpl::focus()
3053 {
3054     getDocument()->setFocusNode(this);
3055 }
3056
3057 bool HTMLTextAreaElementImpl::isEditable()
3058 {
3059     return true;
3060 }
3061
3062 void HTMLTextAreaElementImpl::accessKeyAction()
3063 {
3064     focus();
3065 }
3066
3067 // -------------------------------------------------------------------------
3068
3069 HTMLIsIndexElementImpl::HTMLIsIndexElementImpl(DocumentPtr *doc, HTMLFormElementImpl *f)
3070     : HTMLInputElementImpl(doc, f)
3071 {
3072     m_type = TEXT;
3073     setName("isindex");
3074 }
3075
3076 NodeImpl::Id HTMLIsIndexElementImpl::id() const
3077 {
3078     return ID_ISINDEX;
3079 }
3080
3081 void HTMLIsIndexElementImpl::parseHTMLAttribute(HTMLAttributeImpl* attr)
3082 {
3083     switch(attr->id())
3084     {
3085     case ATTR_PROMPT:
3086         setValue(attr->value());
3087     default:
3088         // don't call HTMLInputElement::parseHTMLAttribute here, as it would
3089         // accept attributes this element does not support
3090         HTMLGenericFormElementImpl::parseHTMLAttribute(attr);
3091     }
3092 }
3093
3094 // -------------------------------------------------------------------------
3095
3096 unsigned long HTMLOptionsCollectionImpl::length() const
3097 {
3098     // Not yet implemented.
3099     return 0;
3100 }
3101
3102 void HTMLOptionsCollectionImpl::setLength(unsigned long length)
3103 {
3104     // Not yet implemented.
3105 }
3106
3107 NodeImpl *HTMLOptionsCollectionImpl::item(unsigned long index) const
3108 {
3109     // Not yet implemented.
3110     return 0;
3111 }
3112
3113 NodeImpl *HTMLOptionsCollectionImpl::namedItem(const DOMString &name) const
3114 {
3115     // Not yet implemented.
3116     return 0;
3117 }
3118
3119 // -------------------------------------------------------------------------
3120
3121 FormDataList::FormDataList(QTextCodec *c)
3122     : m_codec(c)
3123 {
3124 }
3125
3126 void FormDataList::appendString(const QCString &s)
3127 {
3128     m_strings.append(s);
3129 }
3130
3131 void FormDataList::appendString(const QString &s)
3132 {
3133     QCString cstr = fixLineBreaks(m_codec->fromUnicode(s));
3134     cstr.truncate(cstr.length());
3135     m_strings.append(cstr);
3136 }
3137
3138 QValueListConstIterator<QCString> FormDataList::begin() const
3139 {
3140     return m_strings.begin();
3141 }
3142
3143 QValueListConstIterator<QCString> FormDataList::end() const
3144 {
3145     return m_strings.end();
3146 }
3147
3148 } // namespace