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