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