• 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 
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