2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Peter Kelly (pmk@post.com)
5 * (C) 2001 Dirk Mueller (mueller@kde.org)
6 * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
25 #include "StyledElement.h"
27 #include "CSSStyleSelector.h"
28 #include "CSSStyleSheet.h"
29 #include "CSSValueKeywords.h"
31 #include "HTMLNames.h"
37 using namespace HTMLNames
;
39 struct MappedAttributeKey
{
43 MappedAttributeKey(MappedAttributeEntry t
= eNone
, StringImpl
* n
= 0, StringImpl
* v
= 0)
44 : type(t
), name(n
), value(v
) { }
47 static inline bool operator==(const MappedAttributeKey
& a
, const MappedAttributeKey
& b
)
48 { return a
.type
== b
.type
&& a
.name
== b
.name
&& a
.value
== b
.value
; }
50 struct MappedAttributeKeyTraits
: WTF::GenericHashTraits
<MappedAttributeKey
> {
51 static const bool emptyValueIsZero
= true;
52 static const bool needsDestruction
= false;
53 static void constructDeletedValue(MappedAttributeKey
& slot
) { slot
.type
= eLastEntry
; }
54 static bool isDeletedValue(const MappedAttributeKey
& value
) { return value
.type
== eLastEntry
; }
57 struct MappedAttributeHash
{
58 static unsigned hash(const MappedAttributeKey
&);
59 static bool equal(const MappedAttributeKey
& a
, const MappedAttributeKey
& b
) { return a
== b
; }
60 static const bool safeToCompareToEmptyOrDeleted
= true;
63 typedef HashMap
<MappedAttributeKey
, CSSMappedAttributeDeclaration
*, MappedAttributeHash
, MappedAttributeKeyTraits
> MappedAttributeDecls
;
65 static MappedAttributeDecls
* mappedAttributeDecls
= 0;
67 CSSMappedAttributeDeclaration
* StyledElement::getMappedAttributeDecl(MappedAttributeEntry entryType
, Attribute
* attr
)
69 if (!mappedAttributeDecls
)
71 return mappedAttributeDecls
->get(MappedAttributeKey(entryType
, attr
->name().localName().impl(), attr
->value().impl()));
74 CSSMappedAttributeDeclaration
* StyledElement::getMappedAttributeDecl(MappedAttributeEntry type
, const QualifiedName
& name
, const AtomicString
& value
)
76 if (!mappedAttributeDecls
)
78 return mappedAttributeDecls
->get(MappedAttributeKey(type
, name
.localName().impl(), value
.impl()));
81 void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType
, Attribute
* attr
, CSSMappedAttributeDeclaration
* decl
)
83 if (!mappedAttributeDecls
)
84 mappedAttributeDecls
= new MappedAttributeDecls
;
85 mappedAttributeDecls
->set(MappedAttributeKey(entryType
, attr
->name().localName().impl(), attr
->value().impl()), decl
);
88 void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType
, const QualifiedName
& name
, const AtomicString
& value
, CSSMappedAttributeDeclaration
* decl
)
90 if (!mappedAttributeDecls
)
91 mappedAttributeDecls
= new MappedAttributeDecls
;
92 mappedAttributeDecls
->set(MappedAttributeKey(entryType
, name
.localName().impl(), value
.impl()), decl
);
95 void StyledElement::removeMappedAttributeDecl(MappedAttributeEntry entryType
, const QualifiedName
& attrName
, const AtomicString
& attrValue
)
97 if (!mappedAttributeDecls
)
99 mappedAttributeDecls
->remove(MappedAttributeKey(entryType
, attrName
.localName().impl(), attrValue
.impl()));
102 void StyledElement::invalidateStyleAttribute()
104 m_isStyleAttributeValid
= false;
107 void StyledElement::updateStyleAttribute() const
109 ASSERT(!m_isStyleAttributeValid
);
110 m_isStyleAttributeValid
= true;
111 m_synchronizingStyleAttribute
= true;
112 if (m_inlineStyleDecl
)
113 const_cast<StyledElement
*>(this)->setAttribute(styleAttr
, m_inlineStyleDecl
->cssText());
114 m_synchronizingStyleAttribute
= false;
117 StyledElement::StyledElement(const QualifiedName
& name
, Document
*doc
)
122 StyledElement::~StyledElement()
124 destroyInlineStyleDecl();
127 PassRefPtr
<Attribute
> StyledElement::createAttribute(const QualifiedName
& name
, const AtomicString
& value
)
129 return MappedAttribute::create(name
, value
);
132 void StyledElement::createInlineStyleDecl()
134 m_inlineStyleDecl
= CSSMutableStyleDeclaration::create();
135 m_inlineStyleDecl
->setParent(document()->elementSheet());
136 m_inlineStyleDecl
->setNode(this);
137 m_inlineStyleDecl
->setStrictParsing(isHTMLElement() && !document()->inCompatMode());
140 void StyledElement::destroyInlineStyleDecl()
142 if (m_inlineStyleDecl
) {
143 m_inlineStyleDecl
->setNode(0);
144 m_inlineStyleDecl
->setParent(0);
145 m_inlineStyleDecl
= 0;
149 void StyledElement::attributeChanged(Attribute
* attr
, bool preserveDecls
)
151 if (!attr
->isMappedAttribute()) {
152 Element::attributeChanged(attr
, preserveDecls
);
156 MappedAttribute
* mappedAttr
= static_cast<MappedAttribute
*>(attr
);
157 if (mappedAttr
->decl() && !preserveDecls
) {
158 mappedAttr
->setDecl(0);
161 mappedAttributes()->declRemoved();
164 bool checkDecl
= true;
165 MappedAttributeEntry entry
;
166 bool needToParse
= mapToEntry(attr
->name(), entry
);
168 if (mappedAttr
->decl()) {
171 mappedAttributes()->declAdded();
175 else if (!attr
->isNull() && entry
!= eNone
) {
176 CSSMappedAttributeDeclaration
* decl
= getMappedAttributeDecl(entry
, attr
);
178 mappedAttr
->setDecl(decl
);
181 mappedAttributes()->declAdded();
188 parseMappedAttribute(mappedAttr
);
190 if (entry
== eNone
&& ownerDocument()->attached() && ownerDocument()->styleSelector()->hasSelectorForAttribute(attr
->name().localName()))
193 if (checkDecl
&& mappedAttr
->decl()) {
194 // Add the decl to the table in the appropriate spot.
195 setMappedAttributeDecl(entry
, attr
, mappedAttr
->decl());
196 mappedAttr
->decl()->setMappedState(entry
, attr
->name(), attr
->value());
197 mappedAttr
->decl()->setParent(0);
198 mappedAttr
->decl()->setNode(0);
200 mappedAttributes()->declAdded();
202 Element::attributeChanged(attr
, preserveDecls
);
205 bool StyledElement::mapToEntry(const QualifiedName
& attrName
, MappedAttributeEntry
& result
) const
208 if (attrName
== styleAttr
)
209 return !m_synchronizingStyleAttribute
;
213 void StyledElement::classAttributeChanged(const AtomicString
& newClassString
)
215 const UChar
* characters
= newClassString
.characters();
216 unsigned length
= newClassString
.length();
218 for (i
= 0; i
< length
; ++i
) {
219 if (!isClassWhitespace(characters
[i
]))
222 setHasClass(i
< length
);
225 mappedAttributes()->setClass(newClassString
);
227 mappedAttributes()->clearClass();
230 dispatchSubtreeModifiedEvent();
233 void StyledElement::parseMappedAttribute(MappedAttribute
*attr
)
235 if (attr
->name() == idAttr
) {
237 setHasID(!attr
->isNull());
240 namedAttrMap
->setID(nullAtom
);
241 else if (document()->inCompatMode() && !attr
->value().impl()->isLower())
242 namedAttrMap
->setID(AtomicString(attr
->value().string().lower()));
244 namedAttrMap
->setID(attr
->value());
247 } else if (attr
->name() == classAttr
)
248 classAttributeChanged(attr
->value());
249 else if (attr
->name() == styleAttr
) {
251 destroyInlineStyleDecl();
253 getInlineStyleDecl()->parseDeclaration(attr
->value());
254 m_isStyleAttributeValid
= true;
259 void StyledElement::createAttributeMap() const
261 namedAttrMap
= NamedMappedAttrMap::create(const_cast<StyledElement
*>(this));
264 CSSMutableStyleDeclaration
* StyledElement::getInlineStyleDecl()
266 if (!m_inlineStyleDecl
)
267 createInlineStyleDecl();
268 return m_inlineStyleDecl
.get();
271 CSSStyleDeclaration
* StyledElement::style()
273 return getInlineStyleDecl();
276 static inline int toHex(UChar c
) {
277 return ((c
>= '0' && c
<= '9') ? (c
- '0')
278 : ((c
>= 'a' && c
<= 'f') ? (c
- 'a' + 10)
279 : (( c
>= 'A' && c
<= 'F') ? (c
- 'A' + 10)
283 void StyledElement::addCSSProperty(MappedAttribute
* attr
, int id
, const String
&value
)
285 if (!attr
->decl()) createMappedDecl(attr
);
286 attr
->decl()->setProperty(id
, value
, false);
289 void StyledElement::addCSSProperty(MappedAttribute
* attr
, int id
, int value
)
291 if (!attr
->decl()) createMappedDecl(attr
);
292 attr
->decl()->setProperty(id
, value
, false);
295 void StyledElement::addCSSStringProperty(MappedAttribute
* attr
, int id
, const String
&value
, CSSPrimitiveValue::UnitTypes type
)
297 if (!attr
->decl()) createMappedDecl(attr
);
298 attr
->decl()->setStringProperty(id
, value
, type
, false);
301 void StyledElement::addCSSImageProperty(MappedAttribute
* attr
, int id
, const String
& url
)
303 if (!attr
->decl()) createMappedDecl(attr
);
304 attr
->decl()->setImageProperty(id
, url
, false);
307 void StyledElement::addCSSLength(MappedAttribute
* attr
, int id
, const String
&value
)
309 // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
310 // length unit and make the appropriate parsed value.
312 createMappedDecl(attr
);
314 // strip attribute garbage..
315 StringImpl
* v
= value
.impl();
319 while (l
< v
->length() && (*v
)[l
] <= ' ')
322 for (; l
< v
->length(); l
++) {
327 if (cc
== '%' || cc
== '*')
334 if (l
!= v
->length()) {
335 attr
->decl()->setLengthProperty(id
, v
->substring(0, l
), false);
340 attr
->decl()->setLengthProperty(id
, value
, false);
343 /* color parsing that tries to match as close as possible IE 6. */
344 void StyledElement::addCSSColor(MappedAttribute
* attr
, int id
, const String
& c
)
346 // this is the only case no color gets applied in IE.
351 createMappedDecl(attr
);
353 if (attr
->decl()->setProperty(id
, c
, false))
357 // not something that fits the specs.
359 // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value
360 // out of everyhting you put in. The algorithm is experimentally determined, but seems to work for all test cases I have.
362 // the length of the color value is rounded up to the next
363 // multiple of 3. each part of the rgb triple then gets one third
366 // Each triplet is parsed byte by byte, mapping
367 // each number to a hex value (0-9a-fA-F to their values
368 // everything else to 0).
370 // The highest non zero digit in all triplets is remembered, and
371 // used as a normalization point to normalize to values between 0
374 if (!equalIgnoringCase(color
, "transparent")) {
377 int basicLength
= (color
.length() + 2) / 3;
378 if (basicLength
> 1) {
379 // IE ignores colors with three digits or less
380 int colors
[3] = { 0, 0, 0 };
383 int maxDigit
= basicLength
-1;
384 while (component
< 3) {
385 // search forward for digits in the string
387 while (pos
< (int)color
.length() && numDigits
< basicLength
) {
388 int hex
= toHex(color
[pos
]);
389 colors
[component
] = (colors
[component
] << 4);
391 colors
[component
] += hex
;
392 maxDigit
= min(maxDigit
, numDigits
);
397 while (numDigits
++ < basicLength
)
398 colors
[component
] <<= 4;
401 maxDigit
= basicLength
- maxDigit
;
403 // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits
405 colors
[0] >>= 4*maxDigit
;
406 colors
[1] >>= 4*maxDigit
;
407 colors
[2] >>= 4*maxDigit
;
408 // ASSERT(colors[0] < 0x100 && colors[1] < 0x100 && colors[2] < 0x100);
410 color
= String::format("#%02x%02x%02x", colors
[0], colors
[1], colors
[2]);
411 if (attr
->decl()->setProperty(id
, color
, false))
415 attr
->decl()->setProperty(id
, CSSValueBlack
, false);
418 void StyledElement::createMappedDecl(MappedAttribute
* attr
)
420 RefPtr
<CSSMappedAttributeDeclaration
> decl
= CSSMappedAttributeDeclaration::create();
422 decl
->setParent(document()->elementSheet());
424 decl
->setStrictParsing(false); // Mapped attributes are just always quirky.
427 // Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's
428 // or anything like that.
429 const unsigned PHI
= 0x9e3779b9U
;
431 // Paul Hsieh's SuperFastHash
432 // http://www.azillionmonkeys.com/qed/hash.html
433 unsigned MappedAttributeHash::hash(const MappedAttributeKey
& key
)
440 p
= reinterpret_cast<const uint16_t*>(&key
.name
);
442 tmp
= (p
[1] << 11) ^ hash
;
443 hash
= (hash
<< 16) ^ tmp
;
445 ASSERT(sizeof(key
.name
) == 4 || sizeof(key
.name
) == 8);
446 if (sizeof(key
.name
) == 8) {
449 tmp
= (p
[1] << 11) ^ hash
;
450 hash
= (hash
<< 16) ^ tmp
;
454 p
= reinterpret_cast<const uint16_t*>(&key
.value
);
456 tmp
= (p
[1] << 11) ^ hash
;
457 hash
= (hash
<< 16) ^ tmp
;
459 ASSERT(sizeof(key
.value
) == 4 || sizeof(key
.value
) == 8);
460 if (sizeof(key
.value
) == 8) {
463 tmp
= (p
[1] << 11) ^ hash
;
464 hash
= (hash
<< 16) ^ tmp
;
473 // Force "avalanching" of final 127 bits
480 // This avoids ever returning a hash code of 0, since that is used to
481 // signal "hash not computed yet", using a value that is likely to be
482 // effectively the same as 0 when the low bits are masked
489 void StyledElement::copyNonAttributeProperties(const Element
*sourceElement
)
491 const StyledElement
* source
= static_cast<const StyledElement
*>(sourceElement
);
492 if (!source
->m_inlineStyleDecl
)
495 *getInlineStyleDecl() = *source
->m_inlineStyleDecl
;
496 m_isStyleAttributeValid
= source
->m_isStyleAttributeValid
;
497 m_synchronizingStyleAttribute
= source
->m_synchronizingStyleAttribute
;
499 Element::copyNonAttributeProperties(sourceElement
);