diff --git a/mozilla/content/html/document/src/nsHTMLDocument.cpp b/mozilla/content/html/document/src/nsHTMLDocument.cpp index 75c67b7c1eb..8594efafa60 100644 --- a/mozilla/content/html/document/src/nsHTMLDocument.cpp +++ b/mozilla/content/html/document/src/nsHTMLDocument.cpp @@ -1915,8 +1915,8 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace) nsCOMPtr kungFuDeathGrip = do_QueryInterface((nsIHTMLDocument*)this); - rv = mScriptGlobalObject->SetNewDocument((nsDocument *)this, PR_FALSE, - PR_FALSE); + rv = mScriptGlobalObject->SetNewDocument((nsDocument *)this, nsnull, + PR_FALSE, PR_FALSE); if (NS_FAILED(rv)) { return rv; diff --git a/mozilla/content/xbl/src/nsXBLDocumentInfo.cpp b/mozilla/content/xbl/src/nsXBLDocumentInfo.cpp index 43a8d43419c..c8b440f6cf3 100644 --- a/mozilla/content/xbl/src/nsXBLDocumentInfo.cpp +++ b/mozilla/content/xbl/src/nsXBLDocumentInfo.cpp @@ -67,6 +67,7 @@ public: virtual void SetContext(nsIScriptContext *aContext); virtual nsIScriptContext *GetContext(); virtual nsresult SetNewDocument(nsIDOMDocument *aDocument, + nsISupports *aState, PRBool aRemoveEventListeners, PRBool aClearScope); virtual void SetDocShell(nsIDocShell *aDocShell); @@ -220,6 +221,7 @@ nsXBLDocGlobalObject::GetContext() nsresult nsXBLDocGlobalObject::SetNewDocument(nsIDOMDocument *aDocument, + nsISupports *aState, PRBool aRemoveEventListeners, PRBool aClearScope) { diff --git a/mozilla/content/xul/document/src/nsXULPrototypeDocument.cpp b/mozilla/content/xul/document/src/nsXULPrototypeDocument.cpp index cc6c27ba75f..2833c2148b3 100644 --- a/mozilla/content/xul/document/src/nsXULPrototypeDocument.cpp +++ b/mozilla/content/xul/document/src/nsXULPrototypeDocument.cpp @@ -89,6 +89,7 @@ public: virtual void SetContext(nsIScriptContext *aContext); virtual nsIScriptContext *GetContext(); virtual nsresult SetNewDocument(nsIDOMDocument *aDocument, + nsISupports *aState, PRBool aRemoveEventListeners, PRBool aClearScope); virtual void SetDocShell(nsIDocShell *aDocShell); @@ -846,6 +847,7 @@ nsXULPDGlobalObject::GetContext() nsresult nsXULPDGlobalObject::SetNewDocument(nsIDOMDocument *aDocument, + nsISupports *aState, PRBool aRemoveEventListeners, PRBool aClearScope) { diff --git a/mozilla/docshell/base/nsDocShell.cpp b/mozilla/docshell/base/nsDocShell.cpp index 13864d7450d..87427bc9d10 100644 --- a/mozilla/docshell/base/nsDocShell.cpp +++ b/mozilla/docshell/base/nsDocShell.cpp @@ -5233,8 +5233,14 @@ nsDocShell::RestoreFromHistory() mContentViewer.swap(viewer); viewer = nsnull; // force a release to complete ownership transfer + // Grab the window state up here so we can pass it to Open. + nsCOMPtr windowState; + mLSHE->GetWindowState(getter_AddRefs(windowState)); + + mLSHE->SetWindowState(nsnull); + // Reattach to the window object. - rv = mContentViewer->Open(); + rv = mContentViewer->Open(windowState); // Now remove it from the cached presentation. mLSHE->SetContentViewer(nsnull); @@ -5285,14 +5291,9 @@ nsDocShell::RestoreFromHistory() do_GetInterface(NS_STATIC_CAST(nsIInterfaceRequestor*, this)); NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface"); - nsCOMPtr windowState; - mLSHE->GetWindowState(getter_AddRefs(windowState)); - rv = privWin->RestoreWindowState(windowState); NS_ENSURE_SUCCESS(rv, rv); - mLSHE->SetWindowState(nsnull); - // Now, dispatch a title change event which would happed as the // is parsed. nsCOMPtr nsDoc = do_QueryInterface(document); diff --git a/mozilla/docshell/base/nsIContentViewer.idl b/mozilla/docshell/base/nsIContentViewer.idl index 8ee4564d22c..f5e92c72721 100644 --- a/mozilla/docshell/base/nsIContentViewer.idl +++ b/mozilla/docshell/base/nsIContentViewer.idl @@ -14,7 +14,7 @@ struct nsRect; [ptr] native nsIDeviceContextPtr(nsIDeviceContext); [ref] native nsRectRef(nsRect); -[scriptable, uuid(62d0e866-e608-4b1a-9ab0-467142b3e3bd)] +[scriptable, uuid(6a7ddb40-8a9e-4576-8ad1-71c5641d8780)] interface nsIContentViewer : nsISupports { @@ -85,8 +85,10 @@ interface nsIContentViewer : nsISupports /** * Attach the content viewer to its DOM window and docshell. + * @param aState A state object that might be useful in attaching the DOM + * window. */ - void open(); + void open(in nsISupports aState); /** * Clears the current history entry. This is used if we need to clear out diff --git a/mozilla/dom/public/nsIScriptGlobalObject.h b/mozilla/dom/public/nsIScriptGlobalObject.h index cc16c6997de..8a9a7aa399f 100644 --- a/mozilla/dom/public/nsIScriptGlobalObject.h +++ b/mozilla/dom/public/nsIScriptGlobalObject.h @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -51,8 +52,8 @@ class nsIScriptGlobalObjectOwner; struct JSObject; #define NS_ISCRIPTGLOBALOBJECT_IID \ -{ 0x2b16fc80, 0xfa41, 0x11d1, \ -{ 0x9b, 0xc3, 0x00, 0x60, 0x08, 0x8c, 0xa6, 0xb3} } +{ 0xd4ddb2f8, 0x385f, 0x4baa, \ + { 0xba, 0x69, 0x6c, 0x42, 0xb3, 0xc2, 0xd0, 0xd0 } } /** * The JavaScript specific global object. This often used to store @@ -67,6 +68,7 @@ public: virtual void SetContext(nsIScriptContext *aContext) = 0; virtual nsIScriptContext *GetContext() = 0; virtual nsresult SetNewDocument(nsIDOMDocument *aDocument, + nsISupports *aState, PRBool aRemoveEventListeners, PRBool aClearScope) = 0; virtual void SetDocShell(nsIDocShell *aDocShell) = 0; diff --git a/mozilla/dom/src/base/nsGlobalWindow.cpp b/mozilla/dom/src/base/nsGlobalWindow.cpp index b540ecd4d9a..80206297cf7 100644 --- a/mozilla/dom/src/base/nsGlobalWindow.cpp +++ b/mozilla/dom/src/base/nsGlobalWindow.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set sw=2 ts=2 et tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -272,14 +273,15 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow) mInClose(PR_FALSE), mOpenerWasCleared(PR_FALSE), mIsPopupSpam(PR_FALSE), - mJSObject(nsnull), mArguments(nsnull), + mGlobalObjectOwner(nsnull), + mDocShell(nsnull), mTimeouts(nsnull), mTimeoutInsertionPoint(&mTimeouts), mTimeoutPublicIdCounter(1), mTimeoutFiringDepth(0), - mGlobalObjectOwner(nsnull), - mDocShell(nsnull) + mIsFrozen(PR_FALSE), + mJSObject(nsnull) { // Initialize the PRCList (this). PR_INIT_CLIST(this); @@ -415,6 +417,48 @@ nsGlobalWindow::ClearControllers() } } +void +nsGlobalWindow::FreeInnerObjects(JSContext *cx) +{ + NS_ASSERTION(IsInnerWindow(), "Don't free inner objects on an outer window"); + + ClearAllTimeouts(); + + mChromeEventHandler = nsnull; + + if (mLocation) { + // Invalidate the inner window's location object now that + // the inner window is being torn down. We need to do this + // to prevent people from holding on to an old inner + // window's location object somehow and tracking the + // location of the docshell... + mLocation->SetDocShell(nsnull); + } + + if (mListenerManager) { + mListenerManager->RemoveAllListeners(PR_FALSE); + mListenerManager = nsnull; + } + + if (mDocument) { + nsCOMPtr doc = + do_QueryInterface(mDocument); + + // Remember the document's principal. + mDocumentPrincipal = doc->GetPrincipal(); + } + + // Remove our reference to the document and the document principal. + mDocument = nsnull; + + if (mJSObject && cx) { + ::JS_ClearScope(cx, mJSObject); + ::JS_ClearWatchPointsForObject(cx, mJSObject); + + nsWindowSH::InvalidateGlobalScopePolluter(cx, mJSObject); + } +} + //***************************************************************************** // nsGlobalWindow::nsISupports //***************************************************************************** @@ -537,17 +581,125 @@ nsGlobalWindow::GetPopupControlState() const return gPopupControlState; } +#define WINDOWSTATEHOLDER_IID \ +{0x2aa29291, 0x3ac9, 0x4d37, {0xa4, 0x3d, 0x45, 0x15, 0x2f, 0x16, 0x23, 0x04 }} + +class WindowStateHolder : public nsISupports +{ +public: + NS_DEFINE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID) + NS_DECL_ISUPPORTS + + WindowStateHolder(nsGlobalWindow *aWindow, + nsIXPConnectJSObjectHolder *aHolder, + nsNavigator *aNavigator); + + // Get the contents of focus memory when the state was saved + // (if the focus was inside of this window). + nsIDOMElement* GetFocusedElement() { return mFocusedElement; } + nsIDOMWindowInternal* GetFocusedWindow() { return mFocusedWindow; } + + nsGlobalWindow* GetInnerWindow() { return mInnerWindow; } + nsIXPConnectJSObjectHolder* GetInnerWindowHolder() + { return mInnerWindowHolder; } + + nsNavigator* GetNavigator() { return mNavigator; } + + void DidRestoreWindow() + { + mInnerWindow = nsnull; + mInnerWindowHolder = nsnull; + mNavigator = nsnull; + } + +protected: + ~WindowStateHolder(); + + nsGlobalWindow *mInnerWindow; + // We hold onto this to make sure the inner window doesn't go away. The outer + // window ends up recalculating it anyway. + nsCOMPtr mInnerWindowHolder; + nsRefPtr mNavigator; + nsCOMPtr mFocusedElement; + nsCOMPtr mFocusedWindow; +}; + +WindowStateHolder::WindowStateHolder(nsGlobalWindow *aWindow, + nsIXPConnectJSObjectHolder *aHolder, + nsNavigator *aNavigator) + : mInnerWindow(aWindow), + mInnerWindowHolder(aHolder), + mNavigator(aNavigator) +{ + NS_PRECONDITION(aWindow, "null window"); + NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window"); + + nsIFocusController *fc = aWindow->GetRootFocusController(); + NS_ASSERTION(fc, "null focus controller"); + + // We want to save the focused element/window only if they are inside of + // this window. + + nsCOMPtr focusWinInternal; + fc->GetFocusedWindow(getter_AddRefs(focusWinInternal)); + + nsCOMPtr focusedWindow = do_QueryInterface(focusWinInternal); + + // The outer window is used for focus purposes, so make sure that's what + // we're looking for. + nsPIDOMWindow *targetWindow = aWindow->GetOuterWindow(); + + while (focusedWindow) { + if (focusedWindow == targetWindow) { + fc->GetFocusedWindow(getter_AddRefs(mFocusedWindow)); + fc->GetFocusedElement(getter_AddRefs(mFocusedElement)); + break; + } + + focusedWindow = + NS_STATIC_CAST(nsGlobalWindow*, + NS_STATIC_CAST(nsPIDOMWindow*, + focusedWindow))->GetPrivateParent(); + } + + aWindow->SuspendTimeouts(); +} + +WindowStateHolder::~WindowStateHolder() +{ + if (mInnerWindow) { + // This window was left in the bfcache and is now going away. We need to + // free it up. + nsCOMPtr stack(do_GetService(sJSStackContractID)); + JSContext *cx = nsnull; + + if (stack) + stack->GetSafeJSContext(&cx); + + if (!cx) { + NS_WARNING("Trusting GC to finish cleaning up this inner window"); + return; + } + + mInnerWindow->FreeInnerObjects(cx); + } +} + +NS_IMPL_ISUPPORTS1(WindowStateHolder, WindowStateHolder) + nsresult nsGlobalWindow::SetNewDocument(nsIDOMDocument* aDocument, + nsISupports* aState, PRBool aRemoveEventListeners, PRBool aClearScopeHint) { - return SetNewDocument(aDocument, aRemoveEventListeners, aClearScopeHint, - PR_FALSE); + return SetNewDocument(aDocument, aState, aRemoveEventListeners, + aClearScopeHint, PR_FALSE); } nsresult nsGlobalWindow::SetNewDocument(nsIDOMDocument* aDocument, + nsISupports* aState, PRBool aRemoveEventListeners, PRBool aClearScopeHint, PRBool aIsInternalCall) @@ -561,6 +713,7 @@ nsGlobalWindow::SetNewDocument(nsIDOMDocument* aDocument, } return GetOuterWindowInternal()->SetNewDocument(aDocument, + aState, aRemoveEventListeners, aClearScopeHint, PR_TRUE); } @@ -749,11 +902,20 @@ nsGlobalWindow::SetNewDocument(nsIDOMDocument* aDocument, // listener manager over to the new inner window. nsCOMPtr listenerManager; - if (currentInner) { + if (currentInner && !currentInner->IsFrozen()) { if (!reUseInnerWindow) { currentInner->ClearAllTimeouts(); currentInner->mChromeEventHandler = nsnull; + + if (currentInner->mLocation) { + // Invalidate the inner window's location object now that + // the inner window is being torn down. We need to do this + // to prevent people from holding on to an old inner + // window's location object somehow and tracking the + // location of the docshell... + currentInner->mLocation->SetDocShell(nsnull); + } } if (aRemoveEventListeners && currentInner->mListenerManager) { @@ -774,35 +936,47 @@ nsGlobalWindow::SetNewDocument(nsIDOMDocument* aDocument, PRUint32 flags = 0; - if (reUseInnerWindow) { + if (reUseInnerWindow && !currentInner->IsFrozen()) { // We're reusing the current inner window. newInnerWindow = currentInner; } else { - if (thisChrome) { - newInnerWindow = new nsGlobalChromeWindow(this); + if (aState) { + nsCOMPtr wsh = do_QueryInterface(aState); + NS_ASSERTION(wsh, "What kind of weird state are you giving me here?"); - flags = nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT; + newInnerWindow = wsh->GetInnerWindow(); + mInnerWindowHolder = wsh->GetInnerWindowHolder(); + mNavigator = wsh->GetNavigator(); // This assignment addrefs. } else { - newInnerWindow = new nsGlobalWindow(this); + if (thisChrome) { + newInnerWindow = new nsGlobalChromeWindow(this); + + flags = nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT; + } else { + newInnerWindow = new nsGlobalWindow(this); + } } if (!newInnerWindow) { return NS_ERROR_OUT_OF_MEMORY; } - nsIScriptGlobalObject *sgo = - (nsIScriptGlobalObject *)newInnerWindow.get(); + if (!aState) { + // This is redundant if we're restoring from a previous inner window. + nsIScriptGlobalObject *sgo = + (nsIScriptGlobalObject *)newInnerWindow.get(); - nsresult rv = xpc-> - InitClassesWithNewWrappedGlobal(cx, sgo, NS_GET_IID(nsISupports), - flags, - getter_AddRefs(mInnerWindowHolder)); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv = xpc-> + InitClassesWithNewWrappedGlobal(cx, sgo, NS_GET_IID(nsISupports), + flags, + getter_AddRefs(mInnerWindowHolder)); + NS_ENSURE_SUCCESS(rv, rv); - mInnerWindowHolder->GetJSObject(&newInnerWindow->mJSObject); + mInnerWindowHolder->GetJSObject(&newInnerWindow->mJSObject); + } if (currentInner && currentInner->mJSObject) { - if (mNavigator) { + 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 @@ -828,7 +1002,8 @@ nsGlobalWindow::SetNewDocument(nsIDOMDocument* aDocument, } nsIScriptContext *callerScx; - if (cx && (callerScx = GetScriptContextFromJSContext(cx))) { + if (cx && (callerScx = GetScriptContextFromJSContext(cx)) && + !currentInner->IsFrozen()) { // 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 @@ -844,21 +1019,27 @@ nsGlobalWindow::SetNewDocument(nsIDOMDocument* aDocument, // Clear scope on the outer window ::JS_ClearScope(cx, mJSObject); ::JS_ClearWatchPointsForObject(cx, mJSObject); + // Clear the regexp statics for the new page unconditionally. + // XXX They don't get restored on the inner window when we go back. + ::JS_ClearRegExpStatics(cx); // Re-initialize the outer window. scx->InitContext(this); - if (!termFuncSet) { - ::JS_ClearScope(cx, currentInner->mJSObject); - ::JS_ClearWatchPointsForObject(cx, currentInner->mJSObject); - ::JS_ClearRegExpStatics(cx); - } + // Don't clear scope on our current inner window if it's going to be + // held in the bfcache. + if (!currentInner->IsFrozen()) { + if (!termFuncSet) { + ::JS_ClearScope(cx, currentInner->mJSObject); + ::JS_ClearWatchPointsForObject(cx, currentInner->mJSObject); + } - // Make the current inner window release its strong references - // to the document to prevent it from keeping everything - // around. But remember the document's principal. - currentInner->mDocument = nsnull; - currentInner->mDocumentPrincipal = oldPrincipal; + // Make the current inner window release its strong references + // to the document to prevent it from keeping everything + // around. But remember the document's principal. + currentInner->mDocument = nsnull; + currentInner->mDocumentPrincipal = oldPrincipal; + } } mInnerWindow = newInnerWindow; @@ -869,54 +1050,57 @@ nsGlobalWindow::SetNewDocument(nsIDOMDocument* aDocument, ::JS_SetGlobalObject(cx, mJSObject); - if (newDoc != oldDoc) { + if (newDoc != oldDoc && !aState) { nsCOMPtr html_doc(do_QueryInterface(mDocument)); nsWindowSH::InstallGlobalScopePolluter(cx, newInnerWindow->mJSObject, html_doc); } + + newInnerWindow->mListenerManager = listenerManager; } if (newDoc) { newDoc->SetScriptGlobalObject(newInnerWindow); } - if (reUseInnerWindow) { - newInnerWindow->mDocument = aDocument; - } else { - rv = newInnerWindow->SetNewDocument(aDocument, aRemoveEventListeners, - aClearScopeHint, PR_TRUE); - NS_ENSURE_SUCCESS(rv, rv); + if (!aState) { + if (reUseInnerWindow) { + newInnerWindow->mDocument = aDocument; + } else { + rv = newInnerWindow->SetNewDocument(aDocument, nsnull, + aRemoveEventListeners, + aClearScopeHint, PR_TRUE); + NS_ENSURE_SUCCESS(rv, rv); - // Initialize DOM classes etc on the inner window. - rv = scx->InitClasses(newInnerWindow->mJSObject); - NS_ENSURE_SUCCESS(rv, rv); + // Initialize DOM classes etc on the inner window. + rv = scx->InitClasses(newInnerWindow->mJSObject); + NS_ENSURE_SUCCESS(rv, rv); - if (navigatorHolder) { - // Restore window.navigator onto the new inner window. - JSObject *nav; - navigatorHolder->GetJSObject(&nav); + if (navigatorHolder) { + // Restore window.navigator onto the new inner window. + JSObject *nav; + navigatorHolder->GetJSObject(&nav); - ::JS_DefineProperty(cx, newInnerWindow->mJSObject, "navigator", - OBJECT_TO_JSVAL(nav), nsnull, nsnull, - JSPROP_ENUMERATE); + ::JS_DefineProperty(cx, newInnerWindow->mJSObject, "navigator", + OBJECT_TO_JSVAL(nav), nsnull, nsnull, + JSPROP_ENUMERATE); + } } - newInnerWindow->mListenerManager = listenerManager; + if (mArguments) { + jsval args = OBJECT_TO_JSVAL(mArguments); + + ::JS_SetProperty(cx, newInnerWindow->mJSObject, "arguments", + &args); + + ::JS_UnlockGCThing(cx, mArguments); + mArguments = nsnull; + } + + // Give the new inner window our chrome event handler (since it + // doesn't have one). + newInnerWindow->mChromeEventHandler = mChromeEventHandler; } - - if (mArguments) { - jsval args = OBJECT_TO_JSVAL(mArguments); - - ::JS_SetProperty(cx, newInnerWindow->mJSObject, "arguments", - &args); - - ::JS_UnlockGCThing(cx, mArguments); - mArguments = nsnull; - } - - // Give the new inner window our chrome event handler (since it - // doesn't have one). - newInnerWindow->mChromeEventHandler = mChromeEventHandler; } if (scx && IsOuterWindow()) { @@ -951,40 +1135,10 @@ nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell) NS_ASSERTION(!mTimeouts, "Uh, outer window holds timeouts!"); JSContext *cx = (JSContext *)mContext->GetNativeContext(); - nsGlobalWindow *currentInner = GetCurrentInnerWindowInternal(); + if (currentInner) { - currentInner->ClearAllTimeouts(); - - if (currentInner->mListenerManager) { - currentInner->mListenerManager->RemoveAllListeners(PR_FALSE); - currentInner->mListenerManager = nsnull; - } - - JSContext *cx = (JSContext *)mContext->GetNativeContext(); - - // XXXjst: We shouldn't need to do this, but if we don't we leak - // the world... actually, even with this we leak the - // world... need to figure this out. - if (currentInner->mJSObject) { - ::JS_ClearScope(cx, currentInner->mJSObject); - ::JS_ClearWatchPointsForObject(cx, currentInner->mJSObject); - - if (currentInner->mDocument) { - nsCOMPtr doc = - do_QueryInterface(currentInner->mDocument); - - // Remember the document's principal. - currentInner->mDocumentPrincipal = doc->GetPrincipal(); - } - - // Release the current inner window's document reference - // through the global scope polluter. - nsWindowSH::InvalidateGlobalScopePolluter(cx, currentInner->mJSObject); - } - - // Release the current inner window's document references. - currentInner->mDocument = nsnull; + currentInner->FreeInnerObjects(cx); nsCOMPtr doc = do_QueryInterface(mDocument); @@ -1006,8 +1160,6 @@ nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell) } ::JS_ClearRegExpStatics(cx); - - currentInner->mChromeEventHandler = nsnull; } // if we are closing the window while in full screen mode, be sure @@ -1051,8 +1203,8 @@ nsGlobalWindow::SetDocShell(nsIDocShell* aDocShell) mDocShell = aDocShell; // Weak Reference - if (mLocation) - mLocation->SetDocShell(aDocShell); + NS_ASSERTION(!mLocation, "Uh, outer window has location object!"); + if (mNavigator) mNavigator->SetDocShell(aDocShell); if (mHistory) @@ -4918,12 +5070,12 @@ nsGlobalWindow::GetPrivateRoot() NS_IMETHODIMP nsGlobalWindow::GetLocation(nsIDOMLocation ** aLocation) { - FORWARD_TO_OUTER(GetLocation, (aLocation), NS_ERROR_NOT_INITIALIZED); + FORWARD_TO_INNER(GetLocation, (aLocation), NS_ERROR_NOT_INITIALIZED); *aLocation = nsnull; - if (!mLocation && mDocShell) { - mLocation = new nsLocation(mDocShell); + if (!mLocation && GetDocShellInternal()) { + mLocation = new nsLocation(GetDocShellInternal()); if (!mLocation) { return NS_ERROR_OUT_OF_MEMORY; } @@ -6441,247 +6593,42 @@ nsGlobalWindow::EnsureSizeUpToDate() } } -#define WINDOWSTATEHOLDER_IID \ -{0xae1c7401, 0xcdee, 0x404a, {0xbd, 0x63, 0x05, 0xc0, 0x35, 0x0d, 0xa7, 0x72}} - -class WindowStateHolder : public nsISupports -{ -public: - NS_DEFINE_STATIC_IID_ACCESSOR(WINDOWSTATEHOLDER_IID) - NS_DECL_ISUPPORTS - - WindowStateHolder(JSContext *cx, // The JSContext for the window - JSObject *aObject, // An object to save the properties onto - nsGlobalWindow *aWindow); // The window to operate on - - // This is the property store object that holds the window properties. - JSObject* GetObject() { return mJSObj; } - - // Get the listener manager, which holds all event handlers for the window. - nsIEventListenerManager* GetListenerManager() { return mListenerManager; } - - // Get the saved value of the mMutationBits field. - PRUint32 GetMutationBits() { return mMutationBits; } - - // Get the contents of focus memory when the state was saved - // (if the focus was inside of this window). - nsIDOMElement* GetFocusedElement() { return mFocusedElement; } - nsIDOMWindowInternal* GetFocusedWindow() { return mFocusedWindow; } - - // Manage the list of saved timeouts for the window. - nsTimeout* GetSavedTimeouts() { return mSavedTimeouts; } - nsTimeout** GetTimeoutInsertionPoint() { return mTimeoutInsertionPoint; } - void ClearSavedTimeouts() { mSavedTimeouts = nsnull; } - -protected: - ~WindowStateHolder(); - - JSRuntime *mRuntime; - JSObject *mJSObj; - nsCOMPtr mListenerManager; - nsCOMPtr mFocusedElement; - nsCOMPtr mFocusedWindow; - nsTimeout *mSavedTimeouts; - nsTimeout **mTimeoutInsertionPoint; - PRUint32 mMutationBits; -}; - -WindowStateHolder::WindowStateHolder(JSContext *cx, JSObject *aObject, - nsGlobalWindow *aWindow) - : mRuntime(::JS_GetRuntime(cx)), mJSObj(aObject) -{ - NS_ASSERTION(aWindow, "null window"); - - // Prevent mJSObj from being gc'd for the lifetime of this object. - ::JS_AddNamedRoot(cx, &mJSObj, "WindowStateHolder::mJSObj"); - - aWindow->GetListenerManager(getter_AddRefs(mListenerManager)); - mMutationBits = aWindow->mMutationBits; - - // Clear the window's EventListenerManager pointer so that it can't have - // listeners removed from it later. - aWindow->mListenerManager = nsnull; - - nsIFocusController *fc = aWindow->GetRootFocusController(); - NS_ASSERTION(fc, "null focus controller"); - - // We want to save the focused element/window only if they are inside of - // this window. - - nsCOMPtr focusWinInternal; - fc->GetFocusedWindow(getter_AddRefs(focusWinInternal)); - - nsCOMPtr focusedWindow = do_QueryInterface(focusWinInternal); - - // The outer window is used for focus purposes, so make sure that's what - // we're looking for. - nsPIDOMWindow *targetWindow = aWindow->GetOuterWindow(); - - while (focusedWindow) { - if (focusedWindow == targetWindow) { - fc->GetFocusedWindow(getter_AddRefs(mFocusedWindow)); - fc->GetFocusedElement(getter_AddRefs(mFocusedElement)); - break; - } - - focusedWindow = - NS_STATIC_CAST(nsGlobalWindow*, - NS_STATIC_CAST(nsPIDOMWindow*, - focusedWindow))->GetPrivateParent(); - } - - aWindow->SuspendTimeouts(); - - // Clear the timeout list for aWindow (but we don't need to for children) - mSavedTimeouts = aWindow->mTimeouts; - mTimeoutInsertionPoint = aWindow->mTimeoutInsertionPoint; - - aWindow->mTimeouts = nsnull; - aWindow->mTimeoutInsertionPoint = &aWindow->mTimeouts; -} - -WindowStateHolder::~WindowStateHolder() -{ - // Release the timeouts, if we still have any. - nsTimeout *timeout = mSavedTimeouts; - while (timeout) { - nsTimeout *next = timeout->mNext; - NS_ASSERTION(!timeout->mTimer, "live timer in a saved window state"); - timeout->Release(nsnull); - timeout = next; - } - - ::JS_RemoveRootRT(mRuntime, &mJSObj); -} - -NS_IMPL_ISUPPORTS1(WindowStateHolder, WindowStateHolder) - -static JSClass sWindowStateClass = { - "window state", 0, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, - JSCLASS_NO_OPTIONAL_MEMBERS -}; - -static nsresult -CopyJSPropertyArray(JSContext *cx, JSObject *aSource, JSObject *aDest, - JSIdArray *props) -{ - jsint length = props->length; - for (jsint i = 0; i < length; ++i) { - jsval propname_value; - - if (!::JS_IdToValue(cx, props->vector[i], &propname_value) || - !JSVAL_IS_STRING(propname_value)) { - NS_WARNING("Failed to copy non-string-named window property"); - return NS_ERROR_FAILURE; - } - - JSString *propname = JSVAL_TO_STRING(propname_value); - jschar *propname_str = ::JS_GetStringChars(propname); - NS_ENSURE_TRUE(propname_str, NS_ERROR_FAILURE); - - // We exclude the "location" property because restoring it this way is - // problematic. It will "just work" without us explicitly saving or - // restoring the value. - - if (!nsCRT::strcmp(NS_REINTERPRET_CAST(PRUnichar*, propname_str), - NS_LITERAL_STRING("location").get())) { - continue; - } - - size_t propname_len = ::JS_GetStringLength(propname); - - JSPropertyOp getter, setter; - uintN attrs; - JSBool found; - if (!::JS_GetUCPropertyAttrsGetterAndSetter(cx, aSource, propname_str, - propname_len, &attrs, &found, - &getter, &setter)) - return NS_ERROR_FAILURE; - - NS_ENSURE_TRUE(found, NS_ERROR_UNEXPECTED); - - jsval propvalue; - if (!::JS_LookupUCProperty(cx, aSource, propname_str, - propname_len, &propvalue)) - return NS_ERROR_FAILURE; - - PRBool res = ::JS_DefineUCProperty(cx, aDest, propname_str, propname_len, - propvalue, getter, setter, attrs); -#ifdef DEBUG_PAGE_CACHE - if (res) - printf("Copied window property: %s\n", - NS_ConvertUTF16toUTF8(NS_REINTERPRET_CAST(PRUnichar*, - propname_str)).get()); -#endif - - if (!res) { -#ifdef DEBUG - printf("failed to copy property: %s\n", - NS_ConvertUTF16toUTF8(NS_REINTERPRET_CAST(PRUnichar*, - propname_str)).get()); -#endif - return NS_ERROR_FAILURE; - } - } - - return NS_OK; -} - -static nsresult -CopyJSProperties(JSContext *cx, JSObject *aSource, JSObject *aDest) -{ - // Enumerate all of the properties on aSource and install them on aDest. - - JSIdArray *props = ::JS_Enumerate(cx, aSource); - if (props) { - props = ::JS_EnumerateResolvedStandardClasses(cx, aSource, props); - } - if (!props) { -#ifdef DEBUG_PAGE_CACHE - printf("failed to enumerate JS properties\n"); -#endif - return NS_ERROR_FAILURE; - } - -#ifdef DEBUG_PAGE_CACHE - printf("props length = %d\n", props->length); -#endif - - nsresult rv = CopyJSPropertyArray(cx, aSource, aDest, props); - ::JS_DestroyIdArray(cx, props); - return rv; -} - nsresult nsGlobalWindow::SaveWindowState(nsISupports **aState) { + NS_PRECONDITION(IsOuterWindow(), "Can't save the inner window's state"); + *aState = nsnull; - if (IsOuterWindow() && (!mContext || !mJSObject)) { + if (!mContext || !mJSObject) { // The window may be getting torn down; don't bother saving state. return NS_OK; } - FORWARD_TO_INNER(SaveWindowState, (aState), NS_ERROR_NOT_INITIALIZED); + nsGlobalWindow *inner = GetCurrentInnerWindowInternal(); + NS_ASSERTION(inner, "No inner window to save"); - JSContext *cx = NS_STATIC_CAST(JSContext*, - GetContextInternal()->GetNativeContext()); - NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE); - - JSObject *stateObj = ::JS_NewObject(cx, &sWindowStateClass, NULL, NULL); - NS_ENSURE_TRUE(stateObj, NS_ERROR_OUT_OF_MEMORY); - - // The window state object will root the JSObject. - nsCOMPtr state = new WindowStateHolder(cx, stateObj, this); + nsCOMPtr state = new WindowStateHolder(inner, + mInnerWindowHolder, + mNavigator); NS_ENSURE_TRUE(state, NS_ERROR_OUT_OF_MEMORY); #ifdef DEBUG_PAGE_CACHE printf("saving window state, stateObj = %p\n", (void*)stateObj); #endif - nsresult rv = CopyJSProperties(cx, mJSObject, stateObj); - NS_ENSURE_SUCCESS(rv, rv); + + if (inner->mLocation) { + // Invalidate the inner window's location object now that the inner window + // is being put into the bfcache. We need to do this to prevent people from + // holding on to an old inner window's location object somehow and tracking + // the location of the docshell. The docshell is restored when the window is + // taken out of the bfcache (meaning that any stale references to it will + // only see that particular document). + inner->mLocation->SetDocShell(nsnull); + } + + // Don't do anything else to this inner window! + inner->Freeze(); state.swap(*aState); return NS_OK; @@ -6690,35 +6637,21 @@ nsGlobalWindow::SaveWindowState(nsISupports **aState) nsresult nsGlobalWindow::RestoreWindowState(nsISupports *aState) { - // SetNewDocument() has already been called so we should have a - // new clean inner window to restore state into here. + NS_ASSERTION(IsOuterWindow(), "Cannot restore an inner window"); - if (IsOuterWindow() && (!mContext || !mJSObject)) { + if (!mContext || !mJSObject) { // The window may be getting torn down; don't bother restoring state. return NS_OK; } - FORWARD_TO_INNER(RestoreWindowState, (aState), NS_ERROR_NOT_INITIALIZED); - - JSContext *cx = NS_STATIC_CAST(JSContext*, - GetContextInternal()->GetNativeContext()); - NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE); - - // Note that we don't need to call JS_ClearScope here. The scope is already - // cleared by SetNewDocument(), and calling it again here would remove the - // XPConnect properties. - nsCOMPtr holder = do_QueryInterface(aState); NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE); #ifdef DEBUG_PAGE_CACHE printf("restoring window state, stateObj = %p\n", (void*)holder->GetObject()); #endif - nsresult rv = CopyJSProperties(cx, holder->GetObject(), mJSObject); - NS_ENSURE_SUCCESS(rv, rv); - mListenerManager = holder->GetListenerManager(); - mMutationBits = holder->GetMutationBits(); + nsGlobalWindow *inner = GetCurrentInnerWindowInternal(); nsIDOMElement *focusedElement = holder->GetFocusedElement(); nsIDOMWindowInternal *focusedWindow = holder->GetFocusedWindow(); @@ -6737,7 +6670,6 @@ nsGlobalWindow::RestoreWindowState(nsISupports *aState) // We don't bother checking whether the element or frame is focusable. // If it was focusable when we stored the presentation, it must be // focusable now. - PRBool didFocusContent = PR_FALSE; nsIDocument *doc = focusedContent->GetCurrentDoc(); if (doc) { nsIPresShell *shell = doc->GetShellAt(0); @@ -6760,16 +6692,15 @@ nsGlobalWindow::RestoreWindowState(nsISupports *aState) fc->SetFocusedElement(focusedElement); } - mTimeouts = holder->GetSavedTimeouts(); - mTimeoutInsertionPoint = holder->GetTimeoutInsertionPoint(); + // And we're ready to go! + inner->Thaw(); - holder->ClearSavedTimeouts(); + if (inner->mLocation) + inner->mLocation->SetDocShell(mDocShell); - // If our state is being restored from history, we won't be getting an onload - // event. Make sure we're marked as being completely loaded. - mIsDocumentLoaded = PR_TRUE; + holder->DidRestoreWindow(); - return ResumeTimeouts(); + return inner->ResumeTimeouts(); } void diff --git a/mozilla/dom/src/base/nsGlobalWindow.h b/mozilla/dom/src/base/nsGlobalWindow.h index 01311da408c..e8d87869692 100644 --- a/mozilla/dom/src/base/nsGlobalWindow.h +++ b/mozilla/dom/src/base/nsGlobalWindow.h @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -153,6 +154,7 @@ public: virtual void SetContext(nsIScriptContext *aContext); virtual nsIScriptContext *GetContext(); virtual nsresult SetNewDocument(nsIDOMDocument *aDocument, + nsISupports *aState, PRBool aRemoveEventListeners, PRBool aClearScopeHint); virtual void SetDocShell(nsIDocShell* aDocShell); @@ -277,7 +279,10 @@ protected: void CleanUp(); void ClearControllers(); + void FreeInnerObjects(JSContext *cx); + nsresult SetNewDocument(nsIDOMDocument *aDocument, + nsISupports *aState, PRBool aRemoveEventListeners, PRBool aClearScopeHint, PRBool aIsInternalCall); @@ -382,6 +387,25 @@ protected: void SuspendTimeouts(); nsresult ResumeTimeouts(); + void Freeze() + { + NS_ASSERTION(IsInnerWindow(), "Freeze called on an outer window"); + mIsFrozen = PR_TRUE; + } + + void Thaw() + { + NS_ASSERTION(IsInnerWindow(), "Freeze called on an outer window"); + mIsFrozen = PR_FALSE; + } + + PRBool IsFrozen() const + { + NS_ASSERTION(IsInnerWindow(), + "Why do you care if an outer window is frozen?"); + return mIsFrozen; + } + // When adding new member variables, be careful not to create cycles // through JavaScript. If there is any chance that a member variable // could own objects that are implemented in JavaScript, then those @@ -405,7 +429,6 @@ protected: nsRefPtr mScreen; nsRefPtr mHistory; nsRefPtr mFrames; - nsRefPtr mLocation; nsRefPtr mMenubar; nsRefPtr mToolbar; nsRefPtr mLocationbar; @@ -429,6 +452,8 @@ protected: nsTimeout** mTimeoutInsertionPoint; PRUint32 mTimeoutPublicIdCounter; PRUint32 mTimeoutFiringDepth; + nsRefPtr mLocation; + PRPackedBool mIsFrozen; // These member variables are used on both inner and the outer windows. nsCOMPtr mDocumentPrincipal; diff --git a/mozilla/layout/base/nsDocumentViewer.cpp b/mozilla/layout/base/nsDocumentViewer.cpp index bcf1e046e4a..f1b5122fc5b 100644 --- a/mozilla/layout/base/nsDocumentViewer.cpp +++ b/mozilla/layout/base/nsDocumentViewer.cpp @@ -1,4 +1,5 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 sw=2 et tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -354,6 +355,7 @@ private: nsresult MakeWindow(nsIWidget* aParentWidget, const nsRect& aBounds); nsresult InitInternal(nsIWidget* aParentWidget, + nsISupports *aState, nsIDeviceContext* aDeviceContext, const nsRect& aBounds, PRBool aDoCreation, @@ -627,7 +629,7 @@ DocumentViewerImpl::Init(nsIWidget* aParentWidget, nsIDeviceContext* aDeviceContext, const nsRect& aBounds) { - return InitInternal(aParentWidget, aDeviceContext, aBounds, PR_TRUE, PR_FALSE); + return InitInternal(aParentWidget, nsnull, aDeviceContext, aBounds, PR_TRUE, PR_FALSE); } nsresult @@ -765,6 +767,7 @@ DocumentViewerImpl::InitPresentationStuff(PRBool aDoInitialReflow) // all the new objects or just initialize the existing ones nsresult DocumentViewerImpl::InitInternal(nsIWidget* aParentWidget, + nsISupports *aState, nsIDeviceContext* aDeviceContext, const nsRect& aBounds, PRBool aDoCreation, @@ -843,7 +846,7 @@ DocumentViewerImpl::InitInternal(nsIWidget* aParentWidget, nsCOMPtr domdoc(do_QueryInterface(mDocument)); if (domdoc) { - global->SetNewDocument(domdoc, PR_TRUE, PR_TRUE); + global->SetNewDocument(domdoc, aState, PR_TRUE, PR_TRUE); } } } @@ -1203,7 +1206,7 @@ DocumentViewerImpl::PageHide(PRBool aIsUnload) } NS_IMETHODIMP -DocumentViewerImpl::Open() +DocumentViewerImpl::Open(nsISupports *aState) { NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED); @@ -1216,7 +1219,7 @@ DocumentViewerImpl::Open() nsRect bounds; mWindow->GetBounds(bounds); - nsresult rv = InitInternal(mParentWidget, mDeviceContext, bounds, + nsresult rv = InitInternal(mParentWidget, aState, mDeviceContext, bounds, PR_FALSE, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); @@ -1510,7 +1513,7 @@ DocumentViewerImpl::SetDOMDocument(nsIDOMDocument *aDocument) // Set the script global object on the new document nsCOMPtr global = do_GetInterface(container); if (global) { - global->SetNewDocument(aDocument, PR_TRUE, PR_TRUE); + global->SetNewDocument(aDocument, nsnull, PR_TRUE, PR_TRUE); } } @@ -3931,7 +3934,7 @@ DocumentViewerImpl::ReturnToGalleyPresentation() } } - InitInternal(mParentWidget, mDeviceContext, bounds, !wasCached, PR_TRUE); + InitInternal(mParentWidget, nsnull, mDeviceContext, bounds, !wasCached, PR_TRUE); if (mPrintEngine && !wasCached) { mPrintEngine->Destroy(); diff --git a/mozilla/layout/base/nsIDocumentViewer.h b/mozilla/layout/base/nsIDocumentViewer.h index 18b3c5c3c91..7fde76a533d 100644 --- a/mozilla/layout/base/nsIDocumentViewer.h +++ b/mozilla/layout/base/nsIDocumentViewer.h @@ -45,7 +45,7 @@ class nsIPresShell; class nsIStyleSheet; #define NS_IDOCUMENT_VIEWER_IID \ - { 0xbd4fde0c, 0x71fd, 0x4d77,{0xab, 0x66, 0x26, 0xce, 0x43, 0x93, 0x2e, 0x4e}} + { 0x42ecec88, 0x80d5, 0x48ac,{0x9a, 0xcd, 0x12, 0x51, 0xdc, 0x42, 0x60, 0x4a}} /** * A document viewer is a kind of content viewer that uses NGLayout