Let MessageEvent.data hold SerializedScriptValue or String selectively
authoryutak@chromium.org <yutak@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 25 Aug 2011 09:25:16 +0000 (09:25 +0000)
committeryutak@chromium.org <yutak@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 25 Aug 2011 09:25:16 +0000 (09:25 +0000)
https://bugs.webkit.org/show_bug.cgi?id=66841

Reviewed by Adam Barth.

MessageEvent from WebSocket contains a String in its "data" attribute, but it does not have
to be serialized.

No new tests are added, because this is refactoring and the behavior should not change.

* bindings/js/JSMessageEventCustom.cpp:
(WebCore::JSMessageEvent::data):
To call putAnonymousValue(), "this" needs to be converted to non-const using const_cast<>.
(WebCore::JSMessageEvent::initMessageEvent):
Update the cache value as well.
* bindings/v8/custom/V8MessageEventCustom.cpp:
(WebCore::V8MessageEvent::dataAccessorGetter):
ForceSet() is used to cache a value. This is the same as what the code generated by
CodeGeneratorV8 does.
(WebCore::V8MessageEvent::initMessageEventCallback):
* dom/MessageEvent.cpp:
(WebCore::MessageEvent::MessageEvent):
(WebCore::MessageEvent::initMessageEvent):
(WebCore::MessageEvent::data):
This is only used within the Objective-C bindings (JSC and V8 have the custom functions).
Since Objective-C code generator does not support [CustomGetter] IDL attribute, there is
no good way to return a variant value for Objective-C bindings.
* dom/MessageEvent.h:
(WebCore::MessageEvent::create):
(WebCore::MessageEvent::dataType):
(WebCore::MessageEvent::dataAsSerializedScriptValue):
(WebCore::MessageEvent::dataAsString):
* dom/MessageEvent.idl:
* websockets/WebSocket.cpp:
(WebCore::WebSocket::didReceiveMessage):
Construct a MessageEvent without serializing the received message.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@93766 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/fast/dom/Window/script-tests/postmessage-test.js
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSMessageEventCustom.cpp
Source/WebCore/bindings/v8/custom/V8MessageEventCustom.cpp
Source/WebCore/dom/MessageEvent.cpp
Source/WebCore/dom/MessageEvent.h
Source/WebCore/dom/MessageEvent.idl
Source/WebCore/websockets/WebSocket.cpp

index 4acb4ee..763c85a 100644 (file)
@@ -111,7 +111,7 @@ window.doPassFail = function(result, msg) {
 function onmessage(evt) {
     eventData = evt.data;
     if (evt.data !== evt.data)
-        console.innerHTML += "MessageEvent.data does not produce the same value on multiple queries.<br>";
+        console.innerHTML += "MessageEvent.data does not produce the same value on multiple queries. " + evt.data + ", " + evt.data + "<br>";
     var message = messages.shift();
     switch (message) {
     case "cyclicObject":
index 6427413..dd9739f 100644 (file)
@@ -1,3 +1,42 @@
+2011-08-25  Yuta Kitamura  <yutak@chromium.org>
+
+        Let MessageEvent.data hold SerializedScriptValue or String selectively
+        https://bugs.webkit.org/show_bug.cgi?id=66841
+
+        Reviewed by Adam Barth.
+
+        MessageEvent from WebSocket contains a String in its "data" attribute, but it does not have
+        to be serialized.
+
+        No new tests are added, because this is refactoring and the behavior should not change.
+
+        * bindings/js/JSMessageEventCustom.cpp:
+        (WebCore::JSMessageEvent::data):
+        To call putAnonymousValue(), "this" needs to be converted to non-const using const_cast<>.
+        (WebCore::JSMessageEvent::initMessageEvent):
+        Update the cache value as well.
+        * bindings/v8/custom/V8MessageEventCustom.cpp:
+        (WebCore::V8MessageEvent::dataAccessorGetter):
+        ForceSet() is used to cache a value. This is the same as what the code generated by
+        CodeGeneratorV8 does.
+        (WebCore::V8MessageEvent::initMessageEventCallback):
+        * dom/MessageEvent.cpp:
+        (WebCore::MessageEvent::MessageEvent):
+        (WebCore::MessageEvent::initMessageEvent):
+        (WebCore::MessageEvent::data):
+        This is only used within the Objective-C bindings (JSC and V8 have the custom functions).
+        Since Objective-C code generator does not support [CustomGetter] IDL attribute, there is
+        no good way to return a variant value for Objective-C bindings.
+        * dom/MessageEvent.h:
+        (WebCore::MessageEvent::create):
+        (WebCore::MessageEvent::dataType):
+        (WebCore::MessageEvent::dataAsSerializedScriptValue):
+        (WebCore::MessageEvent::dataAsString):
+        * dom/MessageEvent.idl:
+        * websockets/WebSocket.cpp:
+        (WebCore::WebSocket::didReceiveMessage):
+        Construct a MessageEvent without serializing the received message.
+
 2011-08-25  MORITA Hajime  <morrita@google.com>
 
         Unreviewed, rolling out r93762.
index a00c42e..8b6c154 100644 (file)
@@ -42,6 +42,31 @@ using namespace JSC;
 
 namespace WebCore {
 
+JSValue JSMessageEvent::data(ExecState* exec) const
+{
+    if (JSValue cachedValue = getAnonymousValue(JSMessageEvent::dataSlot))
+        return cachedValue;
+
+    MessageEvent* event = static_cast<MessageEvent*>(impl());
+    JSValue result;
+    switch (event->dataType()) {
+    case MessageEvent::DataTypeSerializedScriptValue:
+        if (SerializedScriptValue* serializedValue = event->dataAsSerializedScriptValue())
+            result = serializedValue->deserialize(exec, globalObject(), NonThrowing);
+        else
+            result = jsNull();
+        break;
+
+    case MessageEvent::DataTypeString:
+        result = jsString(exec, event->dataAsString());
+        break;
+    }
+
+    // Save the result so we don't have to deserialize the value again.
+    const_cast<JSMessageEvent*>(this)->putAnonymousValue(exec->globalData(), JSMessageEvent::dataSlot, result);
+    return result;
+}
+
 JSValue JSMessageEvent::ports(ExecState* exec) const
 {
     MessagePortArray* ports = static_cast<MessageEvent*>(impl())->ports();
@@ -75,6 +100,12 @@ JSC::JSValue JSMessageEvent::initMessageEvent(JSC::ExecState* exec)
 
     MessageEvent* event = static_cast<MessageEvent*>(this->impl());
     event->initMessageEvent(ustringToAtomicString(typeArg), canBubbleArg, cancelableArg, dataArg.release(), ustringToString(originArg), ustringToString(lastEventIdArg), sourceArg, messagePorts.release());
+    JSValue result;
+    if (SerializedScriptValue* serializedValue = event->dataAsSerializedScriptValue())
+        result = serializedValue->deserialize(exec, globalObject(), NonThrowing);
+    else
+        result = jsNull();
+    putAnonymousValue(exec->globalData(), JSMessageEvent::dataSlot, result);
     return jsUndefined();
 }
 
index b79f41f..a1afc81 100644 (file)
 
 namespace WebCore {
 
+v8::Handle<v8::Value> V8MessageEvent::dataAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info)
+{
+    INC_STATS("DOM.MessageEvent.data");
+    MessageEvent* event = V8MessageEvent::toNative(info.Holder());
+
+    v8::Handle<v8::Value> result;
+    switch (event->dataType()) {
+    case MessageEvent::DataTypeSerializedScriptValue:
+        if (SerializedScriptValue* serializedValue = event->dataAsSerializedScriptValue())
+            result = serializedValue->deserialize();
+        else
+            result = v8::Null();
+        break;
+
+    case MessageEvent::DataTypeString: {
+        String stringValue = event->dataAsString();
+        result = v8::String::New(fromWebCoreString(stringValue), stringValue.length());
+        break;
+    }
+    }
+
+    // Overwrite the data attribute so it returns the cached result in future invocations.
+    // This custom handler (dataAccessGetter) will not be called again.
+    v8::PropertyAttribute dataAttr = static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly);
+    info.Holder()->ForceSet(name, result, dataAttr);
+    return result;
+}
+
 v8::Handle<v8::Value> V8MessageEvent::portsAccessorGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info)
 {
     INC_STATS("DOM.MessageEvent.ports");
@@ -85,7 +113,7 @@ v8::Handle<v8::Value> V8MessageEvent::initMessageEventCallback(const v8::Argumen
     }
     event->initMessageEvent(typeArg, canBubbleArg, cancelableArg, dataArg.release(), originArg, lastEventIdArg, sourceArg, portArray.release());
     v8::PropertyAttribute dataAttr = static_cast<v8::PropertyAttribute>(v8::DontDelete | v8::ReadOnly);
-    SerializedScriptValue::deserializeAndSetProperty(args.Holder(), "data", dataAttr, event->data());
+    SerializedScriptValue::deserializeAndSetProperty(args.Holder(), "data", dataAttr, event->dataAsSerializedScriptValue());
     return v8::Undefined();
   }
 
index b433381..8d4e8f0 100644 (file)
 namespace WebCore {
 
 MessageEvent::MessageEvent()
-    : m_data(SerializedScriptValue::create())
+    : m_dataType(DataTypeSerializedScriptValue)
+    , m_dataAsSerializedScriptValue(SerializedScriptValue::create())
 {
 }
 
 MessageEvent::MessageEvent(PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, PassRefPtr<DOMWindow> source, PassOwnPtr<MessagePortArray> ports)
     : Event(eventNames().messageEvent, false, false)
-    , m_data(data)
+    , m_dataType(DataTypeSerializedScriptValue)
+    , m_dataAsSerializedScriptValue(data)
     , m_origin(origin)
     , m_lastEventId(lastEventId)
     , m_source(source)
@@ -48,6 +50,15 @@ MessageEvent::MessageEvent(PassRefPtr<SerializedScriptValue> data, const String&
 {
 }
 
+MessageEvent::MessageEvent(const String& data)
+    : Event(eventNames().messageEvent, false, false)
+    , m_dataType(DataTypeString)
+    , m_dataAsString(data)
+    , m_origin("")
+    , m_lastEventId("")
+{
+}
+
 MessageEvent::~MessageEvent()
 {
 }
@@ -58,14 +69,23 @@ void MessageEvent::initMessageEvent(const AtomicString& type, bool canBubble, bo
         return;
         
     initEvent(type, canBubble, cancelable);
-    
-    m_data = data;
+
+    m_dataType = DataTypeSerializedScriptValue;
+    m_dataAsSerializedScriptValue = data;
     m_origin = origin;
     m_lastEventId = lastEventId;
     m_source = source;
     m_ports = ports;
 }
 
+// FIXME: Remove this when we have custom ObjC binding support.
+SerializedScriptValue* MessageEvent::data() const
+{
+    // WebSocket is not exposed in ObjC bindings, thus the data type should always be SerializedScriptValue.
+    ASSERT(m_dataType == DataTypeSerializedScriptValue);
+    return m_dataAsSerializedScriptValue.get();
+}
+
 // FIXME: remove this when we update the ObjC bindings (bug #28774).
 MessagePort* MessageEvent::messagePort()
 {
index b7f9b02..3a90b1e 100644 (file)
@@ -47,16 +47,22 @@ namespace WebCore {
         {
             return adoptRef(new MessageEvent(data, origin, lastEventId, source, ports));
         }
+        static PassRefPtr<MessageEvent> create(const String& data)
+        {
+            return adoptRef(new MessageEvent(data));
+        }
         virtual ~MessageEvent();
 
         void initMessageEvent(const AtomicString& type, bool canBubble, bool cancelable, PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, DOMWindow* source, PassOwnPtr<MessagePortArray>);
 
-        SerializedScriptValue* data() const { return m_data.get(); }
         const String& origin() const { return m_origin; }
         const String& lastEventId() const { return m_lastEventId; }
         DOMWindow* source() const { return m_source.get(); }
         MessagePortArray* ports() const { return m_ports.get(); }
 
+        // FIXME: Remove this when we have custom ObjC binding support.
+        SerializedScriptValue* data() const;
+
         // FIXME: remove this when we update the ObjC bindings (bug #28774).
         MessagePort* messagePort();
         // FIXME: remove this when we update the ObjC bindings (bug #28774).
@@ -64,11 +70,22 @@ namespace WebCore {
 
         virtual bool isMessageEvent() const;
 
+        enum DataType {
+            DataTypeSerializedScriptValue,
+            DataTypeString
+        };
+        DataType dataType() const { return m_dataType; }
+        SerializedScriptValue* dataAsSerializedScriptValue() const { return m_dataAsSerializedScriptValue.get(); }
+        String dataAsString() const { return m_dataAsString; }
+
     private:
         MessageEvent();
         MessageEvent(PassRefPtr<SerializedScriptValue> data, const String& origin, const String& lastEventId, PassRefPtr<DOMWindow> source, PassOwnPtr<MessagePortArray>);
+        explicit MessageEvent(const String& data);
 
-        RefPtr<SerializedScriptValue> m_data;
+        DataType m_dataType;
+        RefPtr<SerializedScriptValue> m_dataAsSerializedScriptValue;
+        String m_dataAsString;
         String m_origin;
         String m_lastEventId;
         RefPtr<DOMWindow> m_source;
index cf45bb4..9694dee 100644 (file)
@@ -29,12 +29,11 @@ module events {
     interface [
         NoStaticTables
     ] MessageEvent : Event {
-        readonly attribute [CachedAttribute] SerializedScriptValue data;
-
         readonly attribute DOMString origin;
         readonly attribute DOMString lastEventId;
         readonly attribute DOMWindow source;
 #if defined(LANGUAGE_JAVASCRIPT) && LANGUAGE_JAVASCRIPT
+        readonly attribute [CachedAttribute, CustomGetter] any data;
         readonly attribute [CustomGetter] Array ports;
 
         [Custom] void initMessageEvent(in [Optional=CallWithDefaultValue] DOMString typeArg, 
@@ -46,6 +45,10 @@ module events {
                                        in [Optional=CallWithDefaultValue] DOMWindow sourceArg, 
                                        in [Optional=CallWithDefaultValue] Array messagePorts);
 #else
+        // Code generator for ObjC bindings does not support custom bindings, thus there is no good way to
+        // return a variant value. As workaround, expose the data attribute as SerializedScriptValue.
+        readonly attribute SerializedScriptValue data;
+
         // There's no good way to expose an array via the ObjC bindings, so for now just expose a single port.
         readonly attribute MessagePort messagePort;
 
index f1797f0..139249a 100644 (file)
@@ -393,9 +393,7 @@ void WebSocket::didReceiveMessage(const String& msg)
     if (m_state != OPEN && m_state != CLOSING)
         return;
     ASSERT(scriptExecutionContext());
-    RefPtr<MessageEvent> evt = MessageEvent::create();
-    evt->initMessageEvent(eventNames().messageEvent, false, false, SerializedScriptValue::create(msg), "", "", 0, 0);
-    dispatchEvent(evt);
+    dispatchEvent(MessageEvent::create(msg));
 }
 
 void WebSocket::didReceiveMessageError()