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

0001 /**
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
0005  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
0006  *           (C) 2003 Dirk Mueller (mueller@kde.org)
0007  * Copyright (C) 2002 Apple Computer, Inc.
0008  *
0009  * This library is free software; you can redistribute it and/or
0010  * modify it under the terms of the GNU Library General Public
0011  * License as published by the Free Software Foundation; either
0012  * version 2 of the License, or (at your option) any later version.
0013  *
0014  * This library is distributed in the hope that it will be useful,
0015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017  * Library General Public License for more details.
0018  *
0019  * You should have received a copy of the GNU Library General Public License
0020  * along with this library; see the file COPYING.LIB.  If not, write to
0021  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0022  * Boston, MA 02110-1301, USA.
0023  *
0024  */
0025 // -------------------------------------------------------------------------
0026 //#define DEBUG
0027 //#define DEBUG_LAYOUT
0028 //#define PAR_DEBUG
0029 //#define EVENT_DEBUG
0030 //#define UNSUPPORTED_ATTR
0031 
0032 #include "html_elementimpl.h"
0033 #include "dtd.h"
0034 #include "html_documentimpl.h"
0035 #include "htmltokenizer.h"
0036 
0037 #include <khtmlview.h>
0038 #include <khtml_part.h>
0039 
0040 #include <rendering/render_object.h>
0041 #include <rendering/render_replaced.h>
0042 #include <css/css_valueimpl.h>
0043 #include <css/css_stylesheetimpl.h>
0044 #include <css/cssproperties.h>
0045 #include <css/cssvalues.h>
0046 #include <xml/dom_textimpl.h>
0047 #include <xml/dom2_eventsimpl.h>
0048 #include <dom/dom_doc.h>
0049 
0050 #include "khtml_debug.h"
0051 
0052 #undef FOCUS_EVENT  // for win32, MinGW
0053 
0054 using namespace DOM;
0055 using namespace khtml;
0056 
0057 HTMLElementImpl::HTMLElementImpl(DocumentImpl *doc)
0058     : ElementImpl(doc)
0059 {
0060     m_htmlCompat = doc && doc->htmlMode() != DocumentImpl::XHtml;
0061 }
0062 
0063 HTMLElementImpl::~HTMLElementImpl()
0064 {
0065 }
0066 
0067 bool HTMLElementImpl::isInline() const
0068 {
0069     if (renderer()) {
0070         return ElementImpl::isInline();
0071     }
0072 
0073     switch (id()) {
0074     case ID_A:
0075     case ID_FONT:
0076     case ID_TT:
0077     case ID_U:
0078     case ID_B:
0079     case ID_I:
0080     case ID_S:
0081     case ID_STRIKE:
0082     case ID_BIG:
0083     case ID_SMALL:
0084 
0085     // %phrase
0086     case ID_EM:
0087     case ID_STRONG:
0088     case ID_DFN:
0089     case ID_CODE:
0090     case ID_SAMP:
0091     case ID_KBD:
0092     case ID_VAR:
0093     case ID_CITE:
0094     case ID_ABBR:
0095     case ID_ACRONYM:
0096 
0097     // %special
0098     case ID_SUB:
0099     case ID_SUP:
0100     case ID_SPAN:
0101     case ID_NOBR:
0102     case ID_WBR:
0103         return true;
0104 
0105     default:
0106         return ElementImpl::isInline();
0107     }
0108 }
0109 
0110 DOMString HTMLElementImpl::namespaceURI() const
0111 {
0112     return DOMString(XHTML_NAMESPACE);
0113 }
0114 
0115 void HTMLElementImpl::parseAttribute(AttributeImpl *attr)
0116 {
0117     DOMString indexstring;
0118     switch (attr->id()) {
0119     case ATTR_ALIGN:
0120         if (attr->val()) {
0121             if (strcasecmp(attr->value(), "middle") == 0) {
0122                 addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL_CENTER);
0123             } else {
0124                 addCSSProperty(CSS_PROP_TEXT_ALIGN, attr->value().lower());
0125             }
0126         } else {
0127             removeCSSProperty(CSS_PROP_TEXT_ALIGN);
0128         }
0129         break;
0130 // the core attributes...
0131     case ATTR_ID:
0132         // unique id
0133         setHasID();
0134         document()->incDOMTreeVersion(DocumentImpl::TV_IDNameHref);
0135         break;
0136     case ATTR_CLASS:
0137         if (attr->val()) {
0138             DOMString v = attr->value();
0139             const QChar *characters = v.unicode();
0140             unsigned length = v.length();
0141             unsigned i;
0142             for (i = 0; i < length && characters[i].isSpace(); ++i) { }
0143             setHasClass(i < length);
0144             attributes()->setClass(v);
0145         } else {
0146             setHasClass(false);
0147         }
0148         document()->incDOMTreeVersion(DocumentImpl::TV_Class);
0149         break;
0150     case ATTR_NAME:
0151         document()->incDOMTreeVersion(DocumentImpl::TV_IDNameHref);
0152         break;
0153     case ATTR_CONTENTEDITABLE:
0154         setContentEditable(attr);
0155         break;
0156     case ATTR_STYLE:
0157         getInlineStyleDecls()->updateFromAttribute(attr->value());
0158         setChanged();
0159         break;
0160     case ATTR_TABINDEX:
0161         indexstring = getAttribute(ATTR_TABINDEX);
0162         if (attr->val()) {
0163             setTabIndex(attr->value().toInt());
0164         } else {
0165             setNoTabIndex();
0166         }
0167         break;
0168 // i18n attributes
0169     case ATTR_LANG:
0170         break;
0171     case ATTR_DIR:
0172         addCSSProperty(CSS_PROP_DIRECTION, attr->value().lower());
0173         break;
0174 // standard events
0175     case ATTR_ONCLICK:
0176         setHTMLEventListener(EventImpl::KHTML_ECMA_CLICK_EVENT,
0177                              document()->createHTMLEventListener(attr->value().string(), "onclick", this));
0178         break;
0179     case ATTR_ONDBLCLICK:
0180         setHTMLEventListener(EventImpl::KHTML_ECMA_DBLCLICK_EVENT,
0181                              document()->createHTMLEventListener(attr->value().string(), "ondblclick", this));
0182         break;
0183     case ATTR_ONMOUSEDOWN:
0184         setHTMLEventListener(EventImpl::MOUSEDOWN_EVENT,
0185                              document()->createHTMLEventListener(attr->value().string(), "onmousedown", this));
0186         break;
0187     case ATTR_ONMOUSEMOVE:
0188         setHTMLEventListener(EventImpl::MOUSEMOVE_EVENT,
0189                              document()->createHTMLEventListener(attr->value().string(), "onmousemove", this));
0190         break;
0191     case ATTR_ONMOUSEOUT:
0192         setHTMLEventListener(EventImpl::MOUSEOUT_EVENT,
0193                              document()->createHTMLEventListener(attr->value().string(), "onmouseout", this));
0194         break;
0195     case ATTR_ONMOUSEOVER:
0196         setHTMLEventListener(EventImpl::MOUSEOVER_EVENT,
0197                              document()->createHTMLEventListener(attr->value().string(), "onmouseover", this));
0198         break;
0199     case ATTR_ONMOUSEUP:
0200         setHTMLEventListener(EventImpl::MOUSEUP_EVENT,
0201                              document()->createHTMLEventListener(attr->value().string(), "onmouseup", this));
0202         break;
0203     case ATTR_ONKEYDOWN:
0204         setHTMLEventListener(EventImpl::KEYDOWN_EVENT,
0205                              document()->createHTMLEventListener(attr->value().string(), "onkeydown", this));
0206         break;
0207     case ATTR_ONKEYPRESS:
0208         setHTMLEventListener(EventImpl::KEYPRESS_EVENT,
0209                              document()->createHTMLEventListener(attr->value().string(), "onkeypress", this));
0210         break;
0211     case ATTR_ONKEYUP:
0212         setHTMLEventListener(EventImpl::KEYUP_EVENT,
0213                              document()->createHTMLEventListener(attr->value().string(), "onkeyup", this));
0214         break;
0215     case ATTR_ONFOCUS:
0216         setHTMLEventListener(EventImpl::FOCUS_EVENT,
0217                              document()->createHTMLEventListener(attr->value().string(), "onfocus", this));
0218         break;
0219     case ATTR_ONBLUR:
0220         setHTMLEventListener(EventImpl::BLUR_EVENT,
0221                              document()->createHTMLEventListener(attr->value().string(), "onblur", this));
0222         break;
0223     case ATTR_ONSCROLL:
0224         setHTMLEventListener(EventImpl::SCROLL_EVENT,
0225                              document()->createHTMLEventListener(attr->value().string(), "onscroll", this));
0226         break;
0227 // other misc attributes
0228     default:
0229 #ifdef UNSUPPORTED_ATTR
0230         qCDebug(KHTML_LOG) << "UATTR: <" << this->nodeName().string() << "> ["
0231                  << attr->name().string() << "]=[" << attr->value().string() << "]";
0232 #endif
0233         break;
0234     }
0235 }
0236 
0237 void HTMLElementImpl::recalcStyle(StyleChange ch)
0238 {
0239     ElementImpl::recalcStyle(ch);
0240 
0241     if (m_render /*&& changed*/) {
0242         m_render->updateFromElement();
0243     }
0244 }
0245 
0246 void HTMLElementImpl::addCSSProperty(int id, const DOMString &value)
0247 {
0248     if (!m_hasCombinedStyle) {
0249         createNonCSSDecl();
0250     }
0251     nonCSSStyleDecls()->setProperty(id, value, false);
0252     setChanged();
0253 }
0254 
0255 void HTMLElementImpl::addCSSProperty(int id, int value)
0256 {
0257     if (!m_hasCombinedStyle) {
0258         createNonCSSDecl();
0259     }
0260     nonCSSStyleDecls()->setProperty(id, value, false);
0261     setChanged();
0262 }
0263 
0264 void HTMLElementImpl::addCSSLength(int id, const DOMString &value, bool numOnly, bool multiLength)
0265 {
0266     if (!m_hasCombinedStyle) {
0267         createNonCSSDecl();
0268     }
0269 
0270     // strip attribute garbage to avoid CSS parsing errors
0271     // ### create specialized hook that avoids parsing every
0272     // value twice!
0273     if (value.implementation()) {
0274         // match \s*[+-]?\d*(\.\d*)?[%\*]?
0275         unsigned i = 0, j = 0;
0276         QChar *s = value.implementation()->s;
0277         unsigned l = value.implementation()->l;
0278 
0279         while (i < l && s[i].isSpace()) {
0280             ++i;
0281         }
0282         if (i < l && (s[i] == '+' || s[i] == '-')) {
0283             ++i;
0284         }
0285         while (i < l && s[i].isDigit()) {
0286             ++i, ++j;
0287         }
0288 
0289         // no digits!
0290         if (j == 0) {
0291             return;
0292         }
0293 
0294         int v = qBound(-8192, QString::fromRawData(s, i).toInt(), 8191);
0295         const char *suffix = "px";
0296         if (!numOnly || multiLength) {
0297             // look if we find a % or *
0298             while (i < l) {
0299                 if (multiLength && s[i] == '*') {
0300                     suffix = "";
0301                     break;
0302                 }
0303                 if (s[i] == '%') {
0304                     suffix = "%";
0305                     break;
0306                 }
0307                 ++i;
0308             }
0309         }
0310         if (numOnly) {
0311             suffix = "";
0312         }
0313 
0314         QString ns = QString::number(v) + suffix;
0315         nonCSSStyleDecls()->setLengthProperty(id, DOMString(ns), false, multiLength);
0316         setChanged();
0317         return;
0318     }
0319 
0320     nonCSSStyleDecls()->setLengthProperty(id, value, false, multiLength);
0321     setChanged();
0322 }
0323 
0324 static inline bool isHexDigit(const QChar &c)
0325 {
0326     return (c >= '0' && c <= '9') ||
0327            (c >= 'a' && c <= 'f') ||
0328            (c >= 'A' && c <= 'F');
0329 }
0330 
0331 static inline int toHex(const QChar &c)
0332 {
0333     return ((c >= '0' && c <= '9')
0334             ? (c.unicode() - '0')
0335             : ((c >= 'a' && c <= 'f')
0336                ? (c.unicode() - 'a' + 10)
0337                : ((c >= 'A' && c <= 'F')
0338                   ? (c.unicode() - 'A' + 10)
0339                   : -1)));
0340 }
0341 
0342 /* color parsing that tries to match as close as possible IE 6. */
0343 void HTMLElementImpl::addHTMLColor(int id, const DOMString &c)
0344 {
0345     if (!m_hasCombinedStyle) {
0346         createNonCSSDecl();
0347     }
0348 
0349     // this is the only case no color gets applied in IE.
0350     if (!c.length()) {
0351         removeCSSProperty(id);
0352         return;
0353     }
0354 
0355     if (nonCSSStyleDecls()->setProperty(id, c, false)) {
0356         return;
0357     }
0358 
0359     QString color = c.string();
0360     // not something that fits the specs.
0361 
0362     // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value
0363     // out of everyhting you put in. The algorithm is experimentally determined, but seems to work for all test cases I have.
0364 
0365     // the length of the color value is rounded up to the next
0366     // multiple of 3. each part of the rgb triple then gets one third
0367     // of the length.
0368     //
0369     // Each triplet is parsed byte by byte, mapping
0370     // each number to a hex value (0-9a-fA-F to their values
0371     // everything else to 0).
0372     //
0373     // The highest non zero digit in all triplets is remembered, and
0374     // used as a normalization point to normalize to values between 0
0375     // and 255.
0376 
0377     if (color.toLower() != "transparent") {
0378         if (color[0] == '#') {
0379             color.remove(0,  1);
0380         }
0381         int basicLength = (color.length() + 2) / 3;
0382         if (basicLength > 1) {
0383             // IE ignores colors with three digits or less
0384 //      qDebug("trying to fix up color '%s'. basicLength=%d, length=%d",
0385 //         color.toLatin1().constData(), basicLength, color.length() );
0386             int colors[3] = { 0, 0, 0 };
0387             int component = 0;
0388             int pos = 0;
0389             int maxDigit = basicLength - 1;
0390             while (component < 3) {
0391                 // search forward for digits in the string
0392                 int numDigits = 0;
0393                 while (pos < (int)color.length() && numDigits < basicLength) {
0394                     int hex = toHex(color[pos]);
0395                     colors[component] = (colors[component] << 4);
0396                     if (hex > 0) {
0397                         colors[component] += hex;
0398                         maxDigit = qMin(maxDigit, numDigits);
0399                     }
0400                     numDigits++;
0401                     pos++;
0402                 }
0403                 while (numDigits++ < basicLength) {
0404                     colors[component] <<= 4;
0405                 }
0406                 component++;
0407             }
0408             maxDigit = basicLength - maxDigit;
0409 //      qDebug("color is %x %x %x, maxDigit=%d",  colors[0], colors[1], colors[2], maxDigit );
0410 
0411             // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits
0412             maxDigit -= 2;
0413             colors[0] >>= 4 * maxDigit;
0414             colors[1] >>= 4 * maxDigit;
0415             colors[2] >>= 4 * maxDigit;
0416 //      qDebug("normalized color is %x %x %x",  colors[0], colors[1], colors[2] );
0417             //  assert( colors[0] < 0x100 && colors[1] < 0x100 && colors[2] < 0x100 );
0418 
0419             color.sprintf("#%02x%02x%02x", colors[0], colors[1], colors[2]);
0420 //      qDebug( "trying to add fixed color string '%s'", color.toLatin1().constData() );
0421             if (nonCSSStyleDecls()->setProperty(id, DOMString(color), false)) {
0422                 return;
0423             }
0424         }
0425     }
0426     nonCSSStyleDecls()->setProperty(id, CSS_VAL_BLACK, false);
0427 }
0428 
0429 void HTMLElementImpl::removeCSSProperty(int id)
0430 {
0431     if (!m_hasCombinedStyle) {
0432         return;
0433     }
0434     nonCSSStyleDecls()->setParent(document()->elementSheet());
0435     nonCSSStyleDecls()->removeProperty(id);
0436     setChanged();
0437 }
0438 
0439 DOMString HTMLElementImpl::innerHTML() const
0440 {
0441     QString result = ""; //Use QString to accumulate since DOMString is poor for appends
0442     for (NodeImpl *child = firstChild(); child != nullptr; child = child->nextSibling()) {
0443         DOMString kid = child->toString();
0444         result += QString::fromRawData(kid.unicode(), kid.length());
0445     }
0446     return result;
0447 }
0448 
0449 DOMString HTMLElementImpl::innerText() const
0450 {
0451     QString text = "";
0452     if (!firstChild()) {
0453         return text;
0454     }
0455 
0456     const NodeImpl *n = this;
0457     // find the next text/image after the anchor, to get a position
0458     while (n) {
0459         if (n->firstChild()) {
0460             n = n->firstChild();
0461         } else if (n->nextSibling()) {
0462             n = n->nextSibling();
0463         } else {
0464             NodeImpl *next = nullptr;
0465             while (!next) {
0466                 n = n->parentNode();
0467                 if (!n || n == (NodeImpl *)this) {
0468                     goto end;
0469                 }
0470                 next = n->nextSibling();
0471             }
0472             n = next;
0473         }
0474         if (n->isTextNode()) {
0475             DOMStringImpl *data = static_cast<const TextImpl *>(n)->string();
0476             text += QString::fromRawData(data->s, data->l);
0477         }
0478     }
0479 end:
0480     return text;
0481 }
0482 
0483 DocumentFragment HTMLElementImpl::createContextualFragment(const DOMString &html)
0484 {
0485     // IE originally restricted innerHTML to a small subset of elements;
0486     // and we largely matched that. Mozilla's embrace of innerHTML, however, extended
0487     // it to pretty much everything, and so the web (and HTML5) requires it now.
0488     // For now, we accept everything, but do not do context-based recovery in the parser.
0489     if (!document()->isHTMLDocument()) {
0490         return DocumentFragment();
0491     }
0492 
0493     DocumentFragmentImpl *fragment = new DocumentFragmentImpl(docPtr());
0494     DocumentFragment f(fragment);
0495     {
0496         HTMLTokenizer tok(docPtr(), fragment);
0497         tok.begin();
0498         tok.write(html.string(), true);
0499         tok.end();
0500     }
0501 
0502     // Exceptions are ignored because none ought to happen here.
0503     int ignoredExceptionCode;
0504 
0505     // we need to pop <html> and <body> elements and remove <head> to
0506     // accomadate folks passing complete HTML documents to make the
0507     // child of an element.
0508     for (NodeImpl *node = fragment->firstChild(); node;) {
0509         if (node->id() == ID_HTML || node->id() == ID_BODY) {
0510             NodeImpl *firstChild = node->firstChild();
0511             NodeImpl *child = firstChild;
0512             while (child) {
0513                 NodeImpl *nextChild = child->nextSibling();
0514                 fragment->insertBefore(child, node, ignoredExceptionCode);
0515                 child = nextChild;
0516             }
0517             if (!firstChild) {
0518                 NodeImpl *nextNode = node->nextSibling();
0519                 fragment->removeChild(node, ignoredExceptionCode);
0520                 node = nextNode;
0521             } else {
0522                 fragment->removeChild(node, ignoredExceptionCode);
0523                 node = firstChild;
0524             }
0525         } else if (node->id() == ID_HEAD) {
0526             NodeImpl *nextNode = node->nextSibling();
0527             fragment->removeChild(node, ignoredExceptionCode);
0528             node = nextNode;
0529         } else {
0530             node = node->nextSibling();
0531         }
0532     }
0533 
0534     return f;
0535 }
0536 
0537 void HTMLElementImpl::setInnerHTML(const DOMString &html, int &exceptioncode)
0538 {
0539     if (id() == ID_SCRIPT || id() == ID_STYLE) {
0540         // Script and CSS source shouldn't be parsed as HTML.
0541         removeChildren();
0542         appendChild(document()->createTextNode(html), exceptioncode);
0543         return;
0544     }
0545 
0546     DocumentFragment fragment = createContextualFragment(html);
0547     if (fragment.isNull()) {
0548         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
0549         return;
0550     }
0551 
0552     // Make sure adding the new child is ok, before removing all children (#96187)
0553     checkAddChild(fragment.handle(), exceptioncode);
0554     if (exceptioncode) {
0555         return;
0556     }
0557 
0558     removeChildren();
0559     appendChild(fragment.handle(), exceptioncode);
0560 }
0561 
0562 void HTMLElementImpl::setInnerText(const DOMString &text, int &exceptioncode)
0563 {
0564     // following the IE specs.
0565     if (endTagRequirement(id()) == FORBIDDEN) {
0566         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
0567         return;
0568     }
0569     // IE disallows innerHTML on inline elements. I don't see why we should have this restriction, as our
0570     // dhtml engine can cope with it. Lars
0571     //if ( isInline() ) return false;
0572     switch (id()) {
0573     case ID_COL:
0574     case ID_COLGROUP:
0575     case ID_FRAMESET:
0576     case ID_HEAD:
0577     case ID_HTML:
0578     case ID_TABLE:
0579     case ID_TBODY:
0580     case ID_TFOOT:
0581     case ID_THEAD:
0582     case ID_TR:
0583         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
0584         return;
0585     default:
0586         break;
0587     }
0588 
0589     removeChildren();
0590 
0591     TextImpl *t = new TextImpl(docPtr(), text.implementation());
0592     appendChild(t, exceptioncode);
0593 }
0594 
0595 void HTMLElementImpl::addHTMLAlignment(DOMString alignment)
0596 {
0597     //qDebug("alignment is %s", alignment.string().toLatin1().constData() );
0598     // vertical alignment with respect to the current baseline of the text
0599     // right or left means floating images
0600     int propfloat = -1;
0601     int propvalign = -1;
0602     if (strcasecmp(alignment, "absmiddle") == 0) {
0603         propvalign = CSS_VAL_MIDDLE;
0604     } else if (strcasecmp(alignment, "absbottom") == 0) {
0605         propvalign = CSS_VAL_BOTTOM;
0606     } else if (strcasecmp(alignment, "left") == 0) {
0607         propfloat = CSS_VAL_LEFT;
0608         propvalign = CSS_VAL_TOP;
0609     } else if (strcasecmp(alignment, "right") == 0) {
0610         propfloat = CSS_VAL_RIGHT;
0611         propvalign = CSS_VAL_TOP;
0612     } else if (strcasecmp(alignment, "top") == 0) {
0613         propvalign = CSS_VAL_TOP;
0614     } else if (strcasecmp(alignment, "middle") == 0) {
0615         propvalign = CSS_VAL__KHTML_BASELINE_MIDDLE;
0616     } else if (strcasecmp(alignment, "center") == 0) {
0617         propvalign = CSS_VAL_MIDDLE;
0618     } else if (strcasecmp(alignment, "bottom") == 0) {
0619         propvalign = CSS_VAL_BASELINE;
0620     } else if (strcasecmp(alignment, "texttop") == 0) {
0621         propvalign = CSS_VAL_TEXT_TOP;
0622     }
0623 
0624     if (propfloat != -1) {
0625         addCSSProperty(CSS_PROP_FLOAT, propfloat);
0626     }
0627     if (propvalign != -1) {
0628         addCSSProperty(CSS_PROP_VERTICAL_ALIGN, propvalign);
0629     }
0630 }
0631 
0632 DOMString HTMLElementImpl::contentEditable() const
0633 {
0634     document()->updateRendering();
0635 
0636     if (!renderer()) {
0637         return "false";
0638     }
0639 
0640     switch (renderer()->style()->userInput()) {
0641     case UI_ENABLED:
0642         return "true";
0643     case UI_DISABLED:
0644     case UI_NONE:
0645         return "false";
0646     default:;
0647     }
0648     return "inherit";
0649 }
0650 
0651 void HTMLElementImpl::setContentEditable(AttributeImpl *attr)
0652 {
0653     const DOMString &enabled = attr->value();
0654     if (enabled.isEmpty() || strcasecmp(enabled, "true") == 0) {
0655         addCSSProperty(CSS_PROP__KHTML_USER_INPUT, CSS_VAL_ENABLED);
0656     } else if (strcasecmp(enabled, "false") == 0) {
0657         addCSSProperty(CSS_PROP__KHTML_USER_INPUT, CSS_VAL_NONE);
0658     } else if (strcasecmp(enabled, "inherit") == 0) {
0659         addCSSProperty(CSS_PROP__KHTML_USER_INPUT, CSS_VAL_INHERIT);
0660     }
0661 }
0662 
0663 void HTMLElementImpl::setContentEditable(const DOMString &enabled)
0664 {
0665     if (enabled == "inherit") {
0666         int exceptionCode;
0667         removeAttribute(ATTR_CONTENTEDITABLE, exceptionCode);
0668     } else {
0669         setAttribute(ATTR_CONTENTEDITABLE, enabled.isEmpty() ? "true" : enabled);
0670     }
0671 }
0672 
0673 DOMString HTMLElementImpl::toString() const
0674 {
0675     if (!hasChildNodes()) {
0676         DOMString result = openTagStartToString();
0677         result += ">";
0678 
0679         if (endTagRequirement(id()) == REQUIRED) {
0680             result += "</";
0681             result += nonCaseFoldedTagName();
0682             result += ">";
0683         }
0684 
0685         return result;
0686     }
0687 
0688     return ElementImpl::toString();
0689 }
0690 
0691 // -------------------------------------------------------------------------
0692 HTMLGenericElementImpl::HTMLGenericElementImpl(DocumentImpl *doc, ushort i)
0693     : HTMLElementImpl(doc)
0694 {
0695     m_localName = LocalName::fromId(i);
0696 }
0697 
0698 HTMLGenericElementImpl::HTMLGenericElementImpl(DocumentImpl *doc, LocalName l)
0699     : HTMLElementImpl(doc),
0700       m_localName(l)
0701 {}
0702 
0703 HTMLGenericElementImpl::~HTMLGenericElementImpl()
0704 {
0705 }