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