Bug 531225 - 'Workers: Share strings across thread boundary'. r+sr=jst, a=belztner
authorBen Turner <bent.mozilla@gmail.com>
Thu, 18 Feb 2010 13:13:05 -0800
changeset 33594 ae89af4f7cd0b45f3c468bec3049d9e165b4880a
parent 33593 a0e3eac94abf4dbe7624f2212c2dcff8bf53aadf
child 33595 05bca8fcc15cc4f94a8926f1b9c063af34cbd137
push id1065
push userbturner@mozilla.com
push dateThu, 18 Feb 2010 21:25:58 +0000
reviewersbelztner
bugs531225
milestone1.9.2.2pre
Bug 531225 - 'Workers: Share strings across thread boundary'. r+sr=jst, a=belztner
dom/src/threads/nsDOMThreadService.cpp
dom/src/threads/nsDOMThreadService.h
dom/src/threads/nsDOMWorker.cpp
dom/src/threads/nsDOMWorker.h
dom/src/threads/nsDOMWorkerEvents.cpp
dom/src/threads/nsDOMWorkerEvents.h
--- a/dom/src/threads/nsDOMThreadService.cpp
+++ b/dom/src/threads/nsDOMThreadService.cpp
@@ -61,16 +61,17 @@
 
 // Other includes
 #include "jscntxt.h"
 #include "nsAutoLock.h"
 #include "nsAutoPtr.h"
 #include "nsContentUtils.h"
 #include "nsDeque.h"
 #include "nsIClassInfoImpl.h"
+#include "nsStringBuffer.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOM.h"
 #include "nsXPCOMCID.h"
 #include "nsXPCOMCIDInternal.h"
 #include "pratom.h"
 #include "prthread.h"
 
 // DOMWorker includes
@@ -121,16 +122,26 @@ PRUintn gJSContextIndex = BAD_TLS_INDEX;
 
 static const char* sPrefsToWatch[] = {
   "dom.max_script_run_time"
 };
 
 // The length of time the close handler is allowed to run in milliseconds.
 static PRUint32 gWorkerCloseHandlerTimeoutMS = 10000;
 
+static int sStringFinalizerIndex = -1;
+
+static void
+StringFinalizer(JSContext* aCx,
+                JSString* aStr)
+{
+  NS_ASSERTION(aStr, "Null string!");
+  nsStringBuffer::FromData(JS_GetStringChars(aStr))->Release();
+}
+
 /**
  * Simple class to automatically destroy a JSContext to make error handling
  * easier.
  */
 class JSAutoContextDestroyer
 {
 public:
   JSAutoContextDestroyer(JSContext* aCx)
@@ -707,27 +718,31 @@ NS_IMPL_THREADSAFE_ISUPPORTS3(nsDOMThrea
                                                   nsIObserver,
                                                   nsIThreadPoolListener)
 
 nsresult
 nsDOMThreadService::Init()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(!gDOMThreadService, "Only one instance should ever be created!");
+  NS_ASSERTION(sStringFinalizerIndex == -1, "String finalizer already set!");
 
   nsresult rv;
   nsCOMPtr<nsIObserverService> obs =
     do_GetService(NS_OBSERVERSERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_FALSE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   obs.forget(&gObserverService);
 
+  sStringFinalizerIndex = JS_AddExternalStringFinalizer(StringFinalizer);
+  NS_ENSURE_TRUE(sStringFinalizerIndex != -1, NS_ERROR_FAILURE);
+
   RegisterPrefCallbacks();
 
   mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mThreadPool->SetListener(this);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -864,16 +879,25 @@ nsDOMThreadService::Cleanup()
       JS_GC(safeContext);
     }
     NS_RELEASE(gThreadJSContextStack);
   }
 
   // These must be released after the thread pool is shut down.
   NS_IF_RELEASE(gJSRuntimeService);
   NS_IF_RELEASE(gWorkerSecurityManager);
+
+  if (sStringFinalizerIndex != -1) {
+#ifdef DEBUG
+    int index =
+#endif
+    JS_RemoveExternalStringFinalizer(StringFinalizer);
+    NS_ASSERTION(index == sStringFinalizerIndex, "Bad index!");
+    sStringFinalizerIndex = -1;
+  }
 }
 
 nsresult
 nsDOMThreadService::Dispatch(nsDOMWorker* aWorker,
                              nsIRunnable* aRunnable,
                              PRIntervalTime aTimeoutInterval,
                              PRBool aClearQueue)
 {
@@ -1211,34 +1235,69 @@ nsDOMThreadService::ChangeThreadPoolMaxT
       rv = mThreadPool->Dispatch(dummy, NS_DISPATCH_NORMAL);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   return NS_OK;
 }
 
+// static
 nsIJSRuntimeService*
 nsDOMThreadService::JSRuntimeService()
 {
   return gJSRuntimeService;
 }
 
+// static
 nsIThreadJSContextStack*
 nsDOMThreadService::ThreadJSContextStack()
 {
   return gThreadJSContextStack;
 }
 
+// static
 nsIXPCSecurityManager*
 nsDOMThreadService::WorkerSecurityManager()
 {
   return gWorkerSecurityManager;
 }
 
+// static
+jsval
+nsDOMThreadService::ShareStringAsJSVal(JSContext* aCx,
+                                       const nsAString& aString)
+{
+  NS_ASSERTION(sStringFinalizerIndex != -1, "Bad index!");
+  NS_ASSERTION(aCx, "Null context!");
+
+  PRUint32 length = aString.Length();
+  if (!length) {
+    JSAtom* atom = aCx->runtime->atomState.emptyAtom;
+    return ATOM_KEY(atom);
+  }
+
+  nsStringBuffer* buf = nsStringBuffer::FromString(aString);
+  if (!buf) {
+    NS_WARNING("Can't share this string buffer!");
+    return JSVAL_VOID;
+  }
+
+  JSString* str =
+    JS_NewExternalString(aCx, reinterpret_cast<jschar*>(buf->Data()), length,
+                         sStringFinalizerIndex);
+  if (str) {
+    buf->AddRef();
+    return STRING_TO_JSVAL(str);
+  }
+
+  NS_WARNING("JS_NewExternalString failed!");
+  return JSVAL_VOID;
+}
+
 /**
  * See nsIEventTarget
  */
 NS_IMETHODIMP
 nsDOMThreadService::Dispatch(nsIRunnable* aEvent,
                              PRUint32 aFlags)
 {
   NS_ENSURE_ARG_POINTER(aEvent);
--- a/dom/src/threads/nsDOMThreadService.h
+++ b/dom/src/threads/nsDOMThreadService.h
@@ -103,16 +103,19 @@ public:
 
   static JSContext* GetCurrentContext();
 
   // Easy access to the services we care about.
   static nsIJSRuntimeService* JSRuntimeService();
   static nsIThreadJSContextStack* ThreadJSContextStack();
   static nsIXPCSecurityManager* WorkerSecurityManager();
 
+  static jsval ShareStringAsJSVal(JSContext* aCx,
+                                  const nsAString& aString);
+
   void CancelWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
   void SuspendWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
   void ResumeWorkersForGlobal(nsIScriptGlobalObject* aGlobalObject);
 
   nsresult ChangeThreadPoolMaxThreads(PRInt16 aDelta);
 
 private:
   nsDOMThreadService();
--- a/dom/src/threads/nsDOMWorker.cpp
+++ b/dom/src/threads/nsDOMWorker.cpp
@@ -423,108 +423,96 @@ WriteCallback(const jschar* aBuffer,
 {
   nsJSONWriter* writer = static_cast<nsJSONWriter*>(aData);
 
   nsresult rv = writer->Write((const PRUnichar*)aBuffer, (PRUint32)aLength);
   return NS_SUCCEEDED(rv) ? JS_TRUE : JS_FALSE;
 }
 
 static nsresult
-GetStringForArgument(nsAString& aString,
+GetStringForArgument(JSContext* aCx,
+                     jsval aVal,
                      PRBool* aIsJSON,
-                     PRBool* aIsPrimitive)
+                     PRBool* aIsPrimitive,
+                     nsAutoJSValHolder& _retval)
 {
   NS_ASSERTION(aIsJSON && aIsPrimitive, "Null pointer!");
 
-  nsIXPConnect* xpc = nsContentUtils::XPConnect();
-  NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
-
-  nsAXPCNativeCallContext* cc;
-  nsresult rv = xpc->GetCurrentNativeCallContext(&cc);
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED);
-
-  PRUint32 argc;
-  rv = cc->GetArgc(&argc);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (!argc) {
-    return NS_ERROR_XPC_NOT_ENOUGH_ARGS;
-  }
-
-  jsval* argv;
-  rv = cc->GetArgvPtr(&argv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  JSContext* cx;
-  rv = cc->GetJSContext(&cx);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  JSAutoRequest ar(cx);
-
-  if (JSVAL_IS_STRING(argv[0])) {
-    aString.Assign(nsDependentJSString(JSVAL_TO_STRING(argv[0])));
+  if (JSVAL_IS_STRING(aVal)) {
     *aIsJSON = *aIsPrimitive = PR_FALSE;
+    _retval = aVal;
     return NS_OK;
   }
 
   nsAutoJSValHolder jsonVal;
 
-  JSBool ok = jsonVal.Hold(cx);
+  JSBool ok = jsonVal.Hold(aCx);
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
-  if (JSVAL_IS_PRIMITIVE(argv[0])) {
+  if (JSVAL_IS_PRIMITIVE(aVal)) {
     // Only objects can be serialized through JSON, currently, so if we've been
     // given a primitive we set it as a property on a dummy object before
     // sending it to the serializer.
-    JSObject* obj = JS_NewObject(cx, NULL, NULL, NULL);
+    JSObject* obj = JS_NewObject(aCx, NULL, NULL, NULL);
     NS_ENSURE_TRUE(obj, NS_ERROR_OUT_OF_MEMORY);
 
     jsonVal = obj;
 
-    ok = JS_DefineProperty(cx, obj, JSON_PRIMITIVE_PROPNAME, argv[0], NULL,
+    ok = JS_DefineProperty(aCx, obj, JSON_PRIMITIVE_PROPNAME, aVal, NULL,
                            NULL, JSPROP_ENUMERATE);
     NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
 
     *aIsPrimitive = PR_TRUE;
   }
   else {
-    jsonVal = argv[0];
+    jsonVal = aVal;
 
     *aIsPrimitive = PR_FALSE;
   }
 
   JSType type;
   jsval* vp = jsonVal.ToJSValPtr();
 
   // This may change vp if there is a 'toJSON' function on the object.
-  ok = JS_TryJSON(cx, vp);
+  ok = JS_TryJSON(aCx, vp);
   if (!(ok && !JSVAL_IS_PRIMITIVE(*vp) &&
-        (type = JS_TypeOfValue(cx, *vp)) != JSTYPE_FUNCTION &&
+        (type = JS_TypeOfValue(aCx, *vp)) != JSTYPE_FUNCTION &&
         type != JSTYPE_XML)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   // Make sure to hold the new vp in case it changed.
   jsonVal = *vp;
 
   nsJSONWriter writer;
 
-  ok = JS_Stringify(cx, jsonVal.ToJSValPtr(), NULL, JSVAL_NULL, WriteCallback, &writer);
+  ok = JS_Stringify(aCx, jsonVal.ToJSValPtr(), NULL, JSVAL_NULL, WriteCallback,
+                    &writer);
   if (!ok) {
     return NS_ERROR_XPC_BAD_CONVERT_JS;
   }
 
   NS_ENSURE_TRUE(writer.DidWrite(), NS_ERROR_UNEXPECTED);
 
   writer.FlushBuffer();
 
-  aString.Assign(writer.mOutputString);
+  _retval = nsDOMThreadService::ShareStringAsJSVal(aCx, writer.mOutputString);
+  if (!JSVAL_IS_STRING(_retval)) {
+    // Yuck, we can't share.
+    const jschar* buf =
+      reinterpret_cast<const jschar*>(writer.mOutputString.get());
+    JSString* str = JS_NewUCStringCopyN(aCx, buf, writer.mOutputString.Length());
+    if (!str) {
+      JS_ReportOutOfMemory(aCx);
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    _retval = STRING_TO_JSVAL(str);
+  }
+
   *aIsJSON = PR_TRUE;
-
   return NS_OK;
 }
 
 nsDOMWorkerScope::nsDOMWorkerScope(nsDOMWorker* aWorker)
 : mWorker(aWorker),
   mWrappedNative(nsnull),
   mHasOnerror(PR_FALSE)
 {
@@ -764,23 +752,17 @@ NS_IMETHODIMP
 nsDOMWorkerScope::PostMessage(/* JSObject aMessage */)
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   if (mWorker->IsCanceled()) {
     return NS_ERROR_ABORT;
   }
 
-  nsString message;
-  PRBool isJSON, isPrimitive;
-
-  nsresult rv = GetStringForArgument(message, &isJSON, &isPrimitive);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return mWorker->PostMessageInternal(message, isJSON, isPrimitive, PR_FALSE);
+  return mWorker->PostMessageInternal(PR_FALSE);
 }
 
 NS_IMETHODIMP
 nsDOMWorkerScope::Close()
 {
   NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
 
   return mWorker->Close();
@@ -1450,30 +1432,65 @@ nsDOMWorker::IsClosing()
 PRBool
 nsDOMWorker::IsSuspended()
 {
   nsAutoLock lock(mLock);
   return mSuspended;
 }
 
 nsresult
-nsDOMWorker::PostMessageInternal(const nsAString& aMessage,
-                                 PRBool aIsJSON,
-                                 PRBool aIsPrimitive,
-                                 PRBool aToInner)
+nsDOMWorker::PostMessageInternal(PRBool aToInner)
 {
+  nsIXPConnect* xpc = nsContentUtils::XPConnect();
+  NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
+
+  nsAXPCNativeCallContext* cc;
+  nsresult rv = xpc->GetCurrentNativeCallContext(&cc);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED);
+
+  PRUint32 argc;
+  rv = cc->GetArgc(&argc);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!argc) {
+    return NS_ERROR_XPC_NOT_ENOUGH_ARGS;
+  }
+
+  jsval* argv;
+  rv = cc->GetArgvPtr(&argv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  JSContext* cx;
+  rv = cc->GetJSContext(&cx);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  JSAutoRequest ar(cx);
+
+  nsAutoJSValHolder val;
+  if (!val.Hold(cx)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  PRBool isJSON, isPrimitive;
+  rv = GetStringForArgument(cx, argv[0], &isJSON, &isPrimitive, val);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(JSVAL_IS_STRING(val), "Bad jsval!");
+
   nsRefPtr<nsDOMWorkerMessageEvent> message = new nsDOMWorkerMessageEvent();
   NS_ENSURE_TRUE(message, NS_ERROR_OUT_OF_MEMORY);
 
-  nsresult rv = message->InitMessageEvent(NS_LITERAL_STRING("message"),
-                                          PR_FALSE, PR_FALSE, aMessage,
-                                          EmptyString(), nsnull);
+  rv = message->InitMessageEvent(NS_LITERAL_STRING("message"), PR_FALSE,
+                                 PR_FALSE, EmptyString(), EmptyString(),
+                                 nsnull);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  message->SetJSONData(aIsJSON, aIsPrimitive);
+  rv = message->SetJSONData(cx, val, isJSON, isPrimitive);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   nsRefPtr<nsDOMFireEventRunnable> runnable =
     new nsDOMFireEventRunnable(this, message, aToInner);
   NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY);
 
   // If aToInner is true then we want to target the runnable at this worker's
   // thread. Otherwise we need to target the parent's thread.
   nsDOMWorker* target = aToInner ? this : mParent;
@@ -1961,23 +1978,17 @@ nsDOMWorker::PostMessage(/* JSObject aMe
     nsAutoLock lock(mLock);
     // There's no reason to dispatch this message after the close handler has
     // been triggered since it will never be allowed to run.
     if (mStatus != eRunning) {
       return NS_OK;
     }
   }
 
-  nsString message;
-  PRBool isJSON, isPrimitive;
-
-  nsresult rv = GetStringForArgument(message, &isJSON, &isPrimitive);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  return PostMessageInternal(message, isJSON, isPrimitive, PR_TRUE);
+  return PostMessageInternal(PR_TRUE);
 }
 
 /**
  * See nsIWorker
  */
 NS_IMETHODIMP
 nsDOMWorker::GetOnerror(nsIDOMEventListener** aOnerror)
 {
--- a/dom/src/threads/nsDOMWorker.h
+++ b/dom/src/threads/nsDOMWorker.h
@@ -230,20 +230,17 @@ public:
 
     // The close handler has run and the worker is effectively dead.
     eKilled
   };
 
 private:
   ~nsDOMWorker();
 
-  nsresult PostMessageInternal(const nsAString& aMessage,
-                               PRBool aIsJSON,
-                               PRBool aIsPrimitive,
-                               PRBool aToInner);
+  nsresult PostMessageInternal(PRBool aToInner);
 
   PRBool CompileGlobalObject(JSContext* aCx);
 
   PRUint32 NextTimeoutId() {
     return ++mNextTimeoutId;
   }
 
   nsresult AddFeature(nsDOMWorkerFeature* aFeature,
--- a/dom/src/threads/nsDOMWorkerEvents.cpp
+++ b/dom/src/threads/nsDOMWorkerEvents.cpp
@@ -258,36 +258,57 @@ nsDOMWorkerEvent::InitEvent(const nsAStr
 NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerMessageEvent, nsDOMWorkerEvent,
                                                       nsIWorkerMessageEvent)
 
 NS_IMPL_CI_INTERFACE_GETTER2(nsDOMWorkerMessageEvent, nsIDOMEvent,
                                                       nsIWorkerMessageEvent)
 
 NS_IMPL_THREADSAFE_DOM_CI_GETINTERFACES(nsDOMWorkerMessageEvent)
 
+nsresult
+nsDOMWorkerMessageEvent::SetJSONData(JSContext* aCx,
+                                     jsval aData,
+                                     PRBool aIsJSON,
+                                     PRBool aIsPrimitive)
+{
+  NS_ASSERTION(JSVAL_IS_STRING(aData), "Bad jsval!");
+
+  mIsJSON = aIsJSON ? PR_TRUE : PR_FALSE;
+  mIsPrimitive = aIsPrimitive ? PR_TRUE : PR_FALSE;
+
+  if (!mDataVal.Hold(aCx)) {
+    NS_WARNING("Failed to hold jsval!");
+    return NS_ERROR_FAILURE;
+  }
+
+  mDataVal = aData;
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsDOMWorkerMessageEvent::GetData(nsAString& aData)
 {
-  if (!mIsJSON) {
-    aData.Assign(mData);
-    return NS_OK;
-  }
-
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   NS_ENSURE_TRUE(xpc, NS_ERROR_UNEXPECTED);
 
   nsAXPCNativeCallContext* cc;
   nsresult rv = xpc->GetCurrentNativeCallContext(&cc);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(cc, NS_ERROR_UNEXPECTED);
 
   jsval* retval;
   rv = cc->GetRetValPtr(&retval);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  if (!mIsJSON) {
+    cc->SetReturnValueWasSet(PR_TRUE);
+    *retval = mDataVal;
+    return NS_OK;
+  }
+
   if (mHaveCachedJSVal) {
     cc->SetReturnValueWasSet(PR_TRUE);
     *retval = mCachedJSVal;
     return NS_OK;
   }
 
   if (mHaveAttemptedConversion) {
     // Don't try to convert again if the first time around we saw an error.
@@ -299,24 +320,27 @@ nsDOMWorkerMessageEvent::GetData(nsAStri
   rv = cc->GetJSContext(&cx);
   NS_ENSURE_SUCCESS(rv, rv);
 
   JSAutoRequest ar(cx);
 
   JSBool ok = mCachedJSVal.Hold(cx);
   NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
 
+  NS_ASSERTION(JSVAL_IS_STRING(mDataVal), "Bad jsval!");
+  JSString* str = JSVAL_TO_STRING(mDataVal);
+
   JSONParser* parser = JS_BeginJSONParse(cx, mCachedJSVal.ToJSValPtr());
   NS_ENSURE_TRUE(parser, NS_ERROR_UNEXPECTED);
 
   // This is slightly sneaky, but now that JS_BeginJSONParse succeeded we always
   // need call JS_FinishJSONParse even if JS_ConsumeJSONText fails. We'll report
   // an error if either failed, though.
-  ok = JS_ConsumeJSONText(cx, parser, (jschar*)mData.get(),
-                          (uint32)mData.Length());
+  ok = JS_ConsumeJSONText(cx, parser, JS_GetStringChars(str),
+                          JS_GetStringLength(str));
 
   // Note the '&& ok' after the call here!
   ok = JS_FinishJSONParse(cx, parser, JSVAL_NULL) && ok;
   if (!ok) {
     mCachedJSVal = JSVAL_NULL;
     return NS_ERROR_UNEXPECTED;
   }
 
@@ -331,17 +355,17 @@ nsDOMWorkerMessageEvent::GetData(nsAStri
       mCachedJSVal = JSVAL_NULL;
       return NS_ERROR_UNEXPECTED;
     }
 
     mCachedJSVal = primitive;
   }
 
   // We no longer need to hold this copy of the data around.
-  mData.Truncate();
+  mDataVal.Release();
 
   // Now that everything has succeeded we'll set this flag so that we return the
   // cached jsval in the future.
   mHaveCachedJSVal = PR_TRUE;
 
   *retval = mCachedJSVal;
   cc->SetReturnValueWasSet(PR_TRUE);
   return NS_OK;
@@ -365,17 +389,16 @@ nsDOMWorkerMessageEvent::GetSource(nsISu
 NS_IMETHODIMP
 nsDOMWorkerMessageEvent::InitMessageEvent(const nsAString& aTypeArg,
                                           PRBool aCanBubbleArg,
                                           PRBool aCancelableArg,
                                           const nsAString& aDataArg,
                                           const nsAString& aOriginArg,
                                           nsISupports* aSourceArg)
 {
-  mData.Assign(aDataArg);
   mOrigin.Assign(aOriginArg);
   mSource = aSourceArg;
   return nsDOMWorkerEvent::InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
 }
 
 NS_IMPL_ISUPPORTS_INHERITED1(nsDOMWorkerProgressEvent, nsDOMWorkerEvent,
                                                        nsIDOMProgressEvent)
 
--- a/dom/src/threads/nsDOMWorkerEvents.h
+++ b/dom/src/threads/nsDOMWorkerEvents.h
@@ -209,26 +209,26 @@ public:
   NS_FORWARD_NSIDOMEVENT(nsDOMWorkerEvent::)
   NS_DECL_NSIWORKERMESSAGEEVENT
   NS_DECL_NSICLASSINFO_GETINTERFACES
 
   nsDOMWorkerMessageEvent()
   : mIsJSON(PR_FALSE), mIsPrimitive(PR_FALSE), mHaveCachedJSVal(PR_FALSE),
     mHaveAttemptedConversion(PR_FALSE) { }
 
-  void SetJSONData(PRBool aIsJSON, PRBool aIsPrimitive) {
-    mIsJSON = aIsJSON ? PR_TRUE : PR_FALSE;
-    mIsPrimitive = aIsPrimitive ? PR_TRUE : PR_FALSE;
-  }
+  nsresult SetJSONData(JSContext* aCx,
+                       jsval aData,
+                       PRBool aIsJSON,
+                       PRBool aIsPrimitive);
 
 protected:
   nsString mOrigin;
   nsCOMPtr<nsISupports> mSource;
 
-  nsString mData;
+  nsAutoJSValHolder mDataVal;
   nsAutoJSValHolder mCachedJSVal;
 
   PRPackedBool mIsJSON;
   PRPackedBool mIsPrimitive;
   PRPackedBool mHaveCachedJSVal;
   PRPackedBool mHaveAttemptedConversion;
 };