Bug 897678 - Make worker code stop using propertyops, as a step toward removing propertyops altogether. r=mrbkap, a=bajaj
authorJeff Walden <jwalden@mit.edu>
Thu, 08 Aug 2013 15:55:23 -0700
changeset 119954 e4cb5a852e3d7807e4da026e885186eb8dad5885
parent 119953 8ab36ae7129b6d15563cca8d22efd7004238425c
child 119955 0c3b698a2bfda01e0a1341e3fe684f4c41b3feda
push id1092
push userjwalden@mit.edu
push dateFri, 18 Oct 2013 17:33:53 +0000
reviewersmrbkap, bajaj
bugs897678
milestone18.1
Bug 897678 - Make worker code stop using propertyops, as a step toward removing propertyops altogether. r=mrbkap, a=bajaj
dom/workers/Events.cpp
dom/workers/Exceptions.cpp
dom/workers/File.cpp
dom/workers/Worker.cpp
dom/workers/WorkerScope.cpp
dom/workers/Workers.h
--- a/dom/workers/Events.cpp
+++ b/dom/workers/Events.cpp
@@ -2,47 +2,42 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Util.h"
 
 #include "Events.h"
+#include "mozilla/dom/BindingUtils.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "nsTraceRefcnt.h"
 
 #include "WorkerInlines.h"
 #include "WorkerPrivate.h"
 
-#define PROPERTY_FLAGS \
-  (JSPROP_ENUMERATE | JSPROP_SHARED)
-
 #define FUNCTION_FLAGS \
   JSPROP_ENUMERATE
 
-#define CONSTANT_FLAGS \
-  JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY
-
 using namespace mozilla;
 USING_WORKERS_NAMESPACE
 
 namespace {
 
 class Event : public PrivatizableBase
 {
   static JSClass sClass;
   static JSClass sMainRuntimeClass;
 
   static JSPropertySpec sProperties[];
   static JSFunctionSpec sFunctions[];
-  static JSPropertySpec sStaticProperties[];
+  static dom::ConstantSpec sStaticConstants[];
 
 protected:
   bool mStopPropagationCalled;
   bool mStopImmediatePropagationCalled;
 
 public:
   static bool
   IsThisClass(JSClass* aClass)
@@ -72,19 +67,28 @@ public:
           parentProto = JSVAL_TO_OBJECT(protoVal);
         }
       }
     }
 
     JSClass* clasp = parentProto ? &sMainRuntimeClass : &sClass;
 
     JSObject* proto = JS_InitClass(aCx, aObj, parentProto, clasp, Construct, 0,
-                                   sProperties, sFunctions, sStaticProperties,
-                                   NULL);
-    if (proto && !JS_DefineProperties(aCx, proto, sStaticProperties)) {
+                                   sProperties, sFunctions, NULL, NULL);
+    if (!proto) {
+      return NULL;
+    }
+
+    JSObject* ctor = JS_GetConstructor(aCx, proto);
+    if (!ctor) {
+      return NULL;
+    }
+
+    if (!dom::DefineConstants(aCx, ctor, sStaticConstants) ||
+        !dom::DefineConstants(aCx, proto, sStaticConstants)) {
       return NULL;
     }
 
     return proto;
   }
 
   static JSObject*
   Create(JSContext* aCx, JSObject* aParent, JSString* aType, bool aBubbles,
@@ -140,17 +144,17 @@ protected:
     MOZ_COUNT_CTOR(mozilla::dom::workers::Event);
   }
 
   virtual ~Event()
   {
     MOZ_COUNT_DTOR(mozilla::dom::workers::Event);
   }
 
-  enum {
+  enum EventPhase {
     CAPTURING_PHASE = 1,
     AT_TARGET = 2,
     BUBBLING_PHASE = 3
   };
 
   enum SLOT {
     SLOT_type = 0,
     SLOT_target,
@@ -214,42 +218,44 @@ private:
 
   static void
   Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(IsThisClass(JS_GetClass(aObj)));
     delete GetJSPrivateSafeish<Event>(aObj);
   }
 
-  static JSBool
-  GetProperty(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool
+  IsEvent(const JS::Value& v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-
-    int32 slot = JSID_TO_INT(aIdval);
+    return v.isObject() && GetPrivate(&v.toObject()) != nullptr;
+  }
 
-    const char*& name = sProperties[slot - SLOT_FIRST].name;
-    if (!GetInstancePrivate(aCx, aObj, name)) {
-      return false;
-    }
-
-    aVp.set(JS_GetReservedSlot(aObj, slot));
+  template<SLOT Slot>
+  static bool
+  GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    aArgs.rval().set(JS_GetReservedSlot(&aArgs.thisv().toObject(), Slot));
     return true;
   }
 
-  static JSBool
-  GetConstant(JSContext* aCx, JSHandleObject aObj, JSHandleId idval, JSMutableHandleValue aVp)
+  // This struct (versus just templating the method directly) is needed only for
+  // gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
+  // GetProperty<Slot> and friends in the JSPropertySpec[] below.
+  template<SLOT Slot>
+  struct Property
   {
-    JS_ASSERT(JSID_IS_INT(idval));
-    JS_ASSERT(JSID_TO_INT(idval) >= CAPTURING_PHASE &&
-              JSID_TO_INT(idval) <= BUBBLING_PHASE);
-
-    aVp.set(INT_TO_JSVAL(JSID_TO_INT(idval)));
-    return true;
-  }
+    static JSBool
+    Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+    {
+      MOZ_STATIC_ASSERT(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
+      JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+      return JS::CallNonGenericMethod<IsEvent, GetPropertyImpl<Slot> >(aCx, args);
+    }
+  };
 
   static JSBool
   StopPropagation(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
     if (!obj) {
       return false;
     }
@@ -336,52 +342,50 @@ private:
   };
 
 DECL_EVENT_CLASS(Event::sClass, "Event")
 DECL_EVENT_CLASS(Event::sMainRuntimeClass, "WorkerEvent")
 
 #undef DECL_EVENT_CLASS
 
 JSPropertySpec Event::sProperties[] = {
-  { "type", SLOT_type, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "target", SLOT_target, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "currentTarget", SLOT_currentTarget, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "eventPhase", SLOT_eventPhase, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "bubbles", SLOT_bubbles, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "cancelable", SLOT_cancelable, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "timeStamp", SLOT_timeStamp, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "defaultPrevented", SLOT_defaultPrevented, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "isTrusted", SLOT_isTrusted, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("type", Property<SLOT_type>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("target", Property<SLOT_target>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("currentTarget", Property<SLOT_currentTarget>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("eventPhase", Property<SLOT_eventPhase>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("bubbles", Property<SLOT_bubbles>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("cancelable", Property<SLOT_cancelable>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("timeStamp", Property<SLOT_timeStamp>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("defaultPrevented", Property<SLOT_defaultPrevented>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("isTrusted", Property<SLOT_isTrusted>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 JSFunctionSpec Event::sFunctions[] = {
   JS_FN("stopPropagation", StopPropagation, 0, FUNCTION_FLAGS),
   JS_FN("preventDefault", PreventDefault, 0, FUNCTION_FLAGS),
   JS_FN("initEvent", InitEvent, 3, FUNCTION_FLAGS),
   JS_FN("stopImmediatePropagation", StopImmediatePropagation, 0, FUNCTION_FLAGS),
   JS_FS_END
 };
 
-JSPropertySpec Event::sStaticProperties[] = {
-  { "CAPTURING_PHASE", CAPTURING_PHASE, CONSTANT_FLAGS,
-    JSOP_WRAPPER(GetConstant), JSOP_NULLWRAPPER },
-  { "AT_TARGET", AT_TARGET, CONSTANT_FLAGS, JSOP_WRAPPER(GetConstant), JSOP_NULLWRAPPER },
-  { "BUBBLING_PHASE", BUBBLING_PHASE, CONSTANT_FLAGS,
-    JSOP_WRAPPER(GetConstant), JSOP_NULLWRAPPER },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+dom::ConstantSpec Event::sStaticConstants[] = {
+  { "CAPTURING_PHASE", JS::Int32Value(CAPTURING_PHASE) },
+  { "AT_TARGET", JS::Int32Value(AT_TARGET) },
+  { "BUBBLING_PHASE", JS::Int32Value(BUBBLING_PHASE) },
+  { NULL, JS::UndefinedValue() }
 };
 
 class MessageEvent : public Event
 {
   static JSClass sClass;
   static JSClass sMainRuntimeClass;
 
   static JSPropertySpec sProperties[];
@@ -507,59 +511,79 @@ private:
       JS_freeop(aFop, priv->mData);
 #ifdef DEBUG
       priv->mData = NULL;
 #endif
       delete priv;
     }
   }
 
-  static JSBool
-  GetProperty(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool
+  IsMessageEvent(const JS::Value& v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-
-    int32 slot = JSID_TO_INT(aIdval);
+    if (!v.isObject())
+      return false;
+    JSObject* obj = &v.toObject();
+    return IsThisClass(JS_GetClass(obj)) &&
+           GetJSPrivateSafeish<MessageEvent>(obj) != NULL;
+  }
 
-    JS_ASSERT(slot >= SLOT_data && slot < SLOT_COUNT);
+  template<SLOT Slot>
+  static bool
+  GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JSObject* obj = &aArgs.thisv().toObject();
 
-    const char*& name = sProperties[slot - SLOT_FIRST].name;
-    MessageEvent* event = GetInstancePrivate(aCx, aObj, name);
-    if (!event) {
-      return false;
-    }
+    const char* name = sProperties[Slot - SLOT_FIRST].name;
+    MessageEvent* event = GetInstancePrivate(aCx, obj, name);
+    MOZ_ASSERT(event);
 
     // Deserialize and save the data value if we can.
-    if (slot == SLOT_data && event->mData) {
+    if (Slot == SLOT_data && event->mData) {
       JSAutoStructuredCloneBuffer buffer;
       buffer.adopt(event->mData, event->mDataByteCount);
 
       event->mData = NULL;
       event->mDataByteCount = 0;
 
       // Release reference to objects that were AddRef'd for
       // cloning into worker when array goes out of scope.
       nsTArray<nsCOMPtr<nsISupports> > clonedObjects;
       clonedObjects.SwapElements(event->mClonedObjects);
 
       jsval data;
       if (!buffer.read(aCx, &data,
                        WorkerStructuredCloneCallbacks(event->mMainRuntime))) {
         return false;
       }
-      JS_SetReservedSlot(aObj, slot, data);
+      JS_SetReservedSlot(obj, Slot, data);
 
-      aVp.set(data);
+      aArgs.rval().set(data);
       return true;
     }
 
-    aVp.set(JS_GetReservedSlot(aObj, slot));
+    aArgs.rval().set(JS_GetReservedSlot(obj, Slot));
     return true;
   }
 
+  // This struct (versus just templating the method directly) is needed only for
+  // gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
+  // GetProperty<Slot> and friends in the JSPropertySpec[] below.
+  template<SLOT Slot>
+  struct Property
+  {
+    static JSBool
+    Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+    {
+      MOZ_STATIC_ASSERT(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
+      JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+      return JS::CallNonGenericMethod<IsMessageEvent, GetPropertyImpl<Slot> >(aCx, args);
+    }
+  };
+
   static JSBool
   InitMessageEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
     if (!obj) {
       return false;
     }
 
@@ -591,23 +615,23 @@ private:
   };
 
 DECL_MESSAGEEVENT_CLASS(MessageEvent::sClass, "MessageEvent")
 DECL_MESSAGEEVENT_CLASS(MessageEvent::sMainRuntimeClass, "WorkerMessageEvent")
 
 #undef DECL_MESSAGEEVENT_CLASS
 
 JSPropertySpec MessageEvent::sProperties[] = {
-  { "data", SLOT_data, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "origin", SLOT_origin, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "source", SLOT_source, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("data", Property<SLOT_data>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("origin", Property<SLOT_origin>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("source", Property<SLOT_source>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 JSFunctionSpec MessageEvent::sFunctions[] = {
   JS_FN("initMessageEvent", InitMessageEvent, 6, FUNCTION_FLAGS),
   JS_FS_END
 };
 
 class ErrorEvent : public Event
@@ -716,35 +740,49 @@ private:
 
   static void
   Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(IsThisClass(JS_GetClass(aObj)));
     delete GetJSPrivateSafeish<ErrorEvent>(aObj);
   }
 
-  static JSBool
-  GetProperty(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool
+  IsErrorEvent(const JS::Value& v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-
-    int32 slot = JSID_TO_INT(aIdval);
+    if (!v.isObject())
+      return false;
+    JSObject* obj = &v.toObject();
+    return IsThisClass(JS_GetClass(obj)) &&
+           GetJSPrivateSafeish<ErrorEvent>(obj) != nullptr;
+  }
 
-    JS_ASSERT(slot >= SLOT_message && slot < SLOT_COUNT);
-
-    const char*& name = sProperties[slot - SLOT_FIRST].name;
-    ErrorEvent* event = GetInstancePrivate(aCx, aObj, name);
-    if (!event) {
-      return false;
-    }
-
-    aVp.set(JS_GetReservedSlot(aObj, slot));
+  template<SLOT Slot>
+  static bool
+  GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    aArgs.rval().set(JS_GetReservedSlot(&aArgs.thisv().toObject(), Slot));
     return true;
   }
 
+  // This struct (versus just templating the method directly) is needed only for
+  // gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
+  // GetProperty<Slot> and friends in the JSPropertySpec[] below.
+  template<SLOT Slot>
+  struct Property
+  {
+    static JSBool
+    Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+    {
+      MOZ_STATIC_ASSERT(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
+      JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+      return JS::CallNonGenericMethod<IsErrorEvent, GetPropertyImpl<Slot> >(aCx, args);
+    }
+  };
+
   static JSBool
   InitErrorEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
     if (!obj) {
       return false;
     }
 
@@ -777,23 +815,23 @@ private:
   };
 
 DECL_ERROREVENT_CLASS(ErrorEvent::sClass, "ErrorEvent")
 DECL_ERROREVENT_CLASS(ErrorEvent::sMainRuntimeClass, "WorkerErrorEvent")
 
 #undef DECL_ERROREVENT_CLASS
 
 JSPropertySpec ErrorEvent::sProperties[] = {
-  { "message", SLOT_message, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "filename", SLOT_filename, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "lineno", SLOT_lineno, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("message", Property<SLOT_message>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("filename", Property<SLOT_filename>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("lineno", Property<SLOT_lineno>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 JSFunctionSpec ErrorEvent::sFunctions[] = {
   JS_FN("initErrorEvent", InitErrorEvent, 6, FUNCTION_FLAGS),
   JS_FS_END
 };
 
 class ProgressEvent : public Event
@@ -896,35 +934,49 @@ private:
 
   static void
   Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
     delete GetJSPrivateSafeish<ProgressEvent>(aObj);
   }
 
-  static JSBool
-  GetProperty(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool
+  IsProgressEvent(const JS::Value& v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-
-    int32 slot = JSID_TO_INT(aIdval);
+    if (!v.isObject())
+      return false;
+    JSObject* obj = &v.toObject();
+    return JS_GetClass(obj) == &sClass &&
+           GetJSPrivateSafeish<ProgressEvent>(obj) != nullptr;
+  }
 
-    JS_ASSERT(slot >= SLOT_lengthComputable && slot < SLOT_COUNT);
-
-    const char*& name = sProperties[slot - SLOT_FIRST].name;
-    ProgressEvent* event = GetInstancePrivate(aCx, aObj, name);
-    if (!event) {
-      return false;
-    }
-
-    aVp.set(JS_GetReservedSlot(aObj, slot));
+  template<SLOT Slot>
+  static bool
+  GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    aArgs.rval().set(JS_GetReservedSlot(&aArgs.thisv().toObject(), Slot));
     return true;
   }
 
+  // This struct (versus just templating the method directly) is needed only for
+  // gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
+  // GetProperty<Slot> and friends in the JSPropertySpec[] below.
+  template<SLOT Slot>
+  struct Property
+  {
+    static JSBool
+    Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+    {
+      MOZ_STATIC_ASSERT(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
+      JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+      return JS::CallNonGenericMethod<IsProgressEvent, GetPropertyImpl<Slot> >(aCx, args);
+    }
+  };
+
   static JSBool
   InitProgressEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
     if (!obj) {
       return false;
     }
 
@@ -951,23 +1003,23 @@ private:
 JSClass ProgressEvent::sClass = {
   "ProgressEvent",
   JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT),
   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize
 };
 
 JSPropertySpec ProgressEvent::sProperties[] = {
-  { "lengthComputable", SLOT_lengthComputable, PROPERTY_FLAGS,
-    JSOP_WRAPPER(GetProperty), JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "loaded", SLOT_loaded, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "total", SLOT_total, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("lengthComputable", Property<SLOT_lengthComputable>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("loaded", Property<SLOT_loaded>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PSGS("total", Property<SLOT_total>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 JSFunctionSpec ProgressEvent::sFunctions[] = {
   JS_FN("initProgressEvent", InitProgressEvent, 6, FUNCTION_FLAGS),
   JS_FS_END
 };
 
 Event*
--- a/dom/workers/Exceptions.cpp
+++ b/dom/workers/Exceptions.cpp
@@ -7,16 +7,17 @@
 #include "Exceptions.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jsprf.h"
 #include "mozilla/Util.h"
 #include "nsDOMException.h"
 #include "nsTraceRefcnt.h"
+#include "mozilla/dom/BindingUtils.h"
 
 #include "WorkerInlines.h"
 
 #define PROPERTY_FLAGS \
   (JSPROP_ENUMERATE | JSPROP_SHARED)
 
 #define CONSTANT_FLAGS \
   JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY
@@ -26,34 +27,44 @@ USING_WORKERS_NAMESPACE
 
 namespace {
 
 class DOMException : public PrivatizableBase
 {
   static JSClass sClass;
   static JSPropertySpec sProperties[];
   static JSFunctionSpec sFunctions[];
-  static JSPropertySpec sStaticProperties[];
+  static dom::ConstantSpec sStaticConstants[];
 
   enum SLOT {
     SLOT_code = 0,
     SLOT_name,
     SLOT_message,
 
-    SLOT_COUNT
+    SLOT_COUNT,
+    SLOT_FIRST = SLOT_code
   };
 
 public:
   static JSObject*
   InitClass(JSContext* aCx, JSObject* aObj)
   {
     JSObject* proto = JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0,
-                                   sProperties, sFunctions, sStaticProperties,
-                                   NULL);
-    if (proto && !JS_DefineProperties(aCx, proto, sStaticProperties)) {
+                                   sProperties, sFunctions, NULL, NULL);
+    if (!proto) {
+      return NULL;
+    }
+
+    JSObject* ctor = JS_GetConstructor(aCx, proto);
+    if (!ctor) {
+      return NULL;
+    }
+
+    if (!dom::DefineConstants(aCx, ctor, sStaticConstants) ||
+        !dom::DefineConstants(aCx, proto, sStaticConstants)) {
       return NULL;
     }
 
     return proto;
   }
 
   static JSObject*
   Create(JSContext* aCx, nsresult aNSResult);
@@ -120,71 +131,74 @@ private:
     if (!out) {
       return false;
     }
 
     JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(out));
     return true;
   }
 
-  static JSBool
-  GetProperty(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool
+  IsDOMException(const JS::Value& v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-
-    int32 slot = JSID_TO_INT(aIdval);
-
-    JSClass* classPtr = JS_GetClass(aObj);
+    if (!v.isObject())
+      return false;
+    JSObject* obj = &v.toObject();
+    return JS_GetClass(obj) == &sClass &&
+           GetJSPrivateSafeish<DOMException>(obj) != nullptr;
+  }
 
-    if (classPtr != &sClass || !GetJSPrivateSafeish<DOMException>(aObj)) {
-      JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
-                           JSMSG_INCOMPATIBLE_PROTO, sClass.name,
-                           sProperties[slot].name, classPtr->name);
-      return false;
-    }
-
-    aVp.set(JS_GetReservedSlot(aObj, slot));
+  template<SLOT Slot>
+  static bool
+  GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    aArgs.rval().set(JS_GetReservedSlot(&aArgs.thisv().toObject(), Slot));
     return true;
   }
 
-  static JSBool
-  GetConstant(JSContext* aCx, JSHandleObject aObj, JSHandleId idval, JSMutableHandleValue aVp)
+  // This struct (versus just templating the method directly) is needed only for
+  // gcc 4.4 (and maybe 4.5 -- 4.6 is okay) being too braindead to allow
+  // GetProperty<Slot> and friends in the JSPropertySpec[] below.
+  template<SLOT Slot>
+  struct Property
   {
-    JS_ASSERT(JSID_IS_INT(idval));
-    aVp.set(INT_TO_JSVAL(JSID_TO_INT(idval)));
-    return true;
-  }
+    static JSBool
+    Get(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+    {
+      MOZ_STATIC_ASSERT(SLOT_FIRST <= Slot && Slot < SLOT_COUNT, "bad slot");
+      JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+      return JS::CallNonGenericMethod<IsDOMException, GetPropertyImpl<Slot> >(aCx, args);
+    }
+  };
 };
 
 JSClass DOMException::sClass = {
   "DOMException",
   JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT),
   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize
 };
 
 JSPropertySpec DOMException::sProperties[] = {
-  { "code", SLOT_code, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "name", SLOT_name, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "message", SLOT_message, PROPERTY_FLAGS, JSOP_WRAPPER(GetProperty),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("code", Property<SLOT_code>::Get, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PSGS("name", Property<SLOT_name>::Get, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PSGS("message", Property<SLOT_message>::Get, GetterOnlyJSNative,
+          JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 JSFunctionSpec DOMException::sFunctions[] = {
   JS_FN("toString", ToString, 0, 0),
   JS_FS_END
 };
 
-JSPropertySpec DOMException::sStaticProperties[] = {
+dom::ConstantSpec DOMException::sStaticConstants[] = {
 
 #define EXCEPTION_ENTRY(_name) \
-  { #_name, _name, CONSTANT_FLAGS, JSOP_WRAPPER(GetConstant), JSOP_NULLWRAPPER },
+  { #_name, JS::Int32Value(_name) },
 
   EXCEPTION_ENTRY(INDEX_SIZE_ERR)
   EXCEPTION_ENTRY(DOMSTRING_SIZE_ERR)
   EXCEPTION_ENTRY(HIERARCHY_REQUEST_ERR)
   EXCEPTION_ENTRY(WRONG_DOCUMENT_ERR)
   EXCEPTION_ENTRY(INVALID_CHARACTER_ERR)
   EXCEPTION_ENTRY(NO_DATA_ALLOWED_ERR)
   EXCEPTION_ENTRY(NO_MODIFICATION_ALLOWED_ERR)
@@ -204,17 +218,17 @@ JSPropertySpec DOMException::sStaticProp
   EXCEPTION_ENTRY(URL_MISMATCH_ERR)
   EXCEPTION_ENTRY(QUOTA_EXCEEDED_ERR)
   EXCEPTION_ENTRY(TIMEOUT_ERR)
   EXCEPTION_ENTRY(INVALID_NODE_TYPE_ERR)
   EXCEPTION_ENTRY(DATA_CLONE_ERR)
 
 #undef EXCEPTION_ENTRY
 
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  { nullptr, JS::UndefinedValue() }
 };
 
 // static
 JSObject*
 DOMException::Create(JSContext* aCx, nsresult aNSResult)
 {
   JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
   if (!obj) {
@@ -245,32 +259,44 @@ DOMException::Create(JSContext* aCx, nsr
   JS_SetReservedSlot(obj, SLOT_message, STRING_TO_JSVAL(jsmessage));
 
   DOMException* priv = new DOMException();
   SetJSPrivateSafeish(obj, priv);
 
   return obj;
 }
 
+static bool
+InitDOMExceptionClass(JSContext* aCx, JSObject* aGlobal)
+{
+  return DOMException::InitClass(aCx, aGlobal);
+}
+
+static JSObject*
+CreateDOMException(JSContext* aCx, nsresult aNSResult)
+{
+  return DOMException::Create(aCx, aNSResult);
+}
+
 } // anonymous namespace
 
 BEGIN_WORKERS_NAMESPACE
 
 namespace exceptions {
 
 bool
 InitClasses(JSContext* aCx, JSObject* aGlobal)
 {
-  return DOMException::InitClass(aCx, aGlobal);
+  return InitDOMExceptionClass(aCx, aGlobal);
 }
 
 void
 ThrowDOMExceptionForNSResult(JSContext* aCx, nsresult aNSResult)
 {
-  JSObject* exception = DOMException::Create(aCx, aNSResult);
+  JSObject* exception = CreateDOMException(aCx, aNSResult);
   if (!exception) {
     return;
   }
 
   JS_SetPendingException(aCx, OBJECT_TO_JSVAL(exception));
 }
 
 } // namespace exceptions
--- a/dom/workers/File.cpp
+++ b/dom/workers/File.cpp
@@ -15,19 +15,16 @@
 #include "nsCOMPtr.h"
 #include "nsJSUtils.h"
 #include "nsStringGlue.h"
 
 #include "Exceptions.h"
 #include "WorkerInlines.h"
 #include "WorkerPrivate.h"
 
-#define PROPERTY_FLAGS \
-  (JSPROP_ENUMERATE | JSPROP_SHARED)
-
 USING_WORKERS_NAMESPACE
 
 using mozilla::dom::workers::exceptions::ThrowDOMExceptionForNSResult;
 
 namespace {
 
 class Blob
 {
@@ -107,57 +104,75 @@ private:
   Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
 
     nsIDOMBlob* blob = GetPrivate(aObj);
     NS_IF_RELEASE(blob);
   }
 
-  static JSBool
-  GetSize(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool
+  IsBlob(const JS::Value& v)
   {
-    nsIDOMBlob* blob = GetInstancePrivate(aCx, aObj, "size");
-    if (!blob) {
-      return false;
-    }
+    return v.isObject() && GetPrivate(&v.toObject()) != nullptr;
+  }
+
+  static bool
+  GetSizeImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JSObject* obj = &aArgs.thisv().toObject();
+    nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "size");
+    MOZ_ASSERT(blob);
 
     uint64_t size;
     if (NS_FAILED(blob->GetSize(&size))) {
       ThrowDOMExceptionForNSResult(aCx, NS_ERROR_DOM_FILE_NOT_READABLE_ERR);
       return false;
     }
 
-    aVp.set(JS_NumberValue(double(size)));
-
+    aArgs.rval().setNumber(double(size));
     return true;
   }
 
   static JSBool
-  GetType(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  GetSize(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    nsIDOMBlob* blob = GetInstancePrivate(aCx, aObj, "type");
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsBlob, GetSizeImpl>(aCx, args);
+  }
+
+  static bool
+  GetTypeImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JSObject* obj = &aArgs.thisv().toObject();
+    nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "type");
     if (!blob) {
       return false;
     }
 
     nsString type;
     if (NS_FAILED(blob->GetType(type))) {
       ThrowDOMExceptionForNSResult(aCx, NS_ERROR_DOM_FILE_NOT_READABLE_ERR);
       return false;
     }
 
     JSString* jsType = JS_NewUCStringCopyN(aCx, type.get(), type.Length());
     if (!jsType) {
       return false;
     }
 
-    aVp.set(STRING_TO_JSVAL(jsType));
+    aArgs.rval().setString(jsType);
+    return true;
+  }
 
-    return true;
+  static JSBool
+  GetType(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsBlob, GetTypeImpl>(aCx, args);
   }
 
   static JSBool
   Slice(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
     if (!obj) {
       return false;
@@ -203,19 +218,19 @@ private:
 JSClass Blob::sClass = {
   "Blob",
   JSCLASS_HAS_PRIVATE,
   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize
 };
 
 JSPropertySpec Blob::sProperties[] = {
-  { "size", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetSize), JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "type", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetType), JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("size", GetSize, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PSGS("type", GetType, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 JSFunctionSpec Blob::sFunctions[] = {
   JS_FN("slice", Slice, 1, JSPROP_ENUMERATE),
   JS_FS_END
 };
 
 class File : public Blob
@@ -296,78 +311,94 @@ private:
   Finalize(JSFreeOp* aFop, JSObject* aObj)
   {
     JS_ASSERT(JS_GetClass(aObj) == &sClass);
 
     nsIDOMFile* file = GetPrivate(aObj);
     NS_IF_RELEASE(file);
   }
 
-  static JSBool
-  GetMozFullPath(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool
+  IsFile(const JS::Value& v)
   {
-    nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "mozFullPath");
-    if (!file) {
-      return false;
-    }
+    return v.isObject() && GetPrivate(&v.toObject()) != nullptr;
+  }
+
+  static bool
+  GetMozFullPathImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JSObject* obj = &aArgs.thisv().toObject();
+    nsIDOMFile* file = GetInstancePrivate(aCx, obj, "mozFullPath");
+    MOZ_ASSERT(file);
 
     nsString fullPath;
 
     if (GetWorkerPrivateFromContext(aCx)->UsesSystemPrincipal() &&
         NS_FAILED(file->GetMozFullPathInternal(fullPath))) {
       ThrowDOMExceptionForNSResult(aCx, NS_ERROR_DOM_FILE_NOT_READABLE_ERR);
       return false;
     }
 
     JSString* jsFullPath = JS_NewUCStringCopyN(aCx, fullPath.get(),
                                                fullPath.Length());
     if (!jsFullPath) {
       return false;
     }
 
-    aVp.set(STRING_TO_JSVAL(jsFullPath));
+    aArgs.rval().setString(jsFullPath);
     return true;
   }
 
   static JSBool
-  GetName(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  GetMozFullPath(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "name");
-    if (!file) {
-      return false;
-    }
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsFile, GetMozFullPathImpl>(aCx, args);
+  }
+
+  static bool
+  GetNameImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JSObject* obj = &aArgs.thisv().toObject();
+    nsIDOMFile* file = GetInstancePrivate(aCx, obj, "name");
+    MOZ_ASSERT(file);
 
     nsString name;
     if (NS_FAILED(file->GetName(name))) {
       name.Truncate();
     }
 
     JSString* jsName = JS_NewUCStringCopyN(aCx, name.get(), name.Length());
     if (!jsName) {
       return false;
     }
 
-    aVp.set(STRING_TO_JSVAL(jsName));
+    aArgs.rval().setString(jsName);
     return true;
   }
+
+  static JSBool
+  GetName(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsFile, GetNameImpl>(aCx, args);
+  }
 };
 
 JSClass File::sClass = {
   "File",
   JSCLASS_HAS_PRIVATE,
   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize
 };
 
 JSPropertySpec File::sProperties[] = {
-  { "name", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetName),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "mozFullPath", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetMozFullPath),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("name", GetName, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PSGS("mozFullPath", GetMozFullPath, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 nsIDOMBlob*
 Blob::GetPrivate(JSObject* aObj)
 {
   if (aObj) {
     JSClass* classPtr = JS_GetClass(aObj);
     if (classPtr == &sClass || classPtr == File::Class()) {
--- a/dom/workers/Worker.cpp
+++ b/dom/workers/Worker.cpp
@@ -3,25 +3,23 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Worker.h"
 
 #include "mozilla/dom/DOMJSClass.h"
 #include "mozilla/dom/BindingUtils.h"
 
+#include "jsapi.h"
 #include "EventTarget.h"
 #include "RuntimeService.h"
 #include "WorkerPrivate.h"
 
 #include "WorkerInlines.h"
 
-#define PROPERTY_FLAGS \
-  (JSPROP_ENUMERATE | JSPROP_SHARED)
-
 #define FUNCTION_FLAGS \
   JSPROP_ENUMERATE
 
 USING_WORKERS_NAMESPACE
 
 using namespace mozilla::dom;
 using mozilla::ErrorResult;
 
@@ -47,26 +45,16 @@ NativePropertyHooks mozilla::dom::worker
 namespace {
 
 class Worker
 {
   static DOMJSClass sClass;
   static JSPropertySpec sProperties[];
   static JSFunctionSpec sFunctions[];
 
-  enum
-  {
-    STRING_onerror = 0,
-    STRING_onmessage,
-
-    STRING_COUNT
-  };
-
-  static const char* const sEventStrings[STRING_COUNT];
-
 protected:
   enum {
     // The constructor function holds a WorkerPrivate* in its first reserved
     // slot.
     CONSTRUCTOR_SLOT_PARENT = 0
   };
 
 public:
@@ -167,70 +155,120 @@ protected:
 
 private:
   // No instance of this class should ever be created so these are explicitly
   // left without an implementation to prevent linking in case someone tries to
   // make one.
   Worker();
   ~Worker();
 
-  static JSBool
-  GetEventListener(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
-  {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
+  static bool
+  IsWorker(const JS::Value& v);
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
-    if (!worker) {
-      return !JS_IsExceptionPending(aCx);
-    }
+  static bool
+  GetEventListener(JSContext* aCx, const JS::CallArgs aArgs,
+                   const nsAString &aNameStr)
+  {
+    WorkerPrivate* worker =
+      GetInstancePrivate(aCx, &aArgs.thisv().toObject(),
+                         NS_ConvertUTF16toUTF8(aNameStr).get());
+    MOZ_ASSERT(worker);
 
-    NS_ConvertASCIItoUTF16 nameStr(name + 2);
     ErrorResult rv;
-    JSObject* listener = worker->GetEventListener(nameStr, rv);
+    JSObject* listener = worker->GetEventListener(Substring(aNameStr, 2), rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to get listener!");
+      return false;
     }
 
-    aVp.set(listener ? OBJECT_TO_JSVAL(listener) : JSVAL_NULL);
+    aArgs.rval().setObjectOrNull(listener);
     return true;
   }
 
+  static bool
+  GetOnerrorImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    return GetEventListener(aCx, aArgs, NS_LITERAL_STRING("onerror"));
+  }
+
   static JSBool
-  SetEventListener(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSBool aStrict,
-                   JSMutableHandleValue aVp)
+  GetOnerror(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorker, GetOnerrorImpl>(aCx, args);
+  }
+
+  static bool
+  GetOnmessageImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    return GetEventListener(aCx, aArgs, NS_LITERAL_STRING("onmessage"));
+  }
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
-    if (!worker) {
-      return !JS_IsExceptionPending(aCx);
-    }
+  static JSBool
+  GetOnmessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorker, GetOnmessageImpl>(aCx, args);
+  }
+
+  static bool
+  SetEventListener(JSContext* aCx, JS::CallArgs aArgs,
+                   const nsAString& aNameStr)
+  {
+    WorkerPrivate* worker =
+      GetInstancePrivate(aCx, &aArgs.thisv().toObject(),
+                         NS_ConvertUTF16toUTF8(aNameStr).get());
+    MOZ_ASSERT(worker);
 
     JSObject* listener;
-    if (!JS_ValueToObject(aCx, aVp, &listener)) {
+    if (!JS_ValueToObject(aCx,
+                          aArgs.length() > 0 ? aArgs[0] : JS::UndefinedValue(),
+                          &listener)) {
       return false;
     }
 
-    NS_ConvertASCIItoUTF16 nameStr(name + 2);
     ErrorResult rv;
-    worker->SetEventListener(nameStr, listener, rv);
+    worker->SetEventListener(Substring(aNameStr, 2), listener, rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to set listener!");
       return false;
     }
 
+    aArgs.rval().setUndefined();
     return true;
   }
 
+  static bool
+  SetOnerrorImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    return SetEventListener(aCx, aArgs, NS_LITERAL_STRING("onerror"));
+  }
+
+  static JSBool
+  SetOnerror(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorker, SetOnerrorImpl>(aCx, args);
+  }
+
+  static bool
+  SetOnmessageImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    return SetEventListener(aCx, aArgs, NS_LITERAL_STRING("onmessage"));
+  }
+
+  static JSBool
+  SetOnmessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorker, SetOnmessageImpl>(aCx, args);
+  }
+
   static JSBool
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     return ConstructInternal(aCx, aArgc, aVp, false, Class());
   }
 
   static void
   Finalize(JSFreeOp* aFop, JSObject* aObj)
@@ -314,34 +352,27 @@ DOMJSClass Worker::sClass = {
     { prototypes::id::EventTarget_workers, prototypes::id::_ID_Count,
       prototypes::id::_ID_Count },
     false,
     &sNativePropertyHooks
   }
 };
 
 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 }
+  JS_PSGS("onerror", GetOnerror, SetOnerror, JSPROP_ENUMERATE),
+  JS_PSGS("onmessage", GetOnmessage, SetOnmessage, JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 JSFunctionSpec Worker::sFunctions[] = {
   JS_FN("terminate", Terminate, 0, FUNCTION_FLAGS),
   JS_FN("postMessage", PostMessage, 1, FUNCTION_FLAGS),
   JS_FS_END
 };
 
-const char* const Worker::sEventStrings[STRING_COUNT] = {
-  "onerror",
-  "onmessage"
-};
-
 class ChromeWorker : public Worker
 {
   static DOMJSClass sClass;
 
 public:
   static JSClass*
   Class()
   {
@@ -438,22 +469,42 @@ DOMJSClass ChromeWorker::sClass = {
   {
     { prototypes::id::EventTarget_workers, prototypes::id::_ID_Count,
       prototypes::id::_ID_Count },
     false,
     &sNativePropertyHooks
   }
 };
 
+} // anonymous namespace
+
+BEGIN_WORKERS_NAMESPACE
+
+bool
+ClassIsWorker(JSClass* aClass)
+{
+  return Worker::Class() == aClass || ChromeWorker::Class() == aClass;
+}
+
+END_WORKERS_NAMESPACE
+
+namespace {
+
+bool
+Worker::IsWorker(const JS::Value& v)
+{
+  return v.isObject() && ClassIsWorker(JS_GetClass(&v.toObject()));
+}
+
 WorkerPrivate*
 Worker::GetInstancePrivate(JSContext* aCx, JSObject* aObj,
                            const char* aFunctionName)
 {
   JSClass* classPtr = JS_GetClass(aObj);
-  if (classPtr == Class() || classPtr == ChromeWorker::Class()) {
+  if (ClassIsWorker(classPtr)) {
     return UnwrapDOMObject<WorkerPrivate>(aObj, eRegularDOMObject);
   }
 
   JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
                        Class()->name, aFunctionName, classPtr->name);
   return NULL;
 }
 
@@ -495,15 +546,16 @@ bool
 InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
           bool aMainRuntime)
 {
   return !!ChromeWorker::InitClass(aCx, aGlobal, aProto, aMainRuntime);
 }
 
 } // namespace chromeworker
 
-bool
-ClassIsWorker(JSClass* aClass)
+JSBool
+GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
 {
-  return Worker::Class() == aClass || ChromeWorker::Class() == aClass;
+    JS_ReportErrorNumber(aCx, js_GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
+    return false;
 }
 
 END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -38,19 +38,16 @@
 #include "Principal.h"
 #include "ScriptLoader.h"
 #include "Worker.h"
 #include "WorkerPrivate.h"
 #include "XMLHttpRequest.h"
 
 #include "WorkerInlines.h"
 
-#define PROPERTY_FLAGS \
-  (JSPROP_ENUMERATE | JSPROP_SHARED)
-
 #define FUNCTION_FLAGS \
   JSPROP_ENUMERATE
 
 using namespace mozilla;
 USING_WORKERS_NAMESPACE
 
 namespace {
 
@@ -134,103 +131,112 @@ protected:
 
   virtual void
   _finalize(JSFreeOp* aFop) MOZ_OVERRIDE
   {
     EventTarget::_finalize(aFop);
   }
 
 private:
-  static JSBool
-  GetEventListener(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
-  {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
+  static bool IsWorkerGlobalScope(const JS::Value& v);
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+  static bool
+  GetOnCloseImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    const char* name = sEventStrings[STRING_onclose];
+    WorkerGlobalScope* scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
 
     ErrorResult rv;
 
     JSObject* listener =
       scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to get event listener!");
       return false;
     }
 
-    aVp.set(listener ? OBJECT_TO_JSVAL(listener) : JSVAL_NULL);
+    aArgs.rval().setObjectOrNull(listener);
     return true;
   }
 
   static JSBool
-  SetEventListener(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSBool aStrict,
-                   JSMutableHandleValue aVp)
+  GetOnClose(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetOnCloseImpl>(aCx, args);
+  }
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+  static bool
+  SetOnCloseImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    const char* name = sEventStrings[STRING_onclose];
+    WorkerGlobalScope* scope =
+      GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
 
-    if (JSVAL_IS_PRIMITIVE(aVp)) {
+    if (aArgs.length() == 0 || !aArgs[0].isObject()) {
       JS_ReportError(aCx, "Not an event listener!");
       return false;
     }
 
     ErrorResult rv;
     scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2),
-                            JSVAL_TO_OBJECT(aVp), rv);
+                            &aArgs[0].toObject(), rv);
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to set event listener!");
       return false;
     }
 
+    aArgs.rval().setUndefined();
     return true;
   }
 
+  static JSBool
+  SetOnClose(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, SetOnCloseImpl>(aCx, args);
+  }
+
   static WorkerGlobalScope*
   GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName);
 
   static JSBool
   Construct(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR,
                          sClass.name);
     return false;
   }
 
-  static JSBool
-  GetSelf(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool
+  GetSelfImpl(JSContext* aCx, JS::CallArgs aArgs)
   {
-    if (!GetInstancePrivate(aCx, aObj, "self")) {
-      return false;
-    }
-
-    aVp.set(OBJECT_TO_JSVAL(aObj));
+    aArgs.rval().setObject(aArgs.thisv().toObject());
     return true;
   }
 
   static JSBool
-  GetLocation(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  GetSelf(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetSelfImpl>(aCx, args);
+  }
+
+  static bool
+  GetLocationImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JSObject* obj = &aArgs.thisv().toObject();
     WorkerGlobalScope* scope =
-      GetInstancePrivate(aCx, aObj, sProperties[SLOT_location].name);
-    if (!scope) {
-      return false;
-    }
+      GetInstancePrivate(aCx, obj, sProperties[SLOT_location].name);
+    MOZ_ASSERT(scope);
 
-    if (JSVAL_IS_VOID(scope->mSlots[SLOT_location])) {
+    if (scope->mSlots[SLOT_location].isUndefined()) {
       JSString* href, *protocol, *host, *hostname;
       JSString* port, *pathname, *search, *hash;
 
       WorkerPrivate::LocationInfo& info = scope->mWorker->GetLocationInfo();
 
 #define COPY_STRING(_jsstr, _cstr)                                             \
   if (info. _cstr .IsEmpty()) {                                                \
     _jsstr = NULL;                                                             \
@@ -255,24 +261,31 @@ private:
 #undef COPY_STRING
 
       JSObject* location = location::Create(aCx, href, protocol, host, hostname,
                                             port, pathname, search, hash);
       if (!location) {
         return false;
       }
 
-      scope->mSlots[SLOT_location] = OBJECT_TO_JSVAL(location);
+      scope->mSlots[SLOT_location].setObject(*location);
     }
 
-    aVp.set(scope->mSlots[SLOT_location]);
+    aArgs.rval().set(scope->mSlots[SLOT_location]);
     return true;
   }
 
   static JSBool
+  GetLocation(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetLocationImpl>(aCx, args);
+  }
+
+  static JSBool
   UnwrapErrorEvent(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JS_ASSERT(aArgc == 1);
     JS_ASSERT((JS_ARGV(aCx, aVp)[0]).isObject());
 
     JSObject* wrapper = &JS_CALLEE(aCx, aVp).toObject();
     JS_ASSERT(JS_ObjectIsFunction(aCx, wrapper));
 
@@ -300,58 +313,60 @@ private:
     if (JSVAL_IS_BOOLEAN(rval) && JSVAL_TO_BOOLEAN(rval) &&
         !JS_CallFunctionName(aCx, event, "preventDefault", 0, NULL, &rval)) {
       return false;
     }
 
     return true;
   }
 
-  static JSBool
-  GetOnErrorListener(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool
+  GetOnErrorListenerImpl(JSContext* aCx, JS::CallArgs aArgs)
   {
     const char* name = sEventStrings[STRING_onerror];
-    WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+    WorkerGlobalScope* scope =
+      GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
 
     ErrorResult rv;
 
     JSObject* adaptor =
       scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to get event listener!");
       return false;
     }
 
     if (!adaptor) {
-      aVp.setNull();
+      aArgs.rval().setNull();
       return true;
     }
 
-    aVp.set(js::GetFunctionNativeReserved(adaptor, SLOT_wrappedFunction));
-
-    JS_ASSERT(!JSVAL_IS_PRIMITIVE(aVp));
-
+    aArgs.rval().set(js::GetFunctionNativeReserved(adaptor, SLOT_wrappedFunction));
+    MOZ_ASSERT(aArgs.rval().isObject());
     return true;
   }
 
   static JSBool
-  SetOnErrorListener(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval,
-                     JSBool aStrict, JSMutableHandleValue aVp)
+  GetOnErrorListener(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetOnErrorListenerImpl>(aCx, args);
+  }
+
+  static bool
+  SetOnErrorListenerImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JSObject* obj = &aArgs.thisv().toObject();
     const char* name = sEventStrings[STRING_onerror];
-    WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+    WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, name);
+    MOZ_ASSERT(scope);
 
-    if (JSVAL_IS_PRIMITIVE(aVp)) {
+    if (aArgs.length() == 0 || !aArgs[0].isObject()) {
       JS_ReportError(aCx, "Not an event listener!");
       return false;
     }
 
     JSFunction* adaptor =
       js::NewFunctionWithReserved(aCx, UnwrapErrorEvent, 1, 0,
                                   JS_GetGlobalObject(aCx), "unwrap");
     if (!adaptor) {
@@ -359,54 +374,68 @@ private:
     }
 
     JSObject* listener = JS_GetFunctionObject(adaptor);
     if (!listener) {
       return false;
     }
 
     js::SetFunctionNativeReserved(listener, SLOT_wrappedScope,
-                                  OBJECT_TO_JSVAL(aObj));
-    js::SetFunctionNativeReserved(listener, SLOT_wrappedFunction, aVp);
+                                  JS::ObjectValue(*obj));
+    js::SetFunctionNativeReserved(listener, SLOT_wrappedFunction, aArgs[0]);
 
     ErrorResult rv;
 
     scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2), listener, rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to set event listener!");
       return false;
     }
 
+    aArgs.rval().setUndefined();
     return true;
   }
 
   static JSBool
-  GetNavigator(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  SetOnErrorListener(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, SetOnErrorListenerImpl>(aCx, args);
+  }
+
+  static bool
+  GetNavigatorImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JSObject* obj = &aArgs.thisv().toObject();
     WorkerGlobalScope* scope =
-      GetInstancePrivate(aCx, aObj, sProperties[SLOT_navigator].name);
-    if (!scope) {
-      return false;
-    }
+      GetInstancePrivate(aCx, obj, sProperties[SLOT_navigator].name);
+    MOZ_ASSERT(scope);
 
-    if (JSVAL_IS_VOID(scope->mSlots[SLOT_navigator])) {
+    if (scope->mSlots[SLOT_navigator].isUndefined()) {
       JSObject* navigator = navigator::Create(aCx);
       if (!navigator) {
         return false;
       }
 
       scope->mSlots[SLOT_navigator] = OBJECT_TO_JSVAL(navigator);
     }
 
-    aVp.set(scope->mSlots[SLOT_navigator]);
+    aArgs.rval().set(scope->mSlots[SLOT_navigator]);
     return true;
   }
 
   static JSBool
+  GetNavigator(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsWorkerGlobalScope, GetNavigatorImpl>(aCx, args);
+  }
+
+  static JSBool
   Close(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
     JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
     if (!obj) {
       return false;
     }
 
     WorkerGlobalScope* scope = GetInstancePrivate(aCx, obj, sFunctions[0].name);
@@ -610,26 +639,24 @@ private:
 JSClass WorkerGlobalScope::sClass = {
   "WorkerGlobalScope",
   0,
   JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
   JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub
 };
 
 JSPropertySpec WorkerGlobalScope::sProperties[] = {
-  { "location", SLOT_location, PROPERTY_FLAGS, JSOP_WRAPPER(GetLocation),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { sEventStrings[STRING_onerror], STRING_onerror, PROPERTY_FLAGS,
-    JSOP_WRAPPER(GetOnErrorListener), JSOP_WRAPPER(SetOnErrorListener) },
-  { sEventStrings[STRING_onclose], STRING_onclose, PROPERTY_FLAGS,
-    JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
-  { "navigator", SLOT_navigator, PROPERTY_FLAGS, JSOP_WRAPPER(GetNavigator),
-    JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { "self", 0, PROPERTY_FLAGS, JSOP_WRAPPER(GetSelf), JSOP_WRAPPER(js_GetterOnlyPropertyStub) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS("location", GetLocation, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PSGS(sEventStrings[STRING_onerror], GetOnErrorListener, SetOnErrorListener,
+          JSPROP_ENUMERATE),
+  JS_PSGS(sEventStrings[STRING_onclose], GetOnClose, SetOnClose,
+          JSPROP_ENUMERATE),
+  JS_PSGS("navigator", GetNavigator, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PSGS("self", GetSelf, GetterOnlyJSNative, JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 JSFunctionSpec WorkerGlobalScope::sFunctions[] = {
   JS_FN("close", Close, 0, FUNCTION_FLAGS),
   JS_FN("importScripts", ImportScripts, 1, FUNCTION_FLAGS),
   JS_FN("setTimeout", SetTimeout, 1, FUNCTION_FLAGS),
   JS_FN("clearTimeout", ClearTimeout, 1, FUNCTION_FLAGS),
   JS_FN("setInterval", SetInterval, 1, FUNCTION_FLAGS),
@@ -704,73 +731,85 @@ protected:
   {
     MOZ_COUNT_DTOR(mozilla::dom::workers::DedicatedWorkerGlobalScope);
   }
 
 private:
   using EventTarget::GetEventListener;
   using EventTarget::SetEventListener;
 
-  static JSBool
-  GetEventListener(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool
+  IsDedicatedWorkerGlobalScope(const JS::Value& v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
+    return v.isObject() && JS_GetClass(&v.toObject()) == Class();
+  }
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    DedicatedWorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+  static bool
+  GetOnMessageImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    const char* name = sEventStrings[STRING_onmessage];
+    DedicatedWorkerGlobalScope* scope =
+      GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
 
     ErrorResult rv;
 
     JSObject* listener =
       scope->GetEventListener(NS_ConvertASCIItoUTF16(name + 2), rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to get event listener!");
       return false;
     }
 
-    aVp.set(listener ? OBJECT_TO_JSVAL(listener) : JSVAL_NULL);
+    aArgs.rval().setObjectOrNull(listener);
     return true;
   }
 
   static JSBool
-  SetEventListener(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSBool aStrict,
-                   JSMutableHandleValue aVp)
+  GetOnMessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsDedicatedWorkerGlobalScope, GetOnMessageImpl>(aCx, args);
+  }
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    DedicatedWorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+  static bool
+  SetOnMessageImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    const char* name = sEventStrings[STRING_onmessage];
+    DedicatedWorkerGlobalScope* scope =
+      GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
 
-    if (JSVAL_IS_PRIMITIVE(aVp)) {
+    if (aArgs.length() == 0 || !aArgs[0].isObject()) {
       JS_ReportError(aCx, "Not an event listener!");
       return false;
     }
 
     ErrorResult rv;
 
     scope->SetEventListener(NS_ConvertASCIItoUTF16(name + 2),
-                            JSVAL_TO_OBJECT(aVp), rv);
+                            &aArgs[0].toObject(), rv);
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to set event listener!");
       return false;
     }
 
+    aArgs.rval().setUndefined();
     return true;
   }
 
+  static JSBool
+  SetOnMessage(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
+  {
+    JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
+    return JS::CallNonGenericMethod<IsDedicatedWorkerGlobalScope, SetOnMessageImpl>(aCx, args);
+  }
+
   static DedicatedWorkerGlobalScope*
   GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
   {
     JSClass* classPtr = JS_GetClass(aObj);
     if (classPtr == Class()) {
       return UnwrapDOMObject<DedicatedWorkerGlobalScope>(aObj,
                                                          eRegularDOMObject);
     }
@@ -871,19 +910,19 @@ DOMJSClass DedicatedWorkerGlobalScope::s
     { prototypes::id::EventTarget_workers, prototypes::id::_ID_Count,
       prototypes::id::_ID_Count },
     false,
     &sNativePropertyHooks
   }
 };
 
 JSPropertySpec DedicatedWorkerGlobalScope::sProperties[] = {
-  { sEventStrings[STRING_onmessage], STRING_onmessage, PROPERTY_FLAGS,
-    JSOP_WRAPPER(GetEventListener), JSOP_WRAPPER(SetEventListener) },
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  JS_PSGS(sEventStrings[STRING_onmessage], GetOnMessage, SetOnMessage,
+          JSPROP_ENUMERATE),
+  JS_PS_END
 };
 
 JSFunctionSpec DedicatedWorkerGlobalScope::sFunctions[] = {
   JS_FN("postMessage", PostMessage, 1, FUNCTION_FLAGS),
   JS_FS_END
 };
 
 const char* const DedicatedWorkerGlobalScope::sEventStrings[STRING_COUNT] = {
@@ -904,16 +943,22 @@ WorkerGlobalScope::GetInstancePrivate(JS
     return UnwrapDOMObject<DedicatedWorkerGlobalScope>(aObj, eRegularDOMObject);
   }
 
   JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO,
                        sClass.name, aFunctionName, classPtr->name);
   return NULL;
 }
 
+bool
+WorkerGlobalScope::IsWorkerGlobalScope(const JS::Value& v)
+{
+  return v.isObject() && JS_GetClass(&v.toObject()) == DedicatedWorkerGlobalScope::Class();
+}
+
 } /* anonymous namespace */
 
 BEGIN_WORKERS_NAMESPACE
 
 JSObject*
 CreateDedicatedWorkerGlobalScope(JSContext* aCx)
 {
   using namespace mozilla::dom;
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -106,11 +106,19 @@ const uint32_t kJSPrincipalsDebugToken =
 namespace exceptions {
 
 // Implemented in Exceptions.cpp
 void
 ThrowDOMExceptionForNSResult(JSContext* aCx, nsresult aNSResult);
 
 } // namespace exceptions
 
+// Throws the JSMSG_GETTER_ONLY exception.  This shouldn't be used going
+// forward -- getter-only properties should just use JS_PSG for the setter
+// (implying no setter at all), which will not throw when set in non-strict
+// code but will in strict code.  Old code should use this only for temporary
+// compatibility reasons.
+extern JSBool
+GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
+
 END_WORKERS_NAMESPACE
 
 #endif // mozilla_dom_workers_workers_h__