1 /**
2 * This file is part of the DOM implementation for KDE.
3 *
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * (C) 2003 Dirk Mueller (mueller@kde.org)
7 * Copyright (C) 2002 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 //#define DEBUG
27 //#define DEBUG_LAYOUT
28 //#define PAR_DEBUG
29 //#define EVENT_DEBUG
30 //#define UNSUPPORTED_ATTR
31
32 #include "html_elementimpl.h"
33 #include "dtd.h"
34 #include "html_documentimpl.h"
35 #include "htmltokenizer.h"
36
37 #include <khtmlview.h>
38 #include <khtml_part.h>
39
40 #include <rendering/render_object.h>
41 #include <rendering/render_replaced.h>
42 #include <css/css_valueimpl.h>
43 #include <css/css_stylesheetimpl.h>
44 #include <css/cssproperties.h>
45 #include <css/cssvalues.h>
46 #include <xml/dom_textimpl.h>
47 #include <xml/dom2_eventsimpl.h>
48 #include <dom/dom_doc.h>
49
50 #include "khtml_debug.h"
51
52 #undef FOCUS_EVENT // for win32, MinGW
53
54 using namespace DOM;
55 using namespace khtml;
56
HTMLElementImpl(DocumentImpl * doc)57 HTMLElementImpl::HTMLElementImpl(DocumentImpl *doc)
58 : ElementImpl(doc)
59 {
60 m_htmlCompat = doc && doc->htmlMode() != DocumentImpl::XHtml;
61 }
62
~HTMLElementImpl()63 HTMLElementImpl::~HTMLElementImpl()
64 {
65 }
66
isInline() const67 bool HTMLElementImpl::isInline() const
68 {
69 if (renderer()) {
70 return ElementImpl::isInline();
71 }
72
73 switch (id()) {
74 case ID_A:
75 case ID_FONT:
76 case ID_TT:
77 case ID_U:
78 case ID_B:
79 case ID_I:
80 case ID_S:
81 case ID_STRIKE:
82 case ID_BIG:
83 case ID_SMALL:
84
85 // %phrase
86 case ID_EM:
87 case ID_STRONG:
88 case ID_DFN:
89 case ID_CODE:
90 case ID_SAMP:
91 case ID_KBD:
92 case ID_VAR:
93 case ID_CITE:
94 case ID_ABBR:
95 case ID_ACRONYM:
96
97 // %special
98 case ID_SUB:
99 case ID_SUP:
100 case ID_SPAN:
101 case ID_NOBR:
102 case ID_WBR:
103 return true;
104
105 default:
106 return ElementImpl::isInline();
107 }
108 }
109
namespaceURI() const110 DOMString HTMLElementImpl::namespaceURI() const
111 {
112 return DOMString(XHTML_NAMESPACE);
113 }
114
parseAttribute(AttributeImpl * attr)115 void HTMLElementImpl::parseAttribute(AttributeImpl *attr)
116 {
117 DOMString indexstring;
118 switch (attr->id()) {
119 case ATTR_ALIGN:
120 if (attr->val()) {
121 if (strcasecmp(attr->value(), "middle") == 0) {
122 addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL_CENTER);
123 } else {
124 addCSSProperty(CSS_PROP_TEXT_ALIGN, attr->value().lower());
125 }
126 } else {
127 removeCSSProperty(CSS_PROP_TEXT_ALIGN);
128 }
129 break;
130 // the core attributes...
131 case ATTR_ID:
132 // unique id
133 setHasID();
134 document()->incDOMTreeVersion(DocumentImpl::TV_IDNameHref);
135 break;
136 case ATTR_CLASS:
137 if (attr->val()) {
138 DOMString v = attr->value();
139 const QChar *characters = v.unicode();
140 unsigned length = v.length();
141 unsigned i;
142 for (i = 0; i < length && characters[i].isSpace(); ++i) { }
143 setHasClass(i < length);
144 attributes()->setClass(v);
145 } else {
146 setHasClass(false);
147 }
148 document()->incDOMTreeVersion(DocumentImpl::TV_Class);
149 break;
150 case ATTR_NAME:
151 document()->incDOMTreeVersion(DocumentImpl::TV_IDNameHref);
152 break;
153 case ATTR_CONTENTEDITABLE:
154 setContentEditable(attr);
155 break;
156 case ATTR_STYLE:
157 getInlineStyleDecls()->updateFromAttribute(attr->value());
158 setChanged();
159 break;
160 case ATTR_TABINDEX:
161 indexstring = getAttribute(ATTR_TABINDEX);
162 if (attr->val()) {
163 setTabIndex(attr->value().toInt());
164 } else {
165 setNoTabIndex();
166 }
167 break;
168 // i18n attributes
169 case ATTR_LANG:
170 break;
171 case ATTR_DIR:
172 addCSSProperty(CSS_PROP_DIRECTION, attr->value().lower());
173 break;
174 // standard events
175 case ATTR_ONCLICK:
176 setHTMLEventListener(EventImpl::KHTML_ECMA_CLICK_EVENT,
177 document()->createHTMLEventListener(attr->value().string(), "onclick", this));
178 break;
179 case ATTR_ONDBLCLICK:
180 setHTMLEventListener(EventImpl::KHTML_ECMA_DBLCLICK_EVENT,
181 document()->createHTMLEventListener(attr->value().string(), "ondblclick", this));
182 break;
183 case ATTR_ONMOUSEDOWN:
184 setHTMLEventListener(EventImpl::MOUSEDOWN_EVENT,
185 document()->createHTMLEventListener(attr->value().string(), "onmousedown", this));
186 break;
187 case ATTR_ONMOUSEMOVE:
188 setHTMLEventListener(EventImpl::MOUSEMOVE_EVENT,
189 document()->createHTMLEventListener(attr->value().string(), "onmousemove", this));
190 break;
191 case ATTR_ONMOUSEOUT:
192 setHTMLEventListener(EventImpl::MOUSEOUT_EVENT,
193 document()->createHTMLEventListener(attr->value().string(), "onmouseout", this));
194 break;
195 case ATTR_ONMOUSEOVER:
196 setHTMLEventListener(EventImpl::MOUSEOVER_EVENT,
197 document()->createHTMLEventListener(attr->value().string(), "onmouseover", this));
198 break;
199 case ATTR_ONMOUSEUP:
200 setHTMLEventListener(EventImpl::MOUSEUP_EVENT,
201 document()->createHTMLEventListener(attr->value().string(), "onmouseup", this));
202 break;
203 case ATTR_ONKEYDOWN:
204 setHTMLEventListener(EventImpl::KEYDOWN_EVENT,
205 document()->createHTMLEventListener(attr->value().string(), "onkeydown", this));
206 break;
207 case ATTR_ONKEYPRESS:
208 setHTMLEventListener(EventImpl::KEYPRESS_EVENT,
209 document()->createHTMLEventListener(attr->value().string(), "onkeypress", this));
210 break;
211 case ATTR_ONKEYUP:
212 setHTMLEventListener(EventImpl::KEYUP_EVENT,
213 document()->createHTMLEventListener(attr->value().string(), "onkeyup", this));
214 break;
215 case ATTR_ONFOCUS:
216 setHTMLEventListener(EventImpl::FOCUS_EVENT,
217 document()->createHTMLEventListener(attr->value().string(), "onfocus", this));
218 break;
219 case ATTR_ONBLUR:
220 setHTMLEventListener(EventImpl::BLUR_EVENT,
221 document()->createHTMLEventListener(attr->value().string(), "onblur", this));
222 break;
223 case ATTR_ONSCROLL:
224 setHTMLEventListener(EventImpl::SCROLL_EVENT,
225 document()->createHTMLEventListener(attr->value().string(), "onscroll", this));
226 break;
227 // other misc attributes
228 default:
229 #ifdef UNSUPPORTED_ATTR
230 qCDebug(KHTML_LOG) << "UATTR: <" << this->nodeName().string() << "> ["
231 << attr->name().string() << "]=[" << attr->value().string() << "]";
232 #endif
233 break;
234 }
235 }
236
recalcStyle(StyleChange ch)237 void HTMLElementImpl::recalcStyle(StyleChange ch)
238 {
239 ElementImpl::recalcStyle(ch);
240
241 if (m_render /*&& changed*/) {
242 m_render->updateFromElement();
243 }
244 }
245
addCSSProperty(int id,const DOMString & value)246 void HTMLElementImpl::addCSSProperty(int id, const DOMString &value)
247 {
248 if (!m_hasCombinedStyle) {
249 createNonCSSDecl();
250 }
251 nonCSSStyleDecls()->setProperty(id, value, false);
252 setChanged();
253 }
254
addCSSProperty(int id,int value)255 void HTMLElementImpl::addCSSProperty(int id, int value)
256 {
257 if (!m_hasCombinedStyle) {
258 createNonCSSDecl();
259 }
260 nonCSSStyleDecls()->setProperty(id, value, false);
261 setChanged();
262 }
263
addCSSLength(int id,const DOMString & value,bool numOnly,bool multiLength)264 void HTMLElementImpl::addCSSLength(int id, const DOMString &value, bool numOnly, bool multiLength)
265 {
266 if (!m_hasCombinedStyle) {
267 createNonCSSDecl();
268 }
269
270 // strip attribute garbage to avoid CSS parsing errors
271 // ### create specialized hook that avoids parsing every
272 // value twice!
273 if (value.implementation()) {
274 // match \s*[+-]?\d*(\.\d*)?[%\*]?
275 unsigned i = 0, j = 0;
276 QChar *s = value.implementation()->s;
277 unsigned l = value.implementation()->l;
278
279 while (i < l && s[i].isSpace()) {
280 ++i;
281 }
282 if (i < l && (s[i] == '+' || s[i] == '-')) {
283 ++i;
284 }
285 while (i < l && s[i].isDigit()) {
286 ++i, ++j;
287 }
288
289 // no digits!
290 if (j == 0) {
291 return;
292 }
293
294 int v = qBound(-8192, QString::fromRawData(s, i).toInt(), 8191);
295 const char *suffix = "px";
296 if (!numOnly || multiLength) {
297 // look if we find a % or *
298 while (i < l) {
299 if (multiLength && s[i] == '*') {
300 suffix = "";
301 break;
302 }
303 if (s[i] == '%') {
304 suffix = "%";
305 break;
306 }
307 ++i;
308 }
309 }
310 if (numOnly) {
311 suffix = "";
312 }
313
314 QString ns = QString::number(v) + suffix;
315 nonCSSStyleDecls()->setLengthProperty(id, DOMString(ns), false, multiLength);
316 setChanged();
317 return;
318 }
319
320 nonCSSStyleDecls()->setLengthProperty(id, value, false, multiLength);
321 setChanged();
322 }
323
isHexDigit(const QChar & c)324 static inline bool isHexDigit(const QChar &c)
325 {
326 return (c >= '0' && c <= '9') ||
327 (c >= 'a' && c <= 'f') ||
328 (c >= 'A' && c <= 'F');
329 }
330
toHex(const QChar & c)331 static inline int toHex(const QChar &c)
332 {
333 return ((c >= '0' && c <= '9')
334 ? (c.unicode() - '0')
335 : ((c >= 'a' && c <= 'f')
336 ? (c.unicode() - 'a' + 10)
337 : ((c >= 'A' && c <= 'F')
338 ? (c.unicode() - 'A' + 10)
339 : -1)));
340 }
341
342 /* color parsing that tries to match as close as possible IE 6. */
addHTMLColor(int id,const DOMString & c)343 void HTMLElementImpl::addHTMLColor(int id, const DOMString &c)
344 {
345 if (!m_hasCombinedStyle) {
346 createNonCSSDecl();
347 }
348
349 // this is the only case no color gets applied in IE.
350 if (!c.length()) {
351 removeCSSProperty(id);
352 return;
353 }
354
355 if (nonCSSStyleDecls()->setProperty(id, c, false)) {
356 return;
357 }
358
359 QString color = c.string();
360 // not something that fits the specs.
361
362 // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value
363 // out of everyhting you put in. The algorithm is experimentally determined, but seems to work for all test cases I have.
364
365 // the length of the color value is rounded up to the next
366 // multiple of 3. each part of the rgb triple then gets one third
367 // of the length.
368 //
369 // Each triplet is parsed byte by byte, mapping
370 // each number to a hex value (0-9a-fA-F to their values
371 // everything else to 0).
372 //
373 // The highest non zero digit in all triplets is remembered, and
374 // used as a normalization point to normalize to values between 0
375 // and 255.
376
377 if (color.toLower() != "transparent") {
378 if (color[0] == '#') {
379 color.remove(0, 1);
380 }
381 int basicLength = (color.length() + 2) / 3;
382 if (basicLength > 1) {
383 // IE ignores colors with three digits or less
384 // qDebug("trying to fix up color '%s'. basicLength=%d, length=%d",
385 // color.toLatin1().constData(), basicLength, color.length() );
386 int colors[3] = { 0, 0, 0 };
387 int component = 0;
388 int pos = 0;
389 int maxDigit = basicLength - 1;
390 while (component < 3) {
391 // search forward for digits in the string
392 int numDigits = 0;
393 while (pos < (int)color.length() && numDigits < basicLength) {
394 int hex = toHex(color[pos]);
395 colors[component] = (colors[component] << 4);
396 if (hex > 0) {
397 colors[component] += hex;
398 maxDigit = qMin(maxDigit, numDigits);
399 }
400 numDigits++;
401 pos++;
402 }
403 while (numDigits++ < basicLength) {
404 colors[component] <<= 4;
405 }
406 component++;
407 }
408 maxDigit = basicLength - maxDigit;
409 // qDebug("color is %x %x %x, maxDigit=%d", colors[0], colors[1], colors[2], maxDigit );
410
411 // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits
412 maxDigit -= 2;
413 colors[0] >>= 4 * maxDigit;
414 colors[1] >>= 4 * maxDigit;
415 colors[2] >>= 4 * maxDigit;
416 // qDebug("normalized color is %x %x %x", colors[0], colors[1], colors[2] );
417 // assert( colors[0] < 0x100 && colors[1] < 0x100 && colors[2] < 0x100 );
418
419 color.sprintf("#%02x%02x%02x", colors[0], colors[1], colors[2]);
420 // qDebug( "trying to add fixed color string '%s'", color.toLatin1().constData() );
421 if (nonCSSStyleDecls()->setProperty(id, DOMString(color), false)) {
422 return;
423 }
424 }
425 }
426 nonCSSStyleDecls()->setProperty(id, CSS_VAL_BLACK, false);
427 }
428
removeCSSProperty(int id)429 void HTMLElementImpl::removeCSSProperty(int id)
430 {
431 if (!m_hasCombinedStyle) {
432 return;
433 }
434 nonCSSStyleDecls()->setParent(document()->elementSheet());
435 nonCSSStyleDecls()->removeProperty(id);
436 setChanged();
437 }
438
innerHTML() const439 DOMString HTMLElementImpl::innerHTML() const
440 {
441 QString result = ""; //Use QString to accumulate since DOMString is poor for appends
442 for (NodeImpl *child = firstChild(); child != nullptr; child = child->nextSibling()) {
443 DOMString kid = child->toString();
444 result += QString::fromRawData(kid.unicode(), kid.length());
445 }
446 return result;
447 }
448
innerText() const449 DOMString HTMLElementImpl::innerText() const
450 {
451 QString text = "";
452 if (!firstChild()) {
453 return text;
454 }
455
456 const NodeImpl *n = this;
457 // find the next text/image after the anchor, to get a position
458 while (n) {
459 if (n->firstChild()) {
460 n = n->firstChild();
461 } else if (n->nextSibling()) {
462 n = n->nextSibling();
463 } else {
464 NodeImpl *next = nullptr;
465 while (!next) {
466 n = n->parentNode();
467 if (!n || n == (NodeImpl *)this) {
468 goto end;
469 }
470 next = n->nextSibling();
471 }
472 n = next;
473 }
474 if (n->isTextNode()) {
475 DOMStringImpl *data = static_cast<const TextImpl *>(n)->string();
476 text += QString::fromRawData(data->s, data->l);
477 }
478 }
479 end:
480 return text;
481 }
482
createContextualFragment(const DOMString & html)483 DocumentFragment HTMLElementImpl::createContextualFragment(const DOMString &html)
484 {
485 // IE originally restricted innerHTML to a small subset of elements;
486 // and we largely matched that. Mozilla's embrace of innerHTML, however, extended
487 // it to pretty much everything, and so the web (and HTML5) requires it now.
488 // For now, we accept everything, but do not do context-based recovery in the parser.
489 if (!document()->isHTMLDocument()) {
490 return DocumentFragment();
491 }
492
493 DocumentFragmentImpl *fragment = new DocumentFragmentImpl(docPtr());
494 DocumentFragment f(fragment);
495 {
496 HTMLTokenizer tok(docPtr(), fragment);
497 tok.begin();
498 tok.write(html.string(), true);
499 tok.end();
500 }
501
502 // Exceptions are ignored because none ought to happen here.
503 int ignoredExceptionCode;
504
505 // we need to pop <html> and <body> elements and remove <head> to
506 // accomadate folks passing complete HTML documents to make the
507 // child of an element.
508 for (NodeImpl *node = fragment->firstChild(); node;) {
509 if (node->id() == ID_HTML || node->id() == ID_BODY) {
510 NodeImpl *firstChild = node->firstChild();
511 NodeImpl *child = firstChild;
512 while (child) {
513 NodeImpl *nextChild = child->nextSibling();
514 fragment->insertBefore(child, node, ignoredExceptionCode);
515 child = nextChild;
516 }
517 if (!firstChild) {
518 NodeImpl *nextNode = node->nextSibling();
519 fragment->removeChild(node, ignoredExceptionCode);
520 node = nextNode;
521 } else {
522 fragment->removeChild(node, ignoredExceptionCode);
523 node = firstChild;
524 }
525 } else if (node->id() == ID_HEAD) {
526 NodeImpl *nextNode = node->nextSibling();
527 fragment->removeChild(node, ignoredExceptionCode);
528 node = nextNode;
529 } else {
530 node = node->nextSibling();
531 }
532 }
533
534 return f;
535 }
536
setInnerHTML(const DOMString & html,int & exceptioncode)537 void HTMLElementImpl::setInnerHTML(const DOMString &html, int &exceptioncode)
538 {
539 if (id() == ID_SCRIPT || id() == ID_STYLE) {
540 // Script and CSS source shouldn't be parsed as HTML.
541 removeChildren();
542 appendChild(document()->createTextNode(html), exceptioncode);
543 return;
544 }
545
546 DocumentFragment fragment = createContextualFragment(html);
547 if (fragment.isNull()) {
548 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
549 return;
550 }
551
552 // Make sure adding the new child is ok, before removing all children (#96187)
553 checkAddChild(fragment.handle(), exceptioncode);
554 if (exceptioncode) {
555 return;
556 }
557
558 removeChildren();
559 appendChild(fragment.handle(), exceptioncode);
560 }
561
setInnerText(const DOMString & text,int & exceptioncode)562 void HTMLElementImpl::setInnerText(const DOMString &text, int &exceptioncode)
563 {
564 // following the IE specs.
565 if (endTagRequirement(id()) == FORBIDDEN) {
566 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
567 return;
568 }
569 // IE disallows innerHTML on inline elements. I don't see why we should have this restriction, as our
570 // dhtml engine can cope with it. Lars
571 //if ( isInline() ) return false;
572 switch (id()) {
573 case ID_COL:
574 case ID_COLGROUP:
575 case ID_FRAMESET:
576 case ID_HEAD:
577 case ID_HTML:
578 case ID_TABLE:
579 case ID_TBODY:
580 case ID_TFOOT:
581 case ID_THEAD:
582 case ID_TR:
583 exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
584 return;
585 default:
586 break;
587 }
588
589 removeChildren();
590
591 TextImpl *t = new TextImpl(docPtr(), text.implementation());
592 appendChild(t, exceptioncode);
593 }
594
addHTMLAlignment(DOMString alignment)595 void HTMLElementImpl::addHTMLAlignment(DOMString alignment)
596 {
597 //qDebug("alignment is %s", alignment.string().toLatin1().constData() );
598 // vertical alignment with respect to the current baseline of the text
599 // right or left means floating images
600 int propfloat = -1;
601 int propvalign = -1;
602 if (strcasecmp(alignment, "absmiddle") == 0) {
603 propvalign = CSS_VAL_MIDDLE;
604 } else if (strcasecmp(alignment, "absbottom") == 0) {
605 propvalign = CSS_VAL_BOTTOM;
606 } else if (strcasecmp(alignment, "left") == 0) {
607 propfloat = CSS_VAL_LEFT;
608 propvalign = CSS_VAL_TOP;
609 } else if (strcasecmp(alignment, "right") == 0) {
610 propfloat = CSS_VAL_RIGHT;
611 propvalign = CSS_VAL_TOP;
612 } else if (strcasecmp(alignment, "top") == 0) {
613 propvalign = CSS_VAL_TOP;
614 } else if (strcasecmp(alignment, "middle") == 0) {
615 propvalign = CSS_VAL__KHTML_BASELINE_MIDDLE;
616 } else if (strcasecmp(alignment, "center") == 0) {
617 propvalign = CSS_VAL_MIDDLE;
618 } else if (strcasecmp(alignment, "bottom") == 0) {
619 propvalign = CSS_VAL_BASELINE;
620 } else if (strcasecmp(alignment, "texttop") == 0) {
621 propvalign = CSS_VAL_TEXT_TOP;
622 }
623
624 if (propfloat != -1) {
625 addCSSProperty(CSS_PROP_FLOAT, propfloat);
626 }
627 if (propvalign != -1) {
628 addCSSProperty(CSS_PROP_VERTICAL_ALIGN, propvalign);
629 }
630 }
631
contentEditable() const632 DOMString HTMLElementImpl::contentEditable() const
633 {
634 document()->updateRendering();
635
636 if (!renderer()) {
637 return "false";
638 }
639
640 switch (renderer()->style()->userInput()) {
641 case UI_ENABLED:
642 return "true";
643 case UI_DISABLED:
644 case UI_NONE:
645 return "false";
646 default:;
647 }
648 return "inherit";
649 }
650
setContentEditable(AttributeImpl * attr)651 void HTMLElementImpl::setContentEditable(AttributeImpl *attr)
652 {
653 const DOMString &enabled = attr->value();
654 if (enabled.isEmpty() || strcasecmp(enabled, "true") == 0) {
655 addCSSProperty(CSS_PROP__KHTML_USER_INPUT, CSS_VAL_ENABLED);
656 } else if (strcasecmp(enabled, "false") == 0) {
657 addCSSProperty(CSS_PROP__KHTML_USER_INPUT, CSS_VAL_NONE);
658 } else if (strcasecmp(enabled, "inherit") == 0) {
659 addCSSProperty(CSS_PROP__KHTML_USER_INPUT, CSS_VAL_INHERIT);
660 }
661 }
662
setContentEditable(const DOMString & enabled)663 void HTMLElementImpl::setContentEditable(const DOMString &enabled)
664 {
665 if (enabled == "inherit") {
666 int exceptionCode;
667 removeAttribute(ATTR_CONTENTEDITABLE, exceptionCode);
668 } else {
669 setAttribute(ATTR_CONTENTEDITABLE, enabled.isEmpty() ? "true" : enabled);
670 }
671 }
672
toString() const673 DOMString HTMLElementImpl::toString() const
674 {
675 if (!hasChildNodes()) {
676 DOMString result = openTagStartToString();
677 result += ">";
678
679 if (endTagRequirement(id()) == REQUIRED) {
680 result += "</";
681 result += nonCaseFoldedTagName();
682 result += ">";
683 }
684
685 return result;
686 }
687
688 return ElementImpl::toString();
689 }
690
691 // -------------------------------------------------------------------------
HTMLGenericElementImpl(DocumentImpl * doc,ushort i)692 HTMLGenericElementImpl::HTMLGenericElementImpl(DocumentImpl *doc, ushort i)
693 : HTMLElementImpl(doc)
694 {
695 m_localName = LocalName::fromId(i);
696 }
697
HTMLGenericElementImpl(DocumentImpl * doc,LocalName l)698 HTMLGenericElementImpl::HTMLGenericElementImpl(DocumentImpl *doc, LocalName l)
699 : HTMLElementImpl(doc),
700 m_localName(l)
701 {}
702
~HTMLGenericElementImpl()703 HTMLGenericElementImpl::~HTMLGenericElementImpl()
704 {
705 }
706