1 /*
2  * Copyright (C) 2007, 2008, 2009 Apple 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 #include "JSCSSStyleDeclarationCustom.h"
28 
29 #include "CSSMutableStyleDeclaration.h"
30 #include "CSSPrimitiveValue.h"
31 #include "CSSValue.h"
32 #include "JSCSSValue.h"
33 #include "JSNode.h"
34 #include "PlatformString.h"
35 #include <runtime/StringObjectThatMasqueradesAsUndefined.h>
36 #include <runtime/StringPrototype.h>
37 #include <wtf/ASCIICType.h>
38 #include <wtf/text/AtomicString.h>
39 #include <wtf/text/StringBuilder.h>
40 #include <wtf/text/StringConcatenate.h>
41 
42 using namespace JSC;
43 using namespace WTF;
44 
45 namespace WebCore {
46 
visitChildren(SlotVisitor & visitor)47 void JSCSSStyleDeclaration::visitChildren(SlotVisitor& visitor)
48 {
49     Base::visitChildren(visitor);
50     visitor.addOpaqueRoot(root(impl()));
51 }
52 
53 // Check for a CSS prefix.
54 // Passed prefix is all lowercase.
55 // First character of the prefix within the property name may be upper or lowercase.
56 // Other characters in the prefix within the property name must be lowercase.
57 // The prefix within the property name must be followed by a capital letter.
hasCSSPropertyNamePrefix(const Identifier & propertyName,const char * prefix)58 static bool hasCSSPropertyNamePrefix(const Identifier& propertyName, const char* prefix)
59 {
60 #ifndef NDEBUG
61     ASSERT(*prefix);
62     for (const char* p = prefix; *p; ++p)
63         ASSERT(isASCIILower(*p));
64     ASSERT(propertyName.length());
65 #endif
66 
67     if (toASCIILower(propertyName.characters()[0]) != prefix[0])
68         return false;
69 
70     unsigned length = propertyName.length();
71     for (unsigned i = 1; i < length; ++i) {
72         if (!prefix[i])
73             return isASCIIUpper(propertyName.characters()[i]);
74         if (propertyName.characters()[i] != prefix[i])
75             return false;
76     }
77     return false;
78 }
79 
cssPropertyName(const Identifier & propertyName,bool * hadPixelOrPosPrefix=0)80 static String cssPropertyName(const Identifier& propertyName, bool* hadPixelOrPosPrefix = 0)
81 {
82     if (hadPixelOrPosPrefix)
83         *hadPixelOrPosPrefix = false;
84 
85     unsigned length = propertyName.length();
86     if (!length)
87         return String();
88 
89     StringBuilder builder;
90     builder.reserveCapacity(length);
91 
92     unsigned i = 0;
93 
94     if (hasCSSPropertyNamePrefix(propertyName, "css"))
95         i += 3;
96     else if (hasCSSPropertyNamePrefix(propertyName, "pixel")) {
97         i += 5;
98         if (hadPixelOrPosPrefix)
99             *hadPixelOrPosPrefix = true;
100     } else if (hasCSSPropertyNamePrefix(propertyName, "pos")) {
101         i += 3;
102         if (hadPixelOrPosPrefix)
103             *hadPixelOrPosPrefix = true;
104     } else if (hasCSSPropertyNamePrefix(propertyName, "webkit")
105             || hasCSSPropertyNamePrefix(propertyName, "khtml")
106             || hasCSSPropertyNamePrefix(propertyName, "apple")
107             || hasCSSPropertyNamePrefix(propertyName, "epub"))
108         builder.append('-');
109     else {
110         if (isASCIIUpper(propertyName.characters()[0]))
111             return String();
112     }
113 
114     builder.append(toASCIILower(propertyName.characters()[i++]));
115 
116     for (; i < length; ++i) {
117         UChar c = propertyName.characters()[i];
118         if (!isASCIIUpper(c))
119             builder.append(c);
120         else
121             builder.append(makeString('-', toASCIILower(c)));
122     }
123 
124     return builder.toString();
125 }
126 
isCSSPropertyName(const Identifier & propertyIdentifier)127 static bool isCSSPropertyName(const Identifier& propertyIdentifier)
128 {
129     // FIXME: This mallocs a string for the property name and then throws it
130     // away.  This shows up on peacekeeper's domDynamicCreationCreateElement.
131     return CSSStyleDeclaration::isPropertyName(cssPropertyName(propertyIdentifier));
132 }
133 
canGetItemsForName(ExecState *,CSSStyleDeclaration *,const Identifier & propertyName)134 bool JSCSSStyleDeclaration::canGetItemsForName(ExecState*, CSSStyleDeclaration*, const Identifier& propertyName)
135 {
136     return isCSSPropertyName(propertyName);
137 }
138 
139 // FIXME: You can get these properties, and set them (see putDelegate below),
140 // but you should also be able to enumerate them.
nameGetter(ExecState * exec,JSValue slotBase,const Identifier & propertyName)141 JSValue JSCSSStyleDeclaration::nameGetter(ExecState* exec, JSValue slotBase, const Identifier& propertyName)
142 {
143     JSCSSStyleDeclaration* thisObj = static_cast<JSCSSStyleDeclaration*>(asObject(slotBase));
144 
145     // Set up pixelOrPos boolean to handle the fact that
146     // pixelTop returns "CSS Top" as number value in unit pixels
147     // posTop returns "CSS top" as number value in unit pixels _if_ its a
148     // positioned element. if it is not a positioned element, return 0
149     // from MSIE documentation FIXME: IMPLEMENT THAT (Dirk)
150     bool pixelOrPos;
151     String prop = cssPropertyName(propertyName, &pixelOrPos);
152     RefPtr<CSSValue> v = thisObj->impl()->getPropertyCSSValue(prop);
153     if (v) {
154         if (pixelOrPos && v->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE)
155             return jsNumber(static_pointer_cast<CSSPrimitiveValue>(v)->getFloatValue(CSSPrimitiveValue::CSS_PX));
156         return jsStringOrNull(exec, v->cssText());
157     }
158 
159     // If the property is a shorthand property (such as "padding"),
160     // it can only be accessed using getPropertyValue.
161 
162     // Make the SVG 'filter' attribute undetectable, to avoid confusion with the IE 'filter' attribute.
163     if (propertyName == "filter")
164         return StringObjectThatMasqueradesAsUndefined::create(exec, stringToUString(thisObj->impl()->getPropertyValue(prop)));
165 
166     return jsString(exec, thisObj->impl()->getPropertyValue(prop));
167 }
168 
putDelegate(ExecState * exec,const Identifier & propertyName,JSValue value,PutPropertySlot &)169 bool JSCSSStyleDeclaration::putDelegate(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot&)
170 {
171     bool pixelOrPos;
172     String prop = cssPropertyName(propertyName, &pixelOrPos);
173     if (!CSSStyleDeclaration::isPropertyName(prop))
174         return false;
175 
176     String propValue = valueToStringWithNullCheck(exec, value);
177     if (pixelOrPos)
178         propValue += "px";
179     ExceptionCode ec = 0;
180     impl()->setProperty(prop, propValue, ec);
181     setDOMException(exec, ec);
182     return true;
183 }
184 
getPropertyCSSValue(ExecState * exec)185 JSValue JSCSSStyleDeclaration::getPropertyCSSValue(ExecState* exec)
186 {
187     const String& propertyName(ustringToString(exec->argument(0).toString(exec)));
188     if (exec->hadException())
189         return jsUndefined();
190 
191     RefPtr<CSSValue> cssValue = impl()->getPropertyCSSValue(propertyName);
192     if (!cssValue)
193         return jsNull();
194 
195     currentWorld(exec)->m_cssValueRoots.add(cssValue.get(), root(impl())); // Balanced by JSCSSValueOwner::finalize().
196     return toJS(exec, globalObject(), WTF::getPtr(cssValue));
197 }
198 
199 } // namespace WebCore
200