Bug 990729 - Add compartmentPerAddon option for XUL overlays (r=bholley)

This commit is contained in:
Bill McCloskey 2014-06-21 11:55:15 -07:00
parent 1ddea0f108
commit 78c2618e3d
8 changed files with 126 additions and 4 deletions

View File

@ -82,6 +82,7 @@
#include "nsXULPopupManager.h"
#include "nsCCUncollectableMarker.h"
#include "nsURILoader.h"
#include "mozilla/AddonPathService.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/NodeInfoInlines.h"
@ -3691,9 +3692,14 @@ XULDocument::ExecuteScript(nsIScriptContext * aContext,
nsAutoMicroTask mt;
JSContext *cx = aContext->GetNativeContext();
AutoCxPusher pusher(cx);
JS::Rooted<JSObject*> global(cx, mScriptGlobalObject->GetGlobalJSObject());
JS::Rooted<JSObject*> baseGlobal(cx, mScriptGlobalObject->GetGlobalJSObject());
NS_ENSURE_TRUE(baseGlobal, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(nsContentUtils::GetSecurityManager()->ScriptAllowed(baseGlobal), NS_OK);
JSAddonId *addonId = MapURIToAddonID(mCurrentPrototype->GetURI());
JS::Rooted<JSObject*> global(cx, xpc::GetAddonScope(cx, baseGlobal, addonId));
NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(nsContentUtils::GetSecurityManager()->ScriptAllowed(global), NS_OK);
JS::ExposeObjectToActiveJS(global);
xpc_UnmarkGrayScript(aScriptObject);
JSAutoCompartment ac(cx, global);

View File

@ -6,6 +6,7 @@
// Microsoft's API Name hackery sucks
#undef CreateEvent
#include "mozilla/AddonPathService.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/EventDispatcher.h"
@ -842,6 +843,8 @@ EventListenerManager::CompileEventHandlerInternal(Listener* aListener,
typeAtom, win,
&argCount, &argNames);
JSAddonId *addonId = MapURIToAddonID(uri);
// Wrap the event target, so that we can use it as the scope for the event
// handler. Note that mTarget is different from aElement in the <body> case,
// where mTarget is a Window.
@ -858,6 +861,17 @@ EventListenerManager::CompileEventHandlerInternal(Listener* aListener,
return rv;
}
}
if (addonId) {
JS::Rooted<JSObject*> vObj(cx, &v.toObject());
JS::Rooted<JSObject*> addonScope(cx, xpc::GetAddonScope(cx, vObj, addonId));
if (!addonScope) {
return NS_ERROR_FAILURE;
}
JSAutoCompartment ac(cx, addonScope);
if (!JS_WrapValue(cx, &v)) {
return NS_ERROR_FAILURE;
}
}
JS::Rooted<JSObject*> target(cx, &v.toObject());
JSAutoCompartment ac(cx, target);

View File

@ -44,6 +44,7 @@
#include "nsCxPusher.h"
#include "WrapperFactory.h"
#include "mozilla/AddonPathService.h"
#include "mozilla/scache/StartupCache.h"
#include "mozilla/scache/StartupCacheUtils.h"
#include "mozilla/MacroForEach.h"
@ -682,7 +683,9 @@ mozJSComponentLoader::PrepareObjectForLocation(JSCLContextHelper& aCx,
CompartmentOptions options;
options.setZone(SystemZone)
.setVersion(JSVERSION_LATEST);
.setVersion(JSVERSION_LATEST)
.setAddonId(aReuseLoaderGlobal ? nullptr : MapURIToAddonID(aURI));
// Defer firing OnNewGlobalObject until after the __URI__ property has
// been defined so the JS debugger can tell what module the global is
// for

View File

@ -522,6 +522,14 @@ IsInContentXBLScope(JSObject *obj)
return IsContentXBLScope(js::GetObjectCompartment(obj));
}
bool
IsInAddonScope(JSObject *obj)
{
// We always eagerly create compartment privates for addon scopes.
XPCWrappedNativeScope *scope = GetObjectScope(obj);
return scope && scope->IsAddonScope();
}
bool
IsUniversalXPConnectEnabled(JSCompartment *compartment)
{

View File

@ -73,7 +73,8 @@ XPCWrappedNativeScope::XPCWrappedNativeScope(JSContext *cx,
mComponents(nullptr),
mNext(nullptr),
mGlobalJSObject(aGlobal),
mIsContentXBLScope(false)
mIsContentXBLScope(false),
mIsAddonScope(false)
{
// add ourselves to the scopes list
{
@ -185,6 +186,20 @@ XPCWrappedNativeScope::AttachComponentsObject(JSContext* aCx)
JSPROP_PERMANENT | JSPROP_READONLY);
}
static bool
CompartmentPerAddon()
{
static bool initialized = false;
static bool pref = false;
if (!initialized) {
pref = Preferences::GetBool("dom.compartment_per_addon", false);
initialized = true;
}
return pref;
}
JSObject*
XPCWrappedNativeScope::EnsureContentXBLScope(JSContext *cx)
{
@ -249,6 +264,8 @@ XPCWrappedNativeScope::AllowContentXBLScope()
namespace xpc {
JSObject *GetXBLScope(JSContext *cx, JSObject *contentScopeArg)
{
MOZ_ASSERT(!IsInAddonScope(contentScopeArg));
JS::RootedObject contentScope(cx, contentScopeArg);
JSAutoCompartment ac(cx, contentScope);
JSObject *scope = EnsureCompartmentPrivate(contentScope)->scope->EnsureContentXBLScope(cx);
@ -274,6 +291,57 @@ UseContentXBLScope(JSCompartment *c)
} /* namespace xpc */
JSObject*
XPCWrappedNativeScope::EnsureAddonScope(JSContext *cx, JSAddonId *addonId)
{
JS::RootedObject global(cx, GetGlobalJSObject());
MOZ_ASSERT(js::IsObjectInContextCompartment(global, cx));
MOZ_ASSERT(!mIsContentXBLScope);
MOZ_ASSERT(!mIsAddonScope);
MOZ_ASSERT(addonId);
MOZ_ASSERT(nsContentUtils::IsSystemPrincipal(GetPrincipal()));
// If we already have an addon scope object, we know what to use.
for (size_t i = 0; i < mAddonScopes.Length(); i++) {
if (JS::AddonIdOfObject(js::UncheckedUnwrap(mAddonScopes[i])) == addonId)
return mAddonScopes[i];
}
SandboxOptions options;
options.wantComponents = true;
options.proto = global;
options.sameZoneAs = global;
options.addonId = JS::StringOfAddonId(addonId);
options.writeToGlobalPrototype = true;
RootedValue v(cx);
nsresult rv = CreateSandboxObject(cx, &v, GetPrincipal(), options);
NS_ENSURE_SUCCESS(rv, nullptr);
mAddonScopes.AppendElement(&v.toObject());
EnsureCompartmentPrivate(js::UncheckedUnwrap(&v.toObject()))->scope->mIsAddonScope = true;
return &v.toObject();
}
JSObject *
xpc::GetAddonScope(JSContext *cx, JS::HandleObject contentScope, JSAddonId *addonId)
{
MOZ_RELEASE_ASSERT(!IsInAddonScope(contentScope));
if (!addonId || !CompartmentPerAddon()) {
return js::GetGlobalForObjectCrossCompartment(contentScope);
}
JSAutoCompartment ac(cx, contentScope);
XPCWrappedNativeScope *nativeScope = EnsureCompartmentPrivate(contentScope)->scope;
JSObject *scope = nativeScope->EnsureAddonScope(cx, addonId);
NS_ENSURE_TRUE(scope, nullptr);
scope = js::UncheckedUnwrap(scope);
JS::ExposeObjectToActiveJS(scope);
return scope;
}
XPCWrappedNativeScope::~XPCWrappedNativeScope()
{
MOZ_COUNT_DTOR(XPCWrappedNativeScope);
@ -304,6 +372,8 @@ XPCWrappedNativeScope::~XPCWrappedNativeScope()
JSRuntime *rt = XPCJSRuntime::Get()->Runtime();
mContentXBLScope.finalize(rt);
for (size_t i = 0; i < mAddonScopes.Length(); i++)
mAddonScopes[i].finalize(rt);
mGlobalJSObject.finalize(rt);
}

View File

@ -1024,6 +1024,8 @@ public:
mGlobalJSObject.trace(trc, "XPCWrappedNativeScope::mGlobalJSObject");
if (mContentXBLScope)
mContentXBLScope.trace(trc, "XPCWrappedNativeScope::mXBLScope");
for (size_t i = 0; i < mAddonScopes.Length(); i++)
mAddonScopes[i].trace(trc, "XPCWrappedNativeScope::mAddonScopes");
if (mXrayExpandos.initialized())
mXrayExpandos.trace(trc);
}
@ -1101,6 +1103,8 @@ public:
// object is wrapped into the compartment of the global.
JSObject *EnsureContentXBLScope(JSContext *cx);
JSObject *EnsureAddonScope(JSContext *cx, JSAddonId *addonId);
XPCWrappedNativeScope(JSContext *cx, JS::HandleObject aGlobal);
nsAutoPtr<JSObject2JSObjectMap> mWaiverWrapperMap;
@ -1109,6 +1113,8 @@ public:
bool AllowContentXBLScope();
bool UseContentXBLScope() { return mUseContentXBLScope; }
bool IsAddonScope() { return mIsAddonScope; }
protected:
virtual ~XPCWrappedNativeScope();
@ -1136,11 +1142,15 @@ private:
// This reference is wrapped into the compartment of mGlobalJSObject.
JS::ObjectPtr mContentXBLScope;
// Lazily created sandboxes for addon code.
nsTArray<JS::ObjectPtr> mAddonScopes;
nsAutoPtr<DOMExpandoSet> mDOMExpandoSet;
JS::WeakMapPtr<JSObject*, JSObject*> mXrayExpandos;
bool mIsContentXBLScope;
bool mIsAddonScope;
// For remote XUL domains, we run all XBL in the content scope for compat
// reasons (though we sometimes pref this off for automation). We separately

View File

@ -109,6 +109,12 @@ AllowContentXBLScope(JSCompartment *c);
bool
UseContentXBLScope(JSCompartment *c);
bool
IsInAddonScope(JSObject *obj);
JSObject *
GetAddonScope(JSContext *cx, JS::HandleObject contentScope, JSAddonId *addonId);
bool
IsSandboxPrototypeProxy(JSObject *obj);

View File

@ -138,6 +138,11 @@ pref("dom.webcrypto.enabled", true);
// Whether the UndoManager API is enabled
pref("dom.undo_manager.enabled", false);
// Whether to run add-on code in different compartments from browser code. This
// causes a separate compartment for each (addon, global) combination, which may
// significantly increase the number of compartments in the system.
pref("dom.compartment_per_addon", false);
// Fastback caching - if this pref is negative, then we calculate the number
// of content viewers to cache based on the amount of available memory.
pref("browser.sessionhistory.max_total_viewers", -1);