Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Bug 707717. Don't dynamically mutate the proto chains of DOM prototyp…

…es. r=peterv
  • Loading branch information...
commit 3db91c7d60faf3e723272c6e334a5d3c2204587f 1 parent 8b38816
@bzbarsky bzbarsky authored
View
304 dom/base/nsDOMClassInfo.cpp
@@ -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) {
- 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())) {
+ nsGlobalWindow *win = FindUsableInnerWindow(XPConnect(), cx, global);
+ if (!win) {
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
10 js/xpconnect/idl/nsIXPCScriptable.idl
@@ -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
9 js/xpconnect/public/xpc_map_end.h
@@ -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
16 js/xpconnect/src/XPCWrappedNativeProto.cpp
@@ -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);
Please sign in to comment.
Something went wrong with that request. Please try again.