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