webengine/osswebengine/WebCore/dom/StyledElement.cpp
changeset 0 dd21522fd290
child 13 10e98eab6f85
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /**
       
     2  * This file is part of the DOM implementation for KDE.
       
     3  *
       
     4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
       
     5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
       
     6  *           (C) 2001 Peter Kelly (pmk@post.com)
       
     7  *           (C) 2001 Dirk Mueller (mueller@kde.org)
       
     8  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
       
     9  *
       
    10  * This library is free software; you can redistribute it and/or
       
    11  * modify it under the terms of the GNU Library General Public
       
    12  * License as published by the Free Software Foundation; either
       
    13  * version 2 of the License, or (at your option) any later version.
       
    14  *
       
    15  * This library is distributed in the hope that it will be useful,
       
    16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    18  * Library General Public License for more details.
       
    19  *
       
    20  * You should have received a copy of the GNU Library General Public License
       
    21  * along with this library; see the file COPYING.LIB.  If not, write to
       
    22  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    23  * Boston, MA 02110-1301, USA.
       
    24  */
       
    25 
       
    26 #include "config.h"
       
    27 #include "HashMap.h"
       
    28 #include "StyledElement.h"
       
    29 
       
    30 #include "CSSStyleSelector.h"
       
    31 #include "CSSStyleSheet.h"
       
    32 #include "CSSValueKeywords.h"
       
    33 #include "Document.h"
       
    34 #include "HTMLNames.h"
       
    35 
       
    36 using namespace std;
       
    37 
       
    38 namespace WebCore {
       
    39 
       
    40 using namespace HTMLNames;
       
    41 
       
    42 struct MappedAttributeKey {
       
    43     uint16_t type;
       
    44     StringImpl* name;
       
    45     StringImpl* value;
       
    46     MappedAttributeKey(MappedAttributeEntry t = eNone, StringImpl* n = 0, StringImpl* v = 0)
       
    47         : type(t), name(n), value(v) { }
       
    48 
       
    49     static MappedAttributeKey sDeletedValueKey;
       
    50 };
       
    51 
       
    52 MappedAttributeKey MappedAttributeKey::sDeletedValueKey(eLastEntry);
       
    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 = true;
       
    59     static MappedAttributeKey* deletedValue() { return &(MappedAttributeKey::sDeletedValueKey); }
       
    60 };
       
    61 
       
    62 struct MappedAttributeHash {
       
    63     static unsigned hash(const MappedAttributeKey*);
       
    64     static bool equal(const MappedAttributeKey* a, const MappedAttributeKey* b) { return *a == *b; }
       
    65 };
       
    66 
       
    67 //typedef HashMap<MappedAttributeKey, CSSMappedAttributeDeclaration*, MappedAttributeHash, MappedAttributeKeyTraits> MappedAttributeDecls;
       
    68 typedef MappedAttributeKey* MappedAttributeKeyPtr;
       
    69 typedef HashMap<MappedAttributeKeyPtr, CSSMappedAttributeDeclaration*, MappedAttributeHash, MappedAttributeKeyTraits> MappedAttributeDecls;
       
    70 
       
    71 static MappedAttributeDecls* mappedAttributeDecls = 0;
       
    72 
       
    73 CSSMappedAttributeDeclaration* StyledElement::getMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr)
       
    74 {
       
    75     if (!mappedAttributeDecls)
       
    76         return 0;
       
    77     MappedAttributeKey* key = new MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl());
       
    78     CSSMappedAttributeDeclaration* value = mappedAttributeDecls->get( key );
       
    79     delete key;
       
    80     return value;
       
    81 }
       
    82 
       
    83 void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr, CSSMappedAttributeDeclaration* decl)
       
    84 {
       
    85     if (!mappedAttributeDecls)
       
    86         mappedAttributeDecls = new MappedAttributeDecls;
       
    87     mappedAttributeDecls->set(new MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl()), decl);
       
    88 }
       
    89 
       
    90 void StyledElement::removeMappedAttributeDecl(MappedAttributeEntry entryType,
       
    91                                                   const QualifiedName& attrName, const AtomicString& attrValue)
       
    92 {
       
    93     if (!mappedAttributeDecls)
       
    94         return;
       
    95     MappedAttributeKey* key = new MappedAttributeKey(entryType, attrName.localName().impl(), attrValue.impl());
       
    96     mappedAttributeDecls->remove(key);
       
    97     delete key;
       
    98 }
       
    99 
       
   100 void StyledElement::invalidateStyleAttribute()
       
   101 {
       
   102     m_isStyleAttributeValid = false;
       
   103 }
       
   104 
       
   105 void StyledElement::updateStyleAttributeIfNeeded() const
       
   106 {
       
   107     if (!m_isStyleAttributeValid) {
       
   108         m_isStyleAttributeValid = true;
       
   109         m_synchronizingStyleAttribute = true;
       
   110         if (m_inlineStyleDecl)
       
   111             const_cast<StyledElement*>(this)->setAttribute(styleAttr, m_inlineStyleDecl->cssText());
       
   112         m_synchronizingStyleAttribute = false;
       
   113     }
       
   114 }
       
   115 
       
   116 inline static bool isClassWhitespace(UChar c)
       
   117 {
       
   118     return c == ' ' || c == '\r' || c == '\n' || c == '\t';
       
   119 }
       
   120 
       
   121 StyledElement::StyledElement(const QualifiedName& name, Document *doc)
       
   122     : Element(name, doc)
       
   123 {
       
   124     m_isStyleAttributeValid = true;
       
   125     m_synchronizingStyleAttribute = false;
       
   126 }
       
   127 
       
   128 StyledElement::~StyledElement()
       
   129 {
       
   130     destroyInlineStyleDecl();
       
   131 }
       
   132 
       
   133 Attribute* StyledElement::createAttribute(const QualifiedName& name, StringImpl* value)
       
   134 {
       
   135     return new MappedAttribute(name, value);
       
   136 }
       
   137 
       
   138 void StyledElement::createInlineStyleDecl()
       
   139 {
       
   140     m_inlineStyleDecl = new CSSMutableStyleDeclaration;
       
   141     m_inlineStyleDecl->setParent(document()->elementSheet());
       
   142     m_inlineStyleDecl->setNode(this);
       
   143     m_inlineStyleDecl->setStrictParsing(!document()->inCompatMode());
       
   144 }
       
   145 
       
   146 void StyledElement::destroyInlineStyleDecl()
       
   147 {
       
   148     if (m_inlineStyleDecl) {
       
   149         m_inlineStyleDecl->setNode(0);
       
   150         m_inlineStyleDecl->setParent(0);
       
   151         m_inlineStyleDecl = 0;
       
   152     }
       
   153 }
       
   154 
       
   155 void StyledElement::attributeChanged(Attribute* attr, bool preserveDecls)
       
   156 {
       
   157     MappedAttribute* mappedAttr = static_cast<MappedAttribute*>(attr);
       
   158     if (mappedAttr->decl() && !preserveDecls) {
       
   159         mappedAttr->setDecl(0);
       
   160         setChanged();
       
   161         if (namedAttrMap)
       
   162             mappedAttributes()->declRemoved();
       
   163     }
       
   164 
       
   165     bool checkDecl = true;
       
   166     MappedAttributeEntry entry;
       
   167     bool needToParse = mapToEntry(attr->name(), entry);
       
   168     if (preserveDecls) {
       
   169         if (mappedAttr->decl()) {
       
   170             setChanged();
       
   171             if (namedAttrMap)
       
   172                 mappedAttributes()->declAdded();
       
   173             checkDecl = false;
       
   174         }
       
   175     }
       
   176     else if (!attr->isNull() && entry != eNone) {
       
   177         CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(entry, attr);
       
   178         if (decl) {
       
   179             mappedAttr->setDecl(decl);
       
   180             setChanged();
       
   181             if (namedAttrMap)
       
   182                 mappedAttributes()->declAdded();
       
   183             checkDecl = false;
       
   184         } else
       
   185             needToParse = true;
       
   186     }
       
   187 
       
   188     if (needToParse)
       
   189         parseMappedAttribute(mappedAttr);
       
   190 
       
   191     if (entry == eNone && ownerDocument()->styleSelector()->hasSelectorForAttribute(attr->name().localName()))
       
   192         setChanged();
       
   193 
       
   194     if (checkDecl && mappedAttr->decl()) {
       
   195         // Add the decl to the table in the appropriate spot.
       
   196         setMappedAttributeDecl(entry, attr, mappedAttr->decl());
       
   197         mappedAttr->decl()->setMappedState(entry, attr->name(), attr->value());
       
   198         mappedAttr->decl()->setParent(0);
       
   199         mappedAttr->decl()->setNode(0);
       
   200         if (namedAttrMap)
       
   201             mappedAttributes()->declAdded();
       
   202     }
       
   203 }
       
   204 
       
   205 bool StyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const
       
   206 {
       
   207     result = eNone;
       
   208     if (attrName == styleAttr)
       
   209         return !m_synchronizingStyleAttribute;
       
   210     return true;
       
   211 }
       
   212 
       
   213 void StyledElement::parseMappedAttribute(MappedAttribute *attr)
       
   214 {
       
   215     if (attr->name() == idAttr) {
       
   216         // unique id
       
   217         setHasID(!attr->isNull());
       
   218         if (namedAttrMap) {
       
   219             if (attr->isNull())
       
   220                 namedAttrMap->setID(nullAtom);
       
   221             else if (document()->inCompatMode() && !attr->value().impl()->isLower())
       
   222                 namedAttrMap->setID(AtomicString(attr->value().domString().lower()));
       
   223             else
       
   224                 namedAttrMap->setID(attr->value());
       
   225         }
       
   226         setChanged();
       
   227     } else if (attr->name() == classAttr) {
       
   228         // class
       
   229         setHasClass(!attr->isNull());
       
   230         if (namedAttrMap)
       
   231             mappedAttributes()->parseClassAttribute(attr->value());
       
   232         setChanged();
       
   233     } else if (attr->name() == styleAttr) {
       
   234         if (attr->isNull())
       
   235             destroyInlineStyleDecl();
       
   236         else
       
   237             getInlineStyleDecl()->parseDeclaration(attr->value());
       
   238         m_isStyleAttributeValid = true;
       
   239         setChanged();
       
   240     }
       
   241 }
       
   242 
       
   243 void StyledElement::createAttributeMap() const
       
   244 {
       
   245     namedAttrMap = new NamedMappedAttrMap(const_cast<StyledElement*>(this));
       
   246 }
       
   247 
       
   248 CSSMutableStyleDeclaration* StyledElement::getInlineStyleDecl()
       
   249 {
       
   250     if (!m_inlineStyleDecl)
       
   251         createInlineStyleDecl();
       
   252     return m_inlineStyleDecl.get();
       
   253 }
       
   254 
       
   255 CSSStyleDeclaration* StyledElement::style()
       
   256 {
       
   257     return getInlineStyleDecl();
       
   258 }
       
   259 
       
   260 CSSMutableStyleDeclaration* StyledElement::additionalAttributeStyleDecl()
       
   261 {
       
   262     return 0;
       
   263 }
       
   264 
       
   265 const AtomicStringList* StyledElement::getClassList() const
       
   266 {
       
   267     return namedAttrMap ? mappedAttributes()->getClassList() : 0;
       
   268 }
       
   269 
       
   270 static inline int toHex(UChar c) {
       
   271     return ((c >= '0' && c <= '9') ? (c - '0')
       
   272         : ((c >= 'a' && c <= 'f') ? (c - 'a' + 10)
       
   273         : (( c >= 'A' && c <= 'F') ? (c - 'A' + 10)
       
   274         : -1)));
       
   275 }
       
   276 
       
   277 void StyledElement::addCSSProperty(MappedAttribute* attr, int id, const String &value)
       
   278 {
       
   279     if (!attr->decl()) createMappedDecl(attr);
       
   280     attr->decl()->setProperty(id, value, false);
       
   281 }
       
   282 
       
   283 void StyledElement::addCSSProperty(MappedAttribute* attr, int id, int value)
       
   284 {
       
   285     if (!attr->decl()) createMappedDecl(attr);
       
   286     attr->decl()->setProperty(id, value, false);
       
   287 }
       
   288 
       
   289 void StyledElement::addCSSStringProperty(MappedAttribute* attr, int id, const String &value, CSSPrimitiveValue::UnitTypes type)
       
   290 {
       
   291     if (!attr->decl()) createMappedDecl(attr);
       
   292     attr->decl()->setStringProperty(id, value, type, false);
       
   293 }
       
   294 
       
   295 void StyledElement::addCSSImageProperty(MappedAttribute* attr, int id, const String &URL)
       
   296 {
       
   297     if (!attr->decl()) createMappedDecl(attr);
       
   298     attr->decl()->setImageProperty(id, URL, false);
       
   299 }
       
   300 
       
   301 void StyledElement::addCSSLength(MappedAttribute* attr, int id, const String &value)
       
   302 {
       
   303     // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct
       
   304     // length unit and make the appropriate parsed value.
       
   305     if (!attr->decl()) createMappedDecl(attr);
       
   306 
       
   307     // strip attribute garbage..
       
   308     StringImpl* v = value.impl();
       
   309     if (v) {
       
   310         unsigned int l = 0;
       
   311         
       
   312         while (l < v->length() && (*v)[l] <= ' ')
       
   313             l++;
       
   314         
       
   315         for (; l < v->length(); l++) {
       
   316             UChar cc = (*v)[l];
       
   317             if (cc > '9' || (cc < '0' && cc != '*' && cc != '%' && cc != '.'))
       
   318                 break;
       
   319         }
       
   320 
       
   321         if (l != v->length()) {
       
   322             attr->decl()->setLengthProperty(id, v->substring(0, l), false);
       
   323             return;
       
   324         }
       
   325     }
       
   326     
       
   327     attr->decl()->setLengthProperty(id, value, false);
       
   328 }
       
   329 
       
   330 /* color parsing that tries to match as close as possible IE 6. */
       
   331 void StyledElement::addCSSColor(MappedAttribute* attr, int id, const String &c)
       
   332 {
       
   333     // this is the only case no color gets applied in IE.
       
   334     if (!c.length())
       
   335         return;
       
   336 
       
   337     if (!attr->decl())
       
   338         createMappedDecl(attr);
       
   339     
       
   340     if (attr->decl()->setProperty(id, c, false))
       
   341         return;
       
   342     
       
   343     String color = c.copy();
       
   344     // not something that fits the specs.
       
   345     
       
   346     // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value
       
   347     // out of everyhting you put in. The algorithm is experimentally determined, but seems to work for all test cases I have.
       
   348     
       
   349     // the length of the color value is rounded up to the next
       
   350     // multiple of 3. each part of the rgb triple then gets one third
       
   351     // of the length.
       
   352     //
       
   353     // Each triplet is parsed byte by byte, mapping
       
   354     // each number to a hex value (0-9a-fA-F to their values
       
   355     // everything else to 0).
       
   356     //
       
   357     // The highest non zero digit in all triplets is remembered, and
       
   358     // used as a normalization point to normalize to values between 0
       
   359     // and 255.
       
   360     
       
   361     if (color.lower() != "transparent") {
       
   362         if (color[0] == '#')
       
   363             color.remove(0, 1);
       
   364         int basicLength = (color.length() + 2) / 3;
       
   365         if (basicLength > 1) {
       
   366             // IE ignores colors with three digits or less
       
   367             int colors[3] = { 0, 0, 0 };
       
   368             int component = 0;
       
   369             int pos = 0;
       
   370             int maxDigit = basicLength-1;
       
   371             while (component < 3) {
       
   372                 // search forward for digits in the string
       
   373                 int numDigits = 0;
       
   374                 while (pos < (int)color.length() && numDigits < basicLength) {
       
   375                     int hex = toHex(color[pos]);
       
   376                     colors[component] = (colors[component] << 4);
       
   377                     if (hex > 0) {
       
   378                         colors[component] += hex;
       
   379                         maxDigit = min(maxDigit, numDigits);
       
   380                     }
       
   381                     numDigits++;
       
   382                     pos++;
       
   383                 }
       
   384                 while (numDigits++ < basicLength)
       
   385                     colors[component] <<= 4;
       
   386                 component++;
       
   387             }
       
   388             maxDigit = basicLength - maxDigit;
       
   389             
       
   390             // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits
       
   391             maxDigit -= 2;
       
   392             colors[0] >>= 4*maxDigit;
       
   393             colors[1] >>= 4*maxDigit;
       
   394             colors[2] >>= 4*maxDigit;
       
   395             // ASSERT(colors[0] < 0x100 && colors[1] < 0x100 && colors[2] < 0x100);
       
   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, CSS_VAL_BLACK, false);
       
   403 }
       
   404 
       
   405 void StyledElement::createMappedDecl(MappedAttribute* attr)
       
   406 {
       
   407     CSSMappedAttributeDeclaration* decl = new CSSMappedAttributeDeclaration(0);
       
   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 // Golden ratio - arbitrary start value to avoid mapping all 0's to all 0's
       
   415 // or anything like that.
       
   416 const unsigned PHI = 0x9e3779b9U;
       
   417 
       
   418 // Paul Hsieh's SuperFastHash
       
   419 // http://www.azillionmonkeys.com/qed/hash.html
       
   420 unsigned MappedAttributeHash::hash(const MappedAttributeKey* key_)
       
   421 {
       
   422     const MappedAttributeKey& key = *key_;
       
   423     uint32_t hash = PHI;
       
   424     uint32_t tmp;
       
   425 
       
   426     const uint16_t* p;
       
   427 
       
   428     p = reinterpret_cast<const uint16_t*>(&key.name);
       
   429     hash += p[0];
       
   430     tmp = (p[1] << 11) ^ hash;
       
   431     hash = (hash << 16) ^ tmp;
       
   432     hash += hash >> 11;
       
   433     ASSERT(sizeof(key.name) == 4 || sizeof(key.name) == 8);
       
   434     if (sizeof(key.name) == 8) {
       
   435         p += 2;
       
   436         hash += p[0];
       
   437         tmp = (p[1] << 11) ^ hash;
       
   438         hash = (hash << 16) ^ tmp;
       
   439         hash += hash >> 11;
       
   440     }
       
   441 
       
   442     p = reinterpret_cast<const uint16_t*>(&key.value);
       
   443     hash += p[0];
       
   444     tmp = (p[1] << 11) ^ hash;
       
   445     hash = (hash << 16) ^ tmp;
       
   446     hash += hash >> 11;
       
   447     ASSERT(sizeof(key.value) == 4 || sizeof(key.value) == 8);
       
   448     if (sizeof(key.value) == 8) {
       
   449         p += 2;
       
   450         hash += p[0];
       
   451         tmp = (p[1] << 11) ^ hash;
       
   452         hash = (hash << 16) ^ tmp;
       
   453         hash += hash >> 11;
       
   454     }
       
   455 
       
   456     // Handle end case
       
   457     hash += key.type;
       
   458     hash ^= hash << 11;
       
   459     hash += hash >> 17;
       
   460 
       
   461     // Force "avalanching" of final 127 bits
       
   462     hash ^= hash << 3;
       
   463     hash += hash >> 5;
       
   464     hash ^= hash << 2;
       
   465     hash += hash >> 15;
       
   466     hash ^= hash << 10;
       
   467 
       
   468     // This avoids ever returning a hash code of 0, since that is used to
       
   469     // signal "hash not computed yet", using a value that is likely to be
       
   470     // effectively the same as 0 when the low bits are masked
       
   471     if (hash == 0)
       
   472         hash = 0x80000000;
       
   473 
       
   474     return hash;
       
   475 }
       
   476 
       
   477 void StyledElement::copyNonAttributeProperties(const Element *sourceElement)
       
   478 {
       
   479     const StyledElement* source = static_cast<const StyledElement*>(sourceElement);
       
   480     if (!source->m_inlineStyleDecl)
       
   481         return;
       
   482 
       
   483     *getInlineStyleDecl() = *source->m_inlineStyleDecl;
       
   484     m_isStyleAttributeValid = source->m_isStyleAttributeValid;
       
   485     m_synchronizingStyleAttribute = source->m_synchronizingStyleAttribute;
       
   486     
       
   487     Element::copyNonAttributeProperties(sourceElement);
       
   488 }
       
   489 
       
   490 }