Backout bug 792215 for suspected Windows m-oth leaks.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 25 Sep 2012 22:40:56 -0400
changeset 108088 0ad22be0b52ac4557dbb05d7f301aa7e02140e3a
parent 108087 0787c32d87f3914b6e79137a3d837cc95fcc7a25
child 108089 31c49a095aa9cf24782d909c03cf66f0ce5009ae
push id15371
push userryanvm@gmail.com
push dateWed, 26 Sep 2012 02:42:52 +0000
treeherdermozilla-inbound@0ad22be0b52a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs792215
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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 = []