Bug 854001 - Part 1: Give workers that are pretending to be on WebIDL bindings separate JSClasses for instance objects and prototype objects. r=peterv, r=bent, a=bajaj
authorBoris Zbarsky <bzbarsky@mit.edu>
Thu, 28 Mar 2013 15:43:33 -0400
changeset 132526 90661ab654d71c39da551889898266f3da2b7e7d
parent 132525 5d85c1b25bcce43702e74e0353d01c47316df462
child 132527 95c8ca26e8bb81d48378974a94a0d3878123e248
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspeterv, bent, bajaj
bugs854001
milestone21.0a2
Bug 854001 - Part 1: Give workers that are pretending to be on WebIDL bindings separate JSClasses for instance objects and prototype objects. r=peterv, r=bent, a=bajaj
dom/workers/Worker.cpp
dom/workers/WorkerScope.cpp
--- a/dom/workers/Worker.cpp
+++ b/dom/workers/Worker.cpp
@@ -25,16 +25,17 @@ USING_WORKERS_NAMESPACE
 using namespace mozilla::dom;
 using mozilla::ErrorResult;
 
 namespace {
 
 class Worker
 {
   static DOMJSClass sClass;
+  static DOMIfaceAndProtoJSClass sProtoClass;
   static JSPropertySpec sProperties[];
   static JSFunctionSpec sFunctions[];
 
   enum
   {
     STRING_onerror = 0,
     STRING_onmessage,
 
@@ -52,27 +53,43 @@ protected:
 
 public:
   static JSClass*
   Class()
   {
     return sClass.ToJSClass();
   }
 
+  static JSClass*
+  ProtoClass()
+  {
+    return sProtoClass.ToJSClass();
+  }
+
+  static DOMClass*
+  DOMClassStruct()
+  {
+    return &sClass.mClass;
+  }
+
   static JSObject*
   InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto,
             bool aMainRuntime)
   {
     JSObject* proto =
-      js::InitClassWithReserved(aCx, aObj, aParentProto, Class(), Construct, 0,
-                                sProperties, sFunctions, NULL, NULL);
+      js::InitClassWithReserved(aCx, aObj, aParentProto, ProtoClass(),
+                                Construct, 0, sProperties, sFunctions,
+                                NULL, NULL);
     if (!proto) {
       return NULL;
     }
 
+    js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
+                        JS::PrivateValue(DOMClassStruct()));
+
     if (!aMainRuntime) {
       WorkerPrivate* parent = GetWorkerPrivateFromContext(aCx);
       parent->AssertIsOnWorkerThread();
 
       JSObject* constructor = JS_GetConstructor(aCx, proto);
       if (!constructor)
         return NULL;
       js::SetFunctionNativeReserved(constructor, CONSTRUCTOR_SLOT_PARENT,
@@ -123,16 +140,20 @@ protected:
     JSObject* obj = JS_NewObject(aCx, aClass, nullptr, nullptr);
     if (!obj) {
       return false;
     }
 
     nsRefPtr<WorkerPrivate> worker =
       WorkerPrivate::Create(aCx, obj, parent, scriptURL, aIsChromeWorker);
     if (!worker) {
+      // It'd be better if we could avoid allocating the JSObject until after we
+      // make sure we have a WorkerPrivate, but failing that we should at least
+      // make sure that the DOM_OBJECT_SLOT always has a PrivateValue.
+      js::SetReservedSlot(obj, DOM_OBJECT_SLOT, JS::PrivateValue(nullptr));
       return false;
     }
 
     // Worker now owned by the JS object.
     NS_ADDREF(worker.get());
     js::SetReservedSlot(obj, DOM_OBJECT_SLOT, PRIVATE_TO_JSVAL(worker));
 
     if (!runtimeService->RegisterWorker(aCx, worker)) {
@@ -286,16 +307,46 @@ DOMJSClass Worker::sClass = {
   },
   {
     INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
     false,
     &sWorkerNativePropertyHooks
   }
 };
 
+DOMIfaceAndProtoJSClass Worker::sProtoClass = {
+  {
+    // XXXbz we use "Worker" here to match sClass so that we can
+    // js::InitClassWithReserved this JSClass and then call
+    // JS_NewObject with our sClass and have it find the right
+    // prototype.
+    "Worker",
+    JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2),
+    JS_PropertyStub,       /* addProperty */
+    JS_PropertyStub,       /* delProperty */
+    JS_PropertyStub,       /* getProperty */
+    JS_StrictPropertyStub, /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    nullptr,               /* finalize */
+    nullptr,               /* checkAccess */
+    nullptr,               /* call */
+    nullptr,               /* hasInstance */
+    nullptr,               /* construct */
+    nullptr,               /* trace */
+    JSCLASS_NO_INTERNAL_MEMBERS
+  },
+  eInterfacePrototype,
+  &sWorkerNativePropertyHooks,
+  "[object Worker]",
+  prototypes::id::_ID_Count,
+  0
+};
+
 JSPropertySpec Worker::sProperties[] = {
   { sEventStrings[STRING_onerror], STRING_onerror, PROPERTY_FLAGS,
     JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
   { sEventStrings[STRING_onmessage], STRING_onmessage, PROPERTY_FLAGS,
     JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
   { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
 };
 
@@ -308,35 +359,51 @@ JSFunctionSpec Worker::sFunctions[] = {
 const char* const Worker::sEventStrings[STRING_COUNT] = {
   "onerror",
   "onmessage"
 };
 
 class ChromeWorker : public Worker
 {
   static DOMJSClass sClass;
+  static DOMIfaceAndProtoJSClass sProtoClass;
 
 public:
   static JSClass*
   Class()
   {
     return sClass.ToJSClass();
   }
 
+  static JSClass*
+  ProtoClass()
+  {
+    return sProtoClass.ToJSClass();
+  }
+
+  static DOMClass*
+  DOMClassStruct()
+  {
+    return &sClass.mClass;
+  }
+
   static JSObject*
   InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto,
             bool aMainRuntime)
   {
     JSObject* proto =
-      js::InitClassWithReserved(aCx, aObj, aParentProto, Class(), Construct, 0,
-                                NULL, NULL, NULL, NULL);
+      js::InitClassWithReserved(aCx, aObj, aParentProto, ProtoClass(),
+                                Construct, 0, NULL, NULL, NULL, NULL);
     if (!proto) {
       return NULL;
     }
 
+    js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
+                        JS::PrivateValue(DOMClassStruct()));
+
     if (!aMainRuntime) {
       WorkerPrivate* parent = GetWorkerPrivateFromContext(aCx);
       parent->AssertIsOnWorkerThread();
 
       JSObject* constructor = JS_GetConstructor(aCx, proto);
       if (!constructor)
         return NULL;
       js::SetFunctionNativeReserved(constructor, CONSTRUCTOR_SLOT_PARENT,
@@ -403,16 +470,46 @@ DOMJSClass ChromeWorker::sClass = {
   },
   {
     INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
     false,
     &sWorkerNativePropertyHooks
   }
 };
 
+DOMIfaceAndProtoJSClass ChromeWorker::sProtoClass = {
+  {
+    // XXXbz we use "ChromeWorker" here to match sClass so that we can
+    // js::InitClassWithReserved this JSClass and then call
+    // JS_NewObject with our sClass and have it find the right
+    // prototype.
+    "ChromeWorker",
+    JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2),
+    JS_PropertyStub,       /* addProperty */
+    JS_PropertyStub,       /* delProperty */
+    JS_PropertyStub,       /* getProperty */
+    JS_StrictPropertyStub, /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    nullptr,               /* finalize */
+    nullptr,               /* checkAccess */
+    nullptr,               /* call */
+    nullptr,               /* hasInstance */
+    nullptr,               /* construct */
+    nullptr,               /* trace */
+    JSCLASS_NO_INTERNAL_MEMBERS
+  },
+  eInterfacePrototype,
+  &sWorkerNativePropertyHooks,
+  "[object ChromeWorker]",
+  prototypes::id::_ID_Count,
+  0
+};
+
 WorkerPrivate*
 Worker::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
                            const char* aFunctionName)
 {
   JSClass* classPtr = JS_GetClass(aObj);
   if (classPtr == Class() || classPtr == ChromeWorker::Class()) {
     return UnwrapDOMObject<WorkerPrivate>(aObj);
   }
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -645,16 +645,17 @@ JSFunctionSpec WorkerGlobalScope::sFunct
 const char* const WorkerGlobalScope::sEventStrings[STRING_COUNT] = {
   "onerror",
   "onclose"
 };
 
 class DedicatedWorkerGlobalScope : public WorkerGlobalScope
 {
   static DOMJSClass sClass;
+  static DOMIfaceAndProtoJSClass sProtoClass;
   static JSPropertySpec sProperties[];
   static JSFunctionSpec sFunctions[];
 
   enum
   {
     STRING_onmessage = 0,
 
     STRING_COUNT
@@ -664,21 +665,39 @@ class DedicatedWorkerGlobalScope : publi
 
 public:
   static JSClass*
   Class()
   {
     return sClass.ToJSClass();
   }
 
+  static JSClass*
+  ProtoClass()
+  {
+    return sProtoClass.ToJSClass();
+  }
+
+  static DOMClass*
+  DOMClassStruct()
+  {
+    return &sClass.mClass;
+  }
+
   static JSObject*
   InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto)
   {
-    return JS_InitClass(aCx, aObj, aParentProto, Class(), Construct, 0,
-                        sProperties, sFunctions, NULL, NULL);
+    JSObject* proto =
+      JS_InitClass(aCx, aObj, aParentProto, ProtoClass(), Construct, 0,
+                   sProperties, sFunctions, NULL, NULL);
+    if (proto) {
+      js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
+                          JS::PrivateValue(DOMClassStruct()));
+    }
+    return proto;
   }
 
   static JSBool
   InitPrivate(JSContext* aCx, JSObject* aObj, WorkerPrivate* aWorkerPrivate)
   {
     JS_ASSERT(JS_GetClass(aObj) == Class());
 
     dom::AllocateProtoAndIfaceCache(aObj);
@@ -865,16 +884,46 @@ DOMJSClass DedicatedWorkerGlobalScope::s
   },
   {
     INTERFACE_CHAIN_1(prototypes::id::EventTarget_workers),
     false,
     &sWorkerNativePropertyHooks
   }
 };
 
+DOMIfaceAndProtoJSClass DedicatedWorkerGlobalScope::sProtoClass = {
+  {
+    // XXXbz we use "DedicatedWorkerGlobalScope" here to match sClass
+    // so that we can JS_InitClass this JSClass and then
+    // call JS_NewObject with our sClass and have it find the right
+    // prototype.
+    "DedicatedWorkerGlobalScope",
+    JSCLASS_IS_DOMIFACEANDPROTOJSCLASS | JSCLASS_HAS_RESERVED_SLOTS(2),
+    JS_PropertyStub,       /* addProperty */
+    JS_PropertyStub,       /* delProperty */
+    JS_PropertyStub,       /* getProperty */
+    JS_StrictPropertyStub, /* setProperty */
+    JS_EnumerateStub,
+    JS_ResolveStub,
+    JS_ConvertStub,
+    nullptr,               /* finalize */
+    nullptr,               /* checkAccess */
+    nullptr,               /* call */
+    nullptr,               /* hasInstance */
+    nullptr,               /* construct */
+    nullptr,               /* trace */
+    JSCLASS_NO_INTERNAL_MEMBERS
+  },
+  eInterfacePrototype,
+  &sWorkerNativePropertyHooks,
+  "[object DedicatedWorkerGlobalScope]",
+  prototypes::id::_ID_Count,
+  0
+};
+
 JSPropertySpec DedicatedWorkerGlobalScope::sProperties[] = {
   { sEventStrings[STRING_onmessage], STRING_onmessage, PROPERTY_FLAGS,
     JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
   { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
 };
 
 JSFunctionSpec DedicatedWorkerGlobalScope::sFunctions[] = {
   JS_FN("postMessage", PostMessage, 1, FUNCTION_FLAGS),