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