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