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