Reviewed by Darin.
[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     case ATTR_ONBEFORECUT:
451         setHTMLEventListener(EventImpl::BEFORECUT_EVENT,
452                              getDocument()->createHTMLEventListener(attr->value().string()));
453         break;
454     case ATTR_ONCUT:
455         setHTMLEventListener(EventImpl::CUT_EVENT,
456                              getDocument()->createHTMLEventListener(attr->value().string()));
457         break;
458     case ATTR_ONBEFORECOPY:
459         setHTMLEventListener(EventImpl::BEFORECOPY_EVENT,
460                              getDocument()->createHTMLEventListener(attr->value().string()));
461         break;
462     case ATTR_ONCOPY:
463         setHTMLEventListener(EventImpl::COPY_EVENT,
464                              getDocument()->createHTMLEventListener(attr->value().string()));
465         break;
466     case ATTR_ONBEFOREPASTE:
467         setHTMLEventListener(EventImpl::BEFOREPASTE_EVENT,
468                              getDocument()->createHTMLEventListener(attr->value().string()));
469         break;
470     case ATTR_ONPASTE:
471         setHTMLEventListener(EventImpl::PASTE_EVENT,
472                              getDocument()->createHTMLEventListener(attr->value().string()));
473         break;        
474     case ATTR_ONDRAGENTER:
475         setHTMLEventListener(EventImpl::DRAGENTER_EVENT,
476                              getDocument()->createHTMLEventListener(attr->value().string()));
477         break;
478     case ATTR_ONDRAGOVER:
479         setHTMLEventListener(EventImpl::DRAGOVER_EVENT,
480                              getDocument()->createHTMLEventListener(attr->value().string()));
481         break;
482     case ATTR_ONDRAGLEAVE:
483         setHTMLEventListener(EventImpl::DRAGLEAVE_EVENT,
484                              getDocument()->createHTMLEventListener(attr->value().string()));
485         break;
486     case ATTR_ONDROP:
487         setHTMLEventListener(EventImpl::DROP_EVENT,
488                              getDocument()->createHTMLEventListener(attr->value().string()));
489         break;
490     case ATTR_ONDRAGSTART:
491         setHTMLEventListener(EventImpl::DRAGSTART_EVENT,
492                              getDocument()->createHTMLEventListener(attr->value().string()));
493         break;
494     case ATTR_ONDRAG:
495         setHTMLEventListener(EventImpl::DRAG_EVENT,
496                              getDocument()->createHTMLEventListener(attr->value().string()));
497         break;
498     case ATTR_ONDRAGEND:
499         setHTMLEventListener(EventImpl::DRAGEND_EVENT,
500                              getDocument()->createHTMLEventListener(attr->value().string()));
501         break;
502     case ATTR_ONSELECTSTART:
503         setHTMLEventListener(EventImpl::SELECTSTART_EVENT,
504                              getDocument()->createHTMLEventListener(attr->value().string()));
505         break;
506         // other misc attributes
507     default:
508 #ifdef UNSUPPORTED_ATTR
509         kdDebug(6030) << "UATTR: <" << this->nodeName().string() << "> ["
510                       << attr->name().string() << "]=[" << attr->value().string() << "]" << endl;
511 #endif
512         break;
513     }
514 }
515
516 void HTMLElementImpl::createAttributeMap() const
517 {
518     namedAttrMap = new HTMLNamedAttrMapImpl(const_cast<HTMLElementImpl*>(this));
519     namedAttrMap->ref();
520 }
521
522 CSSStyleDeclarationImpl* HTMLElementImpl::getInlineStyleDecl()
523 {
524     if (!m_inlineStyleDecl)
525         createInlineStyleDecl();
526     return m_inlineStyleDecl;
527 }
528
529 CSSStyleDeclarationImpl* HTMLElementImpl::additionalAttributeStyleDecl()
530 {
531     return 0;
532 }
533
534 const AtomicStringList* HTMLElementImpl::getClassList() const
535 {
536     return namedAttrMap ? static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->getClassList() : 0;
537 }
538
539 static inline bool isHexDigit( const QChar &c ) {
540     return ( c >= '0' && c <= '9' ) ||
541            ( c >= 'a' && c <= 'f' ) ||
542            ( c >= 'A' && c <= 'F' );
543 }
544
545 static inline int toHex( const QChar &c ) {
546     return ( (c >= '0' && c <= '9')
547              ? (c.unicode() - '0')
548              : ( ( c >= 'a' && c <= 'f' )
549                  ? (c.unicode() - 'a' + 10)
550                  : ( ( c >= 'A' && c <= 'F' )
551                      ? (c.unicode() - 'A' + 10)
552                      : -1 ) ) );
553 }
554
555 void HTMLElementImpl::addCSSProperty(HTMLAttributeImpl* attr, int id, const DOMString &value)
556 {
557     if (!attr->decl()) createMappedDecl(attr);
558     attr->decl()->setProperty(id, value, false);
559 }
560
561 void HTMLElementImpl::addCSSProperty(HTMLAttributeImpl* attr, int id, int value)
562 {
563     if (!attr->decl()) createMappedDecl(attr);
564     attr->decl()->setProperty(id, value, false);
565 }
566
567 void HTMLElementImpl::addCSSStringProperty(HTMLAttributeImpl* attr, int id, const DOMString &value, CSSPrimitiveValue::UnitTypes type)
568 {
569     if (!attr->decl()) createMappedDecl(attr);
570     attr->decl()->setStringProperty(id, value, type, false);
571 }
572
573 void HTMLElementImpl::addCSSImageProperty(HTMLAttributeImpl* attr, int id, const DOMString &URL)
574 {
575     if (!attr->decl()) createMappedDecl(attr);
576     attr->decl()->setImageProperty(id, URL, false);
577 }
578
579 void HTMLElementImpl::addCSSLength(HTMLAttributeImpl* attr, int id, const DOMString &value)
580 {
581     // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
582     // length unit and make the appropriate parsed value.
583     if (!attr->decl()) createMappedDecl(attr);
584
585     // strip attribute garbage..
586     DOMStringImpl* v = value.implementation();
587     if ( v ) {
588         unsigned int l = 0;
589         
590         while ( l < v->l && v->s[l].unicode() <= ' ') l++;
591         
592         for ( ;l < v->l; l++ ) {
593             char cc = v->s[l].latin1();
594             if ( cc > '9' || ( cc < '0' && cc != '*' && cc != '%' && cc != '.') )
595                 break;
596         }
597         if ( l != v->l ) {
598             attr->decl()->setLengthProperty(id, DOMString( v->s, l ), false);
599             return;
600         }
601     }
602     
603     attr->decl()->setLengthProperty(id, value, false);
604 }
605
606 /* color parsing that tries to match as close as possible IE 6. */
607 void HTMLElementImpl::addHTMLColor(HTMLAttributeImpl* attr, int id, const DOMString &c)
608 {
609     // this is the only case no color gets applied in IE.
610     if ( !c.length() )
611         return;
612
613     if (!attr->decl()) createMappedDecl(attr);
614     
615     if (attr->decl()->setProperty(id, c, false) )
616         return;
617     
618     QString color = c.string();
619     // not something that fits the specs.
620     
621     // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value
622     // out of everyhting you put in. The algorithm is experimentally determined, but seems to work for all test cases I have.
623     
624     // the length of the color value is rounded up to the next
625     // multiple of 3. each part of the rgb triple then gets one third
626     // of the length.
627     //
628     // Each triplet is parsed byte by byte, mapping
629     // each number to a hex value (0-9a-fA-F to their values
630     // everything else to 0).
631     //
632     // The highest non zero digit in all triplets is remembered, and
633     // used as a normalization point to normalize to values between 0
634     // and 255.
635     
636     if ( color.lower() != "transparent" ) {
637         if ( color[0] == '#' )
638             color.remove( 0,  1 );
639         int basicLength = (color.length() + 2) / 3;
640         if ( basicLength > 1 ) {
641             // IE ignores colors with three digits or less
642             //      qDebug("trying to fix up color '%s'. basicLength=%d, length=%d",
643             //             color.latin1(), basicLength, color.length() );
644             int colors[3] = { 0, 0, 0 };
645             int component = 0;
646             int pos = 0;
647             int maxDigit = basicLength-1;
648             while ( component < 3 ) {
649                 // search forward for digits in the string
650                 int numDigits = 0;
651                 while ( pos < (int)color.length() && numDigits < basicLength ) {
652                     int hex = toHex( color[pos] );
653                     colors[component] = (colors[component] << 4);
654                     if ( hex > 0 ) {
655                         colors[component] += hex;
656                         maxDigit = QMIN( maxDigit, numDigits );
657                     }
658                     numDigits++;
659                     pos++;
660                 }
661                 while ( numDigits++ < basicLength )
662                     colors[component] <<= 4;
663                 component++;
664             }
665             maxDigit = basicLength - maxDigit;
666             //      qDebug("color is %x %x %x, maxDigit=%d",  colors[0], colors[1], colors[2], maxDigit );
667             
668             // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits
669             maxDigit -= 2;
670             colors[0] >>= 4*maxDigit;
671             colors[1] >>= 4*maxDigit;
672             colors[2] >>= 4*maxDigit;
673             //      qDebug("normalized color is %x %x %x",  colors[0], colors[1], colors[2] );
674             //  assert( colors[0] < 0x100 && colors[1] < 0x100 && colors[2] < 0x100 );
675             
676             color.sprintf("#%02x%02x%02x", colors[0], colors[1], colors[2] );
677             //      qDebug( "trying to add fixed color string '%s'", color.latin1() );
678             if ( attr->decl()->setProperty(id, DOMString(color), false) )
679                 return;
680         }
681     }
682     attr->decl()->setProperty(id, CSS_VAL_BLACK, false);
683 }
684
685 void HTMLElementImpl::createMappedDecl(HTMLAttributeImpl* attr)
686 {
687     CSSMappedAttributeDeclarationImpl* decl = new CSSMappedAttributeDeclarationImpl(0);
688     attr->setDecl(decl);
689     decl->setParent(getDocument()->elementSheet());
690     decl->setNode(this);
691     decl->setStrictParsing(false); // Mapped attributes are just always quirky.
692 }
693
694 DOMString HTMLElementImpl::innerHTML() const
695 {
696     return toHTML();
697 }
698
699 DOMString HTMLElementImpl::outerHTML() const
700 {
701     return recursive_toHTML(true);
702 }
703
704 DOMString HTMLElementImpl::innerText() const
705 {
706     Node startContainer(const_cast<HTMLElementImpl *>(this));
707     long startOffset = 0;
708     Node endContainer(const_cast<HTMLElementImpl *>(this));
709
710     long endOffset = 0;
711
712     for (NodeImpl *child = firstChild(); child; child = child->nextSibling()) {
713         endOffset++;
714     }
715
716     Range innerRange(startContainer, startOffset, endContainer, endOffset);
717
718     return plainText(innerRange);
719 }
720
721 DOMString HTMLElementImpl::outerText() const
722 {
723     // Getting outerText is the same as getting innerText, only
724     // setting is different. You would think this should get the plain
725     // text for the outer range, but this is wrong, <br> for instance
726     // would return different values for inner and outer text by such
727     // a rule, but it doesn't.
728     return innerText();
729 }
730
731
732 DocumentFragmentImpl *HTMLElementImpl::createContextualFragment( const DOMString &html )
733 {
734     // the following is in accordance with the definition as used by IE
735     if( endTag[id()] == FORBIDDEN )
736         return NULL;
737     // IE disallows innerHTML on inline elements. I don't see why we should have this restriction, as our
738     // dhtml engine can cope with it. Lars
739     //if ( isInline() ) return false;
740     switch( id() ) {
741         case ID_COL:
742         case ID_COLGROUP:
743         case ID_FRAMESET:
744         case ID_HEAD:
745         case ID_STYLE:
746         case ID_TABLE:
747         case ID_TBODY:
748         case ID_TFOOT:
749         case ID_THEAD:
750         case ID_TITLE:
751             return NULL;
752         default:
753             break;
754     }
755     if ( !getDocument()->isHTMLDocument() )
756         return NULL;
757
758     DocumentFragmentImpl *fragment = new DocumentFragmentImpl( docPtr() );
759     fragment->ref();
760     {
761         HTMLTokenizer tok( docPtr(), fragment );
762         tok.begin();
763         tok.write( html.string(), true );
764         tok.end();
765     }
766
767     // Exceptions are ignored because none ought to happen here.
768     int ignoredExceptionCode;
769
770     // we need to pop <html> and <body> elements and remove <head> to
771     // accomadate folks passing complete HTML documents to make the
772     // child of an element.
773
774     NodeImpl *nextNode;
775     for (NodeImpl *node = fragment->firstChild(); node != NULL; node = nextNode) {
776         nextNode = node->nextSibling();
777         if (node->id() == ID_HTML || node->id() == ID_BODY) {
778             NodeImpl *firstChild = node->firstChild();
779             if (firstChild != NULL) {
780                 nextNode = firstChild;
781             }
782             NodeImpl *nextChild;
783             for (NodeImpl *child = firstChild; child != NULL; child = nextChild) {
784                 nextChild = child->nextSibling();
785                 child->ref();
786                 node->removeChild(child, ignoredExceptionCode);
787                 fragment->insertBefore(child, node, ignoredExceptionCode);
788                 child->deref();
789             }
790             fragment->removeChild(node, ignoredExceptionCode);
791             // FIXME: Does node leak here?
792         } else if (node->id() == ID_HEAD) {
793             fragment->removeChild(node, ignoredExceptionCode);
794             // FIXME: Does node leak here?
795         }
796     }
797
798     // Trick to get the fragment back to the floating state, with 0
799     // refs but not destroyed.
800     fragment->setParent(this);
801     fragment->deref();
802     fragment->setParent(0);
803
804     return fragment;
805 }
806
807 bool HTMLElementImpl::setInnerHTML( const DOMString &html )
808 {
809     DocumentFragmentImpl *fragment = createContextualFragment( html );
810     if (fragment == NULL) {
811         return false;
812     }
813
814     removeChildren();
815     int ec = 0;
816     appendChild( fragment, ec );
817     delete fragment;
818     return !ec;
819 }
820
821 bool HTMLElementImpl::setOuterHTML( const DOMString &html )
822 {
823     DocumentFragmentImpl *fragment = createContextualFragment( html );
824     if (fragment == NULL) {
825         return false;
826     }
827     
828     int ec = 0;
829     parentNode()->replaceChild(fragment, this, ec);
830     return !ec;
831 }
832
833
834 bool HTMLElementImpl::setInnerText( const DOMString &text )
835 {
836     // following the IE specs.
837     if( endTag[id()] == FORBIDDEN )
838         return false;
839     // IE disallows innerText on inline elements. I don't see why we should have this restriction, as our
840     // dhtml engine can cope with it. Lars
841     //if ( isInline() ) return false;
842     switch( id() ) {
843         case ID_COL:
844         case ID_COLGROUP:
845         case ID_FRAMESET:
846         case ID_HEAD:
847         case ID_HTML:
848         case ID_TABLE:
849         case ID_TBODY:
850         case ID_TFOOT:
851         case ID_THEAD:
852         case ID_TR:
853             return false;
854         default:
855             break;
856     }
857
858     removeChildren();
859
860     TextImpl *t = new TextImpl( docPtr(), text );
861     int ec = 0;
862     appendChild( t, ec );
863     if ( !ec )
864         return true;
865     return false;
866 }
867
868 bool HTMLElementImpl::setOuterText( const DOMString &text )
869 {
870     // following the IE specs.
871     if( endTag[id()] == FORBIDDEN )
872         return false;
873     switch( id() ) {
874         case ID_COL:
875         case ID_COLGROUP:
876         case ID_FRAMESET:
877         case ID_HEAD:
878         case ID_HTML:
879         case ID_TABLE:
880         case ID_TBODY:
881         case ID_TFOOT:
882         case ID_THEAD:
883         case ID_TR:
884             return false;
885         default:
886             break;
887     }
888
889     NodeBaseImpl *parent = static_cast<NodeBaseImpl *>(parentNode());
890
891     if (!parent) {
892         return false;
893     }
894
895     TextImpl *t = new TextImpl( docPtr(), text );
896     int ec = 0;
897     parent->replaceChild(t, this, ec);
898
899     if ( ec )
900         return false;
901
902     // is previous node a text node? if so, merge into it
903     NodeImpl *prev = t->previousSibling();
904     if (prev && prev->isTextNode()) {
905         TextImpl *textPrev = static_cast<TextImpl *>(prev);
906         textPrev->appendData(t->data(), ec);
907         t->parentNode()->removeChild(t, ec);
908         t = textPrev;
909     }
910
911     if ( ec )
912         return false;
913
914     // is next node a text node? if so, merge it in
915     NodeImpl *next = t->nextSibling();
916     if (next && next->isTextNode()) {
917         TextImpl *textNext = static_cast<TextImpl *>(next);
918         t->appendData(textNext->data(), ec);
919         textNext->parentNode()->removeChild(textNext, ec);
920     }
921
922     if ( ec )
923         return false;
924
925     return true;
926 }
927
928
929 DOMString HTMLElementImpl::namespaceURI() const
930 {
931     // For HTML documents, we treat HTML elements as having no namespace. But for XML documents
932     // the elements have the namespace defined in the XHTML spec
933     if (getDocument()->isHTMLDocument())
934         return DOMString();
935     else
936         return XHTML_NAMESPACE;
937 }
938
939 void HTMLElementImpl::addHTMLAlignment(HTMLAttributeImpl* attr)
940 {
941     //qDebug("alignment is %s", alignment.string().latin1() );
942     // vertical alignment with respect to the current baseline of the text
943     // right or left means floating images
944     int propfloat = -1;
945     int propvalign = -1;
946     const AtomicString& alignment = attr->value();
947     if ( strcasecmp( alignment, "absmiddle" ) == 0 ) {
948         propvalign = CSS_VAL_MIDDLE;
949     } else if ( strcasecmp( alignment, "absbottom" ) == 0 ) {
950         propvalign = CSS_VAL_BOTTOM;
951     } else if ( strcasecmp( alignment, "left" ) == 0 ) {
952         propfloat = CSS_VAL_LEFT;
953         propvalign = CSS_VAL_TOP;
954     } else if ( strcasecmp( alignment, "right" ) == 0 ) {
955         propfloat = CSS_VAL_RIGHT;
956         propvalign = CSS_VAL_TOP;
957     } else if ( strcasecmp( alignment, "top" ) == 0 ) {
958         propvalign = CSS_VAL_TOP;
959     } else if ( strcasecmp( alignment, "middle" ) == 0 ) {
960         propvalign = CSS_VAL__KHTML_BASELINE_MIDDLE;
961     } else if ( strcasecmp( alignment, "center" ) == 0 ) {
962         propvalign = CSS_VAL_MIDDLE;
963     } else if ( strcasecmp( alignment, "bottom" ) == 0 ) {
964         propvalign = CSS_VAL_BASELINE;
965     } else if ( strcasecmp ( alignment, "texttop") == 0 ) {
966         propvalign = CSS_VAL_TEXT_TOP;
967     }
968     
969     if ( propfloat != -1 )
970         addCSSProperty( attr, CSS_PROP_FLOAT, propfloat );
971     if ( propvalign != -1 )
972         addCSSProperty( attr, CSS_PROP_VERTICAL_ALIGN, propvalign );
973 }
974
975 bool HTMLElementImpl::isFocusable() const
976 {
977     return isContentEditable() && parent() && !parent()->isContentEditable();
978 }
979
980 bool HTMLElementImpl::isContentEditable() const 
981 {
982     if (getDocument()->part() && getDocument()->part()->isContentEditable())
983         return true;
984
985     getDocument()->updateRendering();
986
987     if (!renderer()) {
988         if (parentNode())
989             return parentNode()->isContentEditable();
990         else
991             return false;
992     }
993     
994     return renderer()->style()->userModify() == READ_WRITE;
995 }
996
997 DOMString HTMLElementImpl::contentEditable() const 
998 {
999     getDocument()->updateRendering();
1000
1001     if (!renderer())
1002         return "false";
1003     
1004     switch (renderer()->style()->userModify()) {
1005         case READ_WRITE:
1006             return "true";
1007         case READ_ONLY:
1008             return "false";
1009         default:
1010             return "inherit";
1011     }
1012     return "inherit";
1013 }
1014
1015 void HTMLElementImpl::setContentEditable(HTMLAttributeImpl* attr) 
1016 {
1017     const AtomicString& enabled = attr->value();
1018     if (enabled.isEmpty() || strcasecmp(enabled, "true") == 0)
1019         addCSSProperty(attr, CSS_PROP__KHTML_USER_MODIFY, CSS_VAL_READ_WRITE);
1020     else if (strcasecmp(enabled, "false") == 0)
1021         addCSSProperty(attr, CSS_PROP__KHTML_USER_MODIFY, CSS_VAL_READ_ONLY);
1022     else if (strcasecmp(enabled, "inherit") == 0)
1023         addCSSProperty(attr, CSS_PROP__KHTML_USER_MODIFY, CSS_VAL_INHERIT);
1024 }
1025
1026 void HTMLElementImpl::setContentEditable(const DOMString &enabled) {
1027     if (enabled == "inherit") {
1028         int exceptionCode;
1029         removeAttribute(ATTR_CONTENTEDITABLE, exceptionCode);
1030     }
1031     else
1032         setAttribute(ATTR_CONTENTEDITABLE, enabled.isEmpty() ? "true" : enabled);
1033 }
1034
1035 void HTMLElementImpl::click()
1036 {
1037     int x = 0;
1038     int y = 0;
1039     if (renderer()) {
1040         renderer()->absolutePosition(x,y);
1041         x += renderer()->width() / 2;
1042         y += renderer()->height() / 2;
1043     }
1044     // always send click
1045     QMouseEvent evt(QEvent::MouseButtonRelease, QPoint(x,y), Qt::LeftButton, 0);
1046     dispatchMouseEvent(&evt, EventImpl::KHTML_CLICK_EVENT);
1047 }
1048
1049 DOMString HTMLElementImpl::toString() const
1050 {
1051     if (!hasChildNodes()) {
1052         DOMString result = openTagStartToString();
1053         result += ">";
1054
1055         if (endTag[id()] == REQUIRED) {
1056             result += "</";
1057             result += tagName();
1058             result += ">";
1059         }
1060
1061         return result;
1062     }
1063
1064     return ElementImpl::toString();
1065 }
1066
1067 // -------------------------------------------------------------------------
1068 HTMLGenericElementImpl::HTMLGenericElementImpl(DocumentPtr *doc, ushort i)
1069     : HTMLElementImpl(doc)
1070 {
1071     _id = i;
1072 }
1073
1074 HTMLGenericElementImpl::~HTMLGenericElementImpl()
1075 {
1076 }
1077