/* * Copyright (C) 2007-2009 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "V8CSSStyleDeclaration.h" #include "CSSParser.h" #include "CSSStyleDeclaration.h" #include "CSSValue.h" #include "CSSPrimitiveValue.h" #include "EventTarget.h" #include "V8Binding.h" #include "V8Proxy.h" #include <wtf/ASCIICType.h> #include <wtf/PassRefPtr.h> #include <wtf/RefPtr.h> #include <wtf/StdLibExtras.h> #include <wtf/Vector.h> namespace WebCore { // FIXME: Next two functions look lifted verbatim from JSCSSStyleDeclarationCustom. Please remove duplication. // Check for a CSS prefix. // Passed prefix is all lowercase. // First character of the prefix within the property name may be upper or lowercase. // Other characters in the prefix within the property name must be lowercase. // The prefix within the property name must be followed by a capital letter. static bool hasCSSPropertyNamePrefix(const String& propertyName, const char* prefix) { #ifndef NDEBUG ASSERT(*prefix); for (const char* p = prefix; *p; ++p) ASSERT(WTF::isASCIILower(*p)); ASSERT(propertyName.length()); #endif if (WTF::toASCIILower(propertyName[0]) != prefix[0]) return false; unsigned length = propertyName.length(); for (unsigned i = 1; i < length; ++i) { if (!prefix[i]) return WTF::isASCIIUpper(propertyName[i]); if (propertyName[i] != prefix[i]) return false; } return false; } class CSSPropertyInfo { public: int propID; bool hadPixelOrPosPrefix; bool wasFilter; }; // When getting properties on CSSStyleDeclarations, the name used from // Javascript and the actual name of the property are not the same, so // we have to do the following translation. The translation turns upper // case characters into lower case characters and inserts dashes to // separate words. // // Example: 'backgroundPositionY' -> 'background-position-y' // // Also, certain prefixes such as 'pos', 'css-' and 'pixel-' are stripped // and the hadPixelOrPosPrefix out parameter is used to indicate whether or // not the property name was prefixed with 'pos-' or 'pixel-'. static CSSPropertyInfo* cssPropertyInfo(v8::Handle<v8::String>v8PropertyName) { String propertyName = toWebCoreString(v8PropertyName); typedef HashMap<String, CSSPropertyInfo*> CSSPropertyInfoMap; DEFINE_STATIC_LOCAL(CSSPropertyInfoMap, map, ()); CSSPropertyInfo* propInfo = map.get(propertyName); if (!propInfo) { unsigned length = propertyName.length(); bool hadPixelOrPosPrefix = false; if (!length) return 0; Vector<UChar> name; name.reserveCapacity(length); unsigned i = 0; if (hasCSSPropertyNamePrefix(propertyName, "css")) i += 3; else if (hasCSSPropertyNamePrefix(propertyName, "pixel")) { i += 5; hadPixelOrPosPrefix = true; } else if (hasCSSPropertyNamePrefix(propertyName, "pos")) { i += 3; hadPixelOrPosPrefix = true; } else if (hasCSSPropertyNamePrefix(propertyName, "webkit") || hasCSSPropertyNamePrefix(propertyName, "khtml") || hasCSSPropertyNamePrefix(propertyName, "apple")) name.append('-'); else if (WTF::isASCIIUpper(propertyName[0])) return 0; name.append(WTF::toASCIILower(propertyName[i++])); for (; i < length; ++i) { UChar c = propertyName[i]; if (!WTF::isASCIIUpper(c)) name.append(c); else { name.append('-'); name.append(WTF::toASCIILower(c)); } } String propName = String::adopt(name); int propertyID = cssPropertyID(propName); if (propertyID) { propInfo = new CSSPropertyInfo(); propInfo->hadPixelOrPosPrefix = hadPixelOrPosPrefix; propInfo->wasFilter = (propName == "filter"); propInfo->propID = propertyID; map.add(propertyName, propInfo); } } return propInfo; } v8::Handle<v8::Value> V8CSSStyleDeclaration::namedPropertyGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) { INC_STATS("DOM.CSSStyleDeclaration.NamedPropertyGetter"); // First look for API defined attributes on the style declaration object. if (info.Holder()->HasRealNamedCallbackProperty(name)) return notHandledByInterceptor(); // Search the style declaration. CSSStyleDeclaration* imp = V8CSSStyleDeclaration::toNative(info.Holder()); CSSPropertyInfo* propInfo = cssPropertyInfo(name); // Do not handle non-property names. if (!propInfo) return notHandledByInterceptor(); RefPtr<CSSValue> cssValue = imp->getPropertyCSSValue(propInfo->propID); if (cssValue) { if (propInfo->hadPixelOrPosPrefix && cssValue->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) { return v8::Number::New(static_cast<CSSPrimitiveValue*>( cssValue.get())->getFloatValue(CSSPrimitiveValue::CSS_PX)); } return v8StringOrNull(cssValue->cssText()); } String result = imp->getPropertyValue(propInfo->propID); if (result.isNull()) result = ""; // convert null to empty string. // The 'filter' attribute is made undetectable in KJS/WebKit // to avoid confusion with IE's filter extension. if (propInfo->wasFilter) return v8UndetectableString(result); return v8String(result); } v8::Handle<v8::Value> V8CSSStyleDeclaration::namedPropertySetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info) { INC_STATS("DOM.CSSStyleDeclaration.NamedPropertySetter"); CSSStyleDeclaration* imp = V8CSSStyleDeclaration::toNative(info.Holder()); CSSPropertyInfo* propInfo = cssPropertyInfo(name); if (!propInfo) return notHandledByInterceptor(); String propertyValue = toWebCoreStringWithNullCheck(value); if (propInfo->hadPixelOrPosPrefix) propertyValue.append("px"); ExceptionCode ec = 0; int importantIndex = propertyValue.find("!important", 0, false); bool important = false; if (importantIndex != -1) { important = true; propertyValue = propertyValue.left(importantIndex - 1); } imp->setProperty(propInfo->propID, propertyValue, important, ec); if (ec) throwError(ec); return value; } } // namespace WebCore