/*
* 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