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

This commit is contained in:
mrbkap@gmail.com 2007-12-29 20:34:49 -08:00
Родитель 59b6437c68
Коммит dc907e2a1a
13 изменённых файлов: 343 добавлений и 101 удалений

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

@ -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);

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

@ -4718,6 +4718,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;
}
@ -5846,10 +5861,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);
}
@ -5960,9 +5973,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);
}
@ -6070,10 +6081,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 =
@ -6283,7 +6292,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;
}
@ -7629,8 +7638,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);
}

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

@ -447,7 +447,7 @@ interface nsIXPCFunctionThisTranslator : nsISupports
{ 0xbd, 0xd6, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } }
%}
[uuid(2d7f1bd7-ef8b-4f62-b2bb-1a8b0801edc3)]
[uuid(20df9082-5b83-416d-ba80-0422af516d57)]
interface nsIXPConnect : nsISupports
{
%{ C++
@ -732,9 +732,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.

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

@ -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)
{
@ -435,43 +473,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) {
@ -480,9 +509,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;
}
@ -507,14 +533,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;

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

@ -1061,28 +1061,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;
}

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

@ -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);

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

@ -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;
}
/* attribute PRBool collectGarbageOnMainThreadOnly; */
NS_IMETHODIMP
nsXPConnect::GetCollectGarbageOnMainThreadOnly(PRBool *aCollectGarbageOnMainThreadOnly)

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

@ -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,70 @@ 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);
newLink->obj = oldLink->obj;
return PR_TRUE;
}
/***************************************************************************/

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

@ -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;
};

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

@ -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"

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

@ -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();

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

@ -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)

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

@ -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>