initial import
[vuplus_webkit] / Source / 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 "CSSMutableStyleDeclaration.h"
29 #include "CSSStyleSelector.h"
30 #include "CSSStyleSheet.h"
31 #include "CSSValueKeywords.h"
32 #include "Color.h"
33 #include "ClassList.h"
34 #include "ContentSecurityPolicy.h"
35 #include "DOMTokenList.h"
36 #include "Document.h"
37 #include "HTMLNames.h"
38 #include "HTMLParserIdioms.h"
39 #include <wtf/HashFunctions.h>
40
41 using namespace std;
42
43 namespace WebCore {
44
45 using namespace HTMLNames;
46
47 struct MappedAttributeKey {
48     uint16_t type;
49     StringImpl* name;
50     StringImpl* value;
51     MappedAttributeKey(MappedAttributeEntry t = eNone, StringImpl* n = 0, StringImpl* v = 0)
52         : type(t), name(n), value(v) { }
53 };
54
55 static inline bool operator==(const MappedAttributeKey& a, const MappedAttributeKey& b)
56     { return a.type == b.type && a.name == b.name && a.value == b.value; } 
57
58 struct MappedAttributeKeyTraits : WTF::GenericHashTraits<MappedAttributeKey> {
59     static const bool emptyValueIsZero = true;
60     static const bool needsDestruction = false;
61     static void constructDeletedValue(MappedAttributeKey& slot) { slot.type = eLastEntry; }
62     static bool isDeletedValue(const MappedAttributeKey& value) { return value.type == eLastEntry; }
63 };
64
65 struct MappedAttributeHash {
66     static unsigned hash(const MappedAttributeKey&);
67     static bool equal(const MappedAttributeKey& a, const MappedAttributeKey& b) { return a == b; }
68     static const bool safeToCompareToEmptyOrDeleted = true;
69 };
70
71 typedef HashMap<MappedAttributeKey, CSSMappedAttributeDeclaration*, MappedAttributeHash, MappedAttributeKeyTraits> MappedAttributeDecls;
72
73 static MappedAttributeDecls* mappedAttributeDecls = 0;
74
75 CSSMappedAttributeDeclaration* StyledElement::getMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr)
76 {
77     if (!mappedAttributeDecls)
78         return 0;
79     return mappedAttributeDecls->get(MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl()));
80 }
81
82 CSSMappedAttributeDeclaration* StyledElement::getMappedAttributeDecl(MappedAttributeEntry type, const QualifiedName& name, const AtomicString& value)
83 {
84     if (!mappedAttributeDecls)
85         return 0;
86     return mappedAttributeDecls->get(MappedAttributeKey(type, name.localName().impl(), value.impl()));
87 }
88
89 void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr, CSSMappedAttributeDeclaration* decl)
90 {
91     if (!mappedAttributeDecls)
92         mappedAttributeDecls = new MappedAttributeDecls;
93     mappedAttributeDecls->set(MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl()), decl);
94 }
95
96 void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType, const QualifiedName& name, const AtomicString& value, CSSMappedAttributeDeclaration* decl)
97 {
98     if (!mappedAttributeDecls)
99         mappedAttributeDecls = new MappedAttributeDecls;
100     mappedAttributeDecls->set(MappedAttributeKey(entryType, name.localName().impl(), value.impl()), decl);
101 }
102
103 void StyledElement::removeMappedAttributeDecl(MappedAttributeEntry entryType, const QualifiedName& attrName, const AtomicString& attrValue)
104 {
105     if (!mappedAttributeDecls)
106         return;
107     mappedAttributeDecls->remove(MappedAttributeKey(entryType, attrName.localName().impl(), attrValue.impl()));
108 }
109
110 void StyledElement::updateStyleAttribute() const
111 {
112     ASSERT(!isStyleAttributeValid());
113     setIsStyleAttributeValid();
114     setIsSynchronizingStyleAttribute();
115     if (m_inlineStyleDecl)
116         const_cast<StyledElement*>(this)->setAttribute(styleAttr, m_inlineStyleDecl->cssText());
117     clearIsSynchronizingStyleAttribute();
118 }
119
120 StyledElement::~StyledElement()
121 {
122     destroyInlineStyleDecl();
123 }
124
125 PassRefPtr<Attribute> StyledElement::createAttribute(const QualifiedName& name, const AtomicString& value)
126 {
127     return Attribute::createMapped(name, value);
128 }
129
130 void StyledElement::createInlineStyleDecl()
131 {
132     m_inlineStyleDecl = CSSMutableStyleDeclaration::create();
133     m_inlineStyleDecl->setParent(document()->elementSheet());
134     m_inlineStyleDecl->setNode(this);
135     m_inlineStyleDecl->setStrictParsing(isHTMLElement() && !document()->inQuirksMode());
136 }
137
138 void StyledElement::destroyInlineStyleDecl()
139 {
140     if (m_inlineStyleDecl) {
141         m_inlineStyleDecl->setNode(0);
142         m_inlineStyleDecl->setParent(0);
143         m_inlineStyleDecl = 0;
144     }
145 }
146
147 void StyledElement::attributeChanged(Attribute* attr, bool preserveDecls)
148 {
149     if (!attr->isMappedAttribute()) {
150         Element::attributeChanged(attr, preserveDecls);
151         return;
152     }
153  
154     if (attr->decl() && !preserveDecls) {
155         attr->setDecl(0);
156         setNeedsStyleRecalc();
157         if (attributeMap())
158             attributeMap()->declRemoved();
159     }
160
161     bool checkDecl = true;
162     MappedAttributeEntry entry;
163     bool needToParse = mapToEntry(attr->name(), entry);
164     if (preserveDecls) {
165         if (attr->decl()) {
166             setNeedsStyleRecalc();
167             if (attributeMap())
168                 attributeMap()->declAdded();
169             checkDecl = false;
170         }
171     } else if (!attr->isNull() && entry != eNone) {
172         CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(entry, attr);
173         if (decl) {
174             attr->setDecl(decl);
175             setNeedsStyleRecalc();
176             if (attributeMap())
177                 attributeMap()->declAdded();
178             checkDecl = false;
179         } else
180             needToParse = true;
181     }
182
183     // parseMappedAttribute() might create a CSSMappedAttributeDeclaration on the attribute.  
184     // Normally we would be concerned about reseting the parent of those declarations in StyledElement::didMoveToNewOwnerDocument().
185     // But currently we always clear its parent and node below when adding it to the decl table.  
186     // If that changes for some reason moving between documents will be buggy.
187     // webarchive/adopt-attribute-styled-node-webarchive.html should catch any resulting crashes.
188     if (needToParse)
189         parseMappedAttribute(attr);
190
191     if (entry == eNone)
192         recalcStyleIfNeededAfterAttributeChanged(attr);
193
194     if (checkDecl && attr->decl()) {
195         // Add the decl to the table in the appropriate spot.
196         setMappedAttributeDecl(entry, attr, attr->decl());
197         attr->decl()->setMappedState(entry, attr->name(), attr->value());
198         attr->decl()->setParent(0);
199         attr->decl()->setNode(0);
200         if (attributeMap())
201             attributeMap()->declAdded();
202     }
203
204     updateAfterAttributeChanged(attr);
205 }
206
207 bool StyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
208 {
209     result = eNone;
210     if (attrName == styleAttr)
211         return !isSynchronizingStyleAttribute();
212     return true;
213 }
214
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 (isNotHTMLSpace(characters[i]))
222             break;
223     }
224     bool hasClass = i < length;
225     setHasClass(hasClass);
226     if (hasClass) {
227         attributes()->setClass(newClassString);
228         if (DOMTokenList* classList = optionalClassList())
229             static_cast<ClassList*>(classList)->reset(newClassString);
230     } else if (attributeMap())
231         attributeMap()->clearClass();
232     setNeedsStyleRecalc();
233     dispatchSubtreeModifiedEvent();
234 }
235
236 void StyledElement::parseMappedAttribute(Attribute* attr)
237 {
238     if (isIdAttributeName(attr->name()))
239         idAttributeChanged(attr);
240     else if (attr->name() == classAttr)
241         classAttributeChanged(attr->value());
242     else if (attr->name() == styleAttr) {
243         if (attr->isNull())
244             destroyInlineStyleDecl();
245         else if (document()->contentSecurityPolicy()->allowInlineStyle())
246             getInlineStyleDecl()->parseDeclaration(attr->value());
247         setIsStyleAttributeValid();
248         setNeedsStyleRecalc();
249     }
250 }
251
252 CSSMutableStyleDeclaration* StyledElement::getInlineStyleDecl()
253 {
254     if (!m_inlineStyleDecl)
255         createInlineStyleDecl();
256     return m_inlineStyleDecl.get();
257 }
258
259 CSSStyleDeclaration* StyledElement::style()
260 {
261     return getInlineStyleDecl();
262 }
263
264 void StyledElement::addCSSProperty(Attribute* attribute, int id, const String &value)
265 {
266     if (!attribute->decl())
267         createMappedDecl(attribute);
268     attribute->decl()->setProperty(id, value, false);
269 }
270
271 void StyledElement::addCSSProperty(Attribute* attribute, int id, int value)
272 {
273     if (!attribute->decl())
274         createMappedDecl(attribute);
275     attribute->decl()->setProperty(id, value, false);
276 }
277
278 void StyledElement::addCSSImageProperty(Attribute* attribute, int id, const String& url)
279 {
280     if (!attribute->decl())
281         createMappedDecl(attribute);
282     attribute->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 static String parseColorStringWithCrazyLegacyRules(const String& colorString)
322 {
323     // Per spec, only look at the first 128 digits of the string.
324     const size_t maxColorLength = 128;
325     // We'll pad the buffer with two extra 0s later, so reserve two more than the max.
326     Vector<char, maxColorLength+2> digitBuffer;
327     
328     size_t i = 0;
329     // Skip a leading #.
330     if (colorString[0] == '#')
331         i = 1;
332     
333     // Grab the first 128 characters, replacing non-hex characters with 0.
334     // Non-BMP characters are replaced with "00" due to them appearing as two "characters" in the String.
335     for (; i < colorString.length() && digitBuffer.size() < maxColorLength; i++) {
336         if (!isASCIIHexDigit(colorString[i]))
337             digitBuffer.append('0');
338         else
339             digitBuffer.append(colorString[i]);
340     }
341
342     if (!digitBuffer.size())
343         return "#000000";
344
345     // Pad the buffer out to at least the next multiple of three in size.
346     digitBuffer.append('0');
347     digitBuffer.append('0');
348
349     if (digitBuffer.size() < 6)
350         return String::format("#0%c0%c0%c", digitBuffer[0], digitBuffer[1], digitBuffer[2]);
351     
352     // Split the digits into three components, then search the last 8 digits of each component.
353     ASSERT(digitBuffer.size() >= 6);
354     size_t componentLength = digitBuffer.size() / 3;
355     size_t componentSearchWindowLength = min<size_t>(componentLength, 8);
356     size_t redIndex = componentLength - componentSearchWindowLength;
357     size_t greenIndex = componentLength * 2 - componentSearchWindowLength;
358     size_t blueIndex = componentLength * 3 - componentSearchWindowLength;
359     // Skip digits until one of them is non-zero, or we've only got two digits left in the component.
360     while (digitBuffer[redIndex] == '0' && digitBuffer[greenIndex] == '0' && digitBuffer[blueIndex] == '0' && (componentLength - redIndex) > 2) {
361         redIndex++;
362         greenIndex++;
363         blueIndex++;
364     }
365     ASSERT(redIndex + 1 < componentLength);
366     ASSERT(greenIndex >= componentLength);
367     ASSERT(greenIndex + 1 < componentLength * 2);
368     ASSERT(blueIndex >= componentLength * 2);
369     ASSERT(blueIndex + 1 < digitBuffer.size());
370     return String::format("#%c%c%c%c%c%c", digitBuffer[redIndex], digitBuffer[redIndex + 1], digitBuffer[greenIndex], digitBuffer[greenIndex + 1], digitBuffer[blueIndex], digitBuffer[blueIndex + 1]);   
371 }
372
373 // Color parsing that matches HTML's "rules for parsing a legacy color value"
374 void StyledElement::addCSSColor(Attribute* attribute, int id, const String& attributeValue)
375 {
376     // The empty string doesn't apply a color. (Just whitespace does, which is why this check occurs before trimming.)
377     if (!attributeValue.length())
378         return;
379     
380     String color = attributeValue.stripWhiteSpace();
381
382     // "transparent" doesn't apply a color either.
383     if (equalIgnoringCase(color, "transparent"))
384         return;
385
386     if (!attribute->decl())
387         createMappedDecl(attribute);
388
389     // If the string is a named CSS color, use that color.
390     Color foundColor;
391     foundColor.setNamedColor(color);
392     if (foundColor.isValid()) {
393         attribute->decl()->setProperty(id, color, false);
394         return;
395     }
396
397     // If the string is a 3 or 6-digit hex color, use that color.
398     if (color[0] == '#' && (color.length() == 4 || color.length() == 7) && attribute->decl()->setProperty(id, color, false))
399         return;
400
401     attribute->decl()->setProperty(id, parseColorStringWithCrazyLegacyRules(color), false);
402 }
403
404 void StyledElement::createMappedDecl(Attribute* attr)
405 {
406     RefPtr<CSSMappedAttributeDeclaration> decl = CSSMappedAttributeDeclaration::create();
407     attr->setDecl(decl);
408     decl->setParent(document()->elementSheet());
409     decl->setNode(this);
410     decl->setStrictParsing(false); // Mapped attributes are just always quirky.
411 }
412
413 unsigned MappedAttributeHash::hash(const MappedAttributeKey& key)
414 {
415     COMPILE_ASSERT(sizeof(key.name) == 4 || sizeof(key.name) == 8, key_name_size);
416     COMPILE_ASSERT(sizeof(key.value) == 4 || sizeof(key.value) == 8, key_value_size);
417
418     StringHasher hasher;
419     const UChar* data;
420
421     data = reinterpret_cast<const UChar*>(&key.name);
422     hasher.addCharacters(data[0], data[1]);
423     if (sizeof(key.name) == 8)
424         hasher.addCharacters(data[2], data[3]);
425
426     data = reinterpret_cast<const UChar*>(&key.value);
427     hasher.addCharacters(data[0], data[1]);
428     if (sizeof(key.value) == 8)
429         hasher.addCharacters(data[2], data[3]);
430
431     return hasher.hash();
432 }
433
434 void StyledElement::copyNonAttributeProperties(const Element *sourceElement)
435 {
436     const StyledElement* source = static_cast<const StyledElement*>(sourceElement);
437     if (!source->m_inlineStyleDecl)
438         return;
439
440     *getInlineStyleDecl() = *source->m_inlineStyleDecl;
441     setIsStyleAttributeValid(source->isStyleAttributeValid());
442     setIsSynchronizingStyleAttribute(source->isSynchronizingStyleAttribute());
443     
444     Element::copyNonAttributeProperties(sourceElement);
445 }
446
447 void StyledElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
448 {
449     if (CSSMutableStyleDeclaration* style = inlineStyleDecl())
450         style->addSubresourceStyleURLs(urls);
451 }
452
453
454 void StyledElement::didMoveToNewOwnerDocument()
455 {
456     if (m_inlineStyleDecl)
457         m_inlineStyleDecl->setParent(document()->elementSheet());
458
459     Element::didMoveToNewOwnerDocument();
460 }
461
462 }