WebKit does not enumerate over CSS properties in HTMLElement.style
https://bugs.webkit.org/show_bug.cgi?id=23946

Reviewed by Darin Adler.

Source/JavaScriptCore:

Add a few exports to follow the JSCSSStyleDeclaration.cpp changes,
introduce an std::sort() comparator function.

* JavaScriptCore.exp:
* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def:
* wtf/text/WTFString.h:
(WTF::codePointCompareLessThan): Used by std::sort() to sort properties.

Source/WebCore:

This change generates a list of JavaScript mirrors of the CSS properties and allows to enumerate them
using the "in" operator on the CSSStyleDeclaration object.

Test: fast/css/style-enumerate-properties.html

* bindings/js/JSCSSStyleDeclarationCustom.cpp:
(WebCore::JSCSSStyleDeclaration::getOwnPropertyNames): Added.
* bindings/v8/custom/V8CSSStyleDeclarationCustom.cpp:
(WebCore::V8CSSStyleDeclaration::namedPropertyEnumerator): Added.
(WebCore::V8CSSStyleDeclaration::namedPropertyQuery): Added.
(WebCore::V8CSSStyleDeclaration::namedPropertyGetter): A small drive-by optimization (local initialization moved down).
* css/CSSStyleDeclaration.idl: Use a custom property enumerator.
* css/makeprop.pl: Add a function to convert CSS property names into JS ones.

LayoutTests:

* fast/css/style-enumerate-properties-expected.txt: Added.
* fast/css/style-enumerate-properties.html: Added.
* fast/dom/domListEnumeration-expected.txt:
* fast/dom/script-tests/domListEnumeration.js:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@102578 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index d96cec0..57bf573 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,15 @@
+2011-11-30  Alexander Pavlov  <apavlov@chromium.org>
+
+        WebKit does not enumerate over CSS properties in HTMLElement.style
+        https://bugs.webkit.org/show_bug.cgi?id=23946
+
+        Reviewed by Darin Adler.
+
+        * fast/css/style-enumerate-properties-expected.txt: Added.
+        * fast/css/style-enumerate-properties.html: Added.
+        * fast/dom/domListEnumeration-expected.txt:
+        * fast/dom/script-tests/domListEnumeration.js:
+
 2011-12-12  Ilya Tikhonovsky  <loislo@chromium.org>
 
         Web Inspector: unreviewed test fix for r102574
diff --git a/LayoutTests/fast/dom/domListEnumeration-expected.txt b/LayoutTests/fast/dom/domListEnumeration-expected.txt
index 722ba07..9ca97ec 100644
--- a/LayoutTests/fast/dom/domListEnumeration-expected.txt
+++ b/LayoutTests/fast/dom/domListEnumeration-expected.txt
@@ -66,7 +66,6 @@
 PASS resultArray[2].item is cssRuleList.item(2)
 
 [object CSSStyleDeclaration]
-PASS resultArray.length is 14
 PASS resultArray[0].i is '0'
 PASS resultArray[0].item is cssStyleDeclaration.item(0)
 PASS resultArray[1].i is '1'
diff --git a/LayoutTests/fast/dom/script-tests/domListEnumeration.js b/LayoutTests/fast/dom/script-tests/domListEnumeration.js
index a099c83..7a98755 100644
--- a/LayoutTests/fast/dom/script-tests/domListEnumeration.js
+++ b/LayoutTests/fast/dom/script-tests/domListEnumeration.js
@@ -173,7 +173,6 @@
 //debug(escapeHTML(document.getElementsByTagName('style')));
 var cssStyleDeclaration = document.styleSheets[2].cssRules[0].style;
 resultArray = iterateList(cssStyleDeclaration);
-shouldBe("resultArray.length", "14");
 shouldBe("resultArray[0].i", "'0'");
 shouldBe("resultArray[0].item", "cssStyleDeclaration.item(0)");
 shouldBe("resultArray[1].i", "'1'");
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 4f15ac5..480c509 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,18 @@
+2011-12-09  Alexander Pavlov  <apavlov@chromium.org>
+
+        WebKit does not enumerate over CSS properties in HTMLElement.style
+        https://bugs.webkit.org/show_bug.cgi?id=23946
+
+        Reviewed by Darin Adler.
+
+        Add a few exports to follow the JSCSSStyleDeclaration.cpp changes,
+        introduce an std::sort() comparator function.
+
+        * JavaScriptCore.exp:
+        * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def:
+        * wtf/text/WTFString.h:
+        (WTF::codePointCompareLessThan): Used by std::sort() to sort properties.
+
 2011-12-12  Alexander Pavlov  <apavlov@chromium.org>
 
         Unreviewed, build fix.
diff --git a/Source/JavaScriptCore/JavaScriptCore.exp b/Source/JavaScriptCore/JavaScriptCore.exp
index 19885a4..b7e9364 100644
--- a/Source/JavaScriptCore/JavaScriptCore.exp
+++ b/Source/JavaScriptCore/JavaScriptCore.exp
@@ -155,6 +155,7 @@
 __ZN3JSC12RegExpObjectC1EPNS_14JSGlobalObjectEPNS_9StructureEPNS_6RegExpE
 __ZN3JSC12SamplingTool5setupEv
 __ZN3JSC12SmallStrings17createEmptyStringEPNS_12JSGlobalDataE
+__ZN3JSC12SmallStrings24singleCharacterStringRepEh
 __ZN3JSC12SmallStrings27createSingleCharacterStringEPNS_12JSGlobalDataEh
 __ZN3JSC12StringObject14finishCreationERNS_12JSGlobalDataEPNS_8JSStringE
 __ZN3JSC12StringObject6s_infoE
@@ -435,6 +436,7 @@
 __ZN3WTF15charactersToIntEPKtmPb
 __ZN3WTF16callOnMainThreadEPFvPvES0_
 __ZN3WTF16codePointCompareERKNS_6StringES2_
+__ZN3WTF16codePointCompareEPKNS_10StringImplES2_
 __ZN3WTF16fastZeroedMallocEm
 __ZN3WTF17charactersToFloatEPKtmPbS2_
 __ZN3WTF17equalIgnoringCaseEPKtPKhj
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def
index 6540179..ffd5a52 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def
+++ b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def
@@ -53,6 +53,7 @@
     ?activityCallback@Heap@JSC@@QAEPAVGCActivityCallback@2@XZ
     ?add@AtomicString@WTF@@CA?AV?$PassRefPtr@VStringImpl@WTF@@@2@PBD@Z
     ?add@Identifier@JSC@@SA?AV?$PassRefPtr@VStringImpl@WTF@@@WTF@@PAVExecState@2@PBD@Z
+    ?add@Identifier@JSC@@CA?AV?$PassRefPtr@VStringImpl@WTF@@@WTF@@PAVJSGlobalData@2@PAVStringImpl@4@@Z
     ?add@PropertyNameArray@JSC@@QAEXPAVStringImpl@WTF@@@Z
     ?addBytes@MD5@WTF@@QAEXPBEI@Z
     ?addBytes@SHA1@WTF@@QAEXPBEI@Z
@@ -62,6 +63,7 @@
     ?addPropertyTransitionToExistingStructure@Structure@JSC@@SAPAV12@PAV12@ABVIdentifier@2@IPAVJSCell@2@AAI@Z
     ?addPropertyWithoutTransition@Structure@JSC@@QAEIAAVJSGlobalData@2@ABVIdentifier@2@IPAVJSCell@2@@Z
     ?addSlowCase@Identifier@JSC@@CA?AV?$PassRefPtr@VStringImpl@WTF@@@WTF@@PAVExecState@2@PAVStringImpl@4@@Z
+    ?addSlowCase@Identifier@JSC@@CA?AV?$PassRefPtr@VStringImpl@WTF@@@WTF@@PAVJSGlobalData@2@PAVStringImpl@4@@Z
     ?addStaticGlobals@JSGlobalObject@JSC@@IAEXPAUGlobalPropertyInfo@12@H@Z
     ?allocatePropertyStorage@JSObject@JSC@@QAEXAAVJSGlobalData@2@II@Z
     ?allocateSlowCase@AllocationSpace@JSC@@AAEPAXAAUSizeClass@MarkedSpace@2@@Z
@@ -131,7 +133,7 @@
     ?currentThread@WTF@@YAIXZ
     ?currentThreadIsHoldingLock@JSLock@JSC@@SA_NXZ
     ?currentTime@WTF@@YANXZ
-	?execute@ParallelEnvironment@WTF@@QAEXPAX@Z
+    ?execute@ParallelEnvironment@WTF@@QAEXPAX@Z
     ?data@CString@WTF@@QBEPBDXZ
     ?dateToDaysFrom1970@WTF@@YANHHH@Z
     ?dayInMonthFromDayInYear@WTF@@YAHH_N@Z
@@ -313,6 +315,7 @@
     ?setter@PropertyDescriptor@JSC@@QBE?AVJSValue@2@XZ
     ?shrinkToFit@StringBuilder@WTF@@QAEXXZ
     ?signal@ThreadCondition@WTF@@QAEXXZ
+    ?singleCharacterStringRep@SmallStrings@JSC@@QAEPAVStringImpl@WTF@@E@Z
     ?size@Heap@JSC@@QAEIXZ
     ?slowAppend@MarkedArgumentBuffer@JSC@@AAEXVJSValue@2@@Z
     ?slowValidateCell@JSC@@YAXPAVJSCell@1@@Z
diff --git a/Source/JavaScriptCore/wtf/text/WTFString.h b/Source/JavaScriptCore/wtf/text/WTFString.h
index 7ffc7ec..e61b80f 100644
--- a/Source/JavaScriptCore/wtf/text/WTFString.h
+++ b/Source/JavaScriptCore/wtf/text/WTFString.h
@@ -506,6 +506,11 @@
 
 WTF_EXPORT_PRIVATE int codePointCompare(const String&, const String&);
 
+inline bool codePointCompareLessThan(const String& a, const String& b)
+{
+    return codePointCompare(a.impl(), b.impl()) < 0;
+}
+
 inline size_t find(const LChar* characters, unsigned length, LChar matchCharacter, unsigned index = 0)
 {
     while (index < length) {
diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog
index b4093d4..38d9748 100644
--- a/Source/WebCore/ChangeLog
+++ b/Source/WebCore/ChangeLog
@@ -1,3 +1,24 @@
+2011-11-25  Alexander Pavlov  <apavlov@chromium.org>
+
+        WebKit does not enumerate over CSS properties in HTMLElement.style
+        https://bugs.webkit.org/show_bug.cgi?id=23946
+
+        Reviewed by Darin Adler.
+
+        This change generates a list of JavaScript mirrors of the CSS properties and allows to enumerate them
+        using the "in" operator on the CSSStyleDeclaration object.
+
+        Test: fast/css/style-enumerate-properties.html
+
+        * bindings/js/JSCSSStyleDeclarationCustom.cpp:
+        (WebCore::JSCSSStyleDeclaration::getOwnPropertyNames): Added.
+        * bindings/v8/custom/V8CSSStyleDeclarationCustom.cpp:
+        (WebCore::V8CSSStyleDeclaration::namedPropertyEnumerator): Added.
+        (WebCore::V8CSSStyleDeclaration::namedPropertyQuery): Added.
+        (WebCore::V8CSSStyleDeclaration::namedPropertyGetter): A small drive-by optimization (local initialization moved down).
+        * css/CSSStyleDeclaration.idl: Use a custom property enumerator.
+        * css/makeprop.pl: Add a function to convert CSS property names into JS ones.
+
 2011-12-12  Alexander Pavlov  <apavlov@chromium.org>
 
         Unreviewed, build fix.
diff --git a/Source/WebCore/bindings/js/JSCSSStyleDeclarationCustom.cpp b/Source/WebCore/bindings/js/JSCSSStyleDeclarationCustom.cpp
index 2904e4b..affe0e0 100644
--- a/Source/WebCore/bindings/js/JSCSSStyleDeclarationCustom.cpp
+++ b/Source/WebCore/bindings/js/JSCSSStyleDeclarationCustom.cpp
@@ -28,6 +28,7 @@
 
 #include "CSSMutableStyleDeclaration.h"
 #include "CSSPrimitiveValue.h"
+#include "CSSPropertyNames.h"
 #include "CSSValue.h"
 #include "JSCSSValue.h"
 #include "JSNode.h"
@@ -37,9 +38,11 @@
 #include <wtf/text/AtomicString.h>
 #include <wtf/text/StringBuilder.h>
 #include <wtf/text/StringConcatenate.h>
+#include <wtf/text/WTFString.h>
 
 using namespace JSC;
 using namespace WTF;
+using namespace std;
 
 namespace WebCore {
 
@@ -139,8 +142,6 @@
     return isCSSPropertyName(propertyName);
 }
 
-// FIXME: You can get these properties, and set them (see putDelegate below),
-// but you should also be able to enumerate them.
 JSValue JSCSSStyleDeclaration::nameGetter(ExecState* exec, JSValue slotBase, const Identifier& propertyName)
 {
     JSCSSStyleDeclaration* thisObj = static_cast<JSCSSStyleDeclaration*>(asObject(slotBase));
@@ -195,4 +196,31 @@
     return toJS(exec, globalObject(), WTF::getPtr(cssValue));
 }
 
+void JSCSSStyleDeclaration::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+    JSCSSStyleDeclaration* thisObject = jsCast<JSCSSStyleDeclaration*>(object);
+    ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
+
+    unsigned length = thisObject->impl()->length();
+    for (unsigned i = 0; i < length; ++i)
+        propertyNames.add(Identifier::from(exec, i));
+
+    static Identifier* propertyIdentifiers = 0;
+    if (!propertyIdentifiers) {
+        Vector<String, numCSSProperties> jsPropertyNames;
+        for (int id = firstCSSProperty; id < firstCSSProperty + numCSSProperties; ++id)
+            jsPropertyNames.append(getJSPropertyName(static_cast<CSSPropertyID>(id)));
+        sort(jsPropertyNames.begin(), jsPropertyNames.end(), WTF::codePointCompareLessThan);
+
+        propertyIdentifiers = new Identifier[numCSSProperties];
+        for (int i = 0; i < numCSSProperties; ++i)
+            propertyIdentifiers[i] = Identifier(exec, jsPropertyNames[i].impl());
+    }
+
+    for (int i = 0; i < numCSSProperties; ++i)
+        propertyNames.add(propertyIdentifiers[i]);
+
+    Base::getOwnPropertyNames(thisObject, exec, propertyNames, mode);
+}
+
 } // namespace WebCore
diff --git a/Source/WebCore/bindings/v8/custom/V8CSSStyleDeclarationCustom.cpp b/Source/WebCore/bindings/v8/custom/V8CSSStyleDeclarationCustom.cpp
index 643f0fe..b435b9b 100644
--- a/Source/WebCore/bindings/v8/custom/V8CSSStyleDeclarationCustom.cpp
+++ b/Source/WebCore/bindings/v8/custom/V8CSSStyleDeclarationCustom.cpp
@@ -32,6 +32,7 @@
 #include "V8CSSStyleDeclaration.h"
 
 #include "CSSParser.h"
+#include "CSSPropertyNames.h"
 #include "CSSStyleDeclaration.h"
 #include "CSSValue.h"
 #include "CSSPrimitiveValue.h"
@@ -48,6 +49,9 @@
 #include <wtf/StdLibExtras.h>
 #include <wtf/Vector.h>
 
+using namespace WTF;
+using namespace std;
+
 namespace WebCore {
 
 // FIXME: Next two functions look lifted verbatim from JSCSSStyleDeclarationCustom. Please remove duplication.
@@ -152,6 +156,42 @@
     return propInfo;
 }
 
+v8::Handle<v8::Array> V8CSSStyleDeclaration::namedPropertyEnumerator(const v8::AccessorInfo& info)
+{
+    typedef Vector<String, numCSSProperties - 1> PreAllocatedPropertyVector;
+    DEFINE_STATIC_LOCAL(PreAllocatedPropertyVector, propertyNames, ());
+    DEFINE_STATIC_LOCAL(String, filterString, ("filter"));
+    static unsigned propertyNamesLength = 0;
+
+    if (propertyNames.isEmpty()) {
+        for (int id = firstCSSProperty; id < firstCSSProperty + numCSSProperties; ++id) {
+            String jsPropertyName = getJSPropertyName(static_cast<CSSPropertyID>(id));
+            // The "filter" property is present in the list but should not be provided in the enumeration.
+            // See a comment in the V8CSSStyleDeclaration::namedPropertyGetter() implementation.
+            // FIXME: this should be removed (see bug 73426).
+            if (jsPropertyName != filterString)
+                propertyNames.append(jsPropertyName);
+        }
+        sort(propertyNames.begin(), propertyNames.end(), codePointCompareLessThan);
+        propertyNamesLength = propertyNames.size();
+    }
+
+    v8::Handle<v8::Array> properties = v8::Array::New(propertyNamesLength);
+    for (unsigned i = 0; i < propertyNamesLength; ++i) {
+        String key = propertyNames.at(i);
+        ASSERT(!key.isNull());
+        properties->Set(v8::Integer::New(i), v8String(key));
+    }
+
+    return properties;
+}
+
+v8::Handle<v8::Integer> V8CSSStyleDeclaration::namedPropertyQuery(v8::Local<v8::String> v8Name, const v8::AccessorInfo& info)
+{
+    INC_STATS("DOM.CSSStyleDeclaration.NamedPropertyQuery");
+    return v8::Integer::New(v8::None);
+}
+
 v8::Handle<v8::Value> V8CSSStyleDeclaration::namedPropertyGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info)
 {
     INC_STATS("DOM.CSSStyleDeclaration.NamedPropertyGetter");
@@ -160,14 +200,13 @@
         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();
 
-
+    CSSStyleDeclaration* imp = V8CSSStyleDeclaration::toNative(info.Holder());
     RefPtr<CSSValue> cssValue = imp->getPropertyCSSValue(propInfo->propID);
     if (cssValue) {
         if (propInfo->hadPixelOrPosPrefix &&
diff --git a/Source/WebCore/css/CSSStyleDeclaration.idl b/Source/WebCore/css/CSSStyleDeclaration.idl
index 2fa609b..d1b470f 100644
--- a/Source/WebCore/css/CSSStyleDeclaration.idl
+++ b/Source/WebCore/css/CSSStyleDeclaration.idl
@@ -27,6 +27,7 @@
         DelegatingPutFunction,
         HasNameGetter,
         HasIndexGetter,
+        CustomGetPropertyNames,
         V8DependentLifetime
     ] CSSStyleDeclaration {
                  attribute [ConvertNullStringTo=Null, ConvertNullToNullString] DOMString        cssText
diff --git a/Source/WebCore/css/makeprop.pl b/Source/WebCore/css/makeprop.pl
index 45af713..7eff615 100644
--- a/Source/WebCore/css/makeprop.pl
+++ b/Source/WebCore/css/makeprop.pl
@@ -62,10 +62,14 @@
 print GPERF << "EOF";
 %{
 /* This file is automatically generated from CSSPropertyNames.in by makeprop, do not edit */
+#include "config.h"
 #include \"CSSPropertyNames.h\"
 #include \"HashTools.h\"
 #include <string.h>
 
+#include <wtf/ASCIICType.h>
+#include <wtf/text/WTFString.h>
+
 namespace WebCore {
 %}
 %struct-type
@@ -114,6 +118,28 @@
     return propertyNameStrings[index];
 }
 
+WTF::String getJSPropertyName(CSSPropertyID id)
+{
+    char result[maxCSSPropertyNameLength + 1];
+    const char* cssPropertyName = getPropertyName(id);
+    const char* propertyNamePointer = cssPropertyName;
+    if (!propertyNamePointer)
+        return emptyString();
+
+    char* resultPointer = result;
+    while (char character = *propertyNamePointer++) {
+        if (character == '-') {
+            char nextCharacter = *propertyNamePointer++;
+            if (!nextCharacter)
+                break;
+            character = (propertyNamePointer - 2 != cssPropertyName) ? toASCIIUpper(nextCharacter) : nextCharacter;
+        }
+        *resultPointer++ = character;
+    }
+    *resultPointer = '\\0';
+    return WTF::String(result);
+}
+
 } // namespace WebCore
 EOF
 
@@ -126,6 +152,10 @@
 
 #include <string.h>
 
+namespace WTF {
+class String;
+}
+
 namespace WebCore {
 
 enum CSSPropertyID {
@@ -160,6 +190,7 @@
 print HEADER << "EOF";
 
 const char* getPropertyName(CSSPropertyID);
+WTF::String getJSPropertyName(CSSPropertyID);
 
 } // namespace WebCore