Reviewed by John
[WebKit-https.git] / WebCore / khtml / css / css_valueimpl.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * (C) 1999-2003 Lars Knoll (knoll@kde.org)
5  * Copyright (C) 2002 Apple Computer, Inc.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB.  If not, write to
19  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #include "dom/css_value.h"
24 #include "dom/dom_exception.h"
25 #include "dom/dom_string.h"
26
27 #include "css/css_valueimpl.h"
28 #include "css/css_ruleimpl.h"
29 #include "css/css_stylesheetimpl.h"
30 #include "css/cssparser.h"
31 #include "css/cssproperties.h"
32 #include "css/cssvalues.h"
33 #include "css/cssstyleselector.h"
34
35 #include "xml/dom_stringimpl.h"
36 #include "xml/dom_docimpl.h"
37
38 #include "misc/loader.h"
39
40 #include "rendering/font.h"
41 #include "rendering/render_style.h"
42
43 #include <kdebug.h>
44 #include <qregexp.h>
45 #include <qpaintdevice.h>
46 #include <qpaintdevicemetrics.h>
47
48 // Hack for debugging purposes
49 extern DOM::DOMString getPropertyName(unsigned short id);
50
51 using khtml::FontDef;
52 using khtml::CSSStyleSelector;
53
54 using namespace DOM;
55
56 CSSStyleDeclarationImpl::CSSStyleDeclarationImpl(CSSRuleImpl *parent)
57     : StyleBaseImpl(parent)
58 {
59     m_lstValues = 0;
60     m_node = 0;
61 }
62
63 CSSStyleDeclarationImpl::CSSStyleDeclarationImpl(CSSRuleImpl *parent, QPtrList<CSSProperty> *lstValues)
64     : StyleBaseImpl(parent)
65 {
66     m_lstValues = lstValues;
67     m_node = 0;
68 }
69
70 CSSStyleDeclarationImpl&  CSSStyleDeclarationImpl::operator= (const CSSStyleDeclarationImpl& o)
71 {
72     // don't attach it to the same node, just leave the current m_node value
73     delete m_lstValues;
74     m_lstValues = 0;
75     if (o.m_lstValues) {
76         m_lstValues = new QPtrList<CSSProperty>;
77         m_lstValues->setAutoDelete( true );
78
79         QPtrListIterator<CSSProperty> lstValuesIt(*o.m_lstValues);
80         for (lstValuesIt.toFirst(); lstValuesIt.current(); ++lstValuesIt)
81             m_lstValues->append(new CSSProperty(*lstValuesIt.current()));
82     }
83
84     return *this;
85 }
86
87 CSSStyleDeclarationImpl::~CSSStyleDeclarationImpl()
88 {
89     delete m_lstValues;
90     // we don't use refcounting for m_node, to avoid cyclic references (see ElementImpl)
91 }
92
93 DOMString CSSStyleDeclarationImpl::getPropertyValue( int propertyID ) const
94 {
95     if(!m_lstValues) return DOMString();
96
97     CSSValueImpl* value = getPropertyCSSValue( propertyID );
98     if ( value )
99         return value->cssText();
100
101     // Shorthand and 4-values properties
102     switch ( propertyID ) {
103     case CSS_PROP_BACKGROUND_POSITION:
104     {
105         // ## Is this correct? The code in cssparser.cpp is confusing
106         const int properties[2] = { CSS_PROP_BACKGROUND_POSITION_X,
107                                     CSS_PROP_BACKGROUND_POSITION_Y };
108         return getShortHandValue( properties, 2 );
109     }
110     case CSS_PROP_BACKGROUND:
111     {
112         const int properties[5] = { CSS_PROP_BACKGROUND_IMAGE, CSS_PROP_BACKGROUND_REPEAT,
113                                     CSS_PROP_BACKGROUND_ATTACHMENT, CSS_PROP_BACKGROUND_POSITION,
114                                     CSS_PROP_BACKGROUND_COLOR };
115         return getShortHandValue( properties, 5 );
116     }
117     case CSS_PROP_BORDER:
118     {
119         const int properties[3] = { CSS_PROP_BORDER_WIDTH, CSS_PROP_BORDER_STYLE,
120                                     CSS_PROP_BORDER_COLOR };
121         return getShortHandValue( properties, 3 );
122     }
123     case CSS_PROP_BORDER_TOP:
124     {
125         const int properties[3] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_TOP_STYLE,
126                                     CSS_PROP_BORDER_TOP_COLOR};
127         return getShortHandValue( properties, 3 );
128     }
129     case CSS_PROP_BORDER_RIGHT:
130     {
131         const int properties[3] = { CSS_PROP_BORDER_RIGHT_WIDTH, CSS_PROP_BORDER_RIGHT_STYLE,
132                                     CSS_PROP_BORDER_RIGHT_COLOR};
133         return getShortHandValue( properties, 3 );
134     }
135     case CSS_PROP_BORDER_BOTTOM:
136     {
137         const int properties[3] = { CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_BOTTOM_STYLE,
138                                     CSS_PROP_BORDER_BOTTOM_COLOR};
139         return getShortHandValue( properties, 3 );
140     }
141     case CSS_PROP_BORDER_LEFT:
142     {
143         const int properties[3] = { CSS_PROP_BORDER_LEFT_WIDTH, CSS_PROP_BORDER_LEFT_STYLE,
144                                     CSS_PROP_BORDER_LEFT_COLOR};
145         return getShortHandValue( properties, 3 );
146     }
147     case CSS_PROP_OUTLINE:
148     {
149         const int properties[3] = { CSS_PROP_OUTLINE_WIDTH, CSS_PROP_OUTLINE_STYLE,
150                                     CSS_PROP_OUTLINE_COLOR };
151         return getShortHandValue( properties, 3 );
152     }
153     case CSS_PROP_BORDER_COLOR:
154     {
155         const int properties[4] = { CSS_PROP_BORDER_TOP_COLOR, CSS_PROP_BORDER_RIGHT_COLOR,
156                                     CSS_PROP_BORDER_BOTTOM_COLOR, CSS_PROP_BORDER_LEFT_COLOR };
157         return get4Values( properties );
158     }
159     case CSS_PROP_BORDER_WIDTH:
160     {
161         const int properties[4] = { CSS_PROP_BORDER_TOP_WIDTH, CSS_PROP_BORDER_RIGHT_WIDTH,
162                                     CSS_PROP_BORDER_BOTTOM_WIDTH, CSS_PROP_BORDER_LEFT_WIDTH };
163         return get4Values( properties );
164     }
165     case CSS_PROP_BORDER_STYLE:
166     {
167         const int properties[4] = { CSS_PROP_BORDER_TOP_STYLE, CSS_PROP_BORDER_RIGHT_STYLE,
168                                     CSS_PROP_BORDER_BOTTOM_STYLE, CSS_PROP_BORDER_LEFT_STYLE };
169         return get4Values( properties );
170     }
171     case CSS_PROP_MARGIN:
172     {
173         const int properties[4] = { CSS_PROP_MARGIN_TOP, CSS_PROP_MARGIN_RIGHT,
174                                     CSS_PROP_MARGIN_BOTTOM, CSS_PROP_MARGIN_LEFT };
175         return get4Values( properties );
176     }
177     case CSS_PROP_PADDING:
178     {
179         const int properties[4] = { CSS_PROP_PADDING_TOP, CSS_PROP_PADDING_RIGHT,
180                                     CSS_PROP_PADDING_BOTTOM, CSS_PROP_PADDING_LEFT };
181         return get4Values( properties );
182     }
183     case CSS_PROP_LIST_STYLE:
184     {
185         const int properties[3] = { CSS_PROP_LIST_STYLE_TYPE, CSS_PROP_LIST_STYLE_POSITION,
186                                     CSS_PROP_LIST_STYLE_IMAGE };
187         return getShortHandValue( properties, 3 );
188     }
189     }
190     //kdDebug() << k_funcinfo << "property not found:" << propertyID << endl;
191     return DOMString();
192 }
193
194 DOMString CSSStyleDeclarationImpl::get4Values( const int* properties ) const
195 {
196     DOMString res;
197     for ( int i = 0 ; i < 4 ; ++i ) {
198         CSSValueImpl* value = getPropertyCSSValue( properties[i] );
199         if ( !value ) { // apparently all 4 properties must be specified.
200             return DOMString();
201         }
202         if ( i > 0 )
203             res += " ";
204         res += value->cssText();
205     }
206     return res;
207 }
208
209 DOMString CSSStyleDeclarationImpl::getShortHandValue( const int* properties, int number ) const
210 {
211     DOMString res;
212     for ( int i = 0 ; i < number ; ++i ) {
213         CSSValueImpl* value = getPropertyCSSValue( properties[i] );
214         if ( value ) { // TODO provide default value if !value
215             if ( !res.isNull() )
216                 res += " ";
217             res += value->cssText();
218         }
219     }
220     return res;
221 }
222
223  CSSValueImpl *CSSStyleDeclarationImpl::getPropertyCSSValue( int propertyID ) const
224 {
225     if(!m_lstValues) return 0;
226
227     QPtrListIterator<CSSProperty> lstValuesIt(*m_lstValues);
228     CSSProperty *current;
229     for ( lstValuesIt.toLast(); (current = lstValuesIt.current()); --lstValuesIt )
230         if (current->m_id == propertyID)
231             return current->value();
232     return 0;
233 }
234
235 DOMString CSSStyleDeclarationImpl::removeProperty(int propertyID, bool notifyChanged)
236 {
237     if(!m_lstValues) return DOMString();
238     DOMString value;
239
240     QPtrListIterator<CSSProperty> lstValuesIt(*m_lstValues);
241      CSSProperty *current;
242      for ( lstValuesIt.toLast(); (current = lstValuesIt.current()); --lstValuesIt )
243          if (current->m_id == propertyID) {
244              value = current->value()->cssText();
245              m_lstValues->removeRef(current);
246              if (notifyChanged)
247                  setChanged();
248              break;
249         }
250
251     return value;
252 }
253
254 void CSSStyleDeclarationImpl::setChanged()
255 {
256     if (m_node) {
257         m_node->setChanged();
258         return;
259     }
260
261     // ### quick&dirty hack for KDE 3.0... make this MUCH better! (Dirk)
262     for (StyleBaseImpl* stylesheet = this; stylesheet; stylesheet = stylesheet->parent())
263         if (stylesheet->isCSSStyleSheet()) {
264             static_cast<CSSStyleSheetImpl*>(stylesheet)->doc()->updateStyleSelector();
265             break;
266         }
267 }
268
269 bool CSSStyleDeclarationImpl::getPropertyPriority( int propertyID ) const
270 {
271     if ( m_lstValues) {
272         QPtrListIterator<CSSProperty> lstValuesIt(*m_lstValues);
273         CSSProperty *current;
274         for ( lstValuesIt.toFirst(); (current = lstValuesIt.current()); ++lstValuesIt ) {
275             if( propertyID == current->m_id )
276                 return current->m_bImportant;
277         }
278     }
279     return false;
280 }
281
282 bool CSSStyleDeclarationImpl::setProperty(int id, const DOMString &value, bool important, bool notifyChanged)
283 {
284     if(!m_lstValues) {
285         m_lstValues = new QPtrList<CSSProperty>;
286         m_lstValues->setAutoDelete(true);
287     }
288     removeProperty(id);
289
290     CSSParser parser( strictParsing );
291     bool success = parser.parseValue(this, id, value, important);
292     if(!success)
293         kdDebug( 6080 ) << "CSSStyleDeclarationImpl::setProperty invalid property: [" << getPropertyName(id).string()
294                         << "] value: [" << value.string() << "]"<< endl;
295     else if (notifyChanged)
296         setChanged();
297     return success;
298 }
299
300 void CSSStyleDeclarationImpl::setProperty(int id, int value, bool important, bool notifyChanged)
301 {
302     if(!m_lstValues) {
303         m_lstValues = new QPtrList<CSSProperty>;
304         m_lstValues->setAutoDelete(true);
305     }
306     removeProperty(id);
307
308     CSSValueImpl * cssValue = new CSSPrimitiveValueImpl(value);
309     setParsedValue(id, cssValue, important, m_lstValues);
310     if (notifyChanged)
311         setChanged();
312 }
313
314 void CSSStyleDeclarationImpl::setStringProperty(int propertyId, const DOMString &value, CSSPrimitiveValue::UnitTypes type, bool important)
315 {
316     if (!m_lstValues) {
317         m_lstValues = new QPtrList<CSSProperty>;
318         m_lstValues->setAutoDelete(true);
319     }
320     removeProperty(propertyId);
321     setParsedValue(propertyId, new CSSPrimitiveValueImpl(value, type), important, m_lstValues);
322     setChanged();
323 }
324
325 void CSSStyleDeclarationImpl::setImageProperty(int propertyId, const DOMString &URL, bool important)
326 {
327     if (!m_lstValues) {
328         m_lstValues = new QPtrList<CSSProperty>;
329         m_lstValues->setAutoDelete(true);
330     }
331     removeProperty(propertyId);
332     setParsedValue(propertyId, new CSSImageValueImpl(URL, this), important, m_lstValues);
333     setChanged();
334 }
335
336 void CSSStyleDeclarationImpl::setProperty ( const DOMString &propertyString)
337 {
338     if(!m_lstValues) {
339         m_lstValues = new QPtrList<CSSProperty>;
340         m_lstValues->setAutoDelete( true );
341     }
342
343     CSSParser parser(strictParsing);
344     parser.parseDeclaration(this, propertyString);
345     setChanged();
346 }
347
348 void CSSStyleDeclarationImpl::setLengthProperty(int id, const DOM::DOMString &value, bool important, bool _multiLength )
349 {
350     bool parseMode = strictParsing;
351     strictParsing = false;
352     multiLength = _multiLength;
353     setProperty( id, value, important);
354     strictParsing = parseMode;
355     multiLength = false;
356 }
357
358 unsigned long CSSStyleDeclarationImpl::length() const
359 {
360     return m_lstValues ? m_lstValues->count() : 0;
361 }
362
363 DOMString CSSStyleDeclarationImpl::item( unsigned long /*index*/ )
364 {
365     // ###
366     //return m_lstValues->at(index);
367     return DOMString();
368 }
369
370 CSSRuleImpl *CSSStyleDeclarationImpl::parentRule() const
371 {
372     return (m_parent && m_parent->isRule() ) ?
373         static_cast<CSSRuleImpl *>(m_parent) : 0;
374 }
375
376 DOM::DOMString CSSStyleDeclarationImpl::cssText() const
377 {
378     DOMString result;
379     
380     if ( m_lstValues) {
381         QPtrListIterator<CSSProperty> lstValuesIt(*m_lstValues);
382         CSSProperty *current;
383         for ( lstValuesIt.toFirst(); (current = lstValuesIt.current()); ++lstValuesIt )
384             result += current->cssText();
385     }
386
387     return result;
388 }
389
390 void CSSStyleDeclarationImpl::setCssText(const DOM::DOMString& text)
391 {
392     if (m_lstValues)
393         m_lstValues->clear();
394     else {
395         m_lstValues = new QPtrList<CSSProperty>;
396         m_lstValues->setAutoDelete(true);
397     }
398
399     CSSParser parser(strictParsing);
400     parser.parseDeclaration(this, text);
401     setChanged();
402 }
403
404 bool CSSStyleDeclarationImpl::parseString( const DOMString &/*string*/, bool )
405 {
406     return false;
407     // ###
408 }
409
410 void CSSStyleDeclarationImpl::merge(CSSStyleDeclarationImpl *other, bool argOverridesOnConflict)
411 {
412     for (QPtrListIterator<CSSProperty> it(*(other->values())); it.current(); ++it) {
413         CSSProperty *property = it.current();
414         if (getPropertyCSSValue(property->id())) {
415             if (!argOverridesOnConflict)
416                 continue;
417             removeProperty(property->id());
418         }
419         if (!m_lstValues) {
420             m_lstValues = new QPtrList<CSSProperty>;
421             m_lstValues->setAutoDelete(true);
422         }
423         m_lstValues->append(new CSSProperty(*property));
424     }
425 }
426
427 void CSSStyleDeclarationImpl::diff(CSSStyleDeclarationImpl *style) const
428 {
429     if (!style)
430         return;
431
432     QValueList<int> properties;
433     for (QPtrListIterator<CSSProperty> it(*style->values()); it.current(); ++it) {
434         CSSProperty *property = it.current();
435         CSSValueImpl *value = getPropertyCSSValue(property->id());
436         if (value && value->cssText() == property->value()->cssText()) {
437             properties.append(property->id());
438         }
439     }
440     
441     for (QValueListIterator<int> it(properties.begin()); it != properties.end(); ++it)
442         style->removeProperty(*it);
443 }
444
445 // This is the list of properties we want to copy in the copyBlockProperties() function.
446 // It is the list of CSS properties that apply to specially to block-level elements.
447 static const int BlockProperties[] = {
448     CSS_PROP_ORPHANS,
449     CSS_PROP_OVERFLOW, // This can be also be applied to replaced elements
450     CSS_PROP_PAGE_BREAK_AFTER,
451     CSS_PROP_PAGE_BREAK_BEFORE,
452     CSS_PROP_PAGE_BREAK_INSIDE,
453     CSS_PROP_TEXT_ALIGN,
454     CSS_PROP_TEXT_INDENT,
455     CSS_PROP_WIDOWS
456 };
457
458 CSSStyleDeclarationImpl *CSSStyleDeclarationImpl::copyBlockProperties() const
459 {
460     return copyPropertiesInSet(BlockProperties, sizeof(BlockProperties) / sizeof(BlockProperties[0]));
461 }
462
463 CSSStyleDeclarationImpl *CSSStyleDeclarationImpl::copyPropertiesInSet(const int *set, unsigned length) const
464 {
465     QPtrList<CSSProperty> *list = new QPtrList<CSSProperty>;
466     list->setAutoDelete(true);
467     for (unsigned i = 0; i < length; i++) {
468         CSSValueImpl *value = getPropertyCSSValue(set[i]);
469         if (value) {
470             CSSProperty *property = new CSSProperty;
471             property->m_id = set[i];
472             property->setValue(value);
473             list->append(property);
474         }
475     }
476     return new CSSStyleDeclarationImpl(0, list);
477 }
478
479 // --------------------------------------------------------------------------------------
480
481 CSSValueImpl::CSSValueImpl()
482     : StyleBaseImpl()
483 {
484 }
485
486 CSSValueImpl::~CSSValueImpl()
487 {
488 }
489
490 unsigned short CSSInheritedValueImpl::cssValueType() const
491 {
492     return CSSValue::CSS_INHERIT;
493 }
494
495 DOM::DOMString CSSInheritedValueImpl::cssText() const
496 {
497     return DOMString("inherit");
498 }
499
500 unsigned short CSSInitialValueImpl::cssValueType() const
501
502     return CSSValue::CSS_INITIAL; 
503 }
504
505 DOM::DOMString CSSInitialValueImpl::cssText() const
506 {
507     return DOMString("initial");
508 }
509
510 // ----------------------------------------------------------------------------------------
511
512 CSSValueListImpl::CSSValueListImpl()
513     : CSSValueImpl()
514 {
515 }
516
517 CSSValueListImpl::~CSSValueListImpl()
518 {
519     CSSValueImpl *val = m_values.first();
520     while( val ) {
521         val->deref();
522         val = m_values.next();
523     }
524 }
525
526 unsigned short CSSValueListImpl::cssValueType() const
527 {
528     return CSSValue::CSS_VALUE_LIST;
529 }
530
531 void CSSValueListImpl::append(CSSValueImpl *val)
532 {
533     m_values.append(val);
534     val->ref();
535 }
536
537 DOM::DOMString CSSValueListImpl::cssText() const
538 {
539     DOMString result = "";
540
541     for (QPtrListIterator<CSSValueImpl> iterator(m_values); iterator.current(); ++iterator) {
542         if (result.length() != 0) {
543             result += ", ";
544         }
545         result += iterator.current()->cssText();
546     }
547     
548     return result;
549 }
550
551 // -------------------------------------------------------------------------------------
552
553 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl()
554     : CSSValueImpl()
555 {
556     m_type = 0;
557 }
558
559 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(int ident)
560     : CSSValueImpl()
561 {
562     m_value.ident = ident;
563     m_type = CSSPrimitiveValue::CSS_IDENT;
564 }
565
566 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(double num, CSSPrimitiveValue::UnitTypes type)
567 {
568     m_value.num = num;
569     m_type = type;
570 }
571
572 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(const DOMString &str, CSSPrimitiveValue::UnitTypes type)
573 {
574     m_value.string = str.implementation();
575     if(m_value.string) m_value.string->ref();
576     m_type = type;
577 }
578
579 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(const Counter &c)
580 {
581     m_value.counter = c.handle();
582     if (m_value.counter)
583         m_value.counter->ref();
584     m_type = CSSPrimitiveValue::CSS_COUNTER;
585 }
586
587 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl( RectImpl *r)
588 {
589     m_value.rect = r;
590     if (m_value.rect)
591         m_value.rect->ref();
592     m_type = CSSPrimitiveValue::CSS_RECT;
593 }
594
595 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl( DashboardRegionImpl *r)
596 {
597     m_value.region = r;
598     if (m_value.region)
599         m_value.region->ref();
600     m_type = CSSPrimitiveValue::CSS_DASHBOARD_REGION;
601 }
602
603 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(QRgb color)
604 {
605     m_value.rgbcolor = color;
606     m_type = CSSPrimitiveValue::CSS_RGBCOLOR;
607 }
608
609 CSSPrimitiveValueImpl::~CSSPrimitiveValueImpl()
610 {
611     cleanup();
612 }
613
614 void CSSPrimitiveValueImpl::cleanup()
615 {
616     switch(m_type) {
617     case CSSPrimitiveValue::CSS_STRING:
618     case CSSPrimitiveValue::CSS_URI:
619     case CSSPrimitiveValue::CSS_ATTR:
620         if(m_value.string) m_value.string->deref();
621         break;
622     case CSSPrimitiveValue::CSS_COUNTER:
623         m_value.counter->deref();
624         break;
625     case CSSPrimitiveValue::CSS_RECT:
626         m_value.rect->deref();
627     default:
628         break;
629     }
630
631     m_type = 0;
632 }
633
634 int CSSPrimitiveValueImpl::computeLength( khtml::RenderStyle *style, QPaintDeviceMetrics *devMetrics )
635 {
636     double result = computeLengthFloat( style, devMetrics );
637 #if APPLE_CHANGES
638     // This conversion is imprecise, often resulting in values of, e.g., 44.99998.  We
639     // need to go ahead and round if we're really close to the next integer value.
640     int intResult = (int)(result + (result < 0 ? -0.01 : +0.01));
641 #else
642     int intResult = (int)result;
643 #endif
644     return intResult;    
645 }
646
647 int CSSPrimitiveValueImpl::computeLength( khtml::RenderStyle *style, QPaintDeviceMetrics *devMetrics, 
648                                           double multiplier )
649 {
650     double result = multiplier * computeLengthFloat( style, devMetrics );
651 #if APPLE_CHANGES
652     // This conversion is imprecise, often resulting in values of, e.g., 44.99998.  We
653     // need to go ahead and round if we're really close to the next integer value.
654     int intResult = (int)(result + (result < 0 ? -0.01 : +0.01));
655 #else
656     int intResult = (int)result;
657 #endif
658     return intResult;    
659 }
660
661 double CSSPrimitiveValueImpl::computeLengthFloat( khtml::RenderStyle *style, QPaintDeviceMetrics *devMetrics,
662                                                   bool applyZoomFactor )
663 {
664     unsigned short type = primitiveType();
665
666     double dpiY = 72.; // fallback
667     if ( devMetrics )
668         dpiY = devMetrics->logicalDpiY();
669     if ( !khtml::printpainter && dpiY < 96 )
670         dpiY = 96.;
671
672     double factor = 1.;
673     switch(type)
674     {
675     case CSSPrimitiveValue::CSS_EMS:
676         factor = applyZoomFactor ?
677           style->htmlFont().getFontDef().computedSize :
678           style->htmlFont().getFontDef().specifiedSize;
679         break;
680     case CSSPrimitiveValue::CSS_EXS:
681         // FIXME: We have a bug right now where the zoom will be applied multiple times to EX units.
682         // We really need to compute EX using fontMetrics for the original specifiedSize and not use
683         // our actual constructed rendering font.
684         {
685         QFontMetrics fm = style->fontMetrics();
686 #if APPLE_CHANGES
687         factor = fm.xHeight();
688 #else
689         QRect b = fm.boundingRect('x');
690         factor = b.height();
691 #endif
692         break;
693         }
694     case CSSPrimitiveValue::CSS_PX:
695         break;
696     case CSSPrimitiveValue::CSS_CM:
697         factor = dpiY/2.54; //72dpi/(2.54 cm/in)
698         break;
699     case CSSPrimitiveValue::CSS_MM:
700         factor = dpiY/25.4;
701         break;
702     case CSSPrimitiveValue::CSS_IN:
703             factor = dpiY;
704         break;
705     case CSSPrimitiveValue::CSS_PT:
706             factor = dpiY/72.;
707         break;
708     case CSSPrimitiveValue::CSS_PC:
709         // 1 pc == 12 pt
710             factor = dpiY*12./72.;
711         break;
712     default:
713         return -1;
714     }
715
716     return getFloatValue(type)*factor;
717 }
718
719 void CSSPrimitiveValueImpl::setFloatValue( unsigned short unitType, double floatValue, int &exceptioncode )
720 {
721     exceptioncode = 0;
722     cleanup();
723     // ### check if property supports this type
724     if(m_type > CSSPrimitiveValue::CSS_DIMENSION) {
725         exceptioncode = CSSException::SYNTAX_ERR + CSSException::_EXCEPTION_OFFSET;
726         return;
727     }
728     //if(m_type > CSSPrimitiveValue::CSS_DIMENSION) throw DOMException(DOMException::INVALID_ACCESS_ERR);
729     m_value.num = floatValue;
730     m_type = unitType;
731 }
732
733 void CSSPrimitiveValueImpl::setStringValue( unsigned short stringType, const DOMString &stringValue, int &exceptioncode )
734 {
735     exceptioncode = 0;
736     cleanup();
737     //if(m_type < CSSPrimitiveValue::CSS_STRING) throw DOMException(DOMException::INVALID_ACCESS_ERR);
738     //if(m_type > CSSPrimitiveValue::CSS_ATTR) throw DOMException(DOMException::INVALID_ACCESS_ERR);
739     if(m_type < CSSPrimitiveValue::CSS_STRING || m_type >> CSSPrimitiveValue::CSS_ATTR) {
740         exceptioncode = CSSException::SYNTAX_ERR + CSSException::_EXCEPTION_OFFSET;
741         return;
742     }
743     if(stringType != CSSPrimitiveValue::CSS_IDENT)
744     {
745         m_value.string = stringValue.implementation();
746         m_value.string->ref();
747         m_type = stringType;
748     }
749     // ### parse ident
750 }
751
752 DOMString CSSPrimitiveValueImpl::getStringValue() const
753 {
754     switch (m_type) {
755         case CSSPrimitiveValue::CSS_STRING:
756         case CSSPrimitiveValue::CSS_ATTR:
757         case CSSPrimitiveValue::CSS_URI:
758             return m_value.string;
759         case CSSPrimitiveValue::CSS_IDENT:
760             return getValueName(m_value.ident);
761         default:
762             // FIXME: The CSS 2.1 spec says you should throw an exception here.
763             break;
764     }
765     
766     return DOMString();
767 }
768
769 unsigned short CSSPrimitiveValueImpl::cssValueType() const
770 {
771     return CSSValue::CSS_PRIMITIVE_VALUE;
772 }
773
774 bool CSSPrimitiveValueImpl::parseString( const DOMString &/*string*/, bool )
775 {
776     // ###
777     return false;
778 }
779
780 int CSSPrimitiveValueImpl::getIdent()
781 {
782     if(m_type != CSSPrimitiveValue::CSS_IDENT) return 0;
783     return m_value.ident;
784 }
785
786 DOM::DOMString CSSPrimitiveValueImpl::cssText() const
787 {
788     // ### return the original value instead of a generated one (e.g. color
789     // name if it was specified) - check what spec says about this
790     DOMString text;
791     switch ( m_type ) {
792         case CSSPrimitiveValue::CSS_UNKNOWN:
793             // ###
794             break;
795         case CSSPrimitiveValue::CSS_NUMBER:
796             text = DOMString(QString::number( (int)m_value.num ));
797             break;
798         case CSSPrimitiveValue::CSS_PERCENTAGE:
799             text = DOMString(QString::number( m_value.num ) + "%");
800             break;
801         case CSSPrimitiveValue::CSS_EMS:
802             text = DOMString(QString::number( m_value.num ) + "em");
803             break;
804         case CSSPrimitiveValue::CSS_EXS:
805             text = DOMString(QString::number( m_value.num ) + "ex");
806             break;
807         case CSSPrimitiveValue::CSS_PX:
808             text = DOMString(QString::number( m_value.num ) + "px");
809             break;
810         case CSSPrimitiveValue::CSS_CM:
811             text = DOMString(QString::number( m_value.num ) + "cm");
812             break;
813         case CSSPrimitiveValue::CSS_MM:
814             text = DOMString(QString::number( m_value.num ) + "mm");
815             break;
816         case CSSPrimitiveValue::CSS_IN:
817             text = DOMString(QString::number( m_value.num ) + "in");
818             break;
819         case CSSPrimitiveValue::CSS_PT:
820             text = DOMString(QString::number( m_value.num ) + "pt");
821             break;
822         case CSSPrimitiveValue::CSS_PC:
823             text = DOMString(QString::number( m_value.num ) + "pc");
824             break;
825         case CSSPrimitiveValue::CSS_DEG:
826             text = DOMString(QString::number( m_value.num ) + "deg");
827             break;
828         case CSSPrimitiveValue::CSS_RAD:
829             text = DOMString(QString::number( m_value.num ) + "rad");
830             break;
831         case CSSPrimitiveValue::CSS_GRAD:
832             text = DOMString(QString::number( m_value.num ) + "grad");
833             break;
834         case CSSPrimitiveValue::CSS_MS:
835             text = DOMString(QString::number( m_value.num ) + "ms");
836             break;
837         case CSSPrimitiveValue::CSS_S:
838             text = DOMString(QString::number( m_value.num ) + "s");
839             break;
840         case CSSPrimitiveValue::CSS_HZ:
841             text = DOMString(QString::number( m_value.num ) + "hz");
842             break;
843         case CSSPrimitiveValue::CSS_KHZ:
844             text = DOMString(QString::number( m_value.num ) + "khz");
845             break;
846         case CSSPrimitiveValue::CSS_DIMENSION:
847             // ###
848             break;
849         case CSSPrimitiveValue::CSS_STRING:
850             text = DOMString(m_value.string);
851             break;
852         case CSSPrimitiveValue::CSS_URI:
853             text  = "url(";
854             text += DOMString( m_value.string );
855             text += ")";
856             break;
857         case CSSPrimitiveValue::CSS_IDENT:
858             text = getValueName(m_value.ident);
859             break;
860         case CSSPrimitiveValue::CSS_ATTR:
861             // ###
862             break;
863         case CSSPrimitiveValue::CSS_COUNTER:
864             // ###
865             break;
866         case CSSPrimitiveValue::CSS_RECT: {
867             RectImpl* rectVal = getRectValue();
868             text = "rect(";
869             text += rectVal->top()->cssText() + " ";
870             text += rectVal->right()->cssText() + " ";
871             text += rectVal->bottom()->cssText() + " ";
872             text += rectVal->left()->cssText() + ")";
873             break;
874         }
875         case CSSPrimitiveValue::CSS_RGBCOLOR: {
876             QColor color(m_value.rgbcolor);
877             if (qAlpha(m_value.rgbcolor) < 0xFF)
878                 text = "rgba(";
879             else
880                 text = "rgb(";
881             text += QString::number(color.red()) + ", ";
882             text += QString::number(color.green()) + ", ";
883             text += QString::number(color.blue());
884             if (qAlpha(m_value.rgbcolor) < 0xFF)
885                 text += ", " + QString::number((float)qAlpha(m_value.rgbcolor) / 0xFF);
886             text += ")";
887             break;
888         }
889 #if APPLE_CHANGES        
890         case CSSPrimitiveValue::CSS_DASHBOARD_REGION: {
891             DashboardRegionImpl *region = getDashboardRegionValue();
892             while (region) {
893                 text = "dashboard-region(";
894                 text += region->m_label;
895                 if (region->m_isCircle){
896                     text += " circle ";
897                 }
898                 else if (region->m_isRectangle){
899                     text += " rectangle ";
900                 }
901                 else
902                     break;
903                 text += region->top()->cssText() + " ";
904                 text += region->right()->cssText() + " ";
905                 text += region->bottom()->cssText() + " ";
906                 text += region->left()->cssText();
907                 text += ")";
908                 region = region->m_next;
909             }
910             break;
911         }
912 #endif
913     }
914     return text;
915 }
916
917 // -----------------------------------------------------------------
918
919 RectImpl::RectImpl()
920 {
921     m_top = 0;
922     m_right = 0;
923     m_bottom = 0;
924     m_left = 0;
925 }
926
927 RectImpl::~RectImpl()
928 {
929     if (m_top) m_top->deref();
930     if (m_right) m_right->deref();
931     if (m_bottom) m_bottom->deref();
932     if (m_left) m_left->deref();
933 }
934
935 void RectImpl::setTop( CSSPrimitiveValueImpl *top )
936 {
937     if( top ) top->ref();
938     if ( m_top ) m_top->deref();
939     m_top = top;
940 }
941
942 void RectImpl::setRight( CSSPrimitiveValueImpl *right )
943 {
944     if( right ) right->ref();
945     if ( m_right ) m_right->deref();
946     m_right = right;
947 }
948
949 void RectImpl::setBottom( CSSPrimitiveValueImpl *bottom )
950 {
951     if( bottom ) bottom->ref();
952     if ( m_bottom ) m_bottom->deref();
953     m_bottom = bottom;
954 }
955
956 void RectImpl::setLeft( CSSPrimitiveValueImpl *left )
957 {
958     if( left ) left->ref();
959     if ( m_left ) m_left->deref();
960     m_left = left;
961 }
962
963 // -----------------------------------------------------------------
964
965 CSSImageValueImpl::CSSImageValueImpl(const DOMString &url, StyleBaseImpl *style)
966     : CSSPrimitiveValueImpl(url, CSSPrimitiveValue::CSS_URI), m_image(0), m_accessedImage(false)
967 {
968 }
969
970 CSSImageValueImpl::CSSImageValueImpl()
971     : CSSPrimitiveValueImpl(CSS_VAL_NONE), m_image(0), m_accessedImage(true)
972 {
973 }
974
975 CSSImageValueImpl::~CSSImageValueImpl()
976 {
977     if(m_image) m_image->deref(this);
978 }
979
980 khtml::CachedImage* CSSImageValueImpl::image(khtml::DocLoader* loader)
981 {
982     if (!m_accessedImage) {
983         m_accessedImage = true;
984
985         if (loader)
986             m_image = loader->requestImage(getStringValue());
987         else
988             m_image = khtml::Cache::requestImage(0, getStringValue());
989         
990         if(m_image) m_image->ref(this);
991     }
992     
993     return m_image;
994 }
995
996 // ------------------------------------------------------------------------
997
998 FontFamilyValueImpl::FontFamilyValueImpl( const QString &string)
999 : CSSPrimitiveValueImpl( DOMString(), CSSPrimitiveValue::CSS_STRING)
1000 {
1001     static const QRegExp parenReg(" \\(.*\\)$");
1002     static const QRegExp braceReg(" \\[.*\\]$");
1003
1004 #if APPLE_CHANGES
1005     parsedFontName = string;
1006     // a language tag is often added in braces at the end. Remove it.
1007     parsedFontName.replace(parenReg, "");
1008     // remove [Xft] qualifiers
1009     parsedFontName.replace(braceReg, "");
1010 #else
1011     const QString &available = KHTMLSettings::availableFamilies();
1012
1013     QString face = string.lower();
1014     // a languge tag is often added in braces at the end. Remove it.
1015     face = face.replace(parenReg, "");
1016     // remove [Xft] qualifiers
1017     face = face.replace(braceReg, "");
1018     //kdDebug(0) << "searching for face '" << face << "'" << endl;
1019
1020     int pos = available.find( face, 0, false );
1021     if( pos == -1 ) {
1022         QString str = face;
1023         int p = face.find(' ');
1024         // Arial Blk --> Arial
1025         // MS Sans Serif --> Sans Serif
1026         if ( p != -1 ) {
1027             if(p > 0 && (int)str.length() - p > p + 1)
1028                 str = str.mid( p+1 );
1029             else
1030                 str.truncate( p );
1031             pos = available.find( str, 0, false);
1032         }
1033     }
1034
1035     if ( pos != -1 ) {
1036         int pos1 = available.findRev( ',', pos ) + 1;
1037         pos = available.find( ',', pos );
1038         if ( pos == -1 )
1039             pos = available.length();
1040         parsedFontName = available.mid( pos1, pos - pos1 );
1041     }
1042 #endif // !APPLE_CHANGES
1043 }
1044
1045 DOM::DOMString FontFamilyValueImpl::cssText() const
1046 {
1047     return parsedFontName;
1048 }
1049
1050 FontValueImpl::FontValueImpl()
1051     : style(0), variant(0), weight(0), size(0), lineHeight(0), family(0)
1052 {
1053 }
1054
1055 FontValueImpl::~FontValueImpl()
1056 {
1057     delete style;
1058     delete variant;
1059     delete weight;
1060     delete size;
1061     delete lineHeight;
1062     delete family;
1063 }
1064
1065 DOMString FontValueImpl::cssText() const
1066 {
1067     // font variant weight size / line-height family 
1068
1069     DOMString result("");
1070
1071     if (style) {
1072         result += style->cssText();
1073     }
1074     if (variant) {
1075         if (result.length() > 0) {
1076             result += " ";
1077         }
1078         result += variant->cssText();
1079     }
1080     if (weight) {
1081         if (result.length() > 0) {
1082             result += " ";
1083         }
1084         result += weight->cssText();
1085     }
1086     if (size) {
1087         if (result.length() > 0) {
1088             result += " ";
1089         }
1090         result += size->cssText();
1091     }
1092     if (lineHeight) {
1093         if (!size) {
1094             result += " ";
1095         }
1096         result += "/";
1097         result += lineHeight->cssText();
1098     }
1099     if (family) {
1100         if (result.length() > 0) {
1101             result += " ";
1102         }
1103         result += family->cssText();
1104     }
1105
1106     return result;
1107 }
1108     
1109
1110 // Used for text-shadow and box-shadow
1111 ShadowValueImpl::ShadowValueImpl(CSSPrimitiveValueImpl* _x, CSSPrimitiveValueImpl* _y,
1112                                  CSSPrimitiveValueImpl* _blur, CSSPrimitiveValueImpl* _color)
1113 :x(_x), y(_y), blur(_blur), color(_color)       
1114 {}
1115
1116 ShadowValueImpl::~ShadowValueImpl()
1117 {
1118     delete x;
1119     delete y;
1120     delete blur;
1121     delete color;
1122 }
1123
1124 DOMString ShadowValueImpl::cssText() const
1125 {
1126     DOMString text("");
1127     if (color) {
1128         text += color->cssText();
1129     }
1130     if (x) {
1131         if (text.length() > 0) {
1132             text += " ";
1133         }
1134         text += x->cssText();
1135     }
1136     if (y) {
1137         if (text.length() > 0) {
1138             text += " ";
1139         }
1140         text += y->cssText();
1141     }
1142     if (blur) {
1143         if (text.length() > 0) {
1144             text += " ";
1145         }
1146         text += blur->cssText();
1147     }
1148
1149     return text;
1150 }
1151
1152 // Used for box-flex-transition-group
1153 FlexGroupTransitionValueImpl::FlexGroupTransitionValueImpl()
1154 :autoValue(true), group1(0), group2(0), length(0)
1155 {}
1156
1157 FlexGroupTransitionValueImpl::FlexGroupTransitionValueImpl(unsigned int _group1, 
1158                                                            unsigned int _group2,
1159                                                            CSSPrimitiveValueImpl* _length)
1160 :autoValue(false), group1(_group1), group2(_group2), length(_length)
1161 {}
1162
1163 FlexGroupTransitionValueImpl::~FlexGroupTransitionValueImpl()
1164 {
1165     delete length;
1166 }
1167
1168 DOMString FlexGroupTransitionValueImpl::cssText() const
1169 {
1170     DOMString text(QString::number(group1));
1171     if (group2) {
1172         text += "/";
1173         text += QString::number(group2);
1174     }
1175     if (length) {
1176         text += " ";
1177         text += length->cssText();
1178     }
1179     return text;
1180 }
1181
1182 DOMString CSSProperty::cssText() const
1183 {
1184     return getPropertyName(m_id) + DOMString(": ") + m_value->cssText() + (m_bImportant ? DOMString(" !important") : DOMString()) + DOMString("; ");
1185 }