KHtml

html_formimpl.cpp
1 /*
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll ([email protected])
5  * (C) 1999 Antti Koivisto ([email protected])
6  * (C) 2001 Dirk Mueller ([email protected])
7  * (C) 2004, 2005, 2006 Apple Computer, Inc.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB. If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  *
24  */
25 
26 #undef FORMS_DEBUG
27 //#define FORMS_DEBUG
28 
29 #include "html/html_formimpl.h"
30 
31 #include "khtmlview.h"
32 #include "khtml_part.h"
33 #include "html/html_documentimpl.h"
34 #include "khtml_settings.h"
35 
36 #include "css/cssstyleselector.h"
37 #include "css/cssproperties.h"
38 #include "css/cssvalues.h"
39 #include "xml/dom_textimpl.h"
40 #include "xml/dom_docimpl.h"
41 #include "xml/dom2_eventsimpl.h"
42 #include "xml/dom_restyler.h"
43 #include "khtml_ext.h"
44 
45 #include "rendering/render_form.h"
46 
47 #include <kcharsets.h>
48 #include "khtml_debug.h"
49 #include <kmessagebox.h>
50 #include <krandom.h>
51 #include <klocalizedstring.h>
52 #ifndef KHTML_NO_WALLET
53 #include <kwallet.h>
54 #endif
55 #include <kio/job.h>
56 #include <kjobwidgets.h>
57 #include <kfileitem.h>
58 #include <qmimedatabase.h>
59 #include <QFile>
60 #include <QDir>
61 #include <QTextCodec>
62 #include <QStandardPaths>
63 #include <QTemporaryFile>
64 #include <QKeyEvent>
65 
66 // for keygen
67 #include <ksslkeygen.h>
68 
69 #include <assert.h>
70 
71 using namespace DOM;
72 using namespace khtml;
73 using namespace WTF;
74 
75 HTMLFormElementImpl::HTMLFormElementImpl(DocumentImpl *doc, bool implicit)
76  : HTMLElementImpl(doc)
77 {
78  m_implicit = implicit;
79  m_post = false;
80  m_multipart = false;
81  m_autocomplete = true;
82  m_insubmit = false;
83  m_doingsubmit = false;
84  m_inreset = false;
85  m_enctype = "application/x-www-form-urlencoded";
86  m_boundary = "----------" + KRandom::randomString(42 + 13);
87  m_acceptcharset = "UNKNOWN";
88  m_malformed = false;
89 }
90 
91 HTMLFormElementImpl::~HTMLFormElementImpl()
92 {
93  if (document() && document()->view() && document()->view()->part()) {
94  document()->view()->part()->dequeueWallet(this);
95  }
97  while (it.hasNext()) {
98  it.next()->m_form = nullptr;
99  }
100  QListIterator<HTMLImageElementImpl *> it2(imgElements);
101  while (it2.hasNext()) {
102  it2.next()->m_form = nullptr;
103  }
104 }
105 
106 NodeImpl::Id HTMLFormElementImpl::id() const
107 {
108  return ID_FORM;
109 }
110 
111 HTMLCollectionImpl *HTMLFormElementImpl::elements()
112 {
113  return new HTMLFormCollectionImpl(this);
114 }
115 
116 DOMString HTMLFormElementImpl::target() const
117 {
118  return getAttribute(ATTR_TARGET);
119 }
120 
121 DOMString HTMLFormElementImpl::action() const
122 {
123  return getAttribute(ATTR_ACTION);
124 }
125 
126 long HTMLFormElementImpl::length() const
127 {
128  int len = 0;
130  while (it.hasNext())
131  if (it.next()->isEnumerable()) {
132  ++len;
133  }
134 
135  return len;
136 }
137 
138 static QByteArray encodeCString(const QByteArray &e)
139 {
140  // https://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1
141  // safe characters like NS handles them for compatibility
142  static const char safe[] = "-._*";
143  QByteArray encoded((e.length() + e.count('\n')) * 3
144  + e.count('\r') * 3 + 1, 0);
145  int enclen = 0;
146  bool crmissing = false;
147  unsigned char oldc;
148  unsigned char c = '\0';
149 
150  //QCString orig(e.data(), e.size());
151 
152  unsigned len = e.length();
153  for (unsigned pos = 0; pos < len; pos++) {
154  oldc = c;
155  c = e[pos];
156 
157  if (crmissing && c != '\n') {
158  encoded[enclen++] = '%';
159  encoded[enclen++] = '0';
160  encoded[enclen++] = 'D';
161  crmissing = false;
162  }
163 
164  if (((c >= 'A') && (c <= 'Z')) ||
165  ((c >= 'a') && (c <= 'z')) ||
166  ((c >= '0') && (c <= '9')) ||
167  (strchr(safe, c))
168  ) {
169  encoded[enclen++] = c;
170  } else if (c == ' ') {
171  encoded[enclen++] = '+';
172  } else if (c == '\n') {
173  encoded[enclen++] = '%';
174  encoded[enclen++] = '0';
175  encoded[enclen++] = 'D';
176  encoded[enclen++] = '%';
177  encoded[enclen++] = '0';
178  encoded[enclen++] = 'A';
179  crmissing = false;
180  } else if (c == '\r' && oldc != '\n') {
181  crmissing = true;
182  } else if (c != '\r') {
183  encoded[enclen++] = '%';
184  unsigned int h = c / 16;
185  h += (h > 9) ? ('A' - 10) : '0';
186  encoded[enclen++] = h;
187 
188  unsigned int l = c % 16;
189  l += (l > 9) ? ('A' - 10) : '0';
190  encoded[enclen++] = l;
191  }
192  }
193  encoded.truncate(enclen);
194  return encoded;
195 }
196 
197 inline static QString ampersandEscape(unsigned val)
198 {
199  QString out;
200  out.sprintf("&#%u;", val);
201  return out;
202 }
203 
204 inline static QString escapeUnencodeable(const QTextCodec *codec, const QString &s)
205 {
206  QString encString;
207  const int len = s.length();
208 
209  for (int i = 0; i < len; ++i) {
210  QChar c = s[i];
211 
212  // We need to hand surrogate pairs to the codec at once;
213  // also do validity checking on those
214  if (c.isLowSurrogate()) {
216  } else if (c.isHighSurrogate()) {
217  if ((i + 1) < len && s[i + 1].isLowSurrogate()) {
218  // A valid SP..
219  ++i;
220  QString pair = QString(c) + s[i];
221  if (codec->canEncode(pair)) {
222  encString += pair;
223  } else {
224  encString += ampersandEscape(QChar::surrogateToUcs4(c, s[i]));
225  }
226  continue;
227  } else {
229  }
230  }
231 
232  // Normal characters.
233  if (codec->canEncode(c)) {
234  encString.append(c);
235  } else {
236  encString.append(ampersandEscape(c.unicode()));
237  }
238  }
239  return encString;
240 }
241 
242 inline static QByteArray fixUpfromUnicode(const QTextCodec *codec, const QString &s)
243 {
244  QByteArray str = codec->fromUnicode(escapeUnencodeable(codec, s));
245  str.truncate(str.length());
246  return str;
247 }
248 
249 Vector<HTMLGenericFormElementImpl *> HTMLFormElementImpl::gatherInTreeOrder(NodeImpl *root,
250  const HashSet<NodeImpl *> &toGather)
251 {
252  Vector<HTMLGenericFormElementImpl *> out;
253  out.reserveCapacity(toGather.size());
254 
255  for (NodeImpl *cur = root; cur; cur = cur->traverseNextNode(root)) {
256  if (toGather.contains(cur)) {
257  out.append(static_cast<HTMLGenericFormElementImpl *>(cur));
258  }
259  }
260  return out;
261 }
262 
263 QByteArray HTMLFormElementImpl::formData(bool &ok)
264 {
265 #ifdef FORMS_DEBUG
266  qCDebug(KHTML_LOG) << "form: formData()";
267 #endif
268 
269  QByteArray form_data;
270  QByteArray enc_string; // used for non-multipart data
271 
272  bool useMultipart = m_multipart && m_post; // as multipart is ignored for GET
273 
274  // find out the QTextcodec to use
275  QString str = m_acceptcharset.string();
276  QTextCodec *codec = nullptr;
277  bool codecOk = false;
278 
279  if (str == "UNKNOWN") {
280  // no accept-charset attribute, try document encoding
281  KHTMLView *view = document()->view();
282  if (view && view->part()) {
283  codec = KCharsets::charsets()->codecForName(view->part()->encoding(), codecOk);
284  }
285  }
286 
287  if (!codecOk) {
288  str.replace(QLatin1Char(','), QLatin1Char(' '));
290  for (QStringList::ConstIterator it = charsets.constBegin(); it != charsets.constEnd(); ++it) {
291  codec = KCharsets::charsets()->codecForName((*it), codecOk);
292  if (codecOk) {
293  break;
294  }
295  }
296  }
297 
298  if (!codecOk) {
299  // none of requested could be used fallback to UTF-8
300  codec = KCharsets::charsets()->codecForName("UTF-8", codecOk);
301  }
302 
303  if (!codecOk) {
304  codec = QTextCodec::codecForLocale();
305  }
306 
307  // we need to map visual hebrew to logical hebrew, as the web
308  // server alsways expects responses in logical ordering
309  if (codec->mibEnum() == 11) {
310  codec = QTextCodec::codecForMib(85);
311  }
312 
313  QStringList fileUploads, fileNotUploads;
314 
315  /**
316  Frameworks such as mootools Sortables expect form element values to be submitted in
317  tree order (and HTML5 specifies this behavior); however formElements need not be
318  ordered thus. Hence we walk through the tree and the formElements as we go ---
319  first in our kids, then if needed in the parent
320  */
321  HashSet<NodeImpl *> formElementsSet;
322  foreach (HTMLGenericFormElementImpl *fe, formElements) {
323  formElementsSet.add(fe);
324  }
325 
326  Vector<HTMLGenericFormElementImpl *> ordered = gatherInTreeOrder(this, formElementsSet);
327 
328  if (ordered.size() < (unsigned)formElements.size()) {
329  // Some of our elements not contained within us due to parsing hijinks -- scan
330  // the entire document.
331  ordered = gatherInTreeOrder(document()->documentElement(), formElementsSet);
332  }
333 
334  for (unsigned i = 0; i < ordered.size(); ++i) {
335  HTMLGenericFormElementImpl *const current = ordered[i];
337 
338  if (!current->disabled() && current->encoding(codec, lst, useMultipart)) {
339  //qCDebug(KHTML_LOG) << "adding name '" << current->name().string() << "'";
341  const khtml::encodingList::ConstIterator itEnd = lst.constEnd();
342  for (; it != itEnd; ++it) {
343  if (!useMultipart) {
344  // handle ISINDEX / <input name=isindex> special
345  // but only if its the first entry
346  if (enc_string.isEmpty() && *it == "isindex") {
347  ++it;
348  enc_string += encodeCString(*it);
349  } else {
350  if (!enc_string.isEmpty()) {
351  enc_string += '&';
352  }
353 
354  enc_string += encodeCString(*it);
355  enc_string += '=';
356  ++it;
357  enc_string += encodeCString(*it);
358  }
359  } else {
360  QByteArray hstr("--");
361  hstr += m_boundary.toLatin1();
362  hstr += "\r\n";
363  hstr += "Content-Disposition: form-data; name=\"";
364  hstr += (*it).data();
365  hstr += "\"";
366 
367  // if the current type is FILE, then we also need to
368  // include the filename
369  if (current->id() == ID_INPUT &&
370  static_cast<HTMLInputElementImpl *>(current)->inputType() == HTMLInputElementImpl::FILE) {
371  QUrl path;
372  QString val = static_cast<HTMLInputElementImpl *>(current)->value().string().trimmed();
373  if (!val.isEmpty() &&
374  QDir::isRelativePath(val) &&
377  } else {
378  path = QUrl(val);
379  }
380 
381  hstr += fixUpfromUnicode(codec, "; filename=\"" + path.fileName() + "\"");
382  if (path.isValid()) {
383  fileUploads << path.toDisplayString(QUrl::PreferLocalFile);
384  QMimeDatabase db;
385  const QMimeType mime = db.mimeTypeForUrl(path);
386  if (!mime.name().isEmpty()) {
387  hstr += "\r\nContent-Type: ";
388  hstr += mime.name().toLatin1().constData();
389  }
390  } else if (!val.isEmpty()) {
391  fileNotUploads << path.toDisplayString(QUrl::PreferLocalFile);
392  }
393  }
394 
395  hstr += "\r\n\r\n";
396  ++it;
397 
398  // append body
399  form_data.append(hstr);
400  form_data.append(*it);
401  form_data.append("\r\n");
402 
403  // reset unsubmittedFormChange flag
404  if (current->id() == ID_INPUT &&
405  static_cast<HTMLInputElementImpl *>(current)->inputType() == HTMLInputElementImpl::TEXT) {
406  static_cast<HTMLInputElementImpl *>(current)->setUnsubmittedFormChange(false);
407  }
408 
409  if (current->id() == ID_TEXTAREA) {
410  static_cast<HTMLTextAreaElementImpl *>(current)->setUnsubmittedFormChange(false);
411  }
412 
413  }
414  }
415  }
416  }
417 
418  if (fileNotUploads.count()) {
419  const int result = KMessageBox::warningContinueCancelList(nullptr,
420  i18n("The following files will not be uploaded"
421  " because they could not be found.\n"
422  "Do you want to continue?"),
423  fileNotUploads,
424  i18n("Submit Confirmation"), KGuiItem(i18n("&Submit Anyway"), "dialog-ok"));
425 
426  if (result == KMessageBox::Cancel) {
427  ok = false;
428  return QByteArray();
429  }
430  }
431 
432  if (fileUploads.count()) {
433  const int result = KMessageBox::warningContinueCancelList(nullptr,
434  i18n("You are about to transfer the following files from "
435  "your local computer to the Internet.\n"
436  "Do you really want to continue?"),
437  fileUploads,
438  i18n("Send Confirmation"), KGuiItem(i18np("&Send File", "&Send Files", fileUploads.count()), "document-export"));
439 
440  if (result == KMessageBox::Cancel) {
441  ok = false;
442  return QByteArray();
443  }
444  }
445 
446  if (useMultipart) {
447  enc_string = QString("--" + m_boundary + "--\r\n").toLatin1().constData();
448  }
449 
450  form_data.append(enc_string.constData());
451  ok = true;
452  return form_data;
453 }
454 
455 void HTMLFormElementImpl::setEnctype(const DOMString &type)
456 {
457  if (type.isEmpty()) { // optimization: majority of real world cases
458  m_enctype = "application/x-www-form-urlencoded";
459  m_multipart = false;
460  } else if (strcasecmp(type, "multipart/form-data") == 0) {
461  m_enctype = "multipart/form-data";
462  m_multipart = true;
463  } else if (strcasecmp(type, "text/plain") == 0) {
464  m_enctype = "text/plain";
465  m_multipart = false;
466  } else {
467  m_enctype = "application/x-www-form-urlencoded";
468  m_multipart = false;
469  }
470 }
471 
472 static QString calculateAutoFillKey(const HTMLFormElementImpl &e)
473 {
474  QUrl k(e.document()->URL());
475  k.setFragment(QString());
476  k.setQuery(QString());
477  // ensure that we have the user / password inside the url
478  // otherwise we might have a potential security problem
479  // by saving passwords under wrong lookup key.
480  const QString name = e.getAttribute(ATTR_NAME).string().trimmed();
481  const QRegExp re("[;,!]");
482  const QStringList url = k.url().split(re);
483  return url[0] + '#' + name;
484 }
485 
486 void HTMLFormElementImpl::doAutoFill()
487 {
488 #ifndef KHTML_NO_WALLET
489  const QString key = calculateAutoFillKey(*this);
490 
493  key)) {
494  return;
495  }
496 
497  // assert(view())
498  document()->view()->part()->openWallet(this);
499 #endif // KHTML_NO_WALLET
500 }
501 
502 void HTMLFormElementImpl::walletOpened(KWallet::Wallet *w)
503 {
504 #ifndef KHTML_NO_WALLET
505  assert(w);
506  const QString key = calculateAutoFillKey(*this);
508  return; // failed
509  }
512  if (w->readMap(key, map)) {
513  return; // failed, abort
514  }
515 
516  if (document()->view()) {
517  document()->view()->part()->addWalletFormKey(key);
518  }
519  for (QListIterator<HTMLGenericFormElementImpl *> it(formElements); it.hasNext();) {
520  HTMLGenericFormElementImpl *const cur = it.next();
521  if (cur->id() == ID_INPUT) {
522  HTMLInputElementImpl *const current = static_cast<HTMLInputElementImpl *>(cur);
523  if ((current->inputType() == HTMLInputElementImpl::PASSWORD ||
524  current->inputType() == HTMLInputElementImpl::TEXT) &&
525  !current->readOnly() &&
526  map.contains(current->name().string())) {
527  document()->setFocusNode(current);
528  current->setValue(map.value(current->name().string()));
529  }
530  }
531  }
532 #endif // KHTML_NO_WALLET
533 }
534 
535 void HTMLFormElementImpl::submitFromKeyboard()
536 {
537  // Activate the first nondisabled submit button
538  // if there is none, do a submit anyway if not more
539  // than one <input type=text> or <input type=password>
540  unsigned int inputtext = 0;
541  for (QListIterator<HTMLGenericFormElementImpl *> it(formElements); it.hasNext();) {
542  HTMLGenericFormElementImpl *const cur = it.next();
543  if (cur->id() == ID_BUTTON) {
544  HTMLButtonElementImpl *const current = static_cast<HTMLButtonElementImpl *>(cur);
545  if (current->buttonType() == HTMLButtonElementImpl::SUBMIT && !current->disabled()) {
546  current->click();
547  return;
548  }
549  } else if (cur->id() == ID_INPUT) {
550  HTMLInputElementImpl *const current = static_cast<HTMLInputElementImpl *>(cur);
551  switch (current->inputType()) {
552  case HTMLInputElementImpl::SUBMIT:
553  case HTMLInputElementImpl::IMAGE:
554  if (!current->disabled()) {
555  current->click();
556  return;
557  }
558  break;
559  case HTMLInputElementImpl::TEXT:
560  case HTMLInputElementImpl::PASSWORD:
561  ++inputtext;
562  default:
563  break;
564  }
565  }
566  }
567 
568  if (inputtext <= 1) {
569  prepareSubmit();
570  }
571 }
572 
573 void HTMLFormElementImpl::gatherWalletData()
574 {
575 #ifndef KHTML_NO_WALLET
576  KHTMLView *const view = document()->view();
577  // check if we have any password input's
578  m_walletMap.clear();
579  m_havePassword = false;
580  m_haveTextarea = false;
581  const QUrl formUrl(document()->URL());
582  if (view && !view->nonPasswordStorableSite(formUrl.host())) {
583  for (QListIterator<HTMLGenericFormElementImpl *> it(formElements); it.hasNext();) {
584  HTMLGenericFormElementImpl *const cur = it.next();
585  if (cur->id() == ID_INPUT) {
586  HTMLInputElementImpl *const c = static_cast<HTMLInputElementImpl *>(cur);
587  if ((c->inputType() == HTMLInputElementImpl::TEXT ||
588  c->inputType() == HTMLInputElementImpl::PASSWORD) &&
589  !c->readOnly()) {
590  m_walletMap.insert(c->name().string(), c->value().string());
591  if (c->inputType() == HTMLInputElementImpl::PASSWORD &&
592  !c->value().isEmpty()) {
593  m_havePassword = true;
594  }
595  }
596  } else if (cur->id() == ID_TEXTAREA) {
597  m_haveTextarea = true;
598  }
599  }
600  }
601 #endif // KHTML_NO_WALLET
602 }
603 
604 bool HTMLFormElementImpl::prepareSubmit()
605 {
606  KHTMLView *const view = document()->view();
607  if (m_insubmit || !view || !view->part() || view->part()->onlyLocalReferences()) {
608  return m_insubmit;
609  }
610 
611  gatherWalletData();
612 
613  m_insubmit = true;
614  m_doingsubmit = false;
615 
616  if (dispatchHTMLEvent(EventImpl::SUBMIT_EVENT, true, true) && !m_doingsubmit) {
617  m_doingsubmit = true;
618  }
619 
620  m_insubmit = false;
621 
622  if (m_doingsubmit) {
623  submit();
624  }
625 
626  return m_doingsubmit;
627 }
628 
629 void HTMLFormElementImpl::submit()
630 {
631  if (m_insubmit) {
632  m_doingsubmit = true;
633  return;
634  }
635 
636  m_insubmit = true;
637 
638 #ifdef FORMS_DEBUG
639  qCDebug(KHTML_LOG) << "submitting!";
640 #endif
641 
642  bool ok;
643  KHTMLView *const view = document()->view();
644  const QByteArray form_data = formData(ok);
645  const QUrl formUrl(document()->URL());
646 
647  if (ok && view) {
648  if (m_walletMap.isEmpty()) {
649  gatherWalletData();
650  }
651 #ifndef KHTML_NO_WALLET
652  if (m_havePassword && !m_haveTextarea && KWallet::Wallet::isEnabled()) {
653  const QString key = calculateAutoFillKey(*this);
655  KWallet::Wallet *const w = view->part()->wallet();
656  bool login_changed = false;
657 
658  if (!doesnotexist && w) {
659  // check if the login information changed from what
660  // we had so far.
664  if (!w->readMap(key, map) && (map.count() == m_walletMap.count())) {
667  for (; it != itEnd; ++it)
668  if (it.value() != m_walletMap.value(it.key())) {
669  login_changed = true;
670  break;
671  }
672  } else {
673  login_changed = true;
674  }
675  }
676  }
677 
678  if (doesnotexist || !w || login_changed) {
679  if (view->part()) {
680  view->part()->saveLoginInformation(formUrl.host(), key, m_walletMap);
681  }
682  }
683  }
684 #endif // KHTML_NO_WALLET
685 
686  QString url = getAttribute(ATTR_ACTION).trimSpaces().string();
687  // ignore base url if 'action' attribute is empty.
688  if (url.isEmpty()) {
689  url = formUrl.url();
690  }
691  if (m_post) {
692  view->part()->submitForm("post", url, form_data,
693  m_target.string(),
694  enctype().string(),
695  m_boundary);
696  } else {
697  view->part()->submitForm("get", url, form_data,
698  m_target.string());
699  }
700  }
701 
702  m_walletMap.clear(); // done with it
703  m_havePassword = m_haveTextarea = false;
704  m_doingsubmit = m_insubmit = false;
705 }
706 
707 void HTMLFormElementImpl::reset()
708 {
709  KHTMLView *const view = document()->view();
710  if (m_inreset || !view || !view->part()) {
711  return;
712  }
713 
714  m_inreset = true;
715 
716 #ifdef FORMS_DEBUG
717  qCDebug(KHTML_LOG) << "reset pressed!";
718 #endif
719 
720  // ### DOM2 labels this event as not cancelable, however
721  // common browsers( sick! ) allow it be canceled.
722  if (!dispatchHTMLEvent(EventImpl::RESET_EVENT, true, true)) {
723  m_inreset = false;
724  return;
725  }
726 
727  for (QListIterator<HTMLGenericFormElementImpl *> it(formElements); it.hasNext();) {
728  it.next()->reset();
729  }
730 
731  m_inreset = false;
732 }
733 
734 void HTMLFormElementImpl::parseAttribute(AttributeImpl *attr)
735 {
736  switch (attr->id()) {
737  case ATTR_ACTION:
738  break;
739  case ATTR_TARGET:
740  m_target = attr->value();
741  break;
742  case ATTR_METHOD:
743  m_post = (strcasecmp(attr->value(), "post") == 0);
744  break;
745  case ATTR_ENCTYPE:
746  setEnctype(attr->value());
747  break;
748  case ATTR_ACCEPT_CHARSET:
749  // space separated list of charsets the server
750  // accepts - see rfc2045
751  m_acceptcharset = attr->value();
752  break;
753  case ATTR_ACCEPT:
754  // ignore this one for the moment...
755  break;
756  case ATTR_AUTOCOMPLETE:
757  m_autocomplete = strcasecmp(attr->value(), "off");
758  break;
759  case ATTR_ONSUBMIT:
760  setHTMLEventListener(EventImpl::SUBMIT_EVENT,
761  document()->createHTMLEventListener(attr->value().string(), "onsubmit", this));
762  break;
763  case ATTR_ONRESET:
764  setHTMLEventListener(EventImpl::RESET_EVENT,
765  document()->createHTMLEventListener(attr->value().string(), "onreset", this));
766  break;
767  case ATTR_NAME:
768  if (inDocument() && m_name != attr->value()) {
769  document()->underDocNamedCache().remove(m_name, this);
770  document()->underDocNamedCache().add(attr->value(), this);
771  }
772  m_name = attr->value();
773  //Fallthrough intentional
774  default:
775  HTMLElementImpl::parseAttribute(attr);
776  }
777 }
778 
779 void HTMLFormElementImpl::removedFromDocument()
780 {
781  document()->underDocNamedCache().remove(m_name, this);
782  HTMLElementImpl::removedFromDocument();
783 }
784 
785 void HTMLFormElementImpl::insertedIntoDocument()
786 {
787  document()->underDocNamedCache().add(m_name, this);
788  HTMLElementImpl::insertedIntoDocument();
789 }
790 
791 void HTMLFormElementImpl::removeId(const DOMString &id)
792 {
793  document()->underDocNamedCache().remove(id, this);
794  HTMLElementImpl::removeId(id);
795 }
796 
797 void HTMLFormElementImpl::addId(const DOMString &id)
798 {
799  document()->underDocNamedCache().add(id, this);
800  HTMLElementImpl::addId(id);
801 }
802 
803 void HTMLFormElementImpl::uncheckOtherRadioButtonsInGroup(HTMLGenericFormElementImpl *caller, bool setDefaultChecked)
804 {
805  for (QListIterator<HTMLGenericFormElementImpl *> it(formElements); it.hasNext();) {
806  HTMLGenericFormElementImpl *const current = it.next();
807  if (current->id() == ID_INPUT &&
808  static_cast<HTMLInputElementImpl *>(current)->inputType() == HTMLInputElementImpl::RADIO &&
809  current != caller && current->form() == caller->form() && current->name() == caller->name()) {
810  static_cast<HTMLInputElementImpl *>(current)->setChecked(false, setDefaultChecked);
811  }
812  }
813 }
814 
815 void HTMLFormElementImpl::registerFormElement(HTMLGenericFormElementImpl *e)
816 {
817  formElements.append(e);
818 }
819 
820 void HTMLFormElementImpl::removeFormElement(HTMLGenericFormElementImpl *e)
821 {
822  int i = formElements.indexOf(e);
823  if (i != -1) {
824  formElements.removeAt(i);
825  }
826 
827  if (e->hasPastNames()) {
829  while (it.hasNext()) {
830  it.next();
831  if (it.value() == e) {
832  it.remove();
833  }
834  }
835  }
836 }
837 
838 void HTMLFormElementImpl::registerImgElement(HTMLImageElementImpl *e)
839 {
840  imgElements.append(e);
841 }
842 
843 void HTMLFormElementImpl::removeImgElement(HTMLImageElementImpl *e)
844 {
845  int i = imgElements.indexOf(e);
846  if (i != -1) {
847  imgElements.removeAt(i);
848  }
849 }
850 
851 HTMLGenericFormElementImpl *HTMLFormElementImpl::lookupByPastName(const DOMString &id)
852 {
853  return m_pastNamesMap.value(id);
854 }
855 
856 void HTMLFormElementImpl::bindPastName(HTMLGenericFormElementImpl *e)
857 {
858  DOMString id = e->getAttribute(ATTR_ID);
859  if (!id.isEmpty()) {
860  m_pastNamesMap.insert(id, e);
861  }
862 
863  DOMString nm = e->getAttribute(ATTR_NAME);
864  if (!nm.isEmpty()) {
865  m_pastNamesMap.insert(nm, e);
866  }
867  e->setHasPastNames();
868 }
869 
870 // -------------------------------------------------------------------------
871 
872 HTMLGenericFormElementImpl::HTMLGenericFormElementImpl(DocumentImpl *doc, HTMLFormElementImpl *f)
873  : HTMLElementImpl(doc)
874 {
875  m_disabled = m_readOnly = m_hasPastNames = false;
876  m_name = nullptr;
877 
878  if (f) {
879  m_form = f;
880  } else {
881  m_form = getForm();
882  }
883  if (m_form) {
884  m_form->registerFormElement(this);
885  }
886 }
887 
888 void HTMLGenericFormElementImpl::insertedIntoDocument()
889 {
890  HTMLElementImpl::insertedIntoDocument();
891 
892  if (!m_form) {
893  HTMLFormElementImpl *const newform = getForm();
894  if (newform) {
895  m_form = newform;
896  m_form->registerFormElement(this);
897  }
898  }
899 }
900 
901 void HTMLGenericFormElementImpl::removedFromDocument()
902 {
903  HTMLElementImpl::removedFromDocument();
904 
905  if (m_form) {
906  m_form->removeFormElement(this);
907  }
908 
909  m_form = nullptr;
910 }
911 
912 HTMLGenericFormElementImpl::~HTMLGenericFormElementImpl()
913 {
914  if (m_form) {
915  m_form->removeFormElement(this);
916  }
917  if (m_name) {
918  m_name->deref();
919  }
920 }
921 
922 void HTMLGenericFormElementImpl::parseAttribute(AttributeImpl *attr)
923 {
924  switch (attr->id()) {
925  case ATTR_DISABLED:
926  setDisabled(attr->val() != nullptr);
927  break;
928  case ATTR_READONLY: {
929  const bool m_oldreadOnly = m_readOnly;
930  m_readOnly = attr->val() != nullptr;
931  if (m_oldreadOnly != m_readOnly) {
932  setChanged();
933  }
934  break;
935  }
936  default:
937  HTMLElementImpl::parseAttribute(attr);
938  }
939 }
940 
941 void HTMLGenericFormElementImpl::attach()
942 {
943  assert(!attached());
944 
945  if (m_render) {
946  assert(m_render->style());
947  parentNode()->renderer()->addChild(m_render, nextRenderer());
948  }
949 
950  // FIXME: This handles the case of a new form element being created by
951  // JavaScript and inserted inside a form. What it does not handle is
952  // a form element being moved from inside a form to outside, or from one
953  // inside one form to another. The reason this other case is hard to fix
954  // is that during parsing, we may have been passed a form that we are not
955  // inside, DOM-tree-wise. If so, it's hard for us to know when we should
956  // be removed from that form's element list.
957  if (!m_form) {
958  m_form = getForm();
959  if (m_form) {
960  m_form->registerFormElement(this);
961  }
962  }
963 
964  NodeBaseImpl::attach();
965 
966  // The call to updateFromElement() needs to go after the call through
967  // to the base class's attach() because that can sometimes do a close
968  // on the renderer.
969  if (m_render) {
970  m_render->updateFromElement();
971  }
972 
973 }
974 
975 HTMLFormElementImpl *HTMLGenericFormElementImpl::getForm() const
976 {
977  NodeImpl *p = parentNode();
978  while (p) {
979  if (p->id() == ID_FORM) {
980  return static_cast<HTMLFormElementImpl *>(p);
981  }
982  if (p->parentNode() && p->parentNode()->id() == ID_TABLE && p->previousSibling()) {
983  p = p->previousSibling();
984  continue;
985  }
986  p = p->parentNode();
987  }
988 #ifdef FORMS_DEBUG
989  qCDebug(KHTML_LOG) << "couldn't find form!";
990 #endif
991  return nullptr;
992 }
993 
994 DOMString HTMLGenericFormElementImpl::name() const
995 {
996  if (m_name) {
997  return m_name;
998  }
999 
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")) {
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()) {
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 {
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 {
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) &&
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 {
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 
KJOBWIDGETS_EXPORT void setWindow(KJob *job, QWidget *widget)
PreferLocalFile
QTextCodec * codecForName(const QString &name) const
QByteArray fromUnicode(const QString &str) const const
ControlModifier
Qt::KeyboardModifiers modifiers() const const
MouseButtonRelease
QString & append(QChar ch)
TabFocus
virtual bool setFolder(const QString &f)
FileCopyJob * file_copy(const QUrl &src, const QUrl &dest, JobFlags flags)=delete
QString toDisplayString(QUrl::FormattingOptions options) const const
Node insertBefore(const Node &newChild, const Node &refChild)
Inserts the node newChild before the existing child node refChild .
Definition: dom_node.cpp:314
void setModal(bool modal)
QString name(const QVariant &location)
void setFragment(const QString &fragment, QUrl::ParsingMode mode)
QString writableLocation(QStandardPaths::StandardLocation type)
bool contains(const Key &key) const const
QString & fill(QChar ch, int size)
Node previousSibling() const
The node immediately preceding this node.
Definition: dom_node.cpp:282
KIOCORE_EXPORT StatJob * stat(const QUrl &url, bool sideIsSource, short int details, JobFlags flags=DefaultFlags)
bool onlyLocalReferences() const
Returns whether only file:/ or data:/ references are allowed to be loaded ( default false )...
This file is part of the HTML rendering engine for KDE.
int contentsY() const
Returns the y coordinate of the contents area point that is currently located at the top left in the ...
Definition: khtmlview.cpp:714
QMap::const_iterator constBegin() const const
bool isEmpty() const const
LeftButton
static bool isEnabled()
virtual int exec()
virtual int readMap(const QString &key, QMap< QString, QString > &value)
Renders and displays HTML in a QScrollArea.
Definition: khtmlview.h:97
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
int length() const const
bool exists() const const
unsigned long index() const
Definition: dom_node.cpp:517
QTextCodec * codecForLocale()
bool isNull() const const
bool isHighSurrogate() const const
QString & sprintf(const char *cformat,...)
QTextStream & left(QTextStream &stream)
void setPath(const QString &path, QUrl::ParsingMode mode)
KHTMLPart * part() const
Returns a pointer to the KHTMLPart that is rendering the page.
Definition: khtmlview.h:139
void setAttribute(const DOMString &name, const DOMString &value)
Adds a new attribute.
virtual bool hasFolder(const QString &f)
int count(const T &value) const const
void append(const T &value)
uint surrogateToUcs4(ushort high, ushort low)
QWidget * topLevelWidget() const const
Node parentNode() const
The parent of this node.
Definition: dom_node.cpp:250
bool isLowSurrogate() const const
QMimeType mimeTypeForUrl(const QUrl &url) const const
void setFocus()
virtual bool focusNextPrevChild(bool next)
KParts::BrowserExtension * browserExtension() const
Returns a pointer to the KParts::BrowserExtension.
bool isEmpty() const const
QString trimmed() const const
QMap::const_iterator constEnd() const const
const char * constData() const const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool isNull() const
tests if this Node is 0.
Definition: dom_node.h:928
KCharsets * charsets()
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const const
This class implements the basic string we use in the DOM.
Definition: dom_string.h:44
static QStringList supportedKeySizes()
List the supported key sizes.
Definition: ksslkeygen.cpp:252
int count(char ch) const const
qint64 read(char *data, qint64 maxSize)
void truncate(int pos)
DOMString nodeValue() const
The value of this node, depending on its type; see the table above.
Definition: dom_node.cpp:218
Node removeChild(const Node &oldChild)
Removes the child node indicated by oldChild from the list of children, and returns it...
Definition: dom_node.cpp:340
static const QString FormDataFolder()
KDE Key Generation dialog.
Definition: ksslkeygen.h:41
QString right(int n) const const
ushort unicode() const const
QByteArray & append(char ch)
void setKeySize(int idx)
Set the key size.
Definition: ksslkeygen.cpp:268
QList::iterator end()
int key() const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
virtual qint64 size() const const override
static KCharsets * charsets()
Node firstChild() const
The first child of this node.
Definition: dom_node.cpp:266
virtual int mibEnum() const const =0
bool isRelativePath(const QString &path)
virtual QString fileName() const const override
const Key key(const T &value, const Key &defaultKey) const const
QString i18n(const char *text, const TYPE &arg...)
QString & replace(int position, int n, QChar after)
This library provides a full-featured HTML parser and widget.
bool isValid() const const
QByteArray toLatin1() const const
QString mid(int position, int n) const const
DOMString getAttribute(const DOMString &name)
Retrieves an attribute value by name.
int count() const const
const QChar at(int position) const const
void setWindowTitle(const QString &)
ButtonCode warningContinueCancelList(QWidget *parent, const QString &text, const QStringList &strlist, const QString &caption=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
int contentsX() const
Returns the x coordinate of the contents area point that is currently located at the top left in the ...
Definition: khtmlview.cpp:709
typedef ConstIterator
QTextCodec * codecForMib(int mib)
int count(const T &value) const const
This is the BrowserExtension for a KHTMLPart document.
Definition: khtml_ext.h:45
int length() const const
void reserve(int size)
bool canEncode(QChar ch) const const
static bool keyDoesNotExist(const QString &wallet, const QString &folder, const QString &key)
static const QString NetworkWallet()
QString left(int n) const const
DOMStringImpl * implementation() const
Definition: dom_string.h:145
QString fromLatin1(const char *str, int size)
ReplacementCharacter
Definition: css_base.h:371
QList::const_iterator constEnd() const const
QList::const_iterator constBegin() const const
const UDSEntry & statResult() const
int size() const const
KCOREADDONS_EXPORT QString randomString(int length)
QFuture< void > map(Sequence &sequence, MapFunctor function)
int count(const Key &key) const const
QList::iterator begin()
void squeeze()
QString fileName(QUrl::ComponentFormattingOptions options) const const
Node nextSibling() const
The node immediately following this node.
Definition: dom_node.cpp:290
QUrl fromLocalFile(const QString &localFile)
HTMLCollection children() const
Retrieves a collection of nodes that are direct descendants of this node.
const T value(const Key &key, const T &defaultValue) const const
int remove(const Key &key)
DOMString trimSpaces() const
Returns a string with Space Characters removed from the start and the end.
Definition: dom_string.cpp:345
unsigned short nodeType() const
A code representing the type of the underlying object, as defined above.
Definition: dom_node.cpp:242
bool hasNext() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Oct 25 2021 22:48:14 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.