diff --git a/dom/workers/Events.cpp b/dom/workers/Events.cpp
--- a/dom/workers/Events.cpp
+++ b/dom/workers/Events.cpp
@@ -7,6 +7,7 @@
 #include "mozilla/Util.h"
 
 #include "Events.h"
+#include "mozilla/dom/BindingUtils.h"
 
 #include "jsapi.h"
 #include "jsfriendapi.h"
@@ -16,15 +17,9 @@
 #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
 
@@ -37,7 +32,7 @@ class Event : public PrivatizableBase
 
   static JSPropertySpec sProperties[];
   static JSFunctionSpec sFunctions[];
-  static JSPropertySpec sStaticProperties[];
+  static dom::ConstantSpec sStaticConstants[];
 
 protected:
   bool mStopPropagationCalled;
@@ -77,9 +72,18 @@ public:
     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;
+    }
+
+    JS::Rooted<JSObject*> ctor(aCx, JS_GetConstructor(aCx, proto));
+    if (!ctor) {
+      return NULL;
+    }
+
+    if (!dom::DefineConstants(aCx, ctor, sStaticConstants) ||
+        !dom::DefineConstants(aCx, proto, sStaticConstants)) {
       return NULL;
     }
 
@@ -145,7 +149,7 @@ protected:
     MOZ_COUNT_DTOR(mozilla::dom::workers::Event);
   }
 
-  enum {
+  enum EventPhase {
     CAPTURING_PHASE = 1,
     AT_TARGET = 2,
     BUBBLING_PHASE = 3
@@ -219,32 +223,34 @@ private:
     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));
+    return v.isObject() && GetPrivate(&v.toObject()) != nullptr;
+  }
 
-    int32 slot = JSID_TO_INT(aIdval);
-
-    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)
@@ -341,25 +347,25 @@ DECL_EVENT_CLASS(Event::sMainRuntimeClas
 #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[] = {
@@ -370,13 +376,11 @@ JSFunctionSpec Event::sFunctions[] = {
   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
@@ -512,23 +516,28 @@ private:
     }
   }
 
-  static JSBool
-  GetProperty(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool
+  IsMessageEvent(const JS::Value& v)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
+    if (!v.isObject())
+      return false;
+    JSObject* obj = &v.toObject();
+    return IsThisClass(JS_GetClass(obj)) &&
+           GetJSPrivateSafeish<MessageEvent>(obj) != NULL;
+  }
 
-    int32 slot = JSID_TO_INT(aIdval);
+  template<SLOT Slot>
+  static bool
+  GetPropertyImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
 
-    JS_ASSERT(slot >= SLOT_data && slot < SLOT_COUNT);
-
-    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);
 
@@ -545,16 +554,31 @@ private:
                        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)
   {
@@ -596,13 +620,13 @@ DECL_MESSAGEEVENT_CLASS(MessageEvent::sM
 #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[] = {
@@ -721,25 +745,39 @@ private:
     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));
+    if (!v.isObject())
+      return false;
+    JSObject* obj = &v.toObject();
+    return IsThisClass(JS_GetClass(obj)) &&
+           GetJSPrivateSafeish<ErrorEvent>(obj) != nullptr;
+  }
 
-    int32 slot = JSID_TO_INT(aIdval);
-
-    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)
   {
@@ -782,13 +820,13 @@ DECL_ERROREVENT_CLASS(ErrorEvent::sMainR
 #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[] = {
@@ -901,25 +939,39 @@ private:
     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));
+    if (!v.isObject())
+      return false;
+    JSObject* obj = &v.toObject();
+    return JS_GetClass(obj) == &sClass &&
+           GetJSPrivateSafeish<ProgressEvent>(obj) != nullptr;
+  }
 
-    int32 slot = JSID_TO_INT(aIdval);
-
-    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)
   {
@@ -956,13 +1008,13 @@ JSClass ProgressEvent::sClass = {
 };
 
 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[] = {
diff --git a/dom/workers/Exceptions.cpp b/dom/workers/Exceptions.cpp
--- a/dom/workers/Exceptions.cpp
+++ b/dom/workers/Exceptions.cpp
@@ -12,6 +12,7 @@
 #include "mozilla/Util.h"
 #include "nsDOMException.h"
 #include "nsTraceRefcnt.h"
+#include "mozilla/dom/BindingUtils.h"
 
 #include "WorkerInlines.h"
 
@@ -31,14 +32,15 @@ class DOMException : public Privatizable
   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:
@@ -46,9 +48,18 @@ public:
   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;
+    }
+
+    JS::Rooted<JSObject*> ctor(aCx, JS_GetConstructor(aCx, proto));
+    if (!ctor) {
+      return NULL;
+    }
+
+    if (!dom::DefineConstants(aCx, ctor, sStaticConstants) ||
+        !dom::DefineConstants(aCx, proto, sStaticConstants)) {
       return NULL;
     }
 
@@ -125,33 +136,38 @@ private:
     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));
+    if (!v.isObject())
+      return false;
+    JSObject* obj = &v.toObject();
+    return JS_GetClass(obj) == &sClass &&
+           GetJSPrivateSafeish<DOMException>(obj) != nullptr;
+  }
 
-    int32 slot = JSID_TO_INT(aIdval);
-
-    JSClass* classPtr = JS_GetClass(aObj);
-
-    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 = {
@@ -162,13 +178,11 @@ JSClass DOMException::sClass = {
 };
 
 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[] = {
@@ -176,10 +190,10 @@ JSFunctionSpec DOMException::sFunctions[
   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)
@@ -209,7 +223,7 @@ JSPropertySpec DOMException::sStaticProp
 
 #undef EXCEPTION_ENTRY
 
-  { 0, 0, 0, JSOP_NULLWRAPPER, JSOP_NULLWRAPPER }
+  { nullptr, JS::UndefinedValue() }
 };
 
 // static
@@ -250,6 +264,18 @@ DOMException::Create(JSContext* aCx, nsr
   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
@@ -259,13 +285,13 @@ 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;
   }
diff --git a/dom/workers/File.cpp b/dom/workers/File.cpp
--- a/dom/workers/File.cpp
+++ b/dom/workers/File.cpp
@@ -21,9 +21,6 @@
 #include "WorkerInlines.h"
 #include "WorkerPrivate.h"
 
-#define PROPERTY_FLAGS \
-  (JSPROP_ENUMERATE | JSPROP_SHARED)
-
 USING_WORKERS_NAMESPACE
 
 using mozilla::dom::workers::exceptions::ThrowDOMExceptionForNSResult;
@@ -114,13 +111,18 @@ private:
     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)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
+    nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "size");
+    MOZ_ASSERT(blob);
 
     uint64_t size;
     if (NS_FAILED(blob->GetSize(&size))) {
@@ -128,15 +130,22 @@ private:
       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)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
+    nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "type");
     if (!blob) {
       return false;
     }
@@ -152,9 +161,15 @@ private:
       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
@@ -210,9 +225,9 @@ JSClass Blob::sClass = {
 };
 
 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[] = {
@@ -303,13 +318,18 @@ private:
     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)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
+    nsIDOMFile* file = GetInstancePrivate(aCx, obj, "mozFullPath");
+    MOZ_ASSERT(file);
 
     nsString fullPath;
 
@@ -325,17 +345,23 @@ private:
       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)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &aArgs.thisv().toObject());
+    nsIDOMFile* file = GetInstancePrivate(aCx, obj, "name");
+    MOZ_ASSERT(file);
 
     nsString name;
     if (NS_FAILED(file->GetName(name))) {
@@ -347,9 +373,16 @@ private:
       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 = {
@@ -360,11 +393,9 @@ JSClass File::sClass = {
 };
 
 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*
diff --git a/dom/workers/Worker.cpp b/dom/workers/Worker.cpp
--- a/dom/workers/Worker.cpp
+++ b/dom/workers/Worker.cpp
@@ -8,15 +8,13 @@
 #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
 
@@ -52,16 +50,6 @@ class Worker
   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
@@ -172,60 +160,110 @@ private:
   Worker();
   ~Worker();
 
-  static JSBool
-  GetEventListener(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool
+  IsWorker(const JS::Value& v);
+
+  static bool
+  GetEventListener(JSContext* aCx, const JS::CallArgs aArgs,
+                   const nsAString &aNameStr)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
+    WorkerPrivate* worker =
+      GetInstancePrivate(aCx, &aArgs.thisv().toObject(),
+                         NS_ConvertUTF16toUTF8(aNameStr).get());
+    MOZ_ASSERT(worker);
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
-    if (!worker) {
-      return !JS_IsExceptionPending(aCx);
-    }
-
-    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);
+  }
 
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
-    if (!worker) {
-      return !JS_IsExceptionPending(aCx);
-    }
+  static bool
+  GetOnmessageImpl(JSContext* aCx, JS::CallArgs aArgs)
+  {
+    return GetEventListener(aCx, aArgs, NS_LITERAL_STRING("onmessage"));
+  }
+
+  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)
   {
@@ -318,11 +356,9 @@ DOMJSClass Worker::sClass = {
 };
 
 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[] = {
@@ -331,11 +367,6 @@ JSFunctionSpec Worker::sFunctions[] = {
   JS_FS_END
 };
 
-const char* const Worker::sEventStrings[STRING_COUNT] = {
-  "onerror",
-  "onmessage"
-};
-
 class ChromeWorker : public Worker
 {
   static DOMJSClass sClass;
@@ -443,12 +474,32 @@ DOMJSClass ChromeWorker::sClass = {
   -1
 };
 
+} // 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);
   }
 
@@ -500,10 +551,11 @@ InitClass(JSContext* aCx, JSObject* aGlo
 
 } // 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
diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -40,9 +40,6 @@
 
 #include "WorkerInlines.h"
 
-#define PROPERTY_FLAGS \
-  (JSPROP_ENUMERATE | JSPROP_SHARED)
-
 #define FUNCTION_FLAGS \
   JSPROP_ENUMERATE
 
@@ -136,17 +133,14 @@ protected:
   }
 
 private:
-  static JSBool
-  GetEventListener(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
+  static bool IsWorkerGlobalScope(const JS::Value& v);
+
+  static bool
+  GetOnCloseImpl(JSContext* aCx, JS::CallArgs aArgs)
   {
-    JS_ASSERT(JSID_IS_INT(aIdval));
-    JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
-
-    const char* name = sEventStrings[JSID_TO_INT(aIdval)];
-    WorkerGlobalScope* scope = GetInstancePrivate(aCx, aObj, name);
-    if (!scope) {
-      return false;
-    }
+    const char* name = sEventStrings[STRING_onclose];
+    WorkerGlobalScope* scope = GetInstancePrivate(aCx, &aArgs.thisv().toObject(), name);
+    MOZ_ASSERT(scope);
 
     ErrorResult rv;
 
@@ -158,39 +152,49 @@ private:
       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);
 
@@ -202,27 +206,29 @@ private:
     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)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &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;
 
@@ -257,14 +263,21 @@ private:
         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);
@@ -302,14 +315,13 @@ private:
     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;
 
@@ -322,28 +334,31 @@ private:
     }
 
     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)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &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;
     }
@@ -361,8 +376,8 @@ private:
     }
 
     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;
 
@@ -373,19 +388,26 @@ private:
       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)
+  {
+    JS::Rooted<JSObject*> obj(aCx, &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;
@@ -394,11 +416,18 @@ private:
       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);
@@ -612,16 +641,14 @@ JSClass WorkerGlobalScope::sClass = {
 };
 
 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[] = {
@@ -706,17 +733,19 @@ 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;
 
@@ -728,24 +757,26 @@ private:
       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;
     }
@@ -753,16 +784,24 @@ private:
     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)
   {
@@ -870,9 +909,9 @@ DOMJSClass DedicatedWorkerGlobalScope::s
 };
 
 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[] = {
@@ -903,6 +942,12 @@ WorkerGlobalScope::GetInstancePrivate(JS
   return NULL;
 }
 
+bool
+WorkerGlobalScope::IsWorkerGlobalScope(const JS::Value& v)
+{
+  return v.isObject() && JS_GetClass(&v.toObject()) == DedicatedWorkerGlobalScope::Class();
+}
+
 } /* anonymous namespace */
 
 BEGIN_WORKERS_NAMESPACE
diff --git a/dom/workers/Workers.h b/dom/workers/Workers.h
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -105,6 +105,14 @@ ThrowDOMExceptionForNSResult(JSContext* 
 
 } // 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__
