File indexing completed on 2024-05-05 16:10:11

0001 /*
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
0005  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
0006  *           (C) 2001 Dirk Mueller (mueller@kde.org)
0007  *           (C) 2004, 2005, 2006 Apple Computer, Inc.
0008  *
0009  * This library is free software; you can redistribute it and/or
0010  * modify it under the terms of the GNU Library General Public
0011  * License as published by the Free Software Foundation; either
0012  * version 2 of the License, or (at your option) any later version.
0013  *
0014  * This library is distributed in the hope that it will be useful,
0015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017  * Library General Public License for more details.
0018  *
0019  * You should have received a copy of the GNU Library General Public License
0020  * along with this library; see the file COPYING.LIB.  If not, write to
0021  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0022  * Boston, MA 02110-1301, USA.
0023  *
0024  */
0025 
0026 #undef FORMS_DEBUG
0027 //#define FORMS_DEBUG
0028 
0029 #include "html/html_formimpl.h"
0030 
0031 #include "khtmlview.h"
0032 #include "khtml_part.h"
0033 #include "html/html_documentimpl.h"
0034 #include "khtml_settings.h"
0035 
0036 #include "css/cssstyleselector.h"
0037 #include "css/cssproperties.h"
0038 #include "css/cssvalues.h"
0039 #include "xml/dom_textimpl.h"
0040 #include "xml/dom_docimpl.h"
0041 #include "xml/dom2_eventsimpl.h"
0042 #include "xml/dom_restyler.h"
0043 #include "khtml_ext.h"
0044 
0045 #include "rendering/render_form.h"
0046 
0047 #include <kcharsets.h>
0048 #include "khtml_debug.h"
0049 #include <kmessagebox.h>
0050 #include <krandom.h>
0051 #include <klocalizedstring.h>
0052 #ifndef KHTML_NO_WALLET
0053 #include <kwallet.h>
0054 #endif
0055 #include <kio/job.h>
0056 #include <kjobwidgets.h>
0057 #include <kfileitem.h>
0058 #include <qmimedatabase.h>
0059 #include <QFile>
0060 #include <QDir>
0061 #include <QTextCodec>
0062 #include <QStandardPaths>
0063 #include <QTemporaryFile>
0064 #include <QKeyEvent>
0065 
0066 // for keygen
0067 #include <ksslkeygen.h>
0068 
0069 #include <assert.h>
0070 
0071 using namespace DOM;
0072 using namespace khtml;
0073 using namespace WTF;
0074 
0075 HTMLFormElementImpl::HTMLFormElementImpl(DocumentImpl *doc, bool implicit)
0076     : HTMLElementImpl(doc)
0077 {
0078     m_implicit = implicit;
0079     m_post = false;
0080     m_multipart = false;
0081     m_autocomplete = true;
0082     m_insubmit = false;
0083     m_doingsubmit = false;
0084     m_inreset = false;
0085     m_enctype = "application/x-www-form-urlencoded";
0086     m_boundary = "----------" + KRandom::randomString(42 + 13);
0087     m_acceptcharset = "UNKNOWN";
0088     m_malformed = false;
0089 }
0090 
0091 HTMLFormElementImpl::~HTMLFormElementImpl()
0092 {
0093     if (document() && document()->view() && document()->view()->part()) {
0094         document()->view()->part()->dequeueWallet(this);
0095     }
0096     QListIterator<HTMLGenericFormElementImpl *> it(formElements);
0097     while (it.hasNext()) {
0098         it.next()->m_form = nullptr;
0099     }
0100     QListIterator<HTMLImageElementImpl *> it2(imgElements);
0101     while (it2.hasNext()) {
0102         it2.next()->m_form = nullptr;
0103     }
0104 }
0105 
0106 NodeImpl::Id HTMLFormElementImpl::id() const
0107 {
0108     return ID_FORM;
0109 }
0110 
0111 HTMLCollectionImpl *HTMLFormElementImpl::elements()
0112 {
0113     return new HTMLFormCollectionImpl(this);
0114 }
0115 
0116 DOMString HTMLFormElementImpl::target() const
0117 {
0118     return getAttribute(ATTR_TARGET);
0119 }
0120 
0121 DOMString HTMLFormElementImpl::action() const
0122 {
0123     return getAttribute(ATTR_ACTION);
0124 }
0125 
0126 long HTMLFormElementImpl::length() const
0127 {
0128     int len = 0;
0129     QListIterator<HTMLGenericFormElementImpl *> it(formElements);
0130     while (it.hasNext())
0131         if (it.next()->isEnumerable()) {
0132             ++len;
0133         }
0134 
0135     return len;
0136 }
0137 
0138 static QByteArray encodeCString(const QByteArray &e)
0139 {
0140     // https://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
0141     // safe characters like NS handles them for compatibility
0142     static const char safe[] = "-._*";
0143     QByteArray encoded((e.length() + e.count('\n')) * 3
0144                        + e.count('\r') * 3 + 1, 0);
0145     int enclen = 0;
0146     bool crmissing = false;
0147     unsigned char oldc;
0148     unsigned char c = '\0';
0149 
0150     //QCString orig(e.data(), e.size());
0151 
0152     unsigned len = e.length();
0153     for (unsigned pos = 0; pos < len; pos++) {
0154         oldc = c;
0155         c = e[pos];
0156 
0157         if (crmissing && c != '\n') {
0158             encoded[enclen++] = '%';
0159             encoded[enclen++] = '0';
0160             encoded[enclen++] = 'D';
0161             crmissing = false;
0162         }
0163 
0164         if (((c >= 'A') && (c <= 'Z')) ||
0165                 ((c >= 'a') && (c <= 'z')) ||
0166                 ((c >= '0') && (c <= '9')) ||
0167                 (strchr(safe, c))
0168            ) {
0169             encoded[enclen++] = c;
0170         } else if (c == ' ') {
0171             encoded[enclen++] = '+';
0172         } else if (c == '\n') {
0173             encoded[enclen++] = '%';
0174             encoded[enclen++] = '0';
0175             encoded[enclen++] = 'D';
0176             encoded[enclen++] = '%';
0177             encoded[enclen++] = '0';
0178             encoded[enclen++] = 'A';
0179             crmissing = false;
0180         } else if (c == '\r' && oldc != '\n') {
0181             crmissing = true;
0182         } else if (c != '\r') {
0183             encoded[enclen++] = '%';
0184             unsigned int h = c / 16;
0185             h += (h > 9) ? ('A' - 10) : '0';
0186             encoded[enclen++] = h;
0187 
0188             unsigned int l = c % 16;
0189             l += (l > 9) ? ('A' - 10) : '0';
0190             encoded[enclen++] = l;
0191         }
0192     }
0193     encoded.truncate(enclen);
0194     return encoded;
0195 }
0196 
0197 inline static QString ampersandEscape(unsigned val)
0198 {
0199     QString out;
0200     out.sprintf("&#%u;", val);
0201     return out;
0202 }
0203 
0204 inline static QString escapeUnencodeable(const QTextCodec *codec, const QString &s)
0205 {
0206     QString encString;
0207     const int len = s.length();
0208 
0209     for (int i = 0; i < len; ++i) {
0210         QChar c = s[i];
0211 
0212         // We need to hand surrogate pairs to the codec at once;
0213         // also do validity checking on those
0214         if (c.isLowSurrogate()) {
0215             c = QChar::ReplacementCharacter;
0216         } else if (c.isHighSurrogate()) {
0217             if ((i + 1) < len && s[i + 1].isLowSurrogate()) {
0218                 // A valid SP..
0219                 ++i;
0220                 QString pair = QString(c) + s[i];
0221                 if (codec->canEncode(pair)) {
0222                     encString += pair;
0223                 } else {
0224                     encString += ampersandEscape(QChar::surrogateToUcs4(c, s[i]));
0225                 }
0226                 continue;
0227             } else {
0228                 c  = QChar::ReplacementCharacter;
0229             }
0230         }
0231 
0232         // Normal characters.
0233         if (codec->canEncode(c)) {
0234             encString.append(c);
0235         } else {
0236             encString.append(ampersandEscape(c.unicode()));
0237         }
0238     }
0239     return encString;
0240 }
0241 
0242 inline static QByteArray fixUpfromUnicode(const QTextCodec *codec, const QString &s)
0243 {
0244     QByteArray str = codec->fromUnicode(escapeUnencodeable(codec, s));
0245     str.truncate(str.length());
0246     return str;
0247 }
0248 
0249 Vector<HTMLGenericFormElementImpl *> HTMLFormElementImpl::gatherInTreeOrder(NodeImpl *root,
0250         const HashSet<NodeImpl *> &toGather)
0251 {
0252     Vector<HTMLGenericFormElementImpl *> out;
0253     out.reserveCapacity(toGather.size());
0254 
0255     for (NodeImpl *cur = root; cur; cur = cur->traverseNextNode(root)) {
0256         if (toGather.contains(cur)) {
0257             out.append(static_cast<HTMLGenericFormElementImpl *>(cur));
0258         }
0259     }
0260     return out;
0261 }
0262 
0263 QByteArray HTMLFormElementImpl::formData(bool &ok)
0264 {
0265 #ifdef FORMS_DEBUG
0266     qCDebug(KHTML_LOG) << "form: formData()";
0267 #endif
0268 
0269     QByteArray form_data;
0270     QByteArray enc_string; // used for non-multipart data
0271 
0272     bool useMultipart = m_multipart && m_post; // as multipart is ignored for GET
0273 
0274     // find out the QTextcodec to use
0275     QString str = m_acceptcharset.string();
0276     QTextCodec *codec = nullptr;
0277     bool codecOk = false;
0278 
0279     if (str == "UNKNOWN") {
0280         // no accept-charset attribute, try document encoding
0281         KHTMLView *view = document()->view();
0282         if (view && view->part()) {
0283             codec = KCharsets::charsets()->codecForName(view->part()->encoding(), codecOk);
0284         }
0285     }
0286 
0287     if (!codecOk) {
0288         str.replace(QLatin1Char(','), QLatin1Char(' '));
0289         const QStringList charsets = str.split(QLatin1Char(' '), QString::SkipEmptyParts);
0290         for (QStringList::ConstIterator it = charsets.constBegin(); it != charsets.constEnd(); ++it) {
0291             codec = KCharsets::charsets()->codecForName((*it), codecOk);
0292             if (codecOk) {
0293                 break;
0294             }
0295         }
0296     }
0297 
0298     if (!codecOk) {
0299         // none of requested could be used fallback to UTF-8
0300         codec = KCharsets::charsets()->codecForName("UTF-8", codecOk);
0301     }
0302 
0303     if (!codecOk) {
0304         codec = QTextCodec::codecForLocale();
0305     }
0306 
0307     // we need to map visual hebrew to logical hebrew, as the web
0308     // server alsways expects responses in logical ordering
0309     if (codec->mibEnum() == 11) {
0310         codec = QTextCodec::codecForMib(85);
0311     }
0312 
0313     QStringList fileUploads, fileNotUploads;
0314 
0315     /**
0316      Frameworks such as mootools Sortables expect form element values to be submitted in
0317      tree order (and HTML5 specifies this behavior); however formElements need not be
0318      ordered thus. Hence we walk through the tree and the formElements as we go ---
0319      first in our kids, then if needed in the parent
0320     */
0321     HashSet<NodeImpl *> formElementsSet;
0322     foreach (HTMLGenericFormElementImpl *fe, formElements) {
0323         formElementsSet.add(fe);
0324     }
0325 
0326     Vector<HTMLGenericFormElementImpl *> ordered = gatherInTreeOrder(this, formElementsSet);
0327 
0328     if (ordered.size() < (unsigned)formElements.size()) {
0329         // Some of our elements not contained within us due to parsing hijinks -- scan
0330         // the entire document.
0331         ordered = gatherInTreeOrder(document()->documentElement(), formElementsSet);
0332     }
0333 
0334     for (unsigned i = 0; i < ordered.size(); ++i) {
0335         HTMLGenericFormElementImpl *const current = ordered[i];
0336         khtml::encodingList lst;
0337 
0338         if (!current->disabled() && current->encoding(codec, lst, useMultipart)) {
0339             //qCDebug(KHTML_LOG) << "adding name '" << current->name().string() << "'";
0340             khtml::encodingList::ConstIterator it = lst.constBegin();
0341             const khtml::encodingList::ConstIterator itEnd = lst.constEnd();
0342             for (; it != itEnd; ++it) {
0343                 if (!useMultipart) {
0344                     // handle ISINDEX / <input name=isindex> special
0345                     // but only if its the first entry
0346                     if (enc_string.isEmpty() && *it == "isindex") {
0347                         ++it;
0348                         enc_string += encodeCString(*it);
0349                     } else {
0350                         if (!enc_string.isEmpty()) {
0351                             enc_string += '&';
0352                         }
0353 
0354                         enc_string += encodeCString(*it);
0355                         enc_string += '=';
0356                         ++it;
0357                         enc_string += encodeCString(*it);
0358                     }
0359                 } else {
0360                     QByteArray hstr("--");
0361                     hstr += m_boundary.toLatin1();
0362                     hstr += "\r\n";
0363                     hstr += "Content-Disposition: form-data; name=\"";
0364                     hstr += (*it).data();
0365                     hstr += "\"";
0366 
0367                     // if the current type is FILE, then we also need to
0368                     // include the filename
0369                     if (current->id() == ID_INPUT &&
0370                             static_cast<HTMLInputElementImpl *>(current)->inputType() == HTMLInputElementImpl::FILE) {
0371                         QUrl path;
0372                         QString val = static_cast<HTMLInputElementImpl *>(current)->value().string().trimmed();
0373                         if (!val.isEmpty() &&
0374                                 QDir::isRelativePath(val) &&
0375                                 QFile::exists(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + val)) {
0376                             path.setPath(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + val);
0377                         } else {
0378                             path = QUrl(val);
0379                         }
0380 
0381                         hstr += fixUpfromUnicode(codec, "; filename=\"" + path.fileName() + "\"");
0382                         if (path.isValid()) {
0383                             fileUploads << path.toDisplayString(QUrl::PreferLocalFile);
0384                             QMimeDatabase db;
0385                             const QMimeType mime = db.mimeTypeForUrl(path);
0386                             if (!mime.name().isEmpty()) {
0387                                 hstr += "\r\nContent-Type: ";
0388                                 hstr += mime.name().toLatin1().constData();
0389                             }
0390                         } else if (!val.isEmpty()) {
0391                             fileNotUploads << path.toDisplayString(QUrl::PreferLocalFile);
0392                         }
0393                     }
0394 
0395                     hstr += "\r\n\r\n";
0396                     ++it;
0397 
0398                     // append body
0399                     form_data.append(hstr);
0400                     form_data.append(*it);
0401                     form_data.append("\r\n");
0402 
0403                     // reset unsubmittedFormChange flag
0404                     if (current->id() == ID_INPUT &&
0405                             static_cast<HTMLInputElementImpl *>(current)->inputType() == HTMLInputElementImpl::TEXT) {
0406                         static_cast<HTMLInputElementImpl *>(current)->setUnsubmittedFormChange(false);
0407                     }
0408 
0409                     if (current->id() == ID_TEXTAREA) {
0410                         static_cast<HTMLTextAreaElementImpl *>(current)->setUnsubmittedFormChange(false);
0411                     }
0412 
0413                 }
0414             }
0415         }
0416     }
0417 
0418     if (fileNotUploads.count()) {
0419         const int result = KMessageBox::warningContinueCancelList(nullptr,
0420                            i18n("The following files will not be uploaded"
0421                                 " because they could not be found.\n"
0422                                 "Do you want to continue?"),
0423                            fileNotUploads,
0424                            i18n("Submit Confirmation"), KGuiItem(i18n("&Submit Anyway"), "dialog-ok"));
0425 
0426         if (result == KMessageBox::Cancel) {
0427             ok = false;
0428             return QByteArray();
0429         }
0430     }
0431 
0432     if (fileUploads.count()) {
0433         const int result = KMessageBox::warningContinueCancelList(nullptr,
0434                            i18n("You are about to transfer the following files from "
0435                                 "your local computer to the Internet.\n"
0436                                 "Do you really want to continue?"),
0437                            fileUploads,
0438                            i18n("Send Confirmation"), KGuiItem(i18np("&Send File", "&Send Files", fileUploads.count()), "document-export"));
0439 
0440         if (result == KMessageBox::Cancel) {
0441             ok = false;
0442             return QByteArray();
0443         }
0444     }
0445 
0446     if (useMultipart) {
0447         enc_string = QString("--" + m_boundary + "--\r\n").toLatin1().constData();
0448     }
0449 
0450     form_data.append(enc_string.constData());
0451     ok = true;
0452     return form_data;
0453 }
0454 
0455 void HTMLFormElementImpl::setEnctype(const DOMString &type)
0456 {
0457     if (type.isEmpty()) { // optimization: majority of real world cases
0458         m_enctype = "application/x-www-form-urlencoded";
0459         m_multipart = false;
0460     } else if (strcasecmp(type, "multipart/form-data") == 0) {
0461         m_enctype = "multipart/form-data";
0462         m_multipart = true;
0463     } else if (strcasecmp(type, "text/plain") == 0) {
0464         m_enctype = "text/plain";
0465         m_multipart = false;
0466     } else {
0467         m_enctype = "application/x-www-form-urlencoded";
0468         m_multipart = false;
0469     }
0470 }
0471 
0472 static QString calculateAutoFillKey(const HTMLFormElementImpl &e)
0473 {
0474     QUrl k(e.document()->URL());
0475     k.setFragment(QString());
0476     k.setQuery(QString());
0477     // ensure that we have the user / password inside the url
0478     // otherwise we might have a potential security problem
0479     // by saving passwords under wrong lookup key.
0480     const QString name = e.getAttribute(ATTR_NAME).string().trimmed();
0481     const QRegExp re("[;,!]");
0482     const QStringList url = k.url().split(re);
0483     return url[0] + '#' + name;
0484 }
0485 
0486 void HTMLFormElementImpl::doAutoFill()
0487 {
0488 #ifndef KHTML_NO_WALLET
0489     const QString key = calculateAutoFillKey(*this);
0490 
0491     if (KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(),
0492                                          KWallet::Wallet::FormDataFolder(),
0493                                          key)) {
0494         return;
0495     }
0496 
0497     // assert(view())
0498     document()->view()->part()->openWallet(this);
0499 #endif // KHTML_NO_WALLET
0500 }
0501 
0502 void HTMLFormElementImpl::walletOpened(KWallet::Wallet *w)
0503 {
0504 #ifndef KHTML_NO_WALLET
0505     assert(w);
0506     const QString key = calculateAutoFillKey(*this);
0507     if (!w->hasFolder(KWallet::Wallet::FormDataFolder())) {
0508         return; // failed
0509     }
0510     w->setFolder(KWallet::Wallet::FormDataFolder());
0511     QMap<QString, QString> map;
0512     if (w->readMap(key, map)) {
0513         return;    // failed, abort
0514     }
0515 
0516     if (document()->view()) {
0517         document()->view()->part()->addWalletFormKey(key);
0518     }
0519     for (QListIterator<HTMLGenericFormElementImpl *> it(formElements); it.hasNext();) {
0520         HTMLGenericFormElementImpl *const cur = it.next();
0521         if (cur->id() == ID_INPUT) {
0522             HTMLInputElementImpl *const current = static_cast<HTMLInputElementImpl *>(cur);
0523             if ((current->inputType() == HTMLInputElementImpl::PASSWORD ||
0524                     current->inputType() == HTMLInputElementImpl::TEXT) &&
0525                     !current->readOnly() &&
0526                     map.contains(current->name().string())) {
0527                 document()->setFocusNode(current);
0528                 current->setValue(map.value(current->name().string()));
0529             }
0530         }
0531     }
0532 #endif // KHTML_NO_WALLET
0533 }
0534 
0535 void HTMLFormElementImpl::submitFromKeyboard()
0536 {
0537     // Activate the first nondisabled submit button
0538     // if there is none, do a submit anyway if not more
0539     // than one <input type=text> or <input type=password>
0540     unsigned int inputtext = 0;
0541     for (QListIterator<HTMLGenericFormElementImpl *> it(formElements); it.hasNext();) {
0542         HTMLGenericFormElementImpl *const cur = it.next();
0543         if (cur->id() == ID_BUTTON) {
0544             HTMLButtonElementImpl *const current = static_cast<HTMLButtonElementImpl *>(cur);
0545             if (current->buttonType() == HTMLButtonElementImpl::SUBMIT && !current->disabled()) {
0546                 current->click();
0547                 return;
0548             }
0549         } else if (cur->id() == ID_INPUT) {
0550             HTMLInputElementImpl *const current = static_cast<HTMLInputElementImpl *>(cur);
0551             switch (current->inputType())  {
0552             case HTMLInputElementImpl::SUBMIT:
0553             case HTMLInputElementImpl::IMAGE:
0554                 if (!current->disabled()) {
0555                     current->click();
0556                     return;
0557                 }
0558                 break;
0559             case HTMLInputElementImpl::TEXT:
0560             case HTMLInputElementImpl::PASSWORD:
0561                 ++inputtext;
0562             default:
0563                 break;
0564             }
0565         }
0566     }
0567 
0568     if (inputtext <= 1) {
0569         prepareSubmit();
0570     }
0571 }
0572 
0573 void HTMLFormElementImpl::gatherWalletData()
0574 {
0575 #ifndef KHTML_NO_WALLET
0576     KHTMLView *const view = document()->view();
0577     // check if we have any password input's
0578     m_walletMap.clear();
0579     m_havePassword = false;
0580     m_haveTextarea = false;
0581     const QUrl formUrl(document()->URL());
0582     if (view && !view->nonPasswordStorableSite(formUrl.host())) {
0583         for (QListIterator<HTMLGenericFormElementImpl *> it(formElements); it.hasNext();) {
0584             HTMLGenericFormElementImpl *const cur = it.next();
0585             if (cur->id() == ID_INPUT)  {
0586                 HTMLInputElementImpl *const c = static_cast<HTMLInputElementImpl *>(cur);
0587                 if ((c->inputType() == HTMLInputElementImpl::TEXT ||
0588                         c->inputType() == HTMLInputElementImpl::PASSWORD) &&
0589                         !c->readOnly())  {
0590                     m_walletMap.insert(c->name().string(), c->value().string());
0591                     if (c->inputType() == HTMLInputElementImpl::PASSWORD &&
0592                             !c->value().isEmpty()) {
0593                         m_havePassword = true;
0594                     }
0595                 }
0596             } else if (cur->id() == ID_TEXTAREA) {
0597                 m_haveTextarea = true;
0598             }
0599         }
0600     }
0601 #endif // KHTML_NO_WALLET
0602 }
0603 
0604 bool HTMLFormElementImpl::prepareSubmit()
0605 {
0606     KHTMLView *const view = document()->view();
0607     if (m_insubmit || !view || !view->part() || view->part()->onlyLocalReferences()) {
0608         return m_insubmit;
0609     }
0610 
0611     gatherWalletData();
0612 
0613     m_insubmit = true;
0614     m_doingsubmit = false;
0615 
0616     if (dispatchHTMLEvent(EventImpl::SUBMIT_EVENT, true, true) && !m_doingsubmit) {
0617         m_doingsubmit = true;
0618     }
0619 
0620     m_insubmit = false;
0621 
0622     if (m_doingsubmit) {
0623         submit();
0624     }
0625 
0626     return m_doingsubmit;
0627 }
0628 
0629 void HTMLFormElementImpl::submit()
0630 {
0631     if (m_insubmit) {
0632         m_doingsubmit = true;
0633         return;
0634     }
0635 
0636     m_insubmit = true;
0637 
0638 #ifdef FORMS_DEBUG
0639     qCDebug(KHTML_LOG) << "submitting!";
0640 #endif
0641 
0642     bool ok;
0643     KHTMLView *const view = document()->view();
0644     const QByteArray form_data = formData(ok);
0645     const QUrl formUrl(document()->URL());
0646 
0647     if (ok && view) {
0648         if (m_walletMap.isEmpty()) {
0649             gatherWalletData();
0650         }
0651 #ifndef KHTML_NO_WALLET
0652         if (m_havePassword && !m_haveTextarea && KWallet::Wallet::isEnabled()) {
0653             const QString key = calculateAutoFillKey(*this);
0654             const bool doesnotexist = KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::FormDataFolder(), key);
0655             KWallet::Wallet *const w = view->part()->wallet();
0656             bool login_changed = false;
0657 
0658             if (!doesnotexist && w) {
0659                 // check if the login information changed from what
0660                 // we had so far.
0661                 if (w->hasFolder(KWallet::Wallet::FormDataFolder())) {
0662                     w->setFolder(KWallet::Wallet::FormDataFolder());
0663                     QMap<QString, QString> map;
0664                     if (!w->readMap(key, map) && (map.count() == m_walletMap.count())) {
0665                         QMap<QString, QString>::const_iterator it = map.constBegin();
0666                         const QMap<QString, QString>::const_iterator itEnd = map.constEnd();
0667                         for (; it != itEnd; ++it)
0668                             if (it.value() != m_walletMap.value(it.key())) {
0669                                 login_changed = true;
0670                                 break;
0671                             }
0672                     } else {
0673                         login_changed = true;
0674                     }
0675                 }
0676             }
0677 
0678             if (doesnotexist || !w || login_changed) {
0679                 if (view->part()) {
0680                     view->part()->saveLoginInformation(formUrl.host(), key, m_walletMap);
0681                 }
0682             }
0683         }
0684 #endif // KHTML_NO_WALLET
0685 
0686         QString url = getAttribute(ATTR_ACTION).trimSpaces().string();
0687         // ignore base url if 'action' attribute is empty.
0688         if (url.isEmpty()) {
0689             url = formUrl.url();
0690         }
0691         if (m_post) {
0692             view->part()->submitForm("post", url, form_data,
0693                                      m_target.string(),
0694                                      enctype().string(),
0695                                      m_boundary);
0696         } else {
0697             view->part()->submitForm("get", url, form_data,
0698                                      m_target.string());
0699         }
0700     }
0701 
0702     m_walletMap.clear(); // done with it
0703     m_havePassword = m_haveTextarea = false;
0704     m_doingsubmit = m_insubmit = false;
0705 }
0706 
0707 void HTMLFormElementImpl::reset()
0708 {
0709     KHTMLView *const view = document()->view();
0710     if (m_inreset || !view || !view->part()) {
0711         return;
0712     }
0713 
0714     m_inreset = true;
0715 
0716 #ifdef FORMS_DEBUG
0717     qCDebug(KHTML_LOG) << "reset pressed!";
0718 #endif
0719 
0720     // ### DOM2 labels this event as not cancelable, however
0721     // common browsers( sick! ) allow it be canceled.
0722     if (!dispatchHTMLEvent(EventImpl::RESET_EVENT, true, true)) {
0723         m_inreset = false;
0724         return;
0725     }
0726 
0727     for (QListIterator<HTMLGenericFormElementImpl *> it(formElements); it.hasNext();) {
0728         it.next()->reset();
0729     }
0730 
0731     m_inreset = false;
0732 }
0733 
0734 void HTMLFormElementImpl::parseAttribute(AttributeImpl *attr)
0735 {
0736     switch (attr->id()) {
0737     case ATTR_ACTION:
0738         break;
0739     case ATTR_TARGET:
0740         m_target = attr->value();
0741         break;
0742     case ATTR_METHOD:
0743         m_post = (strcasecmp(attr->value(), "post") == 0);
0744         break;
0745     case ATTR_ENCTYPE:
0746         setEnctype(attr->value());
0747         break;
0748     case ATTR_ACCEPT_CHARSET:
0749         // space separated list of charsets the server
0750         // accepts - see rfc2045
0751         m_acceptcharset = attr->value();
0752         break;
0753     case ATTR_ACCEPT:
0754         // ignore this one for the moment...
0755         break;
0756     case ATTR_AUTOCOMPLETE:
0757         m_autocomplete = strcasecmp(attr->value(), "off");
0758         break;
0759     case ATTR_ONSUBMIT:
0760         setHTMLEventListener(EventImpl::SUBMIT_EVENT,
0761                              document()->createHTMLEventListener(attr->value().string(), "onsubmit", this));
0762         break;
0763     case ATTR_ONRESET:
0764         setHTMLEventListener(EventImpl::RESET_EVENT,
0765                              document()->createHTMLEventListener(attr->value().string(), "onreset", this));
0766         break;
0767     case ATTR_NAME:
0768         if (inDocument() && m_name != attr->value()) {
0769             document()->underDocNamedCache().remove(m_name,        this);
0770             document()->underDocNamedCache().add(attr->value(), this);
0771         }
0772         m_name = attr->value();
0773     //Fallthrough intentional
0774     default:
0775         HTMLElementImpl::parseAttribute(attr);
0776     }
0777 }
0778 
0779 void HTMLFormElementImpl::removedFromDocument()
0780 {
0781     document()->underDocNamedCache().remove(m_name, this);
0782     HTMLElementImpl::removedFromDocument();
0783 }
0784 
0785 void HTMLFormElementImpl::insertedIntoDocument()
0786 {
0787     document()->underDocNamedCache().add(m_name, this);
0788     HTMLElementImpl::insertedIntoDocument();
0789 }
0790 
0791 void HTMLFormElementImpl::removeId(const DOMString &id)
0792 {
0793     document()->underDocNamedCache().remove(id, this);
0794     HTMLElementImpl::removeId(id);
0795 }
0796 
0797 void HTMLFormElementImpl::addId(const DOMString &id)
0798 {
0799     document()->underDocNamedCache().add(id, this);
0800     HTMLElementImpl::addId(id);
0801 }
0802 
0803 void HTMLFormElementImpl::uncheckOtherRadioButtonsInGroup(HTMLGenericFormElementImpl *caller, bool setDefaultChecked)
0804 {
0805     for (QListIterator<HTMLGenericFormElementImpl *> it(formElements); it.hasNext();) {
0806         HTMLGenericFormElementImpl *const current = it.next();
0807         if (current->id() == ID_INPUT &&
0808                 static_cast<HTMLInputElementImpl *>(current)->inputType() == HTMLInputElementImpl::RADIO &&
0809                 current != caller && current->form() == caller->form() && current->name() == caller->name()) {
0810             static_cast<HTMLInputElementImpl *>(current)->setChecked(false, setDefaultChecked);
0811         }
0812     }
0813 }
0814 
0815 void HTMLFormElementImpl::registerFormElement(HTMLGenericFormElementImpl *e)
0816 {
0817     formElements.append(e);
0818 }
0819 
0820 void HTMLFormElementImpl::removeFormElement(HTMLGenericFormElementImpl *e)
0821 {
0822     int i = formElements.indexOf(e);
0823     if (i != -1) {
0824         formElements.removeAt(i);
0825     }
0826 
0827     if (e->hasPastNames()) {
0828         QMutableHashIterator<DOMString, HTMLGenericFormElementImpl *> it(m_pastNamesMap);
0829         while (it.hasNext()) {
0830             it.next();
0831             if (it.value() == e) {
0832                 it.remove();
0833             }
0834         }
0835     }
0836 }
0837 
0838 void HTMLFormElementImpl::registerImgElement(HTMLImageElementImpl *e)
0839 {
0840     imgElements.append(e);
0841 }
0842 
0843 void HTMLFormElementImpl::removeImgElement(HTMLImageElementImpl *e)
0844 {
0845     int i = imgElements.indexOf(e);
0846     if (i != -1) {
0847         imgElements.removeAt(i);
0848     }
0849 }
0850 
0851 HTMLGenericFormElementImpl *HTMLFormElementImpl::lookupByPastName(const DOMString &id)
0852 {
0853     return m_pastNamesMap.value(id);
0854 }
0855 
0856 void HTMLFormElementImpl::bindPastName(HTMLGenericFormElementImpl *e)
0857 {
0858     DOMString id = e->getAttribute(ATTR_ID);
0859     if (!id.isEmpty()) {
0860         m_pastNamesMap.insert(id, e);
0861     }
0862 
0863     DOMString nm = e->getAttribute(ATTR_NAME);
0864     if (!nm.isEmpty()) {
0865         m_pastNamesMap.insert(nm, e);
0866     }
0867     e->setHasPastNames();
0868 }
0869 
0870 // -------------------------------------------------------------------------
0871 
0872 HTMLGenericFormElementImpl::HTMLGenericFormElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
0873     : HTMLElementImpl(doc)
0874 {
0875     m_disabled = m_readOnly = m_hasPastNames = false;
0876     m_name = nullptr;
0877 
0878     if (f) {
0879         m_form = f;
0880     } else {
0881         m_form = getForm();
0882     }
0883     if (m_form) {
0884         m_form->registerFormElement(this);
0885     }
0886 }
0887 
0888 void HTMLGenericFormElementImpl::insertedIntoDocument()
0889 {
0890     HTMLElementImpl::insertedIntoDocument();
0891 
0892     if (!m_form) {
0893         HTMLFormElementImpl *const newform = getForm();
0894         if (newform) {
0895             m_form = newform;
0896             m_form->registerFormElement(this);
0897         }
0898     }
0899 }
0900 
0901 void HTMLGenericFormElementImpl::removedFromDocument()
0902 {
0903     HTMLElementImpl::removedFromDocument();
0904 
0905     if (m_form) {
0906         m_form->removeFormElement(this);
0907     }
0908 
0909     m_form = nullptr;
0910 }
0911 
0912 HTMLGenericFormElementImpl::~HTMLGenericFormElementImpl()
0913 {
0914     if (m_form) {
0915         m_form->removeFormElement(this);
0916     }
0917     if (m_name) {
0918         m_name->deref();
0919     }
0920 }
0921 
0922 void HTMLGenericFormElementImpl::parseAttribute(AttributeImpl *attr)
0923 {
0924     switch (attr->id()) {
0925     case ATTR_DISABLED:
0926         setDisabled(attr->val() != nullptr);
0927         break;
0928     case ATTR_READONLY: {
0929         const bool m_oldreadOnly = m_readOnly;
0930         m_readOnly = attr->val() != nullptr;
0931         if (m_oldreadOnly != m_readOnly) {
0932             setChanged();
0933         }
0934         break;
0935     }
0936     default:
0937         HTMLElementImpl::parseAttribute(attr);
0938     }
0939 }
0940 
0941 void HTMLGenericFormElementImpl::attach()
0942 {
0943     assert(!attached());
0944 
0945     if (m_render) {
0946         assert(m_render->style());
0947         parentNode()->renderer()->addChild(m_render, nextRenderer());
0948     }
0949 
0950     // FIXME: This handles the case of a new form element being created by
0951     // JavaScript and inserted inside a form. What it does not handle is
0952     // a form element being moved from inside a form to outside, or from one
0953     // inside one form to another. The reason this other case is hard to fix
0954     // is that during parsing, we may have been passed a form that we are not
0955     // inside, DOM-tree-wise. If so, it's hard for us to know when we should
0956     // be removed from that form's element list.
0957     if (!m_form) {
0958         m_form = getForm();
0959         if (m_form) {
0960             m_form->registerFormElement(this);
0961         }
0962     }
0963 
0964     NodeBaseImpl::attach();
0965 
0966     // The call to updateFromElement() needs to go after the call through
0967     // to the base class's attach() because that can sometimes do a close
0968     // on the renderer.
0969     if (m_render) {
0970         m_render->updateFromElement();
0971     }
0972 
0973 }
0974 
0975 HTMLFormElementImpl *HTMLGenericFormElementImpl::getForm() const
0976 {
0977     NodeImpl *p = parentNode();
0978     while (p) {
0979         if (p->id() == ID_FORM) {
0980             return static_cast<HTMLFormElementImpl *>(p);
0981         }
0982         if (p->parentNode() && p->parentNode()->id() == ID_TABLE && p->previousSibling()) {
0983             p = p->previousSibling();
0984             continue;
0985         }
0986         p = p->parentNode();
0987     }
0988 #ifdef FORMS_DEBUG
0989     qCDebug(KHTML_LOG) << "couldn't find form!";
0990 #endif
0991     return nullptr;
0992 }
0993 
0994 DOMString HTMLGenericFormElementImpl::name() const
0995 {
0996     if (m_name) {
0997         return m_name;
0998     }
0999 
1000 // ###
1001 //     DOMString n = document()->htmlMode() != DocumentImpl::XHtml ?
1002 //                   getAttribute(ATTR_NAME) : getAttribute(ATTR_ID);
1003     const DOMString n = getAttribute(ATTR_NAME);
1004     if (n.isNull()) {
1005         return new DOMStringImpl("");
1006     }
1007 
1008     return n;
1009 }
1010 
1011 void HTMLGenericFormElementImpl::setName(const DOMString &name)
1012 {
1013     if (m_name) {
1014         m_name->deref();
1015     }
1016     m_name = name.implementation();
1017     setAttribute(ATTR_NAME, name);
1018     if (m_name) {
1019         m_name->ref();
1020     }
1021 }
1022 
1023 void HTMLGenericFormElementImpl::onSelect()
1024 {
1025     // ### make this work with new form events architecture
1026     dispatchHTMLEvent(EventImpl::SELECT_EVENT, true, false);
1027 }
1028 
1029 void HTMLGenericFormElementImpl::onChange()
1030 {
1031     // ### make this work with new form events architecture
1032     dispatchHTMLEvent(EventImpl::CHANGE_EVENT, true, false);
1033 }
1034 
1035 void HTMLGenericFormElementImpl::setDisabled(bool _disabled)
1036 {
1037     if (m_disabled != _disabled) {
1038         m_disabled = _disabled;
1039         // Trigger dynamic restyles
1040         document()->dynamicDomRestyler().restyleDependent(this, OtherStateDependency);
1041         // We need to update rendering under all circumstances
1042         if (!changed() && m_render) {
1043             m_render->updateFromElement();
1044         }
1045     }
1046 }
1047 
1048 bool HTMLGenericFormElementImpl::isFocusableImpl(FocusType ft) const
1049 {
1050     if (hasTabIndex()) {
1051         return HTMLElementImpl::isFocusableImpl(ft);
1052     }
1053 
1054     if (disabled()) {
1055         return false;
1056     }
1057 
1058     //Non-widget INPUT TYPE="image" and <BUTTON> support focus, too.
1059     if (id() == ID_INPUT && static_cast<const HTMLInputElementImpl *>(this)->inputType() == HTMLInputElementImpl::IMAGE) {
1060         return true;
1061     }
1062 
1063     if (id() == ID_BUTTON) {
1064         return true;
1065     }
1066 
1067     if (!m_render || !m_render->isWidget()) {
1068         return false;
1069     }
1070 
1071     QWidget *widget = static_cast<RenderWidget *>(m_render)->widget();
1072     return widget && widget->focusPolicy() >= Qt::TabFocus;
1073 }
1074 
1075 class FocusHandleWidget : public QWidget
1076 {
1077 public:
1078     void focusNextPrev(bool n)
1079     {
1080         if (!focusNextPrevChild(n) && inherits("QTextEdit")) {
1081             QWidget::focusNextPrevChild(n);
1082         }
1083     }
1084 };
1085 
1086 void HTMLGenericFormElementImpl::defaultEventHandler(EventImpl *evt)
1087 {
1088     if (evt->target() == this && renderer() && renderer()->isWidget()) {
1089         switch (evt->id())  {
1090         case EventImpl::MOUSEDOWN_EVENT:
1091         case EventImpl::MOUSEUP_EVENT:
1092         case EventImpl::MOUSEMOVE_EVENT:
1093         case EventImpl::MOUSEOUT_EVENT:
1094         case EventImpl::MOUSEOVER_EVENT:
1095         case EventImpl::KHTML_MOUSEWHEEL_EVENT:
1096         case EventImpl::KEYDOWN_EVENT:
1097         case EventImpl::KEYUP_EVENT:
1098         case EventImpl::KEYPRESS_EVENT:
1099         case EventImpl::DOMFOCUSIN_EVENT:
1100         case EventImpl::DOMFOCUSOUT_EVENT:
1101             if (static_cast<RenderWidget *>(renderer())->handleEvent(*evt)) {
1102                 evt->setDefaultHandled();
1103             }
1104         default:
1105             break;
1106         }
1107     }
1108 
1109     if (evt->target() == this && !m_disabled) {
1110         // Report focus in/out changes to the browser extension (editable widgets only)
1111         KHTMLView *const view = document()->view();
1112         if (view && evt->id() == EventImpl::DOMFOCUSIN_EVENT && isEditable() && m_render && m_render->isWidget()) {
1113             KHTMLPartBrowserExtension *ext = static_cast<KHTMLPartBrowserExtension *>(view->part()->browserExtension());
1114             QWidget *const widget = static_cast<RenderWidget *>(m_render)->widget();
1115             if (ext) {
1116                 ext->editableWidgetFocused(widget);
1117             }
1118         }
1119         if (evt->id() == EventImpl::MOUSEDOWN_EVENT || evt->id() == EventImpl::KEYDOWN_EVENT) {
1120             setActive();
1121             if (renderer() && renderer()->isWidget()) {
1122                 static_cast<RenderWidget *>(renderer())->widget()->setFocus();    // ### mmh..
1123             }
1124         } else if (evt->id() == EventImpl::MOUSEUP_EVENT || evt->id() == EventImpl::KEYUP_EVENT) {
1125             if (m_active) {
1126                 setActive(false);
1127                 setFocus();
1128             } else {
1129                 setActive(false);
1130             }
1131         }
1132 
1133         if (!evt->defaultHandled() && m_render && m_render->isWidget()) {
1134             // handle tabbing out, either from a single or repeated key event.
1135             if (evt->id() == EventImpl::KEYPRESS_EVENT && evt->isKeyRelatedEvent()) {
1136                 QKeyEvent *const k = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent();
1137                 if (k && (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) &&
1138                         (k->modifiers() & Qt::ControlModifier) == 0) {
1139                     QWidget *const widget = static_cast<RenderWidget *>(m_render)->widget();
1140                     if (widget) {
1141                         static_cast<FocusHandleWidget *>(widget)
1142                         ->focusNextPrev(k->key() == Qt::Key_Tab);
1143                     }
1144                     evt->setDefaultHandled();
1145                 }
1146             }
1147         }
1148 
1149         if (view && evt->id() == EventImpl::DOMFOCUSOUT_EVENT && isEditable() && m_render && m_render->isWidget()) {
1150             KHTMLPartBrowserExtension *const ext = static_cast<KHTMLPartBrowserExtension *>(view->part()->browserExtension());
1151             QWidget *const widget = static_cast<RenderWidget *>(m_render)->widget();
1152             if (ext) {
1153                 ext->editableWidgetBlurred(widget);
1154             }
1155 
1156             // ### Don't count popup as a valid reason for losing the focus (example: opening the options of a select
1157             // combobox shouldn't emit onblur)
1158         }
1159     }
1160     if (evt->target() == this && evt->isMouseEvent() && evt->id() != EventImpl::KHTML_MOUSEWHEEL_EVENT && renderer()) {
1161         evt->setDefaultHandled();
1162     }
1163 
1164     HTMLElementImpl::defaultEventHandler(evt);
1165 }
1166 
1167 bool HTMLGenericFormElementImpl::isEditable()
1168 {
1169     return false;
1170 }
1171 
1172 // -------------------------------------------------------------------------
1173 
1174 HTMLButtonElementImpl::HTMLButtonElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
1175     : HTMLGenericFormElementImpl(doc, f)
1176 {
1177     m_clicked = false;
1178     m_type = SUBMIT;
1179     m_dirty = true;
1180     m_activeSubmit = false;
1181 }
1182 
1183 HTMLButtonElementImpl::~HTMLButtonElementImpl()
1184 {
1185 }
1186 
1187 NodeImpl::Id HTMLButtonElementImpl::id() const
1188 {
1189     return ID_BUTTON;
1190 }
1191 
1192 DOMString HTMLButtonElementImpl::type() const
1193 {
1194     switch (m_type) {
1195     case SUBMIT:
1196         return "submit";
1197     case RESET:
1198         return "reset";
1199     case BUTTON:
1200         return "button";
1201     }
1202     return "";
1203 }
1204 
1205 void HTMLButtonElementImpl::parseAttribute(AttributeImpl *attr)
1206 {
1207     switch (attr->id()) {
1208     case ATTR_TYPE:
1209         // Per WF2.0, any invalid values are to be ignored -- and hence
1210         // handled as the default, SUBMIT.
1211         m_type = SUBMIT;
1212         if (strcasecmp(attr->value(), "reset") == 0) {
1213             m_type = RESET;
1214         } else if (strcasecmp(attr->value(), "button") == 0) {
1215             m_type = BUTTON;
1216         }
1217         break;
1218     case ATTR_VALUE:
1219         m_value = attr->value();
1220         m_currValue = m_value.string();
1221         break;
1222     case ATTR_ACCESSKEY:
1223         break;
1224     case ATTR_ALIGN:
1225         break;
1226     default:
1227         HTMLGenericFormElementImpl::parseAttribute(attr);
1228     }
1229 }
1230 
1231 void HTMLButtonElementImpl::defaultEventHandler(EventImpl *evt)
1232 {
1233     if (m_type != BUTTON && !m_disabled) {
1234         bool act = (evt->id() == EventImpl::DOMACTIVATE_EVENT);
1235         if (!act && evt->id() == EventImpl::KEYUP_EVENT && evt->isKeyRelatedEvent()) {
1236             QKeyEvent *const ke = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent();
1237             if (ke && active() && (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Space)) {
1238                 act = true;
1239             }
1240         }
1241         if (act) {
1242             activate();
1243         }
1244     }
1245     HTMLGenericFormElementImpl::defaultEventHandler(evt);
1246 }
1247 
1248 void HTMLButtonElementImpl::activate()
1249 {
1250     m_clicked = true;
1251 
1252     if (m_form && m_type == SUBMIT) {
1253         m_activeSubmit = true;
1254         m_form->prepareSubmit();
1255         m_activeSubmit = false; // in case we were canceled
1256     }
1257     if (m_form && m_type == RESET) {
1258         m_form->reset();
1259     }
1260 }
1261 
1262 void HTMLButtonElementImpl::click()
1263 {
1264     QMouseEvent me(QEvent::MouseButtonRelease, QPoint(0, 0), Qt::LeftButton, Qt::LeftButton, nullptr);
1265     dispatchMouseEvent(&me, EventImpl::CLICK_EVENT, 1);
1266 }
1267 
1268 bool HTMLButtonElementImpl::encoding(const QTextCodec *codec, khtml::encodingList &encoding, bool /*multipart*/)
1269 {
1270     if (m_type != SUBMIT || name().isEmpty() || !m_activeSubmit) {
1271         return false;
1272     }
1273 
1274     encoding += fixUpfromUnicode(codec, name().string());
1275     const QString enc_str = m_currValue.isNull() ? QString("") : m_currValue;
1276     encoding += fixUpfromUnicode(codec, enc_str);
1277 
1278     return true;
1279 }
1280 
1281 void HTMLButtonElementImpl::attach()
1282 {
1283     // skip the generic handler
1284     HTMLElementImpl::attach();
1285 }
1286 
1287 // -------------------------------------------------------------------------
1288 
1289 HTMLFieldSetElementImpl::HTMLFieldSetElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
1290     : HTMLGenericFormElementImpl(doc, f)
1291 {
1292 }
1293 
1294 HTMLFieldSetElementImpl::~HTMLFieldSetElementImpl()
1295 {
1296 }
1297 
1298 NodeImpl::Id HTMLFieldSetElementImpl::id() const
1299 {
1300     return ID_FIELDSET;
1301 }
1302 
1303 void HTMLFieldSetElementImpl::attach()
1304 {
1305     assert(!attached());
1306     assert(!m_render);
1307     assert(parentNode());
1308 
1309     RenderStyle *const _style = document()->styleSelector()->styleForElement(this);
1310     _style->ref();
1311     if (parentNode()->renderer() && parentNode()->renderer()->childAllowed() &&
1312             _style->display() != NONE) {
1313         m_render = new(document()->renderArena()) RenderFieldset(this);
1314         m_render->setStyle(_style);
1315     }
1316     HTMLGenericFormElementImpl::attach();
1317     _style->deref();
1318 }
1319 
1320 void HTMLFieldSetElementImpl::parseAttribute(AttributeImpl *attr)
1321 {
1322     HTMLElementImpl::parseAttribute(attr);
1323 }
1324 
1325 // -------------------------------------------------------------------------
1326 
1327 HTMLInputElementImpl::HTMLInputElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
1328     : HTMLGenericFormElementImpl(doc, f)
1329 {
1330     m_type = TEXT;
1331     m_maxLen = -1;
1332     m_size = 20;
1333     m_clicked = false;
1334     m_checked = false;
1335     m_defaultChecked = false;
1336     m_useDefaultChecked = true;
1337     m_indeterminate = false;
1338 
1339     m_haveType = false;
1340     m_activeSubmit = false;
1341     m_autocomplete = true;
1342     m_inited = false;
1343     m_unsubmittedFormChange = false;
1344 
1345     xPos = 0;
1346     yPos = 0;
1347 
1348     if (m_form) {
1349         m_autocomplete = f->autoComplete();
1350     }
1351 }
1352 
1353 HTMLInputElementImpl::~HTMLInputElementImpl()
1354 {
1355     if (document()) {
1356         document()->deregisterMaintainsState(this);
1357     }
1358 }
1359 
1360 NodeImpl::Id HTMLInputElementImpl::id() const
1361 {
1362     return ID_INPUT;
1363 }
1364 
1365 // Called from JS. Can't merge with parseType since we
1366 // also need to actually set ATTR_TYPE, which can't be done there.
1367 void HTMLInputElementImpl::setType(const DOMString &t)
1368 {
1369     setAttribute(ATTR_TYPE, t);
1370 }
1371 
1372 void HTMLInputElementImpl::parseType(const DOMString &t)
1373 {
1374     typeEnum newType;
1375 
1376     if (strcasecmp(t, "password") == 0) {
1377         newType = PASSWORD;
1378     } else if (strcasecmp(t, "checkbox") == 0) {
1379         newType = CHECKBOX;
1380     } else if (strcasecmp(t, "radio") == 0) {
1381         newType = RADIO;
1382     } else if (strcasecmp(t, "submit") == 0) {
1383         newType = SUBMIT;
1384     } else if (strcasecmp(t, "reset") == 0) {
1385         newType = RESET;
1386     } else if (strcasecmp(t, "file") == 0) {
1387         newType = FILE;
1388     } else if (strcasecmp(t, "hidden") == 0) {
1389         newType = HIDDEN;
1390     } else if (strcasecmp(t, "image") == 0) {
1391         newType = IMAGE;
1392     } else if (strcasecmp(t, "button") == 0) {
1393         newType = BUTTON;
1394     } else if (strcasecmp(t, "khtml_isindex") == 0) {
1395         newType = ISINDEX;
1396     } else {
1397         newType = TEXT;
1398     }
1399 
1400     // ### IMPORTANT: Don't allow the type to be changed to FILE after the first
1401     // type change, otherwise a JavaScript programmer would be able to set a text
1402     // field's value to something like /etc/passwd and then change it to a file field.
1403     if (m_type != newType) {
1404         if (newType == FILE && m_haveType) {
1405             // Set the attribute back to the old value.
1406             // Note that this calls parseAttribute again.
1407             setAttribute(ATTR_TYPE, type());
1408         } else {
1409             m_type = newType;
1410 
1411             // force reattach if need be.
1412             if (attached()) {
1413                 detach();
1414                 attach();
1415             }
1416         }
1417     }
1418     m_haveType = true;
1419 }
1420 
1421 DOMString HTMLInputElementImpl::type() const
1422 {
1423     // needs to be lowercase according to DOM spec
1424     switch (m_type) {
1425     case TEXT: return "text";
1426     case PASSWORD: return "password";
1427     case CHECKBOX: return "checkbox";
1428     case RADIO: return "radio";
1429     case SUBMIT: return "submit";
1430     case RESET: return "reset";
1431     case FILE: return "file";
1432     case HIDDEN: return "hidden";
1433     case IMAGE: return "image";
1434     case BUTTON: return "button";
1435     default: return "";
1436     }
1437 }
1438 
1439 QString HTMLInputElementImpl::state()
1440 {
1441     switch (m_type) {
1442     case PASSWORD:
1443         return QLatin1String("."); // empty string, avoid restoring
1444     case CHECKBOX:
1445     case RADIO:
1446         return QLatin1String(checked() ? "on" : "off");
1447     case TEXT:
1448         if (autoComplete() && value() != getAttribute(ATTR_VALUE) && document()->view()) {
1449             document()->view()->addFormCompletionItem(name().string(), value().string());
1450         }
1451     /* nobreak */
1452     default:
1453         return value().string() + (m_unsubmittedFormChange ? 'M' : '.') + (value().isNull() ? 'N' : '.');
1454     }
1455 }
1456 
1457 void HTMLInputElementImpl::restoreState(const QString &state)
1458 {
1459     switch (m_type) {
1460     case CHECKBOX:
1461     case RADIO:
1462         setChecked((state == QLatin1String("on")));
1463         break;
1464     case FILE:
1465         m_value = DOMString(state.left(state.length() - 2));
1466         setChanged();
1467         break;
1468     case HIDDEN:
1469     case PASSWORD:
1470         // Don't mess with those...
1471         break;
1472     default:
1473         setValue(state.endsWith('N') ? DOMString() : DOMString(state.left(state.length() - 2)));
1474         m_unsubmittedFormChange = (state.right(1) == "M");
1475         break;
1476     }
1477 }
1478 
1479 void HTMLInputElementImpl::select()
1480 {
1481     if (!m_render) {
1482         return;
1483     }
1484 
1485     if (m_type == TEXT || m_type == PASSWORD) {
1486         static_cast<RenderLineEdit *>(m_render)->select();
1487     } else if (m_type == FILE) {
1488         static_cast<RenderFileButton *>(m_render)->select();
1489     }
1490 }
1491 
1492 void HTMLInputElementImpl::click()
1493 {
1494     QMouseEvent me(QEvent::MouseButtonRelease, QPoint(0, 0), Qt::LeftButton, Qt::LeftButton, nullptr);
1495     dispatchMouseEvent(&me, 0, 1);
1496     dispatchMouseEvent(&me, EventImpl::CLICK_EVENT, 1);
1497 }
1498 
1499 void HTMLInputElementImpl::parseAttribute(AttributeImpl *attr)
1500 {
1501     switch (attr->id()) {
1502     case ATTR_AUTOCOMPLETE:
1503         m_autocomplete = strcasecmp(attr->value(), "off");
1504         break;
1505     case ATTR_TYPE:
1506         parseType(attr->value());
1507         break;
1508     case ATTR_VALUE:
1509         if (m_value.isNull()) {// We only need to setChanged if the form is looking
1510             setChanged();      // at the default value right now.
1511             if (m_type == TEXT && m_render) {
1512                 m_render->updateFromElement();
1513             }
1514         }
1515         break;
1516     case ATTR_CHECKED:
1517         m_defaultChecked = attr->val();
1518         if (m_useDefaultChecked) { // We only need to setChanged if the form is looking
1519             setChanged();    // at the default checked state right now.
1520         }
1521         break;
1522     case ATTR_MAXLENGTH: {
1523         m_maxLen = -1;
1524         if (!attr->val()) {
1525             break;
1526         }
1527         bool ok;
1528         const int ml = attr->val()->toInt(&ok);
1529         if (ml > 0 && ml < 32767) {
1530             m_maxLen = ml;
1531         } else if (ok && ml <= 0) {
1532             m_maxLen = 0;
1533         }
1534         setChanged();
1535     }
1536     break;
1537     case ATTR_SIZE:
1538         m_size = attr->val() ? attr->val()->toInt() : 20;
1539         break;
1540     case ATTR_ALT:
1541     case ATTR_SRC:
1542         if (m_type == IMAGE) {
1543             setChanged();
1544         }
1545         break;
1546     case ATTR_USEMAP:
1547         // ### ignore for the moment
1548         break;
1549     case ATTR_ALIGN:
1550         if (m_inited && m_type == IMAGE) {
1551             addHTMLAlignment(attr->value());
1552         }
1553         break;
1554     case ATTR_ACCESSKEY:
1555         break;
1556     case ATTR_WIDTH:
1557         if (m_type == IMAGE) {
1558             addCSSLength(CSS_PROP_WIDTH, attr->value());
1559         }
1560         break;
1561     case ATTR_HEIGHT:
1562         if (m_type == IMAGE) {
1563             addCSSLength(CSS_PROP_HEIGHT, attr->value());
1564         }
1565         break;
1566     case ATTR_ONSELECT:
1567         setHTMLEventListener(EventImpl::SELECT_EVENT,
1568                              document()->createHTMLEventListener(attr->value().string(), "onselect", this));
1569         break;
1570     case ATTR_ONCHANGE:
1571         setHTMLEventListener(EventImpl::CHANGE_EVENT,
1572                              document()->createHTMLEventListener(attr->value().string(), "onchange", this));
1573         break;
1574     case ATTR_PLACEHOLDER:
1575         setChanged();
1576         break;
1577     default:
1578         HTMLGenericFormElementImpl::parseAttribute(attr);
1579     }
1580 }
1581 
1582 void HTMLInputElementImpl::copyNonAttributeProperties(const ElementImpl *source)
1583 {
1584     const HTMLInputElementImpl *e =
1585         static_cast<const HTMLInputElementImpl *>(source);
1586 
1587     m_value = e->m_value;
1588     m_checked = e->m_checked;
1589     m_defaultChecked = e->m_checked;
1590     m_useDefaultChecked = e->m_defaultChecked;
1591     m_indeterminate = e->m_indeterminate;
1592     // ### copy more?
1593 
1594     HTMLGenericFormElementImpl::copyNonAttributeProperties(source);
1595 }
1596 
1597 void HTMLInputElementImpl::attach()
1598 {
1599     assert(!attached());
1600     assert(!m_render);
1601     assert(parentNode());
1602 
1603     if (!m_inited) {
1604         // FIXME: This needs to be dynamic, doesn't it, since someone could set this
1605         // after attachment?
1606         if ((uint) m_type <= ISINDEX && !m_value.isEmpty()) {
1607             const QString value = m_value.string();
1608             // remove newline stuff..
1609             QString nvalue;
1610             unsigned int valueLength = value.length();
1611             for (unsigned int i = 0; i < valueLength; ++i)
1612                 if (value[i] >= ' ') {
1613                     nvalue += value[i];
1614                 }
1615             m_value = nvalue;
1616         }
1617         m_defaultChecked = (getAttribute(ATTR_CHECKED) != nullptr);
1618         if (m_type == IMAGE) {
1619             addHTMLAlignment(getAttribute(ATTR_ALIGN));
1620         }
1621         m_inited = true;
1622     }
1623 
1624     switch (m_type) {
1625     case PASSWORD:
1626         if (document()->isHTMLDocument()) {
1627             static_cast<HTMLDocumentImpl *>(document())->setAutoFill();
1628         }
1629         break;
1630     case HIDDEN:
1631     case IMAGE:
1632         if (!getAttribute(ATTR_WIDTH).isNull()) {
1633             addCSSLength(CSS_PROP_WIDTH, getAttribute(ATTR_WIDTH));
1634         }
1635         if (!getAttribute(ATTR_HEIGHT).isNull()) {
1636             addCSSLength(CSS_PROP_HEIGHT, getAttribute(ATTR_HEIGHT));
1637         }
1638     default:
1639         break;
1640     };
1641 
1642     RenderStyle *const _style = document()->styleSelector()->styleForElement(this);
1643     _style->ref();
1644     if (parentNode()->renderer() && _style->display() != NONE) {
1645         switch (m_type) {
1646         case TEXT:
1647         case PASSWORD:
1648         case ISINDEX:      m_render = new(document()->renderArena()) RenderLineEdit(this);   break;
1649         case CHECKBOX:  m_render = new(document()->renderArena()) RenderCheckBox(this); break;
1650         case RADIO:        m_render = new(document()->renderArena()) RenderRadioButton(this); break;
1651         case SUBMIT:      m_render = new(document()->renderArena()) RenderSubmitButton(this); break;
1652         case IMAGE:       m_render =  new(document()->renderArena()) RenderImageButton(this); break;
1653         case RESET:      m_render = new(document()->renderArena()) RenderResetButton(this);   break;
1654         case FILE:         m_render =  new(document()->renderArena()) RenderFileButton(this);    break;
1655         case BUTTON:  m_render = new(document()->renderArena()) RenderPushButton(this);
1656         case HIDDEN:   break;
1657         }
1658     }
1659 
1660     // Let check and radio boxes start indeterminate
1661     setIndeterminate(true);
1662 
1663     if (m_render) {
1664         m_render->setStyle(_style);
1665     }
1666 
1667     HTMLGenericFormElementImpl::attach();
1668     _style->deref();
1669 
1670     setChecked(defaultChecked(), true);
1671 }
1672 
1673 DOMString HTMLInputElementImpl::altText() const
1674 {
1675     // https://www.w3.org/TR/1998/REC-html40-19980424/appendix/notes.html#altgen
1676     // also heavily discussed by Hixie on bugzilla
1677     // note this is intentionally different to HTMLImageElementImpl::altText()
1678     DOMString alt = getAttribute(ATTR_ALT);
1679     // fall back to title attribute
1680     if (alt.isNull()) {
1681         alt = getAttribute(ATTR_TITLE);
1682     }
1683     if (alt.isNull()) {
1684         alt = getAttribute(ATTR_VALUE);
1685     }
1686     if (alt.isEmpty()) {
1687         alt = i18n("Submit");
1688     }
1689 
1690     return alt;
1691 }
1692 
1693 bool HTMLInputElementImpl::encoding(const QTextCodec *codec, khtml::encodingList &encoding, bool multipart)
1694 {
1695     const QString nme = name().string();
1696 
1697     // image generates its own name's
1698     if (nme.isEmpty() && m_type != IMAGE) {
1699         return false;
1700     }
1701 
1702     // IMAGE needs special handling later
1703     if (m_type != IMAGE) {
1704         encoding += fixUpfromUnicode(codec, nme);
1705     }
1706 
1707     switch (m_type) {
1708     case CHECKBOX:
1709 
1710         if (checked()) {
1711             encoding += fixUpfromUnicode(codec, value().string());
1712             return true;
1713         }
1714         break;
1715 
1716     case RADIO:
1717 
1718         if (checked()) {
1719             encoding += fixUpfromUnicode(codec, value().string());
1720             return true;
1721         }
1722         break;
1723 
1724     case BUTTON:
1725     case RESET:
1726         // these types of buttons are never successful
1727         return false;
1728 
1729     case IMAGE:
1730 
1731         if (m_clicked) {
1732             m_clicked = false;
1733             QString astr(nme.isEmpty() ? QLatin1String("x") : QString(nme + ".x"));
1734 
1735             encoding += fixUpfromUnicode(codec, astr);
1736             astr.setNum(qMax(clickX(), 0));
1737             encoding += fixUpfromUnicode(codec, astr);
1738             astr = nme.isEmpty() ? QLatin1String("y") : QString(nme + ".y");
1739             encoding += fixUpfromUnicode(codec, astr);
1740             astr.setNum(qMax(clickY(), 0));
1741             encoding += fixUpfromUnicode(codec, astr);
1742             astr = value().string();
1743             if (astr.length() > 0) {
1744                 encoding += fixUpfromUnicode(codec, nme);
1745                 encoding += fixUpfromUnicode(codec, astr);
1746             }
1747 
1748             return true;
1749         }
1750         break;
1751 
1752     case SUBMIT:
1753 
1754         if (m_activeSubmit) {
1755             QString enc_str = valueWithDefault().string();
1756             if (!enc_str.isEmpty()) {
1757                 encoding += fixUpfromUnicode(codec, enc_str);
1758                 return true;
1759             }
1760         }
1761         break;
1762 
1763     case FILE: { // hmm, we have the type FILE also.  bad choice here...
1764         QString local;
1765         QUrl fileurl;
1766         QString val = value().string();
1767         if (!val.isEmpty() &&
1768                 QDir::isRelativePath(val) &&
1769                 QFile::exists(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + val)) {
1770             fileurl.setPath(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + val);
1771         } else {
1772             fileurl = QUrl(val);
1773         }
1774 
1775         // can't submit file in www-url-form encoded
1776         QWidget *const toplevel = document()->view() ? document()->view()->topLevelWidget() : nullptr;
1777         if (multipart) {
1778             QByteArray filearray;
1779             KIO::StatJob *job = KIO::stat(fileurl);
1780             KJobWidgets::setWindow(job, toplevel);
1781 
1782             if (job->exec()) {
1783                 const KFileItem fileitem(job->statResult(), fileurl, true, false);
1784                 if (fileitem.isFile()) {
1785                     QTemporaryFile tmpFile;
1786                     if (tmpFile.open()) {
1787                         KIO::FileCopyJob *job = KIO::file_copy(fileurl, QUrl::fromLocalFile(tmpFile.fileName()), -1, KIO::Overwrite);
1788                         if (job->exec()) {
1789                             filearray = tmpFile.read(tmpFile.size());
1790                         }
1791                     }
1792                 }
1793             }
1794             encoding += filearray;
1795             return true;
1796         }
1797         // else fall through
1798     }
1799     case HIDDEN:
1800     case TEXT:
1801     case PASSWORD:
1802         // always successful
1803         encoding += fixUpfromUnicode(codec, value().string());
1804         return true;
1805     case ISINDEX:
1806         encoding += fixUpfromUnicode(codec, value().string());
1807         return true;
1808     }
1809     return false;
1810 }
1811 
1812 void HTMLInputElementImpl::reset()
1813 {
1814     if (m_type == FILE) {
1815         // set directly to bypass security check. emptying the value
1816         // should mean no risk.
1817         if (!m_value.isEmpty()) {
1818             m_value = DOMString();
1819             setChanged();
1820         }
1821     } else {
1822         setValue(getAttribute(ATTR_VALUE));
1823     }
1824     m_useDefaultChecked = true;
1825     m_checked = m_defaultChecked;
1826     setIndeterminate(true);
1827 }
1828 
1829 void HTMLInputElementImpl::setChecked(bool _checked, bool setDefaultChecked)
1830 {
1831     if (m_type == RADIO && _checked && !name().isEmpty()) {
1832         // uncheck others in the group..
1833         if (m_form) {
1834             m_form->uncheckOtherRadioButtonsInGroup(this, setDefaultChecked);
1835         } else {
1836             // We're not in form, so we group with other formless radios with the same name
1837             HTMLCollectionImpl candidates(document()->documentElement(), HTMLCollectionImpl::FORMLESS_INPUT);
1838             unsigned long len = candidates.length();
1839             for (unsigned long c = 0; c < len; ++c) {
1840                 HTMLInputElementImpl *current = static_cast<HTMLInputElementImpl *>(candidates.item(c));
1841                 if (current != this && current->name() == name() && current->inputType() == HTMLInputElementImpl::RADIO) {
1842                     current->setChecked(false, setDefaultChecked);
1843                 }
1844             }
1845         }
1846     }
1847 
1848     if (setDefaultChecked) {
1849         if (defaultChecked() == _checked) {
1850             return;
1851         }
1852         m_defaultChecked = _checked;
1853         if (!m_useDefaultChecked) {
1854             return;
1855         }
1856     } else {
1857         if (checked() == _checked) {
1858             return;
1859         }
1860         m_useDefaultChecked = false;
1861         m_checked = _checked;
1862     }
1863 
1864 //     setIndeterminate(false);
1865 
1866     // Trigger dynamic restyles
1867     document()->dynamicDomRestyler().restyleDependent(this, OtherStateDependency);
1868     // We need to update rendering under all circumstances
1869     if (!changed() && m_render) {
1870         m_render->updateFromElement();
1871     }
1872 }
1873 
1874 void HTMLInputElementImpl::setIndeterminate(bool _indeterminate)
1875 {
1876     // Only checkboxes and radio-boxes honor indeterminate.
1877     if (inputType() != CHECKBOX || inputType() != RADIO || indeterminate() == _indeterminate) {
1878         return;
1879     }
1880 
1881     m_indeterminate = _indeterminate;
1882 
1883     // Trigger dynamic restyles
1884 //     document()->dynamicDomRestyler().restyleDependent(this, OtherStateDependency);
1885     // We need to update rendering under all circumstances
1886     if (!changed() && m_render) {
1887         m_render->updateFromElement();
1888     }
1889 }
1890 
1891 DOMString HTMLInputElementImpl::valueWithDefault() const
1892 {
1893     DOMString v = value();
1894     if (v.isNull()) {
1895         switch (m_type) {
1896         case RESET:
1897 #ifdef APPLE_CHANGES
1898             v = resetButtonDefaultLabel();
1899 #else
1900             v = i18n("Reset");
1901 #endif
1902             break;
1903 
1904         case SUBMIT:
1905 #ifdef APPLE_CHANGES
1906             v = submitButtonDefaultLabel();
1907 #else
1908             v = i18n("Submit");
1909 #endif
1910             break;
1911 
1912         case BUTTON:
1913         case CHECKBOX:
1914         case FILE:
1915         case HIDDEN:
1916         case IMAGE:
1917         case ISINDEX:
1918         case PASSWORD:
1919         case RADIO:
1920 #ifdef APPLE_CHANGES
1921         case RANGE:
1922         case SEARCH:
1923 #endif
1924         case TEXT:
1925             break;
1926         }
1927     }
1928     return v;
1929 }
1930 
1931 DOMString HTMLInputElementImpl::value() const
1932 {
1933     if (m_type == CHECKBOX || m_type == RADIO) {
1934         const DOMString val = getAttribute(ATTR_VALUE);
1935         // If no attribute exists, then we'll just return "on" as
1936         // other browsers strangely seem to do without respecting the
1937         // checked() state of the control.
1938         if (val.isNull()) {
1939             return DOMString("on");
1940         }
1941         return val;
1942     }
1943 
1944     DOMString val = m_value;
1945     // It's important *not* to fall back to the value attribute for file inputs,
1946     // because that would allow a malicious web page to upload files by setting the
1947     // value attribute in markup.
1948     if (val.isNull() && m_type != FILE) {
1949         val = getAttribute(ATTR_VALUE);
1950     }
1951 
1952     return val;
1953 }
1954 
1955 void HTMLInputElementImpl::setValue(DOMString val)
1956 {
1957     if (m_type == FILE) {
1958         return;
1959     }
1960 
1961     m_value = val;
1962     // ### set attribute for other types, too. no need for m_value
1963     // ### in those cases.
1964     if (m_type == RADIO || m_type == CHECKBOX) {
1965         setAttribute(ATTR_VALUE, m_value);
1966     }
1967     if (m_type == TEXT && m_render) {
1968         m_render->updateFromElement();
1969     }
1970     setChanged();
1971 }
1972 
1973 void HTMLInputElementImpl::defaultEventHandler(EventImpl *evt)
1974 {
1975     if (!m_disabled) {
1976 
1977         if (evt->isMouseEvent()) {
1978             MouseEventImpl *const me = static_cast<MouseEventImpl *>(evt);
1979             if ((m_type == RADIO || m_type == CHECKBOX)
1980                     && me->id() == EventImpl::MOUSEUP_EVENT && me->detail() > 0) {
1981 
1982                 // Did we change? Always yes for checkboxes, and for radio buttons
1983                 // only if we're not already checked.
1984                 bool changed = m_type == CHECKBOX || !checked();
1985 
1986                 // click will follow
1987                 setChecked(m_type == RADIO ? true : !checked());
1988 
1989                 if (changed) {
1990                     onChange();
1991                 }
1992             }
1993             if (evt->id() == EventImpl::CLICK_EVENT && m_type == IMAGE && m_render) {
1994                 // record the mouse position for when we get the DOMActivate event
1995                 int offsetX, offsetY;
1996                 m_render->absolutePosition(offsetX, offsetY);
1997                 xPos = me->clientX() - offsetX;
1998                 yPos = me->clientY() - offsetY;
1999                 KHTMLView *v = document()->view();
2000                 if (v) {
2001                     xPos += v->contentsX();
2002                     yPos += v->contentsY();
2003                 }
2004             }
2005         }
2006 
2007         if (m_type == RADIO || m_type == CHECKBOX || m_type == SUBMIT || m_type == RESET || m_type == BUTTON) {
2008             bool check = false;
2009             if (active() && (evt->id() == EventImpl::KEYUP_EVENT ||
2010                              evt->id() == EventImpl::KEYPRESS_EVENT)) {
2011                 TextEventImpl *const te = static_cast<TextEventImpl *>(evt);
2012                 if (te->keyVal() == ' ') {
2013                     check = true;
2014                 } else if (te->keyVal() == '\r' && (m_type == BUTTON || m_type == RESET || m_type == SUBMIT)) {
2015                     check = true;
2016                 }
2017             }
2018             if (check) {
2019                 if (evt->id() == EventImpl::KEYUP_EVENT) {
2020                     click();
2021                 }
2022                 // Tell the parent that we handle this key (keyup and keydown), even though only keyup activates (#70478)
2023                 evt->setDefaultHandled();
2024             }
2025         }
2026 
2027         // Submit form when a return is pressed in a radio/checkbox
2028         // TODO: move this for text here (from RenderLineEdit)
2029         if (m_type == CHECKBOX || m_type == RADIO) {
2030             if (evt->id() == EventImpl::KEYUP_EVENT && evt->isKeyRelatedEvent()) {
2031                 QKeyEvent *const ke = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent();
2032                 if (ke && m_form && active() && (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter)) {
2033                     m_form->submitFromKeyboard();
2034                 }
2035             }
2036         }
2037 
2038         // DOMActivate events cause the input to be "activated" - in the case of image and submit inputs, this means
2039         // actually submitting the form. For reset inputs, the form is reset. These events are sent when the user clicks
2040         // on the element, or presses enter while it is the active element. Javascript code wishing to activate the element
2041         // must dispatch a DOMActivate event - a click event will not do the job.
2042         if (m_type == IMAGE || m_type == SUBMIT || m_type == RESET) {
2043             bool act = (evt->id() == EventImpl::DOMACTIVATE_EVENT);
2044             if (!act && evt->id() == EventImpl::KEYUP_EVENT && evt->isKeyRelatedEvent()) {
2045                 QKeyEvent *const ke = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent();
2046                 if (ke && active() && (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Space)) {
2047                     act = true;
2048                 }
2049             }
2050             if (act) {
2051                 activate();
2052             }
2053         }
2054     }
2055     HTMLGenericFormElementImpl::defaultEventHandler(evt);
2056 }
2057 
2058 void HTMLInputElementImpl::activate()
2059 {
2060     if (!m_form) {
2061         return;
2062     }
2063 
2064     m_clicked = true;
2065     if (m_type == RESET) {
2066         m_form->reset();
2067     } else {
2068         m_activeSubmit = true;
2069         if (!m_form->prepareSubmit()) {
2070             xPos = 0;
2071             yPos = 0;
2072         }
2073         m_activeSubmit = false;
2074     }
2075 }
2076 
2077 bool HTMLInputElementImpl::isEditable()
2078 {
2079     return ((m_type == TEXT) || (m_type == PASSWORD) || (m_type == ISINDEX) || (m_type == FILE));
2080 }
2081 
2082 long HTMLInputElementImpl::selectionStart()
2083 {
2084     if (m_type != TEXT || !m_render) {
2085         return -1;
2086     }
2087     return static_cast<RenderLineEdit *>(m_render)->selectionStart();
2088 }
2089 
2090 long HTMLInputElementImpl::selectionEnd()
2091 {
2092     if (m_type != TEXT || !m_render) {
2093         return -1;
2094     }
2095     return static_cast<RenderLineEdit *>(m_render)->selectionEnd();
2096 }
2097 
2098 void HTMLInputElementImpl::setSelectionStart(long pos)
2099 {
2100     if (m_type != TEXT || !m_render) {
2101         return;
2102     }
2103     static_cast<RenderLineEdit *>(m_render)->setSelectionStart(pos);
2104 }
2105 
2106 void HTMLInputElementImpl::setSelectionEnd(long pos)
2107 {
2108     if (m_type != TEXT || !m_render) {
2109         return;
2110     }
2111     static_cast<RenderLineEdit *>(m_render)->setSelectionEnd(pos);
2112 }
2113 
2114 void HTMLInputElementImpl::setSelectionRange(long start, long end)
2115 {
2116     if (m_type != TEXT || !m_render) {
2117         return;
2118     }
2119     static_cast<RenderLineEdit *>(m_render)->setSelectionRange(start, end);
2120 }
2121 
2122 void HTMLInputElementImpl::setPlaceholder(const DOMString &p)
2123 {
2124     setAttribute(ATTR_PLACEHOLDER, p);
2125 }
2126 
2127 DOMString HTMLInputElementImpl::placeholder() const
2128 {
2129     return getAttribute(ATTR_PLACEHOLDER);
2130 }
2131 
2132 // -------------------------------------------------------------------------
2133 
2134 HTMLLabelElementImpl::HTMLLabelElementImpl(DocumentImpl *doc)
2135     : HTMLGenericFormElementImpl(doc)
2136 {
2137 }
2138 
2139 HTMLLabelElementImpl::~HTMLLabelElementImpl()
2140 {
2141 }
2142 
2143 NodeImpl::Id HTMLLabelElementImpl::id() const
2144 {
2145     return ID_LABEL;
2146 }
2147 
2148 void HTMLLabelElementImpl::attach()
2149 {
2150     // skip the generic handler
2151     HTMLElementImpl::attach();
2152 }
2153 
2154 bool HTMLLabelElementImpl::isFocusableImpl(FocusType ft) const
2155 {
2156     if (hasTabIndex()) {
2157         return HTMLGenericFormElementImpl::isFocusableImpl(ft);
2158     }
2159 
2160     // We want labels to accept focus on click, but not on tabbing.
2161     return (ft != FT_Tab);
2162 }
2163 
2164 NodeImpl *HTMLLabelElementImpl::getFormElement()
2165 {
2166     const DOMString formElementId = getAttribute(ATTR_FOR);
2167     NodeImpl *newNode = nullptr;
2168     if (!formElementId.isEmpty()) {
2169         newNode = document()->getElementById(formElementId);
2170     }
2171     if (!newNode) {
2172         const uint children = childNodeCount();
2173         if (children > 1)
2174             for (unsigned int i = 0; i < children; ++i) {
2175                 const uint nodeId = childNode(i)->id();
2176                 if (nodeId == ID_INPUT || nodeId == ID_SELECT || nodeId == ID_TEXTAREA) {
2177                     newNode = childNode(i);
2178                     break;
2179                 }
2180             }
2181     }
2182     return newNode;
2183 }
2184 
2185 void HTMLLabelElementImpl::defaultEventHandler(EventImpl *evt)
2186 {
2187     if (!m_disabled) {
2188         bool act = false;
2189         if (evt->id() == EventImpl::CLICK_EVENT) {
2190             act = true;
2191         } else if (evt->isKeyRelatedEvent() && (evt->id() == EventImpl::KEYUP_EVENT ||
2192                                                 evt->id() == EventImpl::KEYPRESS_EVENT)) {
2193             QKeyEvent *const ke = static_cast<KeyEventBaseImpl *>(evt)->qKeyEvent();
2194             if (ke && active() && (ke->key() == Qt::Key_Return || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Space)) {
2195                 act = true;
2196             }
2197         }
2198 
2199         if (act) {
2200             NodeImpl *const formNode = getFormElement();
2201             if (formNode && evt->target() != formNode) {
2202                 document()->setFocusNode(formNode);
2203                 if (formNode->id() == ID_INPUT && !static_cast<DOM::HTMLInputElementImpl *>(formNode)->disabled()) {
2204                     static_cast<DOM::HTMLInputElementImpl *>(formNode)->click();
2205                 }
2206                 evt->setDefaultHandled();
2207             }
2208         }
2209     }
2210     HTMLGenericFormElementImpl::defaultEventHandler(evt);
2211 }
2212 
2213 // -------------------------------------------------------------------------
2214 
2215 HTMLLegendElementImpl::HTMLLegendElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
2216     : HTMLGenericFormElementImpl(doc, f)
2217 {
2218 }
2219 
2220 HTMLLegendElementImpl::~HTMLLegendElementImpl()
2221 {
2222 }
2223 
2224 NodeImpl::Id HTMLLegendElementImpl::id() const
2225 {
2226     return ID_LEGEND;
2227 }
2228 
2229 void HTMLLegendElementImpl::attach()
2230 {
2231     assert(!attached());
2232     assert(!m_render);
2233     assert(parentNode());
2234     RenderStyle *const _style = document()->styleSelector()->styleForElement(this);
2235     _style->ref();
2236     if (parentNode()->renderer() && _style->display() != NONE) {
2237         m_render = new(document()->renderArena()) RenderLegend(this);
2238         m_render->setStyle(_style);
2239     }
2240     HTMLGenericFormElementImpl::attach();
2241     _style->deref();
2242 }
2243 
2244 void HTMLLegendElementImpl::parseAttribute(AttributeImpl *attr)
2245 {
2246     switch (attr->id()) {
2247     case ATTR_ACCESSKEY:
2248         break;
2249     default:
2250         HTMLElementImpl::parseAttribute(attr);
2251     }
2252 }
2253 
2254 // -------------------------------------------------------------------------
2255 
2256 HTMLSelectElementImpl::HTMLSelectElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
2257     : HTMLGenericFormElementImpl(doc, f)
2258 {
2259     m_multiple = false;
2260     m_recalcListItems = false;
2261     // 0 means invalid (i.e. not set)
2262     m_size = 0;
2263     m_minwidth = 0;
2264     m_length   = 0;
2265 }
2266 
2267 HTMLSelectElementImpl::~HTMLSelectElementImpl()
2268 {
2269     if (document()) {
2270         document()->deregisterMaintainsState(this);
2271     }
2272 }
2273 
2274 NodeImpl::Id HTMLSelectElementImpl::id() const
2275 {
2276     return ID_SELECT;
2277 }
2278 
2279 DOMString HTMLSelectElementImpl::type() const
2280 {
2281     return (m_multiple ? "select-multiple" : "select-one");
2282 }
2283 
2284 HTMLCollectionImpl *HTMLSelectElementImpl::options()
2285 {
2286     return new HTMLCollectionImpl(this, HTMLCollectionImpl::SELECT_OPTIONS);
2287 }
2288 
2289 long HTMLSelectElementImpl::selectedIndex() const
2290 {
2291     // return the number of the first option selected
2292     uint o = 0;
2293     const QVector<HTMLGenericFormElementImpl *> items = listItems();
2294     const unsigned int itemsSize = items.size();
2295     for (unsigned int i = 0; i < itemsSize; ++i) {
2296         if (items[i]->id() == ID_OPTION) {
2297             if (static_cast<HTMLOptionElementImpl *>(items[i])->selectedBit()) {
2298                 return o;
2299             }
2300             o++;
2301         }
2302     }
2303 //    Q_ASSERT(m_multiple || items.isEmpty());
2304     return -1;
2305 }
2306 
2307 void HTMLSelectElementImpl::setSelectedIndex(long  index)
2308 {
2309     // deselect all other options and select only the new one
2310     const QVector<HTMLGenericFormElementImpl *> items = listItems();
2311     int listIndex;
2312     const int itemsSize = int(items.size());
2313     for (listIndex = 0; listIndex < itemsSize; ++listIndex) {
2314         if (items[listIndex]->id() == ID_OPTION) {
2315             static_cast<HTMLOptionElementImpl *>(items[listIndex])->setSelected(false);
2316         }
2317     }
2318     listIndex = optionToListIndex(index);
2319     if (listIndex >= 0) {
2320         static_cast<HTMLOptionElementImpl *>(items[listIndex])->setSelected(true);
2321     }
2322 
2323     setChanged(true);
2324 }
2325 
2326 long HTMLSelectElementImpl::length() const
2327 {
2328     if (m_recalcListItems) {
2329         recalcListItems();
2330     }
2331     return m_length;
2332 }
2333 
2334 void HTMLSelectElementImpl::add(HTMLElementImpl *element, HTMLElementImpl *before, int &exceptioncode)
2335 {
2336     if (!element || element->id() != ID_OPTION) {
2337         return;
2338     }
2339 
2340     HTMLOptionElementImpl *option = static_cast<HTMLOptionElementImpl *>(element);
2341     //Fast path for appending an item. Can't be done if it is selected and
2342     //we're single-select, since we may need to drop an implicitly-selected item
2343     bool fastAppendLast = false;
2344     if (before == nullptr && (m_multiple || !option->selectedBit()) && !m_recalcListItems) {
2345         fastAppendLast = true;
2346     }
2347 
2348     insertBefore(option, before, exceptioncode);
2349 
2350     if (fastAppendLast && !exceptioncode) {
2351         m_listItems.resize(m_listItems.size() + 1);
2352         m_listItems[m_listItems.size() - 1] = option;
2353         ++m_length;
2354         if (m_length == 1 && !m_multiple) { //we added the first item in single-select --- select it.
2355             option->setSelected(true);
2356         }
2357 
2358         m_recalcListItems = false; // was set by insertBefore
2359     } else if (!exceptioncode) {
2360         setRecalcListItems();
2361     }
2362 }
2363 
2364 void HTMLSelectElementImpl::remove(long index)
2365 {
2366     int exceptioncode = 0;
2367     const int listIndex = optionToListIndex(index);
2368 
2369     const QVector<HTMLGenericFormElementImpl *> items = listItems();
2370     if (listIndex < 0 || index >= int(items.size())) {
2371         return;    // ### what should we do ? remove the last item?
2372     }
2373 
2374     //Fast path for last element, for e.g. clearing the box
2375     //Note that if this is a single-select, we may have to recompute
2376     //anyway if the item was selected, since we may want to set
2377     //a different one
2378     bool fastRemoveLast = false;
2379     if ((listIndex == items.size() - 1) && !m_recalcListItems &&
2380             (m_multiple || !static_cast<HTMLOptionElementImpl *>(items[listIndex])->selectedBit())) {
2381         fastRemoveLast = true;
2382     }
2383 
2384     removeChild(items[listIndex], exceptioncode);
2385 
2386     if (fastRemoveLast) {
2387         m_listItems.resize(m_listItems.size() - 1);
2388         --m_length;
2389         m_recalcListItems = false;
2390     } else if (!exceptioncode) {
2391         setRecalcListItems();
2392     }
2393 }
2394 
2395 HTMLOptionElementImpl *HTMLSelectElementImpl::firstSelectedItem() const
2396 {
2397     uint i;
2398     const QVector<HTMLGenericFormElementImpl *> items = listItems();
2399     const uint itemsSize = items.size();
2400     for (i = 0; i < itemsSize; ++i) {
2401         if (items[i]->id() == ID_OPTION
2402                 && static_cast<HTMLOptionElementImpl *>(items[i])->selectedBit()) {
2403             return static_cast<HTMLOptionElementImpl *>(items[i]);
2404         }
2405     }
2406     return nullptr;
2407 }
2408 
2409 DOMString HTMLSelectElementImpl::value() const
2410 {
2411     HTMLOptionElementImpl *o = firstSelectedItem();
2412     if (o) {
2413         return o->value();
2414     }
2415     return DOMString("");
2416 }
2417 
2418 void HTMLSelectElementImpl::setValue(DOMStringImpl *value)
2419 {
2420     // find the option with value() matching the given parameter
2421     // and make it the current selection.
2422     const QVector<HTMLGenericFormElementImpl *> items = listItems();
2423     for (int i = 0; i < items.size(); i++)
2424         if (items[i]->id() == ID_OPTION && static_cast<HTMLOptionElementImpl *>(items[i])->value() == value) {
2425             static_cast<HTMLOptionElementImpl *>(items[i])->setSelected(true);
2426             return;
2427         }
2428 }
2429 
2430 QString HTMLSelectElementImpl::state()
2431 {
2432     QString state;
2433     const QVector<HTMLGenericFormElementImpl *> items = listItems();
2434 
2435     const int l = items.count();
2436 
2437     // We have two encoding schemes: for single-select ones, if the selected
2438     // element has an ID, we encoded it as iFoo.
2439     if (!multiple()) {
2440         HTMLOptionElementImpl *item = firstSelectedItem();
2441         if (item && item->hasID()) {
2442             return QString::fromLatin1("i") + item->getAttribute(ATTR_ID).string();
2443         }
2444     }
2445 
2446     // Otherwise we merely go positional..
2447     state.fill('.', l);
2448     for (int i = 0; i < l; ++i)
2449         if (items[i]->id() == ID_OPTION && static_cast<HTMLOptionElementImpl *>(items[i])->selectedBit()) {
2450             state[i] = 'X';
2451         }
2452 
2453     return state;
2454 }
2455 
2456 void HTMLSelectElementImpl::restoreState(const QString &_state)
2457 {
2458     recalcListItems();
2459 
2460     QString state = _state;
2461     const QVector<HTMLGenericFormElementImpl *> items = listItems();
2462     const int l = items.count();
2463 
2464     // First see if we have an id-tagged one, and if it's correct.
2465     if (state.startsWith(QLatin1Char('i'))) {
2466         DOM::DOMString id = state.mid(1);
2467         DOM::ElementImpl *cand = document()->getElementById(id);
2468 
2469         // No such ID or not an option -> wrong
2470         if (!cand || cand->id() != ID_OPTION) {
2471             return;
2472         }
2473 
2474         // See if it's one of ours, and select if so.
2475         for (int i = 0; i < l; ++i) {
2476             if (items[i] == cand) {
2477                 static_cast<HTMLOptionElementImpl *>(cand)->setSelected(true);
2478             }
2479         }
2480 
2481         // If we succeeded, our state is updated --- if not, we better
2482         // leave this be.
2483         return;
2484     }
2485 
2486     if (!state.isEmpty() && !state.contains('X') && !m_multiple && m_size <= 1) {
2487         qWarning("should not happen in restoreState!");
2488         state[0] = 'X';
2489     }
2490 
2491     // Now we have positional encoding. Make sure the length matches.
2492     if (m_length != state.length()) // m_length == number of <option>, while
2493         // l == items.length() includes optgroups, too.
2494     {
2495         return;
2496     }
2497 
2498     for (int i = 0; i < l; ++i) {
2499         if (items[i]->id() == ID_OPTION) {
2500             HTMLOptionElementImpl *const oe = static_cast<HTMLOptionElementImpl *>(items[i]);
2501             oe->setSelected(state[i] == 'X');
2502         }
2503     }
2504     setChanged(true);
2505 }
2506 
2507 NodeImpl *HTMLSelectElementImpl::insertBefore(NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode)
2508 {
2509     NodeImpl *const result = HTMLGenericFormElementImpl::insertBefore(newChild, refChild, exceptioncode);
2510     if (!exceptioncode) {
2511         setRecalcListItems();
2512     }
2513     return result;
2514 }
2515 
2516 void HTMLSelectElementImpl::replaceChild(NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode)
2517 {
2518     HTMLGenericFormElementImpl::replaceChild(newChild, oldChild, exceptioncode);
2519     if (!exceptioncode) {
2520         setRecalcListItems();
2521     }
2522 }
2523 
2524 void HTMLSelectElementImpl::removeChild(NodeImpl *oldChild, int &exceptioncode)
2525 {
2526     HTMLGenericFormElementImpl::removeChild(oldChild, exceptioncode);
2527     if (!exceptioncode) {
2528         setRecalcListItems();
2529     }
2530 }
2531 
2532 void HTMLSelectElementImpl::removeChildren()
2533 {
2534     HTMLGenericFormElementImpl::removeChildren();
2535     setRecalcListItems();
2536 }
2537 
2538 NodeImpl *HTMLSelectElementImpl::appendChild(NodeImpl *newChild, int &exceptioncode)
2539 {
2540     NodeImpl *const result = HTMLGenericFormElementImpl::appendChild(newChild, exceptioncode);
2541     if (!exceptioncode) {
2542         setRecalcListItems();
2543     }
2544     setChanged(true);
2545     return result;
2546 }
2547 
2548 NodeImpl *HTMLSelectElementImpl::addChild(NodeImpl *newChild)
2549 {
2550     setRecalcListItems();
2551     return HTMLGenericFormElementImpl::addChild(newChild);
2552 }
2553 
2554 void HTMLSelectElementImpl::parseAttribute(AttributeImpl *attr)
2555 {
2556     switch (attr->id()) {
2557     case ATTR_SIZE:
2558         m_size = qMax(attr->value().toInt(), 1);
2559         setChanged();
2560         break;
2561     case ATTR_WIDTH:
2562         m_minwidth = qMax(attr->value().toInt(), 0);
2563         break;
2564     case ATTR_MULTIPLE:
2565         m_multiple = (attr->val() != nullptr);
2566         break;
2567     case ATTR_ACCESSKEY:
2568         break;
2569     case ATTR_ALIGN:
2570         addHTMLAlignment(attr->value());
2571         break;
2572     case ATTR_ONCHANGE:
2573         setHTMLEventListener(EventImpl::CHANGE_EVENT,
2574                              document()->createHTMLEventListener(attr->value().string(), "onchange", this));
2575         break;
2576     default:
2577         HTMLGenericFormElementImpl::parseAttribute(attr);
2578     }
2579 }
2580 
2581 void HTMLSelectElementImpl::attach()
2582 {
2583     assert(!attached());
2584     assert(parentNode());
2585     assert(!renderer());
2586 
2587     RenderStyle *const _style = document()->styleSelector()->styleForElement(this);
2588     _style->ref();
2589     if (parentNode()->renderer() && parentNode()->renderer()->childAllowed() &&
2590             _style->display() != NONE) {
2591         m_render = new(document()->renderArena()) RenderSelect(this);
2592         m_render->setStyle(_style);
2593     }
2594 
2595     HTMLGenericFormElementImpl::attach();
2596     _style->deref();
2597 }
2598 
2599 bool HTMLSelectElementImpl::encoding(const QTextCodec *codec, khtml::encodingList &encoded_values, bool)
2600 {
2601     // submitting with no name would lead to empty lhs ?=foo&=bar
2602     if (name().isEmpty()) {
2603         return false;
2604     }
2605 
2606     bool successful = false;
2607     const QByteArray enc_name = fixUpfromUnicode(codec, name().string());
2608     const QVector<HTMLGenericFormElementImpl *> items = listItems();
2609 
2610     uint i;
2611     const uint itemsSize = items.size();
2612     for (i = 0; i < itemsSize; ++i) {
2613         if (items[i]->id() == ID_OPTION) {
2614             HTMLOptionElementImpl *const option = static_cast<HTMLOptionElementImpl *>(items[i]);
2615             if (option->selectedBit() && !option->disabled()) {
2616                 encoded_values += enc_name;
2617                 encoded_values += fixUpfromUnicode(codec, option->value().string());
2618                 successful = true;
2619             }
2620         }
2621     }
2622 
2623     // ### this case should not happen. make sure that we select the first option
2624     // in any case. otherwise we have no consistency with the DOM interface. FIXME!
2625     // we return the first one if it was a combobox select
2626     if (!successful && !m_multiple && m_size <= 1 && itemsSize &&
2627             (items[0]->id() == ID_OPTION && !items[0]->disabled())) {
2628         HTMLOptionElementImpl *const option = static_cast<HTMLOptionElementImpl *>(items[0]);
2629         encoded_values += enc_name;
2630         encoded_values += fixUpfromUnicode(codec, option->value().string());
2631         successful = true;
2632     }
2633 
2634     return successful;
2635 }
2636 
2637 int HTMLSelectElementImpl::optionToListIndex(int optionIndex) const
2638 {
2639     const QVector<HTMLGenericFormElementImpl *> items = listItems();
2640     const int itemsSize = int(items.size());
2641     if (optionIndex < 0 || optionIndex >= itemsSize) {
2642         return -1;
2643     }
2644 
2645     //See if we're asked for the very last item, and check whether it's an <option>
2646     //to fastpath clear
2647     if (optionIndex == (m_length - 1) && items[itemsSize - 1]->id() == ID_OPTION) {
2648         return itemsSize - 1;
2649     }
2650 
2651     int listIndex = 0;
2652     int optionIndex2 = 0;
2653     for (;
2654             optionIndex2 < itemsSize && optionIndex2 <= optionIndex;
2655             ++listIndex) { // not a typo!
2656         if (items[listIndex]->id() == ID_OPTION) {
2657             ++optionIndex2;
2658         }
2659     }
2660     --listIndex;
2661     return listIndex;
2662 }
2663 
2664 int HTMLSelectElementImpl::listToOptionIndex(int listIndex) const
2665 {
2666     const QVector<HTMLGenericFormElementImpl *> items = listItems();
2667     if (listIndex < 0 || listIndex >= int(items.size()) ||
2668             items[listIndex]->id() != ID_OPTION) {
2669         return -1;
2670     }
2671 
2672     int optionIndex = 0; // actual index of option not counting OPTGROUP entries that may be in list
2673     int i;
2674     for (i = 0; i < listIndex; i++)
2675         if (items[i]->id() == ID_OPTION) {
2676             optionIndex++;
2677         }
2678     return optionIndex;
2679 }
2680 
2681 void HTMLSelectElementImpl::recalcListItems() const
2682 {
2683     NodeImpl *current = firstChild();
2684     m_listItems.resize(0);
2685     HTMLOptionElementImpl *foundSelected = nullptr;
2686     m_length = 0;
2687     while (current) {
2688         if (current->id() == ID_OPTGROUP && current->firstChild()) {
2689             // ### what if optgroup contains just comments? don't want one of no options in it...
2690             m_listItems.resize(m_listItems.size() + 1);
2691             m_listItems[m_listItems.size() - 1] = static_cast<HTMLGenericFormElementImpl *>(current);
2692             current = current->firstChild();
2693         }
2694         if (current->id() == ID_OPTION) {
2695             ++m_length;
2696             m_listItems.resize(m_listItems.size() + 1);
2697             m_listItems[m_listItems.size() - 1] = static_cast<HTMLGenericFormElementImpl *>(current);
2698             if (!foundSelected && !m_multiple && m_size <= 1) {
2699                 foundSelected = static_cast<HTMLOptionElementImpl *>(current);
2700                 foundSelected->m_selected = true;
2701             } else if (foundSelected && !m_multiple && static_cast<HTMLOptionElementImpl *>(current)->selectedBit()) {
2702                 foundSelected->m_selected = false;
2703                 foundSelected = static_cast<HTMLOptionElementImpl *>(current);
2704             }
2705         }
2706         NodeImpl *const parent = current->parentNode();
2707         current = current->nextSibling();
2708         if (!current) {
2709             if (static_cast<const NodeImpl *>(parent) != this) {
2710                 current = parent->nextSibling();
2711             }
2712         }
2713     }
2714     m_recalcListItems = false;
2715 }
2716 
2717 void HTMLSelectElementImpl::childrenChanged()
2718 {
2719     setRecalcListItems();
2720 
2721     HTMLGenericFormElementImpl::childrenChanged();
2722 }
2723 
2724 void HTMLSelectElementImpl::setRecalcListItems()
2725 {
2726     m_recalcListItems = true;
2727     if (m_render) {
2728         static_cast<khtml::RenderSelect *>(m_render)->setOptionsChanged(true);
2729     }
2730     setChanged();
2731 }
2732 
2733 void HTMLSelectElementImpl::reset()
2734 {
2735     const QVector<HTMLGenericFormElementImpl *> items = listItems();
2736     uint i;
2737     const uint itemsSize = items.size();
2738     bool anySelected = false;
2739     for (i = 0; i < itemsSize; ++i) {
2740         if (items[i]->id() == ID_OPTION) {
2741             HTMLOptionElementImpl *const option = static_cast<HTMLOptionElementImpl *>(items[i]);
2742             const bool selected = (!option->getAttribute(ATTR_SELECTED).isNull());
2743             option->setSelected(selected);
2744             if (selected) {
2745                 anySelected = true;
2746             }
2747         }
2748     }
2749     // If this is a single-row SELECT and there is no default selection, jump to first option.
2750     if (!anySelected && m_size <= 1) {
2751         for (i = 0; i < itemsSize; ++i) {
2752             if (items[i]->id() == ID_OPTION) {
2753                 static_cast<HTMLOptionElementImpl *>(items[i])->setSelected(true);
2754                 break;
2755             }
2756         }
2757     }
2758     if (m_render) {
2759         static_cast<RenderSelect *>(m_render)->setSelectionChanged(true);
2760     }
2761     setChanged(true);
2762 }
2763 
2764 void HTMLSelectElementImpl::notifyOptionSelected(HTMLOptionElementImpl *selectedOption, bool selected)
2765 {
2766     if (selected && !m_multiple) {
2767         // deselect all other options
2768         const QVector<HTMLGenericFormElementImpl *> items = listItems();
2769         uint i;
2770         const uint itemsSize = items.size();
2771         for (i = 0; i < itemsSize; ++i) {
2772             if (items[i]->id() == ID_OPTION) {
2773                 static_cast<HTMLOptionElementImpl *>(items[i])->m_selected = (items[i] == selectedOption);
2774             }
2775         }
2776     }
2777     if (m_render) {
2778         static_cast<RenderSelect *>(m_render)->setSelectionChanged(true);
2779     }
2780 
2781     setChanged(true);
2782 }
2783 
2784 // -------------------------------------------------------------------------
2785 
2786 HTMLKeygenElementImpl::HTMLKeygenElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
2787     : HTMLSelectElementImpl(doc, f)
2788 {
2789     const QStringList keys = KSSLKeyGen::supportedKeySizes();
2790     QStringList::ConstIterator i = keys.begin();
2791     const QStringList::ConstIterator iEnd = keys.end();
2792     for (; i != iEnd; ++i) {
2793         HTMLOptionElementImpl *const o = new HTMLOptionElementImpl(doc, form());
2794         addChild(o);
2795         o->addChild(doc->createTextNode(DOMString(*i).implementation()));
2796     }
2797 }
2798 
2799 NodeImpl::Id HTMLKeygenElementImpl::id() const
2800 {
2801     return ID_KEYGEN;
2802 }
2803 
2804 void HTMLKeygenElementImpl::parseAttribute(AttributeImpl *attr)
2805 {
2806     switch (attr->id()) {
2807     case ATTR_CHALLENGE:
2808         break;
2809     default:
2810         // skip HTMLSelectElementImpl parsing!
2811         HTMLGenericFormElementImpl::parseAttribute(attr);
2812     }
2813 }
2814 
2815 bool HTMLKeygenElementImpl::encoding(const QTextCodec *codec, khtml::encodingList &encoded_values, bool)
2816 {
2817     bool successful = false;
2818     const QByteArray enc_name = fixUpfromUnicode(codec, name().string());
2819 
2820     encoded_values += enc_name;
2821 
2822     // pop up the fancy certificate creation dialog here
2823     KSSLKeyGen *const kg = new KSSLKeyGen(static_cast<RenderWidget *>(m_render)->widget());
2824     kg->setWindowTitle(i18n("Key Generator"));
2825     kg->setModal(true);
2826 
2827     kg->setKeySize(0);
2828     successful = (QDialog::Accepted == kg->exec());
2829 
2830     delete kg;
2831 
2832     encoded_values += "deadbeef";
2833 
2834     return successful;
2835 }
2836 
2837 // -------------------------------------------------------------------------
2838 
2839 NodeImpl::Id HTMLOptGroupElementImpl::id() const
2840 {
2841     return ID_OPTGROUP;
2842 }
2843 
2844 // -------------------------------------------------------------------------
2845 
2846 HTMLOptionElementImpl::HTMLOptionElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
2847     : HTMLGenericFormElementImpl(doc, f)
2848 {
2849     m_selected = false;
2850     m_defaultSelected = false;
2851 }
2852 
2853 NodeImpl::Id HTMLOptionElementImpl::id() const
2854 {
2855     return ID_OPTION;
2856 }
2857 
2858 DOMString HTMLOptionElementImpl::text() const
2859 {
2860     if (firstChild() && firstChild()->nodeType() == Node::TEXT_NODE && !firstChild()->nextSibling()) {
2861         return firstChild()->nodeValue();
2862     }
2863 
2864     DOMString ret = "";
2865     NodeImpl *n = firstChild();
2866     for (; n; n = n->nextSibling()) {
2867         if (n->nodeType() == Node::TEXT_NODE ||
2868                 n->nodeType() == Node::CDATA_SECTION_NODE) {
2869             ret += n->nodeValue();
2870         }
2871     }
2872     return ret;
2873 }
2874 
2875 long HTMLOptionElementImpl::index() const
2876 {
2877     // Let's do this dynamically. Might be a bit slow, but we're sure
2878     // we won't forget to update a member variable in some cases...
2879     const QVector<HTMLGenericFormElementImpl *> items = getSelect()->listItems();
2880     const int l = items.count();
2881     int optionIndex = 0;
2882     for (int i = 0; i < l; ++i) {
2883         if (items[i]->id() == ID_OPTION) {
2884             if (static_cast<HTMLOptionElementImpl *>(items[i]) == this) {
2885                 return optionIndex;
2886             }
2887             ++optionIndex;
2888         }
2889     }
2890     qCWarning(KHTML_LOG) << "HTMLOptionElementImpl::index(): option not found!";
2891     return 0;
2892 }
2893 
2894 void HTMLOptionElementImpl::setIndex(long)
2895 {
2896     qCWarning(KHTML_LOG) << "Unimplemented HTMLOptionElementImpl::setIndex(long) called";
2897     // ###
2898 }
2899 
2900 void HTMLOptionElementImpl::parseAttribute(AttributeImpl *attr)
2901 {
2902     switch (attr->id()) {
2903     case ATTR_SELECTED:
2904         m_selected = (attr->val() != nullptr);
2905         m_defaultSelected = m_selected;
2906         break;
2907     case ATTR_VALUE:
2908         m_value = attr->value();
2909         break;
2910     default:
2911         HTMLGenericFormElementImpl::parseAttribute(attr);
2912     }
2913 }
2914 
2915 DOMString HTMLOptionElementImpl::value() const
2916 {
2917     if (!m_value.isNull()) {
2918         return m_value;
2919     }
2920     // Use the text if the value wasn't set.
2921     return text();
2922 }
2923 
2924 void HTMLOptionElementImpl::setValue(DOMStringImpl *value)
2925 {
2926     setAttribute(ATTR_VALUE, value);
2927 }
2928 
2929 void HTMLOptionElementImpl::setSelected(bool _selected)
2930 {
2931     if (m_selected == _selected) {
2932         return;
2933     }
2934     m_selected = _selected;
2935     HTMLSelectElementImpl *const select = getSelect();
2936     if (select) {
2937         select->notifyOptionSelected(this, _selected);
2938     }
2939 }
2940 
2941 bool HTMLOptionElementImpl::selected() const
2942 {
2943     // make sure our parent select is up-to-date, since that may update our selected bit
2944     if (HTMLSelectElementImpl *select = getSelect()) {
2945         (void)select->listItems();
2946     }
2947 
2948     return m_selected;
2949 }
2950 
2951 void HTMLOptionElementImpl::setDefaultSelected(bool _defaultSelected)
2952 {
2953     setAttribute(ATTR_SELECTED, _defaultSelected ? "" : nullptr);
2954 }
2955 
2956 HTMLSelectElementImpl *HTMLOptionElementImpl::getSelect() const
2957 {
2958     NodeImpl *select = parentNode();
2959     while (select && select->id() != ID_SELECT) {
2960         select = select->parentNode();
2961     }
2962     return static_cast<HTMLSelectElementImpl *>(select);
2963 }
2964 
2965 // -------------------------------------------------------------------------
2966 
2967 /*
2968  The rules for storing the value are simple:
2969 
2970  If there is no renderer, either m_value or defaultValue() is definitive,
2971     depending on whether m_initialized is true or not.
2972  If there is a renderer, m_render->text() is definitive. During its construction,
2973     m_value is initialized if needed,  so there is no longer any need to worry
2974     about default values or not.
2975 */
2976 
2977 HTMLTextAreaElementImpl::HTMLTextAreaElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
2978     : HTMLGenericFormElementImpl(doc, f)
2979 {
2980     // DTD requires rows & cols be specified, but we will provide reasonable defaults
2981     m_rows = 2;
2982     m_cols = 20;
2983     m_wrap = ta_Virtual;
2984     m_changed     = false;
2985     m_initialized = false;
2986     m_unsubmittedFormChange = false;
2987 }
2988 
2989 HTMLTextAreaElementImpl::~HTMLTextAreaElementImpl()
2990 {
2991     if (document()) {
2992         document()->deregisterMaintainsState(this);
2993     }
2994 }
2995 
2996 NodeImpl::Id HTMLTextAreaElementImpl::id() const
2997 {
2998     return ID_TEXTAREA;
2999 }
3000 
3001 DOMString HTMLTextAreaElementImpl::type() const
3002 {
3003     return "textarea";
3004 }
3005 
3006 QString HTMLTextAreaElementImpl::state()
3007 {
3008     return value().string() + (m_unsubmittedFormChange ? 'M' : '.');
3009 }
3010 
3011 void HTMLTextAreaElementImpl::restoreState(const QString &state)
3012 {
3013     setDefaultValue(state.left(state.length() - 1));
3014     m_unsubmittedFormChange = state.endsWith('M');
3015 }
3016 
3017 void HTMLTextAreaElementImpl::select()
3018 {
3019     if (m_render) {
3020         static_cast<RenderTextArea *>(m_render)->select();
3021     }
3022     onSelect();
3023 }
3024 
3025 void HTMLTextAreaElementImpl::childrenChanged()
3026 {
3027     setValue(defaultValue());
3028 }
3029 
3030 void HTMLTextAreaElementImpl::parseAttribute(AttributeImpl *attr)
3031 {
3032     switch (attr->id()) {
3033     case ATTR_ROWS:
3034         m_rows = 0;
3035         if (attr->val()) {
3036             m_rows = DOMString(attr->val()).string().toInt();
3037         }
3038         if (!m_rows) {
3039             m_rows = 2;
3040         }
3041         if (renderer()) {
3042             renderer()->setNeedsLayoutAndMinMaxRecalc();
3043         }
3044         break;
3045     case ATTR_COLS:
3046         m_cols = 0;
3047         if (attr->val()) {
3048             m_cols = DOMString(attr->val()).string().toInt();
3049         }
3050         if (!m_cols) {
3051             m_cols = 20;
3052         }
3053         if (renderer()) {
3054             renderer()->setNeedsLayoutAndMinMaxRecalc();
3055         }
3056         break;
3057     case ATTR_WRAP:
3058         // virtual / physical is Netscape extension of HTML 3.0, now deprecated
3059         // soft/ hard / off is recommendation for HTML 4 extension by IE and NS 4
3060         if (strcasecmp(attr->value(), "virtual") == 0  || strcasecmp(attr->value(), "soft") == 0) {
3061             m_wrap = ta_Virtual;
3062         } else if (strcasecmp(attr->value(), "physical") == 0 || strcasecmp(attr->value(), "hard") == 0) {
3063             m_wrap = ta_Physical;
3064         } else if (strcasecmp(attr->value(), "on") == 0) {
3065             m_wrap = ta_Physical;
3066         } else if (strcasecmp(attr->value(), "off") == 0) {
3067             m_wrap = ta_NoWrap;
3068         }
3069         break;
3070     case ATTR_ACCESSKEY:
3071         break;
3072     case ATTR_ALIGN:
3073         break;
3074     case ATTR_ONSELECT:
3075         setHTMLEventListener(EventImpl::SELECT_EVENT,
3076                              document()->createHTMLEventListener(attr->value().string(), "onselect", this));
3077         break;
3078     case ATTR_ONCHANGE:
3079         setHTMLEventListener(EventImpl::CHANGE_EVENT,
3080                              document()->createHTMLEventListener(attr->value().string(), "onchange", this));
3081         break;
3082     case ATTR_PLACEHOLDER:
3083         setChanged();
3084         break;
3085     default:
3086         HTMLGenericFormElementImpl::parseAttribute(attr);
3087     }
3088 }
3089 
3090 void HTMLTextAreaElementImpl::attach()
3091 {
3092     assert(!attached());
3093     assert(!m_render);
3094     assert(parentNode());
3095 
3096     RenderStyle *const _style = document()->styleSelector()->styleForElement(this);
3097     _style->ref();
3098     if (parentNode()->renderer() && _style->display() != NONE) {
3099         m_render = new(document()->renderArena()) RenderTextArea(this);
3100         m_render->setStyle(_style);
3101     }
3102 
3103     HTMLGenericFormElementImpl::attach();
3104     _style->deref();
3105 }
3106 
3107 static QString expandLF(const QString &s)
3108 {
3109     // LF -> CRLF
3110     unsigned crs = s.count('\n');
3111     if (crs == 0) {
3112         return s;
3113     }
3114     unsigned len = s.length();
3115 
3116     QString r;
3117     r.reserve(len + crs + 1);
3118     unsigned pos2 = 0;
3119     for (unsigned pos = 0; pos < len; pos++) {
3120         QChar c = s.at(pos);
3121         switch (c.unicode()) {
3122         case '\n':
3123             r[pos2++] = '\r';
3124             r[pos2++] = '\n';
3125             break;
3126 
3127         case '\r':
3128             break;
3129 
3130         default:
3131             r[pos2++] = c;
3132             break;
3133         }
3134     }
3135     r.squeeze();
3136     return r;
3137 }
3138 
3139 bool HTMLTextAreaElementImpl::encoding(const QTextCodec *codec, encodingList &encoding, bool)
3140 {
3141     if (name().isEmpty()) {
3142         return false;
3143     }
3144 
3145     encoding += fixUpfromUnicode(codec, name().string());
3146     encoding += fixUpfromUnicode(codec, expandLF(value().string()));
3147 
3148     return true;
3149 }
3150 
3151 void HTMLTextAreaElementImpl::reset()
3152 {
3153     setValue(defaultValue());
3154 }
3155 
3156 DOMString HTMLTextAreaElementImpl::value()
3157 {
3158     if (m_render) {
3159         RenderTextArea *renderArea = static_cast<RenderTextArea *>(m_render);
3160         m_value = renderArea->text();
3161     } else {
3162         if (!m_initialized) {
3163             m_value = defaultValue().string();
3164             m_initialized = true;
3165         }
3166     }
3167 
3168     if (m_value.isNull()) {
3169         return "";
3170     }
3171 
3172     return m_value;
3173 }
3174 
3175 void HTMLTextAreaElementImpl::setValue(DOMString _value)
3176 {
3177     // \r\n -> \n, \r -> \n
3178     QString str = _value.string().replace("\r\n", "\n");
3179     m_value = str.replace('\r', '\n');
3180     m_initialized = true;
3181 
3182     if (m_render) {
3183         RenderTextArea *renderArea = static_cast<RenderTextArea *>(m_render);
3184         renderArea->setText(m_value);
3185     }
3186 
3187     setChanged(true);
3188 }
3189 
3190 DOMString HTMLTextAreaElementImpl::defaultValue()
3191 {
3192     DOMString val = "";
3193     // there may be comments - just grab the text nodes
3194     NodeImpl *n;
3195     for (n = firstChild(); n; n = n->nextSibling())
3196         if (n->isTextNode()) {
3197             val += static_cast<TextImpl *>(n)->data();
3198         }
3199 
3200     if (val[0] == '\r' && val[1] == '\n') {
3201         val = val.copy();
3202         val.remove(0, 2);
3203     } else if (val[0] == '\r' || val[0] == '\n') {
3204         val = val.copy();
3205         val.remove(0, 1);
3206     }
3207 
3208     return val;
3209 }
3210 
3211 void HTMLTextAreaElementImpl::setDefaultValue(DOMString _defaultValue)
3212 {
3213     // there may be comments - remove all the text nodes and replace them with one
3214     QList<NodeImpl *> toRemove;
3215     NodeImpl *n;
3216     for (n = firstChild(); n; n = n->nextSibling())
3217         if (n->isTextNode()) {
3218             toRemove.append(n);
3219         }
3220     QListIterator<NodeImpl *> it(toRemove);
3221     int exceptioncode = 0;
3222     while (it.hasNext()) {
3223         removeChild(it.next(), exceptioncode);
3224     }
3225     insertBefore(document()->createTextNode(_defaultValue.implementation()), firstChild(), exceptioncode);
3226     setValue(_defaultValue);
3227 }
3228 
3229 bool HTMLTextAreaElementImpl::isEditable()
3230 {
3231     return true;
3232 }
3233 
3234 //Mozilla extensions.
3235 long HTMLTextAreaElementImpl::selectionStart()
3236 {
3237     if (m_render) {
3238         RenderTextArea *renderArea = static_cast<RenderTextArea *>(m_render);
3239         return renderArea->selectionStart();
3240     }
3241 
3242     return 0;
3243 }
3244 
3245 long HTMLTextAreaElementImpl::selectionEnd()
3246 {
3247     if (m_render) {
3248         RenderTextArea *renderArea = static_cast<RenderTextArea *>(m_render);
3249         return renderArea->selectionEnd();
3250     }
3251 
3252     return 0;
3253 }
3254 
3255 void HTMLTextAreaElementImpl::setSelectionStart(long pos)
3256 {
3257     if (m_render) {
3258         RenderTextArea *renderArea = static_cast<RenderTextArea *>(m_render);
3259         renderArea->setSelectionStart(pos);
3260     }
3261 }
3262 
3263 void HTMLTextAreaElementImpl::setSelectionEnd(long pos)
3264 {
3265     if (m_render) {
3266         RenderTextArea *renderArea = static_cast<RenderTextArea *>(m_render);
3267         renderArea->setSelectionEnd(pos);
3268     }
3269 }
3270 
3271 void HTMLTextAreaElementImpl::setSelectionRange(long start, long end)
3272 {
3273     if (m_render) {
3274         RenderTextArea *renderArea = static_cast<RenderTextArea *>(m_render);
3275         renderArea->setSelectionRange(start, end);
3276     }
3277 }
3278 
3279 long HTMLTextAreaElementImpl::textLength()
3280 {
3281     return value().length();
3282 }
3283 
3284 void HTMLTextAreaElementImpl::setPlaceholder(const DOMString &p)
3285 {
3286     setAttribute(ATTR_PLACEHOLDER, p);
3287 }
3288 
3289 DOMString HTMLTextAreaElementImpl::placeholder() const
3290 {
3291     return getAttribute(ATTR_PLACEHOLDER);
3292 }
3293 
3294 // -------------------------------------------------------------------------
3295 
3296 HTMLIsIndexElementImpl::HTMLIsIndexElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
3297     : HTMLInputElementImpl(doc, f)
3298 {
3299     m_type = TEXT;
3300     setName("isindex");
3301 }
3302 
3303 HTMLIsIndexElementImpl::~HTMLIsIndexElementImpl()
3304 {
3305 }
3306 
3307 NodeImpl::Id HTMLIsIndexElementImpl::id() const
3308 {
3309     return ID_ISINDEX;
3310 }
3311 
3312 void HTMLIsIndexElementImpl::parseAttribute(AttributeImpl *attr)
3313 {
3314     // don't call HTMLInputElement::parseAttribute here, as it would
3315     // accept attributes this element does not support
3316     HTMLGenericFormElementImpl::parseAttribute(attr);
3317 }
3318 
3319 DOMString HTMLIsIndexElementImpl::prompt() const
3320 {
3321     // When IsIndex is parsed, <HR/>Prompt: <ISINDEX/><HR/> is created.
3322     // So we have to look at the previous sibling to find the prompt text
3323     DOM::NodeImpl *const prev = previousSibling();
3324     if (prev && prev->nodeType() == DOM::Node::TEXT_NODE) {
3325         return prev->nodeValue();
3326     }
3327     return "";
3328 }
3329 
3330 void HTMLIsIndexElementImpl::setPrompt(const DOMString &str)
3331 {
3332     // When IsIndex is parsed, <HR/>Prompt: <ISINDEX/><HR/> is created.
3333     // So we have to look at the previous sibling to find the prompt text
3334     int exceptioncode = 0;
3335     DOM::NodeImpl *const prev = previousSibling();
3336     if (prev && prev->nodeType() == DOM::Node::TEXT_NODE) {
3337         static_cast<DOM::TextImpl *>(prev)->setData(str, exceptioncode);
3338     }
3339 }
3340 
3341 // -------------------------------------------------------------------------
3342