Fixed <rdar://problem/4008338> REGRESSION (125-178): opacity style not working,...
[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 copyBlockProperties() function.
463 // It is the list of CSS properties that apply to specially to block-level elements.
464 static const int BlockProperties[] = {
465     CSS_PROP_ORPHANS,
466     CSS_PROP_OVERFLOW, // This can be also be applied to replaced elements
467     CSS_PROP_PAGE_BREAK_AFTER,
468     CSS_PROP_PAGE_BREAK_BEFORE,
469     CSS_PROP_PAGE_BREAK_INSIDE,
470     CSS_PROP_TEXT_ALIGN,
471     CSS_PROP_TEXT_INDENT,
472     CSS_PROP_WIDOWS
473 };
474
475 CSSMutableStyleDeclarationImpl *CSSMutableStyleDeclarationImpl::copyBlockProperties() const
476 {
477     return copyPropertiesInSet(BlockProperties, sizeof(BlockProperties) / sizeof(BlockProperties[0]));
478 }
479
480 void CSSMutableStyleDeclarationImpl::removeBlockProperties()
481 {
482     removePropertiesInSet(BlockProperties, sizeof(BlockProperties) / sizeof(BlockProperties[0]));
483 }
484
485 CSSMutableStyleDeclarationImpl *CSSStyleDeclarationImpl::copyPropertiesInSet(const int *set, unsigned length) const
486 {
487     QValueList<CSSProperty> list;
488     for (unsigned i = 0; i < length; i++) {
489         CSSValueImpl *value = getPropertyCSSValue(set[i]);
490         if (value)
491             list.append(CSSProperty(set[i], value, false));
492     }
493     return new CSSMutableStyleDeclarationImpl(0, list);
494 }
495
496 void CSSMutableStyleDeclarationImpl::removePropertiesInSet(const int *set, unsigned length)
497 {
498     for (unsigned i = 0; i < length; i++) {
499         CSSValueImpl *value = getPropertyCSSValue(set[i]);
500         if (value)
501             m_values.remove(CSSProperty(set[i], value, false));
502     }
503 }
504
505 CSSMutableStyleDeclarationImpl *CSSMutableStyleDeclarationImpl::makeMutable()
506 {
507     return this;
508 }
509
510 CSSMutableStyleDeclarationImpl *CSSMutableStyleDeclarationImpl::copy() const
511 {
512     return new CSSMutableStyleDeclarationImpl(0, m_values);
513 }
514
515 // --------------------------------------------------------------------------------------
516
517 unsigned short CSSInheritedValueImpl::cssValueType() const
518 {
519     return CSSValue::CSS_INHERIT;
520 }
521
522 DOM::DOMString CSSInheritedValueImpl::cssText() const
523 {
524     return DOMString("inherit");
525 }
526
527 unsigned short CSSInitialValueImpl::cssValueType() const
528
529     return CSSValue::CSS_INITIAL; 
530 }
531
532 DOM::DOMString CSSInitialValueImpl::cssText() const
533 {
534     return DOMString("initial");
535 }
536
537 // ----------------------------------------------------------------------------------------
538
539 CSSValueListImpl::CSSValueListImpl()
540 {
541 }
542
543 CSSValueListImpl::~CSSValueListImpl()
544 {
545     CSSValueImpl *val = m_values.first();
546     while( val ) {
547         val->deref();
548         val = m_values.next();
549     }
550 }
551
552 unsigned short CSSValueListImpl::cssValueType() const
553 {
554     return CSSValue::CSS_VALUE_LIST;
555 }
556
557 void CSSValueListImpl::append(CSSValueImpl *val)
558 {
559     m_values.append(val);
560     val->ref();
561 }
562
563 DOM::DOMString CSSValueListImpl::cssText() const
564 {
565     DOMString result = "";
566
567     for (QPtrListIterator<CSSValueImpl> iterator(m_values); iterator.current(); ++iterator) {
568         if (result.length() != 0) {
569             result += ", ";
570         }
571         result += iterator.current()->cssText();
572     }
573     
574     return result;
575 }
576
577 // -------------------------------------------------------------------------------------
578
579 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl()
580     : CSSValueImpl()
581 {
582     m_type = 0;
583 }
584
585 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(int ident)
586     : CSSValueImpl()
587 {
588     m_value.ident = ident;
589     m_type = CSSPrimitiveValue::CSS_IDENT;
590 }
591
592 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(double num, CSSPrimitiveValue::UnitTypes type)
593 {
594     m_value.num = num;
595     m_type = type;
596 }
597
598 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(const DOMString &str, CSSPrimitiveValue::UnitTypes type)
599 {
600     m_value.string = str.implementation();
601     if(m_value.string) m_value.string->ref();
602     m_type = type;
603 }
604
605 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(const Counter &c)
606 {
607     m_value.counter = c.handle();
608     if (m_value.counter)
609         m_value.counter->ref();
610     m_type = CSSPrimitiveValue::CSS_COUNTER;
611 }
612
613 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl( RectImpl *r)
614 {
615     m_value.rect = r;
616     if (m_value.rect)
617         m_value.rect->ref();
618     m_type = CSSPrimitiveValue::CSS_RECT;
619 }
620
621 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl( DashboardRegionImpl *r)
622 {
623     m_value.region = r;
624     if (m_value.region)
625         m_value.region->ref();
626     m_type = CSSPrimitiveValue::CSS_DASHBOARD_REGION;
627 }
628
629 CSSPrimitiveValueImpl::CSSPrimitiveValueImpl(QRgb color)
630 {
631     m_value.rgbcolor = color;
632     m_type = CSSPrimitiveValue::CSS_RGBCOLOR;
633 }
634
635 CSSPrimitiveValueImpl::~CSSPrimitiveValueImpl()
636 {
637     cleanup();
638 }
639
640 void CSSPrimitiveValueImpl::cleanup()
641 {
642     switch(m_type) {
643     case CSSPrimitiveValue::CSS_STRING:
644     case CSSPrimitiveValue::CSS_URI:
645     case CSSPrimitiveValue::CSS_ATTR:
646         if(m_value.string) m_value.string->deref();
647         break;
648     case CSSPrimitiveValue::CSS_COUNTER:
649         m_value.counter->deref();
650         break;
651     case CSSPrimitiveValue::CSS_RECT:
652         m_value.rect->deref();
653         break;
654     case CSSPrimitiveValue::CSS_DASHBOARD_REGION:
655         if (m_value.region)
656             m_value.region->deref();
657         break;
658     default:
659         break;
660     }
661
662     m_type = 0;
663 }
664
665 int CSSPrimitiveValueImpl::computeLength( khtml::RenderStyle *style, QPaintDeviceMetrics *devMetrics )
666 {
667     double result = computeLengthFloat( style, devMetrics );
668 #if APPLE_CHANGES
669     // This conversion is imprecise, often resulting in values of, e.g., 44.99998.  We
670     // need to go ahead and round if we're really close to the next integer value.
671     int intResult = (int)(result + (result < 0 ? -0.01 : +0.01));
672 #else
673     int intResult = (int)result;
674 #endif
675     return intResult;    
676 }
677
678 int CSSPrimitiveValueImpl::computeLength( khtml::RenderStyle *style, QPaintDeviceMetrics *devMetrics, 
679                                           double multiplier )
680 {
681     double result = multiplier * computeLengthFloat( style, devMetrics );
682 #if APPLE_CHANGES
683     // This conversion is imprecise, often resulting in values of, e.g., 44.99998.  We
684     // need to go ahead and round if we're really close to the next integer value.
685     int intResult = (int)(result + (result < 0 ? -0.01 : +0.01));
686 #else
687     int intResult = (int)result;
688 #endif
689     return intResult;    
690 }
691
692 double CSSPrimitiveValueImpl::computeLengthFloat( khtml::RenderStyle *style, QPaintDeviceMetrics *devMetrics,
693                                                   bool applyZoomFactor )
694 {
695     unsigned short type = primitiveType();
696
697     double dpiY = 72.; // fallback
698     if ( devMetrics )
699         dpiY = devMetrics->logicalDpiY();
700     if ( !khtml::printpainter && dpiY < 96 )
701         dpiY = 96.;
702
703     double factor = 1.;
704     switch(type)
705     {
706     case CSSPrimitiveValue::CSS_EMS:
707         factor = applyZoomFactor ?
708           style->htmlFont().getFontDef().computedSize :
709           style->htmlFont().getFontDef().specifiedSize;
710         break;
711     case CSSPrimitiveValue::CSS_EXS:
712         // FIXME: We have a bug right now where the zoom will be applied multiple times to EX units.
713         // We really need to compute EX using fontMetrics for the original specifiedSize and not use
714         // our actual constructed rendering font.
715         {
716         QFontMetrics fm = style->fontMetrics();
717 #if APPLE_CHANGES
718         factor = fm.xHeight();
719 #else
720         QRect b = fm.boundingRect('x');
721         factor = b.height();
722 #endif
723         break;
724         }
725     case CSSPrimitiveValue::CSS_PX:
726         break;
727     case CSSPrimitiveValue::CSS_CM:
728         factor = dpiY/2.54; //72dpi/(2.54 cm/in)
729         break;
730     case CSSPrimitiveValue::CSS_MM:
731         factor = dpiY/25.4;
732         break;
733     case CSSPrimitiveValue::CSS_IN:
734             factor = dpiY;
735         break;
736     case CSSPrimitiveValue::CSS_PT:
737             factor = dpiY/72.;
738         break;
739     case CSSPrimitiveValue::CSS_PC:
740         // 1 pc == 12 pt
741             factor = dpiY*12./72.;
742         break;
743     default:
744         return -1;
745     }
746
747     return getFloatValue(type)*factor;
748 }
749
750 void CSSPrimitiveValueImpl::setFloatValue( unsigned short unitType, double floatValue, int &exceptioncode )
751 {
752     exceptioncode = 0;
753     cleanup();
754     // ### check if property supports this type
755     if(m_type > CSSPrimitiveValue::CSS_DIMENSION) {
756         exceptioncode = CSSException::SYNTAX_ERR + CSSException::_EXCEPTION_OFFSET;
757         return;
758     }
759     //if(m_type > CSSPrimitiveValue::CSS_DIMENSION) throw DOMException(DOMException::INVALID_ACCESS_ERR);
760     m_value.num = floatValue;
761     m_type = unitType;
762 }
763
764 void CSSPrimitiveValueImpl::setStringValue( unsigned short stringType, const DOMString &stringValue, int &exceptioncode )
765 {
766     exceptioncode = 0;
767     cleanup();
768     //if(m_type < CSSPrimitiveValue::CSS_STRING) throw DOMException(DOMException::INVALID_ACCESS_ERR);
769     //if(m_type > CSSPrimitiveValue::CSS_ATTR) throw DOMException(DOMException::INVALID_ACCESS_ERR);
770     if(m_type < CSSPrimitiveValue::CSS_STRING || m_type >> CSSPrimitiveValue::CSS_ATTR) {
771         exceptioncode = CSSException::SYNTAX_ERR + CSSException::_EXCEPTION_OFFSET;
772         return;
773     }
774     if(stringType != CSSPrimitiveValue::CSS_IDENT)
775     {
776         m_value.string = stringValue.implementation();
777         m_value.string->ref();
778         m_type = stringType;
779     }
780     // ### parse ident
781 }
782
783 DOMString CSSPrimitiveValueImpl::getStringValue() const
784 {
785     switch (m_type) {
786         case CSSPrimitiveValue::CSS_STRING:
787         case CSSPrimitiveValue::CSS_ATTR:
788         case CSSPrimitiveValue::CSS_URI:
789             return m_value.string;
790         case CSSPrimitiveValue::CSS_IDENT:
791             return getValueName(m_value.ident);
792         default:
793             // FIXME: The CSS 2.1 spec says you should throw an exception here.
794             break;
795     }
796     
797     return DOMString();
798 }
799
800 unsigned short CSSPrimitiveValueImpl::cssValueType() const
801 {
802     return CSSValue::CSS_PRIMITIVE_VALUE;
803 }
804
805 bool CSSPrimitiveValueImpl::parseString( const DOMString &/*string*/, bool )
806 {
807     // ###
808     return false;
809 }
810
811 int CSSPrimitiveValueImpl::getIdent()
812 {
813     if(m_type != CSSPrimitiveValue::CSS_IDENT) return 0;
814     return m_value.ident;
815 }
816
817 DOM::DOMString CSSPrimitiveValueImpl::cssText() const
818 {
819     // ### return the original value instead of a generated one (e.g. color
820     // name if it was specified) - check what spec says about this
821     DOMString text;
822     switch ( m_type ) {
823         case CSSPrimitiveValue::CSS_UNKNOWN:
824             // ###
825             break;
826         case CSSPrimitiveValue::CSS_NUMBER:
827             text = DOMString(QString::number( m_value.num ));
828             break;
829         case CSSPrimitiveValue::CSS_PERCENTAGE:
830             text = DOMString(QString::number( m_value.num ) + "%");
831             break;
832         case CSSPrimitiveValue::CSS_EMS:
833             text = DOMString(QString::number( m_value.num ) + "em");
834             break;
835         case CSSPrimitiveValue::CSS_EXS:
836             text = DOMString(QString::number( m_value.num ) + "ex");
837             break;
838         case CSSPrimitiveValue::CSS_PX:
839             text = DOMString(QString::number( m_value.num ) + "px");
840             break;
841         case CSSPrimitiveValue::CSS_CM:
842             text = DOMString(QString::number( m_value.num ) + "cm");
843             break;
844         case CSSPrimitiveValue::CSS_MM:
845             text = DOMString(QString::number( m_value.num ) + "mm");
846             break;
847         case CSSPrimitiveValue::CSS_IN:
848             text = DOMString(QString::number( m_value.num ) + "in");
849             break;
850         case CSSPrimitiveValue::CSS_PT:
851             text = DOMString(QString::number( m_value.num ) + "pt");
852             break;
853         case CSSPrimitiveValue::CSS_PC:
854             text = DOMString(QString::number( m_value.num ) + "pc");
855             break;
856         case CSSPrimitiveValue::CSS_DEG:
857             text = DOMString(QString::number( m_value.num ) + "deg");
858             break;
859         case CSSPrimitiveValue::CSS_RAD:
860             text = DOMString(QString::number( m_value.num ) + "rad");
861             break;
862         case CSSPrimitiveValue::CSS_GRAD:
863             text = DOMString(QString::number( m_value.num ) + "grad");
864             break;
865         case CSSPrimitiveValue::CSS_MS:
866             text = DOMString(QString::number( m_value.num ) + "ms");
867             break;
868         case CSSPrimitiveValue::CSS_S:
869             text = DOMString(QString::number( m_value.num ) + "s");
870             break;
871         case CSSPrimitiveValue::CSS_HZ:
872             text = DOMString(QString::number( m_value.num ) + "hz");
873             break;
874         case CSSPrimitiveValue::CSS_KHZ:
875             text = DOMString(QString::number( m_value.num ) + "khz");
876             break;
877         case CSSPrimitiveValue::CSS_DIMENSION:
878             // ###
879             break;
880         case CSSPrimitiveValue::CSS_STRING:
881             text = DOMString(m_value.string);
882             break;
883         case CSSPrimitiveValue::CSS_URI:
884             text  = "url(";
885             text += DOMString( m_value.string );
886             text += ")";
887             break;
888         case CSSPrimitiveValue::CSS_IDENT:
889             text = getValueName(m_value.ident);
890             break;
891         case CSSPrimitiveValue::CSS_ATTR:
892             // ###
893             break;
894         case CSSPrimitiveValue::CSS_COUNTER:
895             // ###
896             break;
897         case CSSPrimitiveValue::CSS_RECT: {
898             RectImpl* rectVal = getRectValue();
899             text = "rect(";
900             text += rectVal->top()->cssText() + " ";
901             text += rectVal->right()->cssText() + " ";
902             text += rectVal->bottom()->cssText() + " ";
903             text += rectVal->left()->cssText() + ")";
904             break;
905         }
906         case CSSPrimitiveValue::CSS_RGBCOLOR: {
907             QColor color(m_value.rgbcolor);
908             if (qAlpha(m_value.rgbcolor) < 0xFF)
909                 text = "rgba(";
910             else
911                 text = "rgb(";
912             text += QString::number(color.red()) + ", ";
913             text += QString::number(color.green()) + ", ";
914             text += QString::number(color.blue());
915             if (qAlpha(m_value.rgbcolor) < 0xFF)
916                 text += ", " + QString::number((float)qAlpha(m_value.rgbcolor) / 0xFF);
917             text += ")";
918             break;
919         }
920 #if APPLE_CHANGES        
921         case CSSPrimitiveValue::CSS_DASHBOARD_REGION: {
922             DashboardRegionImpl *region = getDashboardRegionValue();
923             while (region) {
924                 text = "dashboard-region(";
925                 text += region->m_label;
926                 if (region->m_isCircle){
927                     text += " circle ";
928                 }
929                 else if (region->m_isRectangle){
930                     text += " rectangle ";
931                 }
932                 else
933                     break;
934                 text += region->top()->cssText() + " ";
935                 text += region->right()->cssText() + " ";
936                 text += region->bottom()->cssText() + " ";
937                 text += region->left()->cssText();
938                 text += ")";
939                 region = region->m_next;
940             }
941             break;
942         }
943 #endif
944     }
945     return text;
946 }
947
948 // -----------------------------------------------------------------
949
950 RectImpl::RectImpl()
951 {
952     m_top = 0;
953     m_right = 0;
954     m_bottom = 0;
955     m_left = 0;
956 }
957
958 RectImpl::~RectImpl()
959 {
960     if (m_top) m_top->deref();
961     if (m_right) m_right->deref();
962     if (m_bottom) m_bottom->deref();
963     if (m_left) m_left->deref();
964 }
965
966 void RectImpl::setTop( CSSPrimitiveValueImpl *top )
967 {
968     if( top ) top->ref();
969     if ( m_top ) m_top->deref();
970     m_top = top;
971 }
972
973 void RectImpl::setRight( CSSPrimitiveValueImpl *right )
974 {
975     if( right ) right->ref();
976     if ( m_right ) m_right->deref();
977     m_right = right;
978 }
979
980 void RectImpl::setBottom( CSSPrimitiveValueImpl *bottom )
981 {
982     if( bottom ) bottom->ref();
983     if ( m_bottom ) m_bottom->deref();
984     m_bottom = bottom;
985 }
986
987 void RectImpl::setLeft( CSSPrimitiveValueImpl *left )
988 {
989     if( left ) left->ref();
990     if ( m_left ) m_left->deref();
991     m_left = left;
992 }
993
994 // -----------------------------------------------------------------
995
996 CSSImageValueImpl::CSSImageValueImpl(const DOMString &url, StyleBaseImpl *style)
997     : CSSPrimitiveValueImpl(url, CSSPrimitiveValue::CSS_URI), m_image(0), m_accessedImage(false)
998 {
999 }
1000
1001 CSSImageValueImpl::CSSImageValueImpl()
1002     : CSSPrimitiveValueImpl(CSS_VAL_NONE), m_image(0), m_accessedImage(true)
1003 {
1004 }
1005
1006 CSSImageValueImpl::~CSSImageValueImpl()
1007 {
1008     if(m_image) m_image->deref(this);
1009 }
1010
1011 khtml::CachedImage* CSSImageValueImpl::image(khtml::DocLoader* loader)
1012 {
1013     if (!m_accessedImage) {
1014         m_accessedImage = true;
1015
1016         if (loader)
1017             m_image = loader->requestImage(getStringValue());
1018         else
1019             m_image = khtml::Cache::requestImage(0, getStringValue());
1020         
1021         if(m_image) m_image->ref(this);
1022     }
1023     
1024     return m_image;
1025 }
1026
1027 // ------------------------------------------------------------------------
1028
1029 FontFamilyValueImpl::FontFamilyValueImpl( const QString &string)
1030 : CSSPrimitiveValueImpl( DOMString(), CSSPrimitiveValue::CSS_STRING)
1031 {
1032     static const QRegExp parenReg(" \\(.*\\)$");
1033     static const QRegExp braceReg(" \\[.*\\]$");
1034
1035 #if APPLE_CHANGES
1036     parsedFontName = string;
1037     // a language tag is often added in braces at the end. Remove it.
1038     parsedFontName.replace(parenReg, "");
1039     // remove [Xft] qualifiers
1040     parsedFontName.replace(braceReg, "");
1041 #else
1042     const QString &available = KHTMLSettings::availableFamilies();
1043
1044     QString face = string.lower();
1045     // a languge tag is often added in braces at the end. Remove it.
1046     face = face.replace(parenReg, "");
1047     // remove [Xft] qualifiers
1048     face = face.replace(braceReg, "");
1049     //kdDebug(0) << "searching for face '" << face << "'" << endl;
1050
1051     int pos = available.find( face, 0, false );
1052     if( pos == -1 ) {
1053         QString str = face;
1054         int p = face.find(' ');
1055         // Arial Blk --> Arial
1056         // MS Sans Serif --> Sans Serif
1057         if ( p != -1 ) {
1058             if(p > 0 && (int)str.length() - p > p + 1)
1059                 str = str.mid( p+1 );
1060             else
1061                 str.truncate( p );
1062             pos = available.find( str, 0, false);
1063         }
1064     }
1065
1066     if ( pos != -1 ) {
1067         int pos1 = available.findRev( ',', pos ) + 1;
1068         pos = available.find( ',', pos );
1069         if ( pos == -1 )
1070             pos = available.length();
1071         parsedFontName = available.mid( pos1, pos - pos1 );
1072     }
1073 #endif // !APPLE_CHANGES
1074 }
1075
1076 DOM::DOMString FontFamilyValueImpl::cssText() const
1077 {
1078     return parsedFontName;
1079 }
1080
1081 FontValueImpl::FontValueImpl()
1082     : style(0), variant(0), weight(0), size(0), lineHeight(0), family(0)
1083 {
1084 }
1085
1086 FontValueImpl::~FontValueImpl()
1087 {
1088     delete style;
1089     delete variant;
1090     delete weight;
1091     delete size;
1092     delete lineHeight;
1093     delete family;
1094 }
1095
1096 DOMString FontValueImpl::cssText() const
1097 {
1098     // font variant weight size / line-height family 
1099
1100     DOMString result("");
1101
1102     if (style) {
1103         result += style->cssText();
1104     }
1105     if (variant) {
1106         if (result.length() > 0) {
1107             result += " ";
1108         }
1109         result += variant->cssText();
1110     }
1111     if (weight) {
1112         if (result.length() > 0) {
1113             result += " ";
1114         }
1115         result += weight->cssText();
1116     }
1117     if (size) {
1118         if (result.length() > 0) {
1119             result += " ";
1120         }
1121         result += size->cssText();
1122     }
1123     if (lineHeight) {
1124         if (!size) {
1125             result += " ";
1126         }
1127         result += "/";
1128         result += lineHeight->cssText();
1129     }
1130     if (family) {
1131         if (result.length() > 0) {
1132             result += " ";
1133         }
1134         result += family->cssText();
1135     }
1136
1137     return result;
1138 }
1139     
1140
1141 // Used for text-shadow and box-shadow
1142 ShadowValueImpl::ShadowValueImpl(CSSPrimitiveValueImpl* _x, CSSPrimitiveValueImpl* _y,
1143                                  CSSPrimitiveValueImpl* _blur, CSSPrimitiveValueImpl* _color)
1144 :x(_x), y(_y), blur(_blur), color(_color)       
1145 {}
1146
1147 ShadowValueImpl::~ShadowValueImpl()
1148 {
1149     delete x;
1150     delete y;
1151     delete blur;
1152     delete color;
1153 }
1154
1155 DOMString ShadowValueImpl::cssText() const
1156 {
1157     DOMString text("");
1158     if (color) {
1159         text += color->cssText();
1160     }
1161     if (x) {
1162         if (text.length() > 0) {
1163             text += " ";
1164         }
1165         text += x->cssText();
1166     }
1167     if (y) {
1168         if (text.length() > 0) {
1169             text += " ";
1170         }
1171         text += y->cssText();
1172     }
1173     if (blur) {
1174         if (text.length() > 0) {
1175             text += " ";
1176         }
1177         text += blur->cssText();
1178     }
1179
1180     return text;
1181 }
1182
1183 // Used for box-flex-transition-group
1184 FlexGroupTransitionValueImpl::FlexGroupTransitionValueImpl()
1185 :autoValue(true), group1(0), group2(0), length(0)
1186 {}
1187
1188 FlexGroupTransitionValueImpl::FlexGroupTransitionValueImpl(unsigned int _group1, 
1189                                                            unsigned int _group2,
1190                                                            CSSPrimitiveValueImpl* _length)
1191 :autoValue(false), group1(_group1), group2(_group2), length(_length)
1192 {}
1193
1194 FlexGroupTransitionValueImpl::~FlexGroupTransitionValueImpl()
1195 {
1196     delete length;
1197 }
1198
1199 DOMString FlexGroupTransitionValueImpl::cssText() const
1200 {
1201     DOMString text(QString::number(group1));
1202     if (group2) {
1203         text += "/";
1204         text += QString::number(group2);
1205     }
1206     if (length) {
1207         text += " ";
1208         text += length->cssText();
1209     }
1210     return text;
1211 }
1212
1213 DOMString CSSProperty::cssText() const
1214 {
1215     return getPropertyName(m_id) + DOMString(": ") + m_value->cssText() + (m_bImportant ? DOMString(" !important") : DOMString()) + DOMString("; ");
1216 }
1217
1218 bool operator==(const CSSProperty &a, const CSSProperty &b)
1219 {
1220     return a.m_id == b.m_id && a.m_bImportant == b.m_bImportant && a.m_value == b.m_value;
1221 }
1222
1223 }