1/*
2 * Copyright (C) 2007-2011 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "V8CSSStyleDeclaration.h"
33
34#include "CSSPropertyNames.h"
35#include "bindings/v8/ExceptionState.h"
36#include "bindings/v8/V8Binding.h"
37#include "core/css/CSSParser.h"
38#include "core/css/CSSPrimitiveValue.h"
39#include "core/css/CSSStyleDeclaration.h"
40#include "core/css/CSSValue.h"
41#include "core/dom/EventTarget.h"
42#include "core/page/RuntimeCSSEnabled.h"
43#include "wtf/ASCIICType.h"
44#include "wtf/PassRefPtr.h"
45#include "wtf/RefPtr.h"
46#include "wtf/StdLibExtras.h"
47#include "wtf/Vector.h"
48#include "wtf/text/StringBuilder.h"
49#include "wtf/text/StringConcatenate.h"
50
51using namespace WTF;
52using namespace std;
53
54namespace WebCore {
55
56// FIXME: Next two functions look lifted verbatim from JSCSSStyleDeclarationCustom. Please remove duplication.
57
58// Check for a CSS prefix.
59// Passed prefix is all lowercase.
60// First character of the prefix within the property name may be upper or lowercase.
61// Other characters in the prefix within the property name must be lowercase.
62// The prefix within the property name must be followed by a capital letter.
63static bool hasCSSPropertyNamePrefix(const String& propertyName, const char* prefix)
64{
65#ifndef NDEBUG
66    ASSERT(*prefix);
67    for (const char* p = prefix; *p; ++p)
68        ASSERT(isASCIILower(*p));
69    ASSERT(propertyName.length());
70#endif
71
72    if (toASCIILower(propertyName[0]) != prefix[0])
73        return false;
74
75    unsigned length = propertyName.length();
76    for (unsigned i = 1; i < length; ++i) {
77        if (!prefix[i])
78            return isASCIIUpper(propertyName[i]);
79        if (propertyName[i] != prefix[i])
80            return false;
81    }
82    return false;
83}
84
85class CSSPropertyInfo {
86public:
87    CSSPropertyID propID;
88};
89
90// When getting properties on CSSStyleDeclarations, the name used from
91// Javascript and the actual name of the property are not the same, so
92// we have to do the following translation. The translation turns upper
93// case characters into lower case characters and inserts dashes to
94// separate words.
95//
96// Example: 'backgroundPositionY' -> 'background-position-y'
97//
98// Also, certain prefixes such as 'css-' are stripped.
99static CSSPropertyInfo* cssPropertyInfo(v8::Handle<v8::String> v8PropertyName)
100{
101    String propertyName = toWebCoreString(v8PropertyName);
102    typedef HashMap<String, CSSPropertyInfo*> CSSPropertyInfoMap;
103    DEFINE_STATIC_LOCAL(CSSPropertyInfoMap, map, ());
104    CSSPropertyInfo* propInfo = map.get(propertyName);
105    if (!propInfo) {
106        unsigned length = propertyName.length();
107        if (!length)
108            return 0;
109
110        StringBuilder builder;
111        builder.reserveCapacity(length);
112
113        unsigned i = 0;
114
115        if (hasCSSPropertyNamePrefix(propertyName, "css"))
116            i += 3;
117        else if (hasCSSPropertyNamePrefix(propertyName, "webkit"))
118            builder.append('-');
119        else if (isASCIIUpper(propertyName[0]))
120            return 0;
121
122        builder.append(toASCIILower(propertyName[i++]));
123
124        for (; i < length; ++i) {
125            UChar c = propertyName[i];
126            if (!isASCIIUpper(c))
127                builder.append(c);
128            else {
129                builder.append('-');
130                builder.append(toASCIILower(c));
131            }
132        }
133
134        String propName = builder.toString();
135        CSSPropertyID propertyID = cssPropertyID(propName);
136        if (propertyID && RuntimeCSSEnabled::isCSSPropertyEnabled(propertyID)) {
137            propInfo = new CSSPropertyInfo();
138            propInfo->propID = propertyID;
139            map.add(propertyName, propInfo);
140        }
141    }
142    return propInfo;
143}
144
145void V8CSSStyleDeclaration::namedPropertyEnumeratorCustom(const v8::PropertyCallbackInfo<v8::Array>& info)
146{
147    typedef Vector<String, numCSSProperties - 1> PreAllocatedPropertyVector;
148    DEFINE_STATIC_LOCAL(PreAllocatedPropertyVector, propertyNames, ());
149    static unsigned propertyNamesLength = 0;
150
151    if (propertyNames.isEmpty()) {
152        for (int id = firstCSSProperty; id <= lastCSSProperty; ++id) {
153            CSSPropertyID propertyId = static_cast<CSSPropertyID>(id);
154            if (RuntimeCSSEnabled::isCSSPropertyEnabled(propertyId))
155                propertyNames.append(getJSPropertyName(propertyId));
156        }
157        sort(propertyNames.begin(), propertyNames.end(), codePointCompareLessThan);
158        propertyNamesLength = propertyNames.size();
159    }
160
161    v8::Handle<v8::Array> properties = v8::Array::New(propertyNamesLength);
162    for (unsigned i = 0; i < propertyNamesLength; ++i) {
163        String key = propertyNames.at(i);
164        ASSERT(!key.isNull());
165        properties->Set(v8::Integer::New(i, info.GetIsolate()), v8String(key, info.GetIsolate()));
166    }
167
168    v8SetReturnValue(info, properties);
169}
170
171void V8CSSStyleDeclaration::namedPropertyQueryCustom(v8::Local<v8::String> v8Name, const v8::PropertyCallbackInfo<v8::Integer>& info)
172{
173    // NOTE: cssPropertyInfo lookups incur several mallocs.
174    // Successful lookups have the same cost the first time, but are cached.
175    if (cssPropertyInfo(v8Name)) {
176        v8SetReturnValueInt(info, 0);
177        return;
178    }
179}
180
181void V8CSSStyleDeclaration::namedPropertyGetterCustom(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info)
182{
183    // First look for API defined attributes on the style declaration object.
184    if (info.Holder()->HasRealNamedCallbackProperty(name))
185        return;
186
187    // Search the style declaration.
188    CSSPropertyInfo* propInfo = cssPropertyInfo(name);
189
190    // Do not handle non-property names.
191    if (!propInfo)
192        return;
193
194    CSSStyleDeclaration* imp = V8CSSStyleDeclaration::toNative(info.Holder());
195    RefPtr<CSSValue> cssValue = imp->getPropertyCSSValueInternal(static_cast<CSSPropertyID>(propInfo->propID));
196    if (cssValue) {
197        v8SetReturnValueStringOrNull(info, cssValue->cssText(), info.GetIsolate());
198        return;
199    }
200
201    String result = imp->getPropertyValueInternal(static_cast<CSSPropertyID>(propInfo->propID));
202    if (result.isNull())
203        result = ""; // convert null to empty string.
204
205    v8SetReturnValueString(info, result, info.GetIsolate());
206}
207
208void V8CSSStyleDeclaration::namedPropertySetterCustom(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info)
209{
210    CSSStyleDeclaration* imp = V8CSSStyleDeclaration::toNative(info.Holder());
211    CSSPropertyInfo* propInfo = cssPropertyInfo(name);
212    if (!propInfo)
213        return;
214
215    String propertyValue = toWebCoreStringWithNullCheck(value);
216
217    ExceptionState es(info.GetIsolate());
218    imp->setPropertyInternal(static_cast<CSSPropertyID>(propInfo->propID), propertyValue, false, es);
219
220    if (es.throwIfNeeded())
221        return;
222
223    v8SetReturnValue(info, value);
224}
225
226} // namespace WebCore
227