mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 549452: Clean up nsGlobalWindow::SetNewDocument. r=mrbkap sr=jst
--HG-- extra : rebase_source : 1e13da2a339089ccf17b8cf9f41d59e188aa8165
This commit is contained in:
parent
8ce014db8d
commit
986107fb30
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
//*****************************************************************************
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user