Bug 707717. Don't dynamically mutate the proto chains of DOM prototypes. r=peterv

This commit is contained in:
Boris Zbarsky 2011-12-26 11:31:07 -05:00
parent 5731c6fb90
commit 5e4f813534
4 changed files with 203 additions and 136 deletions

View File

@ -1679,25 +1679,6 @@ jsid nsDOMClassInfo::sMultiEntry_id = JSID_VOID;
jsid nsDOMClassInfo::sOnload_id = JSID_VOID;
jsid nsDOMClassInfo::sOnerror_id = JSID_VOID;
static const JSClass *sObjectClass = nsnull;
/**
* Set our JSClass pointer for the Object class
*/
static void
FindObjectClass(JSObject* aGlobalObject)
{
NS_ASSERTION(!sObjectClass,
"Double set of sObjectClass");
JSObject *obj, *proto = aGlobalObject;
do {
obj = proto;
proto = js::GetObjectProto(obj);
} while (proto);
sObjectClass = js::GetObjectJSClass(obj);
}
static void
PrintWarningOnConsole(JSContext *cx, const char *stringBundleProperty)
{
@ -4721,6 +4702,52 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
nsScriptNameSpaceManager *nameSpaceManager,
JSObject *dot_prototype, bool install, bool *did_resolve);
static nsresult
LookupPrototypeProto(JSContext *cx, JSObject *winobj,
const nsDOMClassInfoData *ci_data,
const nsGlobalNameStruct *name_struct,
JSObject **aProtoProto);
static nsGlobalWindow*
FindUsableInnerWindow(nsIXPConnect *xpc, JSContext *cx, JSObject *global)
{
// Only do this if the global object is a window.
// XXX Is there a better way to check this?
nsISupports *globalNative = xpc->GetNativeOfWrapper(cx, global);
nsCOMPtr<nsPIDOMWindow> piwin = do_QueryInterface(globalNative);
if (!piwin) {
return nsnull;
}
nsGlobalWindow *win = nsGlobalWindow::FromSupports(globalNative);
if (win->IsClosedOrClosing()) {
return nsnull;
}
// If the window is in a different compartment than the global object, then
// it's likely that global is a sandbox object whose prototype is a window.
// Don't do anything in this case.
if (win->FastGetGlobalJSObject() &&
js::GetObjectCompartment(global) != js::GetObjectCompartment(win->FastGetGlobalJSObject())) {
return nsnull;
}
if (win->IsOuterWindow()) {
// XXXjst: Do security checks here when we remove the security
// checks on the inner window.
win = win->GetCurrentInnerWindowInternal();
JSObject* global;
if (!win || !(global = win->GetGlobalJSObject()) ||
win->IsClosedOrClosing()) {
return nsnull;
}
}
return win;
}
NS_IMETHODIMP
nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * proto)
@ -4739,19 +4766,6 @@ nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * proto)
JS_ClearPendingException(cx);
}
// This is called before any other location that requires
// sObjectClass, so compute it here. We assume that nobody has had a
// chance to monkey around with proto's prototype chain before this.
if (!sObjectClass) {
FindObjectClass(proto);
NS_ASSERTION(sObjectClass && !strcmp(sObjectClass->name, "Object"),
"Incorrect object class!");
}
NS_ASSERTION(::JS_GetPrototype(cx, proto) &&
JS_GET_CLASS(cx, ::JS_GetPrototype(cx, proto)) == sObjectClass,
"Hmm, somebody did something evil?");
#ifdef DEBUG
if (mData->mHasClassInterface && mData->mProtoChainInterface &&
mData->mProtoChainInterface != &NS_GET_IID(nsISupports)) {
@ -4779,38 +4793,12 @@ nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * proto)
// document.body's prototype will find the right function.
JSObject *global = ::JS_GetGlobalForObject(cx, proto);
// Only do this if the global object is a window.
// XXX Is there a better way to check this?
nsISupports *globalNative = XPConnect()->GetNativeOfWrapper(cx, global);
nsCOMPtr<nsPIDOMWindow> piwin = do_QueryInterface(globalNative);
if (!piwin) {
nsGlobalWindow *win = FindUsableInnerWindow(XPConnect(), cx, global);
if (!win) {
return NS_OK;
}
nsGlobalWindow *win = nsGlobalWindow::FromSupports(globalNative);
if (win->IsClosedOrClosing()) {
return NS_OK;
}
// If the window is in a different compartment than the global object, then
// it's likely that global is a sandbox object whose prototype is a window.
// Don't do anything in this case.
if (win->FastGetGlobalJSObject() &&
js::GetObjectCompartment(global) != js::GetObjectCompartment(win->FastGetGlobalJSObject())) {
return NS_OK;
}
if (win->IsOuterWindow()) {
// XXXjst: Do security checks here when we remove the security
// checks on the inner window.
win = win->GetCurrentInnerWindowInternal();
if (!win || !(global = win->GetGlobalJSObject()) ||
win->IsClosedOrClosing()) {
return NS_OK;
}
}
global = win->FastGetGlobalJSObject();
// Don't overwrite a property set by content.
JSBool found;
@ -4829,6 +4817,23 @@ nsDOMClassInfo::PostCreatePrototype(JSContext * cx, JSObject * proto)
&unused);
}
NS_IMETHODIMP
nsDOMClassInfo::PreCreatePrototype(JSContext * cx, JSObject * global,
JSObject **protoProto)
{
*protoProto = nsnull;
nsGlobalWindow *win = FindUsableInnerWindow(XPConnect(), cx, global);
if (!win) {
return NS_OK;
}
JSObject *winObj = win->FastGetGlobalJSObject();
return LookupPrototypeProto(cx, winObj, mData, nsnull, protoProto);
}
// static
nsIClassInfo *
NS_GetDOMClassInfoInstance(nsDOMClassInfoID aID)
@ -5181,7 +5186,7 @@ nsWindowSH::InstallGlobalScopePolluter(JSContext *cx, JSObject *obj,
// scope polluter (right before Object.prototype).
while ((proto = ::JS_GetPrototype(cx, o))) {
if (JS_GET_CLASS(cx, proto) == sObjectClass) {
if (js::GetObjectClass(proto) == &js::ObjectClass) {
// Set the global scope polluters prototype to Object.prototype
::JS_SplicePrototype(cx, gsp, proto);
@ -6003,6 +6008,105 @@ GetXPCProto(nsIXPConnect *aXPConnect, JSContext *cx, nsGlobalWindow *aWin,
return aXPConnect->HoldObject(cx, proto_obj, aProto);
}
static nsresult
LookupPrototypeProto(JSContext *cx, JSObject *winobj,
const nsDOMClassInfoData *ci_data,
const nsGlobalNameStruct *name_struct,
JSObject **aProtoProto)
{
NS_ASSERTION(ci_data ||
(name_struct &&
name_struct->mType == nsGlobalNameStruct::eTypeClassProto),
"Wrong type or missing ci_data!");
const nsIID *primary_iid = &NS_GET_IID(nsISupports);
if (!ci_data) {
primary_iid = &name_struct->mIID;
} else if (ci_data->mProtoChainInterface) {
primary_iid = ci_data->mProtoChainInterface;
}
if (primary_iid->Equals(NS_GET_IID(nsISupports))) {
*aProtoProto = nsnull;
return NS_OK;
}
nsCOMPtr<nsIInterfaceInfoManager>
iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID));
NS_ENSURE_TRUE(iim, NS_ERROR_NOT_AVAILABLE);
nsCOMPtr<nsIInterfaceInfo> if_info;
iim->GetInfoForIID(primary_iid, getter_AddRefs(if_info));
NS_ENSURE_TRUE(if_info, NS_ERROR_UNEXPECTED);
const nsIID *iid = nsnull;
nsCOMPtr<nsIInterfaceInfo> parent;
if (ci_data && !ci_data->mHasClassInterface) {
if_info->GetIIDShared(&iid);
} else {
if_info->GetParent(getter_AddRefs(parent));
NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
parent->GetIIDShared(&iid);
}
if (!iid || iid->Equals(NS_GET_IID(nsISupports))) {
*aProtoProto = nsnull;
return NS_OK;
}
const char *class_parent_name = nsnull;
if (ci_data && !ci_data->mHasClassInterface) {
// If the class doesn't have a class interface the primary
// interface is the interface that should be
// constructor.prototype.__proto__.
if_info->GetNameShared(&class_parent_name);
} else {
// If the class does have a class interface (or there's no
// real class for this name) then the parent of the
// primary interface is what we want on
// constructor.prototype.__proto__.
NS_ASSERTION(parent, "Whoa, this is bad, null parent here!");
parent->GetNameShared(&class_parent_name);
}
JSObject *protoProto = nsnull;
// Get class_parent_name here
if (class_parent_name) {
jsval val;
JSAutoEnterCompartment ac;
if (!ac.enter(cx, winobj)) {
return NS_ERROR_UNEXPECTED;
}
if (!::JS_LookupProperty(cx, winobj, CutPrefix(class_parent_name), &val)) {
return NS_ERROR_UNEXPECTED;
}
JSObject *tmp = JSVAL_IS_OBJECT(val) ? JSVAL_TO_OBJECT(val) : nsnull;
if (tmp) {
if (!::JS_LookupProperty(cx, tmp, "prototype", &val)) {
return NS_ERROR_UNEXPECTED;
}
if (JSVAL_IS_OBJECT(val)) {
protoProto = JSVAL_TO_OBJECT(val);
}
}
}
*aProtoProto = protoProto;
return NS_OK;
}
// Either ci_data must be non-null or name_struct must be non-null and of type
// eTypeClassProto.
static nsresult
@ -6048,10 +6152,6 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
primary_iid = ci_data->mProtoChainInterface;
}
nsCOMPtr<nsIInterfaceInfo> if_info;
nsCOMPtr<nsIInterfaceInfo> parent;
const char *class_parent_name = nsnull;
if (!primary_iid->Equals(NS_GET_IID(nsISupports))) {
JSAutoEnterCompartment ac;
@ -6075,76 +6175,14 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
!indexedDB::IDBKeyRange::DefineConstructors(cx, class_obj)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIInterfaceInfoManager>
iim(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID));
NS_ENSURE_TRUE(iim, NS_ERROR_NOT_AVAILABLE);
iim->GetInfoForIID(primary_iid, getter_AddRefs(if_info));
NS_ENSURE_TRUE(if_info, NS_ERROR_UNEXPECTED);
const nsIID *iid = nsnull;
if (ci_data && !ci_data->mHasClassInterface) {
if_info->GetIIDShared(&iid);
} else {
if_info->GetParent(getter_AddRefs(parent));
NS_ENSURE_TRUE(parent, NS_ERROR_UNEXPECTED);
parent->GetIIDShared(&iid);
}
if (iid) {
if (!iid->Equals(NS_GET_IID(nsISupports))) {
if (ci_data && !ci_data->mHasClassInterface) {
// If the class doesn't have a class interface the primary
// interface is the interface that should be
// constructor.prototype.__proto__.
if_info->GetNameShared(&class_parent_name);
} else {
// If the class does have a class interface (or there's no
// real class for this name) then the parent of the
// primary interface is what we want on
// constructor.prototype.__proto__.
NS_ASSERTION(parent, "Whoa, this is bad, null parent here!");
parent->GetNameShared(&class_parent_name);
}
}
}
}
{
JSObject *winobj = aWin->FastGetGlobalJSObject();
JSObject *proto = nsnull;
if (class_parent_name) {
jsval val;
JSAutoEnterCompartment ac;
if (!ac.enter(cx, winobj)) {
return NS_ERROR_UNEXPECTED;
}
if (!::JS_LookupProperty(cx, winobj, CutPrefix(class_parent_name), &val)) {
return NS_ERROR_UNEXPECTED;
}
JSObject *tmp = JSVAL_IS_OBJECT(val) ? JSVAL_TO_OBJECT(val) : nsnull;
if (tmp) {
if (!::JS_LookupProperty(cx, tmp, "prototype", &val)) {
return NS_ERROR_UNEXPECTED;
}
if (JSVAL_IS_OBJECT(val)) {
proto = JSVAL_TO_OBJECT(val);
}
}
}
JSObject *proto;
rv = LookupPrototypeProto(cx, winobj, ci_data, name_struct, &proto);
NS_ENSURE_SUCCESS(rv, rv);
if (dot_prototype) {
JSAutoEnterCompartment ac;
@ -6156,7 +6194,7 @@ ResolvePrototype(nsIXPConnect *aXPConnect, nsGlobalWindow *aWin, JSContext *cx,
if (proto &&
(!xpc_proto_proto ||
JS_GET_CLASS(cx, xpc_proto_proto) == sObjectClass)) {
js::GetObjectClass(xpc_proto_proto) == &js::ObjectClass)) {
if (!JS_WrapObject(cx, &proto) ||
!JS_SetPrototype(cx, dot_prototype, proto)) {
return NS_ERROR_UNEXPECTED;
@ -9543,7 +9581,7 @@ nsHTMLPluginObjElementSH::SetupProtoChain(nsIXPConnectWrappedNative *wrapper,
return NS_ERROR_UNEXPECTED;
}
if (pi_proto && JS_GET_CLASS(cx, pi_proto) != sObjectClass) {
if (pi_proto && js::GetObjectClass(pi_proto) != &js::ObjectClass) {
// The plugin wrapper has a proto that's not Object.prototype, set
// 'pi.__proto__.__proto__' to the original 'this.__proto__'
if (pi_proto != my_proto && !::JS_SetPrototype(cx, pi_proto, my_proto)) {

View File

@ -73,7 +73,7 @@
* to *_retval unless they want to return PR_FALSE.
*/
[uuid(a40ce52e-2d8c-400f-9af2-f8784a656070)]
[uuid(9cadb17b-9990-461a-8e01-cf50aeffc2c5)]
interface nsIXPCScriptable : nsISupports
{
/* bitflags used for 'flags' (only 32 bits available!) */
@ -191,6 +191,14 @@ interface nsIXPCScriptable : nsISupports
in JSContextPtr cx, in JSObjectPtr obj);
void postCreatePrototype(in JSContextPtr cx, in JSObjectPtr proto);
/**
* Notify that we're about to create the prototype object for this class,
* and ask what prototype we should use for that.
*/
void preCreatePrototype(in JSContextPtr cx,
in JSObjectPtr globalObj,
out JSObjectPtr protoProtoObj);
};
%{ C++

View File

@ -228,6 +228,11 @@ NS_IMETHODIMP XPC_MAP_CLASSNAME::PostCreatePrototype(JSContext *cx, JSObject *pr
{return NS_OK;}
#endif
#ifndef XPC_MAP_WANT_PRE_CREATE_PROTOTYPE
NS_IMETHODIMP XPC_MAP_CLASSNAME::PreCreatePrototype(JSContext *cx, JSObject *global, JSObject **protoProto)
{*protoProto = nsnull; return NS_OK;}
#endif
/**************************************************************/
#undef XPC_MAP_CLASSNAME
@ -313,6 +318,10 @@ NS_IMETHODIMP XPC_MAP_CLASSNAME::PostCreatePrototype(JSContext *cx, JSObject *pr
#undef XPC_MAP_WANT_POST_CREATE_PROTOTYPE
#endif
#ifdef XPC_MAP_WANT_PRE_CREATE_PROTOTYPE
#undef XPC_MAP_WANT_PRE_CREATE_PROTOTYPE
#endif
#ifdef XPC_MAP_FLAGS
#undef XPC_MAP_FLAGS
#endif

View File

@ -123,10 +123,22 @@ XPCWrappedNativeProto::Init(XPCCallContext& ccx,
JSObject *parent = mScope->GetGlobalJSObject();
JSObject *protoProto = nsnull;
if (callback) {
nsresult rv = callback->PreCreatePrototype(ccx, parent, &protoProto);
if (NS_FAILED(rv)) {
mJSProtoObject = nsnull;
XPCThrower::Throw(rv, ccx);
return false;
}
}
if (!protoProto) {
protoProto = mScope->GetPrototypeJSObject();
}
mJSProtoObject =
xpc_NewSystemInheritingJSObject(ccx, js::Jsvalify(jsclazz),
mScope->GetPrototypeJSObject(),
true, parent);
protoProto, true, parent);
JSBool ok = mJSProtoObject && JS_SetPrivate(ccx, mJSProtoObject, this);