Bug 1166351 - Clear XBL class object maps on XUL prototype cache flush. r?bholley draft
authorMike Conley <mconley@mozilla.com>
Mon, 02 May 2016 12:14:11 -0700
changeset 363655 63f331a97bcf34fd6404538aa27e03a8e1d46c2f
parent 363654 c0132faa89512ee84408feb0d6599dbff905249a
child 520103 a08883c9506da78aed4e87dea45a3e9e6f71430c
push id17273
push usermconley@mozilla.com
push dateThu, 05 May 2016 08:04:34 +0000
reviewersbholley
bugs1166351
milestone48.0a1
Bug 1166351 - Clear XBL class object maps on XUL prototype cache flush. r?bholley MozReview-Commit-ID: AmSzVJ2w6Pj
dom/xbl/nsXBLBinding.cpp
dom/xul/nsXULPrototypeCache.cpp
js/xpconnect/src/XPCWrappedNativeScope.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/src/xpcpublic.h
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -890,16 +890,19 @@ GetOrCreateClassObjectMap(JSContext *cx,
   // We'd make this property permanent, except that we need to be
   // able to clear the WeakMap when the XUL cache gets flushed.
   if (!map || !JS_DefineProperty(cx, scope, mapName, map,
                                  JSPROP_READONLY, JS_STUBGETTER,
                                  JS_STUBSETTER))
   {
     return nullptr;
   }
+
+  xpc::SetHasXBLClassObjectMaps(js::GetObjectCompartment(scope));
+
   return map;
 }
 
 static void
 ClearClassObjectMap(JSContext *cx, JS::Handle<JSObject*> scope, const char* name)
 {
   AssertSameCompartment(cx, scope);
   MOZ_ASSERT(scope == xpc::GetXBLScopeOrGlobal(cx, scope));
--- a/dom/xul/nsXULPrototypeCache.cpp
+++ b/dom/xul/nsXULPrototypeCache.cpp
@@ -23,16 +23,17 @@
 
 #include "js/TracingAPI.h"
 
 #include "mozilla/CSSStyleSheet.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/scache/StartupCache.h"
 #include "mozilla/scache/StartupCacheUtils.h"
 #include "mozilla/Telemetry.h"
+#include "xpcpublic.h"
 
 using namespace mozilla;
 using namespace mozilla::scache;
 
 static bool gDisableXULCache = false; // enabled by default
 static const char kDisableXULCachePref[] = "nglayout.debug.disable_xul_cache";
 static const char kXULCacheInfoKey[] = "nsXULPrototypeCache.startupCache";
 static const char kXULCachePrefix[] = "xulcache";
@@ -278,16 +279,17 @@ nsXULPrototypeCache::FlushScripts()
 
 void
 nsXULPrototypeCache::Flush()
 {
     mPrototypeTable.Clear();
     mScriptTable.Clear();
     mStyleSheetTable.Clear();
     mXBLDocTable.Clear();
+    xpc::ClearClassMapsForAllXBLScopes();
 }
 
 
 bool
 nsXULPrototypeCache::IsEnabled()
 {
     return !gDisableXULCache;
 }
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -12,16 +12,17 @@
 #include "nsCycleCollectionNoteRootCallback.h"
 #include "nsPrincipal.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "nsIAddonInterposition.h"
 #include "nsIXULRuntime.h"
 
 #include "mozilla/dom/BindingUtils.h"
+#include "nsXBLBinding.h"
 
 using namespace mozilla;
 using namespace xpc;
 using namespace JS;
 
 /***************************************************************************/
 
 XPCWrappedNativeScope* XPCWrappedNativeScope::gScopes = nullptr;
@@ -86,17 +87,18 @@ XPCWrappedNativeScope::XPCWrappedNativeS
                                              JS::HandleObject aGlobal)
       : mWrappedNativeMap(Native2WrappedNativeMap::newMap(XPC_NATIVE_MAP_LENGTH)),
         mWrappedNativeProtoMap(ClassInfo2WrappedNativeProtoMap::newMap(XPC_NATIVE_PROTO_MAP_LENGTH)),
         mComponents(nullptr),
         mNext(nullptr),
         mGlobalJSObject(aGlobal),
         mHasCallInterpositions(false),
         mIsContentXBLScope(false),
-        mIsAddonScope(false)
+        mIsAddonScope(false),
+        mHasXBLClassObjectMaps(false)
 {
     // add ourselves to the scopes list
     {
         MOZ_ASSERT(aGlobal);
         DebugOnly<const js::Class*> clasp = js::GetObjectClass(aGlobal);
         MOZ_ASSERT(clasp->flags & (JSCLASS_PRIVATE_IS_NSISUPPORTS |
                                    JSCLASS_HAS_PRIVATE) ||
                    mozilla::dom::IsDOMClass(clasp));
@@ -154,16 +156,32 @@ XPCWrappedNativeScope::XPCWrappedNativeS
             MOZ_ASSERT(mInterposition);
             UpdateInterpositionWhitelist(cx, mInterposition);
           }
         }
     }
 }
 
 // static
+void
+XPCWrappedNativeScope::ClearClassMapsForAllXBLScopes()
+{
+    for (XPCWrappedNativeScope* cur = gScopes; cur; cur = cur->mNext) {
+        if (cur->mHasXBLClassObjectMaps) {
+            AutoJSAPI jsapi;
+            jsapi.Init(cur->mGlobalJSObject);
+            JSContext* cx = jsapi.cx();
+            JS::Rooted<JSObject*> xblscope(cx, cur->mGlobalJSObject);
+            nsXBLBinding::ClearClassObjectMaps(cx, xblscope);
+            cur->mHasXBLClassObjectMaps = false;
+         }
+    }
+}
+
+// static
 bool
 XPCWrappedNativeScope::IsDyingScope(XPCWrappedNativeScope* scope)
 {
     for (XPCWrappedNativeScope* cur = gDyingScopes; cur; cur = cur->mNext) {
         if (scope == cur)
             return true;
     }
     return false;
@@ -359,16 +377,23 @@ AllowContentXBLScope(JSCompartment* c)
 
 bool
 UseContentXBLScope(JSCompartment* c)
 {
   XPCWrappedNativeScope* scope = CompartmentPrivate::Get(c)->scope;
   return scope && scope->UseContentXBLScope();
 }
 
+void
+SetHasXBLClassObjectMaps(JSCompartment* c)
+{
+  XPCWrappedNativeScope* scope = CompartmentPrivate::Get(c)->scope;
+  scope->SetHasXBLClassObjectMaps();
+}
+
 } /* namespace xpc */
 
 JSObject*
 XPCWrappedNativeScope::EnsureAddonScope(JSContext* cx, JSAddonId* addonId)
 {
     JS::RootedObject global(cx, GetGlobalJSObject());
     MOZ_ASSERT(js::IsObjectInContextCompartment(global, cx));
     MOZ_ASSERT(!mIsContentXBLScope);
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -1314,16 +1314,22 @@ SetAddonInterposition(const nsACString& 
     AutoJSAPI jsapi;
     jsapi.Init(xpc::PrivilegedJunkScope());
     addonId = NewAddonId(jsapi.cx(), addonIdStr);
     if (!addonId)
         return false;
     return XPCWrappedNativeScope::SetAddonInterposition(jsapi.cx(), addonId, interposition);
 }
 
+void
+ClearClassMapsForAllXBLScopes()
+{
+  XPCWrappedNativeScope::ClearClassMapsForAllXBLScopes();
+}
+
 } // namespace xpc
 
 namespace mozilla {
 namespace dom {
 
 bool
 IsChromeOrXBL(JSContext* cx, JSObject* /* unused */)
 {
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -1074,16 +1074,19 @@ public:
     AddSizeOfAllScopesIncludingThis(ScopeSizeInfo* scopeSizeInfo);
 
     void
     AddSizeOfIncludingThis(ScopeSizeInfo* scopeSizeInfo);
 
     bool
     IsValid() const {return mRuntime != nullptr;}
 
+    static void
+    ClearClassMapsForAllXBLScopes();
+
     static bool
     IsDyingScope(XPCWrappedNativeScope* scope);
 
     typedef js::HashSet<JS::Heap<JSObject*>,
                         js::MovableCellHasher<JS::Heap<JSObject*>>,
                         js::SystemAllocPolicy> DOMExpandoSet;
 
     bool RegisterDOMExpandoObject(JSObject* expando) {
@@ -1134,16 +1137,17 @@ public:
                                       nsIAddonInterposition* interp);
 
     static InterpositionWhitelist* GetInterpositionWhitelist(nsIAddonInterposition* interposition);
     static bool UpdateInterpositionWhitelist(JSContext* cx,
                                              nsIAddonInterposition* interposition);
 
     void SetAddonCallInterposition() { mHasCallInterpositions = true; }
     bool HasCallInterposition() { return mHasCallInterpositions; };
+    void SetHasXBLClassObjectMaps() { mHasXBLClassObjectMaps = true; }
 
 protected:
     virtual ~XPCWrappedNativeScope();
 
     XPCWrappedNativeScope(); // not implemented
 
 private:
     class ClearInterpositionsObserver final : public nsIObserver {
@@ -1203,16 +1207,17 @@ private:
     // or not.
     //
     // This distinction is useful primarily because, if true, we know that we
     // have no way of distinguishing XBL script from content script for the
     // given scope. In these (unsupported) situations, we just always claim to
     // be XBL.
     bool mAllowContentXBLScope;
     bool mUseContentXBLScope;
+    bool mHasXBLClassObjectMaps;
 };
 
 /***************************************************************************/
 // Slots we use for our functions
 #define XPC_FUNCTION_NATIVE_MEMBER_SLOT 0
 #define XPC_FUNCTION_PARENT_OBJECT_SLOT 1
 
 /***************************************************************************/
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -116,16 +116,19 @@ AllowContentXBLScope(JSCompartment* c);
 
 // Returns whether we will use an XBL scope for this compartment. This is
 // semantically equivalent to comparing global != GetXBLScope(global), but it
 // does not have the side-effect of eagerly creating the XBL scope if it does
 // not already exist.
 bool
 UseContentXBLScope(JSCompartment* c);
 
+void
+SetHasXBLClassObjectMaps(JSCompartment* c);
+
 bool
 IsInAddonScope(JSObject* obj);
 
 JSObject*
 GetAddonScope(JSContext* cx, JS::HandleObject contentScope, JSAddonId* addonId);
 
 bool
 IsSandboxPrototypeProxy(JSObject* obj);
@@ -488,16 +491,19 @@ bool
 ShouldDiscardSystemSource();
 
 bool
 SharedMemoryEnabled();
 
 bool
 SetAddonInterposition(const nsACString& addonId, nsIAddonInterposition* interposition);
 
+void
+ClearClassMapsForAllXBLScopes();
+
 bool
 ExtraWarningsForSystemJS();
 
 class ErrorReport {
   public:
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ErrorReport);
 
     ErrorReport() : mWindowID(0)