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