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