Merge after pull
authorDoug Turner <dougt@meer.net>
Mon, 29 Sep 2008 14:20:20 -0700
changeset 19881 8858457b51cec34d1b43b066876c07710b5175c1
parent 19880 7bb45bc91ebeb28595e4b79fd35772b91826f5c4 (current diff)
parent 19876 6357eb31cec64741a90e209cb55af1159fb30f25 (diff)
child 19882 ec3a3468d1b9bb65c6230519dc8867f873c72221
child 19885 04de094bf5debeb11bef59985b692e8684050237
push idunknown
push userunknown
push dateunknown
milestone1.9.1b1pre
Merge after pull
--- a/.hgtags
+++ b/.hgtags
@@ -8,8 +8,11 @@ c1d7e318a27574c995631fec166ad42672475702
 c1d7e318a27574c995631fec166ad42672475702 FIREFOX_3_1a1_RELEASE
 afc4ee509d9ca3bb4031015c3c22963dcb4b7e7f FIREFOX_3_1a1_RELEASE
 afc4ee509d9ca3bb4031015c3c22963dcb4b7e7f FIREFOX_3_1a1_BUILD2
 f197b51bbc29a30860e750ee87fd0a047a024f2e FIREFOX_3_1a2_BUILD1
 f197b51bbc29a30860e750ee87fd0a047a024f2e FIREFOX_3_1a2_RELEASE
 269af1ed75649989575d41f53a12048015c6d50e FENNEC_M8
 920a4326d1087b174c2fa2b9a8358e12c697022c SEAMONKEY_2_0a1_BUILD1
 920a4326d1087b174c2fa2b9a8358e12c697022c SEAMONKEY_2_0a1_RELEASE
+f197b51bbc29a30860e750ee87fd0a047a024f2e UPDATE_PACKAGING_R5
+f197b51bbc29a30860e750ee87fd0a047a024f2e -m
+f197b51bbc29a30860e750ee87fd0a047a024f2e Adding UPDATE_PACKAGING_R5 tag in order to make patcher work.
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -803,16 +803,23 @@ public:
                                         nsXPIDLString& aResult);
 
   /**
    * Returns true if aDocument is a chrome document
    */
   static PRBool IsChromeDoc(nsIDocument *aDocument);
 
   /**
+   * Returns true if aDocument belongs to a chrome docshell for
+   * display purposes.  Returns false for null documents or documents
+   * which do not belong to a docshell.
+   */
+  static PRBool IsInChromeDocshell(nsIDocument *aDocument);
+
+  /**
    * Release *aSupportsPtr when the shutdown notification is received
    */
   static nsresult ReleasePtrOnShutdown(nsISupports** aSupportsPtr) {
     NS_ASSERTION(aSupportsPtr, "Expect to crash!");
     NS_ASSERTION(*aSupportsPtr, "Expect to crash!");
     return sPtrsToPtrsToRelease->AppendElement(aSupportsPtr) ? NS_OK :
       NS_ERROR_OUT_OF_MEMORY;
   }
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -2899,16 +2899,34 @@ nsContentUtils::IsChromeDoc(nsIDocument 
   
   nsCOMPtr<nsIPrincipal> systemPrincipal;
   sSecurityManager->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
 
   return aDocument->NodePrincipal() == systemPrincipal;
 }
 
 // static
+PRBool
+nsContentUtils::IsInChromeDocshell(nsIDocument *aDocument)
+{
+  if (!aDocument) {
+    return PR_FALSE;
+  }
+
+  nsCOMPtr<nsISupports> docContainer = aDocument->GetContainer();
+  nsCOMPtr<nsIDocShellTreeItem> docShell(do_QueryInterface(docContainer));
+  PRInt32 itemType = nsIDocShellTreeItem::typeContent;
+  if (docShell) {
+    docShell->GetItemType(&itemType);
+  }
+
+  return itemType == nsIDocShellTreeItem::typeChrome;
+}
+
+// static
 nsIContentPolicy*
 nsContentUtils::GetContentPolicy()
 {
   if (!sTriedToGetContentPolicy) {
     CallGetService(NS_CONTENTPOLICY_CONTRACTID, &sContentPolicyService);
     // It's OK to not have a content policy service
     sTriedToGetContentPolicy = PR_TRUE;
   }
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -5722,19 +5722,17 @@ nsDocument::FlushPendingNotifications(mo
   // Determine if it is safe to flush the sink notifications
   // by determining if it safe to flush all the presshells.
   if (sink && (aType == Flush_Content || IsSafeToFlush())) {
     sink->FlushPendingNotifications(aType);
   }
 
   // Should we be flushing pending binding constructors in here?
 
-  nsPIDOMWindow *window = GetWindow();
-
-  if (aType <= Flush_ContentAndNotify || !window) {
+  if (aType <= Flush_ContentAndNotify) {
     // Nothing to do here
     return;
   }
 
   // If we have a parent we must flush the parent too to ensure that our
   // container is reflown if its size was changed.  But if it's not safe to
   // flush ourselves, then don't flush the parent, since that can cause things
   // like resizes of our frame's widget, which we can't handle while flushing
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -210,16 +210,18 @@ include $(topsrcdir)/config/rules.mk
 		test_title.html \
 		test_bug453521.html \
 		test_bug391728.html \
 		file_bug391728.html \
 		file_bug391728_2.html \
 		test_bug368972.html \
 		test_bug450160.html \
 		test_bug454326.html \
+		test_bug457746.html \
+		bug457746.sjs \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
 check::
 	@$(EXIT_ON_ERROR) \
 	for f in $(subst .cpp,,$(CPP_UNIT_TESTS)); do \
new file mode 100644
--- /dev/null
+++ b/content/base/test/bug457746.sjs
@@ -0,0 +1,11 @@
+function handleRequest(request, response)
+{
+  response.setHeader("Content-Type", "text/plain; charset=ISO-8859-1", false);
+  const body = [0xC1];
+  var bos = Components.classes["@mozilla.org/binaryoutputstream;1"]
+      .createInstance(Components.interfaces.nsIBinaryOutputStream);
+  bos.setOutputStream(response.bodyOutputStream);
+
+  bos.writeByteArray(body, body.length);
+}
+
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug457746.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=457746
+-->
+<head>
+  <title>Test for Bug 457746</title>
+  <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=457746">Mozilla Bug 457746</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 457746 **/
+SimpleTest.waitForExplicitFinish();
+
+var xhr = new XMLHttpRequest();
+xhr.open("GET", "bug457746.sjs");
+xhr.send("");
+xhr.abort();
+xhr.open("GET", "bug457746.sjs");
+xhr.onreadystatechange = function() {
+  if (xhr.readyState == 4) {
+    is(xhr.responseText, "\u00c1", "Broken encoding conversion?");
+    SimpleTest.finish();
+  }
+}
+xhr.send("");
+</script>
+</pre>
+</body>
+</html>
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -6277,16 +6277,19 @@ nsEventStateManager::ShiftFocusByDoc(PRB
   // ShiftFocus.  The docshell tree should be kept in depth-first frame tree
   // order, the same as we use for tabbing, so the effect should be the same,
   // but this is much faster.
 
   NS_ASSERTION(mPresContext, "no prescontext");
 
   nsCOMPtr<nsISupports> pcContainer = mPresContext->GetContainer();
   nsCOMPtr<nsIDocShellTreeNode> curNode = do_QueryInterface(pcContainer);
+  if (!curNode) {
+    return;
+  }
 
   // perform a depth first search (preorder) of the docshell tree
   // looking for an HTML Frame or a chrome document
 
   nsCOMPtr<nsIDocShellTreeItem> nextItem;
   nsCOMPtr<nsIDocShell> nextShell;
   do {
     if (aForward) {
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -704,34 +704,36 @@ nsHTMLDocument::StartDocumentLoad(const 
       cacheDescriptor = do_QueryInterface(cacheToken);
   }
 
   if (needsParser) {
     mParser = do_CreateInstance(kCParserCID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
-
-  nsCOMPtr<nsIDocumentCharsetInfo> dcInfo;
-  docShell->GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
   PRInt32 textType = GET_BIDI_OPTION_TEXTTYPE(GetBidiOptions());
 
   // Look for the parent document.  Note that at this point we don't have our
   // content viewer set up yet, and therefore do not have a useful
   // mParentDocument.
 
   // in this block of code, if we get an error result, we return it
   // but if we get a null pointer, that's perfectly legal for parent
   // and parentContentViewer
+  nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aContainer));
+
+  // No support yet for docshell-less HTML
+  NS_ENSURE_TRUE(docShell || IsXHTML(), NS_ERROR_FAILURE);
+
   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(docShell));
-  NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
-  docShellAsItem->GetSameTypeParent(getter_AddRefs(parentAsItem));
+  if (docShellAsItem) {
+    docShellAsItem->GetSameTypeParent(getter_AddRefs(parentAsItem));
+  }
 
   nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));
   nsCOMPtr<nsIDocument> parentDocument;
   nsCOMPtr<nsIContentViewer> parentContentViewer;
   if (parent) {
     rv = parent->GetContentViewer(getter_AddRefs(parentContentViewer));
     NS_ENSURE_SUCCESS(rv, rv);
     nsCOMPtr<nsIDocumentViewer> docViewer =
@@ -742,17 +744,19 @@ nsHTMLDocument::StartDocumentLoad(const 
   }
 
   //
   // The following logic is mirrored in nsWebShell::Embed!
   //
   nsCOMPtr<nsIMarkupDocumentViewer> muCV;
   PRBool muCVIsParent = PR_FALSE;
   nsCOMPtr<nsIContentViewer> cv;
-  docShell->GetContentViewer(getter_AddRefs(cv));
+  if (docShell) {
+    docShell->GetContentViewer(getter_AddRefs(cv));
+  }
   if (cv) {
      muCV = do_QueryInterface(cv);
   } else {
     muCV = do_QueryInterface(parentContentViewer);
     if (muCV) {
       muCVIsParent = PR_TRUE;
     }
   }
@@ -779,16 +783,21 @@ nsHTMLDocument::StartDocumentLoad(const 
   
   if (IsXHTML()) {
     charsetSource = kCharsetFromDocTypeDefault;
     charset.AssignLiteral("UTF-8");
     TryChannelCharset(aChannel, charsetSource, charset);
     parserCharsetSource = charsetSource;
     parserCharset = charset;
   } else {
+    NS_ASSERTION(docShell && docShellAsItem, "Unexpected null value");
+    
+    nsCOMPtr<nsIDocumentCharsetInfo> dcInfo;
+    docShell->GetDocumentCharsetInfo(getter_AddRefs(dcInfo));
+
     charsetSource = kCharsetUninitialized;
     wyciwygChannel = do_QueryInterface(aChannel);
 
     // The following charset resolving calls has implied knowledge
     // about charset source priority order. Each try will return true
     // if the source is higher or equal to the source as its name
     // describes. Some try call might change charset source to
     // multiple values, like TryHintCharset and TryParentCharset. It
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -303,16 +303,18 @@ nsDocShell::nsDocShell():
     mSavingOldViewer(PR_FALSE),
     mAppType(nsIDocShell::APP_TYPE_UNKNOWN),
     mChildOffset(0),
     mBusyFlags(BUSY_FLAGS_NONE),
     mMarginWidth(0),
     mMarginHeight(0),
     mItemType(typeContent),
     mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto),
+    mPreviousTransIndex(-1),
+    mLoadedTransIndex(-1),
     mTreeOwner(nsnull),
     mChromeEventHandler(nsnull)
 #ifdef DEBUG
     , mInEnsureScriptEnv(PR_FALSE)
 #endif
 {
     if (gDocShellCount++ == 0) {
         NS_ASSERTION(sURIFixup == nsnull,
@@ -1636,16 +1638,52 @@ nsDocShell::SetUseErrorPages(PRBool aUse
             mObserveErrorPages = PR_FALSE;
         }
     }
     mUseErrorPages = aUseErrorPages;
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShell::GetPreviousTransIndex(PRInt32 *aPreviousTransIndex)
+{
+    *aPreviousTransIndex = mPreviousTransIndex;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetLoadedTransIndex(PRInt32 *aLoadedTransIndex)
+{
+    *aLoadedTransIndex = mLoadedTransIndex;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::HistoryPurged(PRInt32 aNumEntries)
+{
+    // These indices are used for fastback cache eviction, to determine
+    // which session history entries are candidates for content viewer
+    // eviction.  We need to adjust by the number of entries that we
+    // just purged from history, so that we look at the right session history
+    // entries during eviction.
+    mPreviousTransIndex = PR_MAX(-1, mPreviousTransIndex - aNumEntries);
+    mLoadedTransIndex = PR_MAX(0, mLoadedTransIndex - aNumEntries);
+
+    PRInt32 count = mChildList.Count();
+    for (PRInt32 i = 0; i < count; ++i) {
+        nsCOMPtr<nsIDocShell> shell = do_QueryInterface(ChildAt(i));
+        if (shell) {
+            shell->HistoryPurged(aNumEntries);
+        }
+    }
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::GetSessionStorageForURI(nsIURI* aURI,
                                     nsIDOMStorage** aStorage)
 {
     NS_ENSURE_ARG_POINTER(aStorage);
 
     *aStorage = nsnull;
 
     nsCOMPtr<nsIDocShellTreeItem> topItem;
@@ -2680,23 +2718,40 @@ nsDocShell::DoAddChildSHEntry(nsISHEntry
 {
     /* You will get here when you are in a subframe and
      * a new url has been loaded on you. 
      * The mOSHE in this subframe will be the previous url's
      * mOSHE. This mOSHE will be used as the identification
      * for this subframe in the  CloneAndReplace function.
      */
 
+    // In this case, we will end up calling AddEntry, which increases the
+    // current index by 1
+    nsCOMPtr<nsISHistory> rootSH;
+    GetRootSessionHistory(getter_AddRefs(rootSH));
+    if (rootSH) {
+        rootSH->GetIndex(&mPreviousTransIndex);
+    }
+
     nsresult rv;
     nsCOMPtr<nsIDocShellHistory> parent =
         do_QueryInterface(GetAsSupports(mParent), &rv);
     if (parent) {
         rv = parent->AddChildSHEntry(mOSHE, aNewEntry, aChildOffset);
     }
 
+
+    if (rootSH) {
+        rootSH->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+        printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
+               mLoadedTransIndex);
+#endif
+    }
+
     return rv;
 }
 
 NS_IMETHODIMP
 nsDocShell::SetUseGlobalHistory(PRBool aUseGlobalHistory)
 {
     nsresult rv;
 
@@ -5408,27 +5463,22 @@ nsDocShell::DetachEditorFromWindow()
 nsresult
 nsDocShell::CaptureState()
 {
     if (!mOSHE || mOSHE == mLSHE) {
         // No entry to save into, or we're replacing the existing entry.
         return NS_ERROR_FAILURE;
     }
 
-    PRBool shouldSaveContentViewer = PR_FALSE;
-    nsresult rv = mOSHE->GetSaveContentViewerFlag(&shouldSaveContentViewer);
-    if (NS_FAILED(rv) || !shouldSaveContentViewer)
-        return NS_ERROR_FAILURE;
-
     nsCOMPtr<nsPIDOMWindow> privWin = do_QueryInterface(mScriptGlobal);
     if (!privWin)
         return NS_ERROR_FAILURE;
 
     nsCOMPtr<nsISupports> windowState;
-    rv = privWin->SaveWindowState(getter_AddRefs(windowState));
+    nsresult rv = privWin->SaveWindowState(getter_AddRefs(windowState));
     NS_ENSURE_SUCCESS(rv, rv);
 
 #ifdef DEBUG_PAGE_CACHE
     nsCOMPtr<nsIURI> uri;
     mOSHE->GetURI(getter_AddRefs(uri));
     nsCAutoString spec;
     if (uri)
         uri->GetSpec(spec);
@@ -5729,17 +5779,23 @@ nsDocShell::RestoreFromHistory()
     // *new* document will fire.
     mFiredUnloadEvent = PR_FALSE;
 
     mURIResultedInDocument = PR_TRUE;
     nsCOMPtr<nsISHistory> rootSH;
     GetRootSessionHistory(getter_AddRefs(rootSH));
     if (rootSH) {
         nsCOMPtr<nsISHistoryInternal> hist = do_QueryInterface(rootSH);
+        rootSH->GetIndex(&mPreviousTransIndex);
         hist->UpdateIndex();
+        rootSH->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+        printf("Previous index: %d, Loaded index: %d\n\n", mPreviousTransIndex,
+                   mLoadedTransIndex);
+#endif
     }
 
     // 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()?
     PersistLayoutHistoryState();
     nsresult rv;
     if (mContentViewer) {
@@ -8047,17 +8103,23 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIC
         }
     }
 
     // If this was a history load, update the index in 
     // SH. 
     if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
         nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
         if (shInternal) {
+            rootSH->GetIndex(&mPreviousTransIndex);
             shInternal->UpdateIndex();
+            rootSH->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+            printf("Previous index: %d, Loaded index: %d\n\n",
+                   mPreviousTransIndex, mLoadedTransIndex);
+#endif
         }
     }
     PRBool onLocationChangeNeeded = SetCurrentURI(aURI, aChannel,
                                                   aFireOnLocationChange);
     // Make sure to store the referrer from the channel, if any
     SetupReferrerFromChannel(aChannel);
     return onLocationChangeNeeded;
 }
@@ -8254,17 +8316,23 @@ nsDocShell::AddToSessionHistory(nsIURI *
             if (shPrivate)
                 rv = shPrivate->ReplaceEntry(index, entry);          
         }
         else {
             // Add to session history
             nsCOMPtr<nsISHistoryInternal>
                 shPrivate(do_QueryInterface(mSessionHistory));
             NS_ENSURE_TRUE(shPrivate, NS_ERROR_FAILURE);
+            mSessionHistory->GetIndex(&mPreviousTransIndex);
             rv = shPrivate->AddEntry(entry, shouldPersist);
+            mSessionHistory->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+            printf("Previous index: %d, Loaded index: %d\n\n",
+                   mPreviousTransIndex, mLoadedTransIndex);
+#endif
         }
     }
     else {  
         // This is a subframe.
         if (!mOSHE || !LOAD_TYPE_HAS_FLAGS(mLoadType,
                                            LOAD_FLAGS_REPLACE_HISTORY))
             rv = DoAddChildSHEntry(entry, mChildOffset);
     }
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -644,16 +644,21 @@ protected:
     // Holds a weak pointer to a RestorePresentationEvent object if any that
     // holds a weak pointer back to us.  We use this pointer to possibly revoke
     // the event whenever necessary.
     nsRevocableEventPtr<RestorePresentationEvent> mRestorePresentationEvent;
 
     // hash of session storages, keyed by domain
     nsInterfaceHashtable<nsCStringHashKey, nsIDOMStorage> mStorages;
 
+    // Index into the SHTransaction list, indicating the previous and current
+    // transaction at the time that this DocShell begins to load
+    PRInt32                    mPreviousTransIndex;
+    PRInt32                    mLoadedTransIndex;
+
     // Editor data, if this document is designMode or contentEditable.
     nsAutoPtr<nsDocShellEditorData> mEditorData;
 
     // Transferable hooks/callbacks
     nsCOMPtr<nsIClipboardDragDropHookList>  mTransferableHookData;
 
     // Secure browser UI object
     nsCOMPtr<nsISecureBrowserUI> mSecurityUI;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -63,17 +63,17 @@ interface nsIWebNavigation;
 interface nsISimpleEnumerator;
 interface nsIInputStream;
 interface nsIRequest;
 interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIDOMStorage;
 
-[scriptable, uuid(cf974f45-d2f1-4bd5-aecd-568a1c4da454)]
+[scriptable, uuid(dc4daea1-b43d-406f-bd62-c2ee879192ad)]
 interface nsIDocShell : nsISupports
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -395,16 +395,32 @@ interface nsIDocShell : nsISupports
   void finishRestore();
 
   /* Track whether we're currently restoring a document presentation. */
   readonly attribute boolean restoringDocument;
 
   /* attribute to access whether error pages are enabled */
   attribute boolean useErrorPages;
 
+  /**
+   * Keeps track of the previous SHTransaction index and the current
+   * SHTransaction index at the time that the doc shell begins to load.
+   * Used for ContentViewer eviction.
+   */
+  readonly attribute long previousTransIndex;
+  readonly attribute long loadedTransIndex;
+
+  /**
+   * Notification that entries have been removed from the beginning of a
+   * nsSHistory which has this as its rootDocShell.
+   *
+   * @param numEntries - The number of entries removed
+   */
+  void historyPurged(in long numEntries);
+
   /*
    * Retrieves the WebApps session storage object for the supplied domain.
    * If it doesn't already exist, a new one will be created.
    *
    * @param domain the domain of the storage object to retrieve
    */
   nsIDOMStorage getSessionStorageForURI(in nsIURI uri);
 
--- a/docshell/base/nsWebShell.cpp
+++ b/docshell/base/nsWebShell.cpp
@@ -1234,17 +1234,23 @@ nsresult nsWebShell::EndPageLoad(nsIWebP
             rootAsWebnav->GetSessionHistory(getter_AddRefs(rootSH));
             }
           }
         }  // mSessionHistory
 
         if (rootSH && (mLoadType & LOAD_CMD_HISTORY)) {
           nsCOMPtr<nsISHistoryInternal> shInternal(do_QueryInterface(rootSH));
           if (shInternal) {
+            rootSH->GetIndex(&mPreviousTransIndex);
             shInternal->UpdateIndex();
+            rootSH->GetIndex(&mLoadedTransIndex);
+#ifdef DEBUG_PAGE_CACHE
+            printf("Previous index: %d, Loaded index: %d\n\n",
+                  mPreviousTransIndex, mLoadedTransIndex);
+#endif
           }
         }
 
         // Make it look like we really did honestly finish loading the
         // history page we were loading, since the "reload" load we're
         // about to kick off will reload our current history entry.  This
         // is a bit of a hack, and if the force-load fails I think we'll
         // end up being confused about what page we're on... but we would
--- a/docshell/shistory/public/nsISHEntry.idl
+++ b/docshell/shistory/public/nsISHEntry.idl
@@ -53,17 +53,17 @@ interface nsISupportsArray;
 %{C++
 struct nsRect;
 class nsDocShellEditorData;
 %}
 [ref] native nsRect(nsRect);
 [ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
 
 
-[scriptable, uuid(622412da-be41-407e-b57e-9aba2a282b00)]
+[scriptable, uuid(c16fde76-3108-450e-8c8c-ae8286f286ed)]
 interface nsISHEntry : nsIHistoryEntry
 {
     /** URI for the document */
     void setURI(in nsIURI aURI);
 
     /** Referrer URI */
     attribute nsIURI referrerURI;
 
@@ -147,19 +147,16 @@ interface nsISHEntry : nsIHistoryEntry
      * entries are separated by an anchor traversal or a subframe navigation in
      * some other frame).
      */
     attribute unsigned long pageIdentifier;
 
     /** attribute to set and get the cache key for the entry */
     attribute nsISupports cacheKey;
 
-    /** attribute to indicate whether the contentViewer should be saved */
-    attribute boolean saveContentViewerFlag;
-
     /** attribute to indicate whether layoutHistoryState should be saved */
     attribute boolean saveLayoutStateFlag;
 
     /** attribute to indicate whether the page is already expired in cache */
     attribute boolean expirationStatus;
 
     /**
      * attribute to indicate the content-type of the document that this
--- a/docshell/shistory/public/nsISHistoryInternal.idl
+++ b/docshell/shistory/public/nsISHistoryInternal.idl
@@ -47,17 +47,17 @@ interface nsIDocShell;
 %{C++
 #define NS_SHISTORY_INTERNAL_CID \
 { 0x9c47c121, 0x1c6e, 0x4d8f, \
   { 0xb9, 0x04, 0x3a, 0xc9, 0x68, 0x11, 0x6e, 0x88 } }
 
 #define NS_SHISTORY_INTERNAL_CONTRACTID "@mozilla.org/browser/shistory-internal;1"
 %}
 
-[scriptable, uuid(0e91697a-4ba0-4ede-b08a-0bae9ee1a701)]
+[scriptable, uuid(9c47c121-1c6e-4d8f-b904-3ac968116e88)]
 interface nsISHistoryInternal: nsISupports
 {
   /**
    * Add a new Entry to the History List
    * @param aEntry - The entry to add
    * @param aPersist - If true this specifies that the entry should persist
    * in the list.  If false, this means that when new entries are added
    * this element will not appear in the session history list.
@@ -87,20 +87,22 @@ interface nsISHistoryInternal: nsISuppor
    void replaceEntry(in long aIndex, in nsISHEntry aReplaceEntry);
 
   /** 
    * Get handle to the history listener
    */
    readonly attribute nsISHistoryListener listener;
 
   /**
-   * Count total number of content viewers globally and evict one if we are over
-   * our total max.  This is always called in Show(), after we destroy the
-   * previous viewer.
+   * Evict content viewers until the number of content viewers per tab
+   * is no more than gHistoryMaxViewers.  Also, count
+   * total number of content viewers globally and evict one if we are over
+   * our total max.  This is always called in Show(), after we destroy
+   * the previous viewer.
    */
-   void evictContentViewers();
+   void evictContentViewers(in long previousIndex, in long index);
    
    /**
     * Evict the content viewer associated with a session history entry
     * that has timed out.
     */
    void evictExpiredContentViewerForEntry(in nsISHEntry aEntry);
 };
--- a/docshell/shistory/src/nsSHEntry.cpp
+++ b/docshell/shistory/src/nsSHEntry.cpp
@@ -101,17 +101,16 @@ static void StopTrackingEntry(nsSHEntry 
 
 
 nsSHEntry::nsSHEntry() 
   : mLoadType(0)
   , mID(gEntryID++)
   , mPageIdentifier(mID)
   , mScrollPositionX(0)
   , mScrollPositionY(0)
-  , mSaveContentViewer(PR_TRUE)
   , mIsFrameNavigation(PR_FALSE)
   , mSaveLayoutState(PR_TRUE)
   , mExpired(PR_FALSE)
   , mSticky(PR_TRUE)
   , mParent(nsnull)
   , mViewerBounds(0, 0, 0, 0)
 {
 }
@@ -123,17 +122,16 @@ nsSHEntry::nsSHEntry(const nsSHEntry &ot
   , mTitle(other.mTitle)
   , mPostData(other.mPostData)
   , mLayoutHistoryState(other.mLayoutHistoryState)
   , mLoadType(0)         // XXX why not copy?
   , mID(other.mID)
   , mPageIdentifier(other.mPageIdentifier)
   , mScrollPositionX(0)  // XXX why not copy?
   , mScrollPositionY(0)  // XXX why not copy?
-  , mSaveContentViewer(other.mSaveContentViewer)
   , mIsFrameNavigation(other.mIsFrameNavigation)
   , mSaveLayoutState(other.mSaveLayoutState)
   , mExpired(other.mExpired)
   , mSticky(PR_TRUE)
   // XXX why not copy mContentType?
   , mCacheKey(other.mCacheKey)
   , mParent(other.mParent)
   , mViewerBounds(0, 0, 0, 0)
@@ -413,28 +411,16 @@ NS_IMETHODIMP nsSHEntry::GetCacheKey(nsI
 }
 
 NS_IMETHODIMP nsSHEntry::SetCacheKey(nsISupports* aCacheKey)
 {
   mCacheKey = aCacheKey;
   return NS_OK;
 }
 
-NS_IMETHODIMP nsSHEntry::GetSaveContentViewerFlag(PRBool * aFlag)
-{
-  *aFlag = mSaveContentViewer;
-  return NS_OK;
-}
-
-NS_IMETHODIMP nsSHEntry::SetSaveContentViewerFlag(PRBool  aFlag)
-{
-  mSaveContentViewer = aFlag;
-  return NS_OK;
-}
-
 NS_IMETHODIMP nsSHEntry::GetSaveLayoutStateFlag(PRBool * aFlag)
 {
   *aFlag = mSaveLayoutState;
   return NS_OK;
 }
 
 NS_IMETHODIMP nsSHEntry::SetSaveLayoutStateFlag(PRBool  aFlag)
 {
--- a/docshell/shistory/src/nsSHEntry.h
+++ b/docshell/shistory/src/nsSHEntry.h
@@ -97,17 +97,16 @@ private:
   nsCOMPtr<nsIInputStream>        mPostData;
   nsCOMPtr<nsILayoutHistoryState> mLayoutHistoryState;
   nsCOMArray<nsISHEntry>          mChildren;
   PRUint32                        mLoadType;  
   PRUint32                        mID;
   PRUint32                        mPageIdentifier;
   PRInt32                         mScrollPositionX;
   PRInt32                         mScrollPositionY;
-  PRPackedBool                    mSaveContentViewer;
   PRPackedBool                    mIsFrameNavigation;
   PRPackedBool                    mSaveLayoutState;
   PRPackedBool                    mExpired;
   PRPackedBool                    mSticky;
   nsCString                       mContentType;
   nsCOMPtr<nsISupports>           mCacheKey;
   nsISHEntry *                    mParent;  // weak reference
   nsCOMPtr<nsISupports>           mWindowState;
--- a/docshell/shistory/src/nsSHistory.cpp
+++ b/docshell/shistory/src/nsSHistory.cpp
@@ -325,27 +325,22 @@ nsSHistory::AddEntry(nsISHEntry * aSHEnt
   // Set the ShEntry and parent for the transaction. setting the 
   // parent will properly set the parent child relationship
   txn->SetPersist(aPersist);
   NS_ENSURE_SUCCESS(txn->Create(aSHEntry, currentTxn), NS_ERROR_FAILURE);
    
   // A little tricky math here...  Basically when adding an object regardless of
   // what the length was before, it should always be set back to the current and
   // lop off the forward.
-  PRInt32 oldIndex = mIndex;
   mLength = (++mIndex + 1);
 
   // If this is the very first transaction, initialize the list
   if(!mListRoot)
     mListRoot = txn;
 
-  // Evict a content viewer if we might have too many behind us.  Make sure to
-  // do this before calling PurgeHistory, since that might change our indexing.
-  EvictWindowContentViewers(oldIndex, mIndex);
-
   // Purge History list if it is too long
   if ((gHistoryMaxSize >= 0) && (mLength > gHistoryMaxSize))
     PurgeHistory(mLength-gHistoryMaxSize);
   
   return NS_OK;
 }
 
 /* Get size of the history list */
@@ -583,16 +578,19 @@ nsSHistory::PurgeHistory(PRInt32 aEntrie
   mIndex -= cnt;
 
   // Now if we were not at the end of the history, mIndex could have
   // become far too negative.  If so, just set it to -1.
   if (mIndex < -1) {
     mIndex = -1;
   }
 
+  if (mRootDocShell)
+    mRootDocShell->HistoryPurged(cnt);
+
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsSHistory::AddSHistoryListener(nsISHistoryListener * aListener)
 {
   NS_ENSURE_ARG_POINTER(aListener);
@@ -652,18 +650,20 @@ nsSHistory::GetListener(nsISHistoryListe
   NS_ENSURE_ARG_POINTER(aListener);
   if (mListener) 
     CallQueryReferent(mListener.get(),  aListener);
   // Don't addref aListener. It is a weak pointer.
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsSHistory::EvictContentViewers()
+nsSHistory::EvictContentViewers(PRInt32 aPreviousIndex, PRInt32 aIndex)
 {
+  // Check our per SHistory object limit in the currently navigated SHistory
+  EvictWindowContentViewers(aPreviousIndex, aIndex);
   // Check our total limit across all SHistory objects
   EvictGlobalContentViewer();
   return NS_OK;
 }
 
 //*****************************************************************************
 //    nsSHistory: nsIWebNavigation
 //*****************************************************************************
@@ -793,19 +793,25 @@ nsSHistory::EvictWindowContentViewers(PR
     return;
   }
 
   // These indices give the range of SHEntries whose content viewers will be
   // evicted
   PRInt32 startIndex, endIndex;
   if (aToIndex > aFromIndex) { // going forward
     endIndex = aToIndex - gHistoryMaxViewers;
+    if (endIndex <= 0) {
+      return;
+    }
     startIndex = PR_MAX(0, aFromIndex - gHistoryMaxViewers);
   } else { // going backward
     startIndex = aToIndex + gHistoryMaxViewers + 1;
+    if (startIndex >= mLength) {
+      return;
+    }
     endIndex = PR_MIN(mLength, aFromIndex + gHistoryMaxViewers + 1);
   }
 
 #ifdef DEBUG
   nsCOMPtr<nsISHTransaction> trans;
   GetTransactionAtIndex(0, getter_AddRefs(trans));
 
   // Walk the full session history and check that entries outside the window
@@ -823,21 +829,16 @@ nsSHistory::EvictWindowContentViewers(PR
                    "ContentViewer exists outside gHistoryMaxViewer range");
     }
 
     nsISHTransaction *temp = trans;
     temp->GetNext(getter_AddRefs(trans));
   }
 #endif
 
-  if (endIndex <= 0 || startIndex >= mLength) {
-    // We can guarantee that there is nothing to do in this case
-    return;
-  }
-  
   EvictContentViewersInRange(startIndex, endIndex);
 }
 
 void
 nsSHistory::EvictContentViewersInRange(PRInt32 aStart, PRInt32 aEnd)
 {
   nsCOMPtr<nsISHTransaction> trans;
   GetTransactionAtIndex(aStart, getter_AddRefs(trans));
@@ -1120,25 +1121,16 @@ nsSHistory::LoadURI(const PRUnichar* aUR
                     nsIInputStream* aExtraHeaderStream)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHistory::GotoIndex(PRInt32 aIndex)
 {
- 
-  if (mIndex > -1 && PR_ABS(aIndex - mIndex) > gHistoryMaxViewers) {
-    // The current entry is too far from the new index, so mark it so its
-    // content viewer doesn't get saved.
-    nsCOMPtr<nsISHEntry> currentEntry;
-    nsresult rv = GetEntryAtIndex(mIndex, PR_FALSE, getter_AddRefs(currentEntry));
-    if (NS_SUCCEEDED(rv) && currentEntry)
-      currentEntry->SetSaveContentViewerFlag(PR_FALSE);
-  }
   return LoadEntry(aIndex, nsIDocShellLoadInfo::loadHistory, HIST_CMD_GOTOINDEX);
 }
 
 NS_IMETHODIMP
 nsSHistory::LoadEntry(PRInt32 aIndex, long aLoadType, PRUint32 aHistCmd)
 {
   nsCOMPtr<nsIDocShell> docShell;
   nsCOMPtr<nsISHEntry> shEntry;
@@ -1224,27 +1216,17 @@ nsSHistory::LoadEntry(PRInt32 aIndex, lo
   if (!docShell) {
     // we did not successfully go to the proper index.
     // return error.
     mRequestedIndex = -1;
     return NS_ERROR_FAILURE;
   }
 
   // Start the load on the appropriate docshell
-  nsresult rv = InitiateLoad(nextEntry, docShell, aLoadType);
-
-  if (NS_SUCCEEDED(rv)) {
-    // mark the entry as being able to save its content viewer
-    nextEntry->SetSaveContentViewerFlag(PR_TRUE);
-    // evict any content viewers that are now gHistoryMaxViewers
-    // or more away from the new index
-    EvictWindowContentViewers(mIndex, mRequestedIndex);
-  }
-
-  return rv;
+  return InitiateLoad(nextEntry, docShell, aLoadType);
 }
 
 
 
 nsresult
 nsSHistory::CompareFrames(nsISHEntry * aPrevEntry, nsISHEntry * aNextEntry, nsIDocShell * aParent, long aLoadType, PRBool * aIsFrameFound)
 {
   if (!aPrevEntry || !aNextEntry || !aParent)
--- a/dom/src/base/nsFocusController.cpp
+++ b/dom/src/base/nsFocusController.cpp
@@ -418,17 +418,17 @@ nsFocusController::Blur(nsIDOMEvent* aEv
   return NS_OK;
 }
 
 nsPIDOMWindow *
 nsFocusController::GetWindowFromDocument(nsIDOMDocument* aDocument)
 {
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
   if (!doc)
-    return NS_OK;
+    return nsnull;
 
   return doc->GetWindow();
 }
 
 NS_IMETHODIMP
 nsFocusController::GetControllerForCommand(const char * aCommand,
                                            nsIController** _retval)
 {
new file mode 100644
--- /dev/null
+++ b/dom/src/jsurl/crashtests/457050-1-inner.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<link rel="stylesheet" type="text/css" href="javascript:'p { color: green }'">
+</head>
+<body onload="parent.next(window);">
+<p>Hello</p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/src/jsurl/crashtests/457050-1.html
@@ -0,0 +1,18 @@
+<html class="reftest-wait">
+<head>
+<script type="text/javascript">
+var i = 0;
+function next(w)
+{
+  ++i;
+  if (i == 1)
+    w.location.reload();
+  if (i == 2)
+    document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+<body>
+<iframe src="457050-1-inner.html"></iframe>
+</body>
+</html>
--- a/dom/src/jsurl/crashtests/crashtests.list
+++ b/dom/src/jsurl/crashtests/crashtests.list
@@ -1,3 +1,4 @@
 load 341963-1.html
 load 344874-1.html
 load 344996-1.xhtml
+load 457050-1.html
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -844,27 +844,52 @@ nsJSChannel::GetLoadFlags(nsLoadFlags *a
     *aLoadFlags = mLoadFlags;
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJSChannel::SetLoadFlags(nsLoadFlags aLoadFlags)
 {
+    // Figure out whether the LOAD_BACKGROUND bit in aLoadFlags is
+    // actually right.
+    PRBool bogusLoadBackground = PR_FALSE;
+    if (mIsActive && !(mActualLoadFlags & LOAD_BACKGROUND) &&
+        (aLoadFlags & LOAD_BACKGROUND)) {
+        // We're getting a LOAD_BACKGROUND, but it's probably just our own fake
+        // flag being mirrored to us.  The one exception is if our loadgroup is
+        // LOAD_BACKGROUND.
+        PRBool loadGroupIsBackground = PR_FALSE;
+        nsCOMPtr<nsILoadGroup> loadGroup;
+        mStreamChannel->GetLoadGroup(getter_AddRefs(loadGroup));
+        if (loadGroup) {
+            nsLoadFlags loadGroupFlags;
+            loadGroup->GetLoadFlags(&loadGroupFlags);
+            loadGroupIsBackground = ((loadGroupFlags & LOAD_BACKGROUND) != 0);
+        }
+        bogusLoadBackground = !loadGroupIsBackground;
+    }
+    
     // Since the javascript channel is never the actual channel that
     // any data is loaded through, don't ever set the
     // LOAD_DOCUMENT_URI flag on it, since that could lead to two
     // 'document channels' in the loadgroup if a javascript: URL is
     // loaded while a document is being loaded in the same window.
 
     // XXXbz this, and a whole lot of other hackery, could go away if we'd just
     // cancel the current document load on javascript: load start like IE does.
     
     mLoadFlags = aLoadFlags & ~LOAD_DOCUMENT_URI;
 
+    if (bogusLoadBackground) {
+        aLoadFlags = aLoadFlags & ~LOAD_BACKGROUND;
+    }
+
+    mActualLoadFlags = aLoadFlags;
+
     // ... but the underlying stream channel should get this bit, if
     // set, since that'll be the real document channel if the
     // javascript: URL generated data.
 
     return mStreamChannel->SetLoadFlags(aLoadFlags);
 }
 
 NS_IMETHODIMP
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -428,17 +428,17 @@ protected:
   nsCOMPtr<nsIStyleSheet>  mUAStyleSheet;
 
   nsCOMPtr<nsISelectionListener> mSelectionListener;
   nsCOMPtr<nsIDOMFocusListener> mFocusListener;
 
   nsCOMPtr<nsIContentViewer> mPreviousViewer;
   nsCOMPtr<nsISHEntry> mSHEntry;
 
-  nsIWidget* mParentWidget;          // purposely won't be ref counted
+  nsIWidget* mParentWidget; // purposely won't be ref counted.  May be null
 
   // mTextZoom/mPageZoom record the textzoom/pagezoom of the first (galley)
   // presshell only.
   float mTextZoom;      // Text zoom, defaults to 1.0
   float mPageZoom;
 
   PRInt16 mNumURLStarts;
   PRInt16 mDestroyRefCount;    // a second "refcount" for the document viewer's "destroy"
@@ -953,24 +953,21 @@ DocumentViewerImpl::LoadComplete(nsresul
   }
   
   nsresult rv = NS_OK;
   NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
 
   // First, get the window from the document...
   nsPIDOMWindow *window = mDocument->GetWindow();
 
-  // Fail if no window is available...
-  NS_ENSURE_TRUE(window, NS_ERROR_NULL_POINTER);
-
   mLoaded = PR_TRUE;
 
   // Now, fire either an OnLoad or OnError event to the document...
   PRBool restoring = PR_FALSE;
-  if(NS_SUCCEEDED(aStatus)) {
+  if(NS_SUCCEEDED(aStatus) && window) {
     nsEventStatus status = nsEventStatus_eIgnore;
     nsEvent event(PR_TRUE, NS_LOAD);
     event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
      // XXX Dispatching to |window|, but using |document| as the target.
     event.target = mDocument;
 
     // If the document presentation is being restored, we don't want to fire
     // onload to the document content since that would likely confuse scripts
@@ -1236,42 +1233,38 @@ AttachContainerRecurse(nsIDocShell* aShe
   }
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::Open(nsISupports *aState, nsISHEntry *aSHEntry)
 {
   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
 
-  // Our container might have gone away while we were closed.
-  // If this is the case, we must fail to open so we don't crash.
-  nsCOMPtr<nsISupports> container = do_QueryReferent(mContainer);
-  if (!container)
-    return NS_ERROR_NOT_AVAILABLE;
-
   nsRect bounds;
   mWindow->GetBounds(bounds);
 
   nsresult rv = InitInternal(mParentWidget, aState, bounds, PR_FALSE, PR_FALSE);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (mDocument)
     mDocument->SetContainer(nsCOMPtr<nsISupports>(do_QueryReferent(mContainer)));
 
   if (mPresShell)
     mPresShell->SetForwardingContainer(nsnull);
 
   // Rehook the child presentations.  The child shells are still in
   // session history, so get them from there.
 
-  nsCOMPtr<nsIDocShellTreeItem> item;
-  PRInt32 itemIndex = 0;
-  while (NS_SUCCEEDED(aSHEntry->ChildShellAt(itemIndex++,
-                                             getter_AddRefs(item))) && item) {
-    AttachContainerRecurse(nsCOMPtr<nsIDocShell>(do_QueryInterface(item)));
+  if (aSHEntry) {
+    nsCOMPtr<nsIDocShellTreeItem> item;
+    PRInt32 itemIndex = 0;
+    while (NS_SUCCEEDED(aSHEntry->ChildShellAt(itemIndex++,
+                                               getter_AddRefs(item))) && item) {
+      AttachContainerRecurse(nsCOMPtr<nsIDocShell>(do_QueryInterface(item)));
+    }
   }
   
   SyncParentSubDocMap();
 
   if (mFocusListener && mDocument) {
     mDocument->AddEventListenerByIID(mFocusListener,
                                      NS_GET_IID(nsIDOMFocusListener));
   }
@@ -1854,52 +1847,62 @@ DocumentViewerImpl::Show(void)
       // SHistory and we need the SHistory to evict content viewers
       nsCOMPtr<nsIDocShellTreeItem> root;
       treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
       nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
       nsCOMPtr<nsISHistory> history;
       webNav->GetSessionHistory(getter_AddRefs(history));
       nsCOMPtr<nsISHistoryInternal> historyInt = do_QueryInterface(history);
       if (historyInt) {
+        PRInt32 prevIndex,loadedIndex;
+        nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(treeItem);
+        docShell->GetPreviousTransIndex(&prevIndex);
+        docShell->GetLoadedTransIndex(&loadedIndex);
 #ifdef DEBUG_PAGE_CACHE
-        printf("About to evict content viewers\n");
+        printf("About to evict content viewers: prev=%d, loaded=%d\n",
+               prevIndex, loadedIndex);
 #endif
-        historyInt->EvictContentViewers();
+        historyInt->EvictContentViewers(prevIndex, loadedIndex);
       }
     }
   }
 
   if (mWindow) {
     mWindow->Show(PR_TRUE);
   }
 
   if (mDocument && !mPresShell && !mWindow) {
     nsCOMPtr<nsIBaseWindow> base_win(do_QueryReferent(mContainer));
-    NS_ENSURE_TRUE(base_win, NS_ERROR_UNEXPECTED);
-
-    base_win->GetParentWidget(&mParentWidget);
-    NS_ENSURE_TRUE(mParentWidget, NS_ERROR_UNEXPECTED);
-    mParentWidget->Release(); // GetParentWidget AddRefs, but mParentWidget is weak
+    if (base_win) {
+      base_win->GetParentWidget(&mParentWidget);
+      NS_ENSURE_TRUE(mParentWidget, NS_ERROR_UNEXPECTED);
+      mParentWidget->Release(); // GetParentWidget AddRefs, but mParentWidget is weak
+    }
 
     nsresult rv = CreateDeviceContext(mParentWidget);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Create presentation context
     NS_ASSERTION(!mPresContext, "Shouldn't have a prescontext if we have no shell!");
     mPresContext = new nsPresContext(mDocument, nsPresContext::eContext_Galley);
     NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
 
     rv = mPresContext->Init(mDeviceContext);
     if (NS_FAILED(rv)) {
       mPresContext = nsnull;
       return rv;
     }
 
     nsRect tbounds;
-    mParentWidget->GetBounds(tbounds);
+    if (mParentWidget) {
+      mParentWidget->GetBounds(tbounds);
+    } else {
+      // No good default size; just size to 0 by 0 for lack of anything better.
+      tbounds = nsRect(0, 0, 0, 0);
+    }
 
     rv = MakeWindow(nsSize(mPresContext->DevPixelsToAppUnits(tbounds.width),
                            mPresContext->DevPixelsToAppUnits(tbounds.height)));
     if (NS_FAILED(rv))
       return rv;
 
     if (mPresContext && base_win) {
       nsCOMPtr<nsILinkHandler> linkHandler(do_GetInterface(base_win));
@@ -2093,19 +2096,21 @@ DocumentViewerImpl::CreateStyleSet(nsIDo
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   styleSet->BeginUpdate();
   
   // The document will fill in the document sheets when we create the presshell
   
   // Handle the user sheets.
+  PRInt32 shellType = nsIDocShellTreeItem::typeContent;;
   nsCOMPtr<nsIDocShellTreeItem> docShell(do_QueryReferent(mContainer));
-  PRInt32 shellType;
-  docShell->GetItemType(&shellType);
+  if (docShell) {
+    docShell->GetItemType(&shellType);
+  }
   nsICSSStyleSheet* sheet = nsnull;
   if (shellType == nsIDocShellTreeItem::typeChrome) {
     sheet = nsLayoutStylesheetCache::UserChromeSheet();
   }
   else {
     sheet = nsLayoutStylesheetCache::UserContentSheet();
   }
 
@@ -2114,17 +2119,19 @@ DocumentViewerImpl::CreateStyleSet(nsIDo
 
   // Append chrome sheets (scrollbars + forms).
   PRBool shouldOverride = PR_FALSE;
   nsCOMPtr<nsIDocShell> ds(do_QueryInterface(docShell));
   nsCOMPtr<nsIDOMEventTarget> chromeHandler;
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsICSSStyleSheet> csssheet;
 
-  ds->GetChromeEventHandler(getter_AddRefs(chromeHandler));
+  if (ds) {
+    ds->GetChromeEventHandler(getter_AddRefs(chromeHandler));
+  }
   if (chromeHandler) {
     nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(chromeHandler));
     nsCOMPtr<nsIContent> content(do_QueryInterface(elt));
     if (elt && content) {
       nsCOMPtr<nsIURI> baseURI = content->GetBaseURI();
 
       nsAutoString sheets;
       elt->GetAttribute(NS_LITERAL_STRING("usechromesheets"), sheets);
@@ -2204,17 +2211,18 @@ DocumentViewerImpl::MakeWindow(const nsS
   nsIDeviceContext *dx = mPresContext->DeviceContext();
 
   rv = mViewManager->Init(dx);
   if (NS_FAILED(rv))
     return rv;
 
   // Create a child window of the parent that is our "root view/window"
   // if aParentWidget has a view, we'll hook our view manager up to its view tree
-  nsIView* containerView = nsIView::GetViewFor(mParentWidget);
+  nsIView* containerView =
+    mParentWidget ? nsIView::GetViewFor(mParentWidget) : nsnull;
 
   if (containerView) {
     // see if the containerView has already been hooked into a foreign view manager hierarchy
     // if it has, then we have to hook into the hierarchy too otherwise bad things will happen.
     nsIViewManager* containerVM = containerView->GetViewManager();
     nsIView* pView = containerView;
     do {
       pView = pView->GetParent();
@@ -2246,18 +2254,31 @@ DocumentViewerImpl::MakeWindow(const nsS
   // Create a view
   nsIView* view = mViewManager->CreateView(tbounds, containerView);
   if (!view)
     return NS_ERROR_OUT_OF_MEMORY;
 
   // pass in a native widget to be the parent widget ONLY if the view hierarchy will stand alone.
   // otherwise the view will find its own parent widget and "do the right thing" to
   // establish a parent/child widget relationship
-  rv = view->CreateWidget(kWidgetCID, nsnull,
-                          containerView != nsnull ? nsnull : mParentWidget->GetNativeData(NS_NATIVE_WIDGET),
+  nsWidgetInitData initData;
+  nsWidgetInitData* initDataPtr;
+  if (!mParentWidget) {
+    initDataPtr = &initData;
+    initData.mWindowType = eWindowType_invisible;
+
+    initData.mContentType =
+      nsContentUtils::IsInChromeDocshell(mDocument) ?
+        eContentTypeUI : eContentTypeContent;
+  } else {
+    initDataPtr = nsnull;
+  }
+  rv = view->CreateWidget(kWidgetCID, initDataPtr,
+                          (containerView != nsnull || !mParentWidget) ?
+                            nsnull : mParentWidget->GetNativeData(NS_NATIVE_WIDGET),
                           PR_TRUE, PR_FALSE);
   if (NS_FAILED(rv))
     return rv;
 
   // Setup hierarchical relationship in view manager
   mViewManager->SetRootView(view);
 
   mWindow = view->GetWidget();
@@ -2268,22 +2289,24 @@ DocumentViewerImpl::MakeWindow(const nsS
   // mWindow->SetFocus();
 
   return rv;
 }
 
 nsresult
 DocumentViewerImpl::CreateDeviceContext(nsIWidget* aWidget)
 {
-  NS_PRECONDITION(!mDeviceContext, "How come we're calling this?");
-  if (aWidget) {
-    mDeviceContext = do_CreateInstance(kDeviceContextCID);
-    NS_ENSURE_TRUE(mDeviceContext, NS_ERROR_FAILURE);
-    mDeviceContext->Init(aWidget->GetNativeData(NS_NATIVE_WIDGET));
-  }
+  NS_PRECONDITION(!mPresShell && !mPresContext && !mWindow,
+                  "This will screw up our existing presentation");
+  // Create a device context even if we already have one, since our widget
+  // might have changed.
+  mDeviceContext = do_CreateInstance(kDeviceContextCID);
+  NS_ENSURE_TRUE(mDeviceContext, NS_ERROR_FAILURE);
+  mDeviceContext->Init(aWidget ?
+                       aWidget->GetNativeData(NS_NATIVE_WIDGET) : nsnull);
   return NS_OK;
 }
 
 // Return the selection for the document. Note that text fields have their
 // own selection, which cannot be accessed with this method. Use
 // mPresShell->GetSelectionForCopy() instead.
 nsresult DocumentViewerImpl::GetDocumentSelection(nsISelection **aSelection)
 {
@@ -2757,18 +2780,16 @@ DocumentViewerImpl::GetAuthorStyleDisabl
     *aStyleDisabled = PR_FALSE;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DocumentViewerImpl::GetDefaultCharacterSet(nsACString& aDefaultCharacterSet)
 {
-  NS_ENSURE_STATE(nsCOMPtr<nsISupports>(do_QueryReferent(mContainer)));
-
   if (mDefaultCharacterSet.IsEmpty())
   {
     const nsAdoptingString& defCharset =
       nsContentUtils::GetLocalizedStringPref("intl.charset.default");
 
     if (!defCharset.IsEmpty())
       LossyCopyUTF16toASCII(defCharset, mDefaultCharacterSet);
     else
@@ -3053,19 +3074,20 @@ NS_IMETHODIMP DocumentViewerImpl::GetBid
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP DocumentViewerImpl::SizeToContent()
 {
    NS_ENSURE_TRUE(mDocument, NS_ERROR_NOT_AVAILABLE);
 
+   // Skip doing this on docshell-less documents for now
    nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryReferent(mContainer));
-   NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
-
+   NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_NOT_AVAILABLE);
+   
    nsCOMPtr<nsIDocShellTreeItem> docShellParent;
    docShellAsItem->GetSameTypeParent(getter_AddRefs(docShellParent));
 
    // It's only valid to access this from a top frame.  Doesn't work from
    // sub-frames.
    NS_ENSURE_TRUE(!docShellParent, NS_ERROR_FAILURE);
 
    nsCOMPtr<nsIPresShell> presShell;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -1799,59 +1799,45 @@ PresShell::SetPreferenceStyleRules(PRBoo
   // DocumentViewerImpl::Close()).
 
   if (!window) {
     return NS_ERROR_NULL_POINTER;
   } 
 
   NS_PRECONDITION(mPresContext, "presContext cannot be null");
   if (mPresContext) {
-    nsresult result = NS_OK;
-
     // first, make sure this is not a chrome shell 
-    nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
-    if (container) {
-      nsCOMPtr<nsIDocShellTreeItem> docShell(do_QueryInterface(container, &result));
-      if (NS_SUCCEEDED(result) && docShell){
-        PRInt32 docShellType;
-        result = docShell->GetItemType(&docShellType);
-        if (NS_SUCCEEDED(result)){
-          if (nsIDocShellTreeItem::typeChrome == docShellType){
-            return NS_OK;
-          }
-        }      
-      }
+    if (nsContentUtils::IsInChromeDocshell(mDocument)) {
+      return NS_OK;
+    }
+
+#ifdef DEBUG_attinasi
+    printf("Setting Preference Style Rules:\n");
+#endif
+    // if here, we need to create rules for the prefs
+    // - this includes the background-color, the text-color,
+    //   the link color, the visited link color and the link-underlining
+    
+    // first clear any exising rules
+    nsresult result = ClearPreferenceStyleRules();
+      
+    // now the link rules (must come after the color rules, or links will not be correct color!)
+    // XXX - when there is both an override and agent pref stylesheet this won't matter,
+    //       as the color rules will be overrides and the links rules will be agent
+    if (NS_SUCCEEDED(result)) {
+      result = SetPrefLinkRules();
     }
     if (NS_SUCCEEDED(result)) {
-    
-#ifdef DEBUG_attinasi
-      printf("Setting Preference Style Rules:\n");
-#endif
-      // if here, we need to create rules for the prefs
-      // - this includes the background-color, the text-color,
-      //   the link color, the visited link color and the link-underlining
-    
-      // first clear any exising rules
-      result = ClearPreferenceStyleRules();
-      
-      // now the link rules (must come after the color rules, or links will not be correct color!)
-      // XXX - when there is both an override and agent pref stylesheet this won't matter,
-      //       as the color rules will be overrides and the links rules will be agent
-      if (NS_SUCCEEDED(result)) {
-        result = SetPrefLinkRules();
-      }
-      if (NS_SUCCEEDED(result)) {
-        result = SetPrefFocusRules();
-      }
-      if (NS_SUCCEEDED(result)) {
-        result = SetPrefNoScriptRule();
-      }
-      if (NS_SUCCEEDED(result)) {
-        result = SetPrefNoFramesRule();
-      }
+      result = SetPrefFocusRules();
+    }
+    if (NS_SUCCEEDED(result)) {
+      result = SetPrefNoScriptRule();
+    }
+    if (NS_SUCCEEDED(result)) {
+      result = SetPrefNoFramesRule();
     }
 #ifdef DEBUG_attinasi
     printf( "Preference Style Rules set: error=%ld\n", (long)result);
 #endif
 
     // Note that this method never needs to force any calculation; the caller
     // will recalculate style if needed
 
@@ -5483,17 +5469,21 @@ nsresult PresShell::RetargetEventToParen
   nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
   nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
   if (!container)
     container = do_QueryReferent(mForwardingContainer);
 
   // Now, find the parent pres shell and send the event there
   nsCOMPtr<nsIDocShellTreeItem> treeItem = 
     do_QueryInterface(container);
-  NS_ASSERTION(treeItem, "No tree item for container.");
+  if (!treeItem) {
+    // Might have gone away, or never been around to start with
+    return NS_ERROR_FAILURE;
+  }
+  
   nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
   treeItem->GetParent(getter_AddRefs(parentTreeItem));
   nsCOMPtr<nsIDocShell> parentDocShell = 
     do_QueryInterface(parentTreeItem);
   if (!parentDocShell || treeItem == parentTreeItem) {
     return NS_ERROR_FAILURE;
   }
 
--- a/layout/forms/nsFileControlFrame.cpp
+++ b/layout/forms/nsFileControlFrame.cpp
@@ -301,17 +301,22 @@ nsFileControlFrame::MouseListener::Mouse
   nsXPIDLString title;
   nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
                                      "FileUpload", title);
 
   nsCOMPtr<nsIFilePicker> filePicker = do_CreateInstance("@mozilla.org/filepicker;1");
   if (!filePicker)
     return NS_ERROR_FAILURE;
 
-  result = filePicker->Init(doc->GetWindow(), title, nsIFilePicker::modeOpen);
+  nsPIDOMWindow* win = doc->GetWindow();
+  if (!win) {
+    return NS_ERROR_FAILURE;
+  }
+
+  result = filePicker->Init(win, title, nsIFilePicker::modeOpen);
   if (NS_FAILED(result))
     return result;
 
   // Set filter "All Files"
   filePicker->AppendFilters(nsIFilePicker::filterAll);
 
   // Set default directry and filename
   nsAutoString defaultName;
--- a/layout/style/nsMediaFeatures.cpp
+++ b/layout/style/nsMediaFeatures.cpp
@@ -79,19 +79,23 @@ GetHeight(nsPresContext* aPresContext, n
 
 static nsIDeviceContext*
 GetDeviceContextFor(nsPresContext* aPresContext)
 {
   // Do this dance rather than aPresContext->DeviceContext() to get
   // things right in multi-monitor situations.
   // (It's not clear if this is really needed for GetDepth and GetColor,
   // but do it anyway.)
-  return nsLayoutUtils::GetDeviceContextForScreenInfo(
+  nsIDeviceContext* ctx = nsLayoutUtils::GetDeviceContextForScreenInfo(
     nsCOMPtr<nsIDocShell>(do_QueryInterface(
       nsCOMPtr<nsISupports>(aPresContext->GetContainer()))));
+  if (!ctx) {
+    ctx = aPresContext->DeviceContext();
+  }
+  return ctx;
 }
 
 PR_STATIC_CALLBACK(nsresult)
 GetDeviceWidth(nsPresContext* aPresContext, nsCSSValue& aResult)
 {
     // XXX: I'm not sure if this is really the right thing for print:
     // do we want to include unprintable areas / page margins?
     nsIDeviceContext *dx = GetDeviceContextFor(aPresContext);
--- a/layout/xul/base/src/nsTitleBarFrame.cpp
+++ b/layout/xul/base/src/nsTitleBarFrame.cpp
@@ -173,17 +173,19 @@ nsTitleBarFrame::HandleEvent(nsPresConte
              GetWidget(getter_AddRefs(widget));
            nsRect bounds;
            widget->GetScreenBounds(bounds);
            widget->Move(bounds.x + nsMoveBy.x, bounds.y + nsMoveBy.y);
          }
          else {
            nsIPresShell* presShell = aPresContext->PresShell();
            nsPIDOMWindow *window = presShell->GetDocument()->GetWindow();
-           window->MoveBy(nsMoveBy.x, nsMoveBy.y);
+           if (window) {
+             window->MoveBy(nsMoveBy.x, nsMoveBy.y);
+           }
          }
 
          *aEventStatus = nsEventStatus_eConsumeNoDefault;
 
          doDefault = PR_FALSE;
        }
      }
      break;
--- a/modules/libpr0n/src/imgRequest.cpp
+++ b/modules/libpr0n/src/imgRequest.cpp
@@ -604,16 +604,31 @@ NS_IMETHODIMP imgRequest::OnStartRequest
   LOG_SCOPE(gImgLog, "imgRequest::OnStartRequest");
 
   NS_ASSERTION(!mDecoder, "imgRequest::OnStartRequest -- we already have a decoder");
 
   nsCOMPtr<nsIMultiPartChannel> mpchan(do_QueryInterface(aRequest));
   if (mpchan)
       mIsMultiPartChannel = PR_TRUE;
 
+  /*
+   * If mRequest is null here, then we need to set it so that we'll be able to
+   * cancel it if our Cancel() method is called.  Note that this can only
+   * happen for multipart channels.  We could simply not null out mRequest for
+   * non-last parts, if GetIsLastPart() were reliable, but it's not.  See
+   * https://bugzilla.mozilla.org/show_bug.cgi?id=339610
+   */
+  if (!mRequest) {
+    NS_ASSERTION(mpchan,
+                 "We should have an mRequest here unless we're multipart");
+    nsCOMPtr<nsIChannel> chan;
+    mpchan->GetBaseChannel(getter_AddRefs(chan));
+    mRequest = chan;
+  }
+
   /* set our state variables to their initial values, but advance mState
      to onStartRequest. */
   mImageStatus = imgIRequest::STATUS_NONE;
   mState = onStartRequest;
 
   nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
   if (channel)
     channel->GetSecurityInfo(getter_AddRefs(mSecurityInfo));
--- a/netwerk/base/src/nsBaseChannel.cpp
+++ b/netwerk/base/src/nsBaseChannel.cpp
@@ -57,17 +57,17 @@ CopyProperties(const nsAString &key, nsI
   return PL_DHASH_NEXT;
 }
 
 // This class is used to suspend a request across a function scope.
 class ScopedRequestSuspender {
 public:
   ScopedRequestSuspender(nsIRequest *request)
     : mRequest(request) {
-    if (NS_FAILED(mRequest->Suspend())) {
+    if (mRequest && NS_FAILED(mRequest->Suspend())) {
       NS_WARNING("Couldn't suspend pump");
       mRequest = nsnull;
     }
   }
   ~ScopedRequestSuspender() {
     if (mRequest)
       mRequest->Resume();
   }
@@ -89,17 +89,18 @@ nsBaseChannel::nsBaseChannel()
   , mQueriedProgressSink(PR_TRUE)
   , mSynthProgressEvents(PR_FALSE)
   , mWasOpened(PR_FALSE)
 {
   mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
 }
 
 nsresult
-nsBaseChannel::Redirect(nsIChannel *newChannel, PRUint32 redirectFlags)
+nsBaseChannel::Redirect(nsIChannel *newChannel, PRUint32 redirectFlags,
+                        PRBool openNewChannel)
 {
   SUSPEND_PUMP_FOR_SCOPE();
 
   // Transfer properties
 
   newChannel->SetOriginalURI(OriginalURI());
   newChannel->SetLoadGroup(mLoadGroup);
   newChannel->SetNotificationCallbacks(mCallbacks);
@@ -142,19 +143,21 @@ nsBaseChannel::Redirect(nsIChannel *newC
     if (NS_FAILED(rv))
       return rv;
   }
 
   // If we fail to open the new channel, then we want to leave this channel
   // unaffected, so we defer tearing down our channel until we have succeeded
   // with the redirect.
 
-  rv = newChannel->AsyncOpen(mListener, mListenerContext);
-  if (NS_FAILED(rv))
-    return rv;
+  if (openNewChannel) {
+    rv = newChannel->AsyncOpen(mListener, mListenerContext);
+    if (NS_FAILED(rv))
+      return rv;
+  }
 
   // close down this channel
   Cancel(NS_BINDING_REDIRECTED);
   mListener = nsnull;
   mListenerContext = nsnull;
 
   return NS_OK;
 }
@@ -211,34 +214,64 @@ nsBaseChannel::PushStreamConverter(const
   }
   return rv;
 }
 
 nsresult
 nsBaseChannel::BeginPumpingData()
 {
   nsCOMPtr<nsIInputStream> stream;
-  nsresult rv = OpenContentStream(PR_TRUE, getter_AddRefs(stream));
+  nsCOMPtr<nsIChannel> channel;
+  nsresult rv = OpenContentStream(PR_TRUE, getter_AddRefs(stream),
+                                  getter_AddRefs(channel));
   if (NS_FAILED(rv))
     return rv;
 
+  NS_ASSERTION(!stream || !channel, "Got both a channel and a stream?");
+
+  if (channel)
+      return NS_DispatchToCurrentThread(new RedirectRunnable(this, channel));
+
   // By assigning mPump, we flag this channel as pending (see IsPending).  It's
   // important that the pending flag is set when we call into the stream (the
   // call to AsyncRead results in the stream's AsyncWait method being called)
   // and especially when we call into the loadgroup.  Our caller takes care to
   // release mPump if we return an error.
  
   rv = nsInputStreamPump::Create(getter_AddRefs(mPump), stream, -1, -1, 0, 0,
                                  PR_TRUE);
   if (NS_SUCCEEDED(rv))
     rv = mPump->AsyncRead(this, nsnull);
 
   return rv;
 }
 
+void
+nsBaseChannel::HandleAsyncRedirect(nsIChannel* newChannel)
+{
+  NS_ASSERTION(!mPump, "Shouldn't have gotten here");
+  nsresult rv = Redirect(newChannel, nsIChannelEventSink::REDIRECT_INTERNAL,
+                         PR_TRUE);
+  if (NS_FAILED(rv)) {
+    // Notify our consumer ourselves
+    Cancel(rv);
+    mListener->OnStartRequest(this, mListenerContext);
+    mListener->OnStopRequest(this, mListenerContext, mStatus);
+    mListener = nsnull;
+    mListenerContext = nsnull;
+  }
+
+  if (mLoadGroup)
+    mLoadGroup->RemoveRequest(this, nsnull, mStatus);
+
+  // Drop notification callbacks to prevent cycles.
+  mCallbacks = nsnull;
+  CallbacksChanged();
+}
+
 //-----------------------------------------------------------------------------
 // nsBaseChannel::nsISupports
 
 NS_IMPL_ISUPPORTS_INHERITED6(nsBaseChannel,
                              nsHashPropertyBag,
                              nsIRequest,
                              nsIChannel,
                              nsIInterfaceRequestor,
@@ -446,18 +479,25 @@ nsBaseChannel::SetContentLength(PRInt32 
 
 NS_IMETHODIMP
 nsBaseChannel::Open(nsIInputStream **result)
 {
   NS_ENSURE_TRUE(mURI, NS_ERROR_NOT_INITIALIZED);
   NS_ENSURE_TRUE(!mPump, NS_ERROR_IN_PROGRESS);
   NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_IN_PROGRESS);
 
-  nsresult rv = OpenContentStream(PR_FALSE, result);
-  if (rv == NS_ERROR_NOT_IMPLEMENTED)
+  nsCOMPtr<nsIChannel> chan;
+  nsresult rv = OpenContentStream(PR_FALSE, result, getter_AddRefs(chan));
+  NS_ASSERTION(!chan || !*result, "Got both a channel and a stream?");
+  if (NS_SUCCEEDED(rv) && chan) {
+      rv = Redirect(chan, nsIChannelEventSink::REDIRECT_INTERNAL, PR_FALSE);
+      if (NS_FAILED(rv))
+          return rv;
+      rv = chan->Open(result);
+  } else if (rv == NS_ERROR_NOT_IMPLEMENTED)
     return NS_ImplementChannelOpen(this, result);
 
   mWasOpened = NS_SUCCEEDED(rv);
 
   return rv;
 }
 
 NS_IMETHODIMP
--- a/netwerk/base/src/nsBaseChannel.h
+++ b/netwerk/base/src/nsBaseChannel.h
@@ -47,16 +47,17 @@
 #include "nsIChannel.h"
 #include "nsIInputStream.h"
 #include "nsIURI.h"
 #include "nsILoadGroup.h"
 #include "nsIStreamListener.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIProgressEventSink.h"
 #include "nsITransport.h"
+#include "nsThreadUtils.h"
 
 //-----------------------------------------------------------------------------
 // nsBaseChannel is designed to be subclassed.  The subclass is responsible for
 // implementing the OpenContentStream method, which will be called by the
 // nsIChannel::AsyncOpen and nsIChannel::Open implementations.
 //
 // nsBaseChannel implements nsIInterfaceRequestor to provide a convenient way
 // for subclasses to query both the nsIChannel::notificationCallbacks and
@@ -98,17 +99,22 @@ private:
   // instance.  This means that if it is a non-blocking stream that supports
   // nsIAsyncInputStream that it will be read entirely on the main application
   // thread, and its AsyncWait method will be called whenever ReadSegments
   // returns NS_BASE_STREAM_WOULD_BLOCK.  Otherwise, if the stream is blocking,
   // then it will be read on one of the background I/O threads, and it does not
   // need to implement ReadSegments.  If async is false, this method may return
   // NS_ERROR_NOT_IMPLEMENTED to cause the basechannel to implement Open in
   // terms of AsyncOpen (see NS_ImplementChannelOpen).
-  virtual nsresult OpenContentStream(PRBool async, nsIInputStream **stream) = 0;
+  // A callee is allowed to return an nsIChannel instead of an nsIInputStream.
+  // That case will be treated as a redirect to the new channel.  By default
+  // *channel will be set to null by the caller, so callees who don't want to
+  // return one an just not touch it.
+  virtual nsresult OpenContentStream(PRBool async, nsIInputStream **stream,
+                                     nsIChannel** channel) = 0;
 
   // The basechannel calls this method from its OnTransportStatus method to
   // determine whether to call nsIProgressEventSink::OnStatus in addition to
   // nsIProgressEventSink::OnProgress.  This method may be overriden by the
   // subclass to enable nsIProgressEventSink::OnStatus events.  If this method
   // returns true, then the statusArg out param specifies the "statusArg" value
   // to pass to the OnStatus method.  By default, OnStatus messages are
   // suppressed.  The status parameter passed to this method is the status value
@@ -121,22 +127,24 @@ private:
   virtual void OnCallbacksChanged() {
   }
 
 public:
   // ----------------------------------------------
   // Methods provided for use by the derived class:
 
   // Redirect to another channel.  This method takes care of notifying
-  // observers of this redirect as well as of opening the new channel.  It also
-  // cancels |this| with the status code NS_BINDING_REDIRECTED.  A failure
-  // return from this method means that the redirect could not be performed (no
-  // channel was opened; this channel wasn't canceled.)  The redirectFlags
-  // parameter consists of the flag values defined on nsIChannelEventSink.
-  nsresult Redirect(nsIChannel *newChannel, PRUint32 redirectFlags);
+  // observers of this redirect as well as of opening the new channel, if asked
+  // to do so.  It also cancels |this| with the status code
+  // NS_BINDING_REDIRECTED.  A failure return from this method means that the
+  // redirect could not be performed (no channel was opened; this channel
+  // wasn't canceled.)  The redirectFlags parameter consists of the flag values
+  // defined on nsIChannelEventSink.
+  nsresult Redirect(nsIChannel *newChannel, PRUint32 redirectFlags,
+                    PRBool openNewChannel);
 
   // Tests whether a type hint was set. Subclasses can use this to decide
   // whether to call SetContentType.
   // NOTE: This is only reliable if the subclass didn't itself call
   // SetContentType, and should also not be called after OpenContentStream.
   PRBool HasContentTypeHint() const;
 
   // The URI member should be initialized before the channel is used, and then
@@ -228,16 +236,41 @@ private:
 
   // Called when the callbacks available to this channel may have changed.
   void CallbacksChanged() {
     mProgressSink = nsnull;
     mQueriedProgressSink = PR_FALSE;
     OnCallbacksChanged();
   }
 
+  // Handle an async redirect callback.  This will only be called if we
+  // returned success from AsyncOpen while posting a redirect runnable.
+  void HandleAsyncRedirect(nsIChannel* newChannel);
+
+  class RedirectRunnable : public nsRunnable
+  {
+  public:
+    RedirectRunnable(nsBaseChannel* chan, nsIChannel* newChannel)
+      : mChannel(chan), mNewChannel(newChannel)
+    {
+      NS_PRECONDITION(newChannel, "Must have channel to redirect to");
+    }
+    
+    NS_IMETHOD Run()
+    {
+      mChannel->HandleAsyncRedirect(mNewChannel);
+      return NS_OK;
+    }
+
+  private:
+    nsRefPtr<nsBaseChannel> mChannel;
+    nsCOMPtr<nsIChannel> mNewChannel;
+  };
+  friend class RedirectRunnable;
+
   nsRefPtr<nsInputStreamPump>         mPump;
   nsCOMPtr<nsIInterfaceRequestor>     mCallbacks;
   nsCOMPtr<nsIProgressEventSink>      mProgressSink;
   nsCOMPtr<nsIURI>                    mOriginalURI;
   nsCOMPtr<nsIURI>                    mURI;
   nsCOMPtr<nsILoadGroup>              mLoadGroup;
   nsCOMPtr<nsISupports>               mOwner;
   nsCOMPtr<nsISupports>               mSecurityInfo;
--- a/netwerk/base/src/nsInputStreamChannel.cpp
+++ b/netwerk/base/src/nsInputStreamChannel.cpp
@@ -36,17 +36,18 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsInputStreamChannel.h"
 
 //-----------------------------------------------------------------------------
 // nsInputStreamChannel
 
 nsresult
-nsInputStreamChannel::OpenContentStream(PRBool async, nsIInputStream **result)
+nsInputStreamChannel::OpenContentStream(PRBool async, nsIInputStream **result,
+                                        nsIChannel** channel)
 {
   NS_ENSURE_TRUE(mContentStream, NS_ERROR_NOT_INITIALIZED);
 
   // If content length is unknown, then we must guess.  In this case, we assume
   // the stream can tell us.  If the stream is a pipe, then this will not work.
 
   PRInt64 len = ContentLength64();
   if (len < 0) {
--- a/netwerk/base/src/nsInputStreamChannel.h
+++ b/netwerk/base/src/nsInputStreamChannel.h
@@ -50,15 +50,16 @@ public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIINPUTSTREAMCHANNEL
 
     nsInputStreamChannel() {}
 
 protected:
     virtual ~nsInputStreamChannel() {}
 
-    virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result);
+    virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result,
+                                       nsIChannel** channel);
 
 private:
     nsCOMPtr<nsIInputStream> mContentStream;
 };
 
 #endif // !nsInputStreamChannel_h__
--- a/netwerk/protocol/data/src/nsDataChannel.cpp
+++ b/netwerk/protocol/data/src/nsDataChannel.cpp
@@ -46,17 +46,18 @@
 #include "nsReadableUtils.h"
 #include "nsNetSegmentUtils.h"
 #include "nsEscape.h"
 #include "plbase64.h"
 #include "plstr.h"
 #include "prmem.h"
 
 nsresult
-nsDataChannel::OpenContentStream(PRBool async, nsIInputStream **result)
+nsDataChannel::OpenContentStream(PRBool async, nsIInputStream **result,
+                                 nsIChannel** channel)
 {
     NS_ENSURE_TRUE(URI(), NS_ERROR_NOT_INITIALIZED);
 
     nsresult rv;
 
     nsCAutoString spec;
     rv = URI()->GetAsciiSpec(spec);
     if (NS_FAILED(rv)) return rv;
--- a/netwerk/protocol/data/src/nsDataChannel.h
+++ b/netwerk/protocol/data/src/nsDataChannel.h
@@ -47,12 +47,13 @@
 
 class nsDataChannel : public nsBaseChannel {
 public:
     nsDataChannel(nsIURI *uri) {
         SetURI(uri);
     }
 
 protected:
-    virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result);
+    virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result,
+                                       nsIChannel** channel);
 };
 
 #endif /* nsDataChannel_h___ */
--- a/netwerk/protocol/file/src/nsFileChannel.cpp
+++ b/netwerk/protocol/file/src/nsFileChannel.cpp
@@ -302,25 +302,44 @@ nsFileChannel::MakeFileInputStream(nsIFi
         mime->GetTypeFromFile(file, contentType);
       }
     }
   }
   return rv;
 }
 
 nsresult
-nsFileChannel::OpenContentStream(PRBool async, nsIInputStream **result)
+nsFileChannel::OpenContentStream(PRBool async, nsIInputStream **result,
+                                 nsIChannel** channel)
 {
   // NOTE: the resulting file is a clone, so it is safe to pass it to the
   //       file input stream which will be read on a background thread.
   nsCOMPtr<nsIFile> file;
   nsresult rv = GetFile(getter_AddRefs(file));
   if (NS_FAILED(rv))
     return rv;
 
+  nsCOMPtr<nsIFileProtocolHandler> fileHandler;
+  rv = NS_GetFileProtocolHandler(getter_AddRefs(fileHandler));
+  if (NS_FAILED(rv))
+    return rv;
+    
+  nsCOMPtr<nsIURI> newURI;
+  rv = fileHandler->ReadURLFile(file, getter_AddRefs(newURI));
+  if (NS_SUCCEEDED(rv)) {
+    nsCOMPtr<nsIChannel> newChannel;
+    rv = NS_NewChannel(getter_AddRefs(newChannel), newURI);
+    if (NS_FAILED(rv))
+      return rv;
+
+    *result = nsnull;
+    newChannel.forget(channel);
+    return NS_OK;
+  }
+
   nsCOMPtr<nsIInputStream> stream;
 
   if (mUploadStream) {
     // Pass back a nsFileUploadContentStream instance that knows how to perform
     // the file copy when "read" (the resulting stream in this case does not
     // actually return any data).
 
     nsCOMPtr<nsIOutputStream> fileStream;
--- a/netwerk/protocol/file/src/nsFileChannel.h
+++ b/netwerk/protocol/file/src/nsFileChannel.h
@@ -60,16 +60,17 @@ public:
 protected:
   // Called to construct a blocking file input stream for the given file.  This
   // method also returns a best guess at the content-type for the data stream.
   // NOTE: If the channel has a type hint set, contentType will be left
   // untouched. The caller should not use it in that case.
   nsresult MakeFileInputStream(nsIFile *file, nsCOMPtr<nsIInputStream> &stream,
                                nsCString &contentType);
 
-  virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result);
+  virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result,
+                                     nsIChannel** channel);
 
 private:
   nsCOMPtr<nsIInputStream> mUploadStream;
   PRInt64 mUploadLength;
 };
 
 #endif // !nsFileChannel_h__
--- a/netwerk/protocol/file/src/nsFileProtocolHandler.cpp
+++ b/netwerk/protocol/file/src/nsFileProtocolHandler.cpp
@@ -273,40 +273,22 @@ nsFileProtocolHandler::NewURI(const nsAC
     if (NS_FAILED(rv)) return rv;
 
     return CallQueryInterface(url, result);
 }
 
 NS_IMETHODIMP
 nsFileProtocolHandler::NewChannel(nsIURI *uri, nsIChannel **result)
 {
-    nsresult rv;
-
-    // This file may be a url file
-    nsCOMPtr<nsIFileURL> url(do_QueryInterface(uri));
-    if (url) {
-        nsCOMPtr<nsIFile> file;
-        rv = url->GetFile(getter_AddRefs(file));
-        if (NS_SUCCEEDED(rv)) {
-            nsCOMPtr<nsIURI> uri;
-            rv = ReadURLFile(file, getter_AddRefs(uri));
-            if (NS_SUCCEEDED(rv)) {
-                rv = NS_NewChannel(result, uri);
-                if (NS_SUCCEEDED(rv))
-                    return rv;
-            }
-        }
-    }
-
     nsFileChannel *chan = new nsFileChannel(uri);
     if (!chan)
         return NS_ERROR_OUT_OF_MEMORY;
     NS_ADDREF(chan);
 
-    rv = chan->Init();
+    nsresult rv = chan->Init();
     if (NS_FAILED(rv)) {
         NS_RELEASE(chan);
         return rv;
     }
 
     *result = chan;
     return NS_OK;
 }
--- a/netwerk/protocol/ftp/src/nsFTPChannel.cpp
+++ b/netwerk/protocol/ftp/src/nsFTPChannel.cpp
@@ -138,17 +138,18 @@ nsFtpChannel::GetProxyInfo(nsIProxyInfo*
     *aProxyInfo = ProxyInfo();
     NS_IF_ADDREF(*aProxyInfo);
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
 
 nsresult
-nsFtpChannel::OpenContentStream(PRBool async, nsIInputStream **result)
+nsFtpChannel::OpenContentStream(PRBool async, nsIInputStream **result,
+                                nsIChannel** channel)
 {
     if (!async)
         return NS_ERROR_NOT_IMPLEMENTED;
 
     nsFtpState *state = new nsFtpState();
     if (!state)
         return NS_ERROR_OUT_OF_MEMORY;
     NS_ADDREF(state);
--- a/netwerk/protocol/ftp/src/nsFTPChannel.h
+++ b/netwerk/protocol/ftp/src/nsFTPChannel.h
@@ -112,17 +112,18 @@ public:
         return mUploadStream;
     }
 
     // Helper function for getting the nsIFTPEventSink.
     void GetFTPEventSink(nsCOMPtr<nsIFTPEventSink> &aResult);
 
 protected:
     virtual ~nsFtpChannel() {}
-    virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result);
+    virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result,
+                                       nsIChannel** channel);
     virtual PRBool GetStatusArg(nsresult status, nsString &statusArg);
     virtual void OnCallbacksChanged();
 
 private:
     nsCOMPtr<nsIProxyInfo>    mProxyInfo; 
     nsCOMPtr<nsIFTPEventSink> mFTPEventSink;
     nsCOMPtr<nsIInputStream>  mUploadStream;
     PRUint64                  mStartPos;
--- a/netwerk/protocol/gopher/src/nsGopherChannel.cpp
+++ b/netwerk/protocol/gopher/src/nsGopherChannel.cpp
@@ -464,17 +464,18 @@ NS_IMETHODIMP
 nsGopherChannel::GetProxyInfo(nsIProxyInfo** aProxyInfo)
 {
     *aProxyInfo = ProxyInfo();
     NS_IF_ADDREF(*aProxyInfo);
     return NS_OK;
 }
 
 nsresult
-nsGopherChannel::OpenContentStream(PRBool async, nsIInputStream **result)
+nsGopherChannel::OpenContentStream(PRBool async, nsIInputStream **result,
+                                   nsIChannel** channel)
 {
     // Implement nsIChannel::Open in terms of nsIChannel::AsyncOpen
     if (!async)
         return NS_ERROR_NOT_IMPLEMENTED;
 
     nsRefPtr<nsIInputStream> stream = new nsGopherContentStream(this);
     if (!stream)
         return NS_ERROR_OUT_OF_MEMORY;
--- a/netwerk/protocol/gopher/src/nsGopherChannel.h
+++ b/netwerk/protocol/gopher/src/nsGopherChannel.h
@@ -54,17 +54,18 @@ public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIPROXIEDCHANNEL
 
     nsIProxyInfo *ProxyInfo() { return mProxyInfo; }
 
 protected:
     virtual ~nsGopherChannel() {}
 
-    virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result);
+    virtual nsresult OpenContentStream(PRBool async, nsIInputStream **result,
+                                       nsIChannel** channel);
     virtual PRBool GetStatusArg(nsresult status, nsString &statusArg);
 
 private:
     nsresult SendRequest(nsIOutputStream *stream);
 
     nsCOMPtr<nsIProxyInfo> mProxyInfo;
 };
 
--- a/uriloader/exthandler/nsExternalHelperAppService.cpp
+++ b/uriloader/exthandler/nsExternalHelperAppService.cpp
@@ -1149,16 +1149,22 @@ NS_IMETHODIMP nsExternalAppHandler::GetT
 }
 
 NS_IMETHODIMP nsExternalAppHandler::GetTimeDownloadStarted(PRTime* aTime)
 {
   *aTime = mTimeDownloadStarted;
   return NS_OK;
 }
 
+NS_IMETHODIMP nsExternalAppHandler::GetContentLength(PRInt64 *aContentLength)
+{
+  *aContentLength = mContentLength;
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsExternalAppHandler::CloseProgressWindow()
 {
   // release extra state...
   mWebProgressListener = nsnull;
   return NS_OK;
 }
 
 void nsExternalAppHandler::RetargetLoadNotifications(nsIRequest *request)
--- a/uriloader/exthandler/nsIExternalHelperAppService.idl
+++ b/uriloader/exthandler/nsIExternalHelperAppService.idl
@@ -161,9 +161,14 @@ interface nsIHelperAppLauncher : nsICanc
    * The executable-ness of the target file
    */
   readonly attribute boolean targetFileIsExecutable;
 
   /**
    * Time when the download started
    */
   readonly attribute PRTime timeDownloadStarted;
+
+  /**
+   * The download content length, or -1 if the length is not available.
+   */
+  readonly attribute PRInt64 contentLength;
 };