diff --git a/mozilla/docshell/base/nsDocShell.cpp b/mozilla/docshell/base/nsDocShell.cpp index d69c087101e..8ff7e46447c 100644 --- a/mozilla/docshell/base/nsDocShell.cpp +++ b/mozilla/docshell/base/nsDocShell.cpp @@ -4644,7 +4644,7 @@ nsDocShell::EndPageLoad(nsIWebProgress * aProgress, // This will cause any OnLoad(...) handlers to fire, if it is a HTML // document... // - if ((mIsRestoringDocument || !mEODForCurrentDocument) && mContentViewer) { + if (!mEODForCurrentDocument && mContentViewer) { mIsExecutingOnLoadHandler = PR_TRUE; mContentViewer->LoadComplete(aStatus); mIsExecutingOnLoadHandler = PR_FALSE; @@ -4932,7 +4932,11 @@ public: PR_STATIC_CALLBACK(void*) HandleRestorePresentationEvent(PLEvent *aEvent) { - NS_STATIC_CAST(RestorePresentationEvent*, aEvent)->mDocShell->FinishRestore(); + RestorePresentationEvent *event = + NS_STATIC_CAST(RestorePresentationEvent*, aEvent); + + nsresult rv = event->mDocShell->RestoreFromHistory(); + NS_ASSERTION(NS_SUCCEEDED(rv), "RestoreFromHistory failed"); return nsnull; } @@ -4955,7 +4959,7 @@ nsDocShell::BeginRestore(nsIContentViewer *aContentViewer, PRBool aTop) nsresult rv; if (!aContentViewer) { rv = EnsureContentViewer(); - NS_ENSURE_TRUE(rv, rv); + NS_ENSURE_SUCCESS(rv, rv); aContentViewer = mContentViewer; } @@ -4977,23 +4981,7 @@ nsDocShell::BeginRestore(nsIContentViewer *aContentViewer, PRBool aTop) } } - if (aTop) { - // Post a PLEvent that will remove the request after we've returned - // to the event loop. This mimics the way it is called by nsIChannel - // implementations. - - nsCOMPtr uiThreadQueue; - NS_GetMainEventQ(getter_AddRefs(uiThreadQueue)); - NS_ENSURE_TRUE(uiThreadQueue, NS_ERROR_UNEXPECTED); - - PLEvent *evt = new RestorePresentationEvent(this); - NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY); - - rv = uiThreadQueue->PostEvent(evt); - if (NS_FAILED(rv)) { - PL_DestroyEvent(evt); - } - } else { + if (!aTop) { // For non-top frames, there is no notion of making sure that the // previous document is in the domwindow when STATE_START notifications // happen. We can just call BeginRestore for all of the child shells @@ -5062,55 +5050,25 @@ nsDocShell::GetRestoringDocument(PRBool *aRestoring) return NS_OK; } -class ContentViewerDestroyEvent : public PLEvent -{ -public: - ContentViewerDestroyEvent(nsIContentViewer *aViewer, nsDocShell *aShell); - - nsCOMPtr mViewer; -}; - -PR_STATIC_CALLBACK(void*) -HandleContentViewerDestroyEvent(PLEvent *aEvent) -{ - NS_STATIC_CAST(ContentViewerDestroyEvent*, aEvent)->mViewer->Destroy(); - return nsnull; -} - -PR_STATIC_CALLBACK(void) -DestroyContentViewerDestroyEvent(PLEvent *aEvent) -{ - delete NS_STATIC_CAST(ContentViewerDestroyEvent*, aEvent); -} - -ContentViewerDestroyEvent::ContentViewerDestroyEvent(nsIContentViewer *aViewer, - nsDocShell *aShell) - : mViewer(aViewer) -{ - PL_InitEvent(this, aShell, ::HandleContentViewerDestroyEvent, - ::DestroyContentViewerDestroyEvent); -} - nsresult -nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool aSavePresentation, - PRBool *aRestored) +nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool *aRestoring) { NS_ASSERTION(mLoadType & LOAD_CMD_HISTORY, "RestorePresentation should only be called for history loads"); - nsCOMPtr uri; - aSHEntry->GetURI(getter_AddRefs(uri)); - nsCOMPtr viewer; aSHEntry->GetContentViewer(getter_AddRefs(viewer)); #ifdef DEBUG_PAGE_CACHE + nsCOMPtr uri; + aSHEntry->GetURI(getter_AddRefs(uri)); + nsCAutoString spec; if (uri) uri->GetSpec(spec); #endif - *aRestored = PR_FALSE; + *aRestoring = PR_FALSE; if (!viewer) { #ifdef DEBUG_PAGE_CACHE @@ -5141,9 +5099,7 @@ nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool aSavePresentation, printf("restoring presentation from session history: %s\n", spec.get()); #endif - // Notify the old content viewer that it's being hidden. - FirePageHideNotification(!aSavePresentation); - mFiredUnloadEvent = PR_FALSE; + mLSHE = aSHEntry; // Add the request to our load group. We do this before swapping out // the content viewers so that consumers of STATE_START can access @@ -5153,6 +5109,83 @@ nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool aSavePresentation, BeginRestore(viewer, PR_TRUE); + // Post a PLEvent that will remove the request after we've returned + // to the event loop. This mimics the way it is called by nsIChannel + // implementations. + + nsCOMPtr uiThreadQueue; + NS_GetMainEventQ(getter_AddRefs(uiThreadQueue)); + NS_ENSURE_TRUE(uiThreadQueue, NS_ERROR_UNEXPECTED); + + PLEvent *evt = new RestorePresentationEvent(this); + NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY); + + nsresult rv = uiThreadQueue->PostEvent(evt); + if (NS_SUCCEEDED(rv)) { + // The rest of the restore processing will happen on our PLEvent + // callback. + *aRestoring = PR_TRUE; + } else { + PL_DestroyEvent(evt); + } + + return NS_OK; +} + +nsresult +nsDocShell::RestoreFromHistory() +{ + // This section of code follows the same ordering as CreateContentViewer. + if (!mLSHE) + return NS_ERROR_FAILURE; + + nsCOMPtr viewer; + mLSHE->GetContentViewer(getter_AddRefs(viewer)); + if (!viewer) + return NS_ERROR_FAILURE; + + if (mSavingOldViewer) { + // We determined that it was safe to cache the document presentation + // at the time we initiated the new load. We need to check whether + // it's still safe to do so, since there may have been DOM mutations + // or new requests initiated. + nsCOMPtr domDoc; + viewer->GetDOMDocument(getter_AddRefs(domDoc)); + nsCOMPtr doc = do_QueryInterface(domDoc); + nsIRequest *request = nsnull; + if (doc) + request = doc->GetChannel(); + mSavingOldViewer = CanSavePresentation(mLoadType, request); + } + + // Notify the old content viewer that it's being hidden. + FirePageHideNotification(!mSavingOldViewer); + // Set mFiredUnloadEvent = PR_FALSE so that the unload handler for the + // *new* document will fire. + mFiredUnloadEvent = PR_FALSE; + + mURIResultedInDocument = PR_TRUE; + nsCOMPtr rootSH; + GetRootSessionHistory(getter_AddRefs(rootSH)); + if (rootSH) { + nsCOMPtr hist = do_QueryInterface(rootSH); + hist->UpdateIndex(); + } + + // Rather than call Embed(), we will retrieve the viewer from the session + // history entry and swap it in. + // XXX can we refactor this so that we can just call Embed()? + // XXX PersistLayoutHistoryState() ? + nsresult rv; + if (mContentViewer) { + if (mSavingOldViewer && NS_FAILED(CaptureState())) { + if (mOSHE) { + mOSHE->SyncPresentationState(); + } + mSavingOldViewer = PR_FALSE; + } + } + // Save off the root view's parent and sibling so that we can insert the // new content viewer's root view at the same position. Also save the // bounds of the root view's widget. @@ -5185,36 +5218,9 @@ nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool aSavePresentation, // content viewer, we prevent the viewer from being torn down after // Destroy() is called. - nsresult rv; - if (mContentViewer) { - if (aSavePresentation) { - rv = CaptureState(); - if (NS_FAILED(rv) && mOSHE) { - mOSHE->SyncPresentationState(); - aSavePresentation = PR_FALSE; - } - } - - mContentViewer->Close(aSavePresentation ? mOSHE.get() : nsnull); - mContentViewer->Hide(); - - // It's unsafe to actually destroy the content viewer here. - // In particular, if we're called from within a plugin's event loop, - // then attempting to tear down the plugin can cause a hang. - // So, post an event to destroy and release the viewer. - - nsCOMPtr uiThreadQueue; - NS_GetMainEventQ(getter_AddRefs(uiThreadQueue)); - NS_ENSURE_TRUE(uiThreadQueue, NS_ERROR_UNEXPECTED); - - PLEvent *evt = new ContentViewerDestroyEvent(mContentViewer, this); - NS_ENSURE_TRUE(evt, NS_ERROR_OUT_OF_MEMORY); - - rv = uiThreadQueue->PostEvent(evt); - if (NS_FAILED(rv)) { - PL_DestroyEvent(evt); - } + mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull); + mContentViewer->Destroy(); } mContentViewer.swap(viewer); @@ -5224,37 +5230,84 @@ nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool aSavePresentation, rv = mContentViewer->Open(); // Now remove it from the cached presentation. - aSHEntry->SetContentViewer(nsnull); - - // Restore our child docshell hierarchy. - DestroyChildren(); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt32 i = 0; - nsCOMPtr childShell; - while (NS_SUCCEEDED(aSHEntry->ChildShellAt(i++, - getter_AddRefs(childShell))) && - childShell) { - AddChild(childShell); - } - - // And release the references in the history entry. - aSHEntry->ClearChildShells(); - - // Dispatch STATE_START notifications for the new child shells. - rv = BeginRestoreChildren(); - NS_ENSURE_SUCCESS(rv, rv); + mLSHE->SetContentViewer(nsnull); + mEODForCurrentDocument = PR_FALSE; // Restore the sticky state of the viewer. The viewer has set this state // on the history entry in Destroy() just before marking itself non-sticky, // to avoid teardown of the presentation. PRBool sticky; - aSHEntry->GetSticky(&sticky); + mLSHE->GetSticky(&sticky); mContentViewer->SetSticky(sticky); + // Now that we have switched documents, forget all of our children. + DestroyChildren(); + NS_ENSURE_SUCCESS(rv, rv); + + // mLSHE is now our currently-loaded document. + mOSHE = mLSHE; + + // XXX special wyciwyg handling in Embed()? + // XXX SetLayoutHistoryState(null)? + // This is the end of our Embed() replacement + + mSavingOldViewer = PR_FALSE; + mEODForCurrentDocument = PR_FALSE; + + // Tell the event loop to favor plevents over user events, see comments + // in CreateContentViewer. + if (++gNumberOfDocumentsLoading == 1) + PL_FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT); + + nsCOMPtr domDoc; + mContentViewer->GetDOMDocument(getter_AddRefs(domDoc)); + nsCOMPtr document = do_QueryInterface(domDoc); + if (document) { + SetCurrentURI(document->GetDocumentURI(), + document->GetChannel(), PR_TRUE); + } + + // This is the end of our CreateContentViewer() replacement. + // Now we simulate a load. First, we restore the state of the javascript + // window object. + nsCOMPtr privWin = + 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); + if (nsDoc) { + const nsAFlatString &title = document->GetDocumentTitle(); + nsDoc->SetTitle(title); + } + + // Now we simulate appending child docshells for subframes. + PRInt32 i = 0; + nsCOMPtr child; + while (NS_SUCCEEDED(mLSHE->ChildShellAt(i++, getter_AddRefs(child))) && + child) { + AddChild(child); + + nsCOMPtr childShell = do_QueryInterface(child); + rv = childShell->BeginRestore(nsnull, PR_FALSE); + NS_ENSURE_SUCCESS(rv, rv); + } + + // And release the references in the history entry. + mLSHE->ClearChildShells(); + // get the previous content viewer size nsRect oldBounds(0, 0, 0, 0); - aSHEntry->GetViewerBounds(oldBounds); + mLSHE->GetViewerBounds(oldBounds); nsCOMPtr shell; nsDocShell::GetPresShell(getter_AddRefs(shell)); @@ -5285,26 +5338,10 @@ nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool aSavePresentation, } } - // restore the state of the window object - nsCOMPtr privWin = - do_GetInterface(NS_STATIC_CAST(nsIInterfaceRequestor*, this)); - NS_ASSERTION(privWin, "could not get nsPIDOMWindow interface"); - - nsCOMPtr windowState; - aSHEntry->GetWindowState(getter_AddRefs(windowState)); - - rv = privWin->RestoreWindowState(windowState); - NS_ENSURE_SUCCESS(rv, rv); - - aSHEntry->SetWindowState(nsnull); - // Restore the refresh URI list. The refresh timers will be restarted // when EndPageLoad() is called. - aSHEntry->GetRefreshURIList(getter_AddRefs(mRefreshURIList)); - aSHEntry->SetRefreshURIList(nsnull); - - // aSHEntry is now our currently-loaded document. - mOSHE = aSHEntry; + mLSHE->GetRefreshURIList(getter_AddRefs(mRefreshURIList)); + mLSHE->SetRefreshURIList(nsnull); // Meta-refresh timers have been restarted for this shell, but not // for our children. Walk the child shells and restart their timers. @@ -5315,13 +5352,6 @@ nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool aSavePresentation, child->ResumeRefreshURIs(); } - nsCOMPtr rootSH; - GetRootSessionHistory(getter_AddRefs(rootSH)); - if (rootSH) { - nsCOMPtr hist = do_QueryInterface(rootSH); - hist->UpdateIndex(); - } - // Make sure this presentation is the same size as the previous // presentation. If this is not the same size we showed it at last time, // then we need to resize the widget. @@ -5348,29 +5378,13 @@ nsDocShell::RestorePresentation(nsISHEntry *aSHEntry, PRBool aSavePresentation, } } - // Tell the event loop to favor plevents over user events, see comments - // in CreateContentViewer. - if (++gNumberOfDocumentsLoading == 1) - PL_FavorPerformanceHint(PR_TRUE, NS_EVENT_STARVATION_DELAY_HINT); + // Simulate the completion of the load. + nsDocShell::FinishRestore(); - SetCurrentURI(uri); - - // Restore the page title. Re-setting the current title on the document - // will update the docshell title and dispatch DOMTitleChanged. - // (We must do this after setting mOSHE so that the correct history entry - // title is set). - nsIDocument *doc = shell->GetDocument(); - nsCOMPtr nsDoc = do_QueryInterface(doc); - if (doc && nsDoc) { - const nsAFlatString &title = doc->GetDocumentTitle(); - nsDoc->SetTitle(title); - } - // Restart plugins, and paint the content. if (shell) shell->Thaw(); - *aRestored = PR_TRUE; return NS_OK; } @@ -5582,7 +5596,6 @@ nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer) PRBool styleDisabled; // |newMUDV| also serves as a flag to set the data from the above vars nsCOMPtr newMUDV; - PRBool savePresentation = mSavingOldViewer; if (mContentViewer || parent) { nsCOMPtr oldMUDV; @@ -5595,11 +5608,11 @@ nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer) // Tell the old content viewer to hibernate in session history when // it is destroyed. - if (savePresentation && NS_FAILED(CaptureState())) { + if (mSavingOldViewer && NS_FAILED(CaptureState())) { if (mOSHE) { mOSHE->SyncPresentationState(); } - savePresentation = PR_FALSE; + mSavingOldViewer = PR_FALSE; } } else { @@ -5724,7 +5737,7 @@ nsDocShell::SetupNewViewer(nsIContentViewer * aNewViewer) } } - mContentViewer->Close(savePresentation ? mOSHE.get() : nsnull); + mContentViewer->Close(mSavingOldViewer ? mOSHE.get() : nsnull); aNewViewer->SetPreviousViewer(mContentViewer); mContentViewer = nsnull; @@ -6359,9 +6372,9 @@ nsDocShell::InternalLoad(nsIURI * aURI, // If we have a saved content viewer in history, restore and show it now. if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) { nsCOMPtr oldEntry = mOSHE; - PRBool restored; - rv = RestorePresentation(aSHEntry, savePresentation, &restored); - if (restored) + PRBool restoring; + rv = RestorePresentation(aSHEntry, &restoring); + if (restoring) return rv; // We failed to restore the presentation, so clean up. diff --git a/mozilla/docshell/base/nsDocShell.h b/mozilla/docshell/base/nsDocShell.h index ba3d3511d8b..23b85f0f30d 100644 --- a/mozilla/docshell/base/nsDocShell.h +++ b/mozilla/docshell/base/nsDocShell.h @@ -261,6 +261,12 @@ public: // nsIScriptGlobalObjectOwner methods virtual nsIScriptGlobalObject* GetScriptGlobalObject(); + + // Restores a cached presentation from history (mLSHE). + // This method swaps out the content viewer and simulates loads for + // subframes. It then simulates the completion of the toplevel load. + nsresult RestoreFromHistory(); + protected: // Object Management virtual ~nsDocShell(); @@ -440,12 +446,10 @@ protected: // state) and stores them on |mOSHE|. nsresult CaptureState(); - // Restores the presentation stored in |aSHEntry|. The old presentation - // is saved in session history if |aSavePresentation| is true. - // If a presentation could be restored, |aRestored| is set to true. - nsresult RestorePresentation(nsISHEntry *aSHEntry, - PRBool aSavePresentation, - PRBool *aRestored); + // Begin the toplevel restore process for |aSHEntry|. + // This simulates a channel open, and defers the real work until + // RestoreFromHistory is called from a PLEvent. + nsresult RestorePresentation(nsISHEntry *aSHEntry, PRBool *aRestoring); // Call BeginRestore(nsnull, PR_FALSE) for each child of this shell. nsresult BeginRestoreChildren(); diff --git a/mozilla/docshell/base/nsIDocShell.idl b/mozilla/docshell/base/nsIDocShell.idl index 9db7ca7b954..f172f89f481 100644 --- a/mozilla/docshell/base/nsIDocShell.idl +++ b/mozilla/docshell/base/nsIDocShell.idl @@ -365,13 +365,13 @@ interface nsIDocShell : nsISupports void resumeRefreshURIs(); /** - * Begin firing WebProgressListener notifications and DOM events for - * restoring a page presentation. |viewer| is the content viewer whose - * document we are starting to load. If null, it defaults to the docshell's - * current content viewer, creating one if necessary. |top| should be true - * for the toplevel docshell that is being restored; it will be set to false - * when this method is called for child docshells. This method will post an - * event to call finishRestore() after returning to the event loop. + * Begin firing WebProgressListener notifications for restoring a page + * presentation. |viewer| is the content viewer whose document we are + * starting to load. If null, it defaults to the docshell's current content + * viewer, creating one if necessary. |top| should be true for the toplevel + * docshell that is being restored; it will be set to false when this method + * is called for child docshells. This method will post an event to + * complete the simulated load after returning to the event loop. */ void beginRestore(in nsIContentViewer viewer, in boolean top);