2 * This file is part of the DOM implementation for KDE.
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * (C) 2001 Peter Kelly (pmk@post.com)
7 * (C) 2001 Dirk Mueller (mueller@kde.org)
8 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
27 #include "StyledElement.h"
29 #include "CSSStyleSelector.h"
30 #include "CSSStyleSheet.h"
31 #include "CSSValueKeywords.h"
33 #include "HTMLNames.h"
39 using namespace HTMLNames;
41 struct MappedAttributeKey {
45 MappedAttributeKey(MappedAttributeEntry t = eNone, StringImpl* n = 0, StringImpl* v = 0)
46 : type(t), name(n), value(v) { }
49 static inline bool operator==(const MappedAttributeKey& a, const MappedAttributeKey& b)
50 { return a.type == b.type && a.name == b.name && a.value == b.value; }
52 struct MappedAttributeKeyTraits : WTF::GenericHashTraits<MappedAttributeKey> {
53 static const bool emptyValueIsZero = true;
54 static const bool needsDestruction = false;
55 static MappedAttributeKey deletedValue() { return eLastEntry; }
58 struct MappedAttributeHash {
59 static unsigned hash(const MappedAttributeKey&);
60 static bool equal(const MappedAttributeKey& a, const MappedAttributeKey& b) { return a == b; }
61 static const bool safeToCompareToEmptyOrDeleted = true;
64 typedef HashMap<MappedAttributeKey, CSSMappedAttributeDeclaration*, MappedAttributeHash, MappedAttributeKeyTraits> MappedAttributeDecls;
66 static MappedAttributeDecls* mappedAttributeDecls = 0;
68 CSSMappedAttributeDeclaration* StyledElement::getMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr)
70 if (!mappedAttributeDecls)
72 return mappedAttributeDecls->get(MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl()));
75 void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr, CSSMappedAttributeDeclaration* decl)
77 if (!mappedAttributeDecls)
78 mappedAttributeDecls = new MappedAttributeDecls;
79 mappedAttributeDecls->set(MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl()), decl);
82 void StyledElement::removeMappedAttributeDecl(MappedAttributeEntry entryType,
83 const QualifiedName& attrName, const AtomicString& attrValue)
85 if (!mappedAttributeDecls)
87 mappedAttributeDecls->remove(MappedAttributeKey(entryType, attrName.localName().impl(), attrValue.impl()));
90 void StyledElement::invalidateStyleAttribute()
92 m_isStyleAttributeValid = false;
95 void StyledElement::updateStyleAttributeIfNeeded() const
97 if (!m_isStyleAttributeValid) {
98 m_isStyleAttributeValid = true;
99 m_synchronizingStyleAttribute = true;
100 if (m_inlineStyleDecl)
101 const_cast<StyledElement*>(this)->setAttribute(styleAttr, m_inlineStyleDecl->cssText());
102 m_synchronizingStyleAttribute = false;
106 inline static bool isClassWhitespace(UChar c)
108 return c == ' ' || c == '\r' || c == '\n' || c == '\t';
111 StyledElement::StyledElement(const QualifiedName& name, Document *doc)
114 m_isStyleAttributeValid = true;
115 m_synchronizingStyleAttribute = false;
118 StyledElement::~StyledElement()
120 destroyInlineStyleDecl();
123 Attribute* StyledElement::createAttribute(const QualifiedName& name, StringImpl* value)
125 return new MappedAttribute(name, value);
128 void StyledElement::createInlineStyleDecl()
130 m_inlineStyleDecl = new CSSMutableStyleDeclaration;
131 m_inlineStyleDecl->setParent(document()->elementSheet());
132 m_inlineStyleDecl->setNode(this);
133 m_inlineStyleDecl->setStrictParsing(isHTMLElement() && !document()->inCompatMode());
136 void StyledElement::destroyInlineStyleDecl()
138 if (m_inlineStyleDecl) {
139 m_inlineStyleDecl->setNode(0);
140 m_inlineStyleDecl->setParent(0);
141 m_inlineStyleDecl = 0;
145 void StyledElement::attributeChanged(Attribute* attr, bool preserveDecls)
147 MappedAttribute* mappedAttr = static_cast<MappedAttribute*>(attr);
148 if (mappedAttr->decl() && !preserveDecls) {
149 mappedAttr->setDecl(0);
152 mappedAttributes()->declRemoved();
155 bool checkDecl = true;
156 MappedAttributeEntry entry;
157 bool needToParse = mapToEntry(attr->name(), entry);
159 if (mappedAttr->decl()) {
162 mappedAttributes()->declAdded();
166 else if (!attr->isNull() && entry != eNone) {
167 CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(entry, attr);
169 mappedAttr->setDecl(decl);
172 mappedAttributes()->declAdded();
179 parseMappedAttribute(mappedAttr);
181 if (entry == eNone && ownerDocument()->styleSelector()->hasSelectorForAttribute(attr->name().localName()))
184 if (checkDecl && mappedAttr->decl()) {
185 // Add the decl to the table in the appropriate spot.
186 setMappedAttributeDecl(entry, attr, mappedAttr->decl());
187 mappedAttr->decl()->setMappedState(entry, attr->name(), attr->value());
188 mappedAttr->decl()->setParent(0);
189 mappedAttr->decl()->setNode(0);
191 mappedAttributes()->declAdded();
195 bool StyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
198 if (attrName == styleAttr)
199 return !m_synchronizingStyleAttribute;
203 void StyledElement::parseMappedAttribute(MappedAttribute *attr)
205 if (attr->name() == idAttr) {
207 setHasID(!attr->isNull());
210 namedAttrMap->setID(nullAtom);
211 else if (document()->inCompatMode() && !attr->value().impl()->isLower())
212 namedAttrMap->setID(AtomicString(attr->value().domString().lower()));
214 namedAttrMap->setID(attr->value());
217 } else if (attr->name() == classAttr) {
219 bool hasClass = false;
220 if (!attr->isEmpty()) {
221 const AtomicString& value = attr->value();
222 unsigned len = value.length();
223 for (unsigned i = 0; i < len; ++i) {
224 if (!isClassWhitespace(value[i])) {
230 setHasClass(hasClass);
232 mappedAttributes()->parseClassAttribute(attr->value());
234 } else if (attr->name() == styleAttr) {
236 destroyInlineStyleDecl();
238 getInlineStyleDecl()->parseDeclaration(attr->value());
239 m_isStyleAttributeValid = true;
244 void StyledElement::createAttributeMap() const
246 namedAttrMap = new NamedMappedAttrMap(const_cast<StyledElement*>(this));
249 CSSMutableStyleDeclaration* StyledElement::getInlineStyleDecl()
251 if (!m_inlineStyleDecl)
252 createInlineStyleDecl();
253 return m_inlineStyleDecl.get();
256 CSSStyleDeclaration* StyledElement::style()
258 return getInlineStyleDecl();
261 CSSMutableStyleDeclaration* StyledElement::additionalAttributeStyleDecl()
266 const AtomicStringList* StyledElement::getClassList() const
268 return namedAttrMap ? mappedAttributes()->getClassList() : 0;
271 static inline int toHex(UChar c) {
272 return ((c >= '0' && c <= '9') ? (c - '0')
273 : ((c >= 'a' && c <= 'f') ? (c - 'a' + 10)
274 : (( c >= 'A' && c <= 'F') ? (c - 'A' + 10)
278 void StyledElement::addCSSProperty(MappedAttribute* attr, int id, const String &value)
280 if (!attr->decl()) createMappedDecl(attr);
281 attr->decl()->setProperty(id, value, false);
284 void StyledElement::addCSSProperty(MappedAttribute* attr, int id, int value)
286 if (!attr->decl()) createMappedDecl(attr);
287 attr->decl()->setProperty(id, value, false);
290 void StyledElement::addCSSStringProperty(MappedAttribute* attr, int id, const String &value, CSSPrimitiveValue::UnitTypes type)
292 if (!attr->decl()) createMappedDecl(attr);
293 attr->decl()->setStringProperty(id, value, type, false);
296 void StyledElement::addCSSImageProperty(MappedAttribute* attr, int id, const String &URL)
298 if (!attr->decl()) createMappedDecl(attr);
299 attr->decl()->setImageProperty(id, URL, false);
302 void StyledElement::addCSSLength(MappedAttribute* attr, int id, const String &value)
304 // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
305 // length unit and make the appropriate parsed value.
306 if (!attr->decl()) createMappedDecl(attr);
308 // strip attribute garbage..
309 StringImpl* v = value.impl();
313 while (l < v->length() && (*v)[l] <= ' ')
316 for (; l < v->length(); l++) {
318 if (cc > '9' || (cc < '0' && cc != '*' && cc != '%' && cc != '.'))
322 if (l != v->length()) {
323 attr->decl()->setLengthProperty(id, v->substring(0, l), false);
328 attr->decl()->setLengthProperty(id, value, false);
331 /* color parsing that tries to match as close as possible IE 6. */
332 void StyledElement::addCSSColor(MappedAttribute* attr, int id, const String &c)
334 // this is the only case no color gets applied in IE.
339 createMappedDecl(attr);
341 if (attr->decl()->setProperty(id, c, false))
344 String color = c.copy();
345 // not something that fits the specs.
347 // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value
348 // out of everyhting you put in. The algorithm is experimentally determined, but seems to work for all test cases I have.
350 // the length of the color value is rounded up to the next
351 // multiple of 3. each part of the rgb triple then gets one third
354 // Each triplet is parsed byte by byte, mapping
355 // each number to a hex value (0-9a-fA-F to their values
356 // everything else to 0).
358 // The highest non zero digit in all triplets is remembered, and
359 // used as a normalization point to normalize to values between 0
362 if (color.lower() != "transparent") {
365 int basicLength = (color.length() + 2) / 3;
366 if (basicLength > 1) {
367 // IE ignores colors with three digits or less
368 int colors[3] = { 0, 0, 0 };
371 int maxDigit = basicLength-1;
372 while (component < 3) {
373 // search forward for digits in the string
375 while (pos < (int)color.length() && numDigits < basicLength) {
376 int hex = toHex(color[pos]);
377 colors[component] = (colors[component] << 4);
379 colors[component] += hex;
380 maxDigit = min(maxDigit, numDigits);
385 while (numDigits++ < basicLength)
386 colors[component] <<= 4;
389 maxDigit = basicLength - maxDigit;
391 // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits
393 colors[0] >>= 4*maxDigit;
394 colors[1] >>= 4*maxDigit;
395 colors[2] >>= 4*maxDigit;
396 // ASSERT(colors[0] < 0x100 && colors[1] < 0x100 && colors[2] < 0x100);
398 color = String::format("#%02x%02x%02x", colors[0], colors[1], colors[2]);
399 if (attr->decl()->setProperty(id, color, false))
403 attr->decl()->setProperty(id, CSS_VAL_BLACK, false);
406 void StyledElement::createMappedDecl(MappedAttribute* attr)
408 CSSMappedAttributeDeclaration* decl = new CSSMappedAttributeDeclaration(0);
410 decl->setParent(document()->elementSheet());
412 decl->setStrictParsing(false); // Mapped attributes are just always quirky.
415 // Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's
416 // or anything like that.
417 const unsigned PHI = 0x9e3779b9U;
419 // Paul Hsieh's SuperFastHash
420 // http://www.azillionmonkeys.com/qed/hash.html
421 unsigned MappedAttributeHash::hash(const MappedAttributeKey& key)
428 p = reinterpret_cast<const uint16_t*>(&key.name);
430 tmp = (p[1] << 11) ^ hash;
431 hash = (hash << 16) ^ tmp;
433 ASSERT(sizeof(key.name) == 4 || sizeof(key.name) == 8);
434 if (sizeof(key.name) == 8) {
437 tmp = (p[1] << 11) ^ hash;
438 hash = (hash << 16) ^ tmp;
442 p = reinterpret_cast<const uint16_t*>(&key.value);
444 tmp = (p[1] << 11) ^ hash;
445 hash = (hash << 16) ^ tmp;
447 ASSERT(sizeof(key.value) == 4 || sizeof(key.value) == 8);
448 if (sizeof(key.value) == 8) {
451 tmp = (p[1] << 11) ^ hash;
452 hash = (hash << 16) ^ tmp;
461 // Force "avalanching" of final 127 bits
468 // This avoids ever returning a hash code of 0, since that is used to
469 // signal "hash not computed yet", using a value that is likely to be
470 // effectively the same as 0 when the low bits are masked
477 void StyledElement::copyNonAttributeProperties(const Element *sourceElement)
479 const StyledElement* source = static_cast<const StyledElement*>(sourceElement);
480 if (!source->m_inlineStyleDecl)
483 *getInlineStyleDecl() = *source->m_inlineStyleDecl;
484 m_isStyleAttributeValid = source->m_isStyleAttributeValid;
485 m_synchronizingStyleAttribute = source->m_synchronizingStyleAttribute;
487 Element::copyNonAttributeProperties(sourceElement);