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