4be224c01395060ae08ce1b02fc1161f66abe236
[WebKit-https.git] / 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 (CSSMutableStyleDeclaration* inlineStyle = inlineStyleDecl())
116         const_cast<StyledElement*>(this)->setAttribute(styleAttr, inlineStyle->asText());
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     ASSERT(!m_inlineStyleDecl);
133     m_inlineStyleDecl = CSSMutableStyleDeclaration::createInline(this);
134     m_inlineStyleDecl->setStrictParsing(isHTMLElement() && !document()->inQuirksMode());
135 }
136
137 void StyledElement::destroyInlineStyleDecl()
138 {
139     if (!m_inlineStyleDecl)
140         return;
141     m_inlineStyleDecl->clearParentElement();
142     m_inlineStyleDecl = 0;
143 }
144
145 void StyledElement::attributeChanged(Attribute* attr, bool preserveDecls)
146 {
147     if (attr->name() == HTMLNames::nameAttr)
148         setHasName(!attr->isNull());
149
150     if (!attr->isMappedAttribute()) {
151         Element::attributeChanged(attr, preserveDecls);
152         return;
153     }
154  
155     if (attr->decl() && !preserveDecls) {
156         attr->setDecl(0);
157         setNeedsStyleRecalc();
158     }
159
160     bool checkDecl = true;
161     MappedAttributeEntry entry;
162     bool needToParse = mapToEntry(attr->name(), entry);
163     if (preserveDecls) {
164         if (attr->decl()) {
165             setNeedsStyleRecalc();
166             checkDecl = false;
167         }
168     } else if (!attr->isNull() && entry != eNone) {
169         CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(entry, attr);
170         if (decl) {
171             attr->setDecl(decl);
172             setNeedsStyleRecalc();
173             checkDecl = false;
174         } else
175             needToParse = true;
176     }
177
178     // parseMappedAttribute() might create a CSSMappedAttributeDeclaration on the attribute.  
179     // Normally we would be concerned about reseting the parent of those declarations in StyledElement::didMoveToNewDocument().
180     // But currently we always clear its parent and node below when adding it to the decl table.  
181     // If that changes for some reason moving between documents will be buggy.
182     // webarchive/adopt-attribute-styled-node-webarchive.html should catch any resulting crashes.
183     if (needToParse)
184         parseMappedAttribute(attr);
185
186     if (entry == eNone)
187         recalcStyleIfNeededAfterAttributeChanged(attr);
188
189     if (checkDecl && attr->decl()) {
190         // Add the decl to the table in the appropriate spot.
191         setMappedAttributeDecl(entry, attr, attr->decl());
192         attr->decl()->setMappedState(entry, attr->name(), attr->value());
193     }
194
195     updateAfterAttributeChanged(attr);
196 }
197
198 bool StyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
199 {
200     result = eNone;
201     if (attrName == styleAttr)
202         return !isSynchronizingStyleAttribute();
203     return true;
204 }
205
206 void StyledElement::classAttributeChanged(const AtomicString& newClassString)
207 {
208     const UChar* characters = newClassString.characters();
209     unsigned length = newClassString.length();
210     unsigned i;
211     for (i = 0; i < length; ++i) {
212         if (isNotHTMLSpace(characters[i]))
213             break;
214     }
215     bool hasClass = i < length;
216     setHasClass(hasClass);
217     if (hasClass) {
218         const bool shouldFoldCase = document()->inQuirksMode();
219         ensureAttributeData()->setClass(newClassString, shouldFoldCase);
220         if (DOMTokenList* classList = optionalClassList())
221             static_cast<ClassList*>(classList)->reset(newClassString);
222     } else if (attributeData())
223         attributeData()->clearClass();
224     setNeedsStyleRecalc();
225     dispatchSubtreeModifiedEvent();
226 }
227
228 void StyledElement::parseMappedAttribute(Attribute* attr)
229 {
230     if (isIdAttributeName(attr->name()))
231         idAttributeChanged(attr);
232     else if (attr->name() == classAttr)
233         classAttributeChanged(attr->value());
234     else if (attr->name() == styleAttr) {
235         if (attr->isNull())
236             destroyInlineStyleDecl();
237         else if (document()->contentSecurityPolicy()->allowInlineStyle())
238             ensureInlineStyleDecl()->parseDeclaration(attr->value());
239         setIsStyleAttributeValid();
240         setNeedsStyleRecalc();
241     }
242 }
243
244 CSSMutableStyleDeclaration* StyledElement::ensureInlineStyleDecl()
245 {
246     if (!m_inlineStyleDecl)
247         createInlineStyleDecl();
248     return m_inlineStyleDecl.get();
249 }
250
251 CSSStyleDeclaration* StyledElement::style()
252 {
253     return ensureInlineStyleDecl();
254 }
255
256 void StyledElement::removeCSSProperty(Attribute* attribute, int id)
257 {
258     if (!attribute->decl())
259         createMappedDecl(attribute);
260     attribute->decl()->removeMappedProperty(this, id);
261 }
262
263 void StyledElement::addCSSProperty(Attribute* attribute, int id, const String &value)
264 {
265     if (!attribute->decl())
266         createMappedDecl(attribute);
267     attribute->decl()->setMappedProperty(this, id, value);
268 }
269
270 void StyledElement::addCSSProperty(Attribute* attribute, int id, int value)
271 {
272     if (!attribute->decl())
273         createMappedDecl(attribute);
274     attribute->decl()->setMappedProperty(this, id, value);
275 }
276
277 void StyledElement::addCSSImageProperty(Attribute* attribute, int id, const String& url)
278 {
279     if (!attribute->decl())
280         createMappedDecl(attribute);
281     attribute->decl()->setMappedImageProperty(this, id, url);
282 }
283
284 void StyledElement::addCSSLength(Attribute* attribute, int id, const String &value)
285 {
286     // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
287     // length unit and make the appropriate parsed value.
288     if (!attribute->decl())
289         createMappedDecl(attribute);
290
291     // strip attribute garbage..
292     StringImpl* v = value.impl();
293     if (v) {
294         unsigned int l = 0;
295         
296         while (l < v->length() && (*v)[l] <= ' ')
297             l++;
298         
299         for (; l < v->length(); l++) {
300             UChar cc = (*v)[l];
301             if (cc > '9')
302                 break;
303             if (cc < '0') {
304                 if (cc == '%' || cc == '*')
305                     l++;
306                 if (cc != '.')
307                     break;
308             }
309         }
310
311         if (l != v->length()) {
312             attribute->decl()->setMappedLengthProperty(this, id, v->substring(0, l));
313             return;
314         }
315     }
316
317     attribute->decl()->setMappedLengthProperty(this, id, value);
318 }
319
320 static String parseColorStringWithCrazyLegacyRules(const String& colorString)
321 {
322     // Per spec, only look at the first 128 digits of the string.
323     const size_t maxColorLength = 128;
324     // We'll pad the buffer with two extra 0s later, so reserve two more than the max.
325     Vector<char, maxColorLength+2> digitBuffer;
326     
327     size_t i = 0;
328     // Skip a leading #.
329     if (colorString[0] == '#')
330         i = 1;
331     
332     // Grab the first 128 characters, replacing non-hex characters with 0.
333     // Non-BMP characters are replaced with "00" due to them appearing as two "characters" in the String.
334     for (; i < colorString.length() && digitBuffer.size() < maxColorLength; i++) {
335         if (!isASCIIHexDigit(colorString[i]))
336             digitBuffer.append('0');
337         else
338             digitBuffer.append(colorString[i]);
339     }
340
341     if (!digitBuffer.size())
342         return "#000000";
343
344     // Pad the buffer out to at least the next multiple of three in size.
345     digitBuffer.append('0');
346     digitBuffer.append('0');
347
348     if (digitBuffer.size() < 6)
349         return String::format("#0%c0%c0%c", digitBuffer[0], digitBuffer[1], digitBuffer[2]);
350     
351     // Split the digits into three components, then search the last 8 digits of each component.
352     ASSERT(digitBuffer.size() >= 6);
353     size_t componentLength = digitBuffer.size() / 3;
354     size_t componentSearchWindowLength = min<size_t>(componentLength, 8);
355     size_t redIndex = componentLength - componentSearchWindowLength;
356     size_t greenIndex = componentLength * 2 - componentSearchWindowLength;
357     size_t blueIndex = componentLength * 3 - componentSearchWindowLength;
358     // Skip digits until one of them is non-zero, or we've only got two digits left in the component.
359     while (digitBuffer[redIndex] == '0' && digitBuffer[greenIndex] == '0' && digitBuffer[blueIndex] == '0' && (componentLength - redIndex) > 2) {
360         redIndex++;
361         greenIndex++;
362         blueIndex++;
363     }
364     ASSERT(redIndex + 1 < componentLength);
365     ASSERT(greenIndex >= componentLength);
366     ASSERT(greenIndex + 1 < componentLength * 2);
367     ASSERT(blueIndex >= componentLength * 2);
368     ASSERT(blueIndex + 1 < digitBuffer.size());
369     return String::format("#%c%c%c%c%c%c", digitBuffer[redIndex], digitBuffer[redIndex + 1], digitBuffer[greenIndex], digitBuffer[greenIndex + 1], digitBuffer[blueIndex], digitBuffer[blueIndex + 1]);   
370 }
371
372 // Color parsing that matches HTML's "rules for parsing a legacy color value"
373 void StyledElement::addCSSColor(Attribute* attribute, int id, const String& attributeValue)
374 {
375     // An empty string doesn't apply a color. (One containing only whitespace does, which is why this check occurs before stripping.)
376     if (attributeValue.isEmpty())
377         return;
378
379     String colorString = attributeValue.stripWhiteSpace();
380
381     // "transparent" doesn't apply a color either.
382     if (equalIgnoringCase(colorString, "transparent"))
383         return;
384
385     if (!attribute->decl())
386         createMappedDecl(attribute);
387
388     // If the string is a named CSS color or a 3/6-digit hex color, use that.
389     Color parsedColor(colorString);
390     if (parsedColor.isValid()) {
391         attribute->decl()->setMappedProperty(this, id, colorString);
392         return;
393     }
394
395     attribute->decl()->setMappedProperty(this, id, parseColorStringWithCrazyLegacyRules(colorString));
396 }
397
398 void StyledElement::createMappedDecl(Attribute* attr)
399 {
400     RefPtr<CSSMappedAttributeDeclaration> decl = CSSMappedAttributeDeclaration::create();
401     attr->setDecl(decl);
402     ASSERT(!decl->useStrictParsing());
403 }
404
405 unsigned MappedAttributeHash::hash(const MappedAttributeKey& key)
406 {
407     COMPILE_ASSERT(sizeof(key.name) == 4 || sizeof(key.name) == 8, key_name_size);
408     COMPILE_ASSERT(sizeof(key.value) == 4 || sizeof(key.value) == 8, key_value_size);
409
410     StringHasher hasher;
411     const UChar* data;
412
413     data = reinterpret_cast<const UChar*>(&key.name);
414     hasher.addCharacters(data[0], data[1]);
415     if (sizeof(key.name) == 8)
416         hasher.addCharacters(data[2], data[3]);
417
418     data = reinterpret_cast<const UChar*>(&key.value);
419     hasher.addCharacters(data[0], data[1]);
420     if (sizeof(key.value) == 8)
421         hasher.addCharacters(data[2], data[3]);
422
423     return hasher.hash();
424 }
425
426 void StyledElement::copyNonAttributeProperties(const Element* sourceElement)
427 {
428     ASSERT(sourceElement);
429     ASSERT(sourceElement->isStyledElement());
430
431     const StyledElement* source = static_cast<const StyledElement*>(sourceElement);
432     if (!source->inlineStyleDecl())
433         return;
434
435     CSSMutableStyleDeclaration* inlineStyle = ensureInlineStyleDecl();
436     inlineStyle->copyPropertiesFrom(*source->inlineStyleDecl());
437     inlineStyle->setStrictParsing(source->inlineStyleDecl()->useStrictParsing());
438
439     setIsStyleAttributeValid(source->isStyleAttributeValid());
440     setIsSynchronizingStyleAttribute(source->isSynchronizingStyleAttribute());
441     
442     Element::copyNonAttributeProperties(sourceElement);
443 }
444
445 void StyledElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
446 {
447     if (!m_inlineStyleDecl)
448         return;
449     m_inlineStyleDecl->addSubresourceStyleURLs(urls);
450 }
451
452 }