Bug 1017310 - Associate JS compartments with add-on chrome XBL (r=bholley)

This commit is contained in:
Bill McCloskey 2014-06-23 16:33:37 -07:00
Родитель f0e84d2f79
Коммит 64b94c4b6d
12 изменённых файлов: 83 добавлений и 15 удалений

Просмотреть файл

@ -1404,7 +1404,8 @@ WrapNativeParent(JSContext* cx, T* p, nsWrapperCache* cache,
}
// If useXBLScope is true, it means that the canonical reflector for this
// native object should live in the XBL scope.
// native object should live in the content XBL scope. Note that we never put
// anonymous content inside an add-on scope.
if (xpc::IsInContentXBLScope(parent)) {
return parent;
}

Просмотреть файл

@ -639,7 +639,8 @@ nsBindingManager::GetBindingImplementation(nsIContent* aContent, REFNSIID aIID,
// If we're using an XBL scope, we need to use the Xray view to the bound
// content in order to view the full array of methods defined in the
// binding, some of which may not be exposed on the prototype of
// untrusted content.
// untrusted content. We don't need to consider add-on scopes here
// because they're chrome-only and no Xrays are involved.
//
// If there's no separate XBL scope, or if the reflector itself lives in
// the XBL scope, we'll end up with the global of the reflector, and this

Просмотреть файл

@ -924,7 +924,8 @@ GetOrCreateMapEntryForPrototype(JSContext *cx, JS::Handle<JSObject*> proto)
: "__XBLClassObjectMap__";
// Now, enter the XBL scope, since that's where we need to operate, and wrap
// the proto accordingly.
// the proto accordingly. We hang the map off of the content XBL scope for
// content, and the Window for chrome (whether add-ons are involved or not).
JS::Rooted<JSObject*> scope(cx, xpc::GetXBLScopeOrGlobal(cx, proto));
JS::Rooted<JSObject*> wrappedProto(cx, proto);
JSAutoCompartment ac(cx, scope);
@ -980,6 +981,8 @@ nsXBLBinding::DoInitJSClass(JSContext *cx,
// but we need to make sure never to assume that the the reflector and
// prototype are same-compartment with the bound document.
JS::Rooted<JSObject*> global(cx, js::GetGlobalForObjectCrossCompartment(obj));
// We never store class objects in add-on scopes.
JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScopeOrGlobal(cx, global));
JS::Rooted<JSObject*> parent_proto(cx);
@ -1117,8 +1120,12 @@ nsXBLBinding::LookupMember(JSContext* aCx, JS::Handle<jsid> aId,
// never get here. But on the off-chance that someone adds new callsites to
// LookupMember, we do a release-mode assertion as belt-and-braces.
// We do a release-mode assertion here to be extra safe.
//
// This code is only called for content XBL, so we don't have to worry about
// add-on scopes here.
JS::Rooted<JSObject*> boundScope(aCx,
js::GetGlobalForObjectCrossCompartment(mBoundElement->GetWrapper()));
MOZ_RELEASE_ASSERT(!xpc::IsInAddonScope(boundScope));
MOZ_RELEASE_ASSERT(!xpc::IsInContentXBLScope(boundScope));
JS::Rooted<JSObject*> xblScope(aCx, xpc::GetXBLScope(aCx, boundScope));
NS_ENSURE_TRUE(xblScope, false);

Просмотреть файл

@ -18,6 +18,7 @@
#include "nsXBLPrototypeBinding.h"
#include "nsXBLProtoImplProperty.h"
#include "nsIURI.h"
#include "mozilla/AddonPathService.h"
#include "mozilla/dom/XULElementBinding.h"
#include "xpcpublic.h"
#include "js/CharacterEncoding.h"
@ -75,9 +76,10 @@ nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aPrototypeBinding,
// First, start by entering the compartment of the XBL scope. This may or may
// not be the same compartment as globalObject.
JSAddonId* addonId = MapURIToAddonID(aPrototypeBinding->BindingURI());
JS::Rooted<JSObject*> globalObject(cx,
GetGlobalForObjectCrossCompartment(targetClassObject));
JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScopeOrGlobal(cx, globalObject));
JS::Rooted<JSObject*> scopeObject(cx, xpc::GetScopeForXBLExecution(cx, globalObject, addonId));
NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
JSAutoCompartment ac(cx, scopeObject);
@ -132,13 +134,25 @@ nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aPrototypeBinding,
if (propertyHolder != targetClassObject) {
AssertSameCompartment(propertyHolder, scopeObject);
AssertSameCompartment(targetClassObject, globalObject);
bool inContentXBLScope = xpc::IsInContentXBLScope(scopeObject);
for (nsXBLProtoImplMember* curr = mMembers; curr; curr = curr->GetNext()) {
if (curr->ShouldExposeToUntrustedContent()) {
if (!inContentXBLScope || curr->ShouldExposeToUntrustedContent()) {
JS::Rooted<jsid> id(cx);
JS::TwoByteChars chars(curr->GetName(), NS_strlen(curr->GetName()));
bool ok = JS_CharsToId(cx, chars, &id);
NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
JS_CopyPropertyFrom(cx, id, targetClassObject, propertyHolder);
bool found;
ok = JS_HasPropertyById(cx, propertyHolder, id, &found);
NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
if (!found) {
// Some members don't install anything in InstallMember (e.g.,
// nsXBLProtoImplAnonymousMethod). We need to skip copying in
// those cases.
continue;
}
ok = JS_CopyPropertyFrom(cx, id, targetClassObject, propertyHolder);
NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
}
}

Просмотреть файл

@ -16,6 +16,7 @@
#include "nsIURI.h"
#include "nsXBLSerialize.h"
#include "nsXBLPrototypeBinding.h"
#include "mozilla/AddonPathService.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/ScriptSettings.h"
#include "nsGlobalWindow.h"
@ -411,9 +412,11 @@ nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
NS_ASSERTION(!::JS_IsExceptionPending(cx),
"Shouldn't get here when an exception is pending!");
JSAddonId* addonId = MapURIToAddonID(aBindingDocURI);
// First, enter the xbl scope, wrap the node, and use that as the scope for
// the evaluation.
JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScopeOrGlobal(cx, aBoundNode));
JS::Rooted<JSObject*> scopeObject(cx, xpc::GetScopeForXBLExecution(cx, aBoundNode, addonId));
NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
JSAutoCompartment ac(cx, scopeObject);

Просмотреть файл

@ -105,6 +105,7 @@ nsXBLProtoImplMethod::InstallMember(JSContext* aCx,
JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) ||
xpc::IsInAddonScope(globalObject) ||
globalObject == xpc::GetXBLScope(aCx, globalObject));
JS::Rooted<JSObject*> jsMethodObject(aCx, GetCompiledMethod());
@ -264,7 +265,7 @@ nsXBLProtoImplMethod::Write(nsIObjectOutputStream* aStream)
}
nsresult
nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement, JSAddonId* aAddonId)
{
NS_PRECONDITION(IsCompiled(), "Can't execute uncompiled method");
@ -308,7 +309,7 @@ nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
JS::Rooted<JSObject*> thisObject(cx, &v.toObject());
JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScopeOrGlobal(cx, globalObject));
JS::Rooted<JSObject*> scopeObject(cx, xpc::GetScopeForXBLExecution(cx, globalObject, aAddonId));
NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
JSAutoCompartment ac(cx, scopeObject);

Просмотреть файл

@ -139,7 +139,7 @@ public:
nsXBLProtoImplMethod(aName)
{}
nsresult Execute(nsIContent* aBoundElement);
nsresult Execute(nsIContent* aBoundElement, JSAddonId* aAddonId);
// Override InstallMember; these methods never get installed as members on
// binding instantiations (though they may hang out in mMembers on the

Просмотреть файл

@ -130,6 +130,7 @@ nsXBLProtoImplProperty::InstallMember(JSContext *aCx,
MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
MOZ_ASSERT(xpc::IsInContentXBLScope(globalObject) ||
xpc::IsInAddonScope(globalObject) ||
globalObject == xpc::GetXBLScope(aCx, globalObject));
JS::Rooted<JSObject*> getter(aCx, mGetter.GetJSFunction());

Просмотреть файл

@ -39,6 +39,7 @@
#include "nsCSSRuleProcessor.h"
#include "nsXBLResourceLoader.h"
#include "mozilla/AddonPathService.h"
#include "mozilla/dom/CDATASection.h"
#include "mozilla/dom/Comment.h"
#include "mozilla/dom/Element.h"
@ -249,7 +250,7 @@ nsXBLPrototypeBinding::BindingAttached(nsIContent* aBoundElement)
{
if (mImplementation && mImplementation->CompiledMembers() &&
mImplementation->mConstructor)
return mImplementation->mConstructor->Execute(aBoundElement);
return mImplementation->mConstructor->Execute(aBoundElement, MapURIToAddonID(mBindingURI));
return NS_OK;
}
@ -258,7 +259,7 @@ nsXBLPrototypeBinding::BindingDetached(nsIContent* aBoundElement)
{
if (mImplementation && mImplementation->CompiledMembers() &&
mImplementation->mDestructor)
return mImplementation->mDestructor->Execute(aBoundElement);
return mImplementation->mDestructor->Execute(aBoundElement, MapURIToAddonID(mBindingURI));
return NS_OK;
}

Просмотреть файл

@ -35,6 +35,7 @@
#include "nsGkAtoms.h"
#include "nsIXPConnect.h"
#include "nsIDOMScriptObjectFactory.h"
#include "mozilla/AddonPathService.h"
#include "nsDOMCID.h"
#include "nsUnicharUtils.h"
#include "nsCRT.h"
@ -282,8 +283,10 @@ nsXBLPrototypeHandler::ExecuteHandler(EventTarget* aTarget,
rv = EnsureEventHandler(boundGlobal, boundContext, onEventAtom, &handler);
NS_ENSURE_SUCCESS(rv, rv);
JSAddonId* addonId = MapURIToAddonID(mPrototypeBinding->DocURI());
JS::Rooted<JSObject*> globalObject(cx, boundGlobal->GetGlobalJSObject());
JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScopeOrGlobal(cx, globalObject));
JS::Rooted<JSObject*> scopeObject(cx, xpc::GetScopeForXBLExecution(cx, globalObject, addonId));
NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
// Bind it to the bound element. Note that if we're using a separate XBL scope,
@ -358,8 +361,10 @@ nsXBLPrototypeHandler::EnsureEventHandler(nsIScriptGlobalObject* aGlobal,
nsDependentString handlerText(mHandlerText);
NS_ENSURE_TRUE(!handlerText.IsEmpty(), NS_ERROR_FAILURE);
JSAddonId* addonId = MapURIToAddonID(mPrototypeBinding->DocURI());
JS::Rooted<JSObject*> globalObject(cx, aGlobal->GetGlobalJSObject());
JS::Rooted<JSObject*> scopeObject(cx, xpc::GetXBLScopeOrGlobal(cx, globalObject));
JS::Rooted<JSObject*> scopeObject(cx, xpc::GetScopeForXBLExecution(cx, globalObject, addonId));
NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
nsAutoCString bindingURI;

Просмотреть файл

@ -262,7 +262,8 @@ XPCWrappedNativeScope::AllowContentXBLScope()
}
namespace xpc {
JSObject *GetXBLScope(JSContext *cx, JSObject *contentScopeArg)
JSObject *
GetXBLScope(JSContext *cx, JSObject *contentScopeArg)
{
MOZ_ASSERT(!IsInAddonScope(contentScopeArg));
@ -275,6 +276,32 @@ JSObject *GetXBLScope(JSContext *cx, JSObject *contentScopeArg)
return scope;
}
JSObject *
GetScopeForXBLExecution(JSContext *cx, HandleObject contentScope, JSAddonId *addonId)
{
MOZ_RELEASE_ASSERT(!IsInAddonScope(contentScope));
RootedObject global(cx, js::GetGlobalForObjectCrossCompartment(contentScope));
if (IsInContentXBLScope(contentScope))
return global;
JSAutoCompartment ac(cx, contentScope);
XPCWrappedNativeScope *nativeScope = EnsureCompartmentPrivate(contentScope)->scope;
RootedObject scope(cx);
if (nativeScope->UseContentXBLScope())
scope = nativeScope->EnsureContentXBLScope(cx);
else if (addonId && CompartmentPerAddon())
scope = nativeScope->EnsureAddonScope(cx, addonId);
else
scope = global;
NS_ENSURE_TRUE(scope, nullptr); // See bug 858642.
scope = js::UncheckedUnwrap(scope);
JS::ExposeObjectToActiveJS(scope);
return scope;
}
bool
AllowContentXBLScope(JSCompartment *c)
{

Просмотреть файл

@ -97,6 +97,13 @@ GetXBLScopeOrGlobal(JSContext *cx, JSObject *obj)
return GetXBLScope(cx, obj);
}
// This function is similar to GetXBLScopeOrGlobal. However, if |obj| is a
// chrome scope, then it will return an add-on scope if addonId is non-null.
// Like GetXBLScopeOrGlobal, it returns the scope of |obj| if it's already a
// content XBL scope. But it asserts that |obj| is not an add-on scope.
JSObject *
GetScopeForXBLExecution(JSContext *cx, JS::HandleObject obj, JSAddonId *addonId);
// Returns whether XBL scopes have been explicitly disabled for code running
// in this compartment. See the comment around mAllowContentXBLScope.
bool