Bug 614347 - 'XPConnect-wrapped JSObjects must clear their gray bit when they are handed out'. r=peterv+gal, a=blocking.
authorBen Turner <bent.mozilla@gmail.com>
Wed, 16 Feb 2011 12:47:08 -0800
changeset 62786 14dda9fd96862cb20173bf2e346ab575926499b8
parent 62785 34c05b9c0079571c3e15bfaede1e6c5b58d5ada3
child 62788 d7a8d64336ba015136f27e44f3dbaeaa070f755b
push id1
push userroot
push dateTue, 10 Dec 2013 15:46:25 +0000
reviewerspeterv, blocking
bugs614347
milestone2.0b12pre
Bug 614347 - 'XPConnect-wrapped JSObjects must clear their gray bit when they are handed out'. r=peterv+gal, a=blocking.
content/base/public/nsContentUtils.h
content/base/src/nsDOMDocumentType.cpp
content/base/src/nsDocument.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfo.h
dom/base/nsJSEnvironment.cpp
dom/base/nsWrapperCache.h
js/src/jscell.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/xpconnect/idl/nsIXPConnect.idl
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpccallcontext.cpp
js/src/xpconnect/src/xpcconvert.cpp
js/src/xpconnect/src/xpcinlines.h
js/src/xpconnect/src/xpcjsruntime.cpp
js/src/xpconnect/src/xpcmaps.h
js/src/xpconnect/src/xpcprivate.h
js/src/xpconnect/src/xpcpublic.h
js/src/xpconnect/src/xpcquickstubs.cpp
js/src/xpconnect/src/xpcquickstubs.h
js/src/xpconnect/src/xpcvariant.cpp
js/src/xpconnect/src/xpcwrappedjs.cpp
js/src/xpconnect/src/xpcwrappednative.cpp
js/src/xpconnect/src/xpcwrappednativescope.cpp
modules/plugin/base/src/nsJSNPRuntime.cpp
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1275,17 +1275,18 @@ public:
       DropJSObjects(aScriptObjectHolder);
       aCache->SetPreservingWrapper(PR_FALSE);
     }
   }
   static void TraceWrapper(nsWrapperCache* aCache, TraceCallback aCallback,
                            void *aClosure)
   {
     if (aCache->PreservingWrapper()) {
-      aCallback(nsIProgrammingLanguage::JAVASCRIPT, aCache->GetWrapper(),
+      aCallback(nsIProgrammingLanguage::JAVASCRIPT,
+                aCache->GetWrapperPreserveColor(),
                 aClosure);
     }
   }
 
   /**
    * Convert nsIContent::IME_STATUS_* to nsIWidget::IME_STATUS_*
    */
   static PRUint32 GetWidgetStatusFromIMEStatus(PRUint32 aState);
--- a/content/base/src/nsDOMDocumentType.cpp
+++ b/content/base/src/nsDOMDocumentType.cpp
@@ -46,16 +46,17 @@
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
 #include "nsDOMString.h"
 #include "nsIDOM3Node.h"
 #include "nsNodeInfoManager.h"
 #include "nsIDocument.h"
 #include "nsIXPConnect.h"
 #include "nsIDOMDocument.h"
+#include "xpcpublic.h"
 
 nsresult
 NS_NewDOMDocumentType(nsIDOMDocumentType** aDocType,
                       nsNodeInfoManager *aNodeInfoManager,
                       nsIPrincipal *aPrincipal,
                       nsIAtom *aName,
                       nsIDOMNamedNodeMap *aEntities,
                       nsIDOMNamedNodeMap *aNotations,
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -3797,17 +3797,17 @@ nsDocument::SetScriptGlobalObject(nsIScr
     // Go back to using the docshell for the layout history state
     mLayoutHistoryState = nsnull;
     mScopeObject = do_GetWeakReference(aScriptGlobalObject);
 
 #ifdef DEBUG
     if (!mWillReparent) {
       // We really shouldn't have a wrapper here but if we do we need to make sure
       // it has the correct parent.
-      JSObject *obj = GetWrapper();
+      JSObject *obj = GetWrapperPreserveColor();
       if (obj) {
         JSObject *newScope = aScriptGlobalObject->GetGlobalJSObject();
         nsIScriptContext *scx = aScriptGlobalObject->GetContext();
         JSContext *cx = scx ? (JSContext *)scx->GetNativeContext() : nsnull;
         if (!cx) {
           nsContentUtils::ThreadJSContextStack()->Peek(&cx);
           if (!cx) {
             nsContentUtils::ThreadJSContextStack()->GetSafeJSContext(&cx);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -66,16 +66,17 @@
 #include "nsUnicharUtils.h"
 #include "xptcall.h"
 #include "prprf.h"
 #include "nsTArray.h"
 #include "nsCSSValue.h"
 #include "nsIRunnable.h"
 #include "nsThreadUtils.h"
 #include "nsDOMEventTargetWrapperCache.h"
+#include "xpcpublic.h"
 
 // General helper includes
 #include "nsGlobalWindow.h"
 #include "nsHistory.h"
 #include "nsIContent.h"
 #include "nsIAttribute.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
@@ -4241,16 +4242,41 @@ nsDOMClassInfo::GetArrayIndexFromId(JSCo
 
   if (aIsNumber) {
     *aIsNumber = PR_TRUE;
   }
 
   return i;
 }
 
+// static
+nsresult
+nsDOMClassInfo::WrapNative(JSContext *cx, JSObject *scope,
+                           nsISupports *native, nsWrapperCache *cache,
+                           const nsIID* aIID, jsval *vp,
+                           nsIXPConnectJSObjectHolder** aHolder,
+                           PRBool aAllowWrapping)
+{
+  if (!native) {
+    NS_ASSERTION(!aHolder || !*aHolder, "*aHolder should be null!");
+
+    *vp = JSVAL_NULL;
+
+    return NS_OK;
+  }
+
+  JSObject *wrapper = xpc_GetCachedSlimWrapper(cache, scope, vp);
+  if (wrapper) {
+    return NS_OK;
+  }
+
+  return sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
+                                       aAllowWrapping, vp, aHolder);
+}
+
 NS_IMETHODIMP
 nsDOMClassInfo::GetInterfaces(PRUint32 *aCount, nsIID ***aArray)
 {
   PRUint32 count = 0;
 
   while (mData->mInterfaces[count]) {
     count++;
   }
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -43,17 +43,16 @@
 #include "nsIDOMClassInfo.h"
 #include "nsIXPCScriptable.h"
 #include "jsapi.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScriptContext.h"
 #include "nsDOMJSUtils.h" // for GetScriptContextFromJSContext
 #include "nsIScriptGlobalObject.h"
 #include "nsContentUtils.h"
-#include "xpcpublic.h"
 
 class nsIDOMWindow;
 class nsIDOMNSHTMLOptionCollection;
 class nsIPluginInstance;
 class nsIForm;
 class nsIDOMNodeList;
 class nsIDOMDocument;
 class nsIHTMLDocument;
@@ -244,38 +243,21 @@ protected:
             id == sOuterHeight_id  ||
             id == sOuterWidth_id   ||
             id == sScreenX_id      ||
             id == sScreenY_id      ||
             id == sStatus_id       ||
             id == sName_id);
   }
 
-  static nsresult WrapNative(JSContext *cx, JSObject *scope,
-                             nsISupports *native, nsWrapperCache *cache,
-                             const nsIID* aIID, jsval *vp,
-                             nsIXPConnectJSObjectHolder** aHolder,
-                             PRBool aAllowWrapping)
-  {
-    if (!native) {
-      NS_ASSERTION(!aHolder || !*aHolder, "*aHolder should be null!");
-
-      *vp = JSVAL_NULL;
-
-      return NS_OK;
-    }
-
-    JSObject *wrapper = xpc_GetCachedSlimWrapper(cache, scope, vp);
-    if (wrapper) {
-      return NS_OK;
-    }
-
-    return sXPConnect->WrapNativeToJSVal(cx, scope, native, cache, aIID,
-                                         aAllowWrapping, vp, aHolder);
-  }
+  inline static nsresult WrapNative(JSContext *cx, JSObject *scope,
+                                    nsISupports *native, nsWrapperCache *cache,
+                                    const nsIID* aIID, jsval *vp,
+                                    nsIXPConnectJSObjectHolder** aHolder,
+                                    PRBool aAllowWrapping);
 
   static nsIXPConnect *sXPConnect;
   static nsIScriptSecurityManager *sSecMan;
 
   // nsIXPCScriptable code
   static nsresult DefineStaticJSVals(JSContext *cx);
 
   static PRBool sIsInitialized;
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -80,16 +80,17 @@
 #include "nsEventDispatcher.h"
 #include "nsIContent.h"
 #include "nsCycleCollector.h"
 #include "nsNetUtil.h"
 #include "nsXPCOMCIDInternal.h"
 #include "nsIXULRuntime.h"
 
 #include "nsDOMClassInfo.h"
+#include "xpcpublic.h"
 
 #include "jsdbgapi.h"           // for JS_ClearWatchPointsForObject
 #include "jsxdrapi.h"
 #include "nsIArray.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsITimelineService.h"
 #include "nsDOMScriptObjectHolder.h"
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -37,45 +37,66 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsWrapperCache_h___
 #define nsWrapperCache_h___
 
 #include "nsCycleCollectionParticipant.h"
 
 struct JSObject;
+class nsContentUtils;
 
 typedef PRUptrdiff PtrBits;
 
 #define NS_WRAPPERCACHE_IID \
 { 0x3a51ca81, 0xddab, 0x422c, \
   { 0x95, 0x3a, 0x13, 0x06, 0x28, 0x0e, 0xee, 0x14 } }
 
 /**
  * Class to store the XPCWrappedNative for an object. This can only be used
  * with objects that only have one XPCWrappedNative at a time (usually ensured
  * by setting an explicit parent in the PreCreate hook for the class). This
  * object can be gotten by calling QueryInterface, note that this breaks XPCOM
  * rules a bit (this object doesn't derive from nsISupports).
  */
 class nsWrapperCache
 {
+  friend class nsContentUtils;
+
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_WRAPPERCACHE_IID)
 
   nsWrapperCache() : mWrapperPtrBits(0)
   {
   }
   ~nsWrapperCache()
   {
     NS_ASSERTION(!PreservingWrapper(),
                  "Destroying cache with a preserved wrapper!");
   }
 
-  JSObject* GetWrapper() const
+  /**
+   * This getter clears the gray bit before handing out the JSObject which means
+   * that the object is guaranteed to be kept alive past the next CC.
+   *
+   * Implemented in xpcpublic.h because we have to include some JS headers that
+   * don't play nicely with the rest of the codebase. Include xpcpublic.h if you
+   * need to call this method.
+   */
+  inline JSObject* GetWrapper() const;
+
+  /**
+   * This getter does not change the color of the JSObject meaning that the
+   * object returned is not guaranteed to be kept alive past the next CC.
+   *
+   * This should only be called if you are certain that the return value won't
+   * be passed into a JS API function and that it won't be stored without being
+   * rooted (or otherwise signaling the stored value to the CC).
+   */
+  JSObject* GetWrapperPreserveColor() const
   {
     return reinterpret_cast<JSObject*>(mWrapperPtrBits & ~kWrapperBitMask);
   }
 
   void SetWrapper(JSObject* aWrapper)
   {
     NS_ASSERTION(!PreservingWrapper(), "Clearing a preserved wrapper!");
     mWrapperPtrBits = reinterpret_cast<PtrBits>(aWrapper) |
@@ -83,42 +104,43 @@ public:
   }
 
   void ClearWrapper()
   {
     NS_ASSERTION(!PreservingWrapper(), "Clearing a preserved wrapper!");
     mWrapperPtrBits = 0;
   }
 
+  PRBool PreservingWrapper()
+  {
+    return (mWrapperPtrBits & WRAPPER_BIT_PRESERVED) != 0;
+  }
+
+  void SetIsProxy()
+  {
+    mWrapperPtrBits |= WRAPPER_IS_PROXY;
+  }
+
+  PRBool IsProxy()
+  {
+    return (mWrapperPtrBits & WRAPPER_IS_PROXY) != 0;
+  }
+
+private:
+  // Only meant to be called by nsContentUtils.
   void SetPreservingWrapper(PRBool aPreserve)
   {
     if(aPreserve) {
       mWrapperPtrBits |= WRAPPER_BIT_PRESERVED;
     }
     else {
       mWrapperPtrBits &= ~WRAPPER_BIT_PRESERVED;
     }
   }
 
-  PRBool PreservingWrapper()
-  {
-    return (mWrapperPtrBits & WRAPPER_BIT_PRESERVED) != 0;
-  }
-
-  void SetIsProxy()
-  {
-      mWrapperPtrBits |= WRAPPER_IS_PROXY;
-  }
-
-  PRBool IsProxy()
-  {
-      return (mWrapperPtrBits & WRAPPER_IS_PROXY) != 0;
-  }
-
-private:
   enum { WRAPPER_BIT_PRESERVED = 1 << 0 };
   enum { WRAPPER_IS_PROXY = 1 << 1 };
   enum { kWrapperBitMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_PROXY) };
 
   PtrBits mWrapperPtrBits;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
--- a/js/src/jscell.h
+++ b/js/src/jscell.h
@@ -66,16 +66,17 @@ struct Cell {
 
     inline Arena<Cell> *arena() const;
     inline Chunk *chunk() const;
     inline ArenaBitmap *bitmap() const;
     JS_ALWAYS_INLINE size_t cellIndex() const;
 
     JS_ALWAYS_INLINE bool isMarked(uint32 color) const;
     JS_ALWAYS_INLINE bool markIfUnmarked(uint32 color) const;
+    JS_ALWAYS_INLINE void unmark(uint32 color) const;
 
     inline JSCompartment *compartment() const;
 
     /* Needed for compatibility reasons because Cell can't be a base class of JSString */
     JS_ALWAYS_INLINE js::gc::Cell *asCell() { return this; }
 
     JS_ALWAYS_INLINE js::gc::FreeCell *asFreeCell() {
         return reinterpret_cast<FreeCell *>(this);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -500,17 +500,17 @@ IsAboutToBeFinalized(JSContext *cx, void
     JS_ASSERT(rt == thingCompartment->rt);
     if (rt->gcCurrentCompartment != NULL && rt->gcCurrentCompartment != thingCompartment)
         return false;
 
     return !reinterpret_cast<Cell *>(thing)->isMarked();
 }
 
 JS_FRIEND_API(bool)
-js_GCThingIsMarked(void *thing, uint32 color = BLACK)
+js_GCThingIsMarked(void *thing, uintN color = BLACK)
 {
     JS_ASSERT(thing);
     AssertValidColor(thing, color);
     return reinterpret_cast<Cell *>(thing)->isMarked(color);
 }
 
 /*
  * 1/8 life for JIT code. After this number of microseconds have passed, 1/8 of all
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -208,16 +208,23 @@ struct ArenaBitmap {
             mask = (uintptr_t(1) << (bit % JS_BITS_PER_WORD));
             if (*word & mask)
                 return false;
             *word |= mask;
         }
         return true;
     }
 
+    JS_ALWAYS_INLINE void unmark(size_t bit, uint32 color) {
+        bit += color;
+        JS_ASSERT(bit < BitCount);
+        uintptr_t *word = &bitmap[bit / JS_BITS_PER_WORD];
+        *word &= ~(uintptr_t(1) << (bit % JS_BITS_PER_WORD));
+    }
+
 #ifdef DEBUG
     bool noBitsSet() {
         for (unsigned i = 0; i < BitWords; i++) {
             if (bitmap[i] != uintptr_t(0))
                 return false;
         }
         return true;
     }
@@ -456,16 +463,24 @@ Cell::isMarked(uint32 color = BLACK) con
 
 bool
 Cell::markIfUnmarked(uint32 color = BLACK) const
 {
     AssertValidColor(this, color);
     return bitmap()->markIfUnmarked(cellIndex(), color);
 }
 
+void
+Cell::unmark(uint32 color) const
+{
+    JS_ASSERT(color != BLACK);
+    AssertValidColor(this, color);
+    bitmap()->unmark(cellIndex(), color);
+}
+
 JSCompartment *
 Cell::compartment() const
 {
     return arena()->header()->compartment;
 }
 
 template <typename T>
 static inline
@@ -795,17 +810,17 @@ js_LockGCThingRT(JSRuntime *rt, void *th
 
 extern void
 js_UnlockGCThingRT(JSRuntime *rt, void *thing);
 
 extern JS_FRIEND_API(bool)
 IsAboutToBeFinalized(JSContext *cx, void *thing);
 
 extern JS_FRIEND_API(bool)
-js_GCThingIsMarked(void *thing, uint32 color);
+js_GCThingIsMarked(void *thing, uintN color);
 
 extern void
 js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp);
 
 namespace js {
 
 extern JS_REQUIRES_STACK void
 MarkRuntime(JSTracer *trc);
--- a/js/src/xpconnect/idl/nsIXPConnect.idl
+++ b/js/src/xpconnect/idl/nsIXPConnect.idl
@@ -50,17 +50,18 @@
 #include "nsIInterfaceInfoManager.idl"
 #include "nsIExceptionService.idl"
 #include "nsIVariant.idl"
 
 %{ C++
 #include "jspubtd.h"
 #include "xptinfo.h"
 #include "nsAXPCNativeCallContext.h"
-#include "nsWrapperCache.h"
+
+class nsWrapperCache;
 %}
 
 /***************************************************************************/
 
 // NB: jsval and jsid are declared in nsIVariant.idl
 
 [ptr] native JSContextPtr(JSContext);
 [ptr] native JSClassPtr(JSClass);
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -44,16 +44,17 @@
 
 #include "xpcprivate.h"
 #include "XPCWrapper.h"
 #include "nsBaseHashtable.h"
 #include "nsHashKeys.h"
 #include "jsatom.h"
 #include "jsobj.h"
 #include "jsfun.h"
+#include "jsgc.h"
 #include "jsscript.h"
 #include "nsThreadUtilsInternal.h"
 #include "dom_quickstubs.h"
 #include "nsNullPrincipal.h"
 #include "nsIURI.h"
 #include "nsJSEnvironment.h"
 #include "plbase64.h"
 #include "jstypedarray.h"
@@ -553,30 +554,73 @@ nsXPConnect::Unlink(void *p)
 }
 
 NS_IMETHODIMP
 nsXPConnect::Unroot(void *p)
 {
     return NS_OK;
 }
 
+static void
+UnmarkGrayChildren(JSTracer *trc, void *thing, uint32 kind)
+{
+    // If this thing is not a CC-kind or already non-gray then we're done.
+    if(!ADD_TO_CC(kind) || !xpc_IsGrayGCThing(thing))
+        return;
+
+    // Unmark.
+    static_cast<js::gc::Cell *>(thing)->unmark(XPC_GC_COLOR_GRAY);
+
+    // Trace children.
+    JS_TraceChildren(trc, thing, kind);
+}
+
+void
+xpc_UnmarkGrayObjectRecursive(JSObject *obj)
+{
+    NS_ASSERTION(obj, "Don't pass me null!");
+
+    // Unmark.
+    obj->unmark(XPC_GC_COLOR_GRAY);
+
+    // Tracing requires a JSContext...
+    JSContext *cx;
+    nsXPConnect* xpc = nsXPConnect::GetXPConnect();
+    if(!xpc || NS_FAILED(xpc->GetSafeJSContext(&cx)) || !cx)
+    {
+        NS_ERROR("Failed to get safe JSContext!");
+        return;
+    }
+
+    // Trace children.
+    JSTracer trc;
+    JS_TRACER_INIT(&trc, cx, UnmarkGrayChildren);
+    JS_TraceChildren(&trc, obj, JSTRACE_OBJECT);
+}
+
 struct TraversalTracer : public JSTracer
 {
     TraversalTracer(nsCycleCollectionTraversalCallback &aCb) : cb(aCb)
     {
     }
     nsCycleCollectionTraversalCallback &cb;
 };
 
 static void
 NoteJSChild(JSTracer *trc, void *thing, uint32 kind)
 {
     if(ADD_TO_CC(kind))
     {
         TraversalTracer *tracer = static_cast<TraversalTracer*>(trc);
+
+        // There's no point in further traversing a non-gray object here unless
+        // we explicitly want to see all traces.
+        if(!xpc_IsGrayGCThing(thing) && !tracer->cb.WantAllTraces())
+            return;
+
 #if defined(DEBUG)
         if (NS_UNLIKELY(tracer->cb.WantDebugInfo())) {
             // based on DumpNotify in jsapi.c
             if (tracer->debugPrinter) {
                 char buffer[200];
                 tracer->debugPrinter(trc, buffer, sizeof(buffer));
                 tracer->cb.NoteNextEdgeName(buffer);
             } else if (tracer->debugPrintIndex != (size_t)-1) {
@@ -608,22 +652,16 @@ WrapperIsNotMainThreadOnly(XPCWrappedNat
 
     // If the native participates in cycle collection then we know it can only
     // be used on the main thread, in that case we assume the wrapped native
     // can only be used on the main thread too.
     nsXPCOMCycleCollectionParticipant* participant;
     return NS_FAILED(CallQueryInterface(wrapper->Native(), &participant));
 }
 
-JSBool
-nsXPConnect::IsGray(void *thing)
-{
-    return js_GCThingIsMarked(thing, XPC_GC_COLOR_GRAY);
-}
-
 NS_IMETHODIMP
 nsXPConnect::Traverse(void *p, nsCycleCollectionTraversalCallback &cb)
 {
     JSContext *cx = mCycleCollectionContext->GetJSContext();
 
     uint32 traceKind = js_GetGCThingTraceKind(p);
     JSObject *obj;
     js::Class *clazz;
@@ -675,17 +713,17 @@ nsXPConnect::Traverse(void *p, nsCycleCo
             PL_DHashTableOperate(&mJSRoots, p, PL_DHASH_LOOKUP);
         type = markJSObject || PL_DHASH_ENTRY_IS_BUSY(entry) ? GCMarked :
                                                                GCUnmarked;
     }
     else
 #endif
     {
         // Normal codepath (matches non-DEBUG_CC codepath).
-        type = !markJSObject && IsGray(p) ? GCUnmarked : GCMarked;
+        type = !markJSObject && xpc_IsGrayGCThing(p) ? GCUnmarked : GCMarked;
     }
 
     if (cb.WantDebugInfo()) {
         char name[72];
         if(traceKind == JSTRACE_OBJECT)
         {
             JSObject *obj = static_cast<JSObject*>(p);
             js::Class *clazz = obj->getClass();
@@ -770,17 +808,17 @@ nsXPConnect::Traverse(void *p, nsCycleCo
 
     TraversalTracer trc(cb);
 
     JS_TRACER_INIT(&trc, cx, NoteJSChild);
     JS_TraceChildren(&trc, p, traceKind);
 
     if(traceKind != JSTRACE_OBJECT || dontTraverse)
         return NS_OK;
-    
+
     if(clazz == &XPC_WN_Tearoff_JSClass)
     {
         // A tearoff holds a strong reference to its native object
         // (see XPCWrappedNative::FlatJSObjectFinalized). Its XPCWrappedNative
         // will be held alive through the parent of the JSObject of the tearoff.
         XPCWrappedNativeTearOff *to =
             (XPCWrappedNativeTearOff*) xpc_GetJSPrivate(obj);
         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "xpc_GetJSPrivate(obj)->mNative");
@@ -1589,17 +1627,17 @@ MoveWrapper(XPCCallContext& ccx, XPCWrap
 
         XPCWrappedNative *parentWrapper =
             XPCWrappedNative::GetWrappedNativeOfJSObject(ccx, newParent);
 
         rv = MoveWrapper(ccx, parentWrapper, newScope, oldScope);
 
         NS_ENSURE_SUCCESS(rv, rv);
 
-        newParent = parentWrapper->GetFlatJSObjectNoMark();
+        newParent = parentWrapper->GetFlatJSObject();
     }
     else
         NS_ASSERTION(betterScope == newScope, "Weird scope returned");
 
     // Now, reparent the wrapper, since we know that it wants to be
     // reparented.
 
     nsRefPtr<XPCWrappedNative> junk;
--- a/js/src/xpconnect/src/xpccallcontext.cpp
+++ b/js/src/xpconnect/src/xpccallcontext.cpp
@@ -193,17 +193,17 @@ XPCCallContext::Init(XPCContext::LangTyp
         mWrapper = XPCWrappedNative::GetWrappedNativeOfJSObject(mJSContext, obj,
                                                                 funobj,
                                                                 &mFlattenedJSObject,
                                                                 &mTearOff);
         if(mWrapper)
         {
             DEBUG_CheckWrapperThreadSafety(mWrapper);
 
-            mFlattenedJSObject = mWrapper->GetFlatJSObjectAndMark();
+            mFlattenedJSObject = mWrapper->GetFlatJSObject();
 
             if(mTearOff)
                 mScriptableInfo = nsnull;
             else
                 mScriptableInfo = mWrapper->GetScriptableInfo();
         }
         else
         {
--- a/js/src/xpconnect/src/xpcconvert.cpp
+++ b/js/src/xpconnect/src/xpcconvert.cpp
@@ -1305,17 +1305,17 @@ XPCConvert::NativeInterface2JSObject(XPC
         *pErr = rv;
 
     // If creating the wrapped native failed, then return early.
     if(NS_FAILED(rv) || !wrapper)
         return JS_FALSE;
 
     // If we're not creating security wrappers, we can return the
     // XPCWrappedNative as-is here.
-    flat = wrapper->GetFlatJSObjectAndMark();
+    flat = wrapper->GetFlatJSObject();
     jsval v = OBJECT_TO_JSVAL(flat);
     if(!XPCPerThreadData::IsMainThread(lccx.GetJSContext()) ||
        !allowNativeWrapper)
     {
         *d = v;
         if(dest)
             *dest = strongWrapper.forget().get();
         return JS_TRUE;
--- a/js/src/xpconnect/src/xpcinlines.h
+++ b/js/src/xpconnect/src/xpcinlines.h
@@ -786,17 +786,17 @@ inline void
 XPCLazyCallContext::SetWrapper(XPCWrappedNative* wrapper,
                                XPCWrappedNativeTearOff* tearoff)
 {
     mWrapper = wrapper;
     mTearOff = tearoff;
     if(mTearOff)
         mFlattenedJSObject = mTearOff->GetJSObject();
     else
-        mFlattenedJSObject = mWrapper->GetFlatJSObjectAndMark();
+        mFlattenedJSObject = mWrapper->GetFlatJSObject();
 }
 inline void
 XPCLazyCallContext::SetWrapper(JSObject* flattenedJSObject)
 {
     NS_ASSERTION(IS_SLIM_WRAPPER_OBJECT(flattenedJSObject),
                  "What kind of object is this?");
     mWrapper = nsnull;
     mTearOff = nsnull;
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -89,18 +89,20 @@ WrappedJSDyingJSObjectFinder(JSDHashTabl
     nsXPCWrappedJS* wrapper = ((JSObject2WrappedJSMap::Entry*)hdr)->value;
     NS_ASSERTION(wrapper, "found a null JS wrapper!");
 
     // walk the wrapper chain and find any whose JSObject is to be finalized
     while(wrapper)
     {
         if(wrapper->IsSubjectToFinalization())
         {
-            js::SwitchToCompartment sc(data->cx, wrapper->GetJSObject());
-            if(JS_IsAboutToBeFinalized(data->cx, wrapper->GetJSObject()))
+            js::SwitchToCompartment sc(data->cx,
+                                       wrapper->GetJSObjectPreserveColor());
+            if(JS_IsAboutToBeFinalized(data->cx,
+                                       wrapper->GetJSObjectPreserveColor()))
                 data->array->AppendElement(wrapper);
         }
         wrapper = wrapper->GetNextWrapper();
     }
     return JS_DHASH_NEXT;
 }
 
 struct CX_AND_XPCRT_Data
@@ -498,26 +500,23 @@ XPCJSRuntime::SuspectWrappedNative(JSCon
     if(!wrapper->IsValid() || wrapper->IsWrapperExpired())
         return;
 
     NS_ASSERTION(NS_IsMainThread() || NS_IsCycleCollectorThread(), 
                  "Suspecting wrapped natives from non-CC thread");
 
     // Only suspect wrappedJSObjects that are in a compartment that
     // participates in cycle collection.
-    JSObject* obj = wrapper->GetFlatJSObjectAndMark();
+    JSObject* obj = wrapper->GetFlatJSObjectPreserveColor();
     if(!xpc::ParticipatesInCycleCollection(cx, obj))
         return;
 
-    NS_ASSERTION(!JS_IsAboutToBeFinalized(cx, obj),
-                 "SuspectWrappedNative attempting to touch dead object");
-
     // Only record objects that might be part of a cycle as roots, unless
     // the callback wants all traces (a debug feature).
-    if(nsXPConnect::IsGray(obj) || cb.WantAllTraces())
+    if(xpc_IsGrayGCThing(obj) || cb.WantAllTraces())
         cb.NoteRoot(nsIProgrammingLanguage::JAVASCRIPT, obj,
                     nsXPConnect::GetXPConnect());
 }
 
 static PLDHashOperator
 SuspectExpandos(XPCWrappedNative *wrapper, JSObject *&expando, void *arg)
 {
     Closure* closure = static_cast<Closure*>(arg);
@@ -567,17 +566,17 @@ XPCJSRuntime::AddXPConnectRoots(JSContex
     XPCWrappedNativeScope::SuspectAllWrappers(this, cx, cb);
 
     for(XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot())
         cb.NoteXPCOMRoot(static_cast<XPCTraceableVariant*>(e));
 
     for(XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot())
     {
         nsXPCWrappedJS *wrappedJS = static_cast<nsXPCWrappedJS*>(e);
-        JSObject *obj = wrappedJS->GetJSObject();
+        JSObject *obj = wrappedJS->GetJSObjectPreserveColor();
 
         // Only suspect wrappedJSObjects that are in a compartment that
         // participates in cycle collection.
         if(!xpc::ParticipatesInCycleCollection(cx, obj))
             continue;
 
         cb.NoteXPCOMRoot(static_cast<nsIXPConnectWrappedJS *>(wrappedJS));
     }
@@ -635,17 +634,17 @@ SweepWaiverWrappers(JSDHashTable *table,
         return JS_DHASH_REMOVE;
     return JS_DHASH_NEXT;
 }
 
 static PLDHashOperator
 SweepExpandos(XPCWrappedNative *wn, JSObject *&expando, void *arg)
 {
     JSContext *cx = (JSContext *)arg;
-    return IsAboutToBeFinalized(cx, wn->GetFlatJSObjectNoMark())
+    return IsAboutToBeFinalized(cx, wn->GetFlatJSObjectPreserveColor())
            ? PL_DHASH_REMOVE
            : PL_DHASH_NEXT;
 }
 
 static PLDHashOperator
 SweepCompartment(nsCStringHashKey& aKey, JSCompartment *compartment, void *aClosure)
 {
     xpc::CompartmentPrivate *priv = (xpc::CompartmentPrivate *)
--- a/js/src/xpconnect/src/xpcmaps.h
+++ b/js/src/xpconnect/src/xpcmaps.h
@@ -74,32 +74,33 @@ public:
         if(JS_DHASH_ENTRY_IS_FREE(entry))
             return nsnull;
         return entry->value;
     }
 
     inline nsXPCWrappedJS* Add(nsXPCWrappedJS* wrapper)
     {
         NS_PRECONDITION(wrapper,"bad param");
-        JSObject* obj = wrapper->GetJSObject();
+        JSObject* obj = wrapper->GetJSObjectPreserveColor();
         Entry* entry = (Entry*)
             JS_DHashTableOperate(mTable, obj, JS_DHASH_ADD);
         if(!entry)
             return nsnull;
         if(entry->key)
             return entry->value;
         entry->key = obj;
         entry->value = wrapper;
         return wrapper;
     }
 
     inline void Remove(nsXPCWrappedJS* wrapper)
     {
         NS_PRECONDITION(wrapper,"bad param");
-        JS_DHashTableOperate(mTable, wrapper->GetJSObject(), JS_DHASH_REMOVE);
+        JS_DHashTableOperate(mTable, wrapper->GetJSObjectPreserveColor(),
+                             JS_DHASH_REMOVE);
     }
 
     inline uint32 Count() {return mTable->entryCount;}
     inline uint32 Enumerate(JSDHashEnumerator f, void *arg)
         {return JS_DHashTableEnumerate(mTable, f, arg);}
 
     ~JSObject2WrappedJSMap();
 private:
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -55,17 +55,16 @@
 #include "jsdhash.h"
 #include "jsprf.h"
 #include "prprf.h"
 #include "jsinterp.h"
 #include "jscntxt.h"
 #include "jsdbgapi.h"
 #include "jsgc.h"
 #include "jscompartment.h"
-#include "xpcpublic.h"
 #include "nscore.h"
 #include "nsXPCOM.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCycleCollector.h"
 #include "nsDebug.h"
 #include "nsISupports.h"
 #include "nsIServiceManager.h"
@@ -484,19 +483,16 @@ private:
 /***************************************************************************
 ****************************************************************************
 *
 * Core runtime and context classes...
 *
 ****************************************************************************
 ***************************************************************************/
 
-static const uint32 XPC_GC_COLOR_BLACK = 0;
-static const uint32 XPC_GC_COLOR_GRAY = 1;
-
 // We have a general rule internally that getters that return addref'd interface
 // pointer generally do so using an 'out' parm. When interface pointers are
 // returned as function call result values they are not addref'd. Exceptions
 // to this rule are noted explicitly.
 
 const PRBool OBJ_IS_GLOBAL = PR_TRUE;
 const PRBool OBJ_IS_NOT_GLOBAL = PR_FALSE;
 
@@ -552,21 +548,16 @@ public:
     static void InitStatics() { gSelf = nsnull; gOnceAliveNowDead = JS_FALSE; }
     // Called by module code on dll shutdown.
     static void ReleaseXPConnectSingleton();
 
     virtual ~nsXPConnect();
 
     JSBool IsShuttingDown() const {return mShuttingDown;}
 
-    // The JS GC marks objects gray that are held alive directly or indirectly
-    // by an XPConnect root. The cycle collector explores only this subset
-    // of the JS heap.
-    static JSBool IsGray(void *thing);
-
     nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info);
     nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);
 
     static nsresult Base64Encode(const nsACString &aString,
                                  nsACString &aBinary);
 
     static nsresult Base64Encode(const nsAString &aString,
                                  nsAString &aBinaryData);
@@ -2545,24 +2536,36 @@ public:
     GetScope() const
         {return GetProto() ? GetProto()->GetScope() :
          (XPCWrappedNativeScope*)
          (XPC_SCOPE_WORD(mMaybeScope) & ~XPC_SCOPE_MASK);}
 
     nsISupports*
     GetIdentityObject() const {return mIdentity;}
 
+    /**
+     * This getter clears the gray bit before handing out the JSObject which
+     * means that the object is guaranteed to be kept alive past the next CC.
+     */
     JSObject*
-    GetFlatJSObjectAndMark() const
-        {if(mFlatJSObject && mFlatJSObject != INVALID_OBJECT)
-             mFlatJSObject->markIfUnmarked();
+    GetFlatJSObject() const
+        {if(mFlatJSObject != INVALID_OBJECT)
+             xpc_UnmarkGrayObject(mFlatJSObject);
          return mFlatJSObject;}
 
+    /**
+     * This getter does not change the color of the JSObject meaning that the
+     * object returned is not guaranteed to be kept alive past the next CC.
+     *
+     * This should only be called if you are certain that the return value won't
+     * be passed into a JS API function and that it won't be stored without
+     * being rooted (or otherwise signaling the stored value to the CC).
+     */
     JSObject*
-    GetFlatJSObjectNoMark() const {return mFlatJSObject;}
+    GetFlatJSObjectPreserveColor() const {return mFlatJSObject;}
 
     XPCLock*
     GetLock() const {return IsValid() && HasProto() ?
                                 GetProto()->GetLock() : nsnull;}
 
     XPCNativeSet*
     GetSet() const {XPCAutoLock al(GetLock()); return mSet;}
 
@@ -2690,17 +2693,17 @@ public:
     }
 
     // Yes, we *do* need to mark the mScriptableInfo in both cases.
     inline void TraceJS(JSTracer* trc)
     {
         if(mScriptableInfo && JS_IsGCMarkingTracer(trc))
             mScriptableInfo->Mark();
         if(HasProto()) GetProto()->TraceJS(trc);
-        JSObject* wrapper = GetWrapper();
+        JSObject* wrapper = GetWrapperPreserveColor();
         if(wrapper)
             JS_CALL_OBJECT_TRACER(trc, wrapper, "XPCWrappedNative::mWrapper");
     }
 
     inline void AutoTrace(JSTracer* trc)
     {
         // If this got called, we're being kept alive by someone who really
         // needs us alive and whole.  Do not let our mFlatJSObject go away.
@@ -2736,19 +2739,29 @@ public:
 
     JSBool HasExternalReference() const {return mRefCnt > 1;}
 
     JSBool NeedsSOW() { return !!(mWrapperWord & NEEDS_SOW); }
     void SetNeedsSOW() { mWrapperWord |= NEEDS_SOW; }
     JSBool NeedsCOW() { return !!(mWrapperWord & NEEDS_COW); }
     void SetNeedsCOW() { mWrapperWord |= NEEDS_COW; }
 
+    JSObject* GetWrapperPreserveColor() const
+        {return (JSObject*)(mWrapperWord & (size_t)~(size_t)FLAG_MASK);}
+
     JSObject* GetWrapper()
     {
-        return (JSObject *) (mWrapperWord & (size_t)~(size_t)FLAG_MASK);
+        JSObject* wrapper = GetWrapperPreserveColor();
+        if(wrapper)
+        {
+            xpc_UnmarkGrayObject(wrapper);
+            // Call this to unmark mFlatJSObject.
+            GetFlatJSObject();
+        }
+        return wrapper;
     }
     void SetWrapper(JSObject *obj)
     {
         PRWord needsSOW = NeedsSOW() ? NEEDS_SOW : 0;
         PRWord needsCOW = NeedsCOW() ? NEEDS_COW : 0;
         mWrapperWord = PRWord(obj) |
                          needsSOW |
                          needsCOW;
@@ -3000,17 +3013,34 @@ public:
     static nsresult
     GetNewOrUsed(XPCCallContext& ccx,
                  JSObject* aJSObj,
                  REFNSIID aIID,
                  nsISupports* aOuter,
                  nsXPCWrappedJS** wrapper);
 
     nsISomeInterface* GetXPTCStub() { return mXPTCStub; }
-    JSObject* GetJSObject() const {return mJSObj;}
+
+    /**
+     * This getter clears the gray bit before handing out the JSObject which
+     * means that the object is guaranteed to be kept alive past the next CC.
+     */
+    JSObject* GetJSObject() const {xpc_UnmarkGrayObject(mJSObj);
+                                   return mJSObj;}
+
+    /**
+     * This getter does not change the color of the JSObject meaning that the
+     * object returned is not guaranteed to be kept alive past the next CC.
+     *
+     * This should only be called if you are certain that the return value won't
+     * be passed into a JS API function and that it won't be stored without
+     * being rooted (or otherwise signaling the stored value to the CC).
+     */
+    JSObject* GetJSObjectPreserveColor() const {return mJSObj;}
+
     nsXPCWrappedJSClass*  GetClass() const {return mClass;}
     REFNSIID GetIID() const {return GetClass()->GetIID();}
     nsXPCWrappedJS* GetRootWrapper() const {return mRoot;}
     nsXPCWrappedJS* GetNextWrapper() const {return mNext;}
 
     nsXPCWrappedJS* Find(REFNSIID aIID);
     nsXPCWrappedJS* FindInherited(REFNSIID aIID);
 
@@ -4316,20 +4346,35 @@ public:
 
     // We #define and iid so that out module local code can use QI to detect 
     // if a given nsIVariant is in fact an XPCVariant. 
     NS_DECLARE_STATIC_IID_ACCESSOR(XPCVARIANT_IID)
 
     static XPCVariant* newVariant(XPCCallContext& ccx, jsval aJSVal);
 
     /**
-     * nsIVariant exposes a GetAsJSVal() method, which also returns mJSVal.
-     * But if you can, you should call this one, since it can be inlined.
+     * This getter clears the gray bit before handing out the jsval if the jsval
+     * represents a JSObject. That means that the object is guaranteed to be
+     * kept alive past the next CC.
      */
-    jsval GetJSVal() const {return mJSVal;}
+    jsval GetJSVal() const
+        {if(!JSVAL_IS_PRIMITIVE(mJSVal))
+             xpc_UnmarkGrayObject(JSVAL_TO_OBJECT(mJSVal));
+         return mJSVal;}
+
+    /**
+     * This getter does not change the color of the jsval (if it represents a
+     * JSObject) meaning that the value returned is not guaranteed to be kept
+     * alive past the next CC.
+     *
+     * This should only be called if you are certain that the return value won't
+     * be passed into a JS API function and that it won't be stored without
+     * being rooted (or otherwise signaling the stored value to the CC).
+     */
+    jsval GetJSValPreserveColor() const {return mJSVal;}
 
     XPCVariant(XPCCallContext& ccx, jsval aJSVal);
 
     /**
      * Convert a variant into a jsval.
      *
      * @param ccx the context for the whole procedure
      * @param variant the variant to convert
@@ -4510,17 +4555,19 @@ struct CompartmentPrivate
                 return false;
         }
         return expandoMap->Put(wn, expando);
     }
 
     JSObject *LookupExpandoObject(XPCWrappedNative *wn) {
         if (!expandoMap)
             return nsnull;
-        return expandoMap->Get(wn);
+        JSObject *obj = expandoMap->Get(wn);
+        xpc_UnmarkGrayObject(obj);
+        return obj;
     }
 };
 
 inline bool
 CompartmentParticipatesInCycleCollection(JSContext *cx, JSCompartment *compartment)
 {
     CompartmentPrivate *priv =
         static_cast<CompartmentPrivate *>(JS_GetCompartmentPrivate(cx, compartment));
--- a/js/src/xpconnect/src/xpcpublic.h
+++ b/js/src/xpconnect/src/xpcpublic.h
@@ -45,16 +45,19 @@
 #include "jsgc.h"
 
 #include "nsISupports.h"
 #include "nsIPrincipal.h"
 #include "nsWrapperCache.h"
 
 class nsIPrincipal;
 
+static const uint32 XPC_GC_COLOR_BLACK = 0;
+static const uint32 XPC_GC_COLOR_GRAY = 1;
+
 nsresult
 xpc_CreateGlobalObject(JSContext *cx, JSClass *clasp,
                        nsIPrincipal *principal, nsISupports *ptr,
                        bool wantXrays, JSObject **global,
                        JSCompartment **compartment);
 
 nsresult
 xpc_CreateMTGlobalObject(JSContext *cx, JSClass *clasp,
@@ -131,9 +134,39 @@ xpc_GetCachedSlimWrapper(nsWrapperCache 
 
 inline JSObject*
 xpc_GetCachedSlimWrapper(nsWrapperCache *cache, JSObject *scope)
 {
     jsval dummy;
     return xpc_GetCachedSlimWrapper(cache, scope, &dummy);
 }
 
+// The JS GC marks objects gray that are held alive directly or indirectly
+// by an XPConnect root. The cycle collector explores only this subset
+// of the JS heap.
+inline JSBool
+xpc_IsGrayGCThing(void *thing)
+{
+    return js_GCThingIsMarked(thing, XPC_GC_COLOR_GRAY);
+}
+
+// Implemented in nsXPConnect.cpp.
+extern void
+xpc_UnmarkGrayObjectRecursive(JSObject* obj);
+
+// Remove the gray color from the given JSObject and any other objects that can
+// be reached through it.
+inline void
+xpc_UnmarkGrayObject(JSObject *obj)
+{
+    if(obj && xpc_IsGrayGCThing(obj))
+        xpc_UnmarkGrayObjectRecursive(obj);
+}
+
+inline JSObject*
+nsWrapperCache::GetWrapper() const
+{
+  JSObject* obj = GetWrapperPreserveColor();
+  xpc_UnmarkGrayObject(obj);
+  return obj;
+}
+
 #endif
--- a/js/src/xpconnect/src/xpcquickstubs.cpp
+++ b/js/src/xpconnect/src/xpcquickstubs.cpp
@@ -811,18 +811,17 @@ inline nsresult
 getNativeFromWrapper(JSContext *cx,
                      XPCWrappedNative *wrapper,
                      const nsIID &iid,
                      void **ppThis,
                      nsISupports **pThisRef,
                      jsval *vp)
 {
     return getNative(wrapper->GetIdentityObject(), wrapper->GetOffsets(),
-                     wrapper->GetFlatJSObjectAndMark(), iid, ppThis, pThisRef,
-                     vp);
+                     wrapper->GetFlatJSObject(), iid, ppThis, pThisRef, vp);
 }
 
 
 nsresult
 getWrapper(JSContext *cx,
            JSObject *obj,
            JSObject *callee,
            XPCWrappedNative **wrapper,
--- a/js/src/xpconnect/src/xpcquickstubs.h
+++ b/js/src/xpconnect/src/xpcquickstubs.h
@@ -576,17 +576,17 @@ castNativeFromWrapper(JSContext *cx,
         if (NS_FAILED(*rv))
             return nsnull;
     }
 
     nsISupports *native;
     if(wrapper)
     {
         native = wrapper->GetIdentityObject();
-        cur = wrapper->GetFlatJSObjectAndMark();
+        cur = wrapper->GetFlatJSObject();
     }
     else
     {
         native = cur ?
                  static_cast<nsISupports*>(xpc_GetJSPrivate(cur)) :
                  nsnull;
     }
 
--- a/js/src/xpconnect/src/xpcvariant.cpp
+++ b/js/src/xpconnect/src/xpcvariant.cpp
@@ -76,59 +76,66 @@ XPCVariant::XPCVariant(XPCCallContext& c
         mReturnRawObject = !wn && !proto;
     }
     else
         mReturnRawObject = JS_FALSE;
 }
 
 XPCTraceableVariant::~XPCTraceableVariant()
 {
-    NS_ASSERTION(JSVAL_IS_GCTHING(mJSVal), "Must be traceable or unlinked");
+    jsval val = GetJSValPreserveColor();
+
+    NS_ASSERTION(JSVAL_IS_GCTHING(val), "Must be traceable or unlinked");
 
-    // If mJSVal is JSVAL_STRING, we don't need to clean anything up;
-    // simply removing the string from the root set is good.
-    if(!JSVAL_IS_STRING(mJSVal))
+    // If val is JSVAL_STRING, we don't need to clean anything up; simply
+    // removing the string from the root set is good.
+    if(!JSVAL_IS_STRING(val))
         nsVariant::Cleanup(&mData);
 
-    if (!JSVAL_IS_NULL(mJSVal))
+    if (!JSVAL_IS_NULL(val))
         RemoveFromRootSet(nsXPConnect::GetRuntimeInstance()->GetMapLock());
 }
 
 void XPCTraceableVariant::TraceJS(JSTracer* trc)
 {
-    NS_ASSERTION(JSVAL_IS_TRACEABLE(mJSVal), "Must be traceable");
+    jsval val = GetJSValPreserveColor();
+
+    NS_ASSERTION(JSVAL_IS_TRACEABLE(val), "Must be traceable");
     JS_SET_TRACING_DETAILS(trc, PrintTraceName, this, 0);
-    JS_CallTracer(trc, JSVAL_TO_TRACEABLE(mJSVal), JSVAL_TRACE_KIND(mJSVal));
+    JS_CallTracer(trc, JSVAL_TO_TRACEABLE(val), JSVAL_TRACE_KIND(val));
 }
 
 #ifdef DEBUG
 // static
 void
 XPCTraceableVariant::PrintTraceName(JSTracer* trc, char *buf, size_t bufsize)
 {
     JS_snprintf(buf, bufsize, "XPCVariant[0x%p].mJSVal", trc->debugPrintArg);
 }
 #endif
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPCVariant)
-    if(JSVAL_IS_OBJECT(tmp->mJSVal))
+    jsval val = tmp->GetJSValPreserveColor();
+    if(JSVAL_IS_OBJECT(val))
         cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
-                           JSVAL_TO_OBJECT(tmp->mJSVal));
+                           JSVAL_TO_OBJECT(val));
 
     nsVariant::Traverse(tmp->mData, cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XPCVariant)
-    // We're sharing mJSVal's buffer, clear the pointer to it
-    // so Cleanup() won't try to delete it
-    if(JSVAL_IS_STRING(tmp->mJSVal))
+    jsval val = tmp->GetJSValPreserveColor();
+
+    // We're sharing val's buffer, clear the pointer to it so Cleanup() won't
+    // try to delete it
+    if(JSVAL_IS_STRING(val))
         tmp->mData.u.wstr.mWStringValue = nsnull;
     nsVariant::Cleanup(&tmp->mData);
 
-    if(JSVAL_IS_TRACEABLE(tmp->mJSVal))
+    if(JSVAL_IS_TRACEABLE(val))
     {
         XPCTraceableVariant *v = static_cast<XPCTraceableVariant*>(tmp);
         v->RemoveFromRootSet(nsXPConnect::GetRuntimeInstance()->GetMapLock());
     }
     tmp->mJSVal = JSVAL_NULL;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 // static 
@@ -295,34 +302,35 @@ XPCArrayHomogenizer::GetTypeForArray(XPC
     }
     return JS_TRUE;
 }
 
 JSBool XPCVariant::InitializeData(XPCCallContext& ccx)
 {
     JS_CHECK_RECURSION(ccx.GetJSContext(), return JS_FALSE);
 
-    if(JSVAL_IS_INT(mJSVal))
-        return NS_SUCCEEDED(nsVariant::SetFromInt32(&mData, 
-                                                    JSVAL_TO_INT(mJSVal)));
-    if(JSVAL_IS_DOUBLE(mJSVal))
+    jsval val = GetJSVal();
+
+    if(JSVAL_IS_INT(val))
+        return NS_SUCCEEDED(nsVariant::SetFromInt32(&mData, JSVAL_TO_INT(val)));
+    if(JSVAL_IS_DOUBLE(val))
         return NS_SUCCEEDED(nsVariant::SetFromDouble(&mData, 
-                                                     JSVAL_TO_DOUBLE(mJSVal)));
-    if(JSVAL_IS_BOOLEAN(mJSVal))
+                                                     JSVAL_TO_DOUBLE(val)));
+    if(JSVAL_IS_BOOLEAN(val))
         return NS_SUCCEEDED(nsVariant::SetFromBool(&mData, 
-                                                   JSVAL_TO_BOOLEAN(mJSVal)));
-    if(JSVAL_IS_VOID(mJSVal))
+                                                   JSVAL_TO_BOOLEAN(val)));
+    if(JSVAL_IS_VOID(val))
         return NS_SUCCEEDED(nsVariant::SetToVoid(&mData));
-    if(JSVAL_IS_NULL(mJSVal))
+    if(JSVAL_IS_NULL(val))
         return NS_SUCCEEDED(nsVariant::SetToEmpty(&mData));
-    if(JSVAL_IS_STRING(mJSVal))
+    if(JSVAL_IS_STRING(val))
     {
         // Make our string immutable.  This will also ensure null-termination,
         // which nsVariant assumes for its PRUnichar* stuff.
-        JSString* str = JSVAL_TO_STRING(mJSVal);
+        JSString* str = JSVAL_TO_STRING(val);
         if(!JS_MakeStringImmutable(ccx, str))
             return JS_FALSE;
 
         // Don't use nsVariant::SetFromWStringWithSize, because that will copy
         // the data.  Just handle this ourselves.  Note that it's ok to not
         // copy because we added mJSVal as a GC root.
         NS_ASSERTION(mData.mType == nsIDataType::VTYPE_EMPTY,
                      "Why do we already have data?");
@@ -339,19 +347,19 @@ JSBool XPCVariant::InitializeData(XPCCal
         // PRUint32 is not valid on some platforms.
         mData.u.wstr.mWStringLength = (PRUint32)length;
         mData.mType = nsIDataType::VTYPE_WSTRING_SIZE_IS;
         
         return JS_TRUE;
     }
 
     // leaving only JSObject...
-    NS_ASSERTION(JSVAL_IS_OBJECT(mJSVal), "invalid type of jsval!");
+    NS_ASSERTION(JSVAL_IS_OBJECT(val), "invalid type of jsval!");
     
-    JSObject* jsobj = JSVAL_TO_OBJECT(mJSVal);
+    JSObject* jsobj = JSVAL_TO_OBJECT(val);
 
     // Let's see if it is a xpcJSID.
 
     const nsID* id = xpc_JSObjectToID(ccx, jsobj);
     if(id)
         return NS_SUCCEEDED(nsVariant::SetFromID(&mData, *id));
     
     // Let's see if it is a js array object.
@@ -369,17 +377,17 @@ JSBool XPCVariant::InitializeData(XPCCal
         
         nsXPTType type;
         nsID id;
 
         if(!XPCArrayHomogenizer::GetTypeForArray(ccx, jsobj, len, &type, &id))
             return JS_FALSE; 
 
         if(!XPCConvert::JSArray2Native(ccx, &mData.u.array.mArrayValue, 
-                                       mJSVal, len, len,
+                                       val, len, len,
                                        type, type.IsPointer(),
                                        &id, nsnull))
             return JS_FALSE;
 
         mData.mType = nsIDataType::VTYPE_ARRAY;
         if(type.IsInterfacePointer())
             mData.u.array.mArrayInterfaceID = id;
         mData.u.array.mArrayCount = len;
--- a/js/src/xpconnect/src/xpcwrappedjs.cpp
+++ b/js/src/xpconnect/src/xpcwrappedjs.cpp
@@ -76,17 +76,17 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrapp
     // nsXPCWrappedJS keeps its own refcount artificially at or above 1, see the
     // comment above nsXPCWrappedJS::AddRef.
     cb.NoteXPCOMChild(s);
 
     if(refcnt > 1)
         // nsXPCWrappedJS roots its mJSObj when its refcount is > 1, see
         // the comment above nsXPCWrappedJS::AddRef.
         cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT,
-                           tmp->GetJSObject());
+                           tmp->GetJSObjectPreserveColor());
 
     nsXPCWrappedJS* root = tmp->GetRootWrapper();
     if(root == tmp)
         // The root wrapper keeps the aggregated native object alive.
         cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject());
     else
         // Non-root wrappers keep their root alive.
         cb.NoteXPCOMChild(static_cast<nsIXPConnectWrappedJS*>(root));
@@ -257,17 +257,17 @@ do_decrement:
     return cnt;
 }
 
 void
 nsXPCWrappedJS::TraceJS(JSTracer* trc)
 {
     NS_ASSERTION(mRefCnt >= 2 && IsValid(), "must be strongly referenced");
     JS_SET_TRACING_DETAILS(trc, PrintTraceName, this, 0);
-    JS_CallTracer(trc, mJSObj, JSTRACE_OBJECT);
+    JS_CallTracer(trc, GetJSObjectPreserveColor(), JSTRACE_OBJECT);
 }
 
 #ifdef DEBUG
 // static
 void
 nsXPCWrappedJS::PrintTraceName(JSTracer* trc, char *buf, size_t bufsize)
 {
     const nsXPCWrappedJS* self = static_cast<const nsXPCWrappedJS*>
@@ -285,19 +285,19 @@ nsXPCWrappedJS::GetWeakReference(nsIWeak
 
     return nsSupportsWeakReference::GetWeakReference(aInstancePtr);
 }
 
 NS_IMETHODIMP
 nsXPCWrappedJS::GetJSObject(JSObject** aJSObj)
 {
     NS_PRECONDITION(aJSObj, "bad param");
-    NS_PRECONDITION(mJSObj, "bad wrapper");
+    NS_PRECONDITION(IsValid(), "bad wrapper");
 
-    if(!(*aJSObj = mJSObj))
+    if(!(*aJSObj = GetJSObject()))
         return NS_ERROR_OUT_OF_MEMORY;
     return NS_OK;
 }
 
 // static
 nsresult
 nsXPCWrappedJS::GetNewOrUsed(XPCCallContext& ccx,
                              JSObject* aJSObj,
@@ -622,17 +622,18 @@ nsXPCWrappedJS::SystemIsBeingShutDown(JS
 /* readonly attribute nsISimpleEnumerator enumerator; */
 NS_IMETHODIMP 
 nsXPCWrappedJS::GetEnumerator(nsISimpleEnumerator * *aEnumerate)
 {
     XPCCallContext ccx(NATIVE_CALLER);
     if(!ccx.IsValid())
         return NS_ERROR_UNEXPECTED;
 
-    return nsXPCWrappedJSClass::BuildPropertyEnumerator(ccx, mJSObj, aEnumerate);
+    return nsXPCWrappedJSClass::BuildPropertyEnumerator(ccx, GetJSObject(),
+                                                        aEnumerate);
 }
 
 /* nsIVariant getProperty (in AString name); */
 NS_IMETHODIMP 
 nsXPCWrappedJS::GetProperty(const nsAString & name, nsIVariant **_retval)
 {
     XPCCallContext ccx(NATIVE_CALLER);
     if(!ccx.IsValid())
@@ -641,17 +642,17 @@ nsXPCWrappedJS::GetProperty(const nsAStr
     nsStringBuffer* buf;
     jsval jsstr = XPCStringConvert::ReadableToJSVal(ccx, name, &buf);
     if(JSVAL_IS_NULL(jsstr))
         return NS_ERROR_OUT_OF_MEMORY;
     if(buf)
         buf->AddRef();
 
     return nsXPCWrappedJSClass::
-        GetNamedPropertyAsVariant(ccx, mJSObj, jsstr, _retval);
+        GetNamedPropertyAsVariant(ccx, GetJSObject(), jsstr, _retval);
 }
 
 /***************************************************************************/
 
 NS_IMETHODIMP
 nsXPCWrappedJS::DebugDump(PRInt16 depth)
 {
 #ifdef DEBUG
--- a/js/src/xpconnect/src/xpcwrappednative.cpp
+++ b/js/src/xpconnect/src/xpcwrappednative.cpp
@@ -117,20 +117,18 @@ NS_CYCLE_COLLECTION_CLASSNAME(XPCWrapped
         //
         // If our refcount is <= 1, our reference to the flat JS object is
         // considered "weak", and we're *not* going to traverse it.
         //
         // This reasoning is in line with the slightly confusing lifecycle rules
         // for XPCWrappedNatives, described in a larger comment below and also
         // on our wiki at http://wiki.mozilla.org/XPConnect_object_wrapping 
 
-        JSObject *obj = nsnull;
-        nsresult rv = tmp->GetJSObject(&obj);
-        if(NS_SUCCEEDED(rv))
-            cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, obj);
+        JSObject *obj = tmp->GetFlatJSObjectPreserveColor();
+        cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, obj);
     }
 
     XPCJSRuntime *rt = tmp->GetRuntime();
     TraverseExpandoObjectClosure closure = {
          rt->GetXPConnect()->GetCycleCollectionContext()->GetJSContext(),
          tmp,
          cb
     };
@@ -329,17 +327,17 @@ XPCWrappedNative::GetNewOrUsed(XPCCallCo
                                xpcObjectHelper& helper,
                                XPCWrappedNativeScope* Scope,
                                XPCNativeInterface* Interface,
                                JSBool isGlobal,
                                XPCWrappedNative** resultWrapper)
 {
     nsWrapperCache *cache = helper.GetWrapperCache();
 
-    NS_ASSERTION(!cache || !cache->GetWrapper(),
+    NS_ASSERTION(!cache || !cache->GetWrapperPreserveColor(),
                  "We assume the caller already checked if it could get the "
                  "wrapper from the cache.");
 
     nsresult rv;
 
     NS_ASSERTION(!Scope->GetRuntime()->GetThreadRunningGC(), 
                  "XPCWrappedNative::GetNewOrUsed called during GC");
 
@@ -392,17 +390,17 @@ XPCWrappedNative::GetNewOrUsed(XPCCallCo
                 return rv;
             }
             DEBUG_CheckWrapperThreadSafety(wrapper);
             *resultWrapper = wrapper;
             return NS_OK;
         }
     }
 #ifdef DEBUG
-    else if(!cache->GetWrapper())
+    else if(!cache->GetWrapperPreserveColor())
     {   // scoped lock
         XPCAutoLock lock(mapLock);
         NS_ASSERTION(!map->Find(identity),
                      "There's a wrapper in the hashtable but it wasn't cached?");
     }
 #endif
 
     // There is a chance that the object wants to have the self-same JSObject
@@ -670,23 +668,23 @@ FinishCreate(XPCCallContext& ccx,
 
     if(wrapperToKill)
     {
         // Second reference will be released by the FlatJSObject's finializer.
         wrapperToKill->Release();
     }
     else if(wrapper)
     {
-        JSObject *flat = wrapper->GetFlatJSObjectAndMark();
-        NS_ASSERTION(!cache || !cache->GetWrapper() ||
-                     flat == cache->GetWrapper(),
+        JSObject *flat = wrapper->GetFlatJSObject();
+        NS_ASSERTION(!cache || !cache->GetWrapperPreserveColor() ||
+                     flat == cache->GetWrapperPreserveColor(),
                      "This object has a cached wrapper that's different from "
                      "the JSObject held by its native wrapper?");
 
-        if(cache && !cache->GetWrapper())
+        if(cache && !cache->GetWrapperPreserveColor())
             cache->SetWrapper(flat);
 
         // Our newly created wrapper is the one that we just added to the table.
         // All is well. Call PostCreate as necessary.
         XPCNativeScriptableInfo* si = wrapper->GetScriptableInfo();
         if(si && si->GetFlags().WantPostCreate())
         {
             nsresult rv = si->GetCallback()->PostCreate(wrapper, ccx, flat);
@@ -1506,17 +1504,17 @@ XPCWrappedNative::ReparentWrapperIfFound
     }
     else
     {
         rv = XPCWrappedNative::GetUsedOnly(ccx, aCOMObj, aOldScope, iface,
                                            getter_AddRefs(wrapper));
         if(NS_FAILED(rv))
             return rv;
 
-        flat = wrapper->GetFlatJSObjectAndMark();
+        flat = wrapper->GetFlatJSObject();
     }
 
     if(!flat)
     {
         *aWrapper = nsnull;
         return NS_OK;
     }
 
@@ -3101,17 +3099,17 @@ CallMethodHelper::Invoke()
 }
 
 /***************************************************************************/
 // interface methods
 
 /* readonly attribute JSObjectPtr JSObject; */
 NS_IMETHODIMP XPCWrappedNative::GetJSObject(JSObject * *aJSObject)
 {
-    *aJSObject = GetFlatJSObjectAndMark();
+    *aJSObject = GetFlatJSObject();
     return NS_OK;
 }
 
 /* readonly attribute nsISupports Native; */
 NS_IMETHODIMP XPCWrappedNative::GetNative(nsISupports * *aNative)
 {
     // No need to QI here, we already have the correct nsISupports
     // vtable.
@@ -3119,17 +3117,17 @@ NS_IMETHODIMP XPCWrappedNative::GetNativ
     NS_ADDREF(*aNative);
     return NS_OK;
 }
 
 /* reaonly attribute JSObjectPtr JSObjectPrototype; */
 NS_IMETHODIMP XPCWrappedNative::GetJSObjectPrototype(JSObject * *aJSObjectPrototype)
 {
     *aJSObjectPrototype = HasProto() ?
-                GetProto()->GetJSProtoObject() : GetFlatJSObjectAndMark();
+                GetProto()->GetJSProtoObject() : GetFlatJSObject();
     return NS_OK;
 }
 
 #ifndef XPCONNECT_STANDALONE
 nsIPrincipal*
 XPCWrappedNative::GetObjectPrincipal() const
 {
     nsIPrincipal* principal = GetScope()->GetPrincipal();
@@ -3203,17 +3201,17 @@ NS_IMETHODIMP XPCWrappedNative::RefreshP
 
     if(!HasProto())
         return NS_OK;
 
     if(!mFlatJSObject)
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     JSAutoEnterCompartment ac;
-    if(!ac.enter(ccx, GetFlatJSObjectAndMark()))
+    if(!ac.enter(ccx, GetFlatJSObject()))
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     AutoMarkingWrappedNativeProtoPtr oldProto(ccx);
     AutoMarkingWrappedNativeProtoPtr newProto(ccx);
 
     oldProto = GetProto();
 
     XPCNativeScriptableInfo *info = oldProto->GetScriptableInfo();
@@ -3227,17 +3225,17 @@ NS_IMETHODIMP XPCWrappedNative::RefreshP
     if(!newProto)
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     // If nothing needs to change then we're done.
 
     if(newProto.get() == oldProto.get())
         return NS_OK;
 
-    if(!JS_SetPrototype(ccx, GetFlatJSObjectAndMark(),
+    if(!JS_SetPrototype(ccx, GetFlatJSObject(),
                         newProto->GetJSProtoObject()))
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     SetProto(newProto);
 
     if(mScriptableInfo == oldProto->GetScriptableInfo())
         mScriptableInfo = newProto->GetScriptableInfo();
 
--- a/js/src/xpconnect/src/xpcwrappednativescope.cpp
+++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp
@@ -358,17 +358,17 @@ XPCWrappedNativeScope::GetPrototypeNoHel
 static JSDHashOperator
 WrappedNativeJSGCThingTracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
                              uint32 number, void *arg)
 {
     XPCWrappedNative* wrapper = ((Native2WrappedNativeMap::Entry*)hdr)->value;
     if(wrapper->HasExternalReference() && !wrapper->IsWrapperExpired())
     {
         JSTracer* trc = (JSTracer *)arg;
-        JS_CALL_OBJECT_TRACER(trc, wrapper->GetFlatJSObjectNoMark(),
+        JS_CALL_OBJECT_TRACER(trc, wrapper->GetFlatJSObjectPreserveColor(),
                               "XPCWrappedNative::mFlatJSObject");
     }
 
     return JS_DHASH_NEXT;
 }
 
 // static
 void
--- a/modules/plugin/base/src/nsJSNPRuntime.cpp
+++ b/modules/plugin/base/src/nsJSNPRuntime.cpp
@@ -51,16 +51,17 @@
 #include "nsDOMJSUtils.h"
 #include "nsIDocument.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIJSContextStack.h"
 #include "nsIXPConnect.h"
 #include "nsIDOMElement.h"
 #include "prmem.h"
 #include "nsIContent.h"
+#include "xpcpublic.h"
 
 using namespace mozilla::plugins::parent;
 
 #ifdef MOZ_IPC
 #include "mozilla/plugins/PluginScriptableObjectParent.h"
 using mozilla::plugins::PluginScriptableObjectParent;
 using mozilla::plugins::ParentNPObject;
 #endif