Backed out changesets 6235d23be128, 0fbd934a80c8, and 592b49270490 (bug 770535) for Windows build bustage on a CLOSED TREE.
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 25 Nov 2013 15:16:42 -0500
changeset 157788 0fe924afeb74a59dbe450689a98e50ac92a1ad36
parent 157787 7cda5e479b7c843b7d65106e65df292066246fc9
child 157789 4fa706cb38bb4fe05a82ee84efe9b3e889e9afa1
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
bugs770535
milestone28.0a1
backs out6235d23be12869205d4d42784e672462278396f1
0fbd934a80c806ce83d3e46a9d4f4f85c954c2b3
592b49270490075650221c01983fbbb24dd0a0a2
Backed out changesets 6235d23be128, 0fbd934a80c8, and 592b49270490 (bug 770535) for Windows build bustage on a CLOSED TREE.
js/xpconnect/src/XPCInlines.h
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCVariant.cpp
js/xpconnect/src/XPCWrappedJS.cpp
js/xpconnect/src/XPCWrappedJSClass.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/XPCWrappedNativeInfo.cpp
js/xpconnect/src/XPCWrappedNativeJSOps.cpp
js/xpconnect/src/XPCWrappedNativeProto.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
--- a/js/xpconnect/src/XPCInlines.h
+++ b/js/xpconnect/src/XPCInlines.h
@@ -12,29 +12,29 @@
 
 #include <algorithm>
 
 /***************************************************************************/
 
 inline void
 XPCJSRuntime::AddVariantRoot(XPCTraceableVariant* variant)
 {
-    variant->AddToRootSet(&mVariantRoots);
+    variant->AddToRootSet(GetMapLock(), &mVariantRoots);
 }
 
 inline void
 XPCJSRuntime::AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS)
 {
-    wrappedJS->AddToRootSet(&mWrappedJSRoots);
+    wrappedJS->AddToRootSet(GetMapLock(), &mWrappedJSRoots);
 }
 
 inline void
 XPCJSRuntime::AddObjectHolderRoot(XPCJSObjectHolder* holder)
 {
-    holder->AddToRootSet(&mObjectHolderRoots);
+    holder->AddToRootSet(GetMapLock(), &mObjectHolderRoots);
 }
 
 /***************************************************************************/
 
 inline bool
 XPCCallContext::IsValid() const
 {
     return mState != INIT_FAILED;
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -624,28 +624,34 @@ void XPCJSRuntime::TraceNativeBlackRoots
     // Skip this part if XPConnect is shutting down. We get into
     // bad locking problems with the thread iteration otherwise.
     if (!nsXPConnect::XPConnect()->IsShuttingDown()) {
         // Trace those AutoMarkingPtr lists!
         if (AutoMarkingPtr *roots = Get()->mAutoRoots)
             roots->TraceJSAll(trc);
     }
 
-    // XPCJSObjectHolders don't participate in cycle collection, so always
-    // trace them here.
-    XPCRootSetElem *e;
-    for (e = mObjectHolderRoots; e; e = e->GetNextRoot())
-        static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc);
+    {
+        XPCAutoLock lock(mMapLock);
+
+        // XPCJSObjectHolders don't participate in cycle collection, so always
+        // trace them here.
+        XPCRootSetElem *e;
+        for (e = mObjectHolderRoots; e; e = e->GetNextRoot())
+            static_cast<XPCJSObjectHolder*>(e)->TraceJS(trc);
+    }
 
     dom::TraceBlackJS(trc, JS_GetGCParameter(Runtime(), JSGC_NUMBER),
                       nsXPConnect::XPConnect()->IsShuttingDown());
 }
 
 void XPCJSRuntime::TraceAdditionalNativeGrayRoots(JSTracer *trc)
 {
+    XPCAutoLock lock(mMapLock);
+
     XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(trc, this);
 
     for (XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot())
         static_cast<XPCTraceableVariant*>(e)->TraceJS(trc);
 
     for (XPCRootSetElem *e = mWrappedJSRoots; e ; e = e->GetNextRoot())
         static_cast<nsXPCWrappedJS*>(e)->TraceJS(trc);
 }
@@ -695,16 +701,18 @@ CanSkipWrappedJS(nsXPCWrappedJS *wrapped
         }
     }
     return false;
 }
 
 void
 XPCJSRuntime::TraverseAdditionalNativeRoots(nsCycleCollectionNoteRootCallback &cb)
 {
+    XPCAutoLock lock(mMapLock);
+
     XPCWrappedNativeScope::SuspectAllWrappers(this, cb);
 
     for (XPCRootSetElem *e = mVariantRoots; e ; e = e->GetNextRoot()) {
         XPCTraceableVariant* v = static_cast<XPCTraceableVariant*>(e);
         if (nsCCUncollectableMarker::InGeneration(cb,
                                                   v->CCGeneration())) {
            jsval val = v->GetJSValPreserveColor();
            if (val.isObject() && !xpc_IsGrayGCThing(&val.toObject()))
@@ -722,16 +730,17 @@ XPCJSRuntime::TraverseAdditionalNativeRo
 
         cb.NoteXPCOMRoot(static_cast<nsIXPConnectWrappedJS *>(wrappedJS));
     }
 }
 
 void
 XPCJSRuntime::UnmarkSkippableJSHolders()
 {
+    XPCAutoLock lock(mMapLock);
     CycleCollectedJSRuntime::UnmarkSkippableJSHolders();
 }
 
 void
 XPCJSRuntime::PrepareForForgetSkippable()
 {
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
@@ -825,18 +834,22 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp 
     if (!self)
         return;
 
     switch (status) {
         case JSFINALIZE_GROUP_START:
         {
             MOZ_ASSERT(!self->mDoingFinalization, "bad state");
 
-            MOZ_ASSERT(!self->mGCIsRunning, "bad state");
-            self->mGCIsRunning = true;
+            // mThreadRunningGC indicates that GC is running
+            { // scoped lock
+                XPCAutoLock lock(self->GetMapLock());
+                MOZ_ASSERT(!self->mThreadRunningGC, "bad state");
+                self->mThreadRunningGC = PR_GetCurrentThread();
+            }
 
             nsTArray<nsXPCWrappedJS*>* dyingWrappedJSArray =
                 &self->mWrappedJSToReleaseArray;
 
             // Add any wrappers whose JSObjects are to be finalized to
             // this array. Note that we do not want to be changing the
             // refcount of these wrappers.
             // We add them to the array now and Release the array members
@@ -857,25 +870,35 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp 
 
             // Release all the members whose JSObjects are now known
             // to be dead.
             DoDeferredRelease(self->mWrappedJSToReleaseArray);
 
             // Sweep scopes needing cleanup
             XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC();
 
-            MOZ_ASSERT(self->mGCIsRunning, "bad state");
-            self->mGCIsRunning = false;
+            // mThreadRunningGC indicates that GC is running.
+            // Clear it and notify waiters.
+            { // scoped lock
+                XPCAutoLock lock(self->GetMapLock());
+                MOZ_ASSERT(self->mThreadRunningGC == PR_GetCurrentThread(), "bad state");
+                self->mThreadRunningGC = nullptr;
+                xpc_NotifyAll(self->GetMapLock());
+            }
 
             break;
         }
         case JSFINALIZE_COLLECTION_END:
         {
-            MOZ_ASSERT(!self->mGCIsRunning, "bad state");
-            self->mGCIsRunning = true;
+            // mThreadRunningGC indicates that GC is running
+            { // scoped lock
+                XPCAutoLock lock(self->GetMapLock());
+                MOZ_ASSERT(!self->mThreadRunningGC, "bad state");
+                self->mThreadRunningGC = PR_GetCurrentThread();
+            }
 
             // We use this occasion to mark and sweep NativeInterfaces,
             // NativeSets, and the WrappedNativeJSClasses...
 
             // Do the marking...
             XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos();
 
             self->mDetachedWrappedNativeProtoMap->
@@ -1002,18 +1025,24 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp 
             // slated for finalization in this gc cycle. So... at this point
             // we know that any and all wrappers that might have been
             // referencing the protos in the dying list are themselves dead.
             // So, we can safely delete all the protos in the list.
 
             self->mDyingWrappedNativeProtoMap->
                 Enumerate(DyingProtoKiller, nullptr);
 
-            MOZ_ASSERT(self->mGCIsRunning, "bad state");
-            self->mGCIsRunning = false;
+            // mThreadRunningGC indicates that GC is running.
+            // Clear it and notify waiters.
+            { // scoped lock
+                XPCAutoLock lock(self->GetMapLock());
+                MOZ_ASSERT(self->mThreadRunningGC == PR_GetCurrentThread(), "bad state");
+                self->mThreadRunningGC = nullptr;
+                xpc_NotifyAll(self->GetMapLock());
+            }
 
             break;
         }
     }
 }
 
 static void WatchdogMain(void *arg);
 class Watchdog;
@@ -1556,16 +1585,19 @@ XPCJSRuntime::~XPCJSRuntime()
         delete mIID2NativeInterfaceMap;
 
     if (mClassInfo2NativeSetMap)
         delete mClassInfo2NativeSetMap;
 
     if (mNativeSetMap)
         delete mNativeSetMap;
 
+    if (mMapLock)
+        XPCAutoLock::DestroyLock(mMapLock);
+
     if (mThisTranslatorMap)
         delete mThisTranslatorMap;
 
     if (mNativeScriptableSharedMap)
         delete mNativeScriptableSharedMap;
 
     if (mDyingWrappedNativeProtoMap)
         delete mDyingWrappedNativeProtoMap;
@@ -2919,17 +2951,18 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
    mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
    mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)),
    mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
    mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
    mThisTranslatorMap(IID2ThisTranslatorMap::newMap(XPC_THIS_TRANSLATOR_MAP_SIZE)),
    mNativeScriptableSharedMap(XPCNativeScriptableSharedMap::newMap(XPC_NATIVE_JSCLASS_MAP_SIZE)),
    mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_SIZE)),
    mDetachedWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DETACHED_NATIVE_PROTO_MAP_SIZE)),
-   mGCIsRunning(false),
+   mMapLock(XPCAutoLock::NewLock("XPCJSRuntime::mMapLock")),
+   mThreadRunningGC(nullptr),
    mWrappedJSToReleaseArray(),
    mNativesToReleaseArray(),
    mDoingFinalization(false),
    mVariantRoots(nullptr),
    mWrappedJSRoots(nullptr),
    mObjectHolderRoots(nullptr),
    mWatchdogManager(new WatchdogManager(this)),
    mJunkScope(nullptr),
@@ -3107,16 +3140,17 @@ XPCJSRuntime::newXPCJSRuntime(nsXPConnec
         self->GetWrappedJSMap()                 &&
         self->GetWrappedJSClassMap()            &&
         self->GetIID2NativeInterfaceMap()       &&
         self->GetClassInfo2NativeSetMap()       &&
         self->GetNativeSetMap()                 &&
         self->GetThisTranslatorMap()            &&
         self->GetNativeScriptableSharedMap()    &&
         self->GetDyingWrappedNativeProtoMap()   &&
+        self->GetMapLock()                      &&
         self->mWatchdogManager) {
         return self;
     }
 
     NS_RUNTIMEABORT("new XPCJSRuntime failed to initialize.");
 
     delete self;
     return nullptr;
@@ -3222,16 +3256,17 @@ NativeSetDumpEnumerator(PLDHashTable *ta
 void
 XPCJSRuntime::DebugDump(int16_t depth)
 {
 #ifdef DEBUG
     depth--;
     XPC_LOG_ALWAYS(("XPCJSRuntime @ %x", this));
         XPC_LOG_INDENT();
         XPC_LOG_ALWAYS(("mJSRuntime @ %x", Runtime()));
+        XPC_LOG_ALWAYS(("mMapLock @ %x", mMapLock));
 
         XPC_LOG_ALWAYS(("mWrappedJSToReleaseArray @ %x with %d wrappers(s)", \
                         &mWrappedJSToReleaseArray,
                         mWrappedJSToReleaseArray.Length()));
 
         int cxCount = 0;
         JSContext* iter = nullptr;
         while (JS_ContextIterator(Runtime(), &iter))
@@ -3290,37 +3325,41 @@ XPCJSRuntime::DebugDump(int16_t depth)
 
         XPC_LOG_OUTDENT();
 #endif
 }
 
 /***************************************************************************/
 
 void
-XPCRootSetElem::AddToRootSet(XPCRootSetElem **listHead)
+XPCRootSetElem::AddToRootSet(XPCLock *lock, XPCRootSetElem **listHead)
 {
     MOZ_ASSERT(!mSelfp, "Must be not linked");
 
+    XPCAutoLock autoLock(lock);
+
     mSelfp = listHead;
     mNext = *listHead;
     if (mNext) {
         MOZ_ASSERT(mNext->mSelfp == listHead, "Must be list start");
         mNext->mSelfp = &mNext;
     }
     *listHead = this;
 }
 
 void
-XPCRootSetElem::RemoveFromRootSet()
+XPCRootSetElem::RemoveFromRootSet(XPCLock *lock)
 {
     nsXPConnect *xpc = nsXPConnect::XPConnect();
     JS::PokeGC(xpc->GetRuntime()->Runtime());
 
     MOZ_ASSERT(mSelfp, "Must be linked");
 
+    XPCAutoLock autoLock(lock);
+
     MOZ_ASSERT(*mSelfp == this, "Link invariant");
     *mSelfp = mNext;
     if (mNext)
         mNext->mSelfp = mSelfp;
 #ifdef DEBUG
     mSelfp = nullptr;
     mNext = nullptr;
 #endif
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -58,17 +58,17 @@ XPCTraceableVariant::~XPCTraceableVarian
     MOZ_ASSERT(JSVAL_IS_GCTHING(val), "Must be traceable or unlinked");
 
     // 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(val))
-        RemoveFromRootSet();
+        RemoveFromRootSet(nsXPConnect::GetRuntimeInstance()->GetMapLock());
 }
 
 void XPCTraceableVariant::TraceJS(JSTracer* trc)
 {
     MOZ_ASSERT(JSVAL_IS_TRACEABLE(mJSVal));
     JS_SET_TRACING_DETAILS(trc, GetTraceName, this, 0);
     JS_CallHeapValueTracer(trc, &mJSVal, "XPCTraceableVariant::mJSVal");
 }
@@ -98,17 +98,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XP
     // We're sharing val's buffer, clear the pointer to it so Cleanup() won't
     // try to delete it
     if (val.isString())
         tmp->mData.u.wstr.mWStringValue = nullptr;
     nsVariant::Cleanup(&tmp->mData);
 
     if (val.isMarkable()) {
         XPCTraceableVariant *v = static_cast<XPCTraceableVariant*>(tmp);
-        v->RemoveFromRootSet();
+        v->RemoveFromRootSet(nsXPConnect::GetRuntimeInstance()->GetMapLock());
     }
     tmp->mJSVal = JS::NullValue();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 // static
 XPCVariant* XPCVariant::newVariant(JSContext* cx, jsval aJSVal)
 {
     XPCVariant* variant;
--- a/js/xpconnect/src/XPCWrappedJS.cpp
+++ b/js/xpconnect/src/XPCWrappedJS.cpp
@@ -170,28 +170,33 @@ nsXPCWrappedJS::AddRef(void)
 
 nsrefcnt
 nsXPCWrappedJS::Release(void)
 {
     if (!MOZ_LIKELY(NS_IsMainThread()))
         MOZ_CRASH();
     NS_PRECONDITION(0 != mRefCnt, "dup release");
 
+    // need to take the map lock here to prevent GetNewOrUsed from trying
+    // to reuse a wrapper on one thread while it's being destroyed on another
+    XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
+    XPCAutoLock lock(rt->GetMapLock());
+
 do_decrement:
 
     nsrefcnt cnt = --mRefCnt;
     NS_LOG_RELEASE(this, cnt, "nsXPCWrappedJS");
 
     if (0 == cnt) {
         delete this;   // also unlinks us from chain
         return 0;
     }
     if (1 == cnt) {
         if (IsValid())
-            RemoveFromRootSet();
+            RemoveFromRootSet(rt->GetMapLock());
 
         // If we are not the root wrapper or if we are not being used from a
         // weak reference, then this extra ref is not needed and we can let
         // ourself be deleted.
         // Note: HasWeakReferences() could only return true for the root.
         if (!HasWeakReferences())
             goto do_decrement;
     }
@@ -264,35 +269,44 @@ nsXPCWrappedJS::GetNewOrUsed(JS::HandleO
         return NS_ERROR_FAILURE;
     // from here on we need to return through 'return_wrapper'
 
     // always find the root JSObject
     JS::RootedObject rootJSObj(cx, clazz->GetRootJSObject(cx, jsObj));
     if (!rootJSObj)
         goto return_wrapper;
 
-    root = map->Find(rootJSObj);
-    if (root) {
-        if ((nullptr != (wrapper = root->Find(aIID))) ||
-            (nullptr != (wrapper = root->FindInherited(aIID)))) {
-            NS_ADDREF(wrapper);
-            goto return_wrapper;
+    // look for the root wrapper, and if found, hold the map lock until
+    // we've added our ref to prevent another thread from destroying it
+    // under us
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+        root = map->Find(rootJSObj);
+        if (root) {
+            if ((nullptr != (wrapper = root->Find(aIID))) ||
+                (nullptr != (wrapper = root->FindInherited(aIID)))) {
+                NS_ADDREF(wrapper);
+                goto return_wrapper;
+            }
         }
     }
 
     if (!root) {
         // build the root wrapper
         if (rootJSObj == jsObj) {
             // the root will do double duty as the interface wrapper
             wrapper = root = new nsXPCWrappedJS(cx, jsObj, clazz, nullptr,
                                                 aOuter);
             if (!root)
                 goto return_wrapper;
 
-            map->Add(cx, root);
+            {   // scoped lock
+                XPCAutoLock lock(rt->GetMapLock());
+                map->Add(cx, root);
+            }
 
             goto return_wrapper;
         } else {
             // just a root wrapper
             nsXPCWrappedJSClass* rootClazz = nullptr;
             nsXPCWrappedJSClass::GetNewOrUsed(cx, NS_GET_IID(nsISupports),
                                               &rootClazz);
             if (!rootClazz)
@@ -301,17 +315,21 @@ nsXPCWrappedJS::GetNewOrUsed(JS::HandleO
             root = new nsXPCWrappedJS(cx, rootJSObj, rootClazz, nullptr, aOuter);
             NS_RELEASE(rootClazz);
 
             if (!root)
                 goto return_wrapper;
 
             release_root = true;
 
-            map->Add(cx, root);
+            {   // scoped lock
+                XPCAutoLock lock(rt->GetMapLock());
+                map->Add(cx, root);
+            }
+
         }
     }
 
     // at this point we have a root and may need to build the specific wrapper
     MOZ_ASSERT(root,"bad root");
     MOZ_ASSERT(clazz,"bad clazz");
 
     if (!wrapper) {
@@ -364,37 +382,41 @@ nsXPCWrappedJS::nsXPCWrappedJS(JSContext
 nsXPCWrappedJS::~nsXPCWrappedJS()
 {
     NS_PRECONDITION(0 == mRefCnt, "refcounting error");
 
     if (mRoot == this) {
         // Remove this root wrapper from the map
         XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
         JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
-        if (map)
+        if (map) {
+            XPCAutoLock lock(rt->GetMapLock());
             map->Remove(this);
+        }
     }
     Unlink();
 }
 
 void
 nsXPCWrappedJS::Unlink()
 {
     if (IsValid()) {
         XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
         if (rt) {
             if (mRoot == this) {
                 // remove this root wrapper from the map
                 JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
-                if (map)
+                if (map) {
+                    XPCAutoLock lock(rt->GetMapLock());
                     map->Remove(this);
+                }
             }
 
             if (mRefCnt > 1)
-                RemoveFromRootSet();
+                RemoveFromRootSet(rt->GetMapLock());
         }
 
         mJSObj = nullptr;
     }
 
     if (mRoot == this) {
         ClearWeakReferences();
     } else if (mRoot) {
@@ -410,17 +432,17 @@ nsXPCWrappedJS::Unlink()
         }
         // let the root go
         NS_RELEASE(mRoot);
     }
 
     NS_IF_RELEASE(mClass);
     if (mOuter) {
         XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
-        if (rt->GCIsRunning()) {
+        if (rt->GetThreadRunningGC()) {
             nsContentUtils::DeferredFinalize(mOuter);
             mOuter = nullptr;
         } else {
             NS_RELEASE(mOuter);
         }
     }
 }
 
--- a/js/xpconnect/src/XPCWrappedJSClass.cpp
+++ b/js/xpconnect/src/XPCWrappedJSClass.cpp
@@ -100,19 +100,22 @@ bool xpc_IsReportableErrorCode(nsresult 
 // static
 nsresult
 nsXPCWrappedJSClass::GetNewOrUsed(JSContext* cx, REFNSIID aIID,
                                   nsXPCWrappedJSClass** resultClazz)
 {
     nsXPCWrappedJSClass* clazz = nullptr;
     XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
 
-    IID2WrappedJSClassMap* map = rt->GetWrappedJSClassMap();
-    clazz = map->Find(aIID);
-    NS_IF_ADDREF(clazz);
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+        IID2WrappedJSClassMap* map = rt->GetWrappedJSClassMap();
+        clazz = map->Find(aIID);
+        NS_IF_ADDREF(clazz);
+    }
 
     if (!clazz) {
         nsCOMPtr<nsIInterfaceInfo> info;
         nsXPConnect::XPConnect()->GetInfoForIID(&aIID, getter_AddRefs(info));
         if (info) {
             bool canScript, isBuiltin;
             if (NS_SUCCEEDED(info->IsScriptable(&canScript)) && canScript &&
                 NS_SUCCEEDED(info->IsBuiltinClass(&isBuiltin)) && !isBuiltin &&
@@ -133,17 +136,20 @@ nsXPCWrappedJSClass::nsXPCWrappedJSClass
       mInfo(aInfo),
       mName(nullptr),
       mIID(aIID),
       mDescriptors(nullptr)
 {
     NS_ADDREF(mInfo);
     NS_ADDREF_THIS();
 
-    mRuntime->GetWrappedJSClassMap()->Add(this);
+    {   // scoped lock
+        XPCAutoLock lock(mRuntime->GetMapLock());
+        mRuntime->GetWrappedJSClassMap()->Add(this);
+    }
 
     uint16_t methodCount;
     if (NS_SUCCEEDED(mInfo->GetMethodCount(&methodCount))) {
         if (methodCount) {
             int wordCount = (methodCount/32)+1;
             if (nullptr != (mDescriptors = new uint32_t[wordCount])) {
                 int i;
                 // init flags to 0;
@@ -167,18 +173,20 @@ nsXPCWrappedJSClass::nsXPCWrappedJSClass
     }
 }
 
 nsXPCWrappedJSClass::~nsXPCWrappedJSClass()
 {
     if (mDescriptors && mDescriptors != &zero_methods_descriptor)
         delete [] mDescriptors;
     if (mRuntime)
+    {   // scoped lock
+        XPCAutoLock lock(mRuntime->GetMapLock());
         mRuntime->GetWrappedJSClassMap()->Remove(this);
-
+    }
     if (mName)
         nsMemory::Free(mName);
     NS_IF_RELEASE(mInfo);
 }
 
 JSObject*
 nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(JSContext* cx,
                                                   JSObject* jsobjArg,
@@ -1202,17 +1210,20 @@ nsXPCWrappedJSClass::CallMethod(nsXPCWra
                     const nsXPTType& firstType = firstParam.GetType();
 
                     if (firstType.IsInterfacePointer()) {
                         nsIXPCFunctionThisTranslator* translator;
 
                         IID2ThisTranslatorMap* map =
                             mRuntime->GetThisTranslatorMap();
 
-                        translator = map->Find(mIID);
+                        {
+                            XPCAutoLock lock(mRuntime->GetMapLock()); // scoped lock
+                            translator = map->Find(mIID);
+                        }
 
                         if (translator) {
                             nsCOMPtr<nsISupports> newThis;
                             if (NS_FAILED(translator->
                                           TranslateThis((nsISupports*)nativeParams[0].val.p,
                                                         getter_AddRefs(newThis)))) {
                                 goto pre_call_clean_up;
                             }
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -287,33 +287,38 @@ XPCWrappedNative::GetNewOrUsed(xpcObject
     nsWrapperCache *cache = helper.GetWrapperCache();
 
     MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor(),
                "We assume the caller already checked if it could get the "
                "wrapper from the cache.");
 
     nsresult rv;
 
-    MOZ_ASSERT(!Scope->GetRuntime()->GCIsRunning(),
+    MOZ_ASSERT(!Scope->GetRuntime()->GetThreadRunningGC(),
                "XPCWrappedNative::GetNewOrUsed called during GC");
 
     nsISupports *identity = helper.GetCanonical();
 
     if (!identity) {
         NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
         return NS_ERROR_FAILURE;
     }
 
+    XPCLock* mapLock = Scope->GetRuntime()->GetMapLock();
+
     nsRefPtr<XPCWrappedNative> wrapper;
 
     Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
     // Some things are nsWrapperCache subclasses but never use the cache, so go
     // ahead and check our map even if we have a cache and it has no existing
     // wrapper: we might have an XPCWrappedNative anyway.
-    wrapper = map->Find(identity);
+    {   // scoped lock
+        XPCAutoLock lock(mapLock);
+        wrapper = map->Find(identity);
+    }
 
     if (wrapper) {
         if (!wrapper->FindTearOff(Interface, false, &rv)) {
             MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
             return rv;
         }
         *resultWrapper = wrapper.forget().get();
         return NS_OK;
@@ -394,16 +399,18 @@ XPCWrappedNative::GetNewOrUsed(xpcObject
         // the preCreate call caused the wrapper to get created through some
         // interesting path (the DOM code tends to make this happen sometimes).
 
         if (cache) {
             RootedObject cached(cx, cache->GetWrapper());
             if (cached)
                 wrapper = XPCWrappedNative::Get(cached);
         } else {
+            // scoped lock
+            XPCAutoLock lock(mapLock);
             wrapper = map->Find(identity);
         }
 
         if (wrapper) {
             if (wrapper->FindTearOff(Interface, false, &rv)) {
                 MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
                 return rv;
             }
@@ -482,26 +489,31 @@ FinishCreate(XPCWrappedNativeScope* Scop
              XPCNativeInterface* Interface,
              nsWrapperCache *cache,
              XPCWrappedNative* inWrapper,
              XPCWrappedNative** resultWrapper)
 {
     AutoJSContext cx;
     MOZ_ASSERT(inWrapper);
 
+    XPCLock* mapLock = Scope->GetRuntime()->GetMapLock();
     Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
 
     nsRefPtr<XPCWrappedNative> wrapper;
-    // Deal with the case where the wrapper got created as a side effect
-    // of one of our calls out of this code. Add() returns the (possibly
-    // pre-existing) wrapper that ultimately ends up in the map, which is
-    // what we want.
-    wrapper = map->Add(inWrapper);
-    if (!wrapper)
-        return NS_ERROR_FAILURE;
+    {   // scoped lock
+
+        // Deal with the case where the wrapper got created as a side effect
+        // of one of our calls out of this code (or on another thread). Add()
+        // returns the (possibly pre-existing) wrapper that ultimately ends up
+        // in the map, which is what we want.
+        XPCAutoLock lock(mapLock);
+        wrapper = map->Add(inWrapper);
+        if (!wrapper)
+            return NS_ERROR_FAILURE;
+    }
 
     if (wrapper == inWrapper) {
         JSObject *flat = wrapper->GetFlatJSObject();
         MOZ_ASSERT(!cache || !cache->GetWrapperPreserveColor() ||
                    flat == cache->GetWrapperPreserveColor(),
                    "This object has a cached wrapper that's different from "
                    "the JSObject held by its native wrapper?");
 
@@ -522,17 +534,20 @@ FinishCreate(XPCWrappedNativeScope* Scop
                 // XPConnect and tried to wrap the same object. In that case
                 // *we* hand out the invalid wrapper since it is already in our
                 // map :(
                 NS_ERROR("PostCreate failed! This is known to cause "
                          "inconsistent state for some class types and may even "
                          "cause a crash in combination with a JS GC. Fix the "
                          "failing PostCreate ASAP!");
 
-                map->Remove(wrapper);
+                {   // scoped lock
+                    XPCAutoLock lock(mapLock);
+                    map->Remove(wrapper);
+                }
 
                 // This would be a good place to tell the wrapper not to remove
                 // itself from the map when it dies... See bug 429442.
 
                 if (cache)
                     cache->ClearWrapper();
                 wrapper->Release();
                 return rv;
@@ -571,20 +586,23 @@ XPCWrappedNative::GetUsedOnly(nsISupport
 
         if (!identity) {
             NS_ERROR("This XPCOM object fails in QueryInterface to nsISupports!");
             return NS_ERROR_FAILURE;
         }
 
         Native2WrappedNativeMap* map = Scope->GetWrappedNativeMap();
 
-        wrapper = map->Find(identity);
-        if (!wrapper) {
-            *resultWrapper = nullptr;
-            return NS_OK;
+        {   // scoped lock
+            XPCAutoLock lock(Scope->GetRuntime()->GetMapLock());
+            wrapper = map->Find(identity);
+            if (!wrapper) {
+                *resultWrapper = nullptr;
+                return NS_OK;
+            }
         }
     }
 
     nsresult rv;
     if (!wrapper->FindTearOff(Interface, false, &rv)) {
         MOZ_ASSERT(NS_FAILED(rv), "returning NS_OK on failure");
         return rv;
     }
@@ -639,16 +657,19 @@ XPCWrappedNative::Destroy()
         delete mScriptableInfo;
         mScriptableInfo = nullptr;
     }
 
     XPCWrappedNativeScope *scope = GetScope();
     if (scope) {
         Native2WrappedNativeMap* map = scope->GetWrappedNativeMap();
 
+        // scoped lock
+        XPCAutoLock lock(GetRuntime()->GetMapLock());
+
         // Post-1.9 we should not remove this wrapper from the map if it is
         // uninitialized.
         map->Remove(this);
     }
 
     if (mIdentity) {
         XPCJSRuntime* rt = GetRuntime();
         if (rt && rt->GetDoingFinalization()) {
@@ -1228,50 +1249,53 @@ XPCWrappedNative::ReparentWrapperIfFound
         {
             JSAutoCompartment innerAC(cx, aOldScope->GetGlobalJSObject());
             if (!wrapper->GetSameCompartmentSecurityWrapper(cx))
                 return NS_ERROR_FAILURE;
         }
 
         // Update scope maps. This section modifies global state, so from
         // here on out we crash if anything fails.
-        Native2WrappedNativeMap* oldMap = aOldScope->GetWrappedNativeMap();
-        Native2WrappedNativeMap* newMap = aNewScope->GetWrappedNativeMap();
-
-        oldMap->Remove(wrapper);
-
-        if (wrapper->HasProto())
-            wrapper->SetProto(newProto);
-
-        // If the wrapper has no scriptable or it has a non-shared
-        // scriptable, then we don't need to mess with it.
-        // Otherwise...
-
-        if (wrapper->mScriptableInfo &&
-            wrapper->mScriptableInfo == oldProto->GetScriptableInfo()) {
-            // The new proto had better have the same JSClass stuff as
-            // the old one! We maintain a runtime wide unique map of
-            // this stuff. So, if these don't match then the caller is
-            // doing something bad here.
-
-            MOZ_ASSERT(oldProto->GetScriptableInfo()->GetScriptableShared() ==
-                       newProto->GetScriptableInfo()->GetScriptableShared(),
-                       "Changing proto is also changing JSObject Classname or "
-                       "helper's nsIXPScriptable flags. This is not allowed!");
-
-            wrapper->UpdateScriptableInfo(newProto->GetScriptableInfo());
+        {   // scoped lock
+            Native2WrappedNativeMap* oldMap = aOldScope->GetWrappedNativeMap();
+            Native2WrappedNativeMap* newMap = aNewScope->GetWrappedNativeMap();
+            XPCAutoLock lock(aOldScope->GetRuntime()->GetMapLock());
+
+            oldMap->Remove(wrapper);
+
+            if (wrapper->HasProto())
+                wrapper->SetProto(newProto);
+
+            // If the wrapper has no scriptable or it has a non-shared
+            // scriptable, then we don't need to mess with it.
+            // Otherwise...
+
+            if (wrapper->mScriptableInfo &&
+                wrapper->mScriptableInfo == oldProto->GetScriptableInfo()) {
+                // The new proto had better have the same JSClass stuff as
+                // the old one! We maintain a runtime wide unique map of
+                // this stuff. So, if these don't match then the caller is
+                // doing something bad here.
+
+                MOZ_ASSERT(oldProto->GetScriptableInfo()->GetScriptableShared() ==
+                           newProto->GetScriptableInfo()->GetScriptableShared(),
+                           "Changing proto is also changing JSObject Classname or "
+                           "helper's nsIXPScriptable flags. This is not allowed!");
+
+                wrapper->UpdateScriptableInfo(newProto->GetScriptableInfo());
+            }
+
+            // Crash if the wrapper is already in the new scope.
+            if (newMap->Find(wrapper->GetIdentityObject()))
+                MOZ_CRASH();
+
+            if (!newMap->Add(wrapper))
+                MOZ_CRASH();
         }
 
-        // Crash if the wrapper is already in the new scope.
-        if (newMap->Find(wrapper->GetIdentityObject()))
-            MOZ_CRASH();
-
-        if (!newMap->Add(wrapper))
-            MOZ_CRASH();
-
         RootedObject ww(cx, wrapper->GetWrapper());
         if (ww) {
             RootedObject newwrapper(cx);
             MOZ_ASSERT(wrapper->NeedsSOW(), "weird wrapper wrapper");
 
             // Oops. We don't support transplanting objects with SOWs anymore.
             MOZ_CRASH();
 
@@ -1411,32 +1435,35 @@ XPCWrappedNative::RescueOrphans()
     RootedObject flatJSObject(cx, mFlatJSObject);
     return ::RescueOrphans(flatJSObject);
 }
 
 bool
 XPCWrappedNative::ExtendSet(XPCNativeInterface* aInterface)
 {
     AutoJSContext cx;
+    // This is only called while locked (during XPCWrappedNative::FindTearOff).
 
     if (!mSet->HasInterface(aInterface)) {
         AutoMarkingNativeSetPtr newSet(cx);
         newSet = XPCNativeSet::GetNewOrUsed(mSet, aInterface,
                                             mSet->GetInterfaceCount());
         if (!newSet)
             return false;
 
         mSet = newSet;
     }
     return true;
 }
 
 XPCWrappedNativeTearOff*
 XPCWrappedNative::LocateTearOff(XPCNativeInterface* aInterface)
 {
+    XPCAutoLock al(GetLock()); // hold the lock throughout
+
     for (XPCWrappedNativeTearOffChunk* chunk = &mFirstChunk;
          chunk != nullptr;
          chunk = chunk->mNextChunk) {
         XPCWrappedNativeTearOff* tearOff = chunk->mTearOffs;
         XPCWrappedNativeTearOff* const end = tearOff +
             XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK;
         for (tearOff = chunk->mTearOffs;
              tearOff < end;
@@ -1450,16 +1477,18 @@ XPCWrappedNative::LocateTearOff(XPCNativ
 }
 
 XPCWrappedNativeTearOff*
 XPCWrappedNative::FindTearOff(XPCNativeInterface* aInterface,
                               bool needJSObject /* = false */,
                               nsresult* pError /* = nullptr */)
 {
     AutoJSContext cx;
+    XPCAutoLock al(GetLock()); // hold the lock throughout
+
     nsresult rv = NS_OK;
     XPCWrappedNativeTearOff* to;
     XPCWrappedNativeTearOff* firstAvailable = nullptr;
 
     XPCWrappedNativeTearOffChunk* lastChunk;
     XPCWrappedNativeTearOffChunk* chunk;
     for (lastChunk = chunk = &mFirstChunk;
          chunk;
@@ -1525,123 +1554,130 @@ return_result:
 
 nsresult
 XPCWrappedNative::InitTearOff(XPCWrappedNativeTearOff* aTearOff,
                               XPCNativeInterface* aInterface,
                               bool needJSObject)
 {
     AutoJSContext cx;
 
+    // This is only called while locked (during XPCWrappedNative::FindTearOff).
+
     // Determine if the object really does this interface...
 
     const nsIID* iid = aInterface->GetIID();
     nsISupports* identity = GetIdentityObject();
     nsISupports* obj;
 
     // If the scriptable helper forbids us from reflecting additional
     // interfaces, then don't even try the QI, just fail.
     if (mScriptableInfo &&
         mScriptableInfo->GetFlags().ClassInfoInterfacesOnly() &&
         !mSet->HasInterface(aInterface) &&
         !mSet->HasInterfaceWithAncestor(aInterface)) {
         return NS_ERROR_NO_INTERFACE;
     }
 
-    // We are about to call out to other code.
+    // We are about to call out to unlock and other code.
     // So protect our intended tearoff.
 
     aTearOff->SetReserved();
 
-    if (NS_FAILED(identity->QueryInterface(*iid, (void**)&obj)) || !obj) {
-        aTearOff->SetInterface(nullptr);
-        return NS_ERROR_NO_INTERFACE;
-    }
-
-    // Guard against trying to build a tearoff for a shared nsIClassInfo.
-    if (iid->Equals(NS_GET_IID(nsIClassInfo))) {
-        nsCOMPtr<nsISupports> alternate_identity(do_QueryInterface(obj));
-        if (alternate_identity.get() != identity) {
-            NS_RELEASE(obj);
+    {   // scoped *un*lock
+        XPCAutoUnlock unlock(GetLock());
+
+        if (NS_FAILED(identity->QueryInterface(*iid, (void**)&obj)) || !obj) {
             aTearOff->SetInterface(nullptr);
             return NS_ERROR_NO_INTERFACE;
         }
-    }
-
-    // Guard against trying to build a tearoff for an interface that is
-    // aggregated and is implemented as a nsIXPConnectWrappedJS using this
-    // self-same JSObject. The XBL system does this. If we mutate the set
-    // of this wrapper then we will shadow the method that XBL has added to
-    // the JSObject that it has inserted in the JS proto chain between our
-    // JSObject and our XPCWrappedNativeProto's JSObject. If we let this
-    // set mutation happen then the interface's methods will be added to
-    // our JSObject, but calls on those methods will get routed up to
-    // native code and into the wrappedJS - which will do a method lookup
-    // on *our* JSObject and find the same method and make another call
-    // into an infinite loop.
-    // see: http://bugzilla.mozilla.org/show_bug.cgi?id=96725
-
-    // The code in this block also does a check for the double wrapped
-    // nsIPropertyBag case.
-
-    nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(obj));
-    if (wrappedJS) {
-        RootedObject jso(cx, wrappedJS->GetJSObject());
-        if (jso == mFlatJSObject) {
-            // The implementing JSObject is the same as ours! Just say OK
-            // without actually extending the set.
-            //
-            // XXX It is a little cheesy to have FindTearOff return an
-            // 'empty' tearoff. But this is the centralized place to do the
-            // QI activities on the underlying object. *And* most caller to
-            // FindTearOff only look for a non-null result and ignore the
-            // actual tearoff returned. The only callers that do use the
-            // returned tearoff make sure to check for either a non-null
-            // JSObject or a matching Interface before proceeding.
-            // I think we can get away with this bit of ugliness.
-
-            NS_RELEASE(obj);
-            aTearOff->SetInterface(nullptr);
-            return NS_OK;
-        }
-
-        // Decide whether or not to expose nsIPropertyBag to calling
-        // JS code in the double wrapped case.
-        //
-        // Our rule here is that when JSObjects are double wrapped and
-        // exposed to other JSObjects then the nsIPropertyBag interface
-        // is only exposed on an 'opt-in' basis; i.e. if the underlying
-        // JSObject wants other JSObjects to be able to see this interface
-        // then it must implement QueryInterface and not throw an exception
-        // when asked for nsIPropertyBag. It need not actually *implement*
-        // nsIPropertyBag - xpconnect will do that work.
-
-        nsXPCWrappedJSClass* clazz;
-        if (iid->Equals(NS_GET_IID(nsIPropertyBag)) && jso &&
-            NS_SUCCEEDED(nsXPCWrappedJSClass::GetNewOrUsed(cx,*iid,&clazz))&&
-            clazz) {
-            RootedObject answer(cx,
-                                clazz->CallQueryInterfaceOnJSObject(cx, jso, *iid));
-            NS_RELEASE(clazz);
-            if (!answer) {
+
+        // Guard against trying to build a tearoff for a shared nsIClassInfo.
+        if (iid->Equals(NS_GET_IID(nsIClassInfo))) {
+            nsCOMPtr<nsISupports> alternate_identity(do_QueryInterface(obj));
+            if (alternate_identity.get() != identity) {
                 NS_RELEASE(obj);
                 aTearOff->SetInterface(nullptr);
                 return NS_ERROR_NO_INTERFACE;
             }
         }
+
+        // Guard against trying to build a tearoff for an interface that is
+        // aggregated and is implemented as a nsIXPConnectWrappedJS using this
+        // self-same JSObject. The XBL system does this. If we mutate the set
+        // of this wrapper then we will shadow the method that XBL has added to
+        // the JSObject that it has inserted in the JS proto chain between our
+        // JSObject and our XPCWrappedNativeProto's JSObject. If we let this
+        // set mutation happen then the interface's methods will be added to
+        // our JSObject, but calls on those methods will get routed up to
+        // native code and into the wrappedJS - which will do a method lookup
+        // on *our* JSObject and find the same method and make another call
+        // into an infinite loop.
+        // see: http://bugzilla.mozilla.org/show_bug.cgi?id=96725
+
+        // The code in this block also does a check for the double wrapped
+        // nsIPropertyBag case.
+
+        nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS(do_QueryInterface(obj));
+        if (wrappedJS) {
+            RootedObject jso(cx, wrappedJS->GetJSObject());
+            if (jso == mFlatJSObject) {
+                // The implementing JSObject is the same as ours! Just say OK
+                // without actually extending the set.
+                //
+                // XXX It is a little cheesy to have FindTearOff return an
+                // 'empty' tearoff. But this is the centralized place to do the
+                // QI activities on the underlying object. *And* most caller to
+                // FindTearOff only look for a non-null result and ignore the
+                // actual tearoff returned. The only callers that do use the
+                // returned tearoff make sure to check for either a non-null
+                // JSObject or a matching Interface before proceeding.
+                // I think we can get away with this bit of ugliness.
+
+                NS_RELEASE(obj);
+                aTearOff->SetInterface(nullptr);
+                return NS_OK;
+            }
+
+            // Decide whether or not to expose nsIPropertyBag to calling
+            // JS code in the double wrapped case.
+            //
+            // Our rule here is that when JSObjects are double wrapped and
+            // exposed to other JSObjects then the nsIPropertyBag interface
+            // is only exposed on an 'opt-in' basis; i.e. if the underlying
+            // JSObject wants other JSObjects to be able to see this interface
+            // then it must implement QueryInterface and not throw an exception
+            // when asked for nsIPropertyBag. It need not actually *implement*
+            // nsIPropertyBag - xpconnect will do that work.
+
+            nsXPCWrappedJSClass* clazz;
+            if (iid->Equals(NS_GET_IID(nsIPropertyBag)) && jso &&
+                NS_SUCCEEDED(nsXPCWrappedJSClass::GetNewOrUsed(cx,*iid,&clazz))&&
+                clazz) {
+                RootedObject answer(cx,
+                    clazz->CallQueryInterfaceOnJSObject(cx, jso, *iid));
+                NS_RELEASE(clazz);
+                if (!answer) {
+                    NS_RELEASE(obj);
+                    aTearOff->SetInterface(nullptr);
+                    return NS_ERROR_NO_INTERFACE;
+                }
+            }
+        }
+
+        nsIXPCSecurityManager* sm = nsXPConnect::XPConnect()->GetDefaultSecurityManager();
+        if (sm && NS_FAILED(sm->
+                            CanCreateWrapper(cx, *iid, identity,
+                                             GetClassInfo(), GetSecurityInfoAddr()))) {
+            // the security manager vetoed. It should have set an exception.
+            NS_RELEASE(obj);
+            aTearOff->SetInterface(nullptr);
+            return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
+        }
     }
-
-    nsIXPCSecurityManager* sm = nsXPConnect::XPConnect()->GetDefaultSecurityManager();
-    if (sm && NS_FAILED(sm->
-                        CanCreateWrapper(cx, *iid, identity,
-                                         GetClassInfo(), GetSecurityInfoAddr()))) {
-        // the security manager vetoed. It should have set an exception.
-        NS_RELEASE(obj);
-        aTearOff->SetInterface(nullptr);
-        return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
-    }
+    // We are relocked from here on...
 
     // If this is not already in our set we need to extend our set.
     // Note: we do not cache the result of the previous call to HasInterface()
     // because we unlocked and called out in the interim and the result of the
     // previous call might not be correct anymore.
 
     if (!mSet->HasInterface(aInterface) && !ExtendSet(aInterface)) {
         NS_RELEASE(obj);
@@ -1657,16 +1693,18 @@ XPCWrappedNative::InitTearOff(XPCWrapped
     return NS_OK;
 }
 
 bool
 XPCWrappedNative::InitTearOffJSObject(XPCWrappedNativeTearOff* to)
 {
     AutoJSContext cx;
 
+    // This is only called while locked (during XPCWrappedNative::FindTearOff).
+
     JSObject* obj = JS_NewObject(cx, Jsvalify(&XPC_WN_Tearoff_JSClass),
                                  JS_GetObjectPrototype(cx, mFlatJSObject),
                                  mFlatJSObject);
     if (!obj)
         return false;
 
     JS_SetPrivate(obj, to);
     to->SetJSObject(obj);
@@ -2838,17 +2876,17 @@ XPCJSObjectHolder::GetJSObject()
 XPCJSObjectHolder::XPCJSObjectHolder(JSObject* obj)
     : mJSObj(obj)
 {
     XPCJSRuntime::Get()->AddObjectHolderRoot(this);
 }
 
 XPCJSObjectHolder::~XPCJSObjectHolder()
 {
-    RemoveFromRootSet();
+    RemoveFromRootSet(nsXPConnect::GetRuntimeInstance()->GetMapLock());
 }
 
 void
 XPCJSObjectHolder::TraceJS(JSTracer *trc)
 {
     JS_SET_TRACING_DETAILS(trc, GetTraceName, this, 0);
     JS_CallHeapObjectTracer(trc, &mJSObj, "XPCJSObjectHolder::mJSObj");
 }
--- a/js/xpconnect/src/XPCWrappedNativeInfo.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeInfo.cpp
@@ -121,38 +121,44 @@ XPCNativeInterface::GetNewOrUsed(const n
     AutoJSContext cx;
     AutoMarkingNativeInterfacePtr iface(cx);
     XPCJSRuntime* rt = XPCJSRuntime::Get();
 
     IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap();
     if (!map)
         return nullptr;
 
-    iface = map->Find(*iid);
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+        iface = map->Find(*iid);
+    }
 
     if (iface)
         return iface;
 
     nsCOMPtr<nsIInterfaceInfo> info;
     XPTInterfaceInfoManager::GetSingleton()->GetInfoForIID(iid, getter_AddRefs(info));
     if (!info)
         return nullptr;
 
     iface = NewInstance(info);
     if (!iface)
         return nullptr;
 
-    XPCNativeInterface* iface2 = map->Add(iface);
-    if (!iface2) {
-        NS_ERROR("failed to add our interface!");
-        DestroyInstance(iface);
-        iface = nullptr;
-    } else if (iface2 != iface) {
-        DestroyInstance(iface);
-        iface = iface2;
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+        XPCNativeInterface* iface2 = map->Add(iface);
+        if (!iface2) {
+            NS_ERROR("failed to add our interface!");
+            DestroyInstance(iface);
+            iface = nullptr;
+        } else if (iface2 != iface) {
+            DestroyInstance(iface);
+            iface = iface2;
+        }
     }
 
     return iface;
 }
 
 // static
 XPCNativeInterface*
 XPCNativeInterface::GetNewOrUsed(nsIInterfaceInfo* info)
@@ -165,33 +171,39 @@ XPCNativeInterface::GetNewOrUsed(nsIInte
         return nullptr;
 
     XPCJSRuntime* rt = XPCJSRuntime::Get();
 
     IID2NativeInterfaceMap* map = rt->GetIID2NativeInterfaceMap();
     if (!map)
         return nullptr;
 
-    iface = map->Find(*iid);
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+        iface = map->Find(*iid);
+    }
 
     if (iface)
         return iface;
 
     iface = NewInstance(info);
     if (!iface)
         return nullptr;
 
-    XPCNativeInterface* iface2 = map->Add(iface);
-    if (!iface2) {
-        NS_ERROR("failed to add our interface!");
-        DestroyInstance(iface);
-        iface = nullptr;
-    } else if (iface2 != iface) {
-        DestroyInstance(iface);
-        iface = iface2;
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+        XPCNativeInterface* iface2 = map->Add(iface);
+        if (!iface2) {
+            NS_ERROR("failed to add our interface!");
+            DestroyInstance(iface);
+            iface = nullptr;
+        } else if (iface2 != iface) {
+            DestroyInstance(iface);
+            iface = iface2;
+        }
     }
 
     return iface;
 }
 
 // static
 XPCNativeInterface*
 XPCNativeInterface::GetNewOrUsed(const char* name)
@@ -412,35 +424,41 @@ XPCNativeSet::GetNewOrUsed(const nsIID* 
 
     XPCNativeSetKey key(nullptr, iface, 0);
 
     XPCJSRuntime* rt = XPCJSRuntime::Get();
     NativeSetMap* map = rt->GetNativeSetMap();
     if (!map)
         return nullptr;
 
-    set = map->Find(&key);
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+        set = map->Find(&key);
+    }
 
     if (set)
         return set;
 
     // hacky way to get a XPCNativeInterface** using the AutoPtr
     XPCNativeInterface* temp[] = {iface};
     set = NewInstance(temp, 1);
     if (!set)
         return nullptr;
 
-    XPCNativeSet* set2 = map->Add(&key, set);
-    if (!set2) {
-        NS_ERROR("failed to add our set!");
-        DestroyInstance(set);
-        set = nullptr;
-    } else if (set2 != set) {
-        DestroyInstance(set);
-        set = set2;
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+        XPCNativeSet* set2 = map->Add(&key, set);
+        if (!set2) {
+            NS_ERROR("failed to add our set!");
+            DestroyInstance(set);
+            set = nullptr;
+        } else if (set2 != set) {
+            DestroyInstance(set);
+            set = set2;
+        }
     }
 
     return set;
 }
 
 // static
 XPCNativeSet*
 XPCNativeSet::GetNewOrUsed(nsIClassInfo* classInfo)
@@ -448,17 +466,20 @@ XPCNativeSet::GetNewOrUsed(nsIClassInfo*
     AutoJSContext cx;
     AutoMarkingNativeSetPtr set(cx);
     XPCJSRuntime* rt = XPCJSRuntime::Get();
 
     ClassInfo2NativeSetMap* map = rt->GetClassInfo2NativeSetMap();
     if (!map)
         return nullptr;
 
-    set = map->Find(classInfo);
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+        set = map->Find(classInfo);
+    }
 
     if (set)
         return set;
 
     nsIID** iidArray = nullptr;
     AutoMarkingNativeInterfacePtrArrayPtr interfaceArray(cx);
     uint32_t iidCount = 0;
 
@@ -511,34 +532,40 @@ XPCNativeSet::GetNewOrUsed(nsIClassInfo*
             set = NewInstance(interfaceArray, interfaceCount);
             if (set) {
                 NativeSetMap* map2 = rt->GetNativeSetMap();
                 if (!map2)
                     goto out;
 
                 XPCNativeSetKey key(set, nullptr, 0);
 
-                XPCNativeSet* set2 = map2->Add(&key, set);
-                if (!set2) {
-                    NS_ERROR("failed to add our set!");
-                    DestroyInstance(set);
-                    set = nullptr;
-                    goto out;
-                }
-                if (set2 != set) {
-                    DestroyInstance(set);
-                    set = set2;
+                {   // scoped lock
+                    XPCAutoLock lock(rt->GetMapLock());
+                    XPCNativeSet* set2 = map2->Add(&key, set);
+                    if (!set2) {
+                        NS_ERROR("failed to add our set!");
+                        DestroyInstance(set);
+                        set = nullptr;
+                        goto out;
+                    }
+                    if (set2 != set) {
+                        DestroyInstance(set);
+                        set = set2;
+                    }
                 }
             }
         } else
             set = GetNewOrUsed(&NS_GET_IID(nsISupports));
     } else
         set = GetNewOrUsed(&NS_GET_IID(nsISupports));
 
-    if (set) {
+    if (set)
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+
 #ifdef DEBUG
         XPCNativeSet* set2 =
 #endif
           map->Add(classInfo, set);
         MOZ_ASSERT(set2, "failed to add our set!");
         MOZ_ASSERT(set2 == set, "hashtables inconsistent!");
     }
 
@@ -553,17 +580,20 @@ out:
 
 // static
 void
 XPCNativeSet::ClearCacheEntryForClassInfo(nsIClassInfo* classInfo)
 {
     XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
     ClassInfo2NativeSetMap* map = rt->GetClassInfo2NativeSetMap();
     if (map)
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
         map->Remove(classInfo);
+    }
 }
 
 // static
 XPCNativeSet*
 XPCNativeSet::GetNewOrUsed(XPCNativeSet* otherSet,
                            XPCNativeInterface* newInterface,
                            uint16_t position)
 {
@@ -571,37 +601,43 @@ XPCNativeSet::GetNewOrUsed(XPCNativeSet*
     AutoMarkingNativeSetPtr set(cx);
     XPCJSRuntime* rt = XPCJSRuntime::Get();
     NativeSetMap* map = rt->GetNativeSetMap();
     if (!map)
         return nullptr;
 
     XPCNativeSetKey key(otherSet, newInterface, position);
 
-    set = map->Find(&key);
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+        set = map->Find(&key);
+    }
 
     if (set)
         return set;
 
     if (otherSet)
         set = NewInstanceMutate(otherSet, newInterface, position);
     else
         set = NewInstance(&newInterface, 1);
 
     if (!set)
         return nullptr;
 
-    XPCNativeSet* set2 = map->Add(&key, set);
-    if (!set2) {
-        NS_ERROR("failed to add our set!");
-        DestroyInstance(set);
-        set = nullptr;
-    } else if (set2 != set) {
-        DestroyInstance(set);
-        set = set2;
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+        XPCNativeSet* set2 = map->Add(&key, set);
+        if (!set2) {
+            NS_ERROR("failed to add our set!");
+            DestroyInstance(set);
+            set = nullptr;
+        } else if (set2 != set) {
+            DestroyInstance(set);
+            set = set2;
+        }
     }
 
     return set;
 }
 
 // static
 XPCNativeSet*
 XPCNativeSet::GetNewOrUsed(XPCNativeSet* firstSet,
--- a/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeJSOps.cpp
@@ -1124,18 +1124,21 @@ XPCNativeScriptableInfo::Construct(const
         delete newObj;
         return nullptr;
     }
 
     bool success;
 
     XPCJSRuntime* rt = XPCJSRuntime::Get();
     XPCNativeScriptableSharedMap* map = rt->GetNativeScriptableSharedMap();
-    success = map->GetNewOrUsed(sci->GetFlags(), name,
-                                sci->GetInterfacesBitmap(), newObj);
+    {   // scoped lock
+        XPCAutoLock lock(rt->GetMapLock());
+        success = map->GetNewOrUsed(sci->GetFlags(), name,
+                                    sci->GetInterfacesBitmap(), newObj);
+    }
 
     if (!success) {
         delete newObj;
         return nullptr;
     }
 
     return newObj;
 }
--- a/js/xpconnect/src/XPCWrappedNativeProto.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeProto.cpp
@@ -128,18 +128,21 @@ XPCWrappedNativeProto::CallPostCreatePro
     return true;
 }
 
 void
 XPCWrappedNativeProto::JSProtoObjectFinalized(js::FreeOp *fop, JSObject *obj)
 {
     MOZ_ASSERT(obj == mJSProtoObject, "huh?");
 
+    // Map locking is not necessary since we are running gc.
+
     // Only remove this proto from the map if it is the one in the map.
-    ClassInfo2WrappedNativeProtoMap* map = GetScope()->GetWrappedNativeProtoMap();
+    ClassInfo2WrappedNativeProtoMap* map =
+        GetScope()->GetWrappedNativeProtoMap(ClassIsMainThreadOnly());
     if (map->Find(mClassInfo) == this)
         map->Remove(mClassInfo);
 
     GetRuntime()->GetDetachedWrappedNativeProtoMap()->Remove(this);
     GetRuntime()->GetDyingWrappedNativeProtoMap()->Add(this);
 
     mJSProtoObject.finalize(js::CastToJSFreeOp(fop)->runtime());
 }
@@ -165,39 +168,48 @@ XPCWrappedNativeProto::GetNewOrUsed(XPCW
                                     bool callPostCreatePrototype)
 {
     AutoJSContext cx;
     MOZ_ASSERT(scope, "bad param");
     MOZ_ASSERT(classInfo, "bad param");
 
     AutoMarkingWrappedNativeProtoPtr proto(cx);
     ClassInfo2WrappedNativeProtoMap* map = nullptr;
+    XPCLock* lock = nullptr;
 
     uint32_t ciFlags;
     if (NS_FAILED(classInfo->GetFlags(&ciFlags)))
         ciFlags = 0;
 
-    map = scope->GetWrappedNativeProtoMap();
-    proto = map->Find(classInfo);
-    if (proto)
-        return proto;
+    bool mainThreadOnly = !!(ciFlags & nsIClassInfo::MAIN_THREAD_ONLY);
+    map = scope->GetWrappedNativeProtoMap(mainThreadOnly);
+    lock = mainThreadOnly ? nullptr : scope->GetRuntime()->GetMapLock();
+    {   // scoped lock
+        XPCAutoLock al(lock);
+        proto = map->Find(classInfo);
+        if (proto)
+            return proto;
+    }
 
     AutoMarkingNativeSetPtr set(cx);
     set = XPCNativeSet::GetNewOrUsed(classInfo);
     if (!set)
         return nullptr;
 
     proto = new XPCWrappedNativeProto(scope, classInfo, ciFlags, set);
 
     if (!proto || !proto->Init(scriptableCreateInfo, callPostCreatePrototype)) {
         delete proto.get();
         return nullptr;
     }
 
-    map->Add(classInfo, proto);
+    {   // scoped lock
+        XPCAutoLock al(lock);
+        map->Add(classInfo, proto);
+    }
 
     return proto;
 }
 
 void
 XPCWrappedNativeProto::DebugDump(int16_t depth)
 {
 #ifdef DEBUG
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -63,26 +63,30 @@ RemoteXULForbidsXBLScope(nsIPrincipal *a
   // Check the pref to determine how we should behave.
   return !Preferences::GetBool("dom.use_xbl_scopes_for_remote_xul", false);
 }
 
 XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext *cx,
                                              JS::HandleObject aGlobal)
       : mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_SIZE)),
         mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)),
+        mMainThreadWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_SIZE)),
         mComponents(nullptr),
         mNext(nullptr),
         mGlobalJSObject(aGlobal),
         mIsXBLScope(false)
 {
     // add ourselves to the scopes list
     {
         MOZ_ASSERT(aGlobal);
         MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
                                                          JSCLASS_HAS_PRIVATE)); 
+        // scoped lock
+        XPCAutoLock lock(XPCJSRuntime::Get()->GetMapLock());
+
 #ifdef DEBUG
         for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext)
             MOZ_ASSERT(aGlobal != cur->GetGlobalJSObjectPreserveColor(), "dup object");
 #endif
 
         mNext = gScopes;
         gScopes = this;
 
@@ -252,16 +256,21 @@ XPCWrappedNativeScope::~XPCWrappedNative
         delete mWrappedNativeMap;
     }
 
     if (mWrappedNativeProtoMap) {
         MOZ_ASSERT(0 == mWrappedNativeProtoMap->Count(), "scope has non-empty map");
         delete mWrappedNativeProtoMap;
     }
 
+    if (mMainThreadWrappedNativeProtoMap) {
+        MOZ_ASSERT(0 == mMainThreadWrappedNativeProtoMap->Count(), "scope has non-empty map");
+        delete mMainThreadWrappedNativeProtoMap;
+    }
+
     if (mContext)
         mContext->RemoveScope(this);
 
     // This should not be necessary, since the Components object should die
     // with the scope but just in case.
     if (mComponents)
         mComponents->mScope = nullptr;
 
@@ -284,16 +293,20 @@ WrappedNativeJSGCThingTracer(PLDHashTabl
 
     return PL_DHASH_NEXT;
 }
 
 // static
 void
 XPCWrappedNativeScope::TraceWrappedNativesInAllScopes(JSTracer* trc, XPCJSRuntime* rt)
 {
+    // FIXME The lock may not be necessary during tracing as that serializes
+    // access to JS runtime. See bug 380139.
+    XPCAutoLock lock(rt->GetMapLock());
+
     // Do JS_CallTracer for all wrapped natives with external references, as
     // well as any DOM expando objects.
     for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
         cur->mWrappedNativeMap->Enumerate(WrappedNativeJSGCThingTracer, trc);
         if (cur->mDOMExpandoSet) {
             for (DOMExpandoSet::Enum e(*cur->mDOMExpandoSet); !e.empty(); e.popFront())
                 JS_CallHashSetObjectTracer(trc, e, e.front(), "DOM expando object");
         }
@@ -323,29 +336,35 @@ SuspectDOMExpandos(JSObject *obj, nsCycl
     cb.NoteXPCOMRoot(native);
 }
 
 // static
 void
 XPCWrappedNativeScope::SuspectAllWrappers(XPCJSRuntime* rt,
                                           nsCycleCollectionNoteRootCallback& cb)
 {
+    XPCAutoLock lock(rt->GetMapLock());
+
     for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
         cur->mWrappedNativeMap->Enumerate(WrappedNativeSuspecter, &cb);
         if (cur->mDOMExpandoSet) {
             for (DOMExpandoSet::Range r = cur->mDOMExpandoSet->all(); !r.empty(); r.popFront())
                 SuspectDOMExpandos(r.front(), cb);
         }
     }
 }
 
 // static
 void
 XPCWrappedNativeScope::StartFinalizationPhaseOfGC(JSFreeOp *fop, XPCJSRuntime* rt)
 {
+    // FIXME The lock may not be necessary since we are inside JSGC_MARK_END
+    // callback and GX serializes access to JS runtime. See bug 380139.
+    XPCAutoLock lock(rt->GetMapLock());
+
     // We are in JSGC_MARK_END and JSGC_FINALIZE_END must always follow it
     // calling FinishedFinalizationPhaseOfGC and clearing gDyingScopes in
     // KillDyingScopes.
     MOZ_ASSERT(!gDyingScopes, "JSGC_MARK_END without JSGC_FINALIZE_END");
 
     XPCWrappedNativeScope* prev = nullptr;
     XPCWrappedNativeScope* cur = gScopes;
 
@@ -372,16 +391,22 @@ XPCWrappedNativeScope::StartFinalization
         cur = next;
     }
 }
 
 // static
 void
 XPCWrappedNativeScope::FinishedFinalizationPhaseOfGC()
 {
+    XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
+
+    // FIXME The lock may not be necessary since we are inside
+    // JSGC_FINALIZE_END callback and at this point GC still serializes access
+    // to JS runtime. See bug 380139.
+    XPCAutoLock lock(rt->GetMapLock());
     KillDyingScopes();
 }
 
 static PLDHashOperator
 WrappedNativeMarker(PLDHashTable *table, PLDHashEntryHdr *hdr,
                     uint32_t number_t, void *arg)
 {
     ((Native2WrappedNativeMap::Entry*)hdr)->value->Mark();
@@ -400,16 +425,17 @@ WrappedNativeProtoMarker(PLDHashTable *t
 
 // static
 void
 XPCWrappedNativeScope::MarkAllWrappedNativesAndProtos()
 {
     for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
         cur->mWrappedNativeMap->Enumerate(WrappedNativeMarker, nullptr);
         cur->mWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMarker, nullptr);
+        cur->mMainThreadWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMarker, nullptr);
     }
 }
 
 #ifdef DEBUG
 static PLDHashOperator
 ASSERT_WrappedNativeSetNotMarked(PLDHashTable *table, PLDHashEntryHdr *hdr,
                                  uint32_t number, void *arg)
 {
@@ -427,16 +453,17 @@ ASSERT_WrappedNativeProtoSetNotMarked(PL
 
 // static
 void
 XPCWrappedNativeScope::ASSERT_NoInterfaceSetsAreMarked()
 {
     for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
         cur->mWrappedNativeMap->Enumerate(ASSERT_WrappedNativeSetNotMarked, nullptr);
         cur->mWrappedNativeProtoMap->Enumerate(ASSERT_WrappedNativeProtoSetNotMarked, nullptr);
+        cur->mMainThreadWrappedNativeProtoMap->Enumerate(ASSERT_WrappedNativeProtoSetNotMarked, nullptr);
     }
 }
 #endif
 
 static PLDHashOperator
 WrappedNativeTearoffSweeper(PLDHashTable *table, PLDHashEntryHdr *hdr,
                             uint32_t number, void *arg)
 {
@@ -451,16 +478,17 @@ XPCWrappedNativeScope::SweepAllWrappedNa
     for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext)
         cur->mWrappedNativeMap->Enumerate(WrappedNativeTearoffSweeper, nullptr);
 }
 
 // static
 void
 XPCWrappedNativeScope::KillDyingScopes()
 {
+    // always called inside the lock!
     XPCWrappedNativeScope* cur = gDyingScopes;
     while (cur) {
         XPCWrappedNativeScope* next = cur->mNext;
         delete cur;
         cur = next;
     }
     gDyingScopes = nullptr;
 }
@@ -529,16 +557,18 @@ XPCWrappedNativeScope::SystemIsBeingShut
         // Give the Components object a chance to try to clean up.
         if (cur->mComponents)
             cur->mComponents->SystemIsBeingShutDown();
 
         // Walk the protos first. Wrapper shutdown can leave dangling
         // proto pointers in the proto map.
         cur->mWrappedNativeProtoMap->
                 Enumerate(WrappedNativeProtoShutdownEnumerator,  &data);
+        cur->mMainThreadWrappedNativeProtoMap->
+                Enumerate(WrappedNativeProtoShutdownEnumerator,  &data);
         cur->mWrappedNativeMap->
                 Enumerate(WrappedNativeShutdownEnumerator,  &data);
     }
 
     // Now it is safe to kill all the scopes.
     KillDyingScopes();
 }
 
@@ -554,18 +584,22 @@ WNProtoSecPolicyClearer(PLDHashTable *ta
     *(proto->GetSecurityInfoAddr()) = nullptr;
     return PL_DHASH_NEXT;
 }
 
 // static
 nsresult
 XPCWrappedNativeScope::ClearAllWrappedNativeSecurityPolicies()
 {
+    // Hold the lock throughout.
+    XPCAutoLock lock(XPCJSRuntime::Get()->GetMapLock());
+
     for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
         cur->mWrappedNativeProtoMap->Enumerate(WNProtoSecPolicyClearer, nullptr);
+        cur->mMainThreadWrappedNativeProtoMap->Enumerate(WNProtoSecPolicyClearer, nullptr);
     }
 
     return NS_OK;
 }
 
 static PLDHashOperator
 WNProtoRemover(PLDHashTable *table, PLDHashEntryHdr *hdr,
                uint32_t number, void *arg)
@@ -578,18 +612,22 @@ WNProtoRemover(PLDHashTable *table, PLDH
     detachedMap->Add(proto);
 
     return PL_DHASH_REMOVE;
 }
 
 void
 XPCWrappedNativeScope::RemoveWrappedNativeProtos()
 {
+    XPCAutoLock al(XPCJSRuntime::Get()->GetMapLock());
+
     mWrappedNativeProtoMap->Enumerate(WNProtoRemover,
                                       GetRuntime()->GetDetachedWrappedNativeProtoMap());
+    mMainThreadWrappedNativeProtoMap->Enumerate(WNProtoRemover,
+                                                GetRuntime()->GetDetachedWrappedNativeProtoMap());
 }
 
 /***************************************************************************/
 
 // static
 void
 XPCWrappedNativeScope::DebugDumpAllScopes(int16_t depth)
 {
@@ -654,36 +692,50 @@ XPCWrappedNativeScope::DebugDump(int16_t
                         mWrappedNativeProtoMap,                               \
                         mWrappedNativeProtoMap ? mWrappedNativeProtoMap->Count() : 0));
         // iterate contexts...
         if (depth && mWrappedNativeProtoMap && mWrappedNativeProtoMap->Count()) {
             XPC_LOG_INDENT();
             mWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMapDumpEnumerator, &depth);
             XPC_LOG_OUTDENT();
         }
+
+        XPC_LOG_ALWAYS(("mMainThreadWrappedNativeProtoMap @ %x with %d protos(s)", \
+                        mMainThreadWrappedNativeProtoMap,                     \
+                        mMainThreadWrappedNativeProtoMap ? mMainThreadWrappedNativeProtoMap->Count() : 0));
+        // iterate contexts...
+        if (depth && mMainThreadWrappedNativeProtoMap && mMainThreadWrappedNativeProtoMap->Count()) {
+            XPC_LOG_INDENT();
+            mMainThreadWrappedNativeProtoMap->Enumerate(WrappedNativeProtoMapDumpEnumerator, &depth);
+            XPC_LOG_OUTDENT();
+        }
     XPC_LOG_OUTDENT();
 #endif
 }
 
 size_t
 XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(MallocSizeOf mallocSizeOf)
 {
+    XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance();
+    XPCAutoLock lock(rt->GetMapLock());
+
     size_t n = 0;
     for (XPCWrappedNativeScope *cur = gScopes; cur; cur = cur->mNext) {
         n += cur->SizeOfIncludingThis(mallocSizeOf);
     }
     return n;
 }
 
 size_t
 XPCWrappedNativeScope::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
 {
     size_t n = 0;
     n += mallocSizeOf(this);
     n += mWrappedNativeMap->SizeOfIncludingThis(mallocSizeOf);
     n += mWrappedNativeProtoMap->SizeOfIncludingThis(mallocSizeOf);
+    n += mMainThreadWrappedNativeProtoMap->SizeOfIncludingThis(mallocSizeOf);
 
     // There are other XPCWrappedNativeScope members that could be measured;
     // the above ones have been seen by DMD to be worth measuring.  More stuff
     // may be added later.
 
     return n;
 }
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -786,19 +786,22 @@ nsXPConnect::RescueOrphansInScope(JSCont
     XPCWrappedNativeScope *scope = GetObjectScope(aScope);
     if (!scope)
         return UnexpectedFailure(NS_ERROR_FAILURE);
 
     // First, look through the old scope and find all of the wrappers that we
     // might need to rescue.
     nsTArray<nsRefPtr<XPCWrappedNative> > wrappersToMove;
 
-    Native2WrappedNativeMap *map = scope->GetWrappedNativeMap();
-    wrappersToMove.SetCapacity(map->Count());
-    map->Enumerate(MoveableWrapperFinder, &wrappersToMove);
+    {   // scoped lock
+        XPCAutoLock lock(GetRuntime()->GetMapLock());
+        Native2WrappedNativeMap *map = scope->GetWrappedNativeMap();
+        wrappersToMove.SetCapacity(map->Count());
+        map->Enumerate(MoveableWrapperFinder, &wrappersToMove);
+    }
 
     // Now that we have the wrappers, reparent them to the new scope.
     for (uint32_t i = 0, stop = wrappersToMove.Length(); i < stop; ++i) {
         nsresult rv = wrappersToMove[i]->RescueOrphans();
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     return NS_OK;
@@ -867,17 +870,20 @@ nsXPConnect::GetCurrentNativeCallContext
 
 /* void setFunctionThisTranslator (in nsIIDRef aIID, in nsIXPCFunctionThisTranslator aTranslator); */
 NS_IMETHODIMP
 nsXPConnect::SetFunctionThisTranslator(const nsIID & aIID,
                                        nsIXPCFunctionThisTranslator *aTranslator)
 {
     XPCJSRuntime* rt = GetRuntime();
     IID2ThisTranslatorMap* map = rt->GetThisTranslatorMap();
-    map->Add(aIID, aTranslator);
+    {
+        XPCAutoLock lock(rt->GetMapLock()); // scoped lock
+        map->Add(aIID, aTranslator);
+    }
     return NS_OK;
 }
 
 /* void clearAllWrappedNativeSecurityPolicies (); */
 NS_IMETHODIMP
 nsXPConnect::ClearAllWrappedNativeSecurityPolicies()
 {
     return XPCWrappedNativeScope::ClearAllWrappedNativeSecurityPolicies();
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -76,16 +76,18 @@
 #define xpcprivate_h___
 
 #include "mozilla/Alignment.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/ReentrantMonitor.h"
 #include "mozilla/Util.h"
 
 #include <math.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
 #include "xpcpublic.h"
@@ -244,16 +246,130 @@ inline void SetWNExpandoChain(JSObject *
 }
 
 inline JSObject* GetWNExpandoChain(JSObject *obj)
 {
     MOZ_ASSERT(IS_WN_REFLECTOR(obj));
     return JS_GetReservedSlot(obj, WN_XRAYEXPANDOCHAIN_SLOT).toObjectOrNull();
 }
 
+/***************************************************************************/
+// Auto locking support class...
+
+// We PROMISE to never screw this up.
+#ifdef _MSC_VER
+#pragma warning(disable : 4355) // OK to pass "this" in member initializer
+#endif
+
+typedef mozilla::ReentrantMonitor XPCLock;
+
+static inline void xpc_Wait(XPCLock* lock)
+    {
+        MOZ_ASSERT(lock, "xpc_Wait called with null lock!");
+        lock->Wait();
+    }
+
+static inline void xpc_NotifyAll(XPCLock* lock)
+    {
+        MOZ_ASSERT(lock, "xpc_NotifyAll called with null lock!");
+        lock->NotifyAll();
+    }
+
+// This is a cloned subset of nsAutoMonitor. We want the use of a monitor -
+// mostly because we need reenterability - but we also want to support passing
+// a null monitor in without things blowing up. This is used for wrappers that
+// are guaranteed to be used only on one thread. We avoid lock overhead by
+// using a null monitor. By changing this class we can avoid having multiplte
+// code paths or (conditional) manual calls to PR_{Enter,Exit}Monitor.
+//
+// Note that xpconnect only makes *one* monitor and *mostly* holds it locked
+// only through very small critical sections.
+
+class MOZ_STACK_CLASS XPCAutoLock {
+public:
+
+    static XPCLock* NewLock(const char* name)
+                        {return new mozilla::ReentrantMonitor(name);}
+    static void     DestroyLock(XPCLock* lock)
+                        {delete lock;}
+
+    XPCAutoLock(XPCLock* lock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+        : mLock(lock)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        if (mLock)
+            mLock->Enter();
+    }
+
+    ~XPCAutoLock()
+    {
+        if (mLock) {
+            mLock->Exit();
+        }
+    }
+
+private:
+    XPCLock*  mLock;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+
+    // Not meant to be implemented. This makes it a compiler error to
+    // construct or assign an XPCAutoLock object incorrectly.
+    XPCAutoLock(void) {}
+    XPCAutoLock(XPCAutoLock& /*aMon*/) {}
+    XPCAutoLock& operator =(XPCAutoLock& /*aMon*/) {
+        return *this;
+    }
+
+    // Not meant to be implemented. This makes it a compiler error to
+    // attempt to create an XPCAutoLock object on the heap.
+    static void* operator new(size_t /*size*/) CPP_THROW_NEW {
+        return nullptr;
+    }
+    static void operator delete(void* /*memory*/) {}
+};
+
+/************************************************/
+
+class MOZ_STACK_CLASS XPCAutoUnlock {
+public:
+    XPCAutoUnlock(XPCLock* lock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+        : mLock(lock)
+    {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+        if (mLock) {
+            mLock->Exit();
+        }
+    }
+
+    ~XPCAutoUnlock()
+    {
+        if (mLock)
+            mLock->Enter();
+    }
+
+private:
+    XPCLock*  mLock;
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+
+    // Not meant to be implemented. This makes it a compiler error to
+    // construct or assign an XPCAutoUnlock object incorrectly.
+    XPCAutoUnlock(void) {}
+    XPCAutoUnlock(XPCAutoUnlock& /*aMon*/) {}
+    XPCAutoUnlock& operator =(XPCAutoUnlock& /*aMon*/) {
+        return *this;
+    }
+
+    // Not meant to be implemented. This makes it a compiler error to
+    // attempt to create an XPCAutoUnlock object on the heap.
+    static void* operator new(size_t /*size*/) CPP_THROW_NEW {
+        return nullptr;
+    }
+    static void operator delete(void* /*memory*/) {}
+};
+
 /***************************************************************************
 ****************************************************************************
 *
 * Core runtime and context classes...
 *
 ****************************************************************************
 ***************************************************************************/
 
@@ -386,18 +502,18 @@ public:
 
     ~XPCRootSetElem()
     {
         MOZ_ASSERT(!mNext, "Must be unlinked");
         MOZ_ASSERT(!mSelfp, "Must be unlinked");
     }
 
     inline XPCRootSetElem* GetNextRoot() { return mNext; }
-    void AddToRootSet(XPCRootSetElem **listHead);
-    void RemoveFromRootSet();
+    void AddToRootSet(XPCLock *lock, XPCRootSetElem **listHead);
+    void RemoveFromRootSet(XPCLock *lock);
 
 private:
     XPCRootSetElem *mNext;
     XPCRootSetElem **mSelfp;
 };
 
 /***************************************************************************/
 
@@ -489,16 +605,18 @@ public:
         {return mNativeScriptableSharedMap;}
 
     XPCWrappedNativeProtoMap* GetDyingWrappedNativeProtoMap() const
         {return mDyingWrappedNativeProtoMap;}
 
     XPCWrappedNativeProtoMap* GetDetachedWrappedNativeProtoMap() const
         {return mDetachedWrappedNativeProtoMap;}
 
+    XPCLock* GetMapLock() const {return mMapLock;}
+
     bool OnJSContextNew(JSContext* cx);
 
     virtual bool
     DescribeCustomObjects(JSObject* aObject, const js::Class* aClasp,
                           char (&aName)[72]) const MOZ_OVERRIDE;
     virtual bool
     NoteCustomGCThingXPCOMChildren(const js::Class* aClasp, JSObject* aObj,
                                    nsCycleCollectionTraversalCallback& aCb) const MOZ_OVERRIDE;
@@ -576,17 +694,17 @@ public:
 
     static void SuspectWrappedNative(XPCWrappedNative *wrapper,
                                      nsCycleCollectionNoteRootCallback &cb);
 
     void DebugDump(int16_t depth);
 
     void SystemIsBeingShutDown();
 
-    bool GCIsRunning() const {return mGCIsRunning;}
+    PRThread* GetThreadRunningGC() const {return mThreadRunningGC;}
 
     ~XPCJSRuntime();
 
     XPCReadableJSStringWrapper *NewStringWrapper(const PRUnichar *str, uint32_t len);
     void DeleteString(nsAString *string);
 
     void AddGCCallback(xpcGCCallback cb);
     void RemoveGCCallback(xpcGCCallback cb);
@@ -628,17 +746,18 @@ private:
     IID2WrappedJSClassMap*   mWrappedJSClassMap;
     IID2NativeInterfaceMap*  mIID2NativeInterfaceMap;
     ClassInfo2NativeSetMap*  mClassInfo2NativeSetMap;
     NativeSetMap*            mNativeSetMap;
     IID2ThisTranslatorMap*   mThisTranslatorMap;
     XPCNativeScriptableSharedMap* mNativeScriptableSharedMap;
     XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
     XPCWrappedNativeProtoMap* mDetachedWrappedNativeProtoMap;
-    bool mGCIsRunning;
+    XPCLock* mMapLock;
+    PRThread* mThreadRunningGC;
     nsTArray<nsXPCWrappedJS*> mWrappedJSToReleaseArray;
     nsTArray<nsISupports*> mNativesToReleaseArray;
     bool mDoingFinalization;
     XPCRootSetElem *mVariantRoots;
     XPCRootSetElem *mWrappedJSRoots;
     XPCRootSetElem *mObjectHolderRoots;
     nsTArray<xpcGCCallback> extraGCCallbacks;
     nsTArray<xpcContextCallback> extraContextCallbacks;
@@ -1065,17 +1184,20 @@ public:
 
     XPCJSRuntime*
     GetRuntime() const {return XPCJSRuntime::Get();}
 
     Native2WrappedNativeMap*
     GetWrappedNativeMap() const {return mWrappedNativeMap;}
 
     ClassInfo2WrappedNativeProtoMap*
-    GetWrappedNativeProtoMap() const {return mWrappedNativeProtoMap;}
+    GetWrappedNativeProtoMap(bool aMainThreadOnly) const
+        {return aMainThreadOnly ?
+                mMainThreadWrappedNativeProtoMap :
+                mWrappedNativeProtoMap;}
 
     nsXPCComponents*
     GetComponents() const {return mComponents;}
 
     // Returns the JS object reflection of the Components object.
     JSObject*
     GetComponentsJSObject();
 
@@ -1195,16 +1317,17 @@ protected:
 
 private:
     static XPCWrappedNativeScope* gScopes;
     static XPCWrappedNativeScope* gDyingScopes;
 
     XPCJSRuntime*                    mRuntime;
     Native2WrappedNativeMap*         mWrappedNativeMap;
     ClassInfo2WrappedNativeProtoMap* mWrappedNativeProtoMap;
+    ClassInfo2WrappedNativeProtoMap* mMainThreadWrappedNativeProtoMap;
     nsRefPtr<nsXPCComponents>        mComponents;
     XPCWrappedNativeScope*           mNext;
     // The JS global object for this scope.  If non-null, this will be the
     // default parent for the XPCWrappedNatives that have us as the scope,
     // unless a PreCreate hook overrides it.  Note that this _may_ be null (see
     // constructor).
     JS::ObjectPtr                    mGlobalJSObject;
 
@@ -1832,22 +1955,26 @@ public:
     GetClassInfoFlags() const {return mClassInfoFlags;}
 
 #ifdef GET_IT
 #undef GET_IT
 #endif
 #define GET_IT(f_) const {return !!(mClassInfoFlags & nsIClassInfo:: f_ );}
 
     bool ClassIsSingleton()           GET_IT(SINGLETON)
+    bool ClassIsThreadSafe()          GET_IT(THREADSAFE)
     bool ClassIsMainThreadOnly()      GET_IT(MAIN_THREAD_ONLY)
     bool ClassIsDOMObject()           GET_IT(DOM_OBJECT)
     bool ClassIsPluginObject()        GET_IT(PLUGIN_OBJECT)
 
 #undef GET_IT
 
+    XPCLock* GetLock() const
+        {return ClassIsThreadSafe() ? GetRuntime()->GetMapLock() : nullptr;}
+
     void SetScriptableInfo(XPCNativeScriptableInfo* si)
         {MOZ_ASSERT(!mScriptableInfo, "leak here!"); mScriptableInfo = si;}
 
     bool CallPostCreatePrototype();
     void JSProtoObjectFinalized(js::FreeOp *fop, JSObject *obj);
 
     void SystemIsBeingShutDown();
 
@@ -2090,21 +2217,25 @@ public:
      *
      * 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*
     GetFlatJSObjectPreserveColor() const {return mFlatJSObject;}
 
+    XPCLock*
+    GetLock() const {return IsValid() && HasProto() ?
+                                GetProto()->GetLock() : nullptr;}
+
     XPCNativeSet*
-    GetSet() const {return mSet;}
+    GetSet() const {XPCAutoLock al(GetLock()); return mSet;}
 
     void
-    SetSet(XPCNativeSet* set) {mSet = set;}
+    SetSet(XPCNativeSet* set) {XPCAutoLock al(GetLock()); mSet = set;}
 
     static XPCWrappedNative* Get(JSObject *obj) {
         MOZ_ASSERT(IS_WN_REFLECTOR(obj));
         return (XPCWrappedNative*)js::GetObjectPrivate(obj);
     }
 
 private:
     inline void