• 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, 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 "ClassList.h"
33 #include "DOMTokenList.h"
34 #include "Document.h"
35 #include "HTMLNames.h"
36 #include "HTMLParserIdioms.h"
37 #include <wtf/HashFunctions.h>
38 
39 using namespace std;
40 
41 namespace WebCore {
42 
43 using namespace HTMLNames;
44 
45 struct MappedAttributeKey {
46     uint16_t type;
47     StringImpl* name;
48     StringImpl* value;
MappedAttributeKeyWebCore::MappedAttributeKey49     MappedAttributeKey(MappedAttributeEntry t = eNone, StringImpl* n = 0, StringImpl* v = 0)
50         : type(t), name(n), value(v) { }
51 };
52 
operator ==(const MappedAttributeKey & a,const MappedAttributeKey & b)53 static inline bool operator==(const MappedAttributeKey& a, const MappedAttributeKey& b)
54     { return a.type == b.type && a.name == b.name && a.value == b.value; }
55 
56 struct MappedAttributeKeyTraits : WTF::GenericHashTraits<MappedAttributeKey> {
57     static const bool emptyValueIsZero = true;
58     static const bool needsDestruction = false;
constructDeletedValueWebCore::MappedAttributeKeyTraits59     static void constructDeletedValue(MappedAttributeKey& slot) { slot.type = eLastEntry; }
isDeletedValueWebCore::MappedAttributeKeyTraits60     static bool isDeletedValue(const MappedAttributeKey& value) { return value.type == eLastEntry; }
61 };
62 
63 struct MappedAttributeHash {
64     static unsigned hash(const MappedAttributeKey&);
equalWebCore::MappedAttributeHash65     static bool equal(const MappedAttributeKey& a, const MappedAttributeKey& b) { return a == b; }
66     static const bool safeToCompareToEmptyOrDeleted = true;
67 };
68 
69 typedef HashMap<MappedAttributeKey, CSSMappedAttributeDeclaration*, MappedAttributeHash, MappedAttributeKeyTraits> MappedAttributeDecls;
70 
71 static MappedAttributeDecls* mappedAttributeDecls = 0;
72 
getMappedAttributeDecl(MappedAttributeEntry entryType,Attribute * attr)73 CSSMappedAttributeDeclaration* StyledElement::getMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr)
74 {
75     if (!mappedAttributeDecls)
76         return 0;
77     return mappedAttributeDecls->get(MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl()));
78 }
79 
getMappedAttributeDecl(MappedAttributeEntry type,const QualifiedName & name,const AtomicString & value)80 CSSMappedAttributeDeclaration* StyledElement::getMappedAttributeDecl(MappedAttributeEntry type, const QualifiedName& name, const AtomicString& value)
81 {
82     if (!mappedAttributeDecls)
83         return 0;
84     return mappedAttributeDecls->get(MappedAttributeKey(type, name.localName().impl(), value.impl()));
85 }
86 
setMappedAttributeDecl(MappedAttributeEntry entryType,Attribute * attr,CSSMappedAttributeDeclaration * decl)87 void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr, CSSMappedAttributeDeclaration* decl)
88 {
89     if (!mappedAttributeDecls)
90         mappedAttributeDecls = new MappedAttributeDecls;
91     mappedAttributeDecls->set(MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl()), decl);
92 }
93 
setMappedAttributeDecl(MappedAttributeEntry entryType,const QualifiedName & name,const AtomicString & value,CSSMappedAttributeDeclaration * decl)94 void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType, const QualifiedName& name, const AtomicString& value, CSSMappedAttributeDeclaration* decl)
95 {
96     if (!mappedAttributeDecls)
97         mappedAttributeDecls = new MappedAttributeDecls;
98     mappedAttributeDecls->set(MappedAttributeKey(entryType, name.localName().impl(), value.impl()), decl);
99 }
100 
removeMappedAttributeDecl(MappedAttributeEntry entryType,const QualifiedName & attrName,const AtomicString & attrValue)101 void StyledElement::removeMappedAttributeDecl(MappedAttributeEntry entryType, const QualifiedName& attrName, const AtomicString& attrValue)
102 {
103     if (!mappedAttributeDecls)
104         return;
105     mappedAttributeDecls->remove(MappedAttributeKey(entryType, attrName.localName().impl(), attrValue.impl()));
106 }
107 
updateStyleAttribute() const108 void StyledElement::updateStyleAttribute() const
109 {
110     ASSERT(!isStyleAttributeValid());
111     setIsStyleAttributeValid();
112     setIsSynchronizingStyleAttribute();
113     if (m_inlineStyleDecl)
114         const_cast<StyledElement*>(this)->setAttribute(styleAttr, m_inlineStyleDecl->cssText());
115     clearIsSynchronizingStyleAttribute();
116 }
117 
~StyledElement()118 StyledElement::~StyledElement()
119 {
120     destroyInlineStyleDecl();
121 }
122 
createAttribute(const QualifiedName & name,const AtomicString & value)123 PassRefPtr<Attribute> StyledElement::createAttribute(const QualifiedName& name, const AtomicString& value)
124 {
125     return Attribute::createMapped(name, value);
126 }
127 
createInlineStyleDecl()128 void StyledElement::createInlineStyleDecl()
129 {
130     m_inlineStyleDecl = CSSMutableStyleDeclaration::create();
131     m_inlineStyleDecl->setParent(document()->elementSheet());
132     m_inlineStyleDecl->setNode(this);
133     m_inlineStyleDecl->setStrictParsing(isHTMLElement() && !document()->inQuirksMode());
134 }
135 
destroyInlineStyleDecl()136 void StyledElement::destroyInlineStyleDecl()
137 {
138     if (m_inlineStyleDecl) {
139         m_inlineStyleDecl->setNode(0);
140         m_inlineStyleDecl->setParent(0);
141         m_inlineStyleDecl = 0;
142     }
143 }
144 
attributeChanged(Attribute * attr,bool preserveDecls)145 void StyledElement::attributeChanged(Attribute* attr, bool preserveDecls)
146 {
147     if (!attr->isMappedAttribute()) {
148         Element::attributeChanged(attr, preserveDecls);
149         return;
150     }
151 
152     if (attr->decl() && !preserveDecls) {
153         attr->setDecl(0);
154         setNeedsStyleRecalc();
155         if (attributeMap())
156             attributeMap()->declRemoved();
157     }
158 
159     bool checkDecl = true;
160     MappedAttributeEntry entry;
161     bool needToParse = mapToEntry(attr->name(), entry);
162     if (preserveDecls) {
163         if (attr->decl()) {
164             setNeedsStyleRecalc();
165             if (attributeMap())
166                 attributeMap()->declAdded();
167             checkDecl = false;
168         }
169     } else if (!attr->isNull() && entry != eNone) {
170         CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(entry, attr);
171         if (decl) {
172             attr->setDecl(decl);
173             setNeedsStyleRecalc();
174             if (attributeMap())
175                 attributeMap()->declAdded();
176             checkDecl = false;
177         } else
178             needToParse = true;
179     }
180 
181     // parseMappedAttribute() might create a CSSMappedAttributeDeclaration on the attribute.
182     // Normally we would be concerned about reseting the parent of those declarations in StyledElement::didMoveToNewOwnerDocument().
183     // But currently we always clear its parent and node below when adding it to the decl table.
184     // If that changes for some reason moving between documents will be buggy.
185     // webarchive/adopt-attribute-styled-node-webarchive.html should catch any resulting crashes.
186     if (needToParse)
187         parseMappedAttribute(attr);
188 
189     if (entry == eNone)
190         recalcStyleIfNeededAfterAttributeChanged(attr);
191 
192     if (checkDecl && attr->decl()) {
193         // Add the decl to the table in the appropriate spot.
194         setMappedAttributeDecl(entry, attr, attr->decl());
195         attr->decl()->setMappedState(entry, attr->name(), attr->value());
196         attr->decl()->setParent(0);
197         attr->decl()->setNode(0);
198         if (attributeMap())
199             attributeMap()->declAdded();
200     }
201 
202     updateAfterAttributeChanged(attr);
203 }
204 
mapToEntry(const QualifiedName & attrName,MappedAttributeEntry & result) const205 bool StyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
206 {
207     result = eNone;
208     if (attrName == styleAttr)
209         return !isSynchronizingStyleAttribute();
210     return true;
211 }
212 
classAttributeChanged(const AtomicString & newClassString)213 void StyledElement::classAttributeChanged(const AtomicString& newClassString)
214 {
215     const UChar* characters = newClassString.characters();
216     unsigned length = newClassString.length();
217     unsigned i;
218     for (i = 0; i < length; ++i) {
219         if (isNotHTMLSpace(characters[i]))
220             break;
221     }
222     bool hasClass = i < length;
223     setHasClass(hasClass);
224     if (hasClass) {
225         attributes()->setClass(newClassString);
226         if (DOMTokenList* classList = optionalClassList())
227             static_cast<ClassList*>(classList)->reset(newClassString);
228     } else if (attributeMap())
229         attributeMap()->clearClass();
230     setNeedsStyleRecalc();
231     dispatchSubtreeModifiedEvent();
232 }
233 
parseMappedAttribute(Attribute * attr)234 void StyledElement::parseMappedAttribute(Attribute* attr)
235 {
236     if (isIdAttributeName(attr->name()))
237         idAttributeChanged(attr);
238     else if (attr->name() == classAttr)
239         classAttributeChanged(attr->value());
240     else if (attr->name() == styleAttr) {
241         if (attr->isNull())
242             destroyInlineStyleDecl();
243         else
244             getInlineStyleDecl()->parseDeclaration(attr->value());
245         setIsStyleAttributeValid();
246         setNeedsStyleRecalc();
247     }
248 }
249 
getInlineStyleDecl()250 CSSMutableStyleDeclaration* StyledElement::getInlineStyleDecl()
251 {
252     if (!m_inlineStyleDecl)
253         createInlineStyleDecl();
254     return m_inlineStyleDecl.get();
255 }
256 
style()257 CSSStyleDeclaration* StyledElement::style()
258 {
259     return getInlineStyleDecl();
260 }
261 
addCSSProperty(Attribute * attribute,int id,const String & value)262 void StyledElement::addCSSProperty(Attribute* attribute, int id, const String &value)
263 {
264     if (!attribute->decl())
265         createMappedDecl(attribute);
266     attribute->decl()->setProperty(id, value, false);
267 }
268 
addCSSProperty(Attribute * attribute,int id,int value)269 void StyledElement::addCSSProperty(Attribute* attribute, int id, int value)
270 {
271     if (!attribute->decl())
272         createMappedDecl(attribute);
273     attribute->decl()->setProperty(id, value, false);
274 }
275 
addCSSImageProperty(Attribute * attribute,int id,const String & url)276 void StyledElement::addCSSImageProperty(Attribute* attribute, int id, const String& url)
277 {
278     if (!attribute->decl())
279         createMappedDecl(attribute);
280     attribute->decl()->setImageProperty(id, url, false);
281 }
282 
addCSSLength(Attribute * attr,int id,const String & value)283 void StyledElement::addCSSLength(Attribute* attr, int id, const String &value)
284 {
285     // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
286     // length unit and make the appropriate parsed value.
287     if (!attr->decl())
288         createMappedDecl(attr);
289 
290     // strip attribute garbage..
291     StringImpl* v = value.impl();
292     if (v) {
293         unsigned int l = 0;
294 
295         while (l < v->length() && (*v)[l] <= ' ')
296             l++;
297 
298         for (; l < v->length(); l++) {
299             UChar cc = (*v)[l];
300             if (cc > '9')
301                 break;
302             if (cc < '0') {
303                 if (cc == '%' || cc == '*')
304                     l++;
305                 if (cc != '.')
306                     break;
307             }
308         }
309 
310         if (l != v->length()) {
311             attr->decl()->setLengthProperty(id, v->substring(0, l), false);
312             return;
313         }
314     }
315 
316     attr->decl()->setLengthProperty(id, value, false);
317 }
318 
319 /* color parsing that tries to match as close as possible IE 6. */
addCSSColor(Attribute * attr,int id,const String & c)320 void StyledElement::addCSSColor(Attribute* attr, int id, const String& c)
321 {
322     // this is the only case no color gets applied in IE.
323     if (!c.length())
324         return;
325 
326     if (!attr->decl())
327         createMappedDecl(attr);
328 
329     if (attr->decl()->setProperty(id, c, false))
330         return;
331 
332     String color = c;
333     // not something that fits the specs.
334 
335     // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value
336     // out of everything you put in. The algorithm is experimentally determined, but seems to work for all test cases I have.
337 
338     // the length of the color value is rounded up to the next
339     // multiple of 3. each part of the rgb triple then gets one third
340     // of the length.
341     //
342     // Each triplet is parsed byte by byte, mapping
343     // each number to a hex value (0-9a-fA-F to their values
344     // everything else to 0).
345     //
346     // The highest non zero digit in all triplets is remembered, and
347     // used as a normalization point to normalize to values between 0
348     // and 255.
349 
350     if (!equalIgnoringCase(color, "transparent")) {
351         if (color[0] == '#')
352             color.remove(0, 1);
353         int basicLength = (color.length() + 2) / 3;
354         if (basicLength > 1) {
355             // IE ignores colors with three digits or less
356             int colors[3] = { 0, 0, 0 };
357             int component = 0;
358             int pos = 0;
359             int maxDigit = basicLength-1;
360             while (component < 3) {
361                 // search forward for digits in the string
362                 int numDigits = 0;
363                 while (pos < (int)color.length() && numDigits < basicLength) {
364                     colors[component] <<= 4;
365                     if (isASCIIHexDigit(color[pos])) {
366                         colors[component] += toASCIIHexValue(color[pos]);
367                         maxDigit = min(maxDigit, numDigits);
368                     }
369                     numDigits++;
370                     pos++;
371                 }
372                 while (numDigits++ < basicLength)
373                     colors[component] <<= 4;
374                 component++;
375             }
376             maxDigit = basicLength - maxDigit;
377 
378             // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits
379             maxDigit -= 2;
380             colors[0] >>= 4 * maxDigit;
381             colors[1] >>= 4 * maxDigit;
382             colors[2] >>= 4 * maxDigit;
383 
384             color = String::format("#%02x%02x%02x", colors[0], colors[1], colors[2]);
385             if (attr->decl()->setProperty(id, color, false))
386                 return;
387         }
388     }
389     attr->decl()->setProperty(id, CSSValueBlack, false);
390 }
391 
createMappedDecl(Attribute * attr)392 void StyledElement::createMappedDecl(Attribute* attr)
393 {
394     RefPtr<CSSMappedAttributeDeclaration> decl = CSSMappedAttributeDeclaration::create();
395     attr->setDecl(decl);
396     decl->setParent(document()->elementSheet());
397     decl->setNode(this);
398     decl->setStrictParsing(false); // Mapped attributes are just always quirky.
399 }
400 
hash(const MappedAttributeKey & key)401 unsigned MappedAttributeHash::hash(const MappedAttributeKey& key)
402 {
403     COMPILE_ASSERT(sizeof(key.name) == 4 || sizeof(key.name) == 8, key_name_size);
404     COMPILE_ASSERT(sizeof(key.value) == 4 || sizeof(key.value) == 8, key_value_size);
405 
406     StringHasher hasher;
407     const UChar* data;
408 
409     data = reinterpret_cast<const UChar*>(&key.name);
410     hasher.addCharacters(data[0], data[1]);
411     if (sizeof(key.name) == 8)
412         hasher.addCharacters(data[2], data[3]);
413 
414     data = reinterpret_cast<const UChar*>(&key.value);
415     hasher.addCharacters(data[0], data[1]);
416     if (sizeof(key.value) == 8)
417         hasher.addCharacters(data[2], data[3]);
418 
419     return hasher.hash();
420 }
421 
copyNonAttributeProperties(const Element * sourceElement)422 void StyledElement::copyNonAttributeProperties(const Element *sourceElement)
423 {
424     const StyledElement* source = static_cast<const StyledElement*>(sourceElement);
425     if (!source->m_inlineStyleDecl)
426         return;
427 
428     *getInlineStyleDecl() = *source->m_inlineStyleDecl;
429     setIsStyleAttributeValid(source->isStyleAttributeValid());
430     setIsSynchronizingStyleAttribute(source->isSynchronizingStyleAttribute());
431 
432     Element::copyNonAttributeProperties(sourceElement);
433 }
434 
addSubresourceAttributeURLs(ListHashSet<KURL> & urls) const435 void StyledElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
436 {
437     if (CSSMutableStyleDeclaration* style = inlineStyleDecl())
438         style->addSubresourceStyleURLs(urls);
439 }
440 
441 
didMoveToNewOwnerDocument()442 void StyledElement::didMoveToNewOwnerDocument()
443 {
444     if (m_inlineStyleDecl)
445         m_inlineStyleDecl->setParent(document()->elementSheet());
446 
447     Element::didMoveToNewOwnerDocument();
448 }
449 
450 }
451