Bug 1166351 - Clear XBL class object maps on XUL prototype cache flush. r?bholley
MozReview-Commit-ID: AmSzVJ2w6Pj
--- 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)