e1bdd197982f90d55ee1842a85c03c8b25787328
[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) 2004 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 "editing/visible_text.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_textimpl.h"
50 #include "xml/dom2_eventsimpl.h"
51 #include "editing/markup.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->setNode(0);
218         m_inlineStyleDecl->setParent(0);
219         m_inlineStyleDecl->deref();
220     }
221 }
222
223 AttributeImpl* HTMLElementImpl::createAttribute(NodeImpl::Id id, DOMStringImpl* value)
224 {
225     return new HTMLAttributeImpl(id, value);
226 }
227
228 bool HTMLElementImpl::isInline() const
229 {
230     if (renderer())
231         return ElementImpl::isInline();
232     
233     switch(id()) {
234         case ID_A:
235         case ID_FONT:
236         case ID_TT:
237         case ID_U:
238         case ID_B:
239         case ID_I:
240         case ID_S:
241         case ID_STRIKE:
242         case ID_BIG:
243         case ID_SMALL:
244     
245             // %phrase
246         case ID_EM:
247         case ID_STRONG:
248         case ID_DFN:
249         case ID_CODE:
250         case ID_SAMP:
251         case ID_KBD:
252         case ID_VAR:
253         case ID_CITE:
254         case ID_ABBR:
255         case ID_ACRONYM:
256     
257             // %special
258         case ID_SUB:
259         case ID_SUP:
260         case ID_SPAN:
261         case ID_NOBR:
262         case ID_WBR:
263             return true;
264             
265         default:
266             return ElementImpl::isInline();
267     }
268 }
269
270 void HTMLElementImpl::createInlineStyleDecl()
271 {
272     m_inlineStyleDecl = new CSSMutableStyleDeclarationImpl;
273     m_inlineStyleDecl->ref();
274     m_inlineStyleDecl->setParent(getDocument()->elementSheet());
275     m_inlineStyleDecl->setNode(this);
276     m_inlineStyleDecl->setStrictParsing(!getDocument()->inCompatMode());
277 }
278
279 NodeImpl *HTMLElementImpl::cloneNode(bool deep)
280 {
281     HTMLElementImpl *n = static_cast<HTMLElementImpl *>(ElementImpl::cloneNode(deep));
282     if (n && m_inlineStyleDecl) {
283         *n->getInlineStyleDecl() = *m_inlineStyleDecl;
284     }
285     return n;
286 }
287
288 void HTMLElementImpl::attributeChanged(AttributeImpl* attr, bool preserveDecls)
289 {
290     HTMLAttributeImpl* htmlAttr = static_cast<HTMLAttributeImpl*>(attr);
291     if (htmlAttr->decl() && !preserveDecls) {
292         htmlAttr->setDecl(0);
293         setChanged();
294         if (namedAttrMap)
295             static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->declRemoved();
296     }
297
298     bool checkDecl = true;
299     MappedAttributeEntry entry;
300     bool needToParse = mapToEntry(attr->id(), entry);
301     if (preserveDecls) {
302         if (htmlAttr->decl()) {
303             setChanged();
304             if (namedAttrMap)
305                 static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->declAdded();
306             checkDecl = false;
307         }
308     }
309     else if (!attr->isNull() && entry != eNone) {
310         CSSMappedAttributeDeclarationImpl* decl = getMappedAttributeDecl(entry, attr);
311         if (decl) {
312             htmlAttr->setDecl(decl);
313             setChanged();
314             if (namedAttrMap)
315                 static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->declAdded();
316             checkDecl = false;
317         } else
318             needToParse = true;
319     }
320
321     if (needToParse)
322         parseHTMLAttribute(htmlAttr);
323     
324     if (checkDecl && htmlAttr->decl()) {
325         // Add the decl to the table in the appropriate spot.
326         setMappedAttributeDecl(entry, attr, htmlAttr->decl());
327         htmlAttr->decl()->setMappedState(entry, attr->id(), attr->value());
328         htmlAttr->decl()->setParent(0);
329         htmlAttr->decl()->setNode(0);
330         if (namedAttrMap)
331             static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->declAdded();
332     }
333 }
334
335 bool HTMLElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
336 {
337     switch (attr)
338     {
339         case ATTR_ALIGN:
340         case ATTR_CONTENTEDITABLE:
341         case ATTR_DIR:
342             result = eUniversal;
343             return false;
344         default:
345             break;
346     }
347     
348     result = eNone;
349     return true;
350 }
351     
352 void HTMLElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
353 {
354     DOMString indexstring;
355     switch (attr->id())
356     {
357     case ATTR_ALIGN:
358         if (strcasecmp(attr->value(), "middle" ) == 0)
359             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, "center");
360         else
361             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, attr->value());
362         break;
363 // the core attributes...
364     case ATTR_ID:
365         // unique id
366         setHasID(!attr->isNull());
367         if (namedAttrMap) {
368             if (attr->isNull())
369                 namedAttrMap->setID(nullAtom);
370             else if (getDocument()->inCompatMode() && !attr->value().implementation()->isLower())
371                 namedAttrMap->setID(AtomicString(attr->value().domString().lower()));
372             else
373                 namedAttrMap->setID(attr->value());
374         }
375         setChanged();
376         break;
377     case ATTR_CLASS:
378         // class
379         setHasClass(!attr->isNull());
380         if (namedAttrMap) static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->parseClassAttribute(attr->value());
381         setChanged();
382         break;
383     case ATTR_CONTENTEDITABLE:
384         setContentEditable(attr);
385         break;
386     case ATTR_STYLE:
387         // ### we need to remove old style info in case there was any!
388         // ### the inline sheet ay contain more than 1 property!
389         // stylesheet info
390         setHasStyle();
391         getInlineStyleDecl()->parseProperty(attr->value());
392         setChanged();
393         break;
394     case ATTR_TABINDEX:
395         indexstring=getAttribute(ATTR_TABINDEX);
396         if (indexstring.length())
397             setTabIndex(indexstring.toInt());
398         break;
399 // i18n attributes
400     case ATTR_LANG:
401         break;
402     case ATTR_DIR:
403         addCSSProperty(attr, CSS_PROP_DIRECTION, attr->value());
404         addCSSProperty(attr, CSS_PROP_UNICODE_BIDI, CSS_VAL_EMBED);
405         break;
406 // standard events
407     case ATTR_ONCLICK:
408         setHTMLEventListener(EventImpl::KHTML_CLICK_EVENT,
409             getDocument()->createHTMLEventListener(attr->value().string()));
410         break;
411     case ATTR_ONCONTEXTMENU:
412         setHTMLEventListener(EventImpl::CONTEXTMENU_EVENT,
413             getDocument()->createHTMLEventListener(attr->value().string()));
414         break;
415     case ATTR_ONDBLCLICK:
416         setHTMLEventListener(EventImpl::KHTML_DBLCLICK_EVENT,
417             getDocument()->createHTMLEventListener(attr->value().string()));
418         break;
419     case ATTR_ONMOUSEDOWN:
420         setHTMLEventListener(EventImpl::MOUSEDOWN_EVENT,
421             getDocument()->createHTMLEventListener(attr->value().string()));
422         break;
423     case ATTR_ONMOUSEMOVE:
424         setHTMLEventListener(EventImpl::MOUSEMOVE_EVENT,
425             getDocument()->createHTMLEventListener(attr->value().string()));
426         break;
427     case ATTR_ONMOUSEOUT:
428         setHTMLEventListener(EventImpl::MOUSEOUT_EVENT,
429             getDocument()->createHTMLEventListener(attr->value().string()));
430         break;
431     case ATTR_ONMOUSEOVER:
432         setHTMLEventListener(EventImpl::MOUSEOVER_EVENT,
433             getDocument()->createHTMLEventListener(attr->value().string()));
434         break;
435     case ATTR_ONMOUSEUP:
436         setHTMLEventListener(EventImpl::MOUSEUP_EVENT,
437             getDocument()->createHTMLEventListener(attr->value().string()));
438         break;
439     case ATTR_ONFOCUS:
440         setHTMLEventListener(EventImpl::DOMFOCUSIN_EVENT,
441             getDocument()->createHTMLEventListener(attr->value().string()));
442         break;
443     case ATTR_ONKEYDOWN:
444         setHTMLEventListener(EventImpl::KEYDOWN_EVENT,
445             getDocument()->createHTMLEventListener(attr->value().string()));
446         break;
447     case ATTR_ONKEYPRESS:
448         setHTMLEventListener(EventImpl::KEYPRESS_EVENT,
449             getDocument()->createHTMLEventListener(attr->value().string()));
450         break;
451     case ATTR_ONKEYUP:
452         setHTMLEventListener(EventImpl::KEYUP_EVENT,
453             getDocument()->createHTMLEventListener(attr->value().string()));
454         break;
455     case ATTR_ONSCROLL:
456         setHTMLEventListener(EventImpl::SCROLL_EVENT,
457             getDocument()->createHTMLEventListener(attr->value().string()));
458         break;
459     case ATTR_ONBEFORECUT:
460         setHTMLEventListener(EventImpl::BEFORECUT_EVENT,
461                              getDocument()->createHTMLEventListener(attr->value().string()));
462         break;
463     case ATTR_ONCUT:
464         setHTMLEventListener(EventImpl::CUT_EVENT,
465                              getDocument()->createHTMLEventListener(attr->value().string()));
466         break;
467     case ATTR_ONBEFORECOPY:
468         setHTMLEventListener(EventImpl::BEFORECOPY_EVENT,
469                              getDocument()->createHTMLEventListener(attr->value().string()));
470         break;
471     case ATTR_ONCOPY:
472         setHTMLEventListener(EventImpl::COPY_EVENT,
473                              getDocument()->createHTMLEventListener(attr->value().string()));
474         break;
475     case ATTR_ONBEFOREPASTE:
476         setHTMLEventListener(EventImpl::BEFOREPASTE_EVENT,
477                              getDocument()->createHTMLEventListener(attr->value().string()));
478         break;
479     case ATTR_ONPASTE:
480         setHTMLEventListener(EventImpl::PASTE_EVENT,
481                              getDocument()->createHTMLEventListener(attr->value().string()));
482         break;        
483     case ATTR_ONDRAGENTER:
484         setHTMLEventListener(EventImpl::DRAGENTER_EVENT,
485                              getDocument()->createHTMLEventListener(attr->value().string()));
486         break;
487     case ATTR_ONDRAGOVER:
488         setHTMLEventListener(EventImpl::DRAGOVER_EVENT,
489                              getDocument()->createHTMLEventListener(attr->value().string()));
490         break;
491     case ATTR_ONDRAGLEAVE:
492         setHTMLEventListener(EventImpl::DRAGLEAVE_EVENT,
493                              getDocument()->createHTMLEventListener(attr->value().string()));
494         break;
495     case ATTR_ONDROP:
496         setHTMLEventListener(EventImpl::DROP_EVENT,
497                              getDocument()->createHTMLEventListener(attr->value().string()));
498         break;
499     case ATTR_ONDRAGSTART:
500         setHTMLEventListener(EventImpl::DRAGSTART_EVENT,
501                              getDocument()->createHTMLEventListener(attr->value().string()));
502         break;
503     case ATTR_ONDRAG:
504         setHTMLEventListener(EventImpl::DRAG_EVENT,
505                              getDocument()->createHTMLEventListener(attr->value().string()));
506         break;
507     case ATTR_ONDRAGEND:
508         setHTMLEventListener(EventImpl::DRAGEND_EVENT,
509                              getDocument()->createHTMLEventListener(attr->value().string()));
510         break;
511     case ATTR_ONSELECTSTART:
512         setHTMLEventListener(EventImpl::SELECTSTART_EVENT,
513                              getDocument()->createHTMLEventListener(attr->value().string()));
514         break;
515         // other misc attributes
516     default:
517 #ifdef UNSUPPORTED_ATTR
518         kdDebug(6030) << "UATTR: <" << this->nodeName().string() << "> ["
519                       << attr->name().string() << "]=[" << attr->value().string() << "]" << endl;
520 #endif
521         break;
522     }
523 }
524
525 void HTMLElementImpl::createAttributeMap() const
526 {
527     namedAttrMap = new HTMLNamedAttrMapImpl(const_cast<HTMLElementImpl*>(this));
528     namedAttrMap->ref();
529 }
530
531 CSSMutableStyleDeclarationImpl* HTMLElementImpl::getInlineStyleDecl()
532 {
533     if (!m_inlineStyleDecl)
534         createInlineStyleDecl();
535     return m_inlineStyleDecl;
536 }
537
538 CSSMutableStyleDeclarationImpl* HTMLElementImpl::additionalAttributeStyleDecl()
539 {
540     return 0;
541 }
542
543 const AtomicStringList* HTMLElementImpl::getClassList() const
544 {
545     return namedAttrMap ? static_cast<HTMLNamedAttrMapImpl*>(namedAttrMap)->getClassList() : 0;
546 }
547
548 static inline bool isHexDigit( const QChar &c ) {
549     return ( c >= '0' && c <= '9' ) ||
550            ( c >= 'a' && c <= 'f' ) ||
551            ( c >= 'A' && c <= 'F' );
552 }
553
554 static inline int toHex( const QChar &c ) {
555     return ( (c >= '0' && c <= '9')
556              ? (c.unicode() - '0')
557              : ( ( c >= 'a' && c <= 'f' )
558                  ? (c.unicode() - 'a' + 10)
559                  : ( ( c >= 'A' && c <= 'F' )
560                      ? (c.unicode() - 'A' + 10)
561                      : -1 ) ) );
562 }
563
564 void HTMLElementImpl::addCSSProperty(HTMLAttributeImpl* attr, int id, const DOMString &value)
565 {
566     if (!attr->decl()) createMappedDecl(attr);
567     attr->decl()->setProperty(id, value, false);
568 }
569
570 void HTMLElementImpl::addCSSProperty(HTMLAttributeImpl* attr, int id, int value)
571 {
572     if (!attr->decl()) createMappedDecl(attr);
573     attr->decl()->setProperty(id, value, false);
574 }
575
576 void HTMLElementImpl::addCSSStringProperty(HTMLAttributeImpl* attr, int id, const DOMString &value, CSSPrimitiveValue::UnitTypes type)
577 {
578     if (!attr->decl()) createMappedDecl(attr);
579     attr->decl()->setStringProperty(id, value, type, false);
580 }
581
582 void HTMLElementImpl::addCSSImageProperty(HTMLAttributeImpl* attr, int id, const DOMString &URL)
583 {
584     if (!attr->decl()) createMappedDecl(attr);
585     attr->decl()->setImageProperty(id, URL, false);
586 }
587
588 void HTMLElementImpl::addCSSLength(HTMLAttributeImpl* attr, int id, const DOMString &value)
589 {
590     // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
591     // length unit and make the appropriate parsed value.
592     if (!attr->decl()) createMappedDecl(attr);
593
594     // strip attribute garbage..
595     DOMStringImpl* v = value.implementation();
596     if ( v ) {
597         unsigned int l = 0;
598         
599         while ( l < v->l && v->s[l].unicode() <= ' ') l++;
600         
601         for ( ;l < v->l; l++ ) {
602             char cc = v->s[l].latin1();
603             if ( cc > '9' || ( cc < '0' && cc != '*' && cc != '%' && cc != '.') )
604                 break;
605         }
606         if ( l != v->l ) {
607             attr->decl()->setLengthProperty(id, DOMString( v->s, l ), false);
608             return;
609         }
610     }
611     
612     attr->decl()->setLengthProperty(id, value, false);
613 }
614
615 /* color parsing that tries to match as close as possible IE 6. */
616 void HTMLElementImpl::addHTMLColor(HTMLAttributeImpl* attr, int id, const DOMString &c)
617 {
618     // this is the only case no color gets applied in IE.
619     if ( !c.length() )
620         return;
621
622     if (!attr->decl()) createMappedDecl(attr);
623     
624     if (attr->decl()->setProperty(id, c, false) )
625         return;
626     
627     QString color = c.string();
628     // not something that fits the specs.
629     
630     // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value
631     // out of everyhting you put in. The algorithm is experimentally determined, but seems to work for all test cases I have.
632     
633     // the length of the color value is rounded up to the next
634     // multiple of 3. each part of the rgb triple then gets one third
635     // of the length.
636     //
637     // Each triplet is parsed byte by byte, mapping
638     // each number to a hex value (0-9a-fA-F to their values
639     // everything else to 0).
640     //
641     // The highest non zero digit in all triplets is remembered, and
642     // used as a normalization point to normalize to values between 0
643     // and 255.
644     
645     if ( color.lower() != "transparent" ) {
646         if ( color[0] == '#' )
647             color.remove( 0,  1 );
648         int basicLength = (color.length() + 2) / 3;
649         if ( basicLength > 1 ) {
650             // IE ignores colors with three digits or less
651             //      qDebug("trying to fix up color '%s'. basicLength=%d, length=%d",
652             //             color.latin1(), basicLength, color.length() );
653             int colors[3] = { 0, 0, 0 };
654             int component = 0;
655             int pos = 0;
656             int maxDigit = basicLength-1;
657             while ( component < 3 ) {
658                 // search forward for digits in the string
659                 int numDigits = 0;
660                 while ( pos < (int)color.length() && numDigits < basicLength ) {
661                     int hex = toHex( color[pos] );
662                     colors[component] = (colors[component] << 4);
663                     if ( hex > 0 ) {
664                         colors[component] += hex;
665                         maxDigit = QMIN( maxDigit, numDigits );
666                     }
667                     numDigits++;
668                     pos++;
669                 }
670                 while ( numDigits++ < basicLength )
671                     colors[component] <<= 4;
672                 component++;
673             }
674             maxDigit = basicLength - maxDigit;
675             //      qDebug("color is %x %x %x, maxDigit=%d",  colors[0], colors[1], colors[2], maxDigit );
676             
677             // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits
678             maxDigit -= 2;
679             colors[0] >>= 4*maxDigit;
680             colors[1] >>= 4*maxDigit;
681             colors[2] >>= 4*maxDigit;
682             //      qDebug("normalized color is %x %x %x",  colors[0], colors[1], colors[2] );
683             //  assert( colors[0] < 0x100 && colors[1] < 0x100 && colors[2] < 0x100 );
684             
685             color.sprintf("#%02x%02x%02x", colors[0], colors[1], colors[2] );
686             //      qDebug( "trying to add fixed color string '%s'", color.latin1() );
687             if ( attr->decl()->setProperty(id, DOMString(color), false) )
688                 return;
689         }
690     }
691     attr->decl()->setProperty(id, CSS_VAL_BLACK, false);
692 }
693
694 void HTMLElementImpl::createMappedDecl(HTMLAttributeImpl* attr)
695 {
696     CSSMappedAttributeDeclarationImpl* decl = new CSSMappedAttributeDeclarationImpl(0);
697     attr->setDecl(decl);
698     decl->setParent(getDocument()->elementSheet());
699     decl->setNode(this);
700     decl->setStrictParsing(false); // Mapped attributes are just always quirky.
701 }
702
703 DOMString HTMLElementImpl::innerHTML() const
704 {
705     return createMarkup(this, ChildrenOnly);
706 }
707
708 DOMString HTMLElementImpl::outerHTML() const
709 {
710     return createMarkup(this);
711 }
712
713 DOMString HTMLElementImpl::innerText() const
714 {
715     // We need to update layout, since plainText uses line boxes in the render tree.
716     getDocument()->updateLayout();
717     return plainText(Range(const_cast<HTMLElementImpl *>(this), 0,
718         const_cast<HTMLElementImpl *>(this), childNodeCount()));
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 in WinIE, and we want to match that.
728     return innerText();
729 }
730
731 DocumentFragmentImpl *HTMLElementImpl::createContextualFragment(const DOMString &html)
732 {
733     // the following is in accordance with the definition as used by IE
734     if( endTag[id()] == FORBIDDEN )
735         return NULL;
736     // IE disallows innerHTML on inline elements. I don't see why we should have this restriction, as our
737     // dhtml engine can cope with it. Lars
738     //if ( isInline() ) return false;
739     switch( id() ) {
740         case ID_COL:
741         case ID_COLGROUP:
742         case ID_FRAMESET:
743         case ID_HEAD:
744         case ID_STYLE:
745         case ID_TITLE:
746             return NULL;
747         default:
748             break;
749     }
750     if ( !getDocument()->isHTMLDocument() )
751         return NULL;
752
753     DocumentFragmentImpl *fragment = new DocumentFragmentImpl( docPtr() );
754     fragment->ref();
755     {
756         HTMLTokenizer tok(docPtr(), fragment);
757         tok.write( html.string(), true );
758         tok.finish();
759     }
760
761     // Exceptions are ignored because none ought to happen here.
762     int ignoredExceptionCode;
763
764     // we need to pop <html> and <body> elements and remove <head> to
765     // accomadate folks passing complete HTML documents to make the
766     // child of an element.
767
768     NodeImpl *nextNode;
769     for (NodeImpl *node = fragment->firstChild(); node != NULL; node = nextNode) {
770         nextNode = node->nextSibling();
771         node->ref();
772         if (node->id() == ID_HTML || node->id() == ID_BODY) {
773             NodeImpl *firstChild = node->firstChild();
774             if (firstChild != NULL) {
775                 nextNode = firstChild;
776             }
777             NodeImpl *nextChild;
778             for (NodeImpl *child = firstChild; child != NULL; child = nextChild) {
779                 nextChild = child->nextSibling();
780                 child->ref();
781                 node->removeChild(child, ignoredExceptionCode);
782                 fragment->insertBefore(child, node, ignoredExceptionCode);
783                 child->deref();
784             }
785             fragment->removeChild(node, ignoredExceptionCode);
786         } else if (node->id() == ID_HEAD) {
787             fragment->removeChild(node, ignoredExceptionCode);
788         }
789         // Important to do this deref after removeChild, because if the only thing
790         // keeping a node around is a parent that is non-0, removeChild will not
791         // delete the node. This works fine in JavaScript because there's always
792         // a ref of the node, but here in C++ we need to do it explicitly.
793         node->deref();
794     }
795
796     // Trick to get the fragment back to the floating state, with 0
797     // refs but not destroyed.
798     fragment->setParent(this);
799     fragment->deref();
800     fragment->setParent(0);
801
802     return fragment;
803 }
804
805 bool HTMLElementImpl::setInnerHTML( const DOMString &html )
806 {
807     DocumentFragmentImpl *fragment = createContextualFragment( html );
808     if (fragment == NULL) {
809         return false;
810     }
811
812     removeChildren();
813     int ec = 0;
814     appendChild( fragment, ec );
815     return !ec;
816 }
817
818 bool HTMLElementImpl::setOuterHTML( const DOMString &html )
819 {
820     DocumentFragmentImpl *fragment = createContextualFragment( html );
821     if (fragment == NULL) {
822         return false;
823     }
824     
825     int ec = 0;
826     parentNode()->replaceChild(fragment, this, ec);
827     return !ec;
828 }
829
830
831 bool HTMLElementImpl::setInnerText( const DOMString &text )
832 {
833     // following the IE specs.
834     if( endTag[id()] == FORBIDDEN )
835         return false;
836     // IE disallows innerText on inline elements. I don't see why we should have this restriction, as our
837     // dhtml engine can cope with it. Lars
838     //if ( isInline() ) return false;
839     switch( id() ) {
840         case ID_COL:
841         case ID_COLGROUP:
842         case ID_FRAMESET:
843         case ID_HEAD:
844         case ID_HTML:
845         case ID_TABLE:
846         case ID_TBODY:
847         case ID_TFOOT:
848         case ID_THEAD:
849         case ID_TR:
850             return false;
851         default:
852             break;
853     }
854
855     removeChildren();
856
857     TextImpl *t = new TextImpl( docPtr(), text );
858     int ec = 0;
859     appendChild( t, ec );
860     if ( !ec )
861         return true;
862     return false;
863 }
864
865 bool HTMLElementImpl::setOuterText( const DOMString &text )
866 {
867     // following the IE specs.
868     if( endTag[id()] == FORBIDDEN )
869         return false;
870     switch( id() ) {
871         case ID_COL:
872         case ID_COLGROUP:
873         case ID_FRAMESET:
874         case ID_HEAD:
875         case ID_HTML:
876         case ID_TABLE:
877         case ID_TBODY:
878         case ID_TFOOT:
879         case ID_THEAD:
880         case ID_TR:
881             return false;
882         default:
883             break;
884     }
885
886     NodeBaseImpl *parent = static_cast<NodeBaseImpl *>(parentNode());
887
888     if (!parent) {
889         return false;
890     }
891
892     TextImpl *t = new TextImpl( docPtr(), text );
893     int ec = 0;
894     parent->replaceChild(t, this, ec);
895
896     if ( ec )
897         return false;
898
899     // is previous node a text node? if so, merge into it
900     NodeImpl *prev = t->previousSibling();
901     if (prev && prev->isTextNode()) {
902         TextImpl *textPrev = static_cast<TextImpl *>(prev);
903         textPrev->appendData(t->data(), ec);
904         t->parentNode()->removeChild(t, ec);
905         t = textPrev;
906     }
907
908     if ( ec )
909         return false;
910
911     // is next node a text node? if so, merge it in
912     NodeImpl *next = t->nextSibling();
913     if (next && next->isTextNode()) {
914         TextImpl *textNext = static_cast<TextImpl *>(next);
915         t->appendData(textNext->data(), ec);
916         textNext->parentNode()->removeChild(textNext, ec);
917     }
918
919     if ( ec )
920         return false;
921
922     return true;
923 }
924
925
926 DOMString HTMLElementImpl::namespaceURI() const
927 {
928     // For HTML documents, we treat HTML elements as having no namespace. But for XML documents
929     // the elements have the namespace defined in the XHTML spec
930     if (getDocument()->isHTMLDocument())
931         return DOMString();
932     else
933         return XHTML_NAMESPACE;
934 }
935
936 void HTMLElementImpl::addHTMLAlignment(HTMLAttributeImpl* attr)
937 {
938     //qDebug("alignment is %s", alignment.string().latin1() );
939     // vertical alignment with respect to the current baseline of the text
940     // right or left means floating images
941     int propfloat = -1;
942     int propvalign = -1;
943     const AtomicString& alignment = attr->value();
944     if ( strcasecmp( alignment, "absmiddle" ) == 0 ) {
945         propvalign = CSS_VAL_MIDDLE;
946     } else if ( strcasecmp( alignment, "absbottom" ) == 0 ) {
947         propvalign = CSS_VAL_BOTTOM;
948     } else if ( strcasecmp( alignment, "left" ) == 0 ) {
949         propfloat = CSS_VAL_LEFT;
950         propvalign = CSS_VAL_TOP;
951     } else if ( strcasecmp( alignment, "right" ) == 0 ) {
952         propfloat = CSS_VAL_RIGHT;
953         propvalign = CSS_VAL_TOP;
954     } else if ( strcasecmp( alignment, "top" ) == 0 ) {
955         propvalign = CSS_VAL_TOP;
956     } else if ( strcasecmp( alignment, "middle" ) == 0 ) {
957         propvalign = CSS_VAL__KHTML_BASELINE_MIDDLE;
958     } else if ( strcasecmp( alignment, "center" ) == 0 ) {
959         propvalign = CSS_VAL_MIDDLE;
960     } else if ( strcasecmp( alignment, "bottom" ) == 0 ) {
961         propvalign = CSS_VAL_BASELINE;
962     } else if ( strcasecmp ( alignment, "texttop") == 0 ) {
963         propvalign = CSS_VAL_TEXT_TOP;
964     }
965     
966     if ( propfloat != -1 )
967         addCSSProperty( attr, CSS_PROP_FLOAT, propfloat );
968     if ( propvalign != -1 )
969         addCSSProperty( attr, CSS_PROP_VERTICAL_ALIGN, propvalign );
970 }
971
972 bool HTMLElementImpl::isFocusable() const
973 {
974     return isContentEditable() && parent() && !parent()->isContentEditable();
975 }
976
977 bool HTMLElementImpl::isContentEditable() const 
978 {
979     if (getDocument()->part() && getDocument()->part()->isContentEditable())
980         return true;
981
982     getDocument()->updateRendering();
983
984     if (!renderer()) {
985         if (parentNode())
986             return parentNode()->isContentEditable();
987         else
988             return false;
989     }
990     
991     return renderer()->style()->userModify() == READ_WRITE;
992 }
993
994 DOMString HTMLElementImpl::contentEditable() const 
995 {
996     getDocument()->updateRendering();
997
998     if (!renderer())
999         return "false";
1000     
1001     switch (renderer()->style()->userModify()) {
1002         case READ_WRITE:
1003             return "true";
1004         case READ_ONLY:
1005             return "false";
1006         default:
1007             return "inherit";
1008     }
1009     return "inherit";
1010 }
1011
1012 void HTMLElementImpl::setContentEditable(HTMLAttributeImpl* attr) 
1013 {
1014     KHTMLPart *part = getDocument()->part();
1015     const AtomicString& enabled = attr->value();
1016     if (enabled.isEmpty() || strcasecmp(enabled, "true") == 0) {
1017         addCSSProperty(attr, CSS_PROP__KHTML_USER_MODIFY, CSS_VAL_READ_WRITE);
1018         if (part)
1019             part->applyEditingStyleToElement(this);    
1020     }
1021     else if (strcasecmp(enabled, "false") == 0) {
1022         addCSSProperty(attr, CSS_PROP__KHTML_USER_MODIFY, CSS_VAL_READ_ONLY);
1023         if (part)
1024             part->removeEditingStyleFromElement(this);    
1025     }
1026     else if (strcasecmp(enabled, "inherit") == 0) {
1027         addCSSProperty(attr, CSS_PROP__KHTML_USER_MODIFY, CSS_VAL_INHERIT);
1028         if (part)
1029             part->removeEditingStyleFromElement(this);    
1030     }
1031 }
1032
1033 void HTMLElementImpl::setContentEditable(const DOMString &enabled) {
1034     if (enabled == "inherit") {
1035         int exceptionCode;
1036         removeAttribute(ATTR_CONTENTEDITABLE, exceptionCode);
1037     }
1038     else
1039         setAttribute(ATTR_CONTENTEDITABLE, enabled.isEmpty() ? "true" : enabled);
1040 }
1041
1042 void HTMLElementImpl::click()
1043 {
1044     int x = 0;
1045     int y = 0;
1046     if (renderer())
1047         renderer()->absolutePosition(x,y);
1048     // always send click
1049     QMouseEvent evt(QEvent::MouseButtonRelease, QPoint(x,y), Qt::LeftButton, 0);
1050     dispatchMouseEvent(&evt, EventImpl::KHTML_CLICK_EVENT);
1051 }
1052
1053 DOMString HTMLElementImpl::toString() const
1054 {
1055     if (!hasChildNodes()) {
1056         DOMString result = openTagStartToString();
1057         result += ">";
1058
1059         if (endTag[id()] == REQUIRED) {
1060             result += "</";
1061             result += tagName();
1062             result += ">";
1063         }
1064
1065         return result;
1066     }
1067
1068     return ElementImpl::toString();
1069 }
1070
1071 // -------------------------------------------------------------------------
1072 HTMLGenericElementImpl::HTMLGenericElementImpl(DocumentPtr *doc, ushort i)
1073     : HTMLElementImpl(doc)
1074 {
1075     _id = i;
1076 }
1077
1078 HTMLGenericElementImpl::~HTMLGenericElementImpl()
1079 {
1080 }
1081