Reviewed by John.
[WebKit-https.git] / WebCore / khtml / html / html_elementimpl.cpp
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  * Copyright (C) 2003 Apple Computer, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  */
24 // -------------------------------------------------------------------------
25 //#define DEBUG
26 //#define DEBUG_LAYOUT
27 //#define PAR_DEBUG
28 //#define EVENT_DEBUG
29 //#define UNSUPPORTED_ATTR
30
31 #include "html/dtd.h"
32 #include "html/html_elementimpl.h"
33 #include "html/html_documentimpl.h"
34 #include "html/htmltokenizer.h"
35
36 #include "misc/htmlhashes.h"
37 #include "misc/khtml_text_operations.h"
38
39 #include "khtmlview.h"
40 #include "khtml_part.h"
41
42 #include "rendering/render_object.h"
43 #include "rendering/render_replaced.h"
44 #include "css/css_valueimpl.h"
45 #include "css/css_stylesheetimpl.h"
46 #include "css/cssproperties.h"
47 #include "css/cssvalues.h"
48 #include "css/css_ruleimpl.h"
49 #include "xml/dom_selection.h"
50 #include "xml/dom_textimpl.h"
51 #include "xml/dom2_eventsimpl.h"
52
53 #include <kdebug.h>
54
55 using namespace DOM;
56 using namespace khtml;
57
58 CSSMappedAttributeDeclarationImpl::~CSSMappedAttributeDeclarationImpl() {
59     if (m_entryType != ePersistent)
60         HTMLElementImpl::removeMappedAttributeDecl(m_entryType, m_attrName, m_attrValue);
61 }
62
63 QPtrDict<QPtrDict<QPtrDict<CSSMappedAttributeDeclarationImpl> > >* HTMLElementImpl::m_mappedAttributeDecls = 0;
64
65 CSSMappedAttributeDeclarationImpl* HTMLElementImpl::getMappedAttributeDecl(MappedAttributeEntry entryType, AttributeImpl* attr)
66 {
67     if (!m_mappedAttributeDecls)
68         return 0;
69     
70     QPtrDict<QPtrDict<CSSMappedAttributeDeclarationImpl> >* attrNameDict = m_mappedAttributeDecls->find((void*)entryType);
71     if (attrNameDict) {
72         QPtrDict<CSSMappedAttributeDeclarationImpl>* attrValueDict = attrNameDict->find((void*)attr->id());
73         if (attrValueDict)
74             return attrValueDict->find(attr->value().implementation());
75     }
76     return 0;
77 }
78
79 void HTMLElementImpl::setMappedAttributeDecl(MappedAttributeEntry entryType, AttributeImpl* attr, CSSMappedAttributeDeclarationImpl* decl)
80 {
81     if (!m_mappedAttributeDecls)
82         m_mappedAttributeDecls = new QPtrDict<QPtrDict<QPtrDict<CSSMappedAttributeDeclarationImpl> > >;
83     
84     QPtrDict<CSSMappedAttributeDeclarationImpl>* attrValueDict = 0;
85     QPtrDict<QPtrDict<CSSMappedAttributeDeclarationImpl> >* attrNameDict = m_mappedAttributeDecls->find((void*)entryType);
86     if (!attrNameDict) {
87         attrNameDict = new QPtrDict<QPtrDict<CSSMappedAttributeDeclarationImpl> >;
88         attrNameDict->setAutoDelete(true);
89         m_mappedAttributeDecls->insert((void*)entryType, attrNameDict);
90     }
91     else
92         attrValueDict = attrNameDict->find((void*)attr->id());
93     if (!attrValueDict) {
94         attrValueDict = new QPtrDict<CSSMappedAttributeDeclarationImpl>;
95         if (entryType == ePersistent)
96             attrValueDict->setAutoDelete(true);
97         attrNameDict->insert((void*)attr->id(), attrValueDict);
98     }
99     attrValueDict->replace(attr->value().implementation(), decl);
100 }
101
102 void HTMLElementImpl::removeMappedAttributeDecl(MappedAttributeEntry entryType, NodeImpl::Id attrName, const AtomicString& attrValue)
103 {
104     if (!m_mappedAttributeDecls)
105         return;
106     
107     QPtrDict<QPtrDict<CSSMappedAttributeDeclarationImpl> >* attrNameDict = m_mappedAttributeDecls->find((void*)entryType);
108     if (!attrNameDict)
109         return;
110     QPtrDict<CSSMappedAttributeDeclarationImpl>* attrValueDict = attrNameDict->find((void*)attrName);
111     if (!attrValueDict)
112         return;
113     attrValueDict->remove(attrValue.implementation());
114 }
115
116 HTMLAttributeImpl::~HTMLAttributeImpl()
117 {
118     if (m_styleDecl)
119         m_styleDecl->deref();
120 }
121
122 AttributeImpl* HTMLAttributeImpl::clone(bool preserveDecl) const
123 {
124     return new HTMLAttributeImpl(m_id, _value, preserveDecl ? m_styleDecl : 0);
125 }
126
127 HTMLNamedAttrMapImpl::HTMLNamedAttrMapImpl(ElementImpl *e)
128 :NamedAttrMapImpl(e), m_mappedAttributeCount(0)
129 {}
130
131 void HTMLNamedAttrMapImpl::clearAttributes()
132 {
133     m_classList.clear();
134     m_mappedAttributeCount = 0;
135     NamedAttrMapImpl::clearAttributes();
136 }
137
138 bool HTMLNamedAttrMapImpl::isHTMLAttributeMap() const
139 {
140     return true;
141 }
142
143 int HTMLNamedAttrMapImpl::declCount() const
144 {
145     int result = 0;
146     for (uint i = 0; i < length(); i++) {
147         HTMLAttributeImpl* attr = attributeItem(i);
148         if (attr->decl())
149             result++;
150     }
151     return result;
152 }
153
154 bool HTMLNamedAttrMapImpl::mapsEquivalent(const HTMLNamedAttrMapImpl* otherMap) const
155 {
156     // The # of decls must match.
157     if (declCount() != otherMap->declCount())
158         return false;
159     
160     // The values for each decl must match.
161     for (uint i = 0; i < length(); i++) {
162         HTMLAttributeImpl* attr = attributeItem(i);
163         if (attr->decl()) {
164             AttributeImpl* otherAttr = otherMap->getAttributeItem(attr->id());
165             if (!otherAttr || (attr->value() != otherAttr->value()))
166                 return false;
167         }
168     }
169     return true;
170 }
171
172 void HTMLNamedAttrMapImpl::parseClassAttribute(const DOMString& classStr)
173 {
174     m_classList.clear();
175     if (!element->hasClass())
176         return;
177     
178     DOMString classAttr = element->getDocument()->inCompatMode() ? 
179         (classStr.implementation()->isLower() ? classStr : DOMString(classStr.implementation()->lower())) :
180         classStr;
181     
182     if (classAttr.find(' ') == -1)
183         m_classList.setString(AtomicString(classAttr));
184     else {
185         QString val = classAttr.string();
186         QStringList list = QStringList::split(' ', val);
187         
188         AtomicStringList* curr = 0;
189         for (QStringList::Iterator it = list.begin(); it != list.end(); ++it)
190         {
191             const QString& singleClass = *it;
192             if (!singleClass.isEmpty()) {
193                 if (curr) {
194                     curr->setNext(new AtomicStringList(AtomicString(singleClass)));
195                     curr = curr->next();
196                 }
197                 else {
198                     m_classList.setString(AtomicString(singleClass));
199                     curr = &m_classList;
200                 }
201             }
202         }
203     }
204 }
205
206 // ------------------------------------------------------------------
207
208 HTMLElementImpl::HTMLElementImpl(DocumentPtr *doc)
209     : ElementImpl(doc)
210 {
211     m_inlineStyleDecl = 0;
212 }
213
214 HTMLElementImpl::~HTMLElementImpl()
215 {
216     if (m_inlineStyleDecl) {
217         m_inlineStyleDecl->setParent(0);
218         m_inlineStyleDecl->deref();
219     }
220 }
221
222 AttributeImpl* HTMLElementImpl::createAttribute(NodeImpl::Id id, DOMStringImpl* value)
223 {
224     return new HTMLAttributeImpl(id, value);
225 }
226
227 bool HTMLElementImpl::isInline() const
228 {
229     if (renderer())
230         return ElementImpl::isInline();
231     
232     switch(id()) {
233         case ID_A:
234         case ID_FONT:
235         case ID_TT:
236         case ID_U:
237         case ID_B:
238         case ID_I:
239         case ID_S:
240         case ID_STRIKE:
241         case ID_BIG:
242         case ID_SMALL:
243     
244             // %phrase
245         case ID_EM:
246         case ID_STRONG:
247         case ID_DFN:
248         case ID_CODE:
249         case ID_SAMP:
250         case ID_KBD:
251         case ID_VAR:
252         case ID_CITE:
253         case ID_ABBR:
254         case ID_ACRONYM:
255     
256             // %special
257         case ID_SUB:
258         case ID_SUP:
259         case ID_SPAN:
260         case ID_NOBR:
261         case ID_WBR:
262             return true;
263             
264         default:
265             return ElementImpl::isInline();
266     }
267 }
268
269 void HTMLElementImpl::createInlineStyleDecl()
270 {
271     m_inlineStyleDecl = new CSSStyleDeclarationImpl(0);
272     m_inlineStyleDecl->ref();
273     m_inlineStyleDecl->setParent(getDocument()->elementSheet());
274     m_inlineStyleDecl->setNode(this);
275     m_inlineStyleDecl->setStrictParsing(!getDocument()->inCompatMode());
276 }
277
278 void HTMLElementImpl::attributeChanged(AttributeImpl* attr, bool preserveDecls)
279 {
280     HTMLAttributeImpl* htmlAttr = static_cast<HTMLAttributeImpl*>(attr);
281     if (htmlAttr->decl() && !preserveDecls) {
282         htmlAttr->setDecl(0);
283         setChanged();
284         if (namedAttrMap)
285             static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->declRemoved();
286     }
287
288     bool checkDecl = true;
289     MappedAttributeEntry entry;
290     bool needToParse = mapToEntry(attr->id(), entry);
291     if (preserveDecls) {
292         if (htmlAttr->decl()) {
293             setChanged();
294             if (namedAttrMap)
295                 static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->declAdded();
296             checkDecl = false;
297         }
298     }
299     else if (!attr->isNull() && entry != eNone) {
300         CSSMappedAttributeDeclarationImpl* decl = getMappedAttributeDecl(entry, attr);
301         if (decl) {
302             htmlAttr->setDecl(decl);
303             setChanged();
304             if (namedAttrMap)
305                 static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->declAdded();
306             checkDecl = false;
307         } else
308             needToParse = true;
309     }
310
311     if (needToParse)
312         parseHTMLAttribute(htmlAttr);
313     
314     if (checkDecl && htmlAttr->decl()) {
315         // Add the decl to the table in the appropriate spot.
316         setMappedAttributeDecl(entry, attr, htmlAttr->decl());
317         htmlAttr->decl()->setMappedState(entry, attr->id(), attr->value());
318         htmlAttr->decl()->setParent(0);
319         htmlAttr->decl()->setNode(0);
320         if (namedAttrMap)
321             static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->declAdded();
322     }
323 }
324
325 bool HTMLElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
326 {
327     switch (attr)
328     {
329         case ATTR_ALIGN:
330         case ATTR_CONTENTEDITABLE:
331         case ATTR_DIR:
332             result = eUniversal;
333             return false;
334         default:
335             break;
336     }
337     
338     result = eNone;
339     return true;
340 }
341     
342 void HTMLElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
343 {
344     DOMString indexstring;
345     switch (attr->id())
346     {
347     case ATTR_ALIGN:
348         if (strcasecmp(attr->value(), "middle" ) == 0)
349             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, "center");
350         else
351             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, attr->value());
352         break;
353 // the core attributes...
354     case ATTR_ID:
355         // unique id
356         setHasID(!attr->isNull());
357         if (namedAttrMap) {
358             if (attr->isNull())
359                 namedAttrMap->setID(nullAtom);
360             else if (getDocument()->inCompatMode() && !attr->value().implementation()->isLower())
361                 namedAttrMap->setID(AtomicString(attr->value().implementation()->lower()));
362             else
363                 namedAttrMap->setID(attr->value());
364         }
365         setChanged();
366         break;
367     case ATTR_CLASS:
368         // class
369         setHasClass(!attr->isNull());
370         if (namedAttrMap) static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->parseClassAttribute(attr->value());
371         setChanged();
372         break;
373     case ATTR_CONTENTEDITABLE:
374         setContentEditable(attr);
375         break;
376     case ATTR_STYLE:
377         // ### we need to remove old style info in case there was any!
378         // ### the inline sheet ay contain more than 1 property!
379         // stylesheet info
380         setHasStyle();
381         if (!m_inlineStyleDecl) createInlineStyleDecl();
382         m_inlineStyleDecl->setProperty(attr->value());
383         setChanged();
384         break;
385     case ATTR_TABINDEX:
386         indexstring=getAttribute(ATTR_TABINDEX);
387         if (indexstring.length())
388             setTabIndex(indexstring.toInt());
389         break;
390 // i18n attributes
391     case ATTR_LANG:
392         break;
393     case ATTR_DIR:
394         addCSSProperty(attr, CSS_PROP_DIRECTION, attr->value());
395         addCSSProperty(attr, CSS_PROP_UNICODE_BIDI, CSS_VAL_EMBED);
396         break;
397 // standard events
398     case ATTR_ONCLICK:
399         setHTMLEventListener(EventImpl::KHTML_CLICK_EVENT,
400             getDocument()->createHTMLEventListener(attr->value().string()));
401         break;
402     case ATTR_ONCONTEXTMENU:
403         setHTMLEventListener(EventImpl::CONTEXTMENU_EVENT,
404             getDocument()->createHTMLEventListener(attr->value().string()));
405         break;
406     case ATTR_ONDBLCLICK:
407         setHTMLEventListener(EventImpl::KHTML_DBLCLICK_EVENT,
408             getDocument()->createHTMLEventListener(attr->value().string()));
409         break;
410     case ATTR_ONMOUSEDOWN:
411         setHTMLEventListener(EventImpl::MOUSEDOWN_EVENT,
412             getDocument()->createHTMLEventListener(attr->value().string()));
413         break;
414     case ATTR_ONMOUSEMOVE:
415         setHTMLEventListener(EventImpl::MOUSEMOVE_EVENT,
416             getDocument()->createHTMLEventListener(attr->value().string()));
417         break;
418     case ATTR_ONMOUSEOUT:
419         setHTMLEventListener(EventImpl::MOUSEOUT_EVENT,
420             getDocument()->createHTMLEventListener(attr->value().string()));
421         break;
422     case ATTR_ONMOUSEOVER:
423         setHTMLEventListener(EventImpl::MOUSEOVER_EVENT,
424             getDocument()->createHTMLEventListener(attr->value().string()));
425         break;
426     case ATTR_ONMOUSEUP:
427         setHTMLEventListener(EventImpl::MOUSEUP_EVENT,
428             getDocument()->createHTMLEventListener(attr->value().string()));
429         break;
430     case ATTR_ONFOCUS:
431         setHTMLEventListener(EventImpl::DOMFOCUSIN_EVENT,
432             getDocument()->createHTMLEventListener(attr->value().string()));
433         break;
434     case ATTR_ONKEYDOWN:
435         setHTMLEventListener(EventImpl::KEYDOWN_EVENT,
436             getDocument()->createHTMLEventListener(attr->value().string()));
437         break;
438     case ATTR_ONKEYPRESS:
439         setHTMLEventListener(EventImpl::KEYPRESS_EVENT,
440             getDocument()->createHTMLEventListener(attr->value().string()));
441         break;
442     case ATTR_ONKEYUP:
443         setHTMLEventListener(EventImpl::KEYUP_EVENT,
444             getDocument()->createHTMLEventListener(attr->value().string()));
445         break;
446     case ATTR_ONSCROLL:
447         setHTMLEventListener(EventImpl::SCROLL_EVENT,
448             getDocument()->createHTMLEventListener(attr->value().string()));
449         break;
450 // other misc attributes
451     default:
452 #ifdef UNSUPPORTED_ATTR
453         kdDebug(6030) << "UATTR: <" << this->nodeName().string() << "> ["
454                       << attr->name().string() << "]=[" << attr->value().string() << "]" << endl;
455 #endif
456         break;
457     }
458 }
459
460 void HTMLElementImpl::createAttributeMap() const
461 {
462     namedAttrMap = new HTMLNamedAttrMapImpl(const_cast<HTMLElementImpl*>(this));
463     namedAttrMap->ref();
464 }
465
466 CSSStyleDeclarationImpl* HTMLElementImpl::getInlineStyleDecl()
467 {
468     if (!m_inlineStyleDecl)
469         createInlineStyleDecl();
470     return m_inlineStyleDecl;
471 }
472
473 CSSStyleDeclarationImpl* HTMLElementImpl::additionalAttributeStyleDecl()
474 {
475     return 0;
476 }
477
478 const AtomicStringList* HTMLElementImpl::getClassList() const
479 {
480     return namedAttrMap ? static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->getClassList() : 0;
481 }
482
483 static inline bool isHexDigit( const QChar &c ) {
484     return ( c >= '0' && c <= '9' ) ||
485            ( c >= 'a' && c <= 'f' ) ||
486            ( c >= 'A' && c <= 'F' );
487 }
488
489 static inline int toHex( const QChar &c ) {
490     return ( (c >= '0' && c <= '9')
491              ? (c.unicode() - '0')
492              : ( ( c >= 'a' && c <= 'f' )
493                  ? (c.unicode() - 'a' + 10)
494                  : ( ( c >= 'A' && c <= 'F' )
495                      ? (c.unicode() - 'A' + 10)
496                      : -1 ) ) );
497 }
498
499 void HTMLElementImpl::addCSSProperty(HTMLAttributeImpl* attr, int id, const DOMString &value)
500 {
501     if (!attr->decl()) createMappedDecl(attr);
502     attr->decl()->setProperty(id, value, false);
503 }
504
505 void HTMLElementImpl::addCSSProperty(HTMLAttributeImpl* attr, int id, int value)
506 {
507     if (!attr->decl()) createMappedDecl(attr);
508     attr->decl()->setProperty(id, value, false);
509 }
510
511 void HTMLElementImpl::addCSSStringProperty(HTMLAttributeImpl* attr, int id, const DOMString &value, CSSPrimitiveValue::UnitTypes type)
512 {
513     if (!attr->decl()) createMappedDecl(attr);
514     attr->decl()->setStringProperty(id, value, type, false);
515 }
516
517 void HTMLElementImpl::addCSSImageProperty(HTMLAttributeImpl* attr, int id, const DOMString &URL)
518 {
519     if (!attr->decl()) createMappedDecl(attr);
520     attr->decl()->setImageProperty(id, URL, false);
521 }
522
523 void HTMLElementImpl::addCSSLength(HTMLAttributeImpl* attr, int id, const DOMString &value)
524 {
525     // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
526     // length unit and make the appropriate parsed value.
527     if (!attr->decl()) createMappedDecl(attr);
528
529     // strip attribute garbage..
530     DOMStringImpl* v = value.implementation();
531     if ( v ) {
532         unsigned int l = 0;
533         
534         while ( l < v->l && v->s[l].unicode() <= ' ') l++;
535         
536         for ( ;l < v->l; l++ ) {
537             char cc = v->s[l].latin1();
538             if ( cc > '9' || ( cc < '0' && cc != '*' && cc != '%' && cc != '.') )
539                 break;
540         }
541         if ( l != v->l ) {
542             attr->decl()->setLengthProperty(id, DOMString( v->s, l ), false);
543             return;
544         }
545     }
546     
547     attr->decl()->setLengthProperty(id, value, false);
548 }
549
550 /* color parsing that tries to match as close as possible IE 6. */
551 void HTMLElementImpl::addHTMLColor(HTMLAttributeImpl* attr, int id, const DOMString &c)
552 {
553     // this is the only case no color gets applied in IE.
554     if ( !c.length() )
555         return;
556
557     if (!attr->decl()) createMappedDecl(attr);
558     
559     if (attr->decl()->setProperty(id, c, false) )
560         return;
561     
562     QString color = c.string();
563     // not something that fits the specs.
564     
565     // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value
566     // out of everyhting you put in. The algorithm is experimentally determined, but seems to work for all test cases I have.
567     
568     // the length of the color value is rounded up to the next
569     // multiple of 3. each part of the rgb triple then gets one third
570     // of the length.
571     //
572     // Each triplet is parsed byte by byte, mapping
573     // each number to a hex value (0-9a-fA-F to their values
574     // everything else to 0).
575     //
576     // The highest non zero digit in all triplets is remembered, and
577     // used as a normalization point to normalize to values between 0
578     // and 255.
579     
580     if ( color.lower() != "transparent" ) {
581         if ( color[0] == '#' )
582             color.remove( 0,  1 );
583         int basicLength = (color.length() + 2) / 3;
584         if ( basicLength > 1 ) {
585             // IE ignores colors with three digits or less
586             //      qDebug("trying to fix up color '%s'. basicLength=%d, length=%d",
587             //             color.latin1(), basicLength, color.length() );
588             int colors[3] = { 0, 0, 0 };
589             int component = 0;
590             int pos = 0;
591             int maxDigit = basicLength-1;
592             while ( component < 3 ) {
593                 // search forward for digits in the string
594                 int numDigits = 0;
595                 while ( pos < (int)color.length() && numDigits < basicLength ) {
596                     int hex = toHex( color[pos] );
597                     colors[component] = (colors[component] << 4);
598                     if ( hex > 0 ) {
599                         colors[component] += hex;
600                         maxDigit = QMIN( maxDigit, numDigits );
601                     }
602                     numDigits++;
603                     pos++;
604                 }
605                 while ( numDigits++ < basicLength )
606                     colors[component] <<= 4;
607                 component++;
608             }
609             maxDigit = basicLength - maxDigit;
610             //      qDebug("color is %x %x %x, maxDigit=%d",  colors[0], colors[1], colors[2], maxDigit );
611             
612             // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits
613             maxDigit -= 2;
614             colors[0] >>= 4*maxDigit;
615             colors[1] >>= 4*maxDigit;
616             colors[2] >>= 4*maxDigit;
617             //      qDebug("normalized color is %x %x %x",  colors[0], colors[1], colors[2] );
618             //  assert( colors[0] < 0x100 && colors[1] < 0x100 && colors[2] < 0x100 );
619             
620             color.sprintf("#%02x%02x%02x", colors[0], colors[1], colors[2] );
621             //      qDebug( "trying to add fixed color string '%s'", color.latin1() );
622             if ( attr->decl()->setProperty(id, DOMString(color), false) )
623                 return;
624         }
625     }
626     attr->decl()->setProperty(id, CSS_VAL_BLACK, false);
627 }
628
629 void HTMLElementImpl::createMappedDecl(HTMLAttributeImpl* attr)
630 {
631     CSSMappedAttributeDeclarationImpl* decl = new CSSMappedAttributeDeclarationImpl(0);
632     attr->setDecl(decl);
633     decl->setParent(getDocument()->elementSheet());
634     decl->setNode(this);
635     decl->setStrictParsing(false); // Mapped attributes are just always quirky.
636 }
637
638 DOMString HTMLElementImpl::innerHTML() const
639 {
640     return toHTML();
641 }
642
643 DOMString HTMLElementImpl::outerHTML() const
644 {
645     return recursive_toHTML(true);
646 }
647
648 DOMString HTMLElementImpl::innerText() const
649 {
650     Node startContainer(const_cast<HTMLElementImpl *>(this));
651     long startOffset = 0;
652     Node endContainer(const_cast<HTMLElementImpl *>(this));
653
654     long endOffset = 0;
655
656     for (NodeImpl *child = firstChild(); child; child = child->nextSibling()) {
657         endOffset++;
658     }
659
660     Range innerRange(startContainer, startOffset, endContainer, endOffset);
661
662     return plainText(innerRange);
663 }
664
665 DOMString HTMLElementImpl::outerText() const
666 {
667     // Getting outerText is the same as getting innerText, only
668     // setting is different. You would think this should get the plain
669     // text for the outer range, but this is wrong, <br> for instance
670     // would return different values for inner and outer text by such
671     // a rule, but it doesn't.
672     return innerText();
673 }
674
675
676 DocumentFragmentImpl *HTMLElementImpl::createContextualFragment( const DOMString &html )
677 {
678     // the following is in accordance with the definition as used by IE
679     if( endTag[id()] == FORBIDDEN )
680         return NULL;
681     // IE disallows innerHTML on inline elements. I don't see why we should have this restriction, as our
682     // dhtml engine can cope with it. Lars
683     //if ( isInline() ) return false;
684     switch( id() ) {
685         case ID_COL:
686         case ID_COLGROUP:
687         case ID_FRAMESET:
688         case ID_HEAD:
689         case ID_STYLE:
690         case ID_TABLE:
691         case ID_TBODY:
692         case ID_TFOOT:
693         case ID_THEAD:
694         case ID_TITLE:
695             return NULL;
696         default:
697             break;
698     }
699     if ( !getDocument()->isHTMLDocument() )
700         return NULL;
701
702     DocumentFragmentImpl *fragment = new DocumentFragmentImpl( docPtr() );
703     fragment->ref();
704     {
705         HTMLTokenizer tok( docPtr(), fragment );
706         tok.begin();
707         tok.write( html.string(), true );
708         tok.end();
709     }
710
711     // Exceptions are ignored because none ought to happen here.
712     int ignoredExceptionCode;
713
714     // we need to pop <html> and <body> elements and remove <head> to
715     // accomadate folks passing complete HTML documents to make the
716     // child of an element.
717
718     NodeImpl *nextNode;
719     for (NodeImpl *node = fragment->firstChild(); node != NULL; node = nextNode) {
720         nextNode = node->nextSibling();
721         if (node->id() == ID_HTML || node->id() == ID_BODY) {
722             NodeImpl *firstChild = node->firstChild();
723             if (firstChild != NULL) {
724                 nextNode = firstChild;
725             }
726             NodeImpl *nextChild;
727             for (NodeImpl *child = firstChild; child != NULL; child = nextChild) {
728                 nextChild = child->nextSibling();
729                 child->ref();
730                 node->removeChild(child, ignoredExceptionCode);
731                 fragment->insertBefore(child, node, ignoredExceptionCode);
732                 child->deref();
733             }
734             fragment->removeChild(node, ignoredExceptionCode);
735             // FIXME: Does node leak here?
736         } else if (node->id() == ID_HEAD) {
737             fragment->removeChild(node, ignoredExceptionCode);
738             // FIXME: Does node leak here?
739         }
740     }
741
742     // Trick to get the fragment back to the floating state, with 0
743     // refs but not destroyed.
744     fragment->setParent(this);
745     fragment->deref();
746     fragment->setParent(0);
747
748     return fragment;
749 }
750
751 bool HTMLElementImpl::setInnerHTML( const DOMString &html )
752 {
753     DocumentFragmentImpl *fragment = createContextualFragment( html );
754     if (fragment == NULL) {
755         return false;
756     }
757
758     removeChildren();
759     int ec = 0;
760     appendChild( fragment, ec );
761     delete fragment;
762     return !ec;
763 }
764
765 bool HTMLElementImpl::setOuterHTML( const DOMString &html )
766 {
767     DocumentFragmentImpl *fragment = createContextualFragment( html );
768     if (fragment == NULL) {
769         return false;
770     }
771     
772     int ec = 0;
773     parentNode()->replaceChild(fragment, this, ec);
774     return !ec;
775 }
776
777
778 bool HTMLElementImpl::setInnerText( const DOMString &text )
779 {
780     // following the IE specs.
781     if( endTag[id()] == FORBIDDEN )
782         return false;
783     // IE disallows innerText on inline elements. I don't see why we should have this restriction, as our
784     // dhtml engine can cope with it. Lars
785     //if ( isInline() ) return false;
786     switch( id() ) {
787         case ID_COL:
788         case ID_COLGROUP:
789         case ID_FRAMESET:
790         case ID_HEAD:
791         case ID_HTML:
792         case ID_TABLE:
793         case ID_TBODY:
794         case ID_TFOOT:
795         case ID_THEAD:
796         case ID_TR:
797             return false;
798         default:
799             break;
800     }
801
802     removeChildren();
803
804     TextImpl *t = new TextImpl( docPtr(), text );
805     int ec = 0;
806     appendChild( t, ec );
807     if ( !ec )
808         return true;
809     return false;
810 }
811
812 bool HTMLElementImpl::setOuterText( const DOMString &text )
813 {
814     // following the IE specs.
815     if( endTag[id()] == FORBIDDEN )
816         return false;
817     switch( id() ) {
818         case ID_COL:
819         case ID_COLGROUP:
820         case ID_FRAMESET:
821         case ID_HEAD:
822         case ID_HTML:
823         case ID_TABLE:
824         case ID_TBODY:
825         case ID_TFOOT:
826         case ID_THEAD:
827         case ID_TR:
828             return false;
829         default:
830             break;
831     }
832
833     NodeBaseImpl *parent = static_cast<NodeBaseImpl *>(parentNode());
834
835     if (!parent) {
836         return false;
837     }
838
839     TextImpl *t = new TextImpl( docPtr(), text );
840     int ec = 0;
841     parent->replaceChild(t, this, ec);
842
843     if ( ec )
844         return false;
845
846     // is previous node a text node? if so, merge into it
847     NodeImpl *prev = t->previousSibling();
848     if (prev && prev->isTextNode()) {
849         TextImpl *textPrev = static_cast<TextImpl *>(prev);
850         textPrev->appendData(t->data(), ec);
851         t->parentNode()->removeChild(t, ec);
852         t = textPrev;
853     }
854
855     if ( ec )
856         return false;
857
858     // is next node a text node? if so, merge it in
859     NodeImpl *next = t->nextSibling();
860     if (next && next->isTextNode()) {
861         TextImpl *textNext = static_cast<TextImpl *>(next);
862         t->appendData(textNext->data(), ec);
863         textNext->parentNode()->removeChild(textNext, ec);
864     }
865
866     if ( ec )
867         return false;
868
869     return true;
870 }
871
872
873 DOMString HTMLElementImpl::namespaceURI() const
874 {
875     // For HTML documents, we treat HTML elements as having no namespace. But for XML documents
876     // the elements have the namespace defined in the XHTML spec
877     if (getDocument()->isHTMLDocument())
878         return DOMString();
879     else
880         return XHTML_NAMESPACE;
881 }
882
883 void HTMLElementImpl::addHTMLAlignment(HTMLAttributeImpl* attr)
884 {
885     //qDebug("alignment is %s", alignment.string().latin1() );
886     // vertical alignment with respect to the current baseline of the text
887     // right or left means floating images
888     int propfloat = -1;
889     int propvalign = -1;
890     const AtomicString& alignment = attr->value();
891     if ( strcasecmp( alignment, "absmiddle" ) == 0 ) {
892         propvalign = CSS_VAL_MIDDLE;
893     } else if ( strcasecmp( alignment, "absbottom" ) == 0 ) {
894         propvalign = CSS_VAL_BOTTOM;
895     } else if ( strcasecmp( alignment, "left" ) == 0 ) {
896         propfloat = CSS_VAL_LEFT;
897         propvalign = CSS_VAL_TOP;
898     } else if ( strcasecmp( alignment, "right" ) == 0 ) {
899         propfloat = CSS_VAL_RIGHT;
900         propvalign = CSS_VAL_TOP;
901     } else if ( strcasecmp( alignment, "top" ) == 0 ) {
902         propvalign = CSS_VAL_TOP;
903     } else if ( strcasecmp( alignment, "middle" ) == 0 ) {
904         propvalign = CSS_VAL__KHTML_BASELINE_MIDDLE;
905     } else if ( strcasecmp( alignment, "center" ) == 0 ) {
906         propvalign = CSS_VAL_MIDDLE;
907     } else if ( strcasecmp( alignment, "bottom" ) == 0 ) {
908         propvalign = CSS_VAL_BASELINE;
909     } else if ( strcasecmp ( alignment, "texttop") == 0 ) {
910         propvalign = CSS_VAL_TEXT_TOP;
911     }
912     
913     if ( propfloat != -1 )
914         addCSSProperty( attr, CSS_PROP_FLOAT, propfloat );
915     if ( propvalign != -1 )
916         addCSSProperty( attr, CSS_PROP_VERTICAL_ALIGN, propvalign );
917 }
918
919 bool HTMLElementImpl::isFocusable() const
920 {
921     return isContentEditable() && !parent()->isContentEditable();
922 }
923
924 bool HTMLElementImpl::isContentEditable() const 
925 {
926     if (getDocument()->part() && getDocument()->part()->isContentEditable())
927         return true;
928
929     getDocument()->updateRendering();
930
931     if (!renderer()) {
932         if (parentNode())
933             return parentNode()->isContentEditable();
934         else
935             return false;
936     }
937     
938     return renderer()->style()->userModify() == READ_WRITE;
939 }
940
941 DOMString HTMLElementImpl::contentEditable() const 
942 {
943     getDocument()->updateRendering();
944
945     if (!renderer())
946         return "false";
947     
948     switch (renderer()->style()->userModify()) {
949         case READ_WRITE:
950             return "true";
951         case READ_ONLY:
952             return "false";
953         default:
954             return "inherit";
955     }
956     return "inherit";
957 }
958
959 void HTMLElementImpl::setContentEditable(HTMLAttributeImpl* attr) 
960 {
961     const AtomicString& enabled = attr->value();
962     if (enabled.isEmpty() || strcasecmp(enabled, "true") == 0)
963         addCSSProperty(attr, CSS_PROP__KHTML_USER_MODIFY, CSS_VAL_READ_WRITE);
964     else if (strcasecmp(enabled, "false") == 0)
965         addCSSProperty(attr, CSS_PROP__KHTML_USER_MODIFY, CSS_VAL_READ_ONLY);
966     else if (strcasecmp(enabled, "inherit") == 0)
967         addCSSProperty(attr, CSS_PROP__KHTML_USER_MODIFY, CSS_VAL_INHERIT);
968 }
969
970 void HTMLElementImpl::setContentEditable(const DOMString &enabled) {
971     if (enabled == "inherit") {
972         int exceptionCode;
973         removeAttribute(ATTR_CONTENTEDITABLE, exceptionCode);
974     }
975     else
976         setAttribute(ATTR_CONTENTEDITABLE, enabled.isEmpty() ? "true" : enabled);
977 }
978
979 void HTMLElementImpl::click()
980 {
981     int x = 0;
982     int y = 0;
983     if (renderer()) {
984         renderer()->absolutePosition(x,y);
985         x += renderer()->width() / 2;
986         y += renderer()->height() / 2;
987     }
988     // always send click
989     QMouseEvent evt(QEvent::MouseButtonRelease, QPoint(x,y), Qt::LeftButton, 0);
990     dispatchMouseEvent(&evt, EventImpl::KHTML_CLICK_EVENT);
991 }
992
993 DOMString HTMLElementImpl::toString() const
994 {
995     if (!hasChildNodes()) {
996         DOMString result = openTagStartToString();
997         result += ">";
998
999         if (endTag[id()] == REQUIRED) {
1000             result += "</";
1001             result += tagName();
1002             result += ">";
1003         }
1004
1005         return result;
1006     }
1007
1008     return ElementImpl::toString();
1009 }
1010
1011 // -------------------------------------------------------------------------
1012 HTMLGenericElementImpl::HTMLGenericElementImpl(DocumentPtr *doc, ushort i)
1013     : HTMLElementImpl(doc)
1014 {
1015     _id = i;
1016 }
1017
1018 HTMLGenericElementImpl::~HTMLGenericElementImpl()
1019 {
1020 }
1021