diff options
author | Steve Block <steveblock@google.com> | 2011-05-06 11:45:16 +0100 |
---|---|---|
committer | Steve Block <steveblock@google.com> | 2011-05-12 13:44:10 +0100 |
commit | cad810f21b803229eb11403f9209855525a25d57 (patch) | |
tree | 29a6fd0279be608e0fe9ffe9841f722f0f4e4269 /Source/WebCore/dom/StyledElement.cpp | |
parent | 121b0cf4517156d0ac5111caf9830c51b69bae8f (diff) | |
download | external_webkit-cad810f21b803229eb11403f9209855525a25d57.zip external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.gz external_webkit-cad810f21b803229eb11403f9209855525a25d57.tar.bz2 |
Merge WebKit at r75315: Initial merge by git.
Change-Id: I570314b346ce101c935ed22a626b48c2af266b84
Diffstat (limited to 'Source/WebCore/dom/StyledElement.cpp')
-rw-r--r-- | Source/WebCore/dom/StyledElement.cpp | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/Source/WebCore/dom/StyledElement.cpp b/Source/WebCore/dom/StyledElement.cpp new file mode 100644 index 0000000..9a7c9c8 --- /dev/null +++ b/Source/WebCore/dom/StyledElement.cpp @@ -0,0 +1,458 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2001 Peter Kelly (pmk@post.com) + * (C) 2001 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2008, 2010 Apple Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include "config.h" +#include "StyledElement.h" + +#include "Attribute.h" +#include "ClassList.h" +#include "CSSStyleSelector.h" +#include "CSSStyleSheet.h" +#include "CSSValueKeywords.h" +#include "DOMTokenList.h" +#include "Document.h" +#include "HTMLNames.h" +#include "HTMLParserIdioms.h" +#include <wtf/HashFunctions.h> + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +struct MappedAttributeKey { + uint16_t type; + StringImpl* name; + StringImpl* value; + MappedAttributeKey(MappedAttributeEntry t = eNone, StringImpl* n = 0, StringImpl* v = 0) + : type(t), name(n), value(v) { } +}; + +static inline bool operator==(const MappedAttributeKey& a, const MappedAttributeKey& b) + { return a.type == b.type && a.name == b.name && a.value == b.value; } + +struct MappedAttributeKeyTraits : WTF::GenericHashTraits<MappedAttributeKey> { + static const bool emptyValueIsZero = true; + static const bool needsDestruction = false; + static void constructDeletedValue(MappedAttributeKey& slot) { slot.type = eLastEntry; } + static bool isDeletedValue(const MappedAttributeKey& value) { return value.type == eLastEntry; } +}; + +struct MappedAttributeHash { + static unsigned hash(const MappedAttributeKey&); + static bool equal(const MappedAttributeKey& a, const MappedAttributeKey& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; +}; + +typedef HashMap<MappedAttributeKey, CSSMappedAttributeDeclaration*, MappedAttributeHash, MappedAttributeKeyTraits> MappedAttributeDecls; + +static MappedAttributeDecls* mappedAttributeDecls = 0; + +CSSMappedAttributeDeclaration* StyledElement::getMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr) +{ + if (!mappedAttributeDecls) + return 0; + return mappedAttributeDecls->get(MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl())); +} + +CSSMappedAttributeDeclaration* StyledElement::getMappedAttributeDecl(MappedAttributeEntry type, const QualifiedName& name, const AtomicString& value) +{ + if (!mappedAttributeDecls) + return 0; + return mappedAttributeDecls->get(MappedAttributeKey(type, name.localName().impl(), value.impl())); +} + +void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType, Attribute* attr, CSSMappedAttributeDeclaration* decl) +{ + if (!mappedAttributeDecls) + mappedAttributeDecls = new MappedAttributeDecls; + mappedAttributeDecls->set(MappedAttributeKey(entryType, attr->name().localName().impl(), attr->value().impl()), decl); +} + +void StyledElement::setMappedAttributeDecl(MappedAttributeEntry entryType, const QualifiedName& name, const AtomicString& value, CSSMappedAttributeDeclaration* decl) +{ + if (!mappedAttributeDecls) + mappedAttributeDecls = new MappedAttributeDecls; + mappedAttributeDecls->set(MappedAttributeKey(entryType, name.localName().impl(), value.impl()), decl); +} + +void StyledElement::removeMappedAttributeDecl(MappedAttributeEntry entryType, const QualifiedName& attrName, const AtomicString& attrValue) +{ + if (!mappedAttributeDecls) + return; + mappedAttributeDecls->remove(MappedAttributeKey(entryType, attrName.localName().impl(), attrValue.impl())); +} + +void StyledElement::updateStyleAttribute() const +{ + ASSERT(!isStyleAttributeValid()); + setIsStyleAttributeValid(); + setIsSynchronizingStyleAttribute(); + if (m_inlineStyleDecl) + const_cast<StyledElement*>(this)->setAttribute(styleAttr, m_inlineStyleDecl->cssText()); + clearIsSynchronizingStyleAttribute(); +} + +StyledElement::~StyledElement() +{ + destroyInlineStyleDecl(); +} + +PassRefPtr<Attribute> StyledElement::createAttribute(const QualifiedName& name, const AtomicString& value) +{ + return Attribute::createMapped(name, value); +} + +void StyledElement::createInlineStyleDecl() +{ + m_inlineStyleDecl = CSSMutableStyleDeclaration::create(); + m_inlineStyleDecl->setParent(document()->elementSheet()); + m_inlineStyleDecl->setNode(this); + m_inlineStyleDecl->setStrictParsing(isHTMLElement() && !document()->inQuirksMode()); +} + +void StyledElement::destroyInlineStyleDecl() +{ + if (m_inlineStyleDecl) { + m_inlineStyleDecl->setNode(0); + m_inlineStyleDecl->setParent(0); + m_inlineStyleDecl = 0; + } +} + +void StyledElement::attributeChanged(Attribute* attr, bool preserveDecls) +{ + if (!attr->isMappedAttribute()) { + Element::attributeChanged(attr, preserveDecls); + return; + } + + if (attr->decl() && !preserveDecls) { + attr->setDecl(0); + setNeedsStyleRecalc(); + if (attributeMap()) + attributeMap()->declRemoved(); + } + + bool checkDecl = true; + MappedAttributeEntry entry; + bool needToParse = mapToEntry(attr->name(), entry); + if (preserveDecls) { + if (attr->decl()) { + setNeedsStyleRecalc(); + if (attributeMap()) + attributeMap()->declAdded(); + checkDecl = false; + } + } else if (!attr->isNull() && entry != eNone) { + CSSMappedAttributeDeclaration* decl = getMappedAttributeDecl(entry, attr); + if (decl) { + attr->setDecl(decl); + setNeedsStyleRecalc(); + if (attributeMap()) + attributeMap()->declAdded(); + checkDecl = false; + } else + needToParse = true; + } + + // parseMappedAttribute() might create a CSSMappedAttributeDeclaration on the attribute. + // Normally we would be concerned about reseting the parent of those declarations in StyledElement::didMoveToNewOwnerDocument(). + // But currently we always clear its parent and node below when adding it to the decl table. + // If that changes for some reason moving between documents will be buggy. + // webarchive/adopt-attribute-styled-node-webarchive.html should catch any resulting crashes. + if (needToParse) + parseMappedAttribute(attr); + + if (entry == eNone) + recalcStyleIfNeededAfterAttributeChanged(attr); + + if (checkDecl && attr->decl()) { + // Add the decl to the table in the appropriate spot. + setMappedAttributeDecl(entry, attr, attr->decl()); + attr->decl()->setMappedState(entry, attr->name(), attr->value()); + attr->decl()->setParent(0); + attr->decl()->setNode(0); + if (attributeMap()) + attributeMap()->declAdded(); + } + + updateAfterAttributeChanged(attr); +} + +bool StyledElement::mapToEntry(const QualifiedName& attrName, MappedAttributeEntry& result) const +{ + result = eNone; + if (attrName == styleAttr) + return !isSynchronizingStyleAttribute(); + return true; +} + +void StyledElement::classAttributeChanged(const AtomicString& newClassString) +{ + const UChar* characters = newClassString.characters(); + unsigned length = newClassString.length(); + unsigned i; + for (i = 0; i < length; ++i) { + if (isNotHTMLSpace(characters[i])) + break; + } + bool hasClass = i < length; + setHasClass(hasClass); + if (hasClass) { + attributes()->setClass(newClassString); + if (DOMTokenList* classList = optionalClassList()) + static_cast<ClassList*>(classList)->reset(newClassString); + } else if (attributeMap()) + attributeMap()->clearClass(); + setNeedsStyleRecalc(); + dispatchSubtreeModifiedEvent(); +} + +void StyledElement::parseMappedAttribute(Attribute* attr) +{ + if (isIdAttributeName(attr->name())) { + setHasID(!attr->isNull()); + if (attributeMap()) { + if (attr->isNull()) + attributeMap()->setIdForStyleResolution(nullAtom); + else if (document()->inQuirksMode()) + attributeMap()->setIdForStyleResolution(attr->value().lower()); + else + attributeMap()->setIdForStyleResolution(attr->value()); + } + setNeedsStyleRecalc(); + } else if (attr->name() == classAttr) + classAttributeChanged(attr->value()); + else if (attr->name() == styleAttr) { + if (attr->isNull()) + destroyInlineStyleDecl(); + else + getInlineStyleDecl()->parseDeclaration(attr->value()); + setIsStyleAttributeValid(); + setNeedsStyleRecalc(); + } +} + +CSSMutableStyleDeclaration* StyledElement::getInlineStyleDecl() +{ + if (!m_inlineStyleDecl) + createInlineStyleDecl(); + return m_inlineStyleDecl.get(); +} + +CSSStyleDeclaration* StyledElement::style() +{ + return getInlineStyleDecl(); +} + +void StyledElement::addCSSProperty(Attribute* attribute, int id, const String &value) +{ + if (!attribute->decl()) + createMappedDecl(attribute); + attribute->decl()->setProperty(id, value, false); +} + +void StyledElement::addCSSProperty(Attribute* attribute, int id, int value) +{ + if (!attribute->decl()) + createMappedDecl(attribute); + attribute->decl()->setProperty(id, value, false); +} + +void StyledElement::addCSSImageProperty(Attribute* attribute, int id, const String& url) +{ + if (!attribute->decl()) + createMappedDecl(attribute); + attribute->decl()->setImageProperty(id, url, false); +} + +void StyledElement::addCSSLength(Attribute* attr, int id, const String &value) +{ + // FIXME: This function should not spin up the CSS parser, but should instead just figure out the correct + // length unit and make the appropriate parsed value. + if (!attr->decl()) + createMappedDecl(attr); + + // strip attribute garbage.. + StringImpl* v = value.impl(); + if (v) { + unsigned int l = 0; + + while (l < v->length() && (*v)[l] <= ' ') + l++; + + for (; l < v->length(); l++) { + UChar cc = (*v)[l]; + if (cc > '9') + break; + if (cc < '0') { + if (cc == '%' || cc == '*') + l++; + if (cc != '.') + break; + } + } + + if (l != v->length()) { + attr->decl()->setLengthProperty(id, v->substring(0, l), false); + return; + } + } + + attr->decl()->setLengthProperty(id, value, false); +} + +/* color parsing that tries to match as close as possible IE 6. */ +void StyledElement::addCSSColor(Attribute* attr, int id, const String& c) +{ + // this is the only case no color gets applied in IE. + if (!c.length()) + return; + + if (!attr->decl()) + createMappedDecl(attr); + + if (attr->decl()->setProperty(id, c, false)) + return; + + String color = c; + // not something that fits the specs. + + // we're emulating IEs color parser here. It maps transparent to black, otherwise it tries to build a rgb value + // out of everything you put in. The algorithm is experimentally determined, but seems to work for all test cases I have. + + // the length of the color value is rounded up to the next + // multiple of 3. each part of the rgb triple then gets one third + // of the length. + // + // Each triplet is parsed byte by byte, mapping + // each number to a hex value (0-9a-fA-F to their values + // everything else to 0). + // + // The highest non zero digit in all triplets is remembered, and + // used as a normalization point to normalize to values between 0 + // and 255. + + if (!equalIgnoringCase(color, "transparent")) { + if (color[0] == '#') + color.remove(0, 1); + int basicLength = (color.length() + 2) / 3; + if (basicLength > 1) { + // IE ignores colors with three digits or less + int colors[3] = { 0, 0, 0 }; + int component = 0; + int pos = 0; + int maxDigit = basicLength-1; + while (component < 3) { + // search forward for digits in the string + int numDigits = 0; + while (pos < (int)color.length() && numDigits < basicLength) { + colors[component] <<= 4; + if (isASCIIHexDigit(color[pos])) { + colors[component] += toASCIIHexValue(color[pos]); + maxDigit = min(maxDigit, numDigits); + } + numDigits++; + pos++; + } + while (numDigits++ < basicLength) + colors[component] <<= 4; + component++; + } + maxDigit = basicLength - maxDigit; + + // normalize to 00-ff. The highest filled digit counts, minimum is 2 digits + maxDigit -= 2; + colors[0] >>= 4 * maxDigit; + colors[1] >>= 4 * maxDigit; + colors[2] >>= 4 * maxDigit; + + color = String::format("#%02x%02x%02x", colors[0], colors[1], colors[2]); + if (attr->decl()->setProperty(id, color, false)) + return; + } + } + attr->decl()->setProperty(id, CSSValueBlack, false); +} + +void StyledElement::createMappedDecl(Attribute* attr) +{ + RefPtr<CSSMappedAttributeDeclaration> decl = CSSMappedAttributeDeclaration::create(); + attr->setDecl(decl); + decl->setParent(document()->elementSheet()); + decl->setNode(this); + decl->setStrictParsing(false); // Mapped attributes are just always quirky. +} + +unsigned MappedAttributeHash::hash(const MappedAttributeKey& key) +{ + COMPILE_ASSERT(sizeof(key.name) == 4 || sizeof(key.name) == 8, key_name_size); + COMPILE_ASSERT(sizeof(key.value) == 4 || sizeof(key.value) == 8, key_value_size); + + WTF::StringHasher hasher; + const UChar* data; + + data = reinterpret_cast<const UChar*>(&key.name); + hasher.addCharacters(data[0], data[1]); + if (sizeof(key.name) == 8) + hasher.addCharacters(data[2], data[3]); + + data = reinterpret_cast<const UChar*>(&key.value); + hasher.addCharacters(data[0], data[1]); + if (sizeof(key.value) == 8) + hasher.addCharacters(data[2], data[3]); + + return hasher.hash(); +} + +void StyledElement::copyNonAttributeProperties(const Element *sourceElement) +{ + const StyledElement* source = static_cast<const StyledElement*>(sourceElement); + if (!source->m_inlineStyleDecl) + return; + + *getInlineStyleDecl() = *source->m_inlineStyleDecl; + setIsStyleAttributeValid(source->isStyleAttributeValid()); + setIsSynchronizingStyleAttribute(source->isSynchronizingStyleAttribute()); + + Element::copyNonAttributeProperties(sourceElement); +} + +void StyledElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const +{ + if (CSSMutableStyleDeclaration* style = inlineStyleDecl()) + style->addSubresourceStyleURLs(urls); +} + + +void StyledElement::didMoveToNewOwnerDocument() +{ + if (m_inlineStyleDecl) + m_inlineStyleDecl->setParent(document()->elementSheet()); + + Element::didMoveToNewOwnerDocument(); +} + +} |