Bug 549452: Clean up nsGlobalWindow::SetNewDocument. r=mrbkap sr=jst

--HG--
extra : rebase_source : 1e13da2a339089ccf17b8cf9f41d59e188aa8165
This commit is contained in:
Jonas Sicking 2010-03-12 16:59:18 -08:00
parent 8ce014db8d
commit 986107fb30
5 changed files with 421 additions and 428 deletions

View File

@ -1961,7 +1961,7 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace)
mWillReparent = PR_TRUE;
#endif
rv = window->SetNewDocument(this, nsnull, PR_FALSE);
rv = window->SetNewDocument(this, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
#ifdef DEBUG

View File

@ -1568,39 +1568,18 @@ NS_IMPL_ISUPPORTS1(WindowStateHolder, WindowStateHolder)
nsresult
nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
nsISupports* aState,
PRBool aClearScopeHint)
nsISupports* aState)
{
return SetNewDocument(aDocument, aState, aClearScopeHint, PR_FALSE);
}
NS_PRECONDITION(mDocumentPrincipal == nsnull,
"mDocumentPrincipal prematurely set!");
nsresult
nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
nsISupports* aState,
PRBool aClearScopeHint,
PRBool aIsInternalCall)
{
NS_ASSERTION(mDocumentPrincipal == nsnull,
"mDocumentPrincipal prematurely set!");
#ifdef PR_LOGGING
if (IsInnerWindow() && aDocument && gDOMLeakPRLog &&
PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
nsIURI *uri = aDocument->GetDocumentURI();
nsCAutoString spec;
if (uri)
uri->GetSpec(spec);
PR_LogPrint("DOMWINDOW %p SetNewDocument %s", this, spec.get());
}
#endif
if (!aDocument) {
NS_ERROR("SetNewDocument(null) called!");
if (IsOuterWindow() && IsFrozen()) {
// This outer is now getting its first inner, thaw the outer now
// that it's ready and is getting an inner window.
Thaw();
return NS_ERROR_INVALID_ARG;
}
if (!aIsInternalCall && IsInnerWindow()) {
if (IsInnerWindow()) {
if (!mOuterWindow) {
return NS_ERROR_NOT_INITIALIZED;
}
@ -1611,15 +1590,15 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
return NS_ERROR_NOT_AVAILABLE;
}
return GetOuterWindowInternal()->SetNewDocument(aDocument,
aState,
aClearScopeHint, PR_TRUE);
return GetOuterWindowInternal()->SetNewDocument(aDocument, aState);
}
if (!aDocument) {
NS_ERROR("SetNewDocument(null) called!");
NS_PRECONDITION(IsOuterWindow(), "Must only be called on outer windows");
return NS_ERROR_INVALID_ARG;
if (IsFrozen()) {
// This outer is now getting its first inner, thaw the outer now
// that it's ready and is getting an inner window.
Thaw();
}
NS_ASSERTION(!GetCurrentInnerWindow() ||
@ -1667,16 +1646,10 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
SetStatus(EmptyString());
SetDefaultStatus(EmptyString());
// 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();
PRBool reUseInnerWindow = WouldReuseInnerWindow(aDocument);
// Remember the old document's principal.
nsIPrincipal *oldPrincipal = nsnull;
if (oldDoc) {
oldPrincipal = oldDoc->NodePrincipal();
}
@ -1707,11 +1680,405 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
mNavigator->LoadingNewDocument();
}
PRUint32 st_id, st_ndx; // we loop over all our context/globs lots!
// Set mDocument even if this is an outer window to avoid
// having to *always* reach into the inner window to find the
// document.
mDocument = do_QueryInterface(aDocument);
mDoc = aDocument;
#ifdef DEBUG
mLastOpenedURI = aDocument->GetDocumentURI();
#endif
PRUint32 st_id; // we loop over all our context/globs lots!
NS_STID_FOR_ID(st_id) {
nsIScriptContext *langContext = GetScriptContextInternal(st_id);
if (langContext)
langContext->WillInitializeContext();
}
nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
nsRefPtr<nsGlobalWindow> newInnerWindow;
nsCOMPtr<nsIDOMChromeWindow> thisChrome =
do_QueryInterface(static_cast<nsIDOMWindow *>(this));
nsCOMPtr<nsIXPConnectJSObjectHolder> navigatorHolder;
jsval nav;
PRBool isChrome = PR_FALSE;
nsCxPusher cxPusher;
if (!cxPusher.Push(cx)) {
return NS_ERROR_FAILURE;
}
JSAutoRequest ar(cx);
// 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.
NS_STID_FOR_ID(st_id) {
nsIScriptContext *langContext = GetScriptContextInternal(st_id);
if (langContext)
langContext->ClearScope(mScriptGlobals[NS_STID_INDEX(st_id)],
PR_FALSE);
}
if (reUseInnerWindow) {
// We're reusing the current inner window.
NS_ASSERTION(!currentInner->IsFrozen(),
"We should never be reusing a shared inner window");
newInnerWindow = currentInner;
if (aDocument != oldDoc) {
nsWindowSH::InvalidateGlobalScopePolluter(cx, currentInner->mJSObject);
}
} else {
if (aState) {
nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
NS_ASSERTION(wsh, "What kind of weird state are you giving me here?");
newInnerWindow = wsh->GetInnerWindow();
NS_STID_FOR_ID(st_id) {
mInnerWindowHolders[NS_STID_INDEX(st_id)] = wsh->GetInnerWindowHolder(st_id);
}
// These assignments addref.
mNavigator = wsh->GetNavigator();
mLocation = wsh->GetLocation();
if (mNavigator) {
// Update mNavigator's docshell pointer now.
mNavigator->SetDocShell(mDocShell);
mNavigator->LoadingNewDocument();
}
} else {
if (thisChrome) {
newInnerWindow = new nsGlobalChromeWindow(this);
isChrome = PR_TRUE;
} else {
if (mIsModalContentWindow) {
newInnerWindow = new nsGlobalModalWindow(this);
} else {
newInnerWindow = new nsGlobalWindow(this);
}
}
mLocation = nsnull;
}
if (!newInnerWindow) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (currentInner && currentInner->mJSObject) {
if (mNavigator && !aState) {
// Hold on to the navigator wrapper so that we can set
// window.navigator in the new window to point to the same
// object (assuming we didn't change origins etc). See bug
// 163645 for more on why we need this.
nsIDOMNavigator* navigator =
static_cast<nsIDOMNavigator*>(mNavigator.get());
nsContentUtils::WrapNative(cx, currentInner->mJSObject, navigator,
&NS_GET_IID(nsIDOMNavigator), &nav,
getter_AddRefs(navigatorHolder));
}
}
if (!aState) {
// This is redundant if we're restoring from a previous inner window.
nsIScriptGlobalObject *sgo =
(nsIScriptGlobalObject *)newInnerWindow.get();
// Freeze the outer window and null out the inner window so
// that initializing classes on the new inner doesn't end up
// reaching into the old inner window for classes etc.
//
// [This happens with Object.prototype when XPConnect creates
// a temporary global while initializing classes; the reason
// being that xpconnect creates the temp global w/o a parent
// and proto, which makes the JS engine look up classes in
// cx->globalObject, i.e. this outer window].
mInnerWindow = nsnull;
Freeze();
mCreatingInnerWindow = PR_TRUE;
// Every script context we are initialized with must create a
// new global.
rv = NS_OK;
NS_STID_FOR_ID(st_id) {
PRUint32 st_ndx = NS_STID_INDEX(st_id);
nsIScriptContext *this_ctx = GetScriptContextInternal(st_id);
if (this_ctx) {
void *&newGlobal = newInnerWindow->mScriptGlobals[st_ndx];
nsCOMPtr<nsISupports> &holder = mInnerWindowHolders[st_ndx];
rv |= this_ctx->CreateNativeGlobalForInner(sgo, isChrome,
&newGlobal,
getter_AddRefs(holder));
NS_ASSERTION(NS_SUCCEEDED(rv) && newGlobal && holder,
"Failed to get script global and holder");
if (st_id == nsIProgrammingLanguage::JAVASCRIPT) {
newInnerWindow->mJSObject = (JSObject *)newGlobal;
}
}
}
mCreatingInnerWindow = PR_FALSE;
Thaw();
NS_ENSURE_SUCCESS(rv, rv);
}
if (currentInner && currentInner->mJSObject) {
PRBool termFuncSet = PR_FALSE;
if (oldDoc == aDocument) {
// Suspend the current context's request before Pop() resumes the old
// context's request.
JSAutoSuspendRequest asr(cx);
// Pop our context here so that we get the correct one for the
// termination function.
cxPusher.Pop();
JSContext *oldCx = nsContentUtils::GetCurrentJSContext();
nsIScriptContext *callerScx;
if (oldCx && (callerScx = GetScriptContextFromJSContext(oldCx))) {
// We're called from document.open() (and document.open() is
// called from JS), clear the scope etc in a termination
// function on the calling context to prevent clearing the
// calling scope.
NS_ASSERTION(!currentInner->IsFrozen(),
"How does this opened window get into session history");
JSAutoRequest ar(oldCx);
callerScx->SetTerminationFunction(ClearWindowScope,
static_cast<nsIDOMWindow *>
(currentInner));
termFuncSet = PR_TRUE;
}
// Re-push our context.
cxPusher.Push(cx);
}
// Don't clear scope on our current inner window if it's going to be
// held in the bfcache.
if (!currentInner->IsFrozen()) {
// Skip the ClearScope if we set a termination function to do
// it ourselves, later.
currentInner->FreeInnerObjects(!termFuncSet);
}
}
mInnerWindow = newInnerWindow;
}
if (!aState && !reUseInnerWindow) {
// Loading a new page and creating a new inner window, *not*
// restoring from session history.
// InitClassesWithNewWrappedGlobal() (via CreateNativeGlobalForInner)
// for the new inner window
// sets the global object in cx to be the new wrapped global. We
// don't want that, but re-initializing the outer window will
// fix that for us. And perhaps more importantly, this will
// ensure that the outer window gets a new prototype so we don't
// leak prototype properties from the old inner window to the
// new one.
NS_STID_FOR_ID(st_id) {
nsIScriptContext *this_ctx = GetScriptContextInternal(st_id);
if (st_id == nsIProgrammingLanguage::JAVASCRIPT)
JS_BeginRequest((JSContext *)this_ctx->GetNativeContext());
if (this_ctx) {
this_ctx->InitContext(this);
// Now that both the the inner and outer windows are initialized
// let each script context do its magic to hook them together.
void *glob = mScriptGlobals[NS_STID_INDEX(st_id)];
this_ctx->ConnectToInner(newInnerWindow, glob);
}
if (st_id == nsIProgrammingLanguage::JAVASCRIPT)
JS_EndRequest((JSContext *)this_ctx->GetNativeContext());
}
nsCOMPtr<nsIContent> frame = do_QueryInterface(GetFrameElementInternal());
if (frame && frame->GetOwnerDoc()) {
nsPIDOMWindow* parentWindow = frame->GetOwnerDoc()->GetWindow();
if (parentWindow && parentWindow->TimeoutSuspendCount()) {
SuspendTimeouts(parentWindow->TimeoutSuspendCount());
}
}
}
// Tell the contexts we have completed setting up the doc.
NS_STID_FOR_ID(st_id) {
// Add an extra ref in case we release mContext during GC.
nsCOMPtr<nsIScriptContext> this_ctx =
GetScriptContextInternal(st_id);
if (this_ctx) {
nsCOMPtr<nsIDOMDocument> dd(do_QueryInterface(aDocument));
this_ctx->DidSetDocument(dd, newInnerWindow->GetScriptGlobal(st_id));
}
}
// Now that the prototype is all set up, install the global scope
// polluter. This must happen after the above prototype fixup. If
// the GSP was to be installed on the inner window's real
// prototype (as it would be if this was done before the prototype
// fixup above) we would end up holding the GSP alive (through
// XPConnect's internal marking of wrapper prototypes) as long as
// the inner window was around, and if the GSP had properties on
// it that held an element alive we'd hold the document alive,
// which could hold event handlers alive, which hold the context
// alive etc.
if ((!reUseInnerWindow || aDocument != oldDoc) && !aState) {
nsCOMPtr<nsIHTMLDocument> html_doc(do_QueryInterface(mDocument));
nsWindowSH::InstallGlobalScopePolluter(cx, newInnerWindow->mJSObject,
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();
if (aState) {
// Restoring from session history.
nsCOMPtr<WindowStateHolder> 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<nsIClassInfo> 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.
nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
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);
}
if (!aState) {
if (reUseInnerWindow) {
if (newInnerWindow->mDoc != aDocument) {
newInnerWindow->mDocument = do_QueryInterface(aDocument);
newInnerWindow->mDoc = aDocument;
// We're reusing the inner window for a new document. In this
// case we don't clear the inner window's scope, but we must
// make sure the cached document property gets updated.
// XXXmarkh - tell other languages about this?
JSAutoRequest ar(cx);
::JS_DeleteProperty(cx, currentInner->mJSObject, "document");
}
} else {
rv = newInnerWindow->InnerWindowSetNewDocument(aDocument);
NS_ENSURE_SUCCESS(rv, rv);
NS_STID_FOR_ID(st_id) {
nsIScriptContext *this_ctx = GetScriptContextInternal(st_id);
if (this_ctx) {
// Initialize DOM classes etc on the inner window.
void *glob = newInnerWindow->mScriptGlobals[NS_STID_INDEX(st_id)];
rv = this_ctx->InitClasses(glob);
NS_ENSURE_SUCCESS(rv, rv);
if (navigatorHolder &&
st_id == nsIProgrammingLanguage::JAVASCRIPT) {
// Restore window.navigator onto the new inner window.
JSAutoRequest ar(cx);
::JS_DefineProperty(cx, newInnerWindow->mJSObject, "navigator",
nav, nsnull, nsnull,
JSPROP_ENUMERATE | JSPROP_PERMANENT |
JSPROP_READONLY);
// The Navigator's prototype object keeps a reference to the
// window in which it was first created and can thus cause that
// window to stay alive for too long. Reparenting it here allows
// the window to be collected sooner.
nsIDOMNavigator* navigator =
static_cast<nsIDOMNavigator*>(mNavigator);
xpc->
ReparentWrappedNativeIfFound(cx, JSVAL_TO_OBJECT(nav),
newInnerWindow->mJSObject,
navigator,
getter_AddRefs(navigatorHolder));
}
}
}
}
if (mArguments) {
newInnerWindow->DefineArgumentsProperty(mArguments);
newInnerWindow->mArguments = mArguments;
newInnerWindow->mArgumentsOrigin = mArgumentsOrigin;
mArguments = nsnull;
mArgumentsOrigin = nsnull;
}
// Give the new inner window our chrome event handler (since it
// doesn't have one).
newInnerWindow->mChromeEventHandler = mChromeEventHandler;
}
NS_STID_FOR_ID(st_id) {
// Add an extra ref in case we release mContext during GC.
nsCOMPtr<nsIScriptContext> this_ctx =
GetScriptContextInternal(st_id);
if (this_ctx) {
this_ctx->GC();
this_ctx->DidInitializeContext();
}
}
return NS_OK;
}
nsresult
nsGlobalWindow::InnerWindowSetNewDocument(nsIDocument* aDocument)
{
NS_PRECONDITION(IsInnerWindow(), "Must only be called on inner windows");
#ifdef PR_LOGGING
if (aDocument && gDOMLeakPRLog &&
PR_LOG_TEST(gDOMLeakPRLog, PR_LOG_DEBUG)) {
nsIURI *uri = aDocument->GetDocumentURI();
nsCAutoString spec;
if (uri)
uri->GetSpec(spec);
PR_LogPrint("DOMWINDOW %p SetNewDocument %s", this, spec.get());
}
#endif
mDocument = do_QueryInterface(aDocument);
mDoc = aDocument;
@ -1722,374 +2089,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
mLastOpenedURI = aDocument->GetDocumentURI();
#endif
if (IsOuterWindow()) {
NS_STID_FOR_ID(st_id) {
nsIScriptContext *langContext = GetScriptContextInternal(st_id);
if (langContext)
langContext->WillInitializeContext();
}
nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal();
nsRefPtr<nsGlobalWindow> newInnerWindow;
nsCOMPtr<nsIDOMChromeWindow> thisChrome =
do_QueryInterface(static_cast<nsIDOMWindow *>(this));
nsCOMPtr<nsIXPConnectJSObjectHolder> navigatorHolder;
jsval nav;
PRBool isChrome = PR_FALSE;
nsCxPusher cxPusher;
if (!cxPusher.Push(cx)) {
return NS_ERROR_FAILURE;
}
JSAutoRequest ar(cx);
// 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.
NS_STID_FOR_ID(st_id) {
nsIScriptContext *langContext = GetScriptContextInternal(st_id);
if (langContext)
langContext->ClearScope(mScriptGlobals[NS_STID_INDEX(st_id)],
PR_FALSE);
}
if (reUseInnerWindow) {
// We're reusing the current inner window.
NS_ASSERTION(!currentInner->IsFrozen(),
"We should never be reusing a shared inner window");
newInnerWindow = currentInner;
if (aDocument != oldDoc) {
nsWindowSH::InvalidateGlobalScopePolluter(cx, currentInner->mJSObject);
}
} else {
if (aState) {
nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
NS_ASSERTION(wsh, "What kind of weird state are you giving me here?");
newInnerWindow = wsh->GetInnerWindow();
NS_STID_FOR_ID(st_id) {
mInnerWindowHolders[NS_STID_INDEX(st_id)] = wsh->GetInnerWindowHolder(st_id);
}
// These assignments addref.
mNavigator = wsh->GetNavigator();
mLocation = wsh->GetLocation();
if (mNavigator) {
// Update mNavigator's docshell pointer now.
mNavigator->SetDocShell(mDocShell);
mNavigator->LoadingNewDocument();
}
} else {
if (thisChrome) {
newInnerWindow = new nsGlobalChromeWindow(this);
isChrome = PR_TRUE;
} else {
if (mIsModalContentWindow) {
newInnerWindow = new nsGlobalModalWindow(this);
} else {
newInnerWindow = new nsGlobalWindow(this);
}
}
mLocation = nsnull;
}
if (!newInnerWindow) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (currentInner && currentInner->mJSObject) {
if (mNavigator && !aState) {
// Hold on to the navigator wrapper so that we can set
// window.navigator in the new window to point to the same
// object (assuming we didn't change origins etc). See bug
// 163645 for more on why we need this.
nsIDOMNavigator* navigator =
static_cast<nsIDOMNavigator*>(mNavigator.get());
nsContentUtils::WrapNative(cx, currentInner->mJSObject, navigator,
&NS_GET_IID(nsIDOMNavigator), &nav,
getter_AddRefs(navigatorHolder));
}
}
if (!aState) {
// This is redundant if we're restoring from a previous inner window.
nsIScriptGlobalObject *sgo =
(nsIScriptGlobalObject *)newInnerWindow.get();
// Freeze the outer window and null out the inner window so
// that initializing classes on the new inner doesn't end up
// reaching into the old inner window for classes etc.
//
// [This happens with Object.prototype when XPConnect creates
// a temporary global while initializing classes; the reason
// being that xpconnect creates the temp global w/o a parent
// and proto, which makes the JS engine look up classes in
// cx->globalObject, i.e. this outer window].
mInnerWindow = nsnull;
Freeze();
mCreatingInnerWindow = PR_TRUE;
// Every script context we are initialized with must create a
// new global.
rv = NS_OK;
NS_STID_FOR_ID(st_id) {
st_ndx = NS_STID_INDEX(st_id);
nsIScriptContext *this_ctx = GetScriptContextInternal(st_id);
if (this_ctx) {
void *&newGlobal = newInnerWindow->mScriptGlobals[st_ndx];
nsCOMPtr<nsISupports> &holder = mInnerWindowHolders[st_ndx];
rv |= this_ctx->CreateNativeGlobalForInner(sgo, isChrome,
&newGlobal,
getter_AddRefs(holder));
NS_ASSERTION(NS_SUCCEEDED(rv) && newGlobal && holder,
"Failed to get script global and holder");
if (st_id == nsIProgrammingLanguage::JAVASCRIPT) {
newInnerWindow->mJSObject = (JSObject *)newGlobal;
}
}
}
mCreatingInnerWindow = PR_FALSE;
Thaw();
NS_ENSURE_SUCCESS(rv, rv);
}
if (currentInner && currentInner->mJSObject) {
PRBool termFuncSet = PR_FALSE;
if (oldDoc == aDocument) {
// Suspend the current context's request before Pop() resumes the old
// context's request.
JSAutoSuspendRequest asr(cx);
// Pop our context here so that we get the correct one for the
// termination function.
cxPusher.Pop();
JSContext *oldCx = nsContentUtils::GetCurrentJSContext();
nsIScriptContext *callerScx;
if (oldCx && (callerScx = GetScriptContextFromJSContext(oldCx))) {
// We're called from document.open() (and document.open() is
// called from JS), clear the scope etc in a termination
// function on the calling context to prevent clearing the
// calling scope.
NS_ASSERTION(!currentInner->IsFrozen(),
"How does this opened window get into session history");
JSAutoRequest ar(oldCx);
callerScx->SetTerminationFunction(ClearWindowScope,
static_cast<nsIDOMWindow *>
(currentInner));
termFuncSet = PR_TRUE;
}
// Re-push our context.
cxPusher.Push(cx);
}
// Don't clear scope on our current inner window if it's going to be
// held in the bfcache.
if (!currentInner->IsFrozen()) {
// Skip the ClearScope if we set a termination function to do
// it ourselves, later.
currentInner->FreeInnerObjects(!termFuncSet);
}
}
mInnerWindow = newInnerWindow;
}
if (!aState && !reUseInnerWindow) {
// Loading a new page and creating a new inner window, *not*
// restoring from session history.
// InitClassesWithNewWrappedGlobal() (via CreateNativeGlobalForInner)
// for the new inner window
// sets the global object in cx to be the new wrapped global. We
// don't want that, but re-initializing the outer window will
// fix that for us. And perhaps more importantly, this will
// ensure that the outer window gets a new prototype so we don't
// leak prototype properties from the old inner window to the
// new one.
NS_STID_FOR_ID(st_id) {
nsIScriptContext *this_ctx = GetScriptContextInternal(st_id);
if (st_id == nsIProgrammingLanguage::JAVASCRIPT)
JS_BeginRequest((JSContext *)this_ctx->GetNativeContext());
if (this_ctx) {
this_ctx->InitContext(this);
// Now that both the the inner and outer windows are initialized
// let each script context do its magic to hook them together.
void *glob = mScriptGlobals[NS_STID_INDEX(st_id)];
this_ctx->ConnectToInner(newInnerWindow, glob);
}
if (st_id == nsIProgrammingLanguage::JAVASCRIPT)
JS_EndRequest((JSContext *)this_ctx->GetNativeContext());
}
nsCOMPtr<nsIContent> frame = do_QueryInterface(GetFrameElementInternal());
if (frame && frame->GetOwnerDoc()) {
nsPIDOMWindow* parentWindow = frame->GetOwnerDoc()->GetWindow();
if (parentWindow && parentWindow->TimeoutSuspendCount()) {
SuspendTimeouts(parentWindow->TimeoutSuspendCount());
}
}
}
// Tell the contexts we have completed setting up the doc.
NS_STID_FOR_ID(st_id) {
// Add an extra ref in case we release mContext during GC.
nsCOMPtr<nsIScriptContext> this_ctx =
GetScriptContextInternal(st_id);
if (this_ctx) {
nsCOMPtr<nsIDOMDocument> dd(do_QueryInterface(aDocument));
this_ctx->DidSetDocument(dd, newInnerWindow->GetScriptGlobal(st_id));
}
}
// Now that the prototype is all set up, install the global scope
// polluter. This must happen after the above prototype fixup. If
// the GSP was to be installed on the inner window's real
// prototype (as it would be if this was done before the prototype
// fixup above) we would end up holding the GSP alive (through
// XPConnect's internal marking of wrapper prototypes) as long as
// the inner window was around, and if the GSP had properties on
// it that held an element alive we'd hold the document alive,
// which could hold event handlers alive, which hold the context
// alive etc.
if ((!reUseInnerWindow || aDocument != oldDoc) && !aState) {
nsCOMPtr<nsIHTMLDocument> html_doc(do_QueryInterface(mDocument));
nsWindowSH::InstallGlobalScopePolluter(cx, newInnerWindow->mJSObject,
html_doc);
}
if (aState) {
// Restoring from session history.
nsCOMPtr<WindowStateHolder> 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<nsIClassInfo> 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.
nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
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);
}
if (!aState) {
if (reUseInnerWindow) {
if (newInnerWindow->mDoc != aDocument) {
newInnerWindow->mDocument = do_QueryInterface(aDocument);
newInnerWindow->mDoc = aDocument;
// We're reusing the inner window for a new document. In this
// case we don't clear the inner window's scope, but we must
// make sure the cached document property gets updated.
// XXXmarkh - tell other languages about this?
JSAutoRequest ar(cx);
::JS_DeleteProperty(cx, currentInner->mJSObject, "document");
}
} else {
rv = newInnerWindow->SetNewDocument(aDocument, nsnull,
aClearScopeHint, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
NS_STID_FOR_ID(st_id) {
nsIScriptContext *this_ctx = GetScriptContextInternal(st_id);
if (this_ctx) {
// Initialize DOM classes etc on the inner window.
void *glob = newInnerWindow->mScriptGlobals[NS_STID_INDEX(st_id)];
rv = this_ctx->InitClasses(glob);
NS_ENSURE_SUCCESS(rv, rv);
if (navigatorHolder &&
st_id == nsIProgrammingLanguage::JAVASCRIPT) {
// Restore window.navigator onto the new inner window.
JSAutoRequest ar(cx);
::JS_DefineProperty(cx, newInnerWindow->mJSObject, "navigator",
nav, nsnull, nsnull,
JSPROP_ENUMERATE | JSPROP_PERMANENT |
JSPROP_READONLY);
// The Navigator's prototype object keeps a reference to the
// window in which it was first created and can thus cause that
// window to stay alive for too long. Reparenting it here allows
// the window to be collected sooner.
nsIDOMNavigator* navigator =
static_cast<nsIDOMNavigator*>(mNavigator);
xpc->
ReparentWrappedNativeIfFound(cx, JSVAL_TO_OBJECT(nav),
newInnerWindow->mJSObject,
navigator,
getter_AddRefs(navigatorHolder));
}
}
}
}
if (mArguments) {
newInnerWindow->DefineArgumentsProperty(mArguments);
newInnerWindow->mArguments = mArguments;
newInnerWindow->mArgumentsOrigin = mArgumentsOrigin;
mArguments = nsnull;
mArgumentsOrigin = nsnull;
}
// Give the new inner window our chrome event handler (since it
// doesn't have one).
newInnerWindow->mChromeEventHandler = mChromeEventHandler;
}
NS_STID_FOR_ID(st_id) {
// Add an extra ref in case we release mContext during GC.
nsCOMPtr<nsIScriptContext> this_ctx =
GetScriptContextInternal(st_id);
if (this_ctx) {
this_ctx->GC();
this_ctx->DidInitializeContext();
}
}
}
// Clear our mutation bitfield.
mMutationBits = 0;
@ -9445,8 +9444,7 @@ nsGlobalModalWindow::SetReturnValue(nsIVariant *aRetVal)
nsresult
nsGlobalModalWindow::SetNewDocument(nsIDocument *aDocument,
nsISupports *aState,
PRBool aClearScopeHint)
nsISupports *aState)
{
// If we're loading a new document into a modal dialog, clear the
// return value that was set, if any, by the current document.
@ -9454,7 +9452,7 @@ nsGlobalModalWindow::SetNewDocument(nsIDocument *aDocument,
mReturnValue = nsnull;
}
return nsGlobalWindow::SetNewDocument(aDocument, aState, aClearScopeHint);
return nsGlobalWindow::SetNewDocument(aDocument, aState);
}
//*****************************************************************************

View File

@ -335,8 +335,7 @@ public:
virtual NS_HIDDEN_(void) SetDocShell(nsIDocShell* aDocShell);
virtual NS_HIDDEN_(nsresult) SetNewDocument(nsIDocument *aDocument,
nsISupports *aState,
PRBool aClearScopeHint);
nsISupports *aState);
virtual NS_HIDDEN_(void) SetOpenerWindow(nsIDOMWindowInternal *aOpener,
PRBool aOriginalOpener);
virtual NS_HIDDEN_(void) EnsureSizeUpToDate();
@ -463,10 +462,8 @@ protected:
void FreeInnerObjects(PRBool aClearScope);
nsGlobalWindow *CallerInnerWindow();
nsresult SetNewDocument(nsIDocument *aDocument,
nsISupports *aState,
PRBool aClearScopeHint,
PRBool aIsInternalCall);
nsresult InnerWindowSetNewDocument(nsIDocument* aDocument);
nsresult DefineArgumentsProperty(nsIArray *aArguments);
// Get the parent, returns null if this is a toplevel window
@ -848,8 +845,7 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
virtual NS_HIDDEN_(nsresult) SetNewDocument(nsIDocument *aDocument,
nsISupports *aState,
PRBool aClearScopeHint);
nsISupports *aState);
protected:
nsCOMPtr<nsIVariant> mReturnValue;

View File

@ -78,8 +78,8 @@ class nsIArray;
class nsPIWindowRoot;
#define NS_PIDOMWINDOW_IID \
{ 0x2962cfa4, 0x13f9, 0x4606, \
{ 0x84, 0x64, 0xef, 0x4c, 0xfa, 0x33, 0xcc, 0xce } }
{ 0x81cdf500, 0x2183, 0x4af6, \
{ 0xa4, 0x56, 0x35, 0x1f, 0x4a, 0x0d, 0x1a, 0x0b } }
class nsPIDOMWindow : public nsIDOMWindowInternal
{
@ -353,8 +353,7 @@ public:
* created.
*/
virtual nsresult SetNewDocument(nsIDocument *aDocument,
nsISupports *aState,
PRBool aClearScope) = 0;
nsISupports *aState) = 0;
/**
* Set the opener window. aOriginalOpener is true if and only if this is the

View File

@ -946,7 +946,7 @@ DocumentViewerImpl::InitInternal(nsIWidget* aParentWidget,
getter_AddRefs(window));
if (window) {
window->SetNewDocument(mDocument, aState, PR_TRUE);
window->SetNewDocument(mDocument, aState);
nsJSContext::LoadStart();
}
@ -1695,7 +1695,7 @@ DocumentViewerImpl::SetDOMDocument(nsIDOMDocument *aDocument)
// Set the script global object on the new document
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(container);
if (window) {
window->SetNewDocument(newDoc, nsnull, PR_TRUE);
window->SetNewDocument(newDoc, nsnull);
}
// Clear the list of old child docshells. CChild docshells for the new