Bug 462076 - Dynamically inserted iframes on refresh sometimes trade places, r=bz
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Tue, 17 Aug 2010 17:13:55 +0300
changeset 50721 353da09ea0dd
parent 50720 f43f9b764efb
child 50722 6cd8f4fe844e
push id15137
push useropettay@mozilla.com
push date2010-08-17 14:22 +0000
treeherdermozilla-central@353da09ea0dd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs462076
milestone2.0b4pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 462076 - Dynamically inserted iframes on refresh sometimes trade places, r=bz
content/base/public/nsIDocument.h
content/base/src/nsDocument.cpp
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameLoader.h
content/base/src/nsObjectLoadingContent.cpp
content/base/src/nsObjectLoadingContent.h
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsGenericHTMLElement.h
content/html/content/src/nsHTMLFrameElement.cpp
content/html/content/src/nsHTMLIFrameElement.cpp
content/html/content/src/nsHTMLObjectElement.cpp
content/xul/content/src/nsXULElement.cpp
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
docshell/base/nsIDocShellHistory.idl
docshell/shistory/public/nsISHContainer.idl
docshell/shistory/public/nsISHEntry.idl
docshell/shistory/public/nsISHistoryInternal.idl
docshell/shistory/src/nsSHEntry.cpp
docshell/shistory/src/nsSHEntry.h
docshell/shistory/src/nsSHistory.cpp
docshell/shistory/src/nsSHistory.h
docshell/test/navigation/Makefile.in
docshell/test/navigation/file_bug462076_1.html
docshell/test/navigation/file_bug462076_2.html
docshell/test/navigation/file_bug462076_3.html
docshell/test/navigation/file_bug508537_1.html
docshell/test/navigation/file_document_write_1.html
docshell/test/navigation/file_static_and_dynamic_1.html
docshell/test/navigation/frame0.html
docshell/test/navigation/frame1.html
docshell/test/navigation/frame2.html
docshell/test/navigation/frame3.html
docshell/test/navigation/goback.html
docshell/test/navigation/test_sessionhistory.html
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -113,18 +113,18 @@ class Loader;
 namespace dom {
 class Link;
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 
 #define NS_IDOCUMENT_IID      \
-{ 0xb2274bc3, 0x4a1c, 0x4e64, \
-  { 0x8d, 0xe4, 0x3b, 0xc6, 0x50, 0x28, 0x84, 0x38 } }
+{ 0xba594543, 0x95f6, 0x4160, \
+  { 0x90, 0x32, 0x82, 0x87, 0x16, 0x5d, 0x59, 0x7a } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE              (1 << 0)
@@ -1420,16 +1420,18 @@ public:
   virtual Element* LookupImageElement(const nsAString& aElementId) = 0;
 
   void ScheduleBeforePaintEvent();
   void BeforePaintEventFiring()
   {
     mHavePendingPaint = PR_FALSE;
   }
 
+  // This returns true when the document tree is being teared down.
+  PRBool InUnlinkOrDeletion() { return mInUnlinkOrDeletion; }
 protected:
   ~nsIDocument()
   {
     // XXX The cleanup of mNodeInfoManager (calling DropDocumentReference and
     //     releasing it) happens in the nsDocument destructor. We'd prefer to
     //     do it here but nsNodeInfoManager is a concrete class that we don't
     //     want to expose to users of the nsIDocument API outside of Gecko.
   }
@@ -1556,16 +1558,19 @@ protected:
   PRPackedBool mAllowDNSPrefetch;
   
   // True when this document is a static clone of a normal document
   PRPackedBool mIsStaticDocument;
 
   // True while this document is being cloned to a static document.
   PRPackedBool mCreatingStaticClone;
 
+  // True iff the document is being unlinked or deleted.
+  PRPackedBool mInUnlinkOrDeletion;
+
   // True if document has ever had script handling object.
   PRPackedBool mHasHadScriptHandlingObject;
 
   // True if we're waiting for a before-paint event.
   PRPackedBool mHavePendingPaint;
 
   // The document's script global object, the object from which the
   // document can get its script context and scope. This is the
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1484,16 +1484,17 @@ nsDocument::~nsDocument()
            ("DOCUMENT %p destroyed", this));
 #endif
 
 #ifdef DEBUG
   nsCycleCollector_DEBUG_wasFreed(static_cast<nsIDocument*>(this));
 #endif
 
   mInDestructor = PR_TRUE;
+  mInUnlinkOrDeletion = PR_TRUE;
 
   // Clear mObservers to keep it in sync with the mutationobserver list
   mObservers.Clear();
 
   if (mStyleSheetSetList) {
     mStyleSheetSetList->Disconnect();
   }
 
@@ -1763,16 +1764,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
+  tmp->mInUnlinkOrDeletion = PR_TRUE;
+
   // Clear out our external resources
   tmp->mExternalResourceMap.Shutdown();
 
   nsAutoScriptBlocker scriptBlocker;
 
   // Unlink the mChildren nsAttrAndChildArray.
   for (PRInt32 indx = PRInt32(tmp->mChildren.ChildCount()) - 1; 
        indx >= 0; --indx) {
@@ -1802,16 +1805,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
  }
 
   // nsDocument has a pretty complex destructor, so we're going to
   // assume that *most* cycles you actually want to break somewhere
   // else, and not unlink an awful lot here.
   //
   // In rare cases where you think an unlink will help here, add one
   // manually.
+
+  tmp->mInUnlinkOrDeletion = PR_FALSE;
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 nsresult
 nsDocument::Init()
 {
   if (mCSSLoader || mNodeInfoManager || mScriptLoader) {
     return NS_ERROR_ALREADY_INITIALIZED;
@@ -1959,32 +1964,35 @@ nsDocument::ResetToURI(nsIURI *aURI, nsI
 
     mSubDocuments = nsnull;
   }
 
   // Destroy link map now so we don't waste time removing
   // links one by one
   DestroyElementMaps();
 
+  PRBool oldVal = mInUnlinkOrDeletion;
+  mInUnlinkOrDeletion = PR_TRUE;
   PRUint32 count = mChildren.ChildCount();
   { // Scope for update
     MOZ_AUTO_DOC_UPDATE(this, UPDATE_CONTENT_MODEL, PR_TRUE);    
     for (PRInt32 i = PRInt32(count) - 1; i >= 0; i--) {
       nsCOMPtr<nsIContent> content = mChildren.ChildAt(i);
 
       nsIContent* previousSibling = content->GetPreviousSibling();
 
       if (nsINode::GetFirstChild() == content) {
         mFirstChild = content->GetNextSibling();
       }
       mChildren.RemoveChildAt(i);
       nsNodeUtils::ContentRemoved(this, content, i, previousSibling);
       content->UnbindFromTree();
     }
   }
+  mInUnlinkOrDeletion = oldVal;
   mCachedRootElement = nsnull;
 
   // Reset our stylesheets
   ResetStylesheetsToURI(aURI);
   
   // Release the listener manager
   if (mListenerManager) {
     mListenerManager->Disconnect();
@@ -6943,20 +6951,23 @@ nsDocument::Destroy()
   // to drop any references to the document so that it can be destroyed.
   if (mIsGoingAway)
     return;
 
   mIsGoingAway = PR_TRUE;
 
   RemovedFromDocShell();
 
+  PRBool oldVal = mInUnlinkOrDeletion;
+  mInUnlinkOrDeletion = PR_TRUE;
   PRUint32 i, count = mChildren.ChildCount();
   for (i = 0; i < count; ++i) {
     mChildren.ChildAt(i)->DestroyContent();
   }
+  mInUnlinkOrDeletion = oldVal;
 
   mLayoutHistoryState = nsnull;
 
   // Shut down our external resource map.  We might not need this for
   // leak-fixing if we fix DocumentViewerImpl to do cycle-collection, but
   // tearing down all those frame trees right now is the right thing to do.
   mExternalResourceMap.Shutdown();
 
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -85,16 +85,17 @@
 #include "nsIDOMEventTarget.h"
 #include "nsIFrame.h"
 #include "nsIFrameFrame.h"
 #include "nsDOMError.h"
 #include "nsGUIEvent.h"
 #include "nsEventDispatcher.h"
 #include "nsISHistory.h"
 #include "nsISHistoryInternal.h"
+#include "nsIDocShellHistory.h"
 #include "nsIDOMNSHTMLDocument.h"
 #include "nsIXULWindow.h"
 
 #include "nsLayoutUtils.h"
 #include "nsIView.h"
 #include "nsPLDOMEvent.h"
 
 #include "nsIURI.h"
@@ -185,26 +186,26 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameL
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsIFrameLoader)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 nsFrameLoader*
-nsFrameLoader::Create(nsIContent* aOwner)
+nsFrameLoader::Create(nsIContent* aOwner, PRBool aNetworkCreated)
 {
   NS_ENSURE_TRUE(aOwner, nsnull);
   nsIDocument* doc = aOwner->GetOwnerDoc();
   NS_ENSURE_TRUE(doc && !doc->GetDisplayDocument() &&
                  ((!doc->IsLoadedAsData() && aOwner->GetCurrentDoc()) ||
                    doc->IsStaticDocument()),
                  nsnull);
 
-  return new nsFrameLoader(aOwner);
+  return new nsFrameLoader(aOwner, aNetworkCreated);
 }
 
 NS_IMETHODIMP
 nsFrameLoader::LoadFrame()
 {
   NS_ENSURE_TRUE(mOwnerContent, NS_ERROR_NOT_INITIALIZED);
 
   nsAutoString src;
@@ -1191,27 +1192,37 @@ nsFrameLoader::Destroy()
   if (mMessageManager) {
     mMessageManager->Disconnect();
   }
   if (mChildMessageManager) {
     static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->Disconnect();
   }
 
   nsCOMPtr<nsIDocument> doc;
+  PRBool dynamicSubframeRemoval = PR_FALSE;
   if (mOwnerContent) {
     doc = mOwnerContent->GetOwnerDoc();
 
     if (doc) {
+      dynamicSubframeRemoval = !mIsTopLevelContent && !doc->InUnlinkOrDeletion();
       doc->SetSubDocumentFor(mOwnerContent, nsnull);
     }
 
     mOwnerContent = nsnull;
   }
   DestroyChild();
-  
+
+  // Seems like this is a dynamic frame removal.
+  if (dynamicSubframeRemoval) {
+    nsCOMPtr<nsIDocShellHistory> dhistory = do_QueryInterface(mDocShell);
+    if (dhistory) {
+      dhistory->RemoveFromSessionHistory();
+    }
+  }
+
   // Let the tree owner know we're gone.
   if (mIsTopLevelContent) {
     nsCOMPtr<nsIDocShellTreeItem> ourItem = do_QueryInterface(mDocShell);
     if (ourItem) {
       nsCOMPtr<nsIDocShellTreeItem> parentItem;
       ourItem->GetParent(getter_AddRefs(parentItem));
       nsCOMPtr<nsIDocShellTreeOwner> owner = do_GetInterface(parentItem);
       if (owner) {
@@ -1324,16 +1335,23 @@ nsFrameLoader::MaybeCreateDocShell()
   nsCOMPtr<nsISupports> container =
     doc->GetContainer();
   nsCOMPtr<nsIWebNavigation> parentAsWebNav = do_QueryInterface(container);
 
   // Create the docshell...
   mDocShell = do_CreateInstance("@mozilla.org/docshell;1");
   NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
 
+  if (!mNetworkCreated) {
+    nsCOMPtr<nsIDocShellHistory> history = do_QueryInterface(mDocShell);
+    if (history) {
+      history->SetCreatedDynamically(PR_TRUE);
+    }
+  }
+
   // Get the frame name and tell the docshell about it.
   nsCOMPtr<nsIDocShellTreeItem> docShellAsItem(do_QueryInterface(mDocShell));
   NS_ENSURE_TRUE(docShellAsItem, NS_ERROR_FAILURE);
   nsAutoString frameName;
 
   PRInt32 namespaceID = mOwnerContent->GetNameSpaceID();
   if (namespaceID == kNameSpaceID_XHTML && !mOwnerContent->IsInHTMLDocument()) {
     mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName);
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -79,25 +79,26 @@ class nsFrameLoader : public nsIFrameLoa
 {
   friend class AutoResetInShow;
 #ifdef MOZ_IPC
   typedef mozilla::dom::PBrowserParent PBrowserParent;
   typedef mozilla::dom::TabParent TabParent;
 #endif
 
 protected:
-  nsFrameLoader(nsIContent *aOwner) :
+  nsFrameLoader(nsIContent *aOwner, PRBool aNetworkCreated) :
     mOwnerContent(aOwner),
     mDepthTooGreat(PR_FALSE),
     mIsTopLevelContent(PR_FALSE),
     mDestroyCalled(PR_FALSE),
     mNeedsAsyncDestroy(PR_FALSE),
     mInSwap(PR_FALSE),
     mInShow(PR_FALSE),
-    mHideCalled(PR_FALSE)
+    mHideCalled(PR_FALSE),
+    mNetworkCreated(aNetworkCreated)
 #ifdef MOZ_IPC
     , mDelayRemoteDialogs(PR_FALSE)
     , mRemoteWidgetCreated(PR_FALSE)
     , mRemoteFrame(false)
     , mRemoteBrowser(nsnull)
 #if defined(MOZ_WIDGET_GTK2) || defined(MOZ_WIDGET_QT)
     , mRemoteSocket(nsnull)
 #endif
@@ -108,17 +109,17 @@ public:
   ~nsFrameLoader() {
     mNeedsAsyncDestroy = PR_TRUE;
     if (mMessageManager) {
       mMessageManager->Disconnect();
     }
     nsFrameLoader::Destroy();
   }
 
-  static nsFrameLoader* Create(nsIContent* aOwner);
+  static nsFrameLoader* Create(nsIContent* aOwner, PRBool aNetworkCreated);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_CLASS(nsFrameLoader)
   NS_DECL_NSIFRAMELOADER
   NS_HIDDEN_(nsresult) CheckForRecursiveLoad(nsIURI* aURI);
   nsresult ReallyStartLoading();
   void Finalize();
   nsIDocShell* GetExistingDocShell() { return mDocShell; }
@@ -216,16 +217,20 @@ public:
 private:
   PRPackedBool mDepthTooGreat : 1;
   PRPackedBool mIsTopLevelContent : 1;
   PRPackedBool mDestroyCalled : 1;
   PRPackedBool mNeedsAsyncDestroy : 1;
   PRPackedBool mInSwap : 1;
   PRPackedBool mInShow : 1;
   PRPackedBool mHideCalled : 1;
+  // True when the object is created for an element which the parser has
+  // created using NS_FROM_PARSER_NETWORK flag. If the element is modified,
+  // it may lose the flag.
+  PRPackedBool mNetworkCreated : 1;
 
 #ifdef MOZ_IPC
   PRPackedBool mDelayRemoteDialogs : 1;
   PRPackedBool mRemoteWidgetCreated : 1;
   bool mRemoteFrame;
   // XXX leaking
   nsCOMPtr<nsIObserver> mChildHost;
   TabParent* mRemoteBrowser;
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -473,16 +473,17 @@ IsPluginEnabledByExtension(nsIURI* uri, 
 
 nsObjectLoadingContent::nsObjectLoadingContent()
   : mPendingInstantiateEvent(nsnull)
   , mChannel(nsnull)
   , mType(eType_Loading)
   , mInstantiating(PR_FALSE)
   , mUserDisabled(PR_FALSE)
   , mSuppressed(PR_FALSE)
+  , mNetworkCreated(PR_TRUE)
   , mFallbackReason(ePluginOtherState)
 {
 }
 
 nsObjectLoadingContent::~nsObjectLoadingContent()
 {
   DestroyImageLoadingContent();
   if (mFrameLoader) {
@@ -627,17 +628,17 @@ nsObjectLoadingContent::OnStartRequest(n
       // final listener.
       if (!mFinalListener) {
         mType = newType;
         return NS_BINDING_ABORTED;
       }
       break;
     case eType_Document: {
       if (!mFrameLoader) {
-        mFrameLoader = nsFrameLoader::Create(thisContent);
+        mFrameLoader = nsFrameLoader::Create(thisContent, mNetworkCreated);
         if (!mFrameLoader) {
           Fallback(PR_FALSE);
           return NS_ERROR_UNEXPECTED;
         }
       }
 
       rv = mFrameLoader->CheckForRecursiveLoad(uri);
       if (NS_FAILED(rv)) {
@@ -1281,17 +1282,17 @@ nsObjectLoadingContent::LoadObject(nsIUR
     if (newType != mType) {
       LOG(("OBJLC [%p]: (eOverrideServerType) Changing type from %u to %u\n", this, mType, newType));
 
       UnloadContent();
 
       // Must have a frameloader before creating a frame, or the frame will
       // create its own.
       if (!mFrameLoader && newType == eType_Document) {
-        mFrameLoader = nsFrameLoader::Create(thisContent);
+        mFrameLoader = nsFrameLoader::Create(thisContent, mNetworkCreated);
         if (!mFrameLoader) {
           mURI = nsnull;
           return NS_OK;
         }
       }
 
       // Must notify here for plugins
       // If aNotify is false, we'll just wait until we get a frame and use the
@@ -1984,17 +1985,17 @@ nsObjectLoadingContent::CreateStaticClon
       const_cast<nsObjectLoadingContent*>(this)->GetExistingFrame(eDontFlush);
     nsIFrame* f = do_QueryFrame(frame);
     aDest->mPrintFrame = f;
   }
 
   if (mFrameLoader) {
     nsCOMPtr<nsIContent> content =
       do_QueryInterface(static_cast<nsIImageLoadingContent*>((aDest)));
-    nsFrameLoader* fl = nsFrameLoader::Create(content);
+    nsFrameLoader* fl = nsFrameLoader::Create(content, PR_FALSE);
     if (fl) {
       aDest->mFrameLoader = fl;
       mFrameLoader->CreateStaticClone(fl);
     }
   }
 }
 
 NS_IMETHODIMP
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -129,16 +129,20 @@ class nsObjectLoadingContent : public ns
 
     /**
      * Object state. This is a bitmask consisting of a subset of
      * NS_EVENT_STATE_BROKEN, NS_EVENT_STATE_USERDISABLED and
      * NS_EVENT_STATE_SUPPRESSED representing the current state of the object.
      */
     PRInt32 ObjectState() const;
 
+    void SetIsNetworkCreated(PRBool aNetworkCreated)
+    {
+      mNetworkCreated = aNetworkCreated;
+    }
   protected:
     /**
      * Load the object from the given URI.
      * @param aURI       The URI to load.
      * @param aNotify If true, nsIDocumentObserver state change notifications
      *                will be sent as needed.
      * @param aTypeHint  MIME Type hint. Overridden by the server unless this
      *                   class has the eOverrideServerType capability.
@@ -394,16 +398,22 @@ class nsObjectLoadingContent : public ns
     /**
      * Whether we are about to call instantiate on our frame. If we aren't,
      * SetFrame needs to asynchronously call Instantiate.
      */
     PRPackedBool                mInstantiating : 1;
     // Blocking status from content policy
     PRPackedBool                mUserDisabled  : 1;
     PRPackedBool                mSuppressed    : 1;
+
+    // True when the object is created for an element which the parser has
+    // created using NS_FROM_PARSER_NETWORK flag. If the element is modified,
+    // it may lose the flag.
+    PRPackedBool                mNetworkCreated : 1;
+
     // A specific state that caused us to fallback
     PluginSupportState          mFallbackReason;
 
     nsWeakFrame                 mPrintFrame;
 
     friend class nsAsyncInstantiateEvent;
 };
 
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -2868,17 +2868,17 @@ nsGenericHTMLFrameElement::GetContentWin
 nsresult
 nsGenericHTMLFrameElement::EnsureFrameLoader()
 {
   if (!GetParent() || !IsInDoc() || mFrameLoader) {
     // If frame loader is there, we just keep it around, cached
     return NS_OK;
   }
 
-  mFrameLoader = nsFrameLoader::Create(this);
+  mFrameLoader = nsFrameLoader::Create(this, mNetworkCreated);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGenericHTMLFrameElement::GetFrameLoader(nsIFrameLoader **aFrameLoader)
 {
   NS_IF_ADDREF(*aFrameLoader = mFrameLoader);
   return NS_OK;
@@ -2931,17 +2931,20 @@ nsGenericHTMLFrameElement::BindToTree(ns
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aDocument) {
     NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
                  "Missing a script blocker!");
     // We're in a document now.  Kick off the frame load.
     LoadSrc();
   }
-  
+
+  // We're now in document and scripts may move us, so clear
+  // the mNetworkCreated flag.
+  mNetworkCreated = PR_FALSE;
   return rv;
 }
 
 void
 nsGenericHTMLFrameElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
 {
   if (mFrameLoader) {
     // This iframe is being taken out of the document, destroy the
@@ -2992,17 +2995,17 @@ nsGenericHTMLFrameElement::CopyInnerTo(n
 {
   nsresult rv = nsGenericHTMLElement::CopyInnerTo(aDest);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsIDocument* doc = aDest->GetOwnerDoc();
   if (doc->IsStaticDocument() && mFrameLoader) {
     nsGenericHTMLFrameElement* dest =
       static_cast<nsGenericHTMLFrameElement*>(aDest);
-    nsFrameLoader* fl = nsFrameLoader::Create(dest);
+    nsFrameLoader* fl = nsFrameLoader::Create(dest, PR_FALSE);
     NS_ENSURE_STATE(fl);
     dest->mFrameLoader = fl;
     static_cast<nsFrameLoader*>(mFrameLoader.get())->CreateStaticClone(fl);
   }
 
   return rv;
 }
 
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -40,16 +40,17 @@
 
 #include "nsMappedAttributeElement.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsINameSpaceManager.h"  // for kNameSpaceID_None
 #include "nsIFormControl.h"
 #include "nsIDOMNSHTMLFrameElement.h"
 #include "nsFrameLoader.h"
 #include "nsGkAtoms.h"
+#include "nsContentCreatorFunctions.h"
 
 class nsIDOMAttr;
 class nsIDOMEventListener;
 class nsIDOMNodeList;
 class nsIFrame;
 class nsIStyleRule;
 class nsChildContentList;
 class nsDOMCSSDeclaration;
@@ -904,19 +905,21 @@ PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_B
  * A helper class for frame elements
  */
 
 class nsGenericHTMLFrameElement : public nsGenericHTMLElement,
                                   public nsIDOMNSHTMLFrameElement,
                                   public nsIFrameLoaderOwner
 {
 public:
-  nsGenericHTMLFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo)
+  nsGenericHTMLFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo,
+                            PRUint32 aFromParser)
     : nsGenericHTMLElement(aNodeInfo)
   {
+    mNetworkCreated = aFromParser == NS_FROM_PARSER_NETWORK;
   }
   virtual ~nsGenericHTMLFrameElement();
 
   // nsISupports
   NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
 
   // nsIDOMNSHTMLFrameElement
   NS_DECL_NSIDOMNSHTMLFRAMEELEMENT
@@ -953,16 +956,20 @@ public:
 protected:
   // This doesn't really ensure a frame loade in all cases, only when
   // it makes sense.
   nsresult EnsureFrameLoader();
   nsresult LoadSrc();
   nsresult GetContentDocument(nsIDOMDocument** aContentDocument);
 
   nsRefPtr<nsFrameLoader> mFrameLoader;
+  // True when the element is created by the parser
+  // using NS_FROM_PARSER_NETWORK flag.
+  // If the element is modified, it may lose the flag.
+  PRPackedBool            mNetworkCreated;
 };
 
 //----------------------------------------------------------------------
 
 /**
  * A macro to implement the NS_NewHTMLXXXElement() functions.
  */
 #define NS_IMPL_NS_NEW_HTML_ELEMENT(_elementName)                            \
--- a/content/html/content/src/nsHTMLFrameElement.cpp
+++ b/content/html/content/src/nsHTMLFrameElement.cpp
@@ -41,17 +41,18 @@
 #include "nsIDOMDocument.h"
 #include "nsDOMError.h"
 
 
 class nsHTMLFrameElement : public nsGenericHTMLFrameElement,
                            public nsIDOMHTMLFrameElement
 {
 public:
-  nsHTMLFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo);
+  nsHTMLFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo,
+                     PRUint32 aFromParser = NS_NOT_FROM_PARSER);
   virtual ~nsHTMLFrameElement();
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIDOMNode
   NS_FORWARD_NSIDOMNODE(nsGenericHTMLFrameElement::)
 
@@ -71,21 +72,22 @@ public:
                                 nsAttrValue& aResult);
   NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const;
   nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
   virtual nsXPCClassInfo* GetClassInfo();
 };
 
 
-NS_IMPL_NS_NEW_HTML_ELEMENT(Frame)
+NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Frame)
 
 
-nsHTMLFrameElement::nsHTMLFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo)
-  : nsGenericHTMLFrameElement(aNodeInfo)
+nsHTMLFrameElement::nsHTMLFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo,
+                                       PRUint32 aFromParser)
+  : nsGenericHTMLFrameElement(aNodeInfo, aFromParser)
 {
 }
 
 nsHTMLFrameElement::~nsHTMLFrameElement()
 {
 }
 
 
--- a/content/html/content/src/nsHTMLIFrameElement.cpp
+++ b/content/html/content/src/nsHTMLIFrameElement.cpp
@@ -50,17 +50,18 @@
 
 class nsHTMLIFrameElement : public nsGenericHTMLFrameElement,
                             public nsIDOMHTMLIFrameElement
 #ifdef MOZ_SVG
                             , public nsIDOMGetSVGDocument
 #endif
 {
 public:
-  nsHTMLIFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo);
+  nsHTMLIFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo,
+                      PRUint32 aFromParser = NS_NOT_FROM_PARSER);
   virtual ~nsHTMLIFrameElement();
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIDOMNode
   NS_FORWARD_NSIDOMNODE(nsGenericHTMLFrameElement::)
 
@@ -86,21 +87,22 @@ public:
   NS_IMETHOD_(PRBool) IsAttributeMapped(const nsIAtom* aAttribute) const;
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const;
 
   virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
   virtual nsXPCClassInfo* GetClassInfo();
 };
 
 
-NS_IMPL_NS_NEW_HTML_ELEMENT(IFrame)
+NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(IFrame)
 
 
-nsHTMLIFrameElement::nsHTMLIFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo)
-  : nsGenericHTMLFrameElement(aNodeInfo)
+nsHTMLIFrameElement::nsHTMLIFrameElement(already_AddRefed<nsINodeInfo> aNodeInfo,
+                                         PRUint32 aFromParser)
+  : nsGenericHTMLFrameElement(aNodeInfo, aFromParser)
 {
 }
 
 nsHTMLIFrameElement::~nsHTMLIFrameElement()
 {
 }
 
 
--- a/content/html/content/src/nsHTMLObjectElement.cpp
+++ b/content/html/content/src/nsHTMLObjectElement.cpp
@@ -146,16 +146,17 @@ NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER
 
 
 nsHTMLObjectElement::nsHTMLObjectElement(already_AddRefed<nsINodeInfo> aNodeInfo,
                                          PRUint32 aFromParser)
   : nsGenericHTMLFormElement(aNodeInfo),
     mIsDoneAddingChildren(!aFromParser)
 {
   RegisterFreezableElement();
+  SetIsNetworkCreated(aFromParser == NS_FROM_PARSER_NETWORK);
 }
 
 nsHTMLObjectElement::~nsHTMLObjectElement()
 {
   UnregisterFreezableElement();
   DestroyImageLoadingContent();
 }
 
@@ -459,16 +460,17 @@ nsHTMLObjectElement::StartObjectLoad(PRB
     LoadObject(uri, aNotify, ctype);
   }
   else {
     // Be sure to call the nsIURI version if we have no attribute
     // That handles the case where no URI is specified. An empty string would
     // get interpreted as the page itself, instead of absence of URI.
     LoadObject(nsnull, aNotify, ctype);
   }
+  SetIsNetworkCreated(PR_FALSE);
 }
 
 PRInt32
 nsHTMLObjectElement::IntrinsicState() const
 {
   return nsGenericHTMLFormElement::IntrinsicState() | ObjectState();
 }
 
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -1973,17 +1973,21 @@ nsXULElement::LoadSrc()
         !GetOwnerDoc()->GetRootElement() ||
         GetOwnerDoc()->GetRootElement()->
             NodeInfo()->Equals(nsGkAtoms::overlay, kNameSpaceID_XUL)) {
         return NS_OK;
     }
     nsXULSlots* slots = static_cast<nsXULSlots*>(GetSlots());
     NS_ENSURE_TRUE(slots, NS_ERROR_OUT_OF_MEMORY);
     if (!slots->mFrameLoader) {
-        slots->mFrameLoader = nsFrameLoader::Create(this);
+        // PR_FALSE as the last parameter so that xul:iframe/browser/editor
+        // session history handling works like dynamic html:iframes.
+        // Usually xul elements are used in chrome, which doesn't have
+        // session history at all.
+        slots->mFrameLoader = nsFrameLoader::Create(this, PR_FALSE);
         NS_ENSURE_TRUE(slots->mFrameLoader, NS_OK);
     }
 
     return slots->mFrameLoader->LoadFrame();
 }
 
 nsresult
 nsXULElement::GetFrameLoader(nsIFrameLoader **aFrameLoader)
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -671,16 +671,18 @@ DispatchPings(nsIContent *content, nsIUR
 }
 
 static nsISHEntry* GetRootSHEntry(nsISHEntry *entry);
 
 //*****************************************************************************
 //***    nsDocShell: Object Management
 //*****************************************************************************
 
+static PRUint64 gDocshellIDCounter = 0;
+
 // Note: operator new zeros our memory
 nsDocShell::nsDocShell():
     nsDocLoader(),
     mDefaultScrollbarPref(Scrollbar_Auto, Scrollbar_Auto),
     mTreeOwner(nsnull),
     mChromeEventHandler(nsnull),
     mCharsetReloadState(eCharsetReloadInit),
     mChildOffset(0),
@@ -710,16 +712,17 @@ nsDocShell::nsDocShell():
     mIsBeingDestroyed(PR_FALSE),
     mIsExecutingOnLoadHandler(PR_FALSE),
     mIsPrintingOrPP(PR_FALSE),
     mSavingOldViewer(PR_FALSE)
 #ifdef DEBUG
     , mInEnsureScriptEnv(PR_FALSE)
 #endif
 {
+    mHistoryID = ++gDocshellIDCounter;
     if (gDocShellCount++ == 0) {
         NS_ASSERTION(sURIFixup == nsnull,
                      "Huh, sURIFixup not null in first nsDocShell ctor!");
 
         CallGetService(NS_URIFIXUP_CONTRACTID, &sURIFixup);
     }
 
 #ifdef PR_LOGGING
@@ -1220,17 +1223,29 @@ nsDocShell::LoadURI(nsIURI * aURI,
              */
             
             // Get the parent's load type
             parentDS->GetLoadType(&parentLoadType);            
 
             nsCOMPtr<nsIDocShellHistory> parent(do_QueryInterface(parentAsItem));
             if (parent) {
                 // Get the ShEntry for the child from the parent
-                parent->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
+                nsCOMPtr<nsISHEntry> currentSH;
+                PRBool oshe = PR_FALSE;
+                parent->GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
+                PRBool dynamicallyAddedChild = mDynamicallyCreated;
+                if (!dynamicallyAddedChild && !oshe && currentSH) {
+                    currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
+                }
+                if (!dynamicallyAddedChild) {
+                    // Only use the old SHEntry, if we're sure enough that
+                    // it wasn't originally for some other frame.
+                    parent->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
+                }
+
                 // Make some decisions on the child frame's loadType based on the 
                 // parent's loadType. 
                 if (mCurrentURI == nsnull) {
                     // This is a newly created frame. Check for exception cases first. 
                     // By default the subframe will inherit the parent's loadType.
                     if (shEntry && (parentLoadType == LOAD_NORMAL ||
                                     parentLoadType == LOAD_LINK   ||
                                     parentLoadType == LOAD_NORMAL_EXTERNAL)) {
@@ -2952,16 +2967,23 @@ nsDocShell::SetTreeOwner(nsIDocShellTree
 NS_IMETHODIMP
 nsDocShell::SetChildOffset(PRUint32 aChildOffset)
 {
     mChildOffset = aChildOffset;
     return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShell::GetHistoryID(PRUint64* aID)
+{
+  *aID = mHistoryID;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShell::GetIsInUnload(PRBool* aIsInUnload)
 {
     *aIsInUnload = mFiredUnloadEvent;
     return NS_OK;
 }
 
 //*****************************************************************************
 // nsDocShell::nsIDocShellTreeNode
@@ -3004,40 +3026,29 @@ nsDocShell::AddChild(nsIDocShellTreeItem
     // from us.
     aChild->SetTreeOwner(nsnull);
     
     nsresult res = AddChildLoader(childAsDocLoader);
     NS_ENSURE_SUCCESS(res, res);
     NS_ASSERTION(mChildList.Count() > 0,
                  "child list must not be empty after a successful add");
 
-    // Set the child's index in the parent's children list 
-    // XXX What if the parent had different types of children?
-    // XXX in that case docshell hierarchy and SH hierarchy won't match.
-    {
-        nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
-        if (childDocShell) {
-            // If there are frameloaders in the finalization list, reduce
-            // the offset so that the SH hierarchy is more likely to match the
-            // docshell hierarchy
-            nsCOMPtr<nsIDocument> doc = do_GetInterface(GetAsSupports(this));
-            PRUint32 offset = mChildList.Count() - 1;
-            if (doc) {
-               PRUint32 oldChildCount = offset; // Current child count - 1
-               for (PRUint32 i = 0; i < oldChildCount; ++i) {
-                 nsCOMPtr<nsIDocShell> child = do_QueryInterface(ChildAt(i));
-                 if (doc->FrameLoaderScheduledToBeFinalized(child)) {
-                   --offset;
-                 }
-               }
-            }
-
-            childDocShell->SetChildOffset(offset);
-        }
-    }
+    nsCOMPtr<nsIDocShellHistory> docshellhistory = do_QueryInterface(aChild);
+    PRBool dynamic = PR_FALSE;
+    docshellhistory->GetCreatedDynamically(&dynamic);
+    if (!dynamic) {
+        nsCOMPtr<nsISHEntry> currentSH;
+        PRBool oshe = PR_FALSE;
+        GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
+        if (currentSH) {
+            currentSH->HasDynamicallyAddedChild(&dynamic);
+        }
+    }
+    nsCOMPtr<nsIDocShell> childDocShell = do_QueryInterface(aChild);
+    childDocShell->SetChildOffset(dynamic ? -1 : mChildList.Count() - 1);
 
     /* Set the child's global history if the parent has one */
     if (mGlobalHistory) {
         nsCOMPtr<nsIDocShellHistory>
             dsHistoryChild(do_QueryInterface(aChild));
         if (dsHistoryChild)
             dsHistoryChild->SetUseGlobalHistory(PR_TRUE);
     }
@@ -3392,16 +3403,99 @@ nsDocShell::SetUseGlobalHistory(PRBool a
 
 NS_IMETHODIMP
 nsDocShell::GetUseGlobalHistory(PRBool *aUseGlobalHistory)
 {
     *aUseGlobalHistory = (mGlobalHistory != nsnull);
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDocShell::RemoveFromSessionHistory()
+{
+    nsCOMPtr<nsISHistoryInternal> internalHistory;
+    nsCOMPtr<nsISHistory> sessionHistory;
+    nsCOMPtr<nsIDocShellTreeItem> root;
+    GetSameTypeRootTreeItem(getter_AddRefs(root));
+    if (root) {
+        nsCOMPtr<nsIWebNavigation> rootAsWebnav =
+            do_QueryInterface(root);
+        if (rootAsWebnav) {
+            rootAsWebnav->GetSessionHistory(getter_AddRefs(sessionHistory));
+            internalHistory = do_QueryInterface(sessionHistory);
+        }
+    }
+    if (!internalHistory) {
+        return NS_OK;
+    }
+
+    PRInt32 index = 0;
+    sessionHistory->GetIndex(&index);
+    nsAutoTArray<PRUint64, 16> ids;
+    ids.AppendElement(mHistoryID);
+    internalHistory->RemoveEntries(ids, index);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetCreatedDynamically(PRBool aDynamic)
+{
+    mDynamicallyCreated = aDynamic;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCreatedDynamically(PRBool* aDynamic)
+{
+    *aDynamic = mDynamicallyCreated;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::GetCurrentSHEntry(nsISHEntry** aEntry, PRBool* aOSHE)
+{
+    *aOSHE = PR_FALSE;
+    *aEntry = nsnull;
+    if (mLSHE) {
+        NS_ADDREF(*aEntry = mLSHE);
+    } else if (mOSHE) {
+        NS_ADDREF(*aEntry = mOSHE);
+        *aOSHE = PR_TRUE;
+    }
+    return NS_OK;
+}
+
+void
+nsDocShell::ClearFrameHistory(nsISHEntry* aEntry)
+{
+  nsCOMPtr<nsISHContainer> shcontainer = do_QueryInterface(aEntry);
+  nsCOMPtr<nsISHistory> rootSH;
+  GetRootSessionHistory(getter_AddRefs(rootSH));
+  nsCOMPtr<nsISHistoryInternal> history = do_QueryInterface(rootSH);
+  if (!history || !shcontainer) {
+    return;
+  }
+
+  PRInt32 count = 0;
+  shcontainer->GetChildCount(&count);
+  nsAutoTArray<PRUint64, 16> ids;
+  for (PRInt32 i = 0; i < count; ++i) {
+    nsCOMPtr<nsISHEntry> child;
+    shcontainer->GetChildAt(i, getter_AddRefs(child));
+    if (child) {
+      PRUint64 id = 0;
+      child->GetDocshellID(&id);
+      ids.AppendElement(id);
+    }
+  }
+  PRInt32 index = 0;
+  rootSH->GetIndex(&index);
+  history->RemoveEntries(ids, index);
+}
+
 //-------------------------------------
 //-- Helper Method for Print discovery
 //-------------------------------------
 PRBool 
 nsDocShell::IsPrintingOrPP(PRBool aDisplayErrorDialog)
 {
   if (mIsPrintingOrPP && aDisplayErrorDialog) {
     DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nsnull, nsnull);
@@ -5628,16 +5722,41 @@ nsDocShell::OnStateChange(nsIWebProgress
         
             PRBool equalUri = PR_TRUE;
             // Store the wyciwyg url in session history, only if it is
             // being loaded fresh for the first time. We don't want 
             // multiple entries for successive loads
             if (mCurrentURI &&
                 NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) &&
                 !equalUri) {
+
+                nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
+                GetSameTypeParent(getter_AddRefs(parentAsItem));
+                nsCOMPtr<nsIDocShell> parentDS(do_QueryInterface(parentAsItem));
+                PRBool inOnLoadHandler = PR_FALSE;
+                if (parentDS) {
+                  parentDS->GetIsExecutingOnLoadHandler(&inOnLoadHandler);
+                }
+                if (inOnLoadHandler) {
+                    // We're handling parent's load event listener, which causes
+                    // document.write in a subdocument.
+                    // Need to clear the session history for all child
+                    // docshells so that we can handle them like they would
+                    // all be added dynamically.
+                    nsCOMPtr<nsIDocShellHistory> parent =
+                        do_QueryInterface(parentAsItem);
+                    if (parent) {
+                        PRBool oshe = PR_FALSE;
+                        nsCOMPtr<nsISHEntry> entry;
+                        parent->GetCurrentSHEntry(getter_AddRefs(entry), &oshe);
+                        static_cast<nsDocShell*>(parent.get())->
+                            ClearFrameHistory(entry);
+                    }
+                }
+
                 // This is a document.write(). Get the made-up url
                 // from the channel and store it in session history.
                 rv = AddToSessionHistory(uri, wcwgChannel, nsnull,
                                          getter_AddRefs(mLSHE));
                 SetCurrentURI(uri, aRequest, PR_TRUE);
                 // Save history state of the previous page
                 rv = PersistLayoutHistoryState();
                 // We'll never get an Embed() for this load, so just go ahead
@@ -8197,16 +8316,20 @@ nsDocShell::InternalLoad(nsIURI * aURI,
     // mLSHE for the real page.
     if (mLoadType != LOAD_ERROR_PAGE)
         SetHistoryEntry(&mLSHE, aSHEntry);
 
     mSavingOldViewer = savePresentation;
 
     // If we have a saved content viewer in history, restore and show it now.
     if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
+        // Make sure our history ID points to the same ID as
+        // SHEntry's docshell ID.
+        aSHEntry->GetDocshellID(&mHistoryID);
+
         // It's possible that the previous viewer of mContentViewer is the
         // viewer that will end up in aSHEntry when it gets closed.  If that's
         // the case, we need to go ahead and force it into its shentry so we
         // can restore it.
         if (mContentViewer) {
             nsCOMPtr<nsIContentViewer> prevViewer;
             mContentViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
             if (prevViewer) {
@@ -9105,16 +9228,38 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIC
             cacheChannel->GetCacheKey(getter_AddRefs(cacheKey));
         // If we already have a loading history entry, store the new cache key
         // in it.  Otherwise, since we're doing a reload and won't be updating
         // our history entry, store the cache key in our current history entry.
         if (mLSHE)
             mLSHE->SetCacheKey(cacheKey);
         else if (mOSHE)
             mOSHE->SetCacheKey(cacheKey);
+
+        // Since we're force-reloading, clear all the sub frame history.
+        ClearFrameHistory(mLSHE);
+        ClearFrameHistory(mOSHE);
+    }
+
+    if (aLoadType == LOAD_RELOAD_NORMAL) {
+        nsCOMPtr<nsISHEntry> currentSH;
+        PRBool oshe = PR_FALSE;
+        GetCurrentSHEntry(getter_AddRefs(currentSH), &oshe);
+        PRBool dynamicallyAddedChild = PR_FALSE;
+        if (currentSH) {
+          currentSH->HasDynamicallyAddedChild(&dynamicallyAddedChild);
+        }
+        if (dynamicallyAddedChild) {
+          ClearFrameHistory(currentSH);
+        }
+    }
+
+    if (aLoadType == LOAD_REFRESH) {
+        ClearFrameHistory(mLSHE);
+        ClearFrameHistory(mOSHE);
     }
 
     if (updateHistory && shAvailable) { 
         // Update session history if necessary...
         if (!mLSHE && (mItemType == typeContent) && mURIResultedInDocument) {
             /* This is  a fresh page getting loaded for the first time
              *.Create a Entry for it and add it to SH, if this is the
              * rootDocShell
@@ -9594,17 +9739,19 @@ nsDocShell::AddToSessionHistory(nsIURI *
 
     //Title is set in nsDocShell::SetTitle()
     entry->Create(aURI,              // uri
                   EmptyString(),     // Title
                   inputStream,       // Post data stream
                   nsnull,            // LayoutHistory state
                   cacheKey,          // CacheKey
                   mContentTypeHint,  // Content-type
-                  owner);            // Channel or provided owner
+                  owner,             // Channel or provided owner
+                  mHistoryID,
+                  mDynamicallyCreated);
     entry->SetReferrerURI(referrerURI);
     /* If cache got a 'no-store', ask SH not to store
      * HistoryLayoutState. By default, SH will set this
      * flag to PR_TRUE and save HistoryLayoutState.
      */    
     if (discardLayoutState) {
         entry->SetSaveLayoutStateFlag(PR_FALSE);
     }
@@ -9794,16 +9941,18 @@ nsDocShell::WalkHistoryEntries(nsISHEntr
     PRInt32 childCount;
     container->GetChildCount(&childCount);
     for (PRInt32 i = 0; i < childCount; i++) {
         nsCOMPtr<nsISHEntry> childEntry;
         container->GetChildAt(i, getter_AddRefs(childEntry));
         if (!childEntry) {
             // childEntry can be null for valid reasons, for example if the
             // docshell at index i never loaded anything useful.
+            // Remember to clone also nulls in the child array (bug 464064).
+            aCallback(nsnull, nsnull, i, aData);
             continue;
         }
 
         nsDocShell *childShell = nsnull;
         if (aRootShell) {
             // Walk the children of aRootShell and see if one of them
             // has srcChild as a SHEntry.
 
@@ -9846,16 +9995,25 @@ nsDocShell::CloneAndReplaceChild(nsISHEn
 {
     nsresult result = NS_OK;
     nsCOMPtr<nsISHEntry> dest;
 
     CloneAndReplaceData *data = static_cast<CloneAndReplaceData*>(aData);
     PRUint32 cloneID = data->cloneID;
     nsISHEntry *replaceEntry = data->replaceEntry;
 
+    nsCOMPtr<nsISHContainer> container =
+      do_QueryInterface(data->destTreeParent);
+    if (!aEntry) {
+      if (container) {
+        container->AddChild(nsnull, aEntryIndex);
+      }
+      return NS_OK;
+    }
+    
     PRUint32 srcID;
     aEntry->GetID(&srcID);
 
     if (srcID == cloneID) {
         // Just replace the entry, and don't walk the children.
         dest = replaceEntry;
         dest->SetIsSubFrame(PR_TRUE);
     } else {
@@ -9873,18 +10031,16 @@ nsDocShell::CloneAndReplaceChild(nsISHEn
                                     CloneAndReplaceChild, &childData);
         if (NS_FAILED(result))
             return result;
 
         if (aShell)
             aShell->SwapHistoryEntries(aEntry, dest);
     }
 
-    nsCOMPtr<nsISHContainer> container =
-        do_QueryInterface(data->destTreeParent);
     if (container)
         container->AddChild(dest, aEntryIndex);
 
     data->resultEntry = dest;
     return result;
 }
 
 /* static */ nsresult
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -658,16 +658,18 @@ protected:
     nsresult DoCommand(const char * inCommand);
     nsresult EnsureCommandHandler();
 
     nsIChannel* GetCurrentDocChannel();
 protected:
     // Override the parent setter from nsDocLoader
     virtual nsresult SetDocLoaderParent(nsDocLoader * aLoader);
 
+    void ClearFrameHistory(nsISHEntry* aEntry);
+
     // Event type dispatched by RestorePresentation
     class RestorePresentationEvent : public nsRunnable {
     public:
         NS_DECL_NSIRUNNABLE
         RestorePresentationEvent(nsDocShell *ds) : mDocShell(ds) {}
         void Revoke() { mDocShell = nsnull; }
     private:
         nsRefPtr<nsDocShell> mDocShell;
@@ -744,25 +746,17 @@ protected:
     // For that reasons don't use nsCOMPtr.
 
     nsIDocShellTreeOwner *     mTreeOwner; // Weak Reference
     nsPIDOMEventTarget *       mChromeEventHandler; //Weak Reference
 
     eCharsetReloadState        mCharsetReloadState;
 
     // Offset in the parent's child list.
-    // XXXmats the line above is bogus, it's the offset in the parent's
-    // child list at the time this docshell was added to it,
-    // see nsDocShell::AddChild().  It isn't updated after that so if children
-    // with lower indices are removed this offset is no longer valid to be used
-    // as an index into the parent's child list (see bug 162283).  It MUST not
-    // be used for that purpose.  It's used as an index to get/add history
-    // entries into nsIDocShellHistory, although I very much doubt that it
-    // can be correct for that purpose as well...
-    // Try not to use it, we should get rid of it.
+    // -1 if the docshell is added dynamically to the parent shell.
     PRUint32                   mChildOffset;
     PRUint32                   mBusyFlags;
     PRUint32                   mAppType;
     PRUint32                   mLoadType;
 
     PRInt32                    mMarginWidth;
     PRInt32                    mMarginHeight;
 
@@ -807,19 +801,23 @@ protected:
 
     // Indicates that a DocShell in this "docshell tree" is printing
     PRPackedBool               mIsPrintingOrPP;
 
     // Indicates to CreateContentViewer() that it is safe to cache the old
     // presentation of the page, and to SetupNewViewer() that the old viewer
     // should be passed a SHEntry to save itself into.
     PRPackedBool               mSavingOldViewer;
+
+    // @see nsIDocShellHistory::createdDynamically
+    PRPackedBool               mDynamicallyCreated;
 #ifdef DEBUG
     PRPackedBool               mInEnsureScriptEnv;
 #endif
+    PRUint64                   mHistoryID;
 
     static nsIURIFixup *sURIFixup;
 
 #ifdef DEBUG
 private:
     // We're counting the number of |nsDocShells| to help find leaks
     static unsigned long gNumberOfDocShells;
 #endif /* DEBUG */
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -66,17 +66,17 @@ interface nsIRequest;
 interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIDOMStorage;
 interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIVariant;
 
-[scriptable, uuid(bf6db598-3833-400b-9e53-ec220cb2496c)]
+[scriptable, uuid(74470127-87eb-4f79-8293-1616fe9cb689)]
 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.
    *
@@ -523,9 +523,15 @@ interface nsIDocShell : nsISupports
   readonly attribute boolean canExecuteScripts;
 
   /**
    * Sets whether a docshell is active. An active docshell is one that is
    * visible, and thus is not a good candidate for certain optimizations
    * like image frame discarding. Docshells are active unless told otherwise.
    */
   attribute boolean isActive;
+
+
+  /**
+   * The ID of the docshell in the session history.
+   */
+  readonly attribute unsigned long long historyID;
 };
--- a/docshell/base/nsIDocShellHistory.idl
+++ b/docshell/base/nsIDocShellHistory.idl
@@ -34,30 +34,46 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 interface nsISHEntry;
 
-[scriptable, uuid(a89b80a8-3c44-4a25-9d2c-2fb42358b46e)]
+[scriptable, uuid(95e425aa-afc6-40a0-9db4-7f210a58310a)]
 interface nsIDocShellHistory : nsISupports
 {
   /**
    * Get the SHEntry associated with a child docshell
    */
   nsISHEntry getChildSHEntry(in long aChildOffset);
 
   /**
    * Add a Child SHEntry for a frameset page, given the child's loadtype.
    */
   void addChildSHEntry(in nsISHEntry aCloneReference,
                        in nsISHEntry aHistoryEntry,
                        in long aChildOffset,
                        in unsigned long aLoadType);
 
-  /*
+  /**
    * Whether this docshell should save entries in global history.
    */
   attribute boolean useGlobalHistory;
+
+  /**
+   * Removes nsISHEntry objects related to this docshell from session history.
+   * Use this only with subdocuments, like iframes.
+   */
+  void removeFromSessionHistory();
+
+  /**
+   * Set when an iframe/frame is added dynamically.
+   */
+  attribute boolean createdDynamically;
+
+  /**
+   * Returns false for mLSHE, true for mOSHE
+   */
+  boolean getCurrentSHEntry(out nsISHEntry aEntry);
 };
 
--- a/docshell/shistory/public/nsISHContainer.idl
+++ b/docshell/shistory/public/nsISHContainer.idl
@@ -53,17 +53,17 @@ interface nsISHContainer : nsISupports
 {
 	/**
      * The current number of nsISHEntries which are immediate children of the 
 	 * current SHEntry
      */
 	readonly attribute long childCount;
 
 	/**
-	 * Add a new child SHEntry.  Adds to the end of the list.
+	 * Add a new child SHEntry.  If offset is -1 adds to the end of the list.
 	 */
 	void AddChild(in nsISHEntry child, in long offset);
 
 	/**
 	 * Removes a child SHEntry
 	 */
 	void RemoveChild(in nsISHEntry child);
 
--- a/docshell/shistory/public/nsISHEntry.idl
+++ b/docshell/shistory/public/nsISHEntry.idl
@@ -53,17 +53,17 @@ interface nsISupportsArray;
 %{C++
 struct nsIntRect;
 class nsDocShellEditorData;
 %}
 [ref] native nsIntRect(nsIntRect);
 [ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
 
 
-[scriptable, uuid(62b0603f-57ca-439e-a0fb-6f6978500755)]
+[scriptable, uuid(39b73c3a-48eb-4189-8069-247279c3c42d)]
 interface nsISHEntry : nsIHistoryEntry
 {
     /** URI for the document */
     void setURI(in nsIURI aURI);
 
     /** Referrer URI */
     attribute nsIURI referrerURI;
 
@@ -183,17 +183,19 @@ interface nsISHEntry : nsIHistoryEntry
     void setScrollPosition(in long x, in long y);
     void getScrollPosition(out long x, out long y);
 
     /** Additional ways to create an entry */
     [noscript] void create(in nsIURI URI, in AString title,
                            in nsIInputStream inputStream,
                            in nsILayoutHistoryState layoutHistoryState,
                            in nsISupports cacheKey, in ACString contentType,
-                           in nsISupports owner);
+                           in nsISupports owner,
+                           in unsigned long long docshellID,
+                           in boolean dynamicCreation);
 
     nsISHEntry clone();
 
     /** Attribute that indicates if this entry is for a subframe navigation */
     void setIsSubFrame(in boolean aFlag);
 
     /** Return any content viewer present in or below this node in the
         nsSHEntry tree.  This will differ from contentViewer in the case
@@ -224,16 +226,33 @@ interface nsISHEntry : nsIHistoryEntry
      * Sets the owning pointer to the editor data assosicated with
      * this shistory entry. Unless forgetEditorData() is called, this
      * shentry will destroy the editor data when it's destroyed.
      */
     [noscript, notxpcom] void setEditorData(in nsDocShellEditorDataPtr aData);
 
     /** Returns true if this shistory entry is storing a detached editor. */
     [noscript, notxpcom] boolean hasDetachedEditor();
+
+    /**
+     * Returns true if the related docshell was added because of
+     * dynamic addition of an iframe/frame.
+     */
+    boolean isDynamicallyAdded();
+
+    /**
+     * Returns true if any of the child entries returns true
+     * when isDynamicallyAdded is called on it.
+     */
+    boolean hasDynamicallyAddedChild();
+
+    /**
+     * The history ID of the docshell.
+     */
+    attribute unsigned long long docshellID;
 };
 
 
 %{ C++
 // {BFD1A791-AD9F-11d3-BDC7-0050040A9B44}
 #define NS_SHENTRY_CID \
 {0xbfd1a791, 0xad9f, 0x11d3, {0xbd, 0xc7, 0x0, 0x50, 0x4, 0xa, 0x9b, 0x44}}
 
--- a/docshell/shistory/public/nsISHistoryInternal.idl
+++ b/docshell/shistory/public/nsISHistoryInternal.idl
@@ -45,19 +45,23 @@ interface nsISHistoryListener;
 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"
+
+template<class E> class nsTArray;
 %}
 
-[scriptable, uuid(7ca0fd71-437c-48ad-985d-11ce9e2429b4)]
+[ref] native nsDocshellIDArray(nsTArray<PRUint64>);
+
+[scriptable, uuid(2dede933-25e1-47a3-8f61-0127c785ea01)]
 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.
@@ -105,9 +109,16 @@ interface nsISHistoryInternal: nsISuppor
     * that has timed out.
     */
    void evictExpiredContentViewerForEntry(in nsISHEntry aEntry);
 
    /**
     * Evict all the content viewers in this session history
     */
    void evictAllContentViewers();
+
+   /**
+    * Removes entries from the history if their docshellID is in
+    * aIDs array.
+    */
+  [noscript, notxpcom] void RemoveEntries(in nsDocshellIDArray aIDs,
+                                          in long aStartIndex);
 };
--- a/docshell/shistory/src/nsSHEntry.cpp
+++ b/docshell/shistory/src/nsSHEntry.cpp
@@ -107,18 +107,20 @@ nsSHEntry::nsSHEntry()
   , mPageIdentifier(mID)
   , mDocIdentifier(gEntryDocIdentifier++)
   , mScrollPositionX(0)
   , mScrollPositionY(0)
   , mIsFrameNavigation(PR_FALSE)
   , mSaveLayoutState(PR_TRUE)
   , mExpired(PR_FALSE)
   , mSticky(PR_TRUE)
+  , mDynamicallyCreated(PR_FALSE)
   , mParent(nsnull)
   , mViewerBounds(0, 0, 0, 0)
+  , mDocShellID(0)
 {
 }
 
 nsSHEntry::nsSHEntry(const nsSHEntry &other)
   : mURI(other.mURI)
   , mReferrerURI(other.mReferrerURI)
   // XXX why not copy mDocument?
   , mTitle(other.mTitle)
@@ -129,21 +131,23 @@ nsSHEntry::nsSHEntry(const nsSHEntry &ot
   , mPageIdentifier(other.mPageIdentifier)
   , mDocIdentifier(other.mDocIdentifier)
   , mScrollPositionX(0)  // XXX why not copy?
   , mScrollPositionY(0)  // XXX why not copy?
   , mIsFrameNavigation(other.mIsFrameNavigation)
   , mSaveLayoutState(other.mSaveLayoutState)
   , mExpired(other.mExpired)
   , mSticky(PR_TRUE)
+  , mDynamicallyCreated(other.mDynamicallyCreated)
   // XXX why not copy mContentType?
   , mCacheKey(other.mCacheKey)
   , mParent(other.mParent)
   , mViewerBounds(0, 0, 0, 0)
   , mOwner(other.mOwner)
+  , mDocShellID(other.mDocShellID)
 {
 }
 
 static PRBool
 ClearParentPtr(nsISHEntry* aEntry, void* /* aData */)
 {
   if (aEntry) {
     aEntry->SetParent(nsnull);
@@ -484,24 +488,27 @@ NS_IMETHODIMP nsSHEntry::SetContentType(
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::Create(nsIURI * aURI, const nsAString &aTitle,
                   nsIInputStream * aInputStream,
                   nsILayoutHistoryState * aLayoutHistoryState,
                   nsISupports * aCacheKey, const nsACString& aContentType,
-                  nsISupports* aOwner)
+                  nsISupports* aOwner,
+                  PRUint64 aDocShellID, PRBool aDynamicCreation)
 {
   mURI = aURI;
   mTitle = aTitle;
   mPostData = aInputStream;
   mCacheKey = aCacheKey;
   mContentType = aContentType;
   mOwner = aOwner;
+  mDocShellID = aDocShellID;
+  mDynamicallyCreated = aDynamicCreation;
 
   // Set the LoadType by default to loadHistory during creation
   mLoadType = (PRUint32) nsIDocShellLoadInfo::loadHistory;
 
   // By default all entries are set false for subframe flag. 
   // nsDocShell::CloneAndReplace() which creates entries for
   // all subframe navigations, sets the flag to true.
   mIsFrameNavigation = PR_FALSE;
@@ -598,51 +605,68 @@ nsSHEntry::GetChildCount(PRInt32 * aCoun
 {
   *aCount = mChildren.Count();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::AddChild(nsISHEntry * aChild, PRInt32 aOffset)
 {
-  NS_ENSURE_TRUE(aChild, NS_ERROR_FAILURE);
+  if (aChild) {
+    NS_ENSURE_SUCCESS(aChild->SetParent(this), NS_ERROR_FAILURE);
+  }
 
-  NS_ENSURE_SUCCESS(aChild->SetParent(this), NS_ERROR_FAILURE);
+  if (aOffset < 0) {
+    mChildren.AppendObject(aChild);
+    return NS_OK;
+  }
 
   //
   // Bug 52670: Ensure children are added in order.
   //
   //  Later frames in the child list may load faster and get appended
   //  before earlier frames, causing session history to be scrambled.
   //  By growing the list here, they are added to the right position.
   //
   //  Assert that aOffset will not be so high as to grow us a lot.
   //
   NS_ASSERTION(aOffset < (mChildren.Count()+1023), "Large frames array!\n");
 
+#ifdef DEBUG
   if (aOffset < mChildren.Count()) {
     nsISHEntry* oldChild = mChildren.ObjectAt(aOffset);
-    if (oldChild && oldChild != aChild) {
-      NS_WARNING("Adding child where we already have a child?  "
-                 "This will likely misbehave");
-      oldChild->SetParent(nsnull);
+    if (aChild && oldChild && oldChild != aChild) {
+      PRBool dyn = PR_FALSE;
+      oldChild->IsDynamicallyAdded(&dyn);
+      NS_WARN_IF_FALSE(dyn, "Adding child where we already have a child?  "
+                            "This may misbehave");
     }
   }
-  
-  // This implicitly extends the array to include aOffset
-  mChildren.ReplaceObjectAt(aChild, aOffset);
+#endif
+
+  mChildren.InsertObjectAt(aChild, aOffset);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::RemoveChild(nsISHEntry * aChild)
 {
   NS_ENSURE_TRUE(aChild, NS_ERROR_FAILURE);
-  PRBool childRemoved = mChildren.RemoveObject(aChild);
+  PRBool childRemoved = PR_FALSE;
+  PRBool dynamic = PR_FALSE;
+  aChild->IsDynamicallyAdded(&dynamic);
+  if (dynamic) {
+    childRemoved = mChildren.RemoveObject(aChild);
+  } else {
+    PRInt32 index = mChildren.IndexOfObject(aChild);
+    if (index >= 0) {
+      childRemoved = mChildren.ReplaceObjectAt(nsnull, index);
+    }
+  }
   if (childRemoved)
     aChild->SetParent(nsnull);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsSHEntry::GetChildAt(PRInt32 aIndex, nsISHEntry ** aResult)
 {
@@ -904,8 +928,45 @@ nsSHEntry::GetStateData(nsAString &aStat
 
 NS_IMETHODIMP
 nsSHEntry::SetStateData(const nsAString &aDataStr)
 {
   mStateData.Assign(aDataStr);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsSHEntry::IsDynamicallyAdded(PRBool* aAdded)
+{
+  *aAdded = mDynamicallyCreated;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::HasDynamicallyAddedChild(PRBool* aAdded)
+{
+  *aAdded = PR_FALSE;
+  for (PRInt32 i = 0; i < mChildren.Count(); ++i) {
+    nsISHEntry* entry = mChildren[i];
+    if (entry) {
+      entry->IsDynamicallyAdded(aAdded);
+      if (*aAdded) {
+        break;
+      }
+    }
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::GetDocshellID(PRUint64* aID)
+{
+  *aID = mDocShellID;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsSHEntry::SetDocshellID(PRUint64 aID)
+{
+  mDocShellID = aID;
+  return NS_OK;
+}
+
--- a/docshell/shistory/src/nsSHEntry.h
+++ b/docshell/shistory/src/nsSHEntry.h
@@ -101,22 +101,24 @@ private:
   PRUint32                        mPageIdentifier;
   PRInt64                         mDocIdentifier;
   PRInt32                         mScrollPositionX;
   PRInt32                         mScrollPositionY;
   PRPackedBool                    mIsFrameNavigation;
   PRPackedBool                    mSaveLayoutState;
   PRPackedBool                    mExpired;
   PRPackedBool                    mSticky;
+  PRPackedBool                    mDynamicallyCreated;
   nsCString                       mContentType;
   nsCOMPtr<nsISupports>           mCacheKey;
   nsISHEntry *                    mParent;  // weak reference
   nsCOMPtr<nsISupports>           mWindowState;
   nsIntRect                       mViewerBounds;
   nsCOMArray<nsIDocShellTreeItem> mChildShells;
   nsCOMPtr<nsISupportsArray>      mRefreshURIList;
   nsCOMPtr<nsISupports>           mOwner;
   nsExpirationState               mExpirationState;
   nsAutoPtr<nsDocShellEditorData> mEditorData;
   nsString                        mStateData;
+  PRUint64                        mDocShellID;
 };
 
 #endif /* nsSHEntry_h */
--- a/docshell/shistory/src/nsSHistory.cpp
+++ b/docshell/shistory/src/nsSHistory.cpp
@@ -55,16 +55,18 @@
 #include "nsIServiceManager.h"
 #include "nsIPrefService.h"
 #include "nsIURI.h"
 #include "nsIContentViewer.h"
 #include "nsICacheService.h"
 #include "nsIObserverService.h"
 #include "prclist.h"
 #include "mozilla/Services.h"
+#include "nsTArray.h"
+#include "nsCOMArray.h"
 
 // For calculating max history entries and max cachable contentviewers
 #include "nspr.h"
 #include <math.h>  // for log()
 
 #define PREF_SHISTORY_SIZE "browser.sessionhistory.max_entries"
 #define PREF_SHISTORY_MAX_TOTAL_VIEWERS "browser.sessionhistory.max_total_viewers"
 
@@ -340,16 +342,17 @@ nsSHistory::AddEntry(nsISHEntry * aSHEnt
   // If this is the very first transaction, initialize the list
   if(!mListRoot)
     mListRoot = txn;
 
   // Purge History list if it is too long
   if ((gHistoryMaxSize >= 0) && (mLength > gHistoryMaxSize))
     PurgeHistory(mLength-gHistoryMaxSize);
   
+  RemoveDynEntries(mIndex - 1, mIndex);
   return NS_OK;
 }
 
 /* Get size of the history list */
 NS_IMETHODIMP
 nsSHistory::GetCount(PRInt32 * aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
@@ -1058,21 +1061,134 @@ void
 nsSHistory::EvictAllContentViewersGlobally()
 {
   PRInt32 maxViewers = sHistoryMaxTotalViewers;
   sHistoryMaxTotalViewers = 0;
   EvictGlobalContentViewer();
   sHistoryMaxTotalViewers = maxViewers;
 }
 
+void GetDynamicChildren(nsISHContainer* aContainer,
+                        nsTArray<PRUint64>& aDocshellIDs,
+                        PRBool aOnlyTopLevelDynamic)
+{
+  PRInt32 count = 0;
+  aContainer->GetChildCount(&count);
+  for (PRInt32 i = 0; i < count; ++i) {
+    nsCOMPtr<nsISHEntry> child;
+    aContainer->GetChildAt(i, getter_AddRefs(child));
+    if (child) {
+      PRBool dynAdded = PR_FALSE;
+      child->IsDynamicallyAdded(&dynAdded);
+      if (dynAdded) {
+        PRUint64 docshellID = 0;
+        child->GetDocshellID(&docshellID);
+        aDocshellIDs.AppendElement(docshellID);
+      }
+      if (!dynAdded || !aOnlyTopLevelDynamic) {
+        nsCOMPtr<nsISHContainer> childAsContainer = do_QueryInterface(child);
+        if (childAsContainer) {
+          GetDynamicChildren(childAsContainer, aDocshellIDs,
+                             aOnlyTopLevelDynamic);
+        }
+      }
+    }
+  }
+}
+
+PRBool
+RemoveFromSessionHistoryContainer(nsISHContainer* aContainer,
+                                  nsTArray<PRUint64>& aDocshellIDs)
+{
+  nsCOMPtr<nsISHEntry> root = do_QueryInterface(aContainer);
+  NS_ENSURE_TRUE(root, PR_FALSE);
+
+  PRBool didRemove = PR_FALSE;
+  PRInt32 childCount = 0;
+  aContainer->GetChildCount(&childCount);
+  for (PRInt32 i = childCount - 1; i >= 0; --i) {
+    nsCOMPtr<nsISHEntry> child;
+    aContainer->GetChildAt(i, getter_AddRefs(child));
+    if (child) {
+      PRUint64 docshelldID = 0;
+      child->GetDocshellID(&docshelldID);
+      if (aDocshellIDs.Contains(docshelldID)) {
+        didRemove = PR_TRUE;
+        aContainer->RemoveChild(child);
+      } else {
+        nsCOMPtr<nsISHContainer> container = do_QueryInterface(child);
+        if (container) {
+          PRBool childRemoved =
+            RemoveFromSessionHistoryContainer(container, aDocshellIDs);
+          if (childRemoved) {
+            didRemove = PR_TRUE;
+          }
+        }
+      }
+    }
+  }
+  return didRemove;
+}
+
+PRBool RemoveChildEntries(nsISHistory* aHistory, PRInt32 aIndex,
+                          nsTArray<PRUint64>& aEntryIDs)
+{
+  nsCOMPtr<nsIHistoryEntry> rootHE;
+  aHistory->GetEntryAtIndex(aIndex, PR_FALSE, getter_AddRefs(rootHE));
+  nsCOMPtr<nsISHContainer> root = do_QueryInterface(rootHE);
+  return root ? RemoveFromSessionHistoryContainer(root, aEntryIDs) : PR_FALSE;
+}
+
+NS_IMETHODIMP_(void)
+nsSHistory::RemoveEntries(nsTArray<PRUint64>& aIDs, PRInt32 aStartIndex)
+{
+  PRInt32 index = aStartIndex;
+  while(index >= 0 && RemoveChildEntries(this, --index, aIDs));
+  index = aStartIndex;
+  while(index >= 0 && RemoveChildEntries(this, index++, aIDs));
+}
+
+void
+nsSHistory::RemoveDynEntries(PRInt32 aOldIndex, PRInt32 aNewIndex)
+{
+  // Search for the entries which are in the current index,
+  // but not in the new one.
+  nsCOMPtr<nsISHEntry> originalSH;
+  GetEntryAtIndex(aOldIndex, PR_FALSE, getter_AddRefs(originalSH));
+  nsCOMPtr<nsISHContainer> originalContainer = do_QueryInterface(originalSH);
+  nsAutoTArray<PRUint64, 16> toBeRemovedEntries;
+  if (originalContainer) {
+    nsTArray<PRUint64> originalDynDocShellIDs;
+    GetDynamicChildren(originalContainer, originalDynDocShellIDs, PR_TRUE);
+    if (originalDynDocShellIDs.Length()) {
+      nsCOMPtr<nsISHEntry> currentSH;
+      GetEntryAtIndex(aNewIndex, PR_FALSE, getter_AddRefs(currentSH));
+      nsCOMPtr<nsISHContainer> newContainer = do_QueryInterface(currentSH);
+      if (newContainer) {
+        nsTArray<PRUint64> newDynDocShellIDs;
+        GetDynamicChildren(newContainer, newDynDocShellIDs, PR_FALSE);
+        for (PRUint32 i = 0; i < originalDynDocShellIDs.Length(); ++i) {
+          if (!newDynDocShellIDs.Contains(originalDynDocShellIDs[i])) {
+            toBeRemovedEntries.AppendElement(originalDynDocShellIDs[i]);
+          }
+        }
+      }
+    }
+  }
+  if (toBeRemovedEntries.Length()) {
+    RemoveEntries(toBeRemovedEntries, aOldIndex);
+  }
+}
+
 NS_IMETHODIMP
 nsSHistory::UpdateIndex()
 {
   // Update the actual index with the right value. 
   if (mIndex != mRequestedIndex && mRequestedIndex != -1) {
+    RemoveDynEntries(mIndex, mRequestedIndex);
     mIndex = mRequestedIndex;
   }
 
   mRequestedIndex = -1;
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -1141,16 +1257,29 @@ nsSHistory::LoadURI(const PRUnichar* aUR
 }
 
 NS_IMETHODIMP
 nsSHistory::GotoIndex(PRInt32 aIndex)
 {
   return LoadEntry(aIndex, nsIDocShellLoadInfo::loadHistory, HIST_CMD_GOTOINDEX);
 }
 
+nsresult
+nsSHistory::LoadNextPossibleEntry(PRInt32 aNewIndex, long aLoadType, PRUint32 aHistCmd)
+{
+  mRequestedIndex = -1;
+  if (aNewIndex < mIndex) {
+    return LoadEntry(aNewIndex - 1, aLoadType, aHistCmd);
+  }
+  if (aNewIndex > mIndex) {
+    return LoadEntry(aNewIndex + 1, aLoadType, aHistCmd);
+  }
+  return NS_ERROR_FAILURE;
+}
+
 NS_IMETHODIMP
 nsSHistory::LoadEntry(PRInt32 aIndex, long aLoadType, PRUint32 aHistCmd)
 {
   nsCOMPtr<nsIDocShell> docShell;
   nsCOMPtr<nsISHEntry> shEntry;
   // Keep note of requested history index in mRequestedIndex.
   mRequestedIndex = aIndex;
 
@@ -1213,43 +1342,61 @@ nsSHistory::LoadEntry(PRInt32 aIndex, lo
     // Going back or forward.
     if ((pCount > 0) && (nCount > 0)) {
       /* THis is a subframe navigation. Go find 
        * the docshell in which load should happen
        */
       PRBool frameFound = PR_FALSE;
       nsresult rv = CompareFrames(prevEntry, nextEntry, mRootDocShell, aLoadType, &frameFound);
       if (!frameFound) {
-        // we did not successfully find the subframe in which
-        // the new url was to be loaded. return error.
-        mRequestedIndex = -1;
-        return NS_ERROR_FAILURE; 
+        // We did not successfully find the subframe in which
+        // the new url was to be loaded. Go further in the history.
+        return LoadNextPossibleEntry(aIndex, aLoadType, aHistCmd);
       }
       return rv;
     }   // (pCount >0)
-    else
+    else {
+      // Loading top level page.
+      PRUint32 prevID = 0;
+      PRUint32 nextID = 0;
+      prevEntry->GetID(&prevID);
+      nextEntry->GetID(&nextID);
+      if (prevID == nextID) {
+        // Try harder to find something new to load.
+        // This may happen for example if some page removed iframes dynamically.
+        return LoadNextPossibleEntry(aIndex, aLoadType, aHistCmd);
+      }
       docShell = mRootDocShell;
     }
+  }
 
   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
   return InitiateLoad(nextEntry, docShell, aLoadType);
 }
 
 nsresult
 nsSHistory::CompareFrames(nsISHEntry * aPrevEntry, nsISHEntry * aNextEntry, nsIDocShell * aParent, long aLoadType, PRBool * aIsFrameFound)
 {
   if (!aPrevEntry || !aNextEntry || !aParent)
-    return PR_FALSE;
+    return NS_ERROR_FAILURE;
+
+  // We should be comparing only entries which were created for the
+  // same docshell. This is here to just prevent anything strange happening.
+  // This check could be possibly an assertion.
+  PRUint64 prevdID, nextdID;
+  aPrevEntry->GetDocshellID(&prevdID);
+  aNextEntry->GetDocshellID(&nextdID);
+  NS_ENSURE_STATE(prevdID == nextdID);
 
   nsresult result = NS_OK;
   PRUint32 prevID, nextID;
 
   aPrevEntry->GetID(&prevID);
   aNextEntry->GetID(&nextID);
  
   // Check the IDs to verify if the pages are different.
@@ -1273,32 +1420,73 @@ nsSHistory::CompareFrames(nsISHEntry * a
     return NS_ERROR_FAILURE;
   if (!prevContainer || !nextContainer)
     return NS_ERROR_FAILURE;
 
   prevContainer->GetChildCount(&pcnt);
   nextContainer->GetChildCount(&ncnt);
   dsTreeNode->GetChildCount(&dsCount);
 
-  //XXX What to do if the children count don't match
-    
-  for (PRInt32 i=0; i<ncnt; i++){
-    nsCOMPtr<nsISHEntry> pChild, nChild;
-    nsCOMPtr<nsIDocShellTreeItem> dsTreeItemChild;
-	  
-    prevContainer->GetChildAt(i, getter_AddRefs(pChild));
+  // Create an array for child docshells.
+  nsCOMArray<nsIDocShell> docshells;
+  for (PRInt32 i = 0; i < dsCount; ++i) {
+    nsCOMPtr<nsIDocShellTreeItem> treeItem;
+    dsTreeNode->GetChildAt(i, getter_AddRefs(treeItem));
+    nsCOMPtr<nsIDocShell> shell = do_QueryInterface(treeItem);
+    if (shell) {
+      docshells.AppendObject(shell);
+    }
+  }
+
+  // Search for something to load next.
+  for (PRInt32 i = 0; i < ncnt; ++i) {
+    // First get an entry which may cause a new page to be loaded.
+    nsCOMPtr<nsISHEntry> nChild;
     nextContainer->GetChildAt(i, getter_AddRefs(nChild));
-    if (dsCount > 0)
-      dsTreeNode->GetChildAt(i, getter_AddRefs(dsTreeItemChild));
+    if (!nChild) {
+      continue;
+    }
+    PRUint64 docshellID = 0;
+    nChild->GetDocshellID(&docshellID);
 
-    if (!dsTreeItemChild)
-      return NS_ERROR_FAILURE;
+    // Then find the associated docshell.
+    nsIDocShell* dsChild = nsnull;
+    PRInt32 count = docshells.Count();
+    for (PRInt32 j = 0; j < count; ++j) {
+      PRUint64 shellID = 0;
+      nsIDocShell* shell = docshells[j];
+      shell->GetHistoryID(&shellID);
+      if (shellID == docshellID) {
+        dsChild = shell;
+        break;
+      }
+    }
+    if (!dsChild) {
+      continue;
+    }
 
-    nsCOMPtr<nsIDocShell> dsChild(do_QueryInterface(dsTreeItemChild));
+    // Then look at the previous entries to see if there was
+    // an entry for the docshell.
+    nsCOMPtr<nsISHEntry> pChild;
+    for (PRInt32 k = 0; k < pcnt; ++k) {
+      nsCOMPtr<nsISHEntry> child;
+      prevContainer->GetChildAt(k, getter_AddRefs(child));
+      if (child) {
+        PRUint64 dID = 0;
+        child->GetDocshellID(&dID);
+        if (dID == docshellID) {
+          pChild = child;
+          break;
+        }
+      }
+    }
 
+    // Finally recursively call this method.
+    // This will either load a new page to shell or some subshell or
+    // do nothing.
     CompareFrames(pChild, nChild, dsChild, aLoadType, aIsFrameFound);
   }     
   return result;
 }
 
 
 nsresult 
 nsSHistory::InitiateLoad(nsISHEntry * aFrameEntry, nsIDocShell * aFrameDS, long aLoadType)
--- a/docshell/shistory/src/nsSHistory.h
+++ b/docshell/shistory/src/nsSHistory.h
@@ -107,16 +107,19 @@ protected:
   void EvictWindowContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex);
   static void EvictGlobalContentViewer();
   static void EvictAllContentViewersGlobally();
 
   // Calculates a max number of total
   // content viewers to cache, based on amount of total memory
   static PRUint32 CalcMaxTotalViewers();
 
+  void RemoveDynEntries(PRInt32 aOldIndex, PRInt32 aNewIndex);
+
+  nsresult LoadNextPossibleEntry(PRInt32 aNewIndex, long aLoadType, PRUint32 aHistCmd);
 protected:
   nsCOMPtr<nsISHTransaction> mListRoot;
   PRInt32 mIndex;
   PRInt32 mLength;
   PRInt32 mRequestedIndex;
   // Session History listener
   nsWeakPtr mListener;
   // Weak reference. Do not refcount this.
--- a/docshell/test/navigation/Makefile.in
+++ b/docshell/test/navigation/Makefile.in
@@ -61,16 +61,28 @@ include $(topsrcdir)/config/rules.mk
 		test_popup-navigates-children.html \
 		test_reserved.html \
 		NavigationUtils.js \
 		navigate.html \
 		open.html \
 		iframe.html \
 		parent.html \
 		blank.html \
+		test_sessionhistory.html \
+		file_bug462076_1.html \
+		file_bug462076_2.html \
+		file_bug462076_3.html \
+		file_bug508537_1.html \
+		file_document_write_1.html \
+		file_static_and_dynamic_1.html \
+		frame0.html \
+		frame1.html \
+		frame2.html \
+		frame3.html \
+		goback.html \
 		$(NULL)
 
 ifneq (mobile,$(MOZ_BUILD_APP))
 _BROWSER_TEST_FILES = \
 		browser_bug343515.js \
 		bug343515_pg1.html \
 		bug343515_pg2.html \
 		bug343515_pg3.html \
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_bug462076_1.html
@@ -0,0 +1,55 @@
+<html>
+  <head>
+    <title>Bug 462076</title>
+    <script>
+      var srcs = [ "frame0.html",
+                   "frame1.html",
+                   "frame2.html",
+                   "frame3.html" ];
+
+      var checkCount = 0;
+
+      function makeFrame(index) {
+        var ifr = document.createElement("iframe");
+        ifr.src = srcs[index];
+        ifr.onload = checkFrame;
+        document.getElementById("container" + index).appendChild(ifr);
+      }
+
+      function runTest() {
+        var randomNumber = Math.floor(Math.random() * 4);
+        for (var i = randomNumber; i < 4; ++i) {
+          makeFrame(i);
+        }
+        for (var i = 0; i < randomNumber; ++i) {
+          makeFrame(i);
+        }
+      }
+
+      function checkFrame(evt) {
+        var ifr = evt.target;
+        opener.ok(new String(ifr.contentWindow.location).indexOf(ifr.src) >= 0,
+           "Wrong document loaded (" + ifr.src + ", " + 
+           ifr.contentWindow.location + ")!");
+
+        if (++checkCount == 4) {
+          if (++opener.testCount == 10) {
+            opener.nextTest();
+            window.close();
+          } else {
+            window.location.reload();
+          }
+        }
+      }
+    </script>
+  </head>
+  <body>
+    <div id="container0"></div>
+    <div id="container1"></div>
+    <div id="container2"></div>
+    <div id="container3"></div>
+    <script>
+      runTest();
+    </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_bug462076_2.html
@@ -0,0 +1,52 @@
+<html>
+  <head>
+    <title>Bug 462076</title>
+    <script>
+      var srcs = [ "frame0.html",
+                   "frame1.html",
+                   "frame2.html",
+                   "frame3.html" ];
+
+      var checkCount = 0;
+
+      function makeFrame(index) {
+        var ifr = document.createElement("iframe");
+        ifr.src = srcs[index];
+        ifr.onload = checkFrame;
+        document.getElementById("container" + index).appendChild(ifr);
+      }
+
+      function runTest() {
+        var randomNumber = Math.floor(Math.random() * 4);
+        for (var i = randomNumber; i < 4; ++i) {
+          makeFrame(i);
+        }
+        for (var i = 0; i < randomNumber; ++i) {
+          makeFrame(i);
+        }
+      }
+
+      function checkFrame(evt) {
+        var ifr = evt.target;
+        opener.ok(new String(ifr.contentWindow.location).indexOf(ifr.src) >= 0,
+           "Wrong document loaded (" + ifr.src + ", " + 
+           ifr.contentWindow.location + ")!");
+
+        if (++checkCount == 4) {
+          if (++opener.testCount == 10) {
+            opener.nextTest();
+            window.close();
+          } else {
+            window.location.reload();
+          }
+        }
+      }
+    </script>
+  </head>
+  <body onload="runTest();">
+    <div id="container0"></div>
+    <div id="container1"></div>
+    <div id="container2"></div>
+    <div id="container3"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_bug462076_3.html
@@ -0,0 +1,52 @@
+<html>
+  <head>
+    <title>Bug 462076</title>
+    <script>
+      var srcs = [ "frame0.html",
+                   "frame1.html",
+                   "frame2.html",
+                   "frame3.html" ];
+
+      var checkCount = 0;
+
+      function makeFrame(index) {
+        var ifr = document.createElement("iframe");
+        ifr.src = srcs[index];
+        ifr.onload = checkFrame;
+        document.getElementById("container" + index).appendChild(ifr);
+      }
+
+      function runTest() {
+        var randomNumber = Math.floor(Math.random() * 4);
+        for (var i = randomNumber; i < 4; ++i) {
+          makeFrame(i);
+        }
+        for (var i = 0; i < randomNumber; ++i) {
+          makeFrame(i);
+        }
+      }
+
+      function checkFrame(evt) {
+        var ifr = evt.target;
+        opener.ok(new String(ifr.contentWindow.location).indexOf(ifr.src) >= 0,
+           "Wrong document loaded (" + ifr.src + ", " + 
+           ifr.contentWindow.location + ")!");
+
+        if (++checkCount == 4) {
+          if (++opener.testCount == 10) {
+            opener.nextTest();
+            window.close();
+          } else {
+            window.location.reload();
+          }
+        }
+      }
+    </script>
+  </head>
+  <body onload="setTimeout(runTest, 1000);">
+    <div id="container0"></div>
+    <div id="container1"></div>
+    <div id="container2"></div>
+    <div id="container3"></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_bug508537_1.html
@@ -0,0 +1,34 @@
+<html>
+  <head>
+    <script>
+      function dynFrameLoad() {
+        var ifrs = document.getElementsByTagName("iframe");
+        opener.ok(new String(ifrs[0].contentWindow.location).indexOf(ifrs[0].src) >= 0,
+                  "Wrong document loaded (1)\n");
+        opener.ok(new String(ifrs[1].contentWindow.location).indexOf(ifrs[1].src) >= 0,
+                  "Wrong document loaded (2)\n");
+        if (opener && ++opener.testCount == 1) {
+          window.location = "goback.html";
+        } else {
+          opener.nextTest();
+          window.close();
+        }
+      }
+
+      window.addEventListener("load",
+        function () {
+          var container = document.getElementById("t1");
+          container.addEventListener("load", dynFrameLoad, true);
+          container.appendChild(container.appendChild(document.getElementById("i1")));
+        }, false);
+    </script>
+  </head>
+  <body>
+    <h5>Container:</h5>
+    <div id="t1"></div>
+    <h5>Original frames:</h5>
+    <iframe id="i1" src="frame0.html"></iframe>
+    <iframe src="frame1.html"></iframe>
+  </body>
+</html>
+
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_document_write_1.html
@@ -0,0 +1,22 @@
+<html>
+  <head>
+    <script>
+      function run() {
+        document.open();
+        document.write("<h5 id='dynamic'>document.written content</h5>");
+        document.close();
+        window.history.go(-1);
+        opener.setTimeout("isTestDynamic()", 2500);
+      }
+
+      function start() {
+        if (++opener.testCount == 1) {
+          setTimeout(run, 1000);
+        }
+      }
+    </script>
+  </head>
+  <body onload="start();">
+    <h5>static content</h5>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/file_static_and_dynamic_1.html
@@ -0,0 +1,32 @@
+<html>
+  <head>
+    <script>
+    function test() {
+      var ifr = document.createElement("iframe");
+      ifr.src = "frame0.html";
+      document.getElementById("dynamic").appendChild(ifr);
+      var staticFrame = document.getElementById("staticframe");
+      staticFrame.onload = window.location = "goback.html";
+      staticFrame.contentWindow.location = "frame1.html";
+    }
+
+    function start() {
+      if (++opener.testCount == 1) {
+        test();
+      } else {
+        var staticFrame = document.getElementById("staticframe");
+        opener.ok(new String(staticFrame.contentWindow.location).indexOf(staticFrame.src) >= 0,
+                  "Wrong document loaded!");
+        opener.nextTest();
+        window.close();
+      }
+    }
+    </script>
+  </head>
+  <body onload="setTimeout('start()', 1000)">
+    <h5>Dynamic</h5>
+    <div id="dynamic"></div>
+    <h5>Static</h5>
+    <div id="static"><iframe id="staticframe" src="frame0.html"></iframe></div>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/frame0.html
@@ -0,0 +1,3 @@
+<html>
+  <body>Frame 0</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/frame1.html
@@ -0,0 +1,3 @@
+<html>
+  <body>Frame 1</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/frame2.html
@@ -0,0 +1,3 @@
+<html>
+  <body>Frame 2</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/frame3.html
@@ -0,0 +1,3 @@
+<html>
+  <body>Frame 3</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/goback.html
@@ -0,0 +1,5 @@
+<html>
+  <body onload="setTimeout('window.history.go(-1)', 1000);">
+    window.history.go(-1);
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/test_sessionhistory.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=
+-->
+<head>
+  <title>Test for Bug </title>
+  <script type="application/javascript" src="/MochiKit/packed.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 onload="nextTest()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug  **/
+
+var testFiles =
+  [ "file_bug462076_1.html",         // Dynamic frames before onload
+    "file_bug462076_2.html",         // Dynamic frames when handling onload
+    "file_bug462076_3.html",         // Dynamic frames after onload
+    "file_bug508537_1.html",         // Dynamic frames and forward-back
+    "file_document_write_1.html",    // Session history + document.write
+    "file_static_and_dynamic_1.html" // Static and dynamic frames and forward-back
+  ];
+var testCount = 0; // Used by the test files.
+
+SimpleTest.waitForExplicitFinish();
+
+var testWindow;
+function nextTest_() {
+  if (testFiles.length) {
+    testCount = 0;
+    testWindow = window.open(testFiles.shift(), "", "width=300,height=300");
+    testWindow.onunload = function () { } //XXX
+  } else {
+    SimpleTest.finish();
+  }
+}
+
+// Needed by file_document_write_1.html
+function isTestDynamic() {
+  var dyn = testWindow.document.getElementById("dynamic");
+  is(dyn, null, "Should have gone back to the static page!");
+  nextTest();
+  testWindow.close();
+}
+
+function nextTest() {
+  setTimeout(nextTest_, 1000);
+}
+
+</script>
+</pre>
+</body>
+</html>