Backout bug 792215 for suspected Windows m-oth leaks.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 25 Sep 2012 22:40:56 -0400
changeset 108203 0ad22be0b52ac4557dbb05d7f301aa7e02140e3a
parent 108202 0787c32d87f3914b6e79137a3d837cc95fcc7a25
child 108204 31c49a095aa9cf24782d909c03cf66f0ce5009ae
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
bugs792215
milestone18.0a1
Backout bug 792215 for suspected Windows m-oth leaks. CLOSED TREE
browser/base/content/browser.js
content/canvas/src/CustomQS_Canvas2D.h
dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json
js/src/jsobj.cpp
js/xpconnect/src/XPCQuickStubs.cpp
js/xpconnect/src/XPCQuickStubs.h
js/xpconnect/src/codegen.py
js/xpconnect/src/dombindings.cpp
js/xpconnect/src/dombindings.h
js/xpconnect/src/dombindingsgen.py
js/xpconnect/src/qsgen.py
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -58,36 +58,36 @@ XPCOMUtils.defineLazyGetter(window, "gFi
 
   // Force a style flush to ensure that our binding is attached.
   findbar.clientTop;
   findbar.browser = gBrowser;
   window.gFindBarInitialized = true;
   return findbar;
 });
 
-this.__defineGetter__("gPrefService", function() {
+__defineGetter__("gPrefService", function() {
   delete this.gPrefService;
   return this.gPrefService = Services.prefs;
 });
 
-this.__defineGetter__("AddonManager", function() {
+__defineGetter__("AddonManager", function() {
   let tmp = {};
   Cu.import("resource://gre/modules/AddonManager.jsm", tmp);
   return this.AddonManager = tmp.AddonManager;
 });
-this.__defineSetter__("AddonManager", function (val) {
+__defineSetter__("AddonManager", function (val) {
   delete this.AddonManager;
   return this.AddonManager = val;
 });
 
-this.__defineGetter__("PluralForm", function() {
+__defineGetter__("PluralForm", function() {
   Cu.import("resource://gre/modules/PluralForm.jsm");
   return this.PluralForm;
 });
-this.__defineSetter__("PluralForm", function (val) {
+__defineSetter__("PluralForm", function (val) {
   delete this.PluralForm;
   return this.PluralForm = val;
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
                                   "resource://gre/modules/TelemetryStopwatch.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AboutHomeUtils",
@@ -1534,17 +1534,17 @@ var gBrowserInit = {
 
   onUnload: function() {
     // In certain scenarios it's possible for unload to be fired before onload,
     // (e.g. if the window is being closed after browser.js loads but before the
     // load completes). In that case, there's nothing to do here.
     if (!gStartupRan)
       return;
 
-    if (!window.__lookupGetter__("InspectorUI"))
+    if (!__lookupGetter__("InspectorUI"))
       InspectorUI.destroy();
 
     // First clean up services initialized in gBrowserInit.onLoad (or those whose
     // uninit methods don't depend on the services having been initialized).
     allTabs.uninit();
 
     CombinedStopReload.uninit();
 
@@ -3609,17 +3609,17 @@ function BrowserToolboxCustomizeDone(aTo
 
 #ifndef XP_MACOSX
     updateEditUIVisibility();
 #endif
 
     // Hacky: update the PopupNotifications' object's reference to the iconBox,
     // if it already exists, since it may have changed if the URL bar was
     // added/removed.
-    if (!window.__lookupGetter__("PopupNotifications"))
+    if (!__lookupGetter__("PopupNotifications"))
       PopupNotifications.iconBox = document.getElementById("notification-popup-box");
   }
 
   PlacesToolbarHelper.customizeDone();
   BookmarksMenuButton.customizeDone();
   DownloadsButton.customizeDone();
 
   // The url bar splitter state is dependent on whether stop/reload
@@ -4068,17 +4068,17 @@ var XULBrowserWindow = {
         // Remove all the notifications, except for those which want to
         // persist across the first location change.
         let nBox = gBrowser.getNotificationBox(selectedBrowser);
         nBox.removeTransientNotifications();
 
         // Only need to call locationChange if the PopupNotifications object
         // for this window has already been initialized (i.e. its getter no
         // longer exists)
-        if (!window.__lookupGetter__("PopupNotifications"))
+        if (!__lookupGetter__("PopupNotifications"))
           PopupNotifications.locationChange();
       }
     }
 
     // Disable menu entries for images, enable otherwise
     if (content.document && mimeTypeIsTextBased(content.document.contentType))
       this.isImage.removeAttribute('disabled');
     else
--- a/content/canvas/src/CustomQS_Canvas2D.h
+++ b/content/canvas/src/CustomQS_Canvas2D.h
@@ -13,135 +13,119 @@
 #include "jsfriendapi.h"
 
 typedef NS_STDCALL_FUNCPROTO(nsresult, CanvasStyleSetterType, nsIDOMCanvasRenderingContext2D,
                              SetStrokeStyle_multi, (const nsAString &, nsISupports *));
 typedef NS_STDCALL_FUNCPROTO(nsresult, CanvasStyleGetterType, nsIDOMCanvasRenderingContext2D,
                              GetStrokeStyle_multi, (nsAString &, nsISupports **, int32_t *));
 
 static JSBool
-Canvas2D_SetStyleHelper(JSContext *cx, unsigned argc, JS::Value *vp,
-                        const char* propName, CanvasStyleSetterType setfunc)
+Canvas2D_SetStyleHelper(JSContext *cx, JSObject *obj, jsid id, JSMutableHandleValue vp,
+                        CanvasStyleSetterType setfunc)
 {
     XPC_QS_ASSERT_CONTEXT_OK(cx);
-    JSObject *obj = JS_THIS_OBJECT(cx, vp);
-    if (!obj)
-        return JS_FALSE;
     nsIDOMCanvasRenderingContext2D *self;
     xpc_qsSelfRef selfref;
-    if (!xpc_qsUnwrapThis(cx, obj, &self, &selfref.ptr, &vp[1], nullptr))
+    JS::AutoValueRooter tvr(cx);
+    if (!xpc_qsUnwrapThis(cx, obj, &self, &selfref.ptr, tvr.jsval_addr(), nullptr))
         return JS_FALSE;
 
-    if (argc < 1)
-        return xpc_qsThrow(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
-    jsval *argv = JS_ARGV(cx, vp);
-
     nsresult rv = NS_OK;
-    if (JSVAL_IS_STRING(argv[0])) {
-        xpc_qsDOMString arg0(cx, argv[0], &argv[0],
+    if (JSVAL_IS_STRING(vp)) {
+        xpc_qsDOMString arg0(cx, vp, vp.address(),
                              xpc_qsDOMString::eDefaultNullBehavior,
                              xpc_qsDOMString::eDefaultUndefinedBehavior);
         if (!arg0.IsValid())
             return JS_FALSE;
 
         rv = (self->*setfunc)(arg0, nullptr);
     } else {
         nsISupports *arg0;
         xpc_qsSelfRef arg0ref;
-        rv = xpc_qsUnwrapArg<nsISupports>(cx, argv[0], &arg0, &arg0ref.ptr, &argv[0]);
+        rv = xpc_qsUnwrapArg<nsISupports>(cx, vp, &arg0, &arg0ref.ptr, vp.address());
         if (NS_FAILED(rv)) {
-            xpc_qsThrowBadSetterValue(cx, rv, JSVAL_TO_OBJECT(vp[1]), propName);
+            xpc_qsThrowBadSetterValue(cx, rv, JSVAL_TO_OBJECT(*tvr.jsval_addr()), id);
             return JS_FALSE;
         }
 
         rv = (self->*setfunc)(NullString(), arg0);
     }
 
     if (NS_FAILED(rv))
-        return xpc_qsThrowGetterSetterFailed(cx, rv, JSVAL_TO_OBJECT(vp[1]),
-                                             propName);
+        return xpc_qsThrowGetterSetterFailed(cx, rv, JSVAL_TO_OBJECT(*tvr.jsval_addr()), id);
 
     return JS_TRUE;
 }
 
 static JSBool
-Canvas2D_GetStyleHelper(JSContext *cx, unsigned argc, JS::Value *vp,
-                        const char* propName, CanvasStyleGetterType getfunc)
+Canvas2D_GetStyleHelper(JSContext *cx, JSObject *obj, jsid id, JSMutableHandleValue vp,
+                        CanvasStyleGetterType getfunc)
 {
     XPC_QS_ASSERT_CONTEXT_OK(cx);
-    JSObject *obj = JS_THIS_OBJECT(cx, vp);
-    if (!obj)
-        return JS_FALSE;
     nsIDOMCanvasRenderingContext2D *self;
     xpc_qsSelfRef selfref;
     XPCLazyCallContext lccx(JS_CALLER, cx, obj);
-    if (!xpc_qsUnwrapThis(cx, obj, &self, &selfref.ptr, &vp[1], &lccx))
+    if (!xpc_qsUnwrapThis(cx, obj, &self, &selfref.ptr, vp.address(), &lccx))
         return JS_FALSE;
     nsresult rv;
 
     nsString resultString;
     nsCOMPtr<nsISupports> resultInterface;
     int32_t resultType;
     rv = (self->*getfunc)(resultString, getter_AddRefs(resultInterface), &resultType);
     if (NS_FAILED(rv))
-        return xpc_qsThrowGetterSetterFailed(cx, rv, JSVAL_TO_OBJECT(vp[1]),
-                                             propName);
+        return xpc_qsThrowGetterSetterFailed(cx, rv, JSVAL_TO_OBJECT(vp), id);
 
     switch (resultType) {
     case nsIDOMCanvasRenderingContext2D::CMG_STYLE_STRING:
-        return xpc::StringToJsval(cx, resultString, vp);
+        return xpc::StringToJsval(cx, resultString, vp.address());
 
     case nsIDOMCanvasRenderingContext2D::CMG_STYLE_PATTERN:
     {
         qsObjectHelper helper(resultInterface,
                               xpc_qsGetWrapperCache(resultInterface));
         return xpc_qsXPCOMObjectToJsval(lccx, helper,
                                         &NS_GET_IID(nsIDOMCanvasPattern),
-                                        &interfaces[k_nsIDOMCanvasPattern], vp);
+                                        &interfaces[k_nsIDOMCanvasPattern], vp.address());
     }
     case nsIDOMCanvasRenderingContext2D::CMG_STYLE_GRADIENT:
     {
         qsObjectHelper helper(resultInterface,
                               xpc_qsGetWrapperCache(resultInterface));
         return xpc_qsXPCOMObjectToJsval(lccx, helper,
                                         &NS_GET_IID(nsIDOMCanvasGradient),
-                                        &interfaces[k_nsIDOMCanvasGradient], vp);
+                                        &interfaces[k_nsIDOMCanvasGradient], vp.address());
     }
     default:
-        return xpc_qsThrowGetterSetterFailed(cx, NS_ERROR_FAILURE,
-                                             JSVAL_TO_OBJECT(vp[1]), propName);
+        return xpc_qsThrowGetterSetterFailed(cx, NS_ERROR_FAILURE, JSVAL_TO_OBJECT(vp), id);
     }
 }
 
 static JSBool
-nsIDOMCanvasRenderingContext2D_SetStrokeStyle(JSContext *cx, unsigned argc, JS::Value *vp)
+nsIDOMCanvasRenderingContext2D_SetStrokeStyle(JSContext *cx, JSHandleObject obj, JSHandleId id, JSBool strict, JSMutableHandleValue vp)
 {
-    return Canvas2D_SetStyleHelper(cx, argc, vp, "strokeStyle",
-                                   &nsIDOMCanvasRenderingContext2D::SetStrokeStyle_multi);
+    return Canvas2D_SetStyleHelper(cx, obj, id, vp, &nsIDOMCanvasRenderingContext2D::SetStrokeStyle_multi);
 }
 
 static JSBool
-nsIDOMCanvasRenderingContext2D_GetStrokeStyle(JSContext *cx, unsigned argc, JS::Value *vp)
+nsIDOMCanvasRenderingContext2D_GetStrokeStyle(JSContext *cx, JSHandleObject obj, JSHandleId id, JSMutableHandleValue vp)
 {
-    return Canvas2D_GetStyleHelper(cx, argc, vp, "strokeStyle",
-                                   &nsIDOMCanvasRenderingContext2D::GetStrokeStyle_multi);
+    return Canvas2D_GetStyleHelper(cx, obj, id, vp, &nsIDOMCanvasRenderingContext2D::GetStrokeStyle_multi);
 }
 
 static JSBool
-nsIDOMCanvasRenderingContext2D_SetFillStyle(JSContext *cx, unsigned argc, JS::Value *vp)
+nsIDOMCanvasRenderingContext2D_SetFillStyle(JSContext *cx, JSHandleObject obj, JSHandleId id, JSBool strict, JSMutableHandleValue vp)
 {
-    return Canvas2D_SetStyleHelper(cx, argc, vp, "fillStyle",
-                                   &nsIDOMCanvasRenderingContext2D::SetFillStyle_multi);
+    return Canvas2D_SetStyleHelper(cx, obj, id, vp, &nsIDOMCanvasRenderingContext2D::SetFillStyle_multi);
 }
 
 static JSBool
-nsIDOMCanvasRenderingContext2D_GetFillStyle(JSContext *cx, unsigned argc, JS::Value *vp)
+nsIDOMCanvasRenderingContext2D_GetFillStyle(JSContext *cx, JSHandleObject obj, JSHandleId id, JSMutableHandleValue vp)
 {
-    return Canvas2D_GetStyleHelper(cx, argc, vp, "fillStyle",
-                                   &nsIDOMCanvasRenderingContext2D::GetFillStyle_multi);
+    return Canvas2D_GetStyleHelper(cx, obj, id, vp, &nsIDOMCanvasRenderingContext2D::GetFillStyle_multi);
 }
 
 static bool
 CreateImageData(JSContext* cx, JSObject* obj, uint32_t w, uint32_t h, jsval* vp)
 {
     using mozilla::CheckedInt;
 
     if (w == 0)
--- a/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json
+++ b/dom/imptests/failures/webapps/DOMCore/tests/approved/test_interfaces.html.json
@@ -434,28 +434,32 @@
   "NodeFilter interface: constant SHOW_COMMENT on interface prototype object": true,
   "NodeFilter interface: constant SHOW_DOCUMENT on interface prototype object": true,
   "NodeFilter interface: constant SHOW_DOCUMENT_TYPE on interface prototype object": true,
   "NodeFilter interface: constant SHOW_DOCUMENT_FRAGMENT on interface prototype object": true,
   "NodeFilter interface: constant SHOW_NOTATION on interface prototype object": true,
   "NodeFilter interface: operation acceptNode(Node)": true,
   "NodeList interface: existence and properties of interface object": true,
   "NodeList interface: existence and properties of interface prototype object": true,
+  "NodeList interface: attribute length": true,
   "NodeList interface: calling item(unsigned long) on document.querySelectorAll(\"script\") with too few arguments must throw TypeError": true,
   "HTMLCollection interface: existence and properties of interface object": true,
   "HTMLCollection interface: existence and properties of interface prototype object": true,
+  "HTMLCollection interface: attribute length": true,
   "HTMLCollection interface: calling item(unsigned long) on document.body.children with too few arguments must throw TypeError": true,
   "HTMLCollection interface: calling namedItem(DOMString) on document.body.children with too few arguments must throw TypeError": true,
   "DOMStringList interface: existence and properties of interface object": true,
   "DOMStringList interface: existence and properties of interface prototype object": true,
   "DOMStringList interface: existence and properties of interface prototype object's \"constructor\" property": true,
   "DOMStringList interface: attribute length": true,
   "DOMTokenList interface: existence and properties of interface object": true,
   "DOMTokenList interface: existence and properties of interface prototype object": true,
+  "DOMTokenList interface: attribute length": true,
   "Stringification of document.body.classList": true,
   "DOMTokenList interface: calling item(unsigned long) on document.body.classList with too few arguments must throw TypeError": true,
   "DOMTokenList interface: calling contains(DOMString) on document.body.classList with too few arguments must throw TypeError": true,
   "DOMTokenList interface: calling add(DOMString) on document.body.classList with too few arguments must throw TypeError": true,
   "DOMTokenList interface: calling remove(DOMString) on document.body.classList with too few arguments must throw TypeError": true,
   "DOMTokenList interface: calling toggle(DOMString) on document.body.classList with too few arguments must throw TypeError": true,
   "DOMSettableTokenList interface: existence and properties of interface object": true,
-  "DOMSettableTokenList interface: existence and properties of interface prototype object": true
+  "DOMSettableTokenList interface: existence and properties of interface prototype object": true,
+  "DOMSettableTokenList interface: attribute value": true
 }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -820,16 +820,17 @@ NewPropertyDescriptorObject(JSContext *c
 }
 
 void
 PropDesc::initFromPropertyDescriptor(const PropertyDescriptor &desc)
 {
     isUndefined_ = false;
     pd_.setUndefined();
     attrs = uint8_t(desc.attrs);
+    JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
     if (desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
         hasGet_ = true;
         get_ = ((desc.attrs & JSPROP_GETTER) && desc.getter)
                ? CastAsObjectJsval(desc.getter)
                : UndefinedValue();
         hasSet_ = true;
         set_ = ((desc.attrs & JSPROP_SETTER) && desc.setter)
                ? CastAsObjectJsval(desc.setter)
--- a/js/xpconnect/src/XPCQuickStubs.cpp
+++ b/js/xpconnect/src/XPCQuickStubs.cpp
@@ -11,18 +11,16 @@
 #include "xpcprivate.h"
 #include "XPCInlines.h"
 #include "XPCQuickStubs.h"
 #include "XPCWrapper.h"
 #include "mozilla/dom/BindingUtils.h"
 
 using namespace mozilla;
 
-extern const char* xpc_qsStringTable;
-
 static inline QITableEntry *
 GetOffsets(nsISupports *identity, XPCWrappedNativeProto* proto)
 {
     QITableEntry* offsets = proto ? proto->GetOffsets() : nullptr;
     if (!offsets) {
         static NS_DEFINE_IID(kThisPtrOffsetsSID, NS_THISPTROFFSETS_SID);
         identity->QueryInterface(kThisPtrOffsetsSID, (void**)&offsets);
     }
@@ -94,16 +92,192 @@ PointerFinalize(JSFreeOp *fop, JSObject 
 
 JSClass
 PointerHolderClass = {
     "Pointer", JSCLASS_HAS_PRIVATE,
     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, PointerFinalize
 };
 
+static JSBool
+ReifyPropertyOps(JSContext *cx, JSObject *obj, jsid id, unsigned orig_attrs,
+                 JSPropertyOp getter, JSStrictPropertyOp setter,
+                 JSObject **getterobjp, JSObject **setterobjp)
+{
+    // Generate both getter and setter and stash them in the prototype.
+    jsval roots[2] = { JSVAL_NULL, JSVAL_NULL };
+    JS::AutoArrayRooter tvr(cx, ArrayLength(roots), roots);
+
+    unsigned attrs = JSPROP_SHARED | (orig_attrs & JSPROP_ENUMERATE);
+    JSObject *getterobj;
+    if (getter) {
+        getterobj = GeneratePropertyOp(cx, obj, id, 0, getter);
+        if (!getterobj)
+            return false;
+        roots[0] = OBJECT_TO_JSVAL(getterobj);
+        attrs |= JSPROP_GETTER;
+    } else
+        getterobj = nullptr;
+
+    JSObject *setterobj;
+    if (setter) {
+        setterobj = GeneratePropertyOp(cx, obj, id, 1, setter);
+        if (!setterobj)
+            return false;
+        roots[1] = OBJECT_TO_JSVAL(setterobj);
+        attrs |= JSPROP_SETTER;
+    } else
+        setterobj = nullptr;
+
+    if (getterobjp)
+        *getterobjp = getterobj;
+    if (setterobjp)
+        *setterobjp = setterobj;
+    return JS_DefinePropertyById(cx, obj, id, JSVAL_VOID,
+                                 JS_DATA_TO_FUNC_PTR(JSPropertyOp, getterobj),
+                                 JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setterobj),
+                                 attrs);
+}
+
+static JSBool
+LookupGetterOrSetter(JSContext *cx, JSBool wantGetter, unsigned argc, jsval *vp)
+{
+    XPC_QS_ASSERT_CONTEXT_OK(cx);
+
+    if (argc == 0) {
+        JS_SET_RVAL(cx, vp, JSVAL_VOID);
+        return true;
+    }
+
+    JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    if (!obj)
+        return false;
+
+    jsval idval = JS_ARGV(cx, vp)[0];
+    jsid id;
+    JSPropertyDescriptor desc;
+    if (!JS_ValueToId(cx, idval, &id) ||
+        !JS_GetPropertyDescriptorById(cx, obj, id, JSRESOLVE_QUALIFIED, &desc))
+        return false;
+
+    // No property at all means no getters or setters possible.
+    if (!desc.obj) {
+        JS_SET_RVAL(cx, vp, JSVAL_VOID);
+        return true;
+    }
+
+    // Inline obj_lookup[GS]etter here.
+    if (wantGetter) {
+        if (desc.attrs & JSPROP_GETTER) {
+            JS_SET_RVAL(cx, vp,
+                        OBJECT_TO_JSVAL(JS_FUNC_TO_DATA_PTR(JSObject *, desc.getter)));
+            return true;
+        }
+    } else {
+        if (desc.attrs & JSPROP_SETTER) {
+            JS_SET_RVAL(cx, vp,
+                        OBJECT_TO_JSVAL(JS_FUNC_TO_DATA_PTR(JSObject *, desc.setter)));
+            return true;
+        }
+    }
+
+    // Since XPConnect doesn't use JSPropertyOps in any other contexts,
+    // ensuring that we have an XPConnect prototype object ensures that
+    // we are only going to expose quickstubbed properties to script.
+    // Also be careful not to overwrite existing properties!
+
+    if (!JSID_IS_STRING(id) ||
+        !IS_PROTO_CLASS(js::GetObjectClass(desc.obj)) ||
+        (desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)) ||
+        !(desc.getter || desc.setter) ||
+        desc.setter == js::GetObjectJSClass(desc.obj)->setProperty) {
+        JS_SET_RVAL(cx, vp, JSVAL_VOID);
+        return true;
+    }
+
+    JSObject *getterobj, *setterobj;
+    if (!ReifyPropertyOps(cx, desc.obj, id, desc.attrs, desc.getter, desc.setter,
+                          &getterobj, &setterobj)) {
+        return false;
+    }
+
+    JSObject *wantedobj = wantGetter ? getterobj : setterobj;
+    jsval v = wantedobj ? OBJECT_TO_JSVAL(wantedobj) : JSVAL_VOID;
+    JS_SET_RVAL(cx, vp, v);
+    return true;
+}
+
+static JSBool
+SharedLookupGetter(JSContext *cx, unsigned argc, jsval *vp)
+{
+    return LookupGetterOrSetter(cx, true, argc, vp);
+}
+
+static JSBool
+SharedLookupSetter(JSContext *cx, unsigned argc, jsval *vp)
+{
+    return LookupGetterOrSetter(cx, false, argc, vp);
+}
+
+static JSBool
+DefineGetterOrSetter(JSContext *cx, unsigned argc, JSBool wantGetter, jsval *vp)
+{
+    unsigned attrs;
+    JSBool found;
+    JSPropertyOp getter;
+    JSStrictPropertyOp setter;
+    JSObject *obj2;
+    jsval v;
+    jsid id;
+
+    XPC_QS_ASSERT_CONTEXT_OK(cx);
+    JSObject *obj = JS_THIS_OBJECT(cx, vp);
+    if (!obj)
+        return false;
+    JSNative forward = wantGetter ? js::obj_defineGetter : js::obj_defineSetter;
+    jsval idval = (argc >= 1) ? JS_ARGV(cx, vp)[0] : JSVAL_VOID;
+    if (!JSVAL_IS_STRING(idval))
+        return forward(cx, argc, vp);
+
+    if (!JS_ValueToId(cx, idval, &id) ||
+        !JS_LookupPropertyWithFlagsById(cx, obj, id,
+                                        JSRESOLVE_QUALIFIED, &obj2, &v) ||
+        (obj2 &&
+         !JS_GetPropertyAttrsGetterAndSetterById(cx, obj2, id, &attrs,
+                                                 &found, &getter, &setter)))
+        return false;
+
+    // The property didn't exist, already has a getter or setter, or is not
+    // our property, then just forward now.
+    if (!obj2 ||
+        (attrs & (JSPROP_GETTER | JSPROP_SETTER)) ||
+        !(getter || setter) ||
+        !IS_PROTO_CLASS(js::GetObjectClass(obj2)))
+        return forward(cx, argc, vp);
+
+    // Reify the getter and setter...
+    if (!ReifyPropertyOps(cx, obj2, id, attrs, getter, setter, nullptr, nullptr))
+        return false;
+
+    return forward(cx, argc, vp);
+}
+
+static JSBool
+SharedDefineGetter(JSContext *cx, unsigned argc, jsval *vp)
+{
+    return DefineGetterOrSetter(cx, argc, true, vp);
+}
+
+static JSBool
+SharedDefineSetter(JSContext *cx, unsigned argc, jsval *vp)
+{
+    return DefineGetterOrSetter(cx, argc, false, vp);
+}
+
+
 JSBool
 xpc_qsDefineQuickStubs(JSContext *cx, JSObject *proto, unsigned flags,
                        uint32_t ifacec, const nsIID **interfaces,
                        uint32_t tableSize, const xpc_qsHashEntry *table,
                        const xpc_qsPropertySpec *propspecs,
                        const xpc_qsFunctionSpec *funcspecs,
                        const char *stringTable)
 {
@@ -125,20 +299,18 @@ xpc_qsDefineQuickStubs(JSContext *cx, JS
             for (;;) {
                 // Define quick stubs for attributes.
                 const xpc_qsPropertySpec *ps = propspecs + entry->prop_index;
                 const xpc_qsPropertySpec *ps_end = ps + entry->n_props;
                 for ( ; ps < ps_end; ++ps) {
                     definedProperty = true;
                     if (!JS_DefineProperty(cx, proto,
                                            stringTable + ps->name_index,
-                                           JSVAL_VOID,
-                                           (JSPropertyOp)ps->getter,
-                                           (JSStrictPropertyOp)ps->setter,
-                                           flags | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS))
+                                           JSVAL_VOID, ps->getter, ps->setter,
+                                           flags | JSPROP_SHARED)) 
                         return false;
                 }
 
                 // Define quick stubs for methods.
                 const xpc_qsFunctionSpec *fs = funcspecs + entry->func_index;
                 const xpc_qsFunctionSpec *fs_end = fs + entry->n_funcs;
                 for ( ; fs < fs_end; ++fs) {
                     if (!JS_DefineFunction(cx, proto,
@@ -152,16 +324,27 @@ xpc_qsDefineQuickStubs(JSContext *cx, JS
                 size_t j = entry->parentInterface;
                 if (j == XPC_QS_NULL_INDEX)
                     break;
                 entry = table + j;
             }
         }
     }
 
+    static JSFunctionSpec getterfns[] = {
+        JS_FN("__lookupGetter__", SharedLookupGetter, 1, 0),
+        JS_FN("__lookupSetter__", SharedLookupSetter, 1, 0),
+        JS_FN("__defineGetter__", SharedDefineGetter, 2, 0),
+        JS_FN("__defineSetter__", SharedDefineSetter, 2, 0),
+        JS_FS_END
+    };
+
+    if (definedProperty && !JS_DefineFunctions(cx, proto, getterfns))
+        return false;
+
     return true;
 }
 
 JSBool
 xpc_qsThrow(JSContext *cx, nsresult rv)
 {
     XPCThrower::Throw(rv, cx);
     return false;
@@ -276,36 +459,16 @@ xpc_qsThrowGetterSetterFailed(JSContext 
                               jsid memberId)
 {
     const char *ifaceName;
     GetMemberInfo(obj, memberId, &ifaceName);
     return ThrowCallFailed(cx, rv, ifaceName, memberId, NULL);
 }
 
 JSBool
-xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv, JSObject *obj,
-                              const char* memberName)
-{
-    JSString *str = JS_InternString(cx, memberName);
-    if (!str) {
-        return false;
-    }
-    return xpc_qsThrowGetterSetterFailed(cx, rv, obj,
-                                         INTERNED_STRING_TO_JSID(cx, str));
-}
-
-JSBool
-xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv, JSObject *obj,
-                              uint16_t memberIndex)
-{
-    return xpc_qsThrowGetterSetterFailed(cx, rv, obj,
-                                         xpc_qsStringTable + memberIndex);
-}
-
-JSBool
 xpc_qsThrowMethodFailed(JSContext *cx, nsresult rv, jsval *vp)
 {
     const char *ifaceName;
     jsid memberId;
     GetMethodInfo(cx, vp, &ifaceName, &memberId);
     return ThrowCallFailed(cx, rv, ifaceName, memberId, NULL);
 }
 
@@ -379,55 +542,27 @@ void
 xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv,
                           JSObject *obj, jsid propId)
 {
     const char *ifaceName;
     GetMemberInfo(obj, propId, &ifaceName);
     ThrowBadArg(cx, rv, ifaceName, propId, NULL, 0);
 }
 
-void
-xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv,
-                          JSObject *obj, const char* propName)
-{
-    JSString *str = JS_InternString(cx, propName);
-    if (!str) {
-        return;
-    }
-    xpc_qsThrowBadSetterValue(cx, rv, obj, INTERNED_STRING_TO_JSID(cx, str));
-}
-
-void
-xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv, JSObject *obj,
-                          uint16_t name_index)
-{
-    xpc_qsThrowBadSetterValue(cx, rv, obj, xpc_qsStringTable + name_index);
-}
-
 JSBool
 xpc_qsGetterOnlyPropertyStub(JSContext *cx, JSHandleObject obj, JSHandleId id, JSBool strict,
                              JSMutableHandleValue vp)
 {
     return JS_ReportErrorFlagsAndNumber(cx,
                                         JSREPORT_WARNING | JSREPORT_STRICT |
                                         JSREPORT_STRICT_MODE_ERROR,
                                         js_GetErrorMessage, NULL,
                                         JSMSG_GETTER_ONLY);
 }
 
-JSBool
-xpc_qsGetterOnlyNativeStub(JSContext *cx, unsigned argc, jsval *vp)
-{
-    return JS_ReportErrorFlagsAndNumber(cx,
-                                        JSREPORT_WARNING | JSREPORT_STRICT |
-                                        JSREPORT_STRICT_MODE_ERROR,
-                                        js_GetErrorMessage, NULL,
-                                        JSMSG_GETTER_ONLY);
-}
-
 xpc_qsDOMString::xpc_qsDOMString(JSContext *cx, jsval v, jsval *pval,
                                  StringificationBehavior nullBehavior,
                                  StringificationBehavior undefinedBehavior)
 {
     typedef implementation_type::char_traits traits;
     // From the T_DOMSTRING case in XPCConvert::JSData2Native.
     JSString *s = InitOrStringify<traits>(cx, v, pval, nullBehavior,
                                           undefinedBehavior);
--- a/js/xpconnect/src/XPCQuickStubs.h
+++ b/js/xpconnect/src/XPCQuickStubs.h
@@ -14,18 +14,18 @@
 /* XPCQuickStubs.h - Support functions used only by quick stubs. */
 
 class XPCCallContext;
 
 #define XPC_QS_NULL_INDEX  ((uint16_t) -1)
 
 struct xpc_qsPropertySpec {
     uint16_t name_index;
-    JSNative getter;
-    JSNative setter;
+    JSPropertyOp getter;
+    JSStrictPropertyOp setter;
 };
 
 struct xpc_qsFunctionSpec {
     uint16_t name_index;
     uint16_t arity;
     JSNative native;
 };
 
@@ -65,23 +65,16 @@ xpc_qsThrow(JSContext *cx, nsresult rv);
  *
  * This is one reason the UnwrapThis functions below have an out parameter that
  * receives the wrapper JSObject.  (The other reason is to help the caller keep
  * that JSObject GC-reachable.)
  */
 JSBool
 xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv,
                               JSObject *obj, jsid memberId);
-// And variants using strings and string tables
-JSBool
-xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv,
-                              JSObject *obj, const char* memberName);
-JSBool
-xpc_qsThrowGetterSetterFailed(JSContext *cx, nsresult rv,
-                              JSObject *obj, uint16_t memberIndex);
 
 /**
  * Fail after an XPCOM method returned rv.
  *
  * See NOTE at xpc_qsThrowGetterSetterFailed.
  */
 JSBool
 xpc_qsThrowMethodFailed(JSContext *cx, nsresult rv, jsval *vp);
@@ -112,31 +105,21 @@ xpc_qsThrowBadArgWithDetails(JSContext *
 /**
  * Fail after converting a setter argument fails.
  *
  * See NOTE at xpc_qsThrowGetterSetterFailed.
  */
 void
 xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv, JSObject *obj,
                           jsid propId);
-// And variants using strings and string tables
-void
-xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv, JSObject *obj,
-                          const char* propName);
-void
-xpc_qsThrowBadSetterValue(JSContext *cx, nsresult rv, JSObject *obj,
-                          uint16_t name_index);
 
 
 JSBool
 xpc_qsGetterOnlyPropertyStub(JSContext *cx, JSHandleObject obj, JSHandleId id, JSBool strict, JSMutableHandleValue vp);
 
-JSBool
-xpc_qsGetterOnlyNativeStub(JSContext *cx, unsigned argc, jsval *vp);
-
 /* Functions for converting values between COM and JS. */
 
 inline JSBool
 xpc_qsInt64ToJsval(JSContext *cx, int64_t i, jsval *rv)
 {
     *rv = JS_NumberValue(static_cast<double>(i));
     return true;
 }
--- a/js/xpconnect/src/codegen.py
+++ b/js/xpconnect/src/codegen.py
@@ -173,18 +173,18 @@ def writeArgumentUnboxing(f, i, name, ty
     # optional - bool - True if the parameter is optional.
     # rvdeclared - bool - False if no |nsresult rv| has been declared earlier.
 
     typeName = xpidl.getBuiltinOrNativeTypeName(type)
 
     isSetter = (i is None)
 
     if isSetter:
-        argPtr = "argv"
-        argVal = "*argv"
+        argPtr = "vp"
+        argVal = "*vp"
     elif optional:
         if typeName == "[jsval]":
             val = "JSVAL_VOID"
         else:
             val = "JSVAL_NULL"
         argVal = "(%d < argc ? argv[%d] : %s)" % (i, i, val)
         argPtr = "(%d < argc ? &argv[%d] : NULL)" % (i, i)
     else:
@@ -424,18 +424,26 @@ def writeStub(f, customMethodCalls, memb
         member = member.iface.namemap[member.forward]
 
     isAttr = (member.kind == 'attribute')
     isMethod = (member.kind == 'method')
     assert isAttr or isMethod
     isNotxpcom = isMethod and member.notxpcom
     isGetter = isAttr and not isSetter
 
-    signature = ("static JSBool\n"
-                 "%s(JSContext *cx, unsigned argc,%s jsval *vp)\n")
+    signature = "static JSBool\n"
+    if isAttr:
+        # JSPropertyOp signature.
+        if isSetter:
+            signature += "%s(JSContext *cx, JSHandleObject obj, JSHandleId id, JSBool strict,%s JSMutableHandleValue vp_)\n"
+        else:
+            signature += "%s(JSContext *cx, JSHandleObject obj, JSHandleId id,%s JSMutableHandleValue vp_)\n"
+    else:
+        # JSFastNative.
+        signature += "%s(JSContext *cx, unsigned argc,%s jsval *vp)\n"
 
     customMethodCall = customMethodCalls.get(stubName, None)
 
     if customMethodCall is None:
         customMethodCall = customMethodCalls.get(member.iface.name + '_', None)
         if customMethodCall is not None:
             if isMethod:
                 code = customMethodCall.get('code', None)
@@ -457,17 +465,17 @@ def writeStub(f, customMethodCalls, memb
             # that's shared between the stubs. The stubs can't have additional
             # arguments, only the template function can.
             callTemplate = signature % (stubName, '')
             callTemplate += "{\n"
 
             argumentValues = (customMethodCall['additionalArgumentValues']
                               % header.methodNativeName(member))
             if isAttr:
-                callTemplate += ("    return %s(cx, obj, id%s, %s, vp);\n"
+                callTemplate += ("    return %s(cx, obj, id%s, %s, vp_);\n"
                                  % (templateName, ", strict" if isSetter else "", argumentValues))
             else:
                 callTemplate += ("    return %s(cx, argc, %s, vp);\n"
                                  % (templateName, argumentValues))
             callTemplate += "}\n\n"
 
             # Fall through and create the template function stub called from the
             # real stubs, but only generate the stub once. Otherwise, just write
@@ -491,44 +499,43 @@ def writeStub(f, customMethodCalls, memb
     if customMethodCall is None or not 'additionalArguments' in customMethodCall:
         additionalArguments = ''
     else:
         additionalArguments = " %s," % customMethodCall['additionalArguments']
     f.write(signature % (stubName, additionalArguments))
     f.write("{\n")
     f.write("    XPC_QS_ASSERT_CONTEXT_OK(cx);\n")
 
-    # Compute "this".
-    f.write("    JSObject *obj = JS_THIS_OBJECT(cx, vp);\n"
-            "    if (!obj)\n"
-            "        return JS_FALSE;\n")
+    # Convert JSMutableHandleValue to jsval*
+    if isAttr:
+        f.write("    jsval *vp = vp_.address();\n")
+
+    # For methods, compute "this".
+    if isMethod:
+        f.write("    JSObject *obj = JS_THIS_OBJECT(cx, vp);\n"
+                "    if (!obj)\n"
+                "        return JS_FALSE;\n")
 
     selfname = writeThisUnwrapping(f, member, isMethod, isGetter, customMethodCall)
 
     rvdeclared = False
     if isMethod:
         inArgs = argumentsLength(member)
         # If there are any required arguments, check argc.
         requiredArgs = inArgs
         while requiredArgs and member.params[requiredArgs-1].optional:
             requiredArgs -= 1
-    elif isSetter:
-        inArgs = requiredArgs = 1
-    else:
-        inArgs = requiredArgs = 0
+        if requiredArgs:
+            f.write("    if (argc < %d)\n" % requiredArgs)
+            f.write("        return xpc_qsThrow(cx, "
+                    "NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n")
 
-    if requiredArgs:
-        f.write("    if (argc < %d)\n" % requiredArgs)
-        f.write("        return xpc_qsThrow(cx, "
-                "NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n")
-
-    # Convert in-parameters.
-    if inArgs > 0:
-        f.write("    jsval *argv = JS_ARGV(cx, vp);\n")
-    if isMethod:
+        # Convert in-parameters.
+        if inArgs > 0:
+            f.write("    jsval *argv = JS_ARGV(cx, vp);\n")
         for i in range(inArgs):
             param = member.params[i]
             argName = 'arg%d' % i
             argTypeKey = argName + 'Type'
             if customMethodCall is None or not argTypeKey in customMethodCall:
                 validateParam(member, param)
                 realtype = param.realtype
             else:
@@ -620,17 +627,16 @@ def writeStub(f, customMethodCalls, memb
     if canFail:
         # Check for errors.
         writeCheckForFailure(f, isMethod, isGetter)
 
     # Convert the return value.
     if isMethod or isGetter:
         writeResultWrapping(f, member, 'vp', '*vp')
     else:
-        f.write("    JS_SET_RVAL(cx, vp, JS::UndefinedValue());\n"
-                "    return JS_TRUE;\n")
+        f.write("    return JS_TRUE;\n")
 
     # Epilog.
     f.write("}\n\n")
 
     # Now write out the call to the template function.
     if customMethodCall is not None:
         f.write(callTemplate)
--- a/js/xpconnect/src/dombindings.cpp
+++ b/js/xpconnect/src/dombindings.cpp
@@ -205,27 +205,24 @@ ListBase<LC>::instanceIsListObject(JSCon
         JS_ReportError(cx, "type error: wrong object");
         return false;
     }
     return true;
 }
 
 template<class LC>
 JSBool
-ListBase<LC>::length_getter(JSContext *cx, unsigned argc, JS::Value *vp)
+ListBase<LC>::length_getter(JSContext *cx, JSHandleObject obj, JSHandleId id, JSMutableHandleValue vp)
 {
-    JSObject *obj = JS_THIS_OBJECT(cx, vp);
-    if (!obj)
-        return false;
     if (!instanceIsListObject(cx, obj, NULL))
         return false;
     uint32_t length;
     getListObject(obj)->GetLength(&length);
     MOZ_ASSERT(int32_t(length) >= 0);
-    JS_SET_RVAL(cx, vp, UINT_TO_JSVAL(length));
+    vp.set(UINT_TO_JSVAL(length));
     return true;
 }
 
 template<class LC>
 bool
 ListBase<LC>::getItemAt(ListType *list, uint32_t i, IndexGetterType &item)
 {
     JS_STATIC_ASSERT(!hasIndexGetter);
@@ -359,26 +356,22 @@ ListBase<LC>::getPrototype(JSContext *cx
     JSObject *global = scope->GetGlobalJSObject();
     interfacePrototype = JS_NewObject(cx, Jsvalify(&sInterfacePrototypeClass), proto, global);
     if (!interfacePrototype)
         return NULL;
 
     for (size_t n = 0; n < sProtoPropertiesCount; ++n) {
         MOZ_ASSERT(sProtoProperties[n].getter);
         jsid id = sProtoProperties[n].id;
-        unsigned attrs = JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS;
+        unsigned attrs = JSPROP_ENUMERATE | JSPROP_SHARED;
         if (!sProtoProperties[n].setter)
             attrs |= JSPROP_READONLY;
         if (!JS_DefinePropertyById(cx, interfacePrototype, id, JSVAL_VOID,
-                                   (JSPropertyOp) sProtoProperties[n].getter,
-                                   (JSStrictPropertyOp) sProtoProperties[n].setter,
-                                   attrs))
-        {
+                                   sProtoProperties[n].getter, sProtoProperties[n].setter, attrs))
             return NULL;
-        }
     }
 
     for (size_t n = 0; n < sProtoMethodsCount; ++n) {
         jsid id = sProtoMethods[n].id;
         JSFunction *fun = JS_NewFunctionById(cx, sProtoMethods[n].native, sProtoMethods[n].nargs,
                                              0, js::GetObjectParent(interfacePrototype), id);
         if (!fun)
             return NULL;
@@ -765,22 +758,22 @@ ListBase<LC>::has(JSContext *cx, JSObjec
 template<class LC>
 bool
 ListBase<LC>::resolveNativeName(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc)
 {
     MOZ_ASSERT(xpc::WrapperFactory::IsXrayWrapper(proxy));
 
     for (size_t n = 0; n < sProtoPropertiesCount; ++n) {
         if (id == sProtoProperties[n].id) {
-            desc->attrs = JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_NATIVE_ACCESSORS;
+            desc->attrs = JSPROP_ENUMERATE | JSPROP_SHARED;
             if (!sProtoProperties[n].setter)
                 desc->attrs |= JSPROP_READONLY;
             desc->obj = proxy;
-            desc->setter = (JSStrictPropertyOp) sProtoProperties[n].setter;
-            desc->getter = (JSPropertyOp) sProtoProperties[n].getter;
+            desc->setter = sProtoProperties[n].setter;
+            desc->getter = sProtoProperties[n].getter;
             return true;
         }
     }
 
     for (size_t n = 0; n < sProtoMethodsCount; ++n) {
         if (id == sProtoMethods[n].id) {
             JSFunction *fun = JS_NewFunctionById(cx, sProtoMethods[n].native,
                                                  sProtoMethods[n].nargs, 0, proxy, id);
--- a/js/xpconnect/src/dombindings.h
+++ b/js/xpconnect/src/dombindings.h
@@ -112,33 +112,33 @@ private:
     friend void Register(nsScriptNameSpaceManager* aNameSpaceManager);
 
     static ListBase<LC> instance;
 
     static js::Class sInterfaceClass;
 
     struct Properties {
         jsid &id;
-        JSNative getter;
-        JSNative setter;
+        JSPropertyOp getter;
+        JSStrictPropertyOp setter;
     };
     struct Methods {
         jsid &id;
         JSNative native;
         unsigned nargs;
     };
 
     static Properties sProtoProperties[];
     static size_t sProtoPropertiesCount;
     static Methods sProtoMethods[];
     static size_t sProtoMethodsCount;
 
     static JSObject *ensureExpandoObject(JSContext *cx, JSObject *obj);
 
-    static JSBool length_getter(JSContext *cx, unsigned argc, JS::Value *vp);
+    static JSBool length_getter(JSContext *cx, JSHandleObject obj, JSHandleId id, JSMutableHandleValue vp);
 
     static inline bool getItemAt(ListType *list, uint32_t i, IndexGetterType &item);
     static inline bool setItemAt(JSContext *cx, ListType *list, uint32_t i, IndexSetterType item);
 
     static inline bool namedItem(JSContext *cx, JSObject *obj, jsval *name, NameGetterType &result,
                                  bool *hasResult);
 
     static inline bool getNamedItem(ListType *list, const nsAString& aName, NameGetterType &item);
--- a/js/xpconnect/src/dombindingsgen.py
+++ b/js/xpconnect/src/dombindingsgen.py
@@ -594,17 +594,17 @@ def writeBindingStub(f, classname, membe
         writeResultConv(f, member.realtype, template, jsvalPtr, jsvalRef)
 
     writeStub(f, {}, member, stubName, writeThisUnwrapping, writeCheckForFailure, writeResultWrapping, isSetter)
 
 def writeAttrStubs(f, classname, attr):
     getterName = classname + '_' + header.attributeNativeName(attr, True)
     writeBindingStub(f, classname, attr, getterName)
     if attr.readonly:
-        setterName = 'xpc_qsGetterOnlyNativeStub'
+        setterName = 'xpc_qsGetterOnlyPropertyStub'
     else:
         setterName = (classname + '_'
                       + header.attributeNativeName(attr, False))
         writeBindingStub(f, classname, attr, setterName, isSetter=True)
 
     return "    { s_%s_id, %s, %s }" % (attr.name, getterName, setterName)
 
 def writeMethodStub(f, classname, method):
--- a/js/xpconnect/src/qsgen.py
+++ b/js/xpconnect/src/qsgen.py
@@ -380,17 +380,16 @@ class StringTable:
         def explodeToCharArray(string):
             return ", ".join(map(lambda x:"'%s'" % x, string))
         f.write("static const char %s[] = {\n" % name)
         for (string, offset) in entries[:-1]:
             f.write("  /* %5d */ %s, '\\0',\n"
                     % (offset, explodeToCharArray(string)))
         f.write("  /* %5d */ %s, '\\0' };\n\n"
                 % (entries[-1][1], explodeToCharArray(entries[-1][0])))
-        f.write("const char* xpc_qsStringTable = %s;\n\n" % name);
 
 def substitute(template, vals):
     """ Simple replacement for string.Template, which isn't in Python 2.3. """
     def replacement(match):
         return vals[match.group(1)]
     return re.sub(r'\${(\w+)}', replacement, template)
 
 # From JSData2Native.
@@ -487,35 +486,34 @@ argumentUnboxingTemplates = {
 
 # From JSData2Native.
 #
 # Omitted optional arguments are treated as though the caller had passed JS
 # `null`; this behavior is from XPCWrappedNative::CallMethod. The 'jsval' type,
 # however, defaults to 'undefined'.
 #
 def writeArgumentUnboxing(f, i, name, type, optional, rvdeclared,
-                          nullBehavior, undefinedBehavior,
-                          propIndex=None):
+                          nullBehavior, undefinedBehavior):
     # f - file to write to
     # i - int or None - Indicates the source jsval.  If i is an int, the source
-    #     jsval is argv[i]; otherwise it is argv[0].  But if Python i >= C++ argc,
+    #     jsval is argv[i]; otherwise it is *vp.  But if Python i >= C++ argc,
     #     which can only happen if optional is True, the argument is missing;
     #     use JSVAL_NULL as the source jsval instead.
     # name - str - name of the native C++ variable to create.
     # type - xpidl.{Interface,Native,Builtin} - IDL type of argument
     # optional - bool - True if the parameter is optional.
     # rvdeclared - bool - False if no |nsresult rv| has been declared earlier.
 
     typeName = getBuiltinOrNativeTypeName(type)
 
     isSetter = (i is None)
 
     if isSetter:
-        argPtr = "argv"
-        argVal = "argv[0]"
+        argPtr = "vp"
+        argVal = "*vp"
     elif optional:
         if typeName == "[jsval]":
             val = "JSVAL_VOID"
         else:
             val = "JSVAL_NULL"
         argVal = "(%d < argc ? argv[%d] : %s)" % (i, i, val)
         argPtr = "(%d < argc ? &argv[%d] : NULL)" % (i, i)
     else:
@@ -556,28 +554,27 @@ def writeArgumentUnboxing(f, i, name, ty
                 f.write("    nsresult rv;\n");
             f.write("    %s *%s;\n" % (type.name, name))
             f.write("    xpc_qsSelfRef %sref;\n" % name)
             f.write("    rv = xpc_qsUnwrapArg<%s>("
                     "cx, %s, &%s, &%sref.ptr, %s);\n"
                     % (type.name, argVal, name, name, argPtr))
             f.write("    if (NS_FAILED(rv)) {\n")
             if isSetter:
-                assert(propIndex is not None)
-                f.write("        xpc_qsThrowBadSetterValue(cx, rv, JSVAL_TO_OBJECT(vp[1]), (uint16_t)%s);\n" %
-                        propIndex)
+                f.write("        xpc_qsThrowBadSetterValue("
+                        "cx, rv, JSVAL_TO_OBJECT(*tvr.jsval_addr()), id);\n")
             else:
                 f.write("        xpc_qsThrowBadArg(cx, rv, vp, %d);\n" % i)
             f.write("        return JS_FALSE;\n"
                     "    }\n")
             return True
 
     warn("Unable to unbox argument of type %s (native type %s)" % (type.name, typeName))
     if i is None:
-        src = 'argv[0]'
+        src = '*vp'
     else:
         src = 'argv[%d]' % i
     f.write("    !; // TODO - Unbox argument %s = %s\n" % (name, src))
     return rvdeclared
 
 def writeResultDecl(f, type, varname):
     if isVoidType(type):
         return  # nothing to declare
@@ -739,28 +736,35 @@ def validateParam(member, param):
         pfail("size_is parameters are not supported.")
     if param.retval:
         pfail("Unexpected retval parameter!")
     if param.paramtype in ('out', 'inout'):
         pfail("Out parameters are not supported.")
     if param.const or param.array or param.shared:
         pfail("I am a simple caveman.")
 
-def writeQuickStub(f, customMethodCalls, stringtable, member, stubName,
-                   isSetter=False):
+def writeQuickStub(f, customMethodCalls, member, stubName, isSetter=False):
     """ Write a single quick stub (a custom SpiderMonkey getter/setter/method)
     for the specified XPCOM interface-member. 
     """
     isAttr = (member.kind == 'attribute')
     isMethod = (member.kind == 'method')
     assert isAttr or isMethod
     isGetter = isAttr and not isSetter
 
-    signature = ("static JSBool\n" +
-                 "%s(JSContext *cx, unsigned argc,%s jsval *vp)\n")
+    signature = "static JSBool\n"
+    if isAttr:
+        # JSPropertyOp signature.
+        if isSetter:
+            signature += "%s(JSContext *cx, JSHandleObject obj, JSHandleId id, JSBool strict,%s JSMutableHandleValue vp_)\n"
+        else:
+            signature += "%s(JSContext *cx, JSHandleObject obj, JSHandleId id,%s JSMutableHandleValue vp_)\n"
+    else:
+        # JSFastNative.
+        signature += "%s(JSContext *cx, unsigned argc,%s jsval *vp)\n"
 
     customMethodCall = customMethodCalls.get(stubName, None)
 
     if customMethodCall is None:
         customMethodCall = customMethodCalls.get(member.iface.name + '_', None)
         if customMethodCall is not None:
             if isMethod:
                 code = customMethodCall.get('code', None)
@@ -783,18 +787,22 @@ def writeQuickStub(f, customMethodCalls,
             # arguments, only the template function can.
             callTemplate = signature % (stubName, '')
             callTemplate += "{\n"
 
             nativeName = (member.binaryname is not None and member.binaryname
                           or header.firstCap(member.name))
             argumentValues = (customMethodCall['additionalArgumentValues']
                               % nativeName)
-            callTemplate += ("    return %s(cx, argc, %s, vp);\n"
-                             % (templateName, argumentValues))
+            if isAttr:
+                callTemplate += ("    return %s(cx, obj, id%s, %s, vp_);\n"
+                                 % (templateName, ", strict" if isSetter else "", argumentValues))
+            else:
+                callTemplate += ("    return %s(cx, argc, %s, vp);\n"
+                                 % (templateName, argumentValues))
             callTemplate += "}\n\n"
 
             # Fall through and create the template function stub called from the
             # real stubs, but only generate the stub once. Otherwise, just write
             # out the call to the template function and return.
             templateGenerated = templateName + '_generated'
             if templateGenerated in customMethodCall:
                 f.write(callTemplate)
@@ -820,28 +828,39 @@ def writeQuickStub(f, customMethodCalls,
     if customMethodCall is None or not 'additionalArguments' in customMethodCall:
         additionalArguments = ''
     else:
         additionalArguments = " %s," % customMethodCall['additionalArguments']
     f.write(signature % (stubName, additionalArguments))
     f.write("{\n")
     f.write("    XPC_QS_ASSERT_CONTEXT_OK(cx);\n")
 
-    # Compute "this".
-    f.write("    JSObject *obj = JS_THIS_OBJECT(cx, vp);\n"
-            "    if (!obj)\n"
-            "        return JS_FALSE;\n")
+    # Convert JSMutableHandleValue to jsval*
+    if isAttr:
+        f.write("    jsval *vp = vp_.address();\n")
+
+    # For methods, compute "this".
+    if isMethod:
+        f.write("    JSObject *obj = JS_THIS_OBJECT(cx, vp);\n"
+                "    if (!obj)\n"
+                "        return JS_FALSE;\n")
 
     # Get the 'self' pointer.
     if customMethodCall is None or not 'thisType' in customMethodCall:
         f.write("    %s *self;\n" % member.iface.name)
     else:
         f.write("    %s *self;\n" % customMethodCall['thisType'])
     f.write("    xpc_qsSelfRef selfref;\n")
-    pthisval = '&vp[1]' # as above, ok to overwrite vp[1]
+    if isGetter:
+        pthisval = 'vp'
+    elif isSetter:
+        f.write("    JS::AutoValueRooter tvr(cx);\n")
+        pthisval = 'tvr.jsval_addr()'
+    else:
+        pthisval = '&vp[1]' # as above, ok to overwrite vp[1]
 
     if unwrapThisFailureFatal:
         unwrapFatalArg = "true"
     else:
         unwrapFatalArg = "false"
 
     if not isSetter and isInterfaceType(member.realtype):
         f.write("    XPCLazyCallContext lccx(JS_CALLER, cx, obj);\n")
@@ -859,24 +878,20 @@ def writeQuickStub(f, customMethodCalls,
         f.write("        return JS_TRUE;\n")
         f.write("    }\n");
 
     if isMethod:
         # If there are any required arguments, check argc.
         requiredArgs = len(member.params)
         while requiredArgs and member.params[requiredArgs-1].optional:
             requiredArgs -= 1
-    elif isSetter:
-        requiredArgs = 1
-    else:
-        requiredArgs = 0
-    if requiredArgs:
-        f.write("    if (argc < %d)\n" % requiredArgs)
-        f.write("        return xpc_qsThrow(cx, "
-                "NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n")
+        if requiredArgs:
+            f.write("    if (argc < %d)\n" % requiredArgs)
+            f.write("        return xpc_qsThrow(cx, "
+                    "NS_ERROR_XPC_NOT_ENOUGH_ARGS);\n")
 
     # Convert in-parameters.
     rvdeclared = False
     if isMethod:
         if len(member.params) > 0:
             f.write("    jsval *argv = JS_ARGV(cx, vp);\n")
         for i, param in enumerate(member.params):
             argName = 'arg%d' % i
@@ -890,23 +905,21 @@ def writeQuickStub(f, customMethodCalls,
             # Emit code to convert this argument from jsval.
             rvdeclared = writeArgumentUnboxing(
                 f, i, argName, realtype,
                 optional=param.optional,
                 rvdeclared=rvdeclared,
                 nullBehavior=param.null,
                 undefinedBehavior=param.undefined)
     elif isSetter:
-        f.write("    jsval *argv = JS_ARGV(cx, vp);\n")
         rvdeclared = writeArgumentUnboxing(f, None, 'arg0', member.realtype,
                                            optional=False,
                                            rvdeclared=rvdeclared,
                                            nullBehavior=member.null,
-                                           undefinedBehavior=member.undefined,
-                                           propIndex=stringtable.stringIndex(member.name))
+                                           undefinedBehavior=member.undefined)
 
     canFail = customMethodCall is None or customMethodCall.get('canFail', True)
     if canFail and not rvdeclared:
         f.write("    nsresult rv;\n")
         rvdeclared = True
 
     if code is not None:
         f.write("%s\n" % code)
@@ -969,19 +982,22 @@ def writeQuickStub(f, customMethodCalls,
 
     if canFail:
         # Check for errors.
         f.write("    if (NS_FAILED(rv))\n")
         if isMethod:
             f.write("        return xpc_qsThrowMethodFailed("
                     "cx, rv, vp);\n")
         else:
+            if isGetter:
+                thisval = '*vp'
+            else:
+                thisval = '*tvr.jsval_addr()'
             f.write("        return xpc_qsThrowGetterSetterFailed(cx, rv, " +
-                    "JSVAL_TO_OBJECT(vp[1]), (uint16_t)%d);\n" %
-                    stringtable.stringIndex(member.name))
+                    "JSVAL_TO_OBJECT(%s), id);\n" % thisval)
 
     # Convert the return value.
     if isMethod or isGetter:
         writeResultConv(f, member.realtype, 'vp', '*vp')
     else:
         f.write("    return JS_TRUE;\n")
 
     # Epilog.
@@ -993,39 +1009,38 @@ def writeQuickStub(f, customMethodCalls,
 
 def writeAttrStubs(f, customMethodCalls, stringtable, attr):
     cmc = customMethodCalls.get(attr.iface.name + "_" + header.methodNativeName(attr), None)
     custom = cmc and cmc.get('skipgen', False)
 
     getterName = (attr.iface.name + '_'
                   + header.attributeNativeName(attr, True))
     if not custom:
-        writeQuickStub(f, customMethodCalls, stringtable, attr, getterName)
+        writeQuickStub(f, customMethodCalls, attr, getterName)
     if attr.readonly:
-        setterName = 'xpc_qsGetterOnlyNativeStub'
+        setterName = 'xpc_qsGetterOnlyPropertyStub'
     else:
         setterName = (attr.iface.name + '_'
                       + header.attributeNativeName(attr, False))
         if not custom:
-            writeQuickStub(f, customMethodCalls, stringtable, attr, setterName,
-                           isSetter=True)
+            writeQuickStub(f, customMethodCalls, attr, setterName, isSetter=True)
 
     ps = ('{%d, %s, %s}'
           % (stringtable.stringIndex(attr.name), getterName, setterName))
     return ps
 
 def writeMethodStub(f, customMethodCalls, stringtable, method):
     """ Write a method stub to `f`. Return an xpc_qsFunctionSpec initializer. """
 
     cmc = customMethodCalls.get(method.iface.name + "_" + header.methodNativeName(method), None)
     custom = cmc and cmc.get('skipgen', False)
 
     stubName = method.iface.name + '_' + header.methodNativeName(method)
     if not custom:
-        writeQuickStub(f, customMethodCalls, stringtable, method, stubName)
+        writeQuickStub(f, customMethodCalls, method, stubName)
     fs = '{%d, %d, %s}' % (stringtable.stringIndex(method.name),
                            len(method.params), stubName)
     return fs
 
 def writeStubsForInterface(f, customMethodCalls, stringtable, iface):
     f.write("// === interface %s\n\n" % iface.name)
     propspecs = []
     funcspecs = []