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