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