Add a way to find all of the XOWs for an object and use it to deal with hard cases where we have to clear the scope of XOWs in order to reflect changes to the underlying object. Also deal with objects moving between scopes by ensuring that we're always able to find their XOWs. bug 399587, r+sr=jst r=brendan for some last-minute changes added in this version.

This commit is contained in:
mrbkap@gmail.com 2008-01-04 20:45:16 -08:00
parent 90e5fe2fa2
commit 4dfe6493da
14 changed files with 346 additions and 102 deletions

View File

@ -2155,7 +2155,7 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace)
NS_FAILED(callerPrincipal->Equals(NodePrincipal(), &samePrincipal)) ||
!samePrincipal) {
SetIsInitialDocument(PR_FALSE);
}
}
rv = window->SetNewDocument(this, nsnull, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -4737,6 +4737,21 @@ nsWindowSH::DelProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
return NS_ERROR_DOM_SECURITY_ERR;
}
// Notify any XOWs on our outer window.
nsGlobalWindow *outerWin = win->GetOuterWindowInternal();
if (outerWin) {
nsCOMPtr<nsIXPConnectWrappedNative> wn;
nsIXPConnect *xpc = nsContentUtils::XPConnect();
nsresult rv =
xpc->GetWrappedNativeOfJSObject(cx, outerWin->GetGlobalJSObject(),
getter_AddRefs(wn));
NS_ENSURE_SUCCESS(rv, rv);
rv = xpc->UpdateXOWs(cx, wn, nsIXPConnect::XPC_XOW_CLEARSCOPE);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
@ -5865,10 +5880,8 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
// a different context.
if (!win->IsChromeWindow()) {
rv = sXPConnect->GetCrossOriginWrapperForObject(cx,
win->GetGlobalJSObject(),
JSVAL_TO_OBJECT(v),
&v);
rv = sXPConnect->GetXOWForObject(cx, win->GetGlobalJSObject(),
JSVAL_TO_OBJECT(v), &v);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -5979,9 +5992,7 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
sDoSecurityCheckInAddProperty = PR_FALSE;
if (!win->IsChromeWindow()) {
rv = sXPConnect->GetCrossOriginWrapperForObject(cx, scope,
JSVAL_TO_OBJECT(v),
&v);
rv = sXPConnect->GetXOWForObject(cx, scope, JSVAL_TO_OBJECT(v), &v);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -6089,10 +6100,8 @@ nsWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
scope = oldWin->GetGlobalJSObject();
}
rv = sXPConnect->GetCrossOriginWrapperForObject(cx,
scope,
JSVAL_TO_OBJECT(winVal),
&winVal);
rv = sXPConnect->GetXOWForObject(cx, scope, JSVAL_TO_OBJECT(winVal),
&winVal);
NS_ENSURE_SUCCESS(rv, rv);
}
PRBool ok =
@ -6302,7 +6311,7 @@ nsWindowSH::OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
}
scope = ::JS_GetGlobalForObject(cx, scope);
jsval v;
rv = sXPConnect->GetCrossOriginWrapperForObject(cx, scope, winObj, &v);
rv = sXPConnect->GetXOWForObject(cx, scope, winObj, &v);
*_retval = NS_SUCCEEDED(rv) ? JSVAL_TO_OBJECT(v) : nsnull;
}
@ -7648,8 +7657,8 @@ nsDocumentSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
nsGlobalWindow *internalWin = static_cast<nsGlobalWindow *>(sgo);
if (!internalWin->IsChromeWindow()) {
rv = sXPConnect->GetCrossOriginWrapperForObject(cx, sgo->GetGlobalJSObject(),
obj, &docVal);
rv = sXPConnect->GetXOWForObject(cx, sgo->GetGlobalJSObject(), obj,
&docVal);
NS_ENSURE_SUCCESS(rv, rv);
}

View File

@ -447,7 +447,7 @@ interface nsIXPCFunctionThisTranslator : nsISupports
{ 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } }
%}
[uuid(8a47448a-66b0-4beb-a5a2-24f22e9c9dea)]
[uuid(20df9082-5b83-416d-ba80-0422af516d57)]
interface nsIXPConnect : nsISupports
{
%{ C++
@ -729,9 +729,26 @@ interface nsIXPConnect : nsISupports
* @param aParent The parent to create the wrapper with.
* @param aWrappedObj The object to wrap.
*/
[noscript] JSVal getCrossOriginWrapperForObject(in JSContextPtr aJSContext,
in JSObjectPtr aParent,
in JSObjectPtr aWrappedObj);
[noscript] JSVal getXOWForObject(in JSContextPtr aJSContext,
in JSObjectPtr aParent,
in JSObjectPtr aWrappedObj);
/**
* Tells updateXOWs to clear the scope of all of the XOWs it finds.
*/
const PRUint32 XPC_XOW_CLEARSCOPE = 1;
/**
* Performs an operation over all of |object|'s XOWs such as clearing
* their scopes or updating their concept of the current principal.
*
* @param aJSContext A context to use to perform JS operations.
* @param aObject Which XPCWrappedNative we should find the XOWs for.
* @param aWay What operation to perform.
*/
[noscript] void updateXOWs(in JSContextPtr aJSContext,
in nsIXPConnectWrappedNative aObject,
in PRUint32 aWay);
/**
* Root JS objects held by aHolder.

View File

@ -191,6 +191,44 @@ GetSecurityManager()
return gScriptSecurityManager;
}
JSBool
XPC_XOW_WrapperMoved(JSContext *cx, XPCWrappedNative *innerObj,
XPCWrappedNativeScope *newScope)
{
typedef WrappedNative2WrapperMap::Link Link;
XPCJSRuntime *rt = nsXPConnect::GetRuntime();
WrappedNative2WrapperMap *map = innerObj->GetScope()->GetWrapperMap();
Link *link;
{ // Scoped lock
XPCAutoLock al(rt->GetMapLock());
link = map->FindLink(innerObj->GetFlatJSObject());
}
if (!link) {
// No link here means that there were no XOWs for this object.
return JS_TRUE;
}
JSObject *xow = link->obj;
{ // Scoped lock.
XPCAutoLock al(rt->GetMapLock());
if (!newScope->GetWrapperMap()->AddLink(innerObj->GetFlatJSObject(), link))
return JS_FALSE;
map->Remove(innerObj->GetFlatJSObject());
}
if (!xow) {
// Nothing else to do.
return JS_TRUE;
}
return JS_SetReservedSlot(cx, xow, XPCWrapper::sNumSlots,
PRIVATE_TO_JSVAL(newScope)) &&
JS_SetParent(cx, xow, newScope->GetGlobalJSObject());
}
static JSBool
IsValFrame(JSContext *cx, JSObject *obj, jsval v, XPCWrappedNative *wn)
{
@ -433,43 +471,34 @@ XPC_XOW_WrapObject(JSContext *cx, JSObject *parent, jsval *vp)
XPCCallContext ccx(NATIVE_CALLER, cx);
NS_ENSURE_TRUE(ccx.IsValid(), JS_FALSE);
XPCWrappedNativeScope *parentScope =
XPCWrappedNativeScope::FindInJSObjectScope(ccx, parent);
XPCWrappedNativeScope *wrapperScope = wn->GetScope();
#ifdef DEBUG_mrbkap
printf("Wrapping object at %p (%s) [%p %p]\n",
(void *)wrappedObj, JS_GET_CLASS(cx, wrappedObj)->name,
(void *)parentScope, (void *)wrapperScope);
#endif
JSObject *outerObj = nsnull;
JSBool sameOrigin = (parentScope == wrapperScope);
WrappedNative2WrapperMap *map =
sameOrigin ? wrapperScope->GetWrapperMap() : parentScope->GetWrapperMap();
if (sameOrigin) {
outerObj = wn->GetWrapper();
if (outerObj && JS_GET_CLASS(cx, outerObj) == &sXPC_XOW_JSClass.base) {
#ifdef DEBUG_mrbkap
printf("But found a wrapper already there %p!\n", (void *)outerObj);
#endif
*vp = OBJECT_TO_JSVAL(outerObj);
return JS_TRUE;
// The parent must be the inner global object for its scope.
parent = JS_GetGlobalForObject(cx, parent);
JSClass *clasp = JS_GET_CLASS(cx, parent);
if (clasp->flags & JSCLASS_IS_EXTENDED) {
JSExtendedClass *xclasp = reinterpret_cast<JSExtendedClass *>(clasp);
if (xclasp->innerObject) {
parent = xclasp->innerObject(cx, parent);
if (!parent) {
return JS_FALSE;
}
}
}
XPCWrappedNativeScope *parentScope =
XPCWrappedNativeScope::FindInJSObjectScope(ccx, parent);
#ifdef DEBUG_mrbkap
printf("Wrapping object at %p (%s) [%p]\n",
(void *)wrappedObj, JS_GET_CLASS(cx, wrappedObj)->name,
(void *)parentScope);
#endif
JSObject *outerObj = nsnull;
WrappedNative2WrapperMap *map = parentScope->GetWrapperMap();
{ // Scoped lock
XPCAutoLock al(rt->GetMapLock());
if (outerObj) {
outerObj = map->Add(wrappedObj, outerObj);
if (sameOrigin) {
wn->SetWrapper(nsnull);
}
} else {
outerObj = map->Find(wrappedObj);
}
outerObj = map->Find(wrappedObj);
}
if (outerObj) {
@ -478,9 +507,6 @@ XPC_XOW_WrapObject(JSContext *cx, JSObject *parent, jsval *vp)
#ifdef DEBUG_mrbkap
printf("But found a wrapper in the map %p!\n", (void *)outerObj);
#endif
if (sameOrigin) {
wn->SetWrapper(outerObj);
}
*vp = OBJECT_TO_JSVAL(outerObj);
return JS_TRUE;
}
@ -509,14 +535,10 @@ XPC_XOW_WrapObject(JSContext *cx, JSObject *parent, jsval *vp)
}
*vp = OBJECT_TO_JSVAL(outerObj);
if (!sameOrigin) {
{ // Scoped lock
XPCAutoLock al(rt->GetMapLock());
map->Add(wrappedObj, outerObj);
} else {
#ifdef DEBUG_mrbkap
printf("Setting wrapper to %p\n", (void *)outerObj);
#endif
wn->SetWrapper(outerObj);
map->Add(wn->GetScope()->GetWrapperMap(), wrappedObj, outerObj);
}
return JS_TRUE;
@ -860,7 +882,7 @@ XPC_XOW_Finalize(JSContext *cx, JSObject *obj)
// entirely. Scope can be null if we're an enumerating XOW.
XPCWrappedNativeScope *scope = reinterpret_cast<XPCWrappedNativeScope *>
(JSVAL_TO_PRIVATE(scopeVal));
if (!scope || XPCWrappedNativeScope::IsDyingScope(scope)) {
if (!scope) {
return;
}

View File

@ -1080,28 +1080,7 @@ XPCNativeWrapper::GetNewOrUsed(JSContext *cx, XPCWrappedNative *wrapper)
}
JSObject *obj = wrapper->GetWrapper();
if (obj && XPCNativeWrapper::IsNativeWrapper(cx, obj)) {
return obj;
}
XPCWrappedNativeScope *scope = wrapper->GetScope();
XPCJSRuntime *rt = nsXPConnect::GetRuntime();
{ // Scoped lock.
XPCAutoLock al(rt->GetMapLock());
if (obj) {
obj = scope->GetWrapperMap()->Add(wrapper->GetFlatJSObject(), obj);
wrapper->SetWrapper(nsnull);
} else {
obj = scope->GetWrapperMap()->Find(wrapper->GetFlatJSObject());
}
}
if (obj) {
NS_ASSERTION(XPCNativeWrapper::IsNativeWrapper(cx, obj),
"Weird object in the wrapper map");
wrapper->SetWrapper(obj);
return obj;
}

View File

@ -74,6 +74,10 @@ XPC_XOW_WrapFunction(JSContext *cx, JSObject *wrapperObj, JSObject *funobj,
JSBool
XPC_XOW_RewrapIfNeeded(JSContext *cx, JSObject *wrapperObj, jsval *vp);
JSBool
XPC_XOW_WrapperMoved(JSContext *cx, XPCWrappedNative *innerObj,
XPCWrappedNativeScope *newScope);
nsresult
IsWrapperSameOrigin(JSContext *cx, JSObject *wrappedObj);

View File

@ -1916,16 +1916,66 @@ nsXPConnect::GetWrappedNativePrototype(JSContext * aJSContext,
/* [noscript] JSVal GetCrossOriginWrapperForValue(in JSContextPtr aJSContext, in JSVal aCurrentVal); */
NS_IMETHODIMP
nsXPConnect::GetCrossOriginWrapperForObject(JSContext * aJSContext,
JSObject * aParent,
JSObject * aWrappedObj,
jsval * rval)
nsXPConnect::GetXOWForObject(JSContext * aJSContext,
JSObject * aParent,
JSObject * aWrappedObj,
jsval * rval)
{
*rval = OBJECT_TO_JSVAL(aWrappedObj);
return XPC_XOW_WrapObject(aJSContext, aParent, rval)
? NS_OK : NS_ERROR_FAILURE;
}
static inline PRBool
PerformOp(JSContext *cx, PRUint32 aWay, JSObject *obj)
{
NS_ASSERTION(aWay == nsIXPConnect::XPC_XOW_CLEARSCOPE,
"Nothing else is implemented yet");
JS_ClearScope(cx, obj);
return PR_TRUE;
}
/* [noscript] void updateXOWs (in JSContextPtr aJSContext,
* in nsIXPConnectJSObjectHolder aObject,
* in PRUint32 aWay); */
NS_IMETHODIMP
nsXPConnect::UpdateXOWs(JSContext* aJSContext,
nsIXPConnectWrappedNative* aObject,
PRUint32 aWay)
{
typedef WrappedNative2WrapperMap::Link Link;
XPCWrappedNative* wn = static_cast<XPCWrappedNative *>(aObject);
XPCWrappedNativeScope* scope = wn->GetScope();
WrappedNative2WrapperMap* map = scope->GetWrapperMap();
Link* list;
{
XPCJSRuntime* rt = nsXPConnect::GetRuntime();
XPCAutoLock al(rt->GetMapLock());
list = map->FindLink(wn->GetFlatJSObject());
}
if(!list)
return NS_OK; // No wrappers to update.
AutoJSRequestWithNoCallContext req(aJSContext);
Link* cur = list;
if(cur->obj && !PerformOp(aJSContext, aWay, cur->obj))
return NS_ERROR_FAILURE;
for(cur = (Link *)PR_NEXT_LINK(list); cur != list;
cur = (Link *)PR_NEXT_LINK(cur))
{
if(!PerformOp(aJSContext, aWay, cur->obj))
return NS_ERROR_FAILURE;
}
return NS_OK;
}
/* void releaseJSContext (in JSContextPtr aJSContext, in PRBool noGC); */
NS_IMETHODIMP
nsXPConnect::ReleaseJSContext(JSContext * aJSContext, PRBool noGC)

View File

@ -652,6 +652,24 @@ XPCNativeWrapperMap::~XPCNativeWrapperMap()
/***************************************************************************/
// implement WrappedNative2WrapperMap...
struct JSDHashTableOps
WrappedNative2WrapperMap::sOps = { nsnull };
// static
void
WrappedNative2WrapperMap::ClearLink(JSDHashTable* table,
JSDHashEntryHdr* entry)
{
Entry* e = static_cast<Entry*>(entry);
e->key = nsnull;
if(e->value)
{
PR_REMOVE_LINK(e->value);
delete e->value;
e->value = nsnull;
}
}
// static
WrappedNative2WrapperMap*
WrappedNative2WrapperMap::newMap(int size)
@ -665,8 +683,13 @@ WrappedNative2WrapperMap::newMap(int size)
WrappedNative2WrapperMap::WrappedNative2WrapperMap(int size)
{
mTable = JS_NewDHashTable(JS_DHashGetStubOps(), nsnull,
sizeof(Entry), size);
if(!sOps.allocTable)
{
sOps = *JS_DHashGetStubOps();
sOps.clearEntry = WrappedNative2WrapperMap::ClearLink;
}
mTable = JS_NewDHashTable(&sOps, nsnull, sizeof(Entry), size);
}
WrappedNative2WrapperMap::~WrappedNative2WrapperMap()
@ -675,4 +698,71 @@ WrappedNative2WrapperMap::~WrappedNative2WrapperMap()
JS_DHashTableDestroy(mTable);
}
JSObject*
WrappedNative2WrapperMap::Add(WrappedNative2WrapperMap* head,
JSObject* wrappedObject,
JSObject* wrapper)
{
NS_PRECONDITION(wrappedObject,"bad param");
Entry* entry = (Entry*)
JS_DHashTableOperate(mTable, wrappedObject, JS_DHASH_ADD);
if(!entry)
return nsnull;
NS_ASSERTION(!entry->key || this == head, "dangling pointer?");
entry->key = wrappedObject;
Link* l = new Link;
if(!l)
return nsnull;
PR_INIT_CLIST(l);
l->obj = wrapper;
if(this != head)
{
Link* headLink = head->FindLink(wrappedObject);
if(!headLink)
{
Entry* dummy = (Entry*)
JS_DHashTableOperate(head->mTable, wrappedObject, JS_DHASH_ADD);
dummy->key = wrappedObject;
headLink = dummy->value = new Link;
if(!headLink)
{
Remove(wrappedObject);
return nsnull;
}
PR_INIT_CLIST(headLink);
headLink->obj = nsnull;
}
PR_INSERT_BEFORE(l, headLink);
}
entry->value = l;
return wrapper;
}
PRBool
WrappedNative2WrapperMap::AddLink(JSObject* wrappedObject, Link* oldLink)
{
Entry* entry = (Entry*)
JS_DHashTableOperate(mTable, wrappedObject, JS_DHASH_ADD);
if(!entry)
return PR_FALSE;
NS_ASSERTION(!entry->key, "Eh? What's happening?");
entry->key = wrappedObject;
Link* newLink = entry->value = new Link;
if(!newLink)
{
Remove(wrappedObject);
return PR_FALSE;
}
PR_INSERT_LINK(newLink, oldLink);
PR_REMOVE_AND_INIT_LINK(oldLink);
PR_INIT_CLIST(oldLink);
newLink->obj = oldLink->obj;
return PR_TRUE;
}
/***************************************************************************/

View File

@ -686,12 +686,21 @@ private:
class WrappedNative2WrapperMap
{
static struct JSDHashTableOps sOps;
static void ClearLink(JSDHashTable* table, JSDHashEntryHdr* entry);
public:
struct Link : public PRCList
{
JSObject *obj;
};
struct Entry : public JSDHashEntryHdr
{
// Note: key must be the flat JSObject for a wrapped native.
JSObject* key;
JSObject* value;
Link* value;
};
static WrappedNative2WrapperMap* newMap(int size);
@ -703,26 +712,29 @@ public:
JS_DHashTableOperate(mTable, wrapper, JS_DHASH_LOOKUP);
if(JS_DHASH_ENTRY_IS_FREE(entry))
return nsnull;
return entry->value;
return entry->value->obj;
}
// Note: If the entry already exists, then this will overwrite the
// existing entry, returning the old value.
inline JSObject* Add(JSObject* wrapper, JSObject *obj)
JSObject* Add(WrappedNative2WrapperMap* head,
JSObject* wrappedObject,
JSObject* wrapper);
// Function to find a link.
Link* FindLink(JSObject* wrappedObject)
{
NS_PRECONDITION(wrapper,"bad param");
Entry* entry = (Entry*)
JS_DHashTableOperate(mTable, wrapper, JS_DHASH_ADD);
if(!entry)
return nsnull;
JSObject *old;
if(!entry->key)
entry->key = wrapper;
old = entry->value;
entry->value = obj;
return old;
JS_DHashTableOperate(mTable, wrappedObject, JS_DHASH_LOOKUP);
if(JS_DHASH_ENTRY_IS_BUSY(entry))
return entry->value;
return nsnull;
}
// "Internal" function to add an empty link without doing unnecessary
// work.
PRBool AddLink(JSObject* wrappedObject, Link* oldLink);
inline void Remove(JSObject* wrapper)
{
NS_PRECONDITION(wrapper,"bad param");
@ -734,9 +746,11 @@ public:
{return JS_DHashTableEnumerate(mTable, f, arg);}
~WrappedNative2WrapperMap();
private:
WrappedNative2WrapperMap(); // no implementation
WrappedNative2WrapperMap(int size);
private:
JSDHashTable *mTable;
};

View File

@ -91,6 +91,7 @@
#include "prlong.h"
#include "prmem.h"
#include "prenv.h"
#include "prclist.h"
#include "nsString.h"
#include "nsReadableUtils.h"
#include "nsXPIDLString.h"

View File

@ -1158,6 +1158,10 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx,
{
// Oh, so now we need to move the wrapper to a different scope.
// First notify any XOWs.
nsXPConnect* xpc = nsXPConnect::GetXPConnect();
xpc->UpdateXOWs(ccx, wrapper, nsIXPConnect::XPC_XOW_CLEARSCOPE);
AutoMarkingWrappedNativeProtoPtr oldProto(ccx);
AutoMarkingWrappedNativeProtoPtr newProto(ccx);
@ -1179,6 +1183,12 @@ XPCWrappedNative::ReparentWrapperIfFound(XPCCallContext& ccx,
}
}
if(!XPC_XOW_WrapperMoved(ccx, wrapper, aNewScope))
{
NS_RELEASE(wrapper);
return NS_ERROR_FAILURE;
}
Native2WrappedNativeMap* oldMap = aOldScope->GetWrappedNativeMap();
Native2WrappedNativeMap* newMap = aNewScope->GetWrappedNativeMap();

View File

@ -270,6 +270,7 @@ XPCWrappedNativeScope::~XPCWrappedNativeScope()
if(mWrapperMap)
{
NS_ASSERTION(0 == mWrapperMap->Count(), "scope has non-empty map");
delete mWrapperMap;
}

View File

@ -45,6 +45,7 @@ include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_TEST_FILES = test_bug390488.html \
test_bug393269.html \
test_wrappers.html \
$(NULL)

View File

@ -0,0 +1,46 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=393269
-->
<head>
<title>Test for Bug 393269</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=393269">Mozilla Bug 393269</a>
<iframe id="ifr"></iframe>
<pre id="test">
<script class="testbody" type="text/javascript">
(function () {
/** Test for Bug 393269 **/
var doc = $("ifr").contentDocument;
is("UTF-8", doc.characterSet, "control, getting a property");
doc.open();
try {
is("UTF-8", doc.characterSet,
"can get a property after 1 document.open")
} catch (e) {
fail("Shouldn't have thrown: " + e);
return;
} finally {
doc.close();
}
doc.open();
try {
is("UTF-8", doc.characterSet,
"can get a property after 2 document.opens")
} catch (e) {
fail("Shouldn't have thrown: " + e);
} finally {
doc.close();
}
})();
</script>
</pre>
</body>
</html>