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