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.
7 *
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.
12 *
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.
17 *
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.
22 */
23
24 #include "config.h"
25 #include "StyledElement.h"
26
27 #include "CSSStyleSelector.h"
28 #include "CSSStyleSheet.h"
29 #include "CSSValueKeywords.h"
30 #include "Document.h"
31 #include "HTMLNames.h"
32 #include "MappedAttribute.h"
33 #include <wtf/HashFunctions.h>
34
35 using namespace std;
36
37 namespace WebCore {
38
39 using namespace HTMLNames;
40
41 struct MappedAttributeKey {
42 uint16_t type;
43 StringImpl* name;
44 StringImpl* value;
MappedAttributeKeyWebCore::MappedAttributeKey45 MappedAttributeKey(MappedAttributeEntry t = eNone, StringImpl* n = 0, StringImpl* v = 0)
46 : type(t), name(n), value(v) { }
47 };
48
operator ==(const MappedAttributeKey & a,const MappedAttributeKey & b)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; }
51
52 struct MappedAttributeKeyTraits : WTF::GenericHashTraits<MappedAttributeKey> {
53 static const bool emptyValueIsZero = true;
54 static const bool needsDestruction = false;
constructDeletedValueWebCore::MappedAttributeKeyTraits55 static void constructDeletedValue(MappedAttributeKey& slot) { slot.type = eLastEntry; }
isDeletedValueWebCore::MappedAttributeKeyTraits56 static bool isDeletedValue(const MappedAttributeKey& value) { return value.type == eLastEntry; }
57 };
58
59 struct MappedAttributeHash {
60 static unsigned hash(const MappedAttributeKey&);
equalWebCore::MappedAttributeHash61 static bool equal(const MappedAttributeKey& a, const MappedAttributeKey& b) { return a == b; }
62 static const bool safeToCompareToEmptyOrDeleted = true;
63 };
64
65 typedef HashMap<MappedAttributeKey, CSSMappedAttributeDeclaration*, MappedAttributeHash, MappedAttributeKeyTraits> MappedAttributeDecls;
66
67 static MappedAttributeDecls* mappedAttributeDecls = 0;
68
getMappedAttributeDecl(MappedAttributeEntry entryType,Attribute * attr)69 CSSMappedAttributeDeclaration* StyledElement::getMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr)
70 {
71 if (!mappedAttributeDecls)
72 return 0;
73 return mappedAttributeDecls->get(MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl()));
74 }
75
getMappedAttributeDecl(MappedAttributeEntry type,const QualifiedName & name,const AtomicString & value)76 CSSMappedAttributeDeclaration* StyledElement::getMappedAttributeDecl(MappedAttributeEntry type, const QualifiedName& name, const AtomicString& value)
77 {
78 if (!mappedAttributeDecls)
79 return 0;
80 return mappedAttributeDecls->get(MappedAttributeKey(type, name.localName().impl(), value.impl()));
81 }
82
setMappedAttributeDecl(MappedAttributeEntry entryType,Attribute * attr,CSSMappedAttributeDeclaration * decl)83 void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr, CSSMappedAttributeDeclaration* decl)
84 {
85 if (!mappedAttributeDecls)
86 mappedAttributeDecls = new MappedAttributeDecls;
87 mappedAttributeDecls->set(MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl()), decl);
88 }
89
setMappedAttributeDecl(MappedAttributeEntry entryType,const QualifiedName & name,const AtomicString & value,CSSMappedAttributeDeclaration * decl)90 void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType, const QualifiedName& name, const AtomicString& value, CSSMappedAttributeDeclaration* decl)
91 {
92 if (!mappedAttributeDecls)
93 mappedAttributeDecls = new MappedAttributeDecls;
94 mappedAttributeDecls->set(MappedAttributeKey(entryType, name.localName().impl(), value.impl()), decl);
95 }
96
removeMappedAttributeDecl(MappedAttributeEntry entryType,const QualifiedName & attrName,const AtomicString & attrValue)97 void StyledElement::removeMappedAttributeDecl(MappedAttributeEntry entryType, const QualifiedName& attrName, const AtomicString& attrValue)
98 {
99 if (!mappedAttributeDecls)
100 return;
101 mappedAttributeDecls->remove(MappedAttributeKey(entryType, attrName.localName().impl(), attrValue.impl()));
102 }
103
invalidateStyleAttribute()104 void StyledElement::invalidateStyleAttribute()
105 {
106 m_isStyleAttributeValid = false;
107 }
108
updateStyleAttribute() const109 void StyledElement::updateStyleAttribute() const
110 {
111 ASSERT(!m_isStyleAttributeValid);
112 m_isStyleAttributeValid = true;
113 m_synchronizingStyleAttribute = true;
114 if (m_inlineStyleDecl)
115 const_cast<StyledElement*>(this)->setAttribute(styleAttr, m_inlineStyleDecl->cssText());
116 m_synchronizingStyleAttribute = false;
117 }
118
StyledElement(const QualifiedName & name,Document * doc)119 StyledElement::StyledElement(const QualifiedName& name, Document *doc)
120 : Element(name, doc)
121 {
122 }
123
~StyledElement()124 StyledElement::~StyledElement()
125 {
126 destroyInlineStyleDecl();
127 }
128
createAttribute(const QualifiedName & name,const AtomicString & value)129 PassRefPtr<Attribute> StyledElement::createAttribute(const QualifiedName& name, const AtomicString& value)
130 {
131 return MappedAttribute::create(name, value);
132 }
133
createInlineStyleDecl()134 void StyledElement::createInlineStyleDecl()
135 {
136 m_inlineStyleDecl = CSSMutableStyleDeclaration::create();
137 m_inlineStyleDecl->setParent(document()->elementSheet());
138 m_inlineStyleDecl->setNode(this);
139 m_inlineStyleDecl->setStrictParsing(isHTMLElement() && !document()->inCompatMode());
140 }
141
destroyInlineStyleDecl()142 void StyledElement::destroyInlineStyleDecl()
143 {
144 if (m_inlineStyleDecl) {
145 m_inlineStyleDecl->setNode(0);
146 m_inlineStyleDecl->setParent(0);
147 m_inlineStyleDecl = 0;
148 }
149 }
150
attributeChanged(Attribute * attr,bool preserveDecls)151 void StyledElement::attributeChanged(Attribute* attr, bool preserveDecls)
152 {
153 if (!attr->isMappedAttribute()) {
154 Element::attributeChanged(attr, preserveDecls);
155 return;
156 }
157
158 MappedAttribute* mappedAttr = static_cast<MappedAttribute*>(attr);
159 if (mappedAttr->decl() && !preserveDecls) {
160 mappedAttr->setDecl(0);
161 setNeedsStyleRecalc();
162 if (namedAttrMap)
163 mappedAttributes()->declRemoved();
164 }
165
166 bool checkDecl = true;
167 MappedAttributeEntry entry;
168 bool needToParse = mapToEntry(attr->name(), entry);
169 if (preserveDecls) {
170 if (mappedAttr->decl()) {
171 setNeedsStyleRecalc();
172 if (namedAttrMap)
173 mappedAttributes()->declAdded();
174 checkDecl = false;
175 }
176 }
177 else if (!attr->isNull() && entry != eNone) {
178 CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(entry, attr);
179 if (decl) {
180 mappedAttr->setDecl(decl);
181 setNeedsStyleRecalc();
182 if (namedAttrMap)
183 mappedAttributes()->declAdded();
184 checkDecl = false;
185 } else
186 needToParse = true;
187 }
188
189 // parseMappedAttribute() might create a CSSMappedAttributeDeclaration on the attribute.
190 // Normally we would be concerned about reseting the parent of those declarations in StyledElement::didMoveToNewOwnerDocument().
191 // But currently we always clear its parent and node below when adding it to the decl table.
192 // If that changes for some reason moving between documents will be buggy.
193 // webarchive/adopt-attribute-styled-node-webarchive.html should catch any resulting crashes.
194 if (needToParse)
195 parseMappedAttribute(mappedAttr);
196
197 if (entry == eNone)
198 recalcStyleIfNeededAfterAttributeChanged(attr);
199
200 if (checkDecl && mappedAttr->decl()) {
201 // Add the decl to the table in the appropriate spot.
202 setMappedAttributeDecl(entry, attr, mappedAttr->decl());
203 mappedAttr->decl()->setMappedState(entry, attr->name(), attr->value());
204 mappedAttr->decl()->setParent(0);
205 mappedAttr->decl()->setNode(0);
206 if (namedAttrMap)
207 mappedAttributes()->declAdded();
208 }
209 updateAfterAttributeChanged(attr);
210 }
211
mapToEntry(const QualifiedName & attrName,MappedAttributeEntry & result) const212 bool StyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
213 {
214 result = eNone;
215 if (attrName == styleAttr)
216 return !m_synchronizingStyleAttribute;
217 return true;
218 }
219
classAttributeChanged(const AtomicString & newClassString)220 void StyledElement::classAttributeChanged(const AtomicString& newClassString)
221 {
222 const UChar* characters = newClassString.characters();
223 unsigned length = newClassString.length();
224 unsigned i;
225 for (i = 0; i < length; ++i) {
226 if (!isClassWhitespace(characters[i]))
227 break;
228 }
229 setHasClass(i < length);
230 if (namedAttrMap) {
231 if (i < length)
232 mappedAttributes()->setClass(newClassString);
233 else
234 mappedAttributes()->clearClass();
235 }
236 setNeedsStyleRecalc();
237 dispatchSubtreeModifiedEvent();
238 }
239
parseMappedAttribute(MappedAttribute * attr)240 void StyledElement::parseMappedAttribute(MappedAttribute *attr)
241 {
242 if (attr->name() == idAttr) {
243 // unique id
244 setHasID(!attr->isNull());
245 if (namedAttrMap) {
246 if (attr->isNull())
247 namedAttrMap->setID(nullAtom);
248 else if (document()->inCompatMode() && !attr->value().impl()->isLower())
249 namedAttrMap->setID(AtomicString(attr->value().string().lower()));
250 else
251 namedAttrMap->setID(attr->value());
252 }
253 setNeedsStyleRecalc();
254 } else if (attr->name() == classAttr)
255 classAttributeChanged(attr->value());
256 else if (attr->name() == styleAttr) {
257 if (attr->isNull())
258 destroyInlineStyleDecl();
259 else
260 getInlineStyleDecl()->parseDeclaration(attr->value());
261 m_isStyleAttributeValid = true;
262 setNeedsStyleRecalc();
263 }
264 }
265
createAttributeMap() const266 void StyledElement::createAttributeMap() const
267 {
268 namedAttrMap = NamedMappedAttrMap::create(const_cast<StyledElement*>(this));
269 }
270
getInlineStyleDecl()271 CSSMutableStyleDeclaration* StyledElement::getInlineStyleDecl()
272 {
273 if (!m_inlineStyleDecl)
274 createInlineStyleDecl();
275 return m_inlineStyleDecl.get();
276 }
277
style()278 CSSStyleDeclaration* StyledElement::style()
279 {
280 return getInlineStyleDecl();
281 }
282
toHex(UChar c)283 static inline int toHex(UChar c)
284 {
285 return ((c >= '0' && c <= '9') ? (c - '0')
286 : ((c >= 'a' && c <= 'f') ? (c - 'a' + 10)
287 : ((c >= 'A' && c <= 'F') ? (c - 'A' + 10)
288 : -1)));
289 }
290
addCSSProperty(MappedAttribute * attr,int id,const String & value)291 void StyledElement::addCSSProperty(MappedAttribute* attr, int id, const String &value)
292 {
293 if (!attr->decl()) createMappedDecl(attr);
294 attr->decl()->setProperty(id, value, false);
295 }
296
addCSSProperty(MappedAttribute * attr,int id,int value)297 void StyledElement::addCSSProperty(MappedAttribute* attr, int id, int value)
298 {
299 if (!attr->decl()) createMappedDecl(attr);
300 attr->decl()->setProperty(id, value, false);
301 }
302
addCSSStringProperty(MappedAttribute * attr,int id,const String & value,CSSPrimitiveValue::UnitTypes type)303 void StyledElement::addCSSStringProperty(MappedAttribute* attr, int id, const String &value, CSSPrimitiveValue::UnitTypes type)
304 {
305 if (!attr->decl()) createMappedDecl(attr);
306 attr->decl()->setStringProperty(id, value, type, false);
307 }
308
addCSSImageProperty(MappedAttribute * attr,int id,const String & url)309 void StyledElement::addCSSImageProperty(MappedAttribute* attr, int id, const String& url)
310 {
311 if (!attr->decl()) createMappedDecl(attr);
312 attr->decl()->setImageProperty(id, url, false);
313 }
314
addCSSLength(MappedAttribute * attr,int id,const String & value)315 void StyledElement::addCSSLength(MappedAttribute* attr, int id, const String &value)
316 {
317 // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
318 // length unit and make the appropriate parsed value.
319 if (!attr->decl())
320 createMappedDecl(attr);
321
322 // strip attribute garbage..
323 StringImpl* v = value.impl();
324 if (v) {
325 unsigned int l = 0;
326
327 while (l < v->length() && (*v)[l] <= ' ')
328 l++;
329
330 for (; l < v->length(); l++) {
331 UChar cc = (*v)[l];
332 if (cc > '9')
333 break;
334 if (cc < '0') {
335 if (cc == '%' || cc == '*')
336 l++;
337 if (cc != '.')
338 break;
339 }
340 }
341
342 if (l != v->length()) {
343 attr->decl()->setLengthProperty(id, v->substring(0, l), false);
344 return;
345 }
346 }
347
348 attr->decl()->setLengthProperty(id, value, false);
349 }
350
351 /* color parsing that tries to match as close as possible IE 6. */
addCSSColor(MappedAttribute * attr,int id,const String & c)352 void StyledElement::addCSSColor(MappedAttribute* attr, int id, const String& c)
353 {
354 // this is the only case no color gets applied in IE.
355 if (!c.length())
356 return;
357
358 if (!attr->decl())
359 createMappedDecl(attr);
360
361 if (attr->decl()->setProperty(id, c, false))
362 return;
363
364 String color = c;
365 // not something that fits the specs.
366
367 // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value
368 // out of everyhting you put in. The algorithm is experimentally determined, but seems to work for all test cases I have.
369
370 // the length of the color value is rounded up to the next
371 // multiple of 3. each part of the rgb triple then gets one third
372 // of the length.
373 //
374 // Each triplet is parsed byte by byte, mapping
375 // each number to a hex value (0-9a-fA-F to their values
376 // everything else to 0).
377 //
378 // The highest non zero digit in all triplets is remembered, and
379 // used as a normalization point to normalize to values between 0
380 // and 255.
381
382 if (!equalIgnoringCase(color, "transparent")) {
383 if (color[0] == '#')
384 color.remove(0, 1);
385 int basicLength = (color.length() + 2) / 3;
386 if (basicLength > 1) {
387 // IE ignores colors with three digits or less
388 int colors[3] = { 0, 0, 0 };
389 int component = 0;
390 int pos = 0;
391 int maxDigit = basicLength-1;
392 while (component < 3) {
393 // search forward for digits in the string
394 int numDigits = 0;
395 while (pos < (int)color.length() && numDigits < basicLength) {
396 int hex = toHex(color[pos]);
397 colors[component] = (colors[component] << 4);
398 if (hex > 0) {
399 colors[component] += hex;
400 maxDigit = min(maxDigit, numDigits);
401 }
402 numDigits++;
403 pos++;
404 }
405 while (numDigits++ < basicLength)
406 colors[component] <<= 4;
407 component++;
408 }
409 maxDigit = basicLength - maxDigit;
410
411 // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits
412 maxDigit -= 2;
413 colors[0] >>= 4*maxDigit;
414 colors[1] >>= 4*maxDigit;
415 colors[2] >>= 4*maxDigit;
416 // ASSERT(colors[0] < 0x100 && colors[1] < 0x100 && colors[2] < 0x100);
417
418 color = String::format("#%02x%02x%02x", colors[0], colors[1], colors[2]);
419 if (attr->decl()->setProperty(id, color, false))
420 return;
421 }
422 }
423 attr->decl()->setProperty(id, CSSValueBlack, false);
424 }
425
createMappedDecl(MappedAttribute * attr)426 void StyledElement::createMappedDecl(MappedAttribute* attr)
427 {
428 RefPtr<CSSMappedAttributeDeclaration> decl = CSSMappedAttributeDeclaration::create();
429 attr->setDecl(decl);
430 decl->setParent(document()->elementSheet());
431 decl->setNode(this);
432 decl->setStrictParsing(false); // Mapped attributes are just always quirky.
433 }
434
435 // Paul Hsieh's SuperFastHash
436 // http://www.azillionmonkeys.com/qed/hash.html
hash(const MappedAttributeKey & key)437 unsigned MappedAttributeHash::hash(const MappedAttributeKey& key)
438 {
439 uint32_t hash = WTF::stringHashingStartValue;
440 uint32_t tmp;
441
442 const uint16_t* p;
443
444 p = reinterpret_cast<const uint16_t*>(&key.name);
445 hash += p[0];
446 tmp = (p[1] << 11) ^ hash;
447 hash = (hash << 16) ^ tmp;
448 hash += hash >> 11;
449 ASSERT(sizeof(key.name) == 4 || sizeof(key.name) == 8);
450 if (sizeof(key.name) == 8) {
451 p += 2;
452 hash += p[0];
453 tmp = (p[1] << 11) ^ hash;
454 hash = (hash << 16) ^ tmp;
455 hash += hash >> 11;
456 }
457
458 p = reinterpret_cast<const uint16_t*>(&key.value);
459 hash += p[0];
460 tmp = (p[1] << 11) ^ hash;
461 hash = (hash << 16) ^ tmp;
462 hash += hash >> 11;
463 ASSERT(sizeof(key.value) == 4 || sizeof(key.value) == 8);
464 if (sizeof(key.value) == 8) {
465 p += 2;
466 hash += p[0];
467 tmp = (p[1] << 11) ^ hash;
468 hash = (hash << 16) ^ tmp;
469 hash += hash >> 11;
470 }
471
472 // Handle end case
473 hash += key.type;
474 hash ^= hash << 11;
475 hash += hash >> 17;
476
477 // Force "avalanching" of final 127 bits
478 hash ^= hash << 3;
479 hash += hash >> 5;
480 hash ^= hash << 2;
481 hash += hash >> 15;
482 hash ^= hash << 10;
483
484 // This avoids ever returning a hash code of 0, since that is used to
485 // signal "hash not computed yet", using a value that is likely to be
486 // effectively the same as 0 when the low bits are masked
487 if (hash == 0)
488 hash = 0x80000000;
489
490 return hash;
491 }
492
copyNonAttributeProperties(const Element * sourceElement)493 void StyledElement::copyNonAttributeProperties(const Element *sourceElement)
494 {
495 const StyledElement* source = static_cast<const StyledElement*>(sourceElement);
496 if (!source->m_inlineStyleDecl)
497 return;
498
499 *getInlineStyleDecl() = *source->m_inlineStyleDecl;
500 m_isStyleAttributeValid = source->m_isStyleAttributeValid;
501 m_synchronizingStyleAttribute = source->m_synchronizingStyleAttribute;
502
503 Element::copyNonAttributeProperties(sourceElement);
504 }
505
addSubresourceAttributeURLs(ListHashSet<KURL> & urls) const506 void StyledElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
507 {
508 if (CSSMutableStyleDeclaration* style = inlineStyleDecl())
509 style->addSubresourceStyleURLs(urls);
510 }
511
512
didMoveToNewOwnerDocument()513 void StyledElement::didMoveToNewOwnerDocument()
514 {
515 if (m_inlineStyleDecl)
516 m_inlineStyleDecl->setParent(document()->elementSheet());
517
518 Element::didMoveToNewOwnerDocument();
519 }
520
521 }
522