diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 678261ef5611..dd4a24eff41c 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -4892,8 +4892,8 @@ nsDOMClassInfo::ShutDown() // Window helper NS_IMETHODIMP -nsCommonWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx, - JSObject *globalObj, JSObject **parentObj) +nsInnerWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *globalObj, JSObject **parentObj) { // Normally ::PreCreate() is used to give XPConnect the parent // object for the object that's being wrapped, this parent object is @@ -4911,25 +4911,30 @@ nsCommonWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx, NS_ASSERTION(sgo, "nativeObj not a global object!"); nsGlobalWindow *win = nsGlobalWindow::FromSupports(nativeObj); - - if (win->IsOuterWindow()) { - win->EnsureInnerWindow(); + JSObject *winObj = win->FastGetGlobalJSObject(); + if (!winObj) { + NS_ASSERTION(win->GetOuterWindowInternal()->IsCreatingInnerWindow(), + "should have a JS object by this point"); + return NS_OK; } - if (sgo) { - *parentObj = sgo->GetGlobalJSObject(); + *parentObj = winObj; + return NS_OK; +} - if (*parentObj) { - return win->IsChromeWindow() ? NS_OK : NS_SUCCESS_NEEDS_XOW; - } +NS_IMETHODIMP +nsOuterWindowSH::PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *globalObj, JSObject **parentObj) +{ + nsCOMPtr sgo(do_QueryInterface(nativeObj)); + NS_ASSERTION(sgo, "nativeObj not a global object!"); + + nsGlobalWindow *win = nsGlobalWindow::FromSupports(nativeObj); + if (!win->EnsureInnerWindow()) { + return NS_ERROR_FAILURE; } - // We're most likely being called when the global object is - // created, at that point we won't get a nsIScriptContext but we - // know we're called on the correct context so we return globalObj - - *parentObj = globalObj; - + *parentObj = win->GetCurrentInnerWindowInternal()->FastGetGlobalJSObject(); return win->IsChromeWindow() ? NS_OK : NS_SUCCESS_NEEDS_XOW; } @@ -6662,33 +6667,33 @@ nsCommonWindowSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, my_cx = (JSContext *)my_context->GetNativeContext(); } - JSBool ok; - jsval exn; - { + JSBool ok = JS_TRUE; + jsval exn = JSVAL_VOID; + if (win->IsInnerWindow()) { JSAutoRequest transfer(my_cx); JSObject *realObj; wrapper->GetJSObject(&realObj); - + // Don't resolve standard classes on XPCNativeWrapper etc, only // resolve them if we're resolving on the real global object. ok = obj == realObj ? ::JS_ResolveStandardClass(my_cx, obj, id, &did_resolve) : JS_TRUE; - + if (!ok) { // Trust the JS engine (or the script security manager) to set // the exception in the JS engine. - + if (!JS_GetPendingException(my_cx, &exn)) { return NS_ERROR_UNEXPECTED; } - + // Return NS_OK to avoid stomping over the exception that was passed // down from the ResolveStandardClass call. // Note that the order of the JS_ClearPendingException and // JS_SetPendingException is important in the case that my_cx == cx. - + JS_ClearPendingException(my_cx); } } diff --git a/dom/base/nsDOMClassInfo.h b/dom/base/nsDOMClassInfo.h index 3d30279e5a11..f06af68ccf7c 100644 --- a/dom/base/nsDOMClassInfo.h +++ b/dom/base/nsDOMClassInfo.h @@ -514,8 +514,6 @@ protected: PRBool *did_resolve); public: - NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, - JSObject *globalObj, JSObject **parentObj); #ifdef DEBUG NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj) @@ -578,6 +576,8 @@ protected: static PRBool sResolving; public: + NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *globalObj, JSObject **parentObj); NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx, JSObject *obj, jsid id, jsval *vp, PRBool *_retval); NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx, @@ -607,6 +607,8 @@ protected: } public: + NS_IMETHOD PreCreate(nsISupports *nativeObj, JSContext *cx, + JSObject *globalObj, JSObject **parentObj); // We WANT_ADDPROPERTY, but are content to inherit it from nsEventReceiverSH. NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx, JSObject * obj, JSObject * *_retval); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 6e7dd4c1b3f6..5d9b6b49d8f9 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -1512,7 +1512,8 @@ public: WindowStateHolder(nsGlobalWindow *aWindow, nsIXPConnectJSObjectHolder *aHolder, nsNavigator *aNavigator, - nsIXPConnectJSObjectHolder *aOuterProto); + nsIXPConnectJSObjectHolder *aOuterProto, + nsIXPConnectJSObjectHolder *aOuterRealProto); nsGlobalWindow* GetInnerWindow() { return mInnerWindow; } nsIXPConnectJSObjectHolder *GetInnerWindowHolder() @@ -1520,6 +1521,7 @@ public: nsNavigator* GetNavigator() { return mNavigator; } nsIXPConnectJSObjectHolder* GetOuterProto() { return mOuterProto; } + nsIXPConnectJSObjectHolder* GetOuterRealProto() { return mOuterRealProto; } void DidRestoreWindow() { @@ -1528,6 +1530,7 @@ public: mInnerWindowHolder = nsnull; mNavigator = nsnull; mOuterProto = nsnull; + mOuterRealProto = nsnull; } protected: @@ -1539,6 +1542,7 @@ protected: nsCOMPtr mInnerWindowHolder; nsRefPtr mNavigator; nsCOMPtr mOuterProto; + nsCOMPtr mOuterRealProto; }; NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID) @@ -1546,10 +1550,12 @@ NS_DEFINE_STATIC_IID_ACCESSOR(WindowStateHolder, WINDOWSTATEHOLDER_IID) WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow, nsIXPConnectJSObjectHolder *aHolder, nsNavigator *aNavigator, - nsIXPConnectJSObjectHolder *aOuterProto) + nsIXPConnectJSObjectHolder *aOuterProto, + nsIXPConnectJSObjectHolder *aOuterRealProto) : mInnerWindow(aWindow), mNavigator(aNavigator), - mOuterProto(aOuterProto) + mOuterProto(aOuterProto), + mOuterRealProto(aOuterRealProto) { NS_PRECONDITION(aWindow, "null window"); NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window"); @@ -1609,9 +1615,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, if (IsFrozen()) { // This outer is now getting its first inner, thaw the outer now // that it's ready and is getting an inner window. - mContext->CreateOuterObject(this, aDocument->NodePrincipal()); - mContext->DidInitializeContext(); - mJSObject = (JSObject *)mContext->GetNativeGlobal(); Thaw(); } @@ -1725,12 +1728,20 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, JSAutoRequest ar(cx); + nsCOMPtr wsh = do_QueryInterface(aState); + NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?"); + // Make sure to clear scope on the outer window *before* we // initialize the new inner window. If we don't, things // (Object.prototype etc) could leak from the old outer to the new // inner scope. mContext->ClearScope(mJSObject, PR_FALSE); + // This code should not be called during shutdown any more (now that + // we don't ever call SetNewDocument(nsnull), so no need to null + // check xpc here. + nsIXPConnect *xpc = nsContentUtils::XPConnect(); + nsCOMPtr wrapper; if (reUseInnerWindow) { // We're reusing the current inner window. NS_ASSERTION(!currentInner->IsFrozen(), @@ -1742,9 +1753,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, } } else { if (aState) { - nsCOMPtr wsh = do_QueryInterface(aState); - NS_ASSERTION(wsh, "What kind of weird state are you giving me here?"); - newInnerWindow = wsh->GetInnerWindow(); mInnerWindowHolder = wsh->GetInnerWindowHolder(); @@ -1818,7 +1826,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, getter_AddRefs(holder)); NS_ASSERTION(NS_SUCCEEDED(rv) && newGlobal && holder, "Failed to get script global and holder"); - newInnerWindow->mJSObject = (JSObject *)newGlobal; mCreatingInnerWindow = PR_FALSE; Thaw(); @@ -1872,6 +1879,45 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, } mInnerWindow = newInnerWindow; + + if (!mJSObject) { + mContext->CreateOuterObject(this, newInnerWindow); + mContext->DidInitializeContext(); + mJSObject = (JSObject *)mContext->GetNativeGlobal(); + } else { + // XXX New global object and brain transplant! + rv = xpc->GetWrappedNativeOfJSObject(cx, mJSObject, + getter_AddRefs(wrapper)); + NS_ENSURE_SUCCESS(rv, rv); + + // Restore our object's prototype to its original value so we're sure to + // update it under ReparentWrappedNativeIfFound. + JSObject *proto; + wrapper->GetJSObjectPrototype(&proto); + if (!JS_SetPrototype(cx, mJSObject, proto)) { + NS_ERROR("Can't set prototype"); + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr holder; + xpc->ReparentWrappedNativeIfFound(cx, currentInner->mJSObject, + newInnerWindow->mJSObject, + ToSupports(this), + getter_AddRefs(holder)); + + if (aState) { + if (nsIXPConnectJSObjectHolder *holder = wsh->GetOuterRealProto()) { + holder->GetJSObject(&proto); + } else { + proto = nsnull; + } + + if (!JS_SetPrototype(cx, mJSObject, proto)) { + NS_ERROR("can't set prototype"); + return NS_ERROR_FAILURE; + } + } + } } if (!aState && !reUseInnerWindow) { @@ -1924,42 +1970,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, html_doc); } - // This code should not be called during shutdown any more (now that - // we don't ever call SetNewDocument(nsnull), so no need to null - // check xpc here. - nsIXPConnect *xpc = nsContentUtils::XPConnect(); - - nsCOMPtr wrapper; - if (aState) { - // Restoring from session history. - - nsCOMPtr wsh = do_QueryInterface(aState); - NS_ASSERTION(wsh, "What kind of weird state are you giving me here?"); - - // Restore the prototype for the Window/ChromeWindow class in - // the outer window scope. - nsCOMPtr ci = - do_QueryInterface((nsIScriptGlobalObject *)this); - - rv = xpc->RestoreWrappedNativePrototype(cx, mJSObject, ci, - wsh->GetOuterProto()); - NS_ENSURE_SUCCESS(rv, rv); - - // Refresh the outer window's prototype to what it was when the - // window state was saved. This will make the outer window - // object (and wrapper) pick up the prototype it had when the - // window state was saved. This means Object.prototype etc from - // the old inner will again be on the outer window's prototype - // chain. - - rv = xpc->GetWrappedNativeOfJSObject(cx, mJSObject, - getter_AddRefs(wrapper)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = wrapper->RefreshPrototype(); - NS_ENSURE_SUCCESS(rv, rv); - } - if (aDocument) { aDocument->SetScriptGlobalObject(newInnerWindow); } @@ -9073,21 +9083,39 @@ nsGlobalWindow::SaveWindowState(nsISupports **aState) // to the page. inner->Freeze(); - // Remember the outer window's XPConnect prototype. + // Remember the outer window's prototype. + JSContext *cx = (JSContext *)mContext->GetNativeContext(); + JSAutoRequest req(cx); + + nsIXPConnect *xpc = nsContentUtils::XPConnect(); + nsCOMPtr ci = do_QueryInterface((nsIScriptGlobalObject *)this); nsCOMPtr proto; - nsresult rv = nsContentUtils::XPConnect()-> - GetWrappedNativePrototype((JSContext *)mContext->GetNativeContext(), - mJSObject, ci, getter_AddRefs(proto)); + nsresult rv = xpc->GetWrappedNativePrototype(cx, mJSObject, ci, + getter_AddRefs(proto)); NS_ENSURE_SUCCESS(rv, rv); + JSObject *realProto = JS_GetPrototype(cx, mJSObject); + nsCOMPtr realProtoHolder; + if (realProto) { + rv = xpc->HoldObject(cx, realProto, getter_AddRefs(realProtoHolder)); + NS_ENSURE_SUCCESS(rv, rv); + } + nsCOMPtr state = new WindowStateHolder(inner, mInnerWindowHolder, mNavigator, - proto); + proto, + realProtoHolder); NS_ENSURE_TRUE(state, NS_ERROR_OUT_OF_MEMORY); + JSObject *wnProto; + proto->GetJSObject(&wnProto); + if (!JS_SetPrototype(cx, mJSObject, wnProto)) { + return NS_ERROR_FAILURE; + } + #ifdef DEBUG_PAGE_CACHE printf("saving window state, state = %p\n", (void*)state); #endif diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 0891ec0b0146..cbfd9e217930 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -383,6 +383,11 @@ public: // Make sure this matches the casts we do in QueryInterface(). return (nsGlobalWindow *)(nsIScriptGlobalObject *)supports; } + static nsISupports *ToSupports(nsGlobalWindow *win) + { + // Make sure this matches the casts we do in QueryInterface(). + return (nsISupports *)(nsIScriptGlobalObject *)win; + } static nsGlobalWindow *FromWrapper(nsIXPConnectWrappedNative *wrapper) { return FromSupports(wrapper->Native()); diff --git a/dom/base/nsIScriptContext.h b/dom/base/nsIScriptContext.h index dd9480ab4e6a..314172ca1510 100644 --- a/dom/base/nsIScriptContext.h +++ b/dom/base/nsIScriptContext.h @@ -344,11 +344,11 @@ public: * @param aGlobalObject The script global object to use as our global. */ virtual nsresult CreateOuterObject(nsIScriptGlobalObject *aGlobalObject, - nsIPrincipal *aPrincipal) = 0; + nsIScriptGlobalObject *aCurrentInner) = 0; /** * Prepares this context for use with the current inner window for the - * context's global object. This must be called after InitOuterWindow. + * context's global object. This must be called after CreateOuterObject. */ virtual nsresult InitOuterWindow() = 0; diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 062b26273e5f..62a9f9655b9a 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2594,7 +2594,7 @@ nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, void *aOuterGlobal { NS_ENSURE_ARG(aNewInner); JSObject *newInnerJSObject = (JSObject *)aNewInner->GetScriptGlobal(JAVASCRIPT); - JSObject *myobject = (JSObject *)aOuterGlobal; + JSObject *outerGlobal = (JSObject *)aOuterGlobal; // Make the inner and outer window both share the same // prototype. The prototype we share is the outer window's @@ -2615,12 +2615,18 @@ nsJSContext::ConnectToInner(nsIScriptGlobalObject *aNewInner, void *aOuterGlobal // Object.prototype. This way the outer also gets the benefits // of the global scope polluter, and the inner window's // Object.prototype. - JSObject *proto = ::JS_GetPrototype(mContext, myobject); - JSObject *innerProto = ::JS_GetPrototype(mContext, newInnerJSObject); - JSObject *innerProtoProto = ::JS_GetPrototype(mContext, innerProto); + JSObject *proto = JS_GetPrototype(mContext, outerGlobal); + JSObject *innerProto = JS_GetPrototype(mContext, newInnerJSObject); + JSObject *innerProtoProto = JS_GetPrototype(mContext, innerProto); - ::JS_SetPrototype(mContext, newInnerJSObject, proto); - ::JS_SetPrototype(mContext, proto, innerProtoProto); + JS_SetPrototype(mContext, newInnerJSObject, proto); + JS_SetPrototype(mContext, proto, innerProtoProto); + + // Now that we're connecting the outer global to the inner one, + // we must have transplanted it. The JS engine tries to maintain + // the global object's compartment as its default compartment, + // so update that now since it might have changed. + JS_SetGlobalObject(mContext, outerGlobal); return NS_OK; } @@ -2660,11 +2666,8 @@ nsJSContext::InitContext() nsresult nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject, - nsIPrincipal *aPrincipal) + nsIScriptGlobalObject *aCurrentInner) { - NS_PRECONDITION(!JS_GetGlobalObject(mContext), - "Outer window already initialized"); - nsCOMPtr chromeWindow(do_QueryInterface(aGlobalObject)); PRUint32 flags = 0; @@ -2678,18 +2681,21 @@ nsJSContext::CreateOuterObject(nsIScriptGlobalObject *aGlobalObject, // need to preserve the