2008-11-04 Anders Carlsson <andersca@apple.com>
[webkit/qt.git] / WebCore / dom / StyledElement.cpp
blob351c5eb3994daa3b9a79b36a33ed638041fb4a73
1 /*
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.
24 #include "config.h"
25 #include "StyledElement.h"
27 #include "CSSStyleSelector.h"
28 #include "CSSStyleSheet.h"
29 #include "CSSValueKeywords.h"
30 #include "Document.h"
31 #include "HTMLNames.h"
33 using namespace std;
35 namespace WebCore {
37 using namespace HTMLNames;
39 struct MappedAttributeKey {
40 uint16_t type;
41 StringImpl* name;
42 StringImpl* value;
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)
70 return 0;
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)
77 return 0;
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)
98 return;
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)
118 : Element(name, 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);
153 return;
156 MappedAttribute* mappedAttr = static_cast<MappedAttribute*>(attr);
157 if (mappedAttr->decl() && !preserveDecls) {
158 mappedAttr->setDecl(0);
159 setChanged();
160 if (namedAttrMap)
161 mappedAttributes()->declRemoved();
164 bool checkDecl = true;
165 MappedAttributeEntry entry;
166 bool needToParse = mapToEntry(attr->name(), entry);
167 if (preserveDecls) {
168 if (mappedAttr->decl()) {
169 setChanged();
170 if (namedAttrMap)
171 mappedAttributes()->declAdded();
172 checkDecl = false;
175 else if (!attr->isNull() && entry != eNone) {
176 CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(entry, attr);
177 if (decl) {
178 mappedAttr->setDecl(decl);
179 setChanged();
180 if (namedAttrMap)
181 mappedAttributes()->declAdded();
182 checkDecl = false;
183 } else
184 needToParse = true;
187 if (needToParse)
188 parseMappedAttribute(mappedAttr);
190 if (entry == eNone && ownerDocument()->attached() && ownerDocument()->styleSelector()->hasSelectorForAttribute(attr->name().localName()))
191 setChanged();
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);
199 if (namedAttrMap)
200 mappedAttributes()->declAdded();
202 Element::attributeChanged(attr, preserveDecls);
205 bool StyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
207 result = eNone;
208 if (attrName == styleAttr)
209 return !m_synchronizingStyleAttribute;
210 return true;
213 void StyledElement::classAttributeChanged(const AtomicString& newClassString)
215 const UChar* characters = newClassString.characters();
216 unsigned length = newClassString.length();
217 unsigned i;
218 for (i = 0; i < length; ++i) {
219 if (!isClassWhitespace(characters[i]))
220 break;
222 setHasClass(i < length);
223 if (namedAttrMap) {
224 if (i < length)
225 mappedAttributes()->setClass(newClassString);
226 else
227 mappedAttributes()->clearClass();
229 setChanged();
230 dispatchSubtreeModifiedEvent();
233 void StyledElement::parseMappedAttribute(MappedAttribute *attr)
235 if (attr->name() == idAttr) {
236 // unique id
237 setHasID(!attr->isNull());
238 if (namedAttrMap) {
239 if (attr->isNull())
240 namedAttrMap->setID(nullAtom);
241 else if (document()->inCompatMode() && !attr->value().impl()->isLower())
242 namedAttrMap->setID(AtomicString(attr->value().string().lower()));
243 else
244 namedAttrMap->setID(attr->value());
246 setChanged();
247 } else if (attr->name() == classAttr)
248 classAttributeChanged(attr->value());
249 else if (attr->name() == styleAttr) {
250 if (attr->isNull())
251 destroyInlineStyleDecl();
252 else
253 getInlineStyleDecl()->parseDeclaration(attr->value());
254 m_isStyleAttributeValid = true;
255 setChanged();
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)
280 : -1)));
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.
311 if (!attr->decl())
312 createMappedDecl(attr);
314 // strip attribute garbage..
315 StringImpl* v = value.impl();
316 if (v) {
317 unsigned int l = 0;
319 while (l < v->length() && (*v)[l] <= ' ')
320 l++;
322 for (; l < v->length(); l++) {
323 UChar cc = (*v)[l];
324 if (cc > '9')
325 break;
326 if (cc < '0') {
327 if (cc == '%' || cc == '*')
328 l++;
329 if (cc != '.')
330 break;
334 if (l != v->length()) {
335 attr->decl()->setLengthProperty(id, v->substring(0, l), false);
336 return;
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.
347 if (!c.length())
348 return;
350 if (!attr->decl())
351 createMappedDecl(attr);
353 if (attr->decl()->setProperty(id, c, false))
354 return;
356 String color = c;
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
364 // of the length.
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
372 // and 255.
374 if (!equalIgnoringCase(color, "transparent")) {
375 if (color[0] == '#')
376 color.remove(0, 1);
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 };
381 int component = 0;
382 int pos = 0;
383 int maxDigit = basicLength-1;
384 while (component < 3) {
385 // search forward for digits in the string
386 int numDigits = 0;
387 while (pos < (int)color.length() && numDigits < basicLength) {
388 int hex = toHex(color[pos]);
389 colors[component] = (colors[component] << 4);
390 if (hex > 0) {
391 colors[component] += hex;
392 maxDigit = min(maxDigit, numDigits);
394 numDigits++;
395 pos++;
397 while (numDigits++ < basicLength)
398 colors[component] <<= 4;
399 component++;
401 maxDigit = basicLength - maxDigit;
403 // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits
404 maxDigit -= 2;
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))
412 return;
415 attr->decl()->setProperty(id, CSSValueBlack, false);
418 void StyledElement::createMappedDecl(MappedAttribute* attr)
420 RefPtr<CSSMappedAttributeDeclaration> decl = CSSMappedAttributeDeclaration::create();
421 attr->setDecl(decl);
422 decl->setParent(document()->elementSheet());
423 decl->setNode(this);
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)
435 uint32_t hash = PHI;
436 uint32_t tmp;
438 const uint16_t* p;
440 p = reinterpret_cast<const uint16_t*>(&key.name);
441 hash += p[0];
442 tmp = (p[1] << 11) ^ hash;
443 hash = (hash << 16) ^ tmp;
444 hash += hash >> 11;
445 ASSERT(sizeof(key.name) == 4 || sizeof(key.name) == 8);
446 if (sizeof(key.name) == 8) {
447 p += 2;
448 hash += p[0];
449 tmp = (p[1] << 11) ^ hash;
450 hash = (hash << 16) ^ tmp;
451 hash += hash >> 11;
454 p = reinterpret_cast<const uint16_t*>(&key.value);
455 hash += p[0];
456 tmp = (p[1] << 11) ^ hash;
457 hash = (hash << 16) ^ tmp;
458 hash += hash >> 11;
459 ASSERT(sizeof(key.value) == 4 || sizeof(key.value) == 8);
460 if (sizeof(key.value) == 8) {
461 p += 2;
462 hash += p[0];
463 tmp = (p[1] << 11) ^ hash;
464 hash = (hash << 16) ^ tmp;
465 hash += hash >> 11;
468 // Handle end case
469 hash += key.type;
470 hash ^= hash << 11;
471 hash += hash >> 17;
473 // Force "avalanching" of final 127 bits
474 hash ^= hash << 3;
475 hash += hash >> 5;
476 hash ^= hash << 2;
477 hash += hash >> 15;
478 hash ^= hash << 10;
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
483 if (hash == 0)
484 hash = 0x80000000;
486 return hash;
489 void StyledElement::copyNonAttributeProperties(const Element *sourceElement)
491 const StyledElement* source = static_cast<const StyledElement*>(sourceElement);
492 if (!source->m_inlineStyleDecl)
493 return;
495 *getInlineStyleDecl() = *source->m_inlineStyleDecl;
496 m_isStyleAttributeValid = source->m_isStyleAttributeValid;
497 m_synchronizingStyleAttribute = source->m_synchronizingStyleAttribute;
499 Element::copyNonAttributeProperties(sourceElement);