bug 303267: Store the entire inner window in the window state holder instead of copying properties and restoring them. This fixes going back and forth with the bfcache on when the page gone to has javascript in it. r=jst sr=bryner

This commit is contained in:
mrbkap%gmail.com 2005-08-15 18:16:42 +00:00
Родитель 981274f805
Коммит 2b7c7bca7a
10 изменённых файлов: 341 добавлений и 373 удалений

Просмотреть файл

@ -1915,8 +1915,8 @@ nsHTMLDocument::OpenCommon(const nsACString& aContentType, PRBool aReplace)
nsCOMPtr<nsIDOMDocument> 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;

Просмотреть файл

@ -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)
{

Просмотреть файл

@ -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)
{

Просмотреть файл

@ -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<nsISupports> 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<nsISupports> 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
// <head> is parsed.
nsCOMPtr<nsIDOMNSDocument> nsDoc = do_QueryInterface(document);

Просмотреть файл

@ -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

Просмотреть файл

@ -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;

Просмотреть файл

@ -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<nsIDocument> 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<nsIXPConnectJSObjectHolder> mInnerWindowHolder;
nsRefPtr<nsNavigator> mNavigator;
nsCOMPtr<nsIDOMElement> mFocusedElement;
nsCOMPtr<nsIDOMWindowInternal> 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<nsIDOMWindowInternal> focusWinInternal;
fc->GetFocusedWindow(getter_AddRefs(focusWinInternal));
nsCOMPtr<nsPIDOMWindow> 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<nsIThreadJSContextStack> 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<nsIEventListenerManager> 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<WindowStateHolder> 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<nsIHTMLDocument> 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<nsIDocument> 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<nsIDocument> 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<nsIEventListenerManager> mListenerManager;
nsCOMPtr<nsIDOMElement> mFocusedElement;
nsCOMPtr<nsIDOMWindowInternal> 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<nsIDOMWindowInternal> focusWinInternal;
fc->GetFocusedWindow(getter_AddRefs(focusWinInternal));
nsCOMPtr<nsPIDOMWindow> 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<nsISupports> state = new WindowStateHolder(cx, stateObj, this);
nsCOMPtr<nsISupports> 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<WindowStateHolder> 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

Просмотреть файл

@ -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<nsScreen> mScreen;
nsRefPtr<nsHistory> mHistory;
nsRefPtr<nsDOMWindowList> mFrames;
nsRefPtr<nsLocation> mLocation;
nsRefPtr<nsBarProp> mMenubar;
nsRefPtr<nsBarProp> mToolbar;
nsRefPtr<nsBarProp> mLocationbar;
@ -429,6 +452,8 @@ protected:
nsTimeout** mTimeoutInsertionPoint;
PRUint32 mTimeoutPublicIdCounter;
PRUint32 mTimeoutFiringDepth;
nsRefPtr<nsLocation> mLocation;
PRPackedBool mIsFrozen;
// These member variables are used on both inner and the outer windows.
nsCOMPtr<nsIPrincipal> mDocumentPrincipal;

Просмотреть файл

@ -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<nsIDOMDocument> 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<nsIScriptGlobalObject> 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();

Просмотреть файл

@ -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