/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * The contents of this file are subject to the Mozilla Public
 * License Version 1.1 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of
 * the License at http://www.mozilla.org/MPL/
 * 
 * Software distributed under the License is distributed on an "AS
 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * rights and limitations under the License.
 * 
 * The Original Code is the Mozilla browser.
 * 
 * The Initial Developer of the Original Code is Netscape
 * Communications, Inc.  Portions created by Netscape are
 * Copyright (C) 1999, Mozilla.  All Rights Reserved.
 * 
 * Contributor(s):
 *   Travis Bogard <travis@netscape.com>
 *   Pierre Phaneuf <pp@ludusdesign.com>
 */

#include "nsIComponentManager.h"
#include "nsIContent.h"
#include "nsIDocument.h"
#include "nsIDOMDocument.h"
#include "nsIDOMElement.h"
#include "nsIDocumentViewer.h"
#include "nsIDocumentLoaderFactory.h"
#include "nsCURILoader.h"
#include "nsLayoutCID.h"
#include "nsNetUtil.h"
#include "nsRect.h"
#include "prprf.h"
#include "nsIMarkupDocumentViewer.h"
#include "nsXPIDLString.h"
#include "nsIChromeEventHandler.h"
#include "nsIDOMWindow.h"
#include "nsIWebBrowserChrome.h"
#include "nsPoint.h"
#include "nsGfxCIID.h"
#include "nsIPrompt.h"
#include "nsTextFormatter.h"

// Local Includes
#include "nsDocShell.h"
#include "nsDocShellLoadInfo.h"

// Helper Classes
#include "nsDOMError.h"
#include "nsEscape.h"
#include "nsHTTPEnums.h"

// Interfaces Needed
#include "nsICharsetConverterManager.h"
#include "nsIHTTPChannel.h"
#include "nsIDataChannel.h"
#include "nsIProgressEventSink.h"
#include "nsIWebProgress.h"
#include "nsILayoutHistoryState.h"
#include "nsILocaleService.h"
#include "nsIPlatformCharset.h"
#include "nsITimer.h"
#include "nsIFileStream.h"

#include "nsIPrincipal.h"

// For reporting errors with the console service.
// These can go away if error reporting is propagated up past nsDocShell.
#include "nsIConsoleService.h"
#include "nsIScriptError.h"

// used to dispatch urls to default protocol handlers
#include "nsCExternalHandlerService.h"
#include "nsIExternalProtocolService.h"

static NS_DEFINE_IID(kDeviceContextCID, NS_DEVICE_CONTEXT_CID);
static NS_DEFINE_CID(kPlatformCharsetCID, NS_PLATFORMCHARSET_CID);
static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID);
static NS_DEFINE_CID(kSimpleURICID,            NS_SIMPLEURI_CID);

//*****************************************************************************
//***    nsDocShell: Object Management
//*****************************************************************************

nsDocShell::nsDocShell() : 
  mContentListener(nsnull),
  mInitInfo(nsnull), 
  mMarginWidth(0), 
  mMarginHeight(0),
  mItemType(typeContent),
  mCurrentScrollbarPref(-1,-1),
  mDefaultScrollbarPref(-1,-1),
  mInitialPageLoad(PR_TRUE),
  mAllowPlugins(PR_TRUE),
  mViewMode(viewNormal),
  mEODForCurrentDocument (PR_FALSE),
  mUseExternalProtocolHandler (PR_FALSE),
  mParent(nsnull),
  mTreeOwner(nsnull),
  mChromeEventHandler(nsnull)
{
  NS_INIT_REFCNT();
}

nsDocShell::~nsDocShell()
{
   Destroy();
}

NS_IMETHODIMP nsDocShell::DestroyChildren()
{
   PRInt32 i, n = mChildren.Count();
   nsCOMPtr<nsIDocShellTreeItem>   shell;
   for (i = 0; i < n; i++) 
      {
      shell = dont_AddRef((nsIDocShellTreeItem*)mChildren.ElementAt(i));
      if(!NS_WARN_IF_FALSE(shell, "docshell has null child"))
         shell->SetParent(nsnull);
      nsCOMPtr<nsIBaseWindow> shellWin(do_QueryInterface(shell));
      if(shellWin)
         shellWin->Destroy();
      }
   mChildren.Clear();
   return NS_OK;
}

//*****************************************************************************
// nsDocShell::nsISupports
//*****************************************************************************   

NS_IMPL_THREADSAFE_ADDREF(nsDocShell)
NS_IMPL_THREADSAFE_RELEASE(nsDocShell)

NS_INTERFACE_MAP_BEGIN(nsDocShell)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDocShell)
   NS_INTERFACE_MAP_ENTRY(nsIDocShell)
   NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeItem)
   NS_INTERFACE_MAP_ENTRY(nsIDocShellTreeNode)
   NS_INTERFACE_MAP_ENTRY(nsIDocShellHistory)
   NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
   NS_INTERFACE_MAP_ENTRY(nsIBaseWindow)
   NS_INTERFACE_MAP_ENTRY(nsIScrollable)
   NS_INTERFACE_MAP_ENTRY(nsITextScroll)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsIScriptGlobalObjectOwner)
   NS_INTERFACE_MAP_ENTRY(nsIRefreshURI)
   NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END_THREADSAFE


///*****************************************************************************
// nsDocShell::nsIInterfaceRequestor
//*****************************************************************************   

NS_IMETHODIMP nsDocShell::GetInterface(const nsIID& aIID, void** aSink)
{
   NS_ENSURE_ARG_POINTER(aSink);

   if(aIID.Equals(NS_GET_IID(nsIURIContentListener)) &&
      NS_SUCCEEDED(EnsureContentListener()))
      *aSink = mContentListener;
   else if(aIID.Equals(NS_GET_IID(nsIScriptGlobalObject)) &&
      NS_SUCCEEDED(EnsureScriptEnvironment()))
      *aSink = mScriptGlobal;
   else if(aIID.Equals(NS_GET_IID(nsIDOMWindow)) &&
      NS_SUCCEEDED(EnsureScriptEnvironment()))
      {
      NS_ENSURE_SUCCESS(mScriptGlobal->QueryInterface(NS_GET_IID(nsIDOMWindow),
         aSink), NS_ERROR_FAILURE);
      return NS_OK;
      }
   else if(aIID.Equals(NS_GET_IID(nsIPrompt)))
      {
        nsCOMPtr<nsIPrompt> prompter(do_GetInterface(mTreeOwner));
        if (prompter)
        {
            *aSink = prompter;
            NS_ADDREF((nsISupports*)*aSink);
            return NS_OK;
        }
        else
            return NS_NOINTERFACE;
      }
   else if (aIID.Equals(NS_GET_IID(nsIProgressEventSink)) ||
            aIID.Equals(NS_GET_IID(nsIWebProgress)))
   {
     nsCOMPtr<nsIURILoader> uriLoader(do_GetService(NS_URI_LOADER_PROGID));
     NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);
     nsCOMPtr<nsIDocumentLoader> docLoader;
     NS_ENSURE_SUCCESS(uriLoader->GetDocumentLoaderForContext(NS_STATIC_CAST(nsIDocShell*, this),
      getter_AddRefs(docLoader)), NS_ERROR_FAILURE);  
     if (docLoader) {
       nsCOMPtr<nsIInterfaceRequestor> requestor(do_QueryInterface(docLoader));
       return requestor->GetInterface(aIID, aSink);
     }
     else
       return NS_ERROR_FAILURE;
   }
   else
      return QueryInterface(aIID, aSink);

   NS_IF_ADDREF(((nsISupports*)*aSink));
   return NS_OK;   
}

//*****************************************************************************
// nsDocShell::nsIDocShell
//*****************************************************************************   

NS_IMETHODIMP
nsDocShell::LoadURI(nsIURI* aURI, nsIDocShellLoadInfo* aLoadInfo)
{
  nsresult rv;
  nsCOMPtr<nsIURI> referrer;
  nsCOMPtr<nsISupports> owner;
  nsCOMPtr<nsISHEntry> shEntry;
  nsDocShellInfoLoadType loadType = nsIDocShellLoadInfo::loadNormal;

  NS_ENSURE_ARG(aURI);

  // Extract the info from the DocShellLoadInfo struct...
  if(aLoadInfo) {
    aLoadInfo->GetReferrer(getter_AddRefs(referrer));
    aLoadInfo->GetLoadType(&loadType);
    aLoadInfo->GetOwner(getter_AddRefs(owner));
    aLoadInfo->GetSHEntry(getter_AddRefs(shEntry));
  }

  if (!shEntry && loadType != nsIDocShellLoadInfo::loadNormalReplace) {
    /* Check if we are in the middle of loading a subframe whose parent
     * was originally loaded thro' Session History. ie., you were in a frameset
     * page, went somewhere else and clicked 'back'. The loading of the root page
     * is done and we are currently loading one of its children or sub-children.
     */
    nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
    GetSameTypeParent(getter_AddRefs(parentAsItem));

    // Try to get your SHEntry from your parent
    if (parentAsItem) {
      nsCOMPtr<nsIDocShellHistory> parent(do_QueryInterface(parentAsItem));

      // XXX: Should we care if this QI fails?
      if (parent) {
        parent->GetChildSHEntry(mChildOffset, getter_AddRefs(shEntry));
        if (shEntry) {
          loadType = nsIDocShellLoadInfo::loadHistory;
        }
      }
    }
  }

  if (shEntry) {
    rv = LoadHistoryEntry(shEntry, loadType);
  } else {
    rv = InternalLoad(aURI, referrer, owner, nsnull, nsnull, loadType, nsnull);
  }

  return rv;
}

NS_IMETHODIMP nsDocShell::LoadStream(nsIInputStream *aStream, nsIURI *aURI, 
                                     const char *aContentType, PRInt32 aContentLen,
                                     nsIDocShellLoadInfo* aLoadInfo)
{
   NS_ENSURE_ARG(aStream);
   NS_ENSURE_ARG(aContentType);
   NS_ENSURE_ARG(aContentLen);

   // if the caller doesn't pass in a URI we need to create a dummy URI. necko
   // currently requires a URI in various places during the load. Some consumers
   // do as well.
   nsCOMPtr<nsIURI> uri = aURI;
   if (!uri) {
      // HACK ALERT
      nsresult rv = NS_OK;
      uri = do_CreateInstance(kSimpleURICID, &rv);
      if (NS_FAILED(rv)) return rv;
      rv = uri->SetSpec("stream");
      if (NS_FAILED(rv)) return rv;
   }

   nsDocShellInfoLoadType loadType = nsIDocShellLoadInfo::loadNormal;

   if(aLoadInfo)
      (void)aLoadInfo->GetLoadType(&loadType);

   NS_ENSURE_SUCCESS(StopLoad(), NS_ERROR_FAILURE);
   // Cancel any timers that were set for this loader.
   (void)CancelRefreshURITimers();

   mLoadType = loadType;

   // build up a channel for this stream.
   nsCOMPtr<nsIChannel> channel;
   NS_ENSURE_SUCCESS(NS_NewInputStreamChannel(getter_AddRefs(channel), aURI, aStream, 
                                              aContentType, aContentLen), NS_ERROR_FAILURE);
   
   nsCOMPtr<nsIURILoader> uriLoader(do_GetService(NS_URI_LOADER_PROGID));
   NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);

   NS_ENSURE_SUCCESS(DoChannelLoad(channel, nsIURILoader::viewNormal, nsnull, uriLoader), NS_ERROR_FAILURE);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::CreateLoadInfo(nsIDocShellLoadInfo** aLoadInfo)
{
   nsDocShellLoadInfo* loadInfo = new nsDocShellLoadInfo();
   NS_ENSURE_TRUE(loadInfo, NS_ERROR_OUT_OF_MEMORY);
   nsCOMPtr<nsIDocShellLoadInfo> localRef(loadInfo);

   *aLoadInfo = localRef;
   NS_ADDREF(*aLoadInfo);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::StopLoad()
{
   // Cancel any timers that were set for this loader.
   CancelRefreshURITimers();

   if(mLoadCookie)
      {
      nsCOMPtr<nsIURILoader> uriLoader = do_GetService(NS_URI_LOADER_PROGID);
      if(uriLoader)
         uriLoader->Stop(mLoadCookie);
      }

   PRInt32 n;
   PRInt32 count = mChildren.Count();
   for(n = 0; n < count; n++)
      {
      nsIDocShellTreeItem* shellItem = (nsIDocShellTreeItem*)mChildren.ElementAt(n);
      nsCOMPtr<nsIDocShell> shell(do_QueryInterface(shellItem));
      if(shell)
         shell->StopLoad();
      }

   return NS_OK;
}

NS_IMETHODIMP
nsDocShell::SetDocument(nsIDOMDocument *aDOMDoc, nsIDOMElement *aRootNode)
{
  /* XXX: This method is obsolete and will be removed. */
  return NS_ERROR_FAILURE;
}

NS_IMETHODIMP nsDocShell::GetCurrentURI(nsIURI** aURI)
{
   NS_ENSURE_ARG_POINTER(aURI);
   
   *aURI = mCurrentURI;
   NS_IF_ADDREF(*aURI);

   return NS_OK;
}

NS_IMETHODIMP 
nsDocShell::GetDocLoaderObserver(nsIDocumentLoaderObserver * *aDocLoaderObserver)
{
  NS_ENSURE_ARG_POINTER(aDocLoaderObserver);

  *aDocLoaderObserver = mDocLoaderObserver;
  NS_IF_ADDREF(*aDocLoaderObserver);
  return NS_OK;
}

NS_IMETHODIMP 
nsDocShell::SetDocLoaderObserver(nsIDocumentLoaderObserver * aDocLoaderObserver)
{
  // it's legal for aDocLoaderObserver to be null.  
  mDocLoaderObserver = aDocLoaderObserver;
  return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetPresContext(nsIPresContext** aPresContext)
{
  nsresult rv = NS_OK;

  NS_ENSURE_ARG_POINTER(aPresContext);
  *aPresContext = nsnull;

  if (mContentViewer) {
    nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(mContentViewer));

    if (docv) {
      rv = docv->GetPresContext(*aPresContext);
    }
  }

  // Fail silently, if no PresContext is available...
  return rv;
}

NS_IMETHODIMP nsDocShell::GetPresShell(nsIPresShell** aPresShell)
{
  nsresult rv = NS_OK;

  NS_ENSURE_ARG_POINTER(aPresShell);
  *aPresShell = nsnull;
   
  nsCOMPtr<nsIPresContext> presContext;
  (void) GetPresContext(getter_AddRefs(presContext));

  if(presContext) {
    rv = presContext->GetShell(aPresShell);
  }

  return rv;
}

NS_IMETHODIMP nsDocShell::GetContentViewer(nsIContentViewer** aContentViewer)
{
   NS_ENSURE_ARG_POINTER(aContentViewer);

   *aContentViewer = mContentViewer;
   NS_IF_ADDREF(*aContentViewer);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetChromeEventHandler(nsIChromeEventHandler* aChromeEventHandler)
{
   // Weak reference. Don't addref.
   mChromeEventHandler = aChromeEventHandler;
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetChromeEventHandler(nsIChromeEventHandler** aChromeEventHandler)
{
   NS_ENSURE_ARG_POINTER(aChromeEventHandler);

   *aChromeEventHandler = mChromeEventHandler;
   NS_IF_ADDREF(*aChromeEventHandler);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetParentURIContentListener(nsIURIContentListener**
   aParent)
{
   NS_ENSURE_ARG_POINTER(aParent);
   NS_ENSURE_SUCCESS(EnsureContentListener(), NS_ERROR_FAILURE);

   return mContentListener->GetParentContentListener(aParent);
}

NS_IMETHODIMP nsDocShell::SetParentURIContentListener(nsIURIContentListener*
   aParent)
{
   NS_ENSURE_SUCCESS(EnsureContentListener(), NS_ERROR_FAILURE);

   return mContentListener->SetParentContentListener(aParent);
}

NS_IMETHODIMP nsDocShell::GetDocumentCharsetInfo(nsIDocumentCharsetInfo** 
   aDocumentCharsetInfo)
{
   NS_ENSURE_ARG_POINTER(aDocumentCharsetInfo);

   *aDocumentCharsetInfo = mDocumentCharsetInfo;
   NS_IF_ADDREF(*aDocumentCharsetInfo);
   return NS_OK;
} 

NS_IMETHODIMP nsDocShell::SetDocumentCharsetInfo(nsIDocumentCharsetInfo* 
   aDocumentCharsetInfo)
{
   mDocumentCharsetInfo = aDocumentCharsetInfo;
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetAllowPlugins(PRBool* aAllowPlugins)
{
   NS_ENSURE_ARG_POINTER(aAllowPlugins);

   *aAllowPlugins = mAllowPlugins;
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetAllowPlugins(PRBool aAllowPlugins)
{
   mAllowPlugins = aAllowPlugins;
   //XXX should enable or disable a plugin host
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetViewMode(PRInt32* aViewMode)
{
   NS_ENSURE_ARG_POINTER(aViewMode);

   *aViewMode = mViewMode;
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetViewMode(PRInt32 aViewMode)
{
   NS_ENSURE_ARG((viewNormal == aViewMode) || (viewSource == aViewMode));

   PRBool reload = PR_FALSE;

   if((mViewMode != aViewMode) && mCurrentURI)
      reload = PR_TRUE;

   mViewMode = aViewMode;

   if(reload)
	   Reload(nsIDocShellLoadInfo::loadReloadNormal);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetZoom(float* zoom)
{
   NS_ENSURE_ARG_POINTER(zoom);
   NS_ENSURE_SUCCESS(EnsureDeviceContext(), NS_ERROR_FAILURE);

   NS_ENSURE_SUCCESS(mDeviceContext->GetZoom(*zoom), NS_ERROR_FAILURE);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetZoom(float zoom)
{
   NS_ENSURE_SUCCESS(EnsureDeviceContext(), NS_ERROR_FAILURE);
   mDeviceContext->SetZoom(zoom);

   // get the pres shell
   nsCOMPtr<nsIPresShell> presShell;
   NS_ENSURE_SUCCESS(GetPresShell(getter_AddRefs(presShell)), NS_ERROR_FAILURE);
   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);

   // get the view manager
   nsCOMPtr<nsIViewManager> vm;
   NS_ENSURE_SUCCESS(presShell->GetViewManager(getter_AddRefs(vm)), NS_ERROR_FAILURE);
   NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);

   // get the root scrollable view
   nsIScrollableView* scrollableView = nsnull;
   vm->GetRootScrollableView(&scrollableView);
   if(scrollableView)
      scrollableView->ComputeScrollOffsets();

   // get the root view
   nsIView *rootView=nsnull; // views are not ref counted
   vm->GetRootView(rootView);
   if(rootView)
      vm->UpdateView(rootView, 0);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetMarginWidth(PRInt32* aWidth)
{
  NS_ENSURE_ARG_POINTER(aWidth);

  *aWidth = mMarginWidth;
  return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetMarginWidth(PRInt32 aWidth)
{
  mMarginWidth = aWidth;
  return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetMarginHeight(PRInt32* aHeight)
{
  NS_ENSURE_ARG_POINTER(aHeight);

  *aHeight = mMarginHeight;
  return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetMarginHeight(PRInt32 aHeight)
{
  mMarginHeight = aHeight;
  return NS_OK;
}

//*****************************************************************************
// nsDocShell::nsIDocShellTreeItem
//*****************************************************************************   

NS_IMETHODIMP nsDocShell::GetName(PRUnichar** aName)
{
   NS_ENSURE_ARG_POINTER(aName);
   *aName = mName.ToNewUnicode();
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetName(const PRUnichar* aName)
{
   mName = aName;  // this does a copy of aName
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetItemType(PRInt32* aItemType)
{
   NS_ENSURE_ARG_POINTER(aItemType);

   *aItemType = mItemType;
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetItemType(PRInt32 aItemType)
{
   NS_ENSURE_ARG((aItemType == typeChrome) || (typeContent == aItemType));
   NS_ENSURE_STATE(!mParent);

   mItemType = aItemType;

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetParent(nsIDocShellTreeItem** aParent)
{
   NS_ENSURE_ARG_POINTER(aParent);

   *aParent = mParent;
   NS_IF_ADDREF(*aParent);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetParent(nsIDocShellTreeItem* aParent)
{
  // null aParent is ok
   /*
   Note this doesn't do an addref on purpose.  This is because the parent
   is an implied lifetime.  We don't want to create a cycle by refcounting
   the parent.
   */
   mParent = aParent;

   nsCOMPtr<nsIURIContentListener> parentURIListener(do_GetInterface(aParent));
   if(parentURIListener)
      SetParentURIContentListener(parentURIListener);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetSameTypeParent(nsIDocShellTreeItem** aParent)
{
   NS_ENSURE_ARG_POINTER(aParent);
   *aParent = nsnull;

   if(!mParent)
      return NS_OK;
      
   PRInt32  parentType;
   NS_ENSURE_SUCCESS(mParent->GetItemType(&parentType), NS_ERROR_FAILURE);

   if(parentType == mItemType)
      {
      *aParent = mParent;
      NS_ADDREF(*aParent);
      }
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetRootTreeItem(nsIDocShellTreeItem** aRootTreeItem)
{
   NS_ENSURE_ARG_POINTER(aRootTreeItem);
   *aRootTreeItem = NS_STATIC_CAST(nsIDocShellTreeItem*, this);

   nsCOMPtr<nsIDocShellTreeItem> parent;
   NS_ENSURE_SUCCESS(GetParent(getter_AddRefs(parent)), NS_ERROR_FAILURE);
   while(parent)
      {
      *aRootTreeItem = parent;
      NS_ENSURE_SUCCESS((*aRootTreeItem)->GetParent(getter_AddRefs(parent)), NS_ERROR_FAILURE);
      }
   NS_ADDREF(*aRootTreeItem);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem** aRootTreeItem)
{
   NS_ENSURE_ARG_POINTER(aRootTreeItem);
   *aRootTreeItem = NS_STATIC_CAST(nsIDocShellTreeItem*, this);

   nsCOMPtr<nsIDocShellTreeItem> parent;
   NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parent)), NS_ERROR_FAILURE);
   while(parent)
      {
      *aRootTreeItem = parent;
      NS_ENSURE_SUCCESS((*aRootTreeItem)->GetSameTypeParent(getter_AddRefs(parent)), 
         NS_ERROR_FAILURE);
      }
   NS_ADDREF(*aRootTreeItem);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::FindItemWithName(const PRUnichar *aName, 
   nsISupports* aRequestor, nsIDocShellTreeItem **_retval)
{
   NS_ENSURE_ARG(aName);
   NS_ENSURE_ARG_POINTER(_retval);
  
   *_retval = nsnull;  // if we don't find one, we return NS_OK and a null result 
   
   // This QI may fail, but the places where we want to compare, comparing
   // against nsnull serves the same purpose.
   nsCOMPtr<nsIDocShellTreeItem> reqAsTreeItem(do_QueryInterface(aRequestor));

   // First we check our name.
   if(mName.EqualsWithConversion(aName))
      {
      *_retval = NS_STATIC_CAST(nsIDocShellTreeItem*, this);
      NS_ADDREF(*_retval);
      return NS_OK;
      }

   // Second we check our children making sure not to ask a child if it
   // is the aRequestor.
   NS_ENSURE_SUCCESS(FindChildWithName(aName, PR_TRUE, PR_TRUE, reqAsTreeItem,
      _retval), 
      NS_ERROR_FAILURE);
   if(*_retval)
      return NS_OK;

   // Third if we have a parent and it isn't the requestor then we should ask
   // it to do the search.  If it is the requestor we should just stop here
   // and let the parent do the rest.
   // If we don't have a parent, then we should ask the docShellTreeOwner to do
   // the search.
   if(mParent)
      {
      if(mParent == reqAsTreeItem.get())
         return NS_OK;

      PRInt32 parentType;
      mParent->GetItemType(&parentType);
      if(parentType == mItemType)
         {
         NS_ENSURE_SUCCESS(mParent->FindItemWithName(aName,
            NS_STATIC_CAST(nsIDocShellTreeItem*, this), _retval), 
            NS_ERROR_FAILURE);
         return NS_OK;
         }
      // If the parent isn't of the same type fall through and ask tree owner.
      }

   // This QI may fail, but comparing against null serves the same purpose
   nsCOMPtr<nsIDocShellTreeOwner> reqAsTreeOwner(do_QueryInterface(aRequestor));

   if(mTreeOwner && (mTreeOwner != reqAsTreeOwner.get()))
      {
      NS_ENSURE_SUCCESS(mTreeOwner->FindItemWithName(aName, 
         NS_STATIC_CAST(nsIDocShellTreeItem*, this), _retval),
         NS_ERROR_FAILURE);
      }

  return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetTreeOwner(nsIDocShellTreeOwner** aTreeOwner)
{
   NS_ENSURE_ARG_POINTER(aTreeOwner);

   *aTreeOwner = mTreeOwner;
   NS_IF_ADDREF(*aTreeOwner);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetTreeOwner(nsIDocShellTreeOwner* aTreeOwner)
{
   // Don't automatically set the progress based on the tree owner for frames
  if (!IsFrame()) {
    nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(mLoadCookie));
    
    if (webProgress) {
      nsCOMPtr<nsIWebProgressListener> oldListener(do_QueryInterface(mTreeOwner));
      nsCOMPtr<nsIWebProgressListener> newListener(do_QueryInterface(aTreeOwner));

      if (oldListener) {
        webProgress->RemoveProgressListener(oldListener);
      }

      if (newListener) {
        webProgress->AddProgressListener(newListener);
      }
    }
  }

   mTreeOwner = aTreeOwner; // Weak reference per API

   PRInt32 i, n = mChildren.Count();
   for(i = 0; i < n; i++)
      {
      nsIDocShellTreeItem* child = (nsIDocShellTreeItem*) mChildren.ElementAt(i); // doesn't addref the result
      NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
      PRInt32 childType = ~mItemType; // Set it to not us in case the get fails
      child->GetItemType(&childType); // We don't care if this fails, if it does we won't set the owner
      if(childType == mItemType)
         child->SetTreeOwner(aTreeOwner);
      }

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetChildOffset(PRInt32 aChildOffset)
{
  mChildOffset = aChildOffset;
  return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetChildOffset(PRInt32 *aChildOffset)
{
  NS_ENSURE_ARG_POINTER(aChildOffset);
  *aChildOffset = mChildOffset;
  return NS_OK;
}

//*****************************************************************************
// nsDocShell::nsIDocShellTreeNode
//*****************************************************************************   

NS_IMETHODIMP nsDocShell::GetChildCount(PRInt32 *aChildCount)
{
  NS_ENSURE_ARG_POINTER(aChildCount);
  *aChildCount = mChildren.Count();
  return NS_OK;
}



NS_IMETHODIMP nsDocShell::AddChild(nsIDocShellTreeItem *aChild)
{
   NS_ENSURE_ARG_POINTER(aChild);

   NS_ENSURE_SUCCESS(aChild->SetParent(this), NS_ERROR_FAILURE);
   mChildren.AppendElement(aChild);
   NS_ADDREF(aChild);

   // 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 hierarchyand SH hierarchy won't match.
   PRInt32 childCount = mChildren.Count();
   aChild->SetChildOffset(childCount-1);

   /* Set the child's global history if the parent has one */
   if (mGlobalHistory) {
      nsCOMPtr<nsIDocShellHistory> dsHistoryChild(do_QueryInterface(aChild));
      if (dsHistoryChild)
 	     dsHistoryChild->SetGlobalHistory(mGlobalHistory);
   }
 

   PRInt32 childType = ~mItemType; // Set it to not us in case the get fails
   aChild->GetItemType(&childType);
   if(childType != mItemType)
      return NS_OK;    
   // Everything below here is only done when the child is the same type.


   aChild->SetTreeOwner(mTreeOwner);

   nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
   if(!childAsDocShell)
      return NS_OK;

   // Do some docShell Specific stuff.
   nsXPIDLString defaultCharset;
   nsXPIDLString forceCharset;
   NS_ENSURE_TRUE(mContentViewer, NS_ERROR_FAILURE);

   nsCOMPtr<nsIMarkupDocumentViewer> muDV = do_QueryInterface(mContentViewer);
   if(muDV)
      {
      NS_ENSURE_SUCCESS(muDV->GetDefaultCharacterSet(getter_Copies(defaultCharset)),
         NS_ERROR_FAILURE);
      NS_ENSURE_SUCCESS(muDV->GetForceCharacterSet(getter_Copies(forceCharset)),
         NS_ERROR_FAILURE);
      }
   nsCOMPtr<nsIContentViewer> childCV;
   NS_ENSURE_SUCCESS(childAsDocShell->GetContentViewer(getter_AddRefs(childCV)),
      NS_ERROR_FAILURE);
   if(childCV)
      {
      nsCOMPtr<nsIMarkupDocumentViewer> childmuDV = do_QueryInterface(childCV);
      if(childmuDV)
         {
         NS_ENSURE_SUCCESS(childmuDV->SetDefaultCharacterSet(defaultCharset), 
            NS_ERROR_FAILURE);
         NS_ENSURE_SUCCESS(childmuDV->SetForceCharacterSet(forceCharset), 
            NS_ERROR_FAILURE);
         }
      }

  return NS_OK;
}

NS_IMETHODIMP nsDocShell::RemoveChild(nsIDocShellTreeItem *aChild)
{
   NS_ENSURE_ARG_POINTER(aChild);

   if(mChildren.RemoveElement(aChild))
      {
      aChild->SetParent(nsnull);
      aChild->SetTreeOwner(nsnull);
      NS_RELEASE(aChild);
      }
   else
      NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetChildAt(PRInt32 aIndex, nsIDocShellTreeItem** aChild)
{
   NS_ENSURE_ARG_POINTER(aChild);
   NS_ENSURE_ARG_RANGE(aIndex, 0, mChildren.Count() - 1);

   *aChild = (nsIDocShellTreeItem*) mChildren.ElementAt(aIndex);
   NS_IF_ADDREF(*aChild);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::FindChildWithName(const PRUnichar *aName, 
   PRBool aRecurse, PRBool aSameType, nsIDocShellTreeItem* aRequestor, 
   nsIDocShellTreeItem **_retval)
{
   NS_ENSURE_ARG(aName);
   NS_ENSURE_ARG_POINTER(_retval);
  
   *_retval = nsnull;  // if we don't find one, we return NS_OK and a null result 

   nsAutoString name(aName);
   nsXPIDLString childName;
   PRInt32 i, n = mChildren.Count();
   for(i = 0; i < n; i++) 
      {
      nsIDocShellTreeItem* child = (nsIDocShellTreeItem*) mChildren.ElementAt(i); // doesn't addref the result
      NS_ENSURE_TRUE(child, NS_ERROR_FAILURE);
      PRInt32 childType;
      child->GetItemType(&childType);
      
      if(aSameType && (childType != mItemType))
         continue;

      child->GetName(getter_Copies(childName));
      if(name.EqualsWithConversion(childName))
         {
         *_retval = child;
         NS_ADDREF(*_retval);
         break;
         }

      if(childType != mItemType) //Only ask it to check children if it is same type
         continue;

      if(aRecurse && (aRequestor != child)) // Only ask the child if it isn't the requestor
         {
         // See if child contains the shell with the given name
         nsCOMPtr<nsIDocShellTreeNode> childAsNode(do_QueryInterface(child));
         if(child)
            {
            NS_ENSURE_SUCCESS(childAsNode->FindChildWithName(aName, PR_TRUE, 
            aSameType, NS_STATIC_CAST(nsIDocShellTreeItem*, this), _retval),
               NS_ERROR_FAILURE);
            }
         }
      if(*_retval)   // found it
         return NS_OK;
      }
   return NS_OK;
}

//*****************************************************************************
// nsDocShell::nsIDocShellHistory
//*****************************************************************************   
NS_IMETHODIMP
nsDocShell::GetChildSHEntry(PRInt32 aChildOffset, nsISHEntry ** aResult)
{
  nsresult rv = NS_OK;

  NS_ENSURE_ARG_POINTER(aResult);
  *aResult = nsnull;
  
  //
  // A nsISHEntry for a child is *only* available when the parent is in
  // the progress of loading a document too...
  //
  if (LSHE) {
    nsCOMPtr<nsISHContainer> container(do_QueryInterface(LSHE));
    if (container) {
      rv = container->GetChildAt(aChildOffset, aResult);
    }
  }
  return rv;
}

NS_IMETHODIMP
nsDocShell::AddChildSHEntry(nsISHEntry * aCloneRef, nsISHEntry * aNewEntry,
                            PRInt32 aChildOffset)
{
  nsresult rv;
  
  if (LSHE) {
    /* You get here if you are currently building a 
     * hierarchy ie.,you just visited a frameset page
     */
    nsCOMPtr<nsISHContainer>  container(do_QueryInterface(LSHE, &rv));
    if(container)
      rv = container->AddChild(aNewEntry, aChildOffset);
  }
  else if (mSessionHistory) {
    /* You are currently in the rootDocShell.
     * You will get here when a subframe has a new url
     * to load and you have walked up the tree all the 
     * way to the top  
     */
    PRInt32 index=-1;
    nsCOMPtr<nsISHEntry> currentEntry;
    mSessionHistory->GetIndex(&index);
    if (index < 0)
      return NS_ERROR_FAILURE;

    rv = mSessionHistory->GetEntryAtIndex(index, PR_FALSE,
                                          getter_AddRefs(currentEntry));
    if (currentEntry) {
      nsCOMPtr<nsISHEntry> nextEntry; //(do_CreateInstance(NS_SHENTRY_PROGID));
      //   NS_ENSURE_TRUE(result, NS_ERROR_FAILURE);
      rv = CloneAndReplace(currentEntry, aCloneRef, aNewEntry,
                           getter_AddRefs(nextEntry));
      
      if (NS_SUCCEEDED(rv)) {
        rv = mSessionHistory->AddEntry(nextEntry, PR_TRUE);
      }
    }
  }
  else {
    /* You will get here when you are in a subframe and
     * a new url has been loaded on you. 
     * The OSHE in this subframe will be the previous url's
     * OSHE. This OSHE will be used as the identification
     * for this subframe in the  CloneAndReplace function.
     */

    nsCOMPtr<nsIDocShellHistory> parent(do_QueryInterface(mParent, &rv));
    if (parent) {
      if (!aCloneRef) {
        aCloneRef = OSHE;
      }
      rv = parent->AddChildSHEntry(aCloneRef, aNewEntry, aChildOffset);
    }

  }
  return rv;
}

NS_IMETHODIMP nsDocShell::SetGlobalHistory(nsIGlobalHistory* aGlobalHistory)
{
    mGlobalHistory = aGlobalHistory; 
    return NS_OK;
}
 	
NS_IMETHODIMP nsDocShell::GetGlobalHistory(nsIGlobalHistory** aGlobalHistory)
{
    NS_ENSURE_ARG_POINTER(aGlobalHistory);
 
    *aGlobalHistory = mGlobalHistory;
    NS_IF_ADDREF(*aGlobalHistory);
    return NS_OK;
}

//*****************************************************************************
// nsDocShell::nsIWebNavigation
//*****************************************************************************   

NS_IMETHODIMP nsDocShell::GetCanGoBack(PRBool* aCanGoBack)
{
#ifndef SH_IN_FRAMES
   NS_ENSURE_ARG_POINTER(aCanGoBack);
   *aCanGoBack = PR_FALSE;
   if (mSessionHistory == nsnull) {
      return NS_OK;
   }
   
   NS_ENSURE_STATE(mSessionHistory);  

   PRInt32 index = -1;
   NS_ENSURE_SUCCESS(mSessionHistory->GetIndex(&index), NS_ERROR_FAILURE);
   if(index > 0)
      *aCanGoBack = PR_TRUE;
#else
  if (mSessionHistory) {
    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mSessionHistory));

    if (webNav) {
      return webNav->GetCanGoBack(aCanGoBack);
    }
  }
  else {
    nsCOMPtr<nsIDocShellTreeItem> root;
	GetSameTypeRootTreeItem(getter_AddRefs(root));
	NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
    nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));  
    if (rootAsWebnav) {
       rootAsWebnav->GetCanGoBack(aCanGoBack);
 	}
  }
#endif 
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetCanGoForward(PRBool* aCanGoForward)
{
#ifndef SH_IN_FRAMES
   NS_ENSURE_ARG_POINTER(aCanGoForward);
   *aCanGoForward = PR_FALSE;
   if (mSessionHistory == nsnull) {
      return NS_OK;
   }

   NS_ENSURE_STATE(mSessionHistory);  

   PRInt32 index = -1;
   PRInt32 count = -1;

   NS_ENSURE_SUCCESS(mSessionHistory->GetIndex(&index), NS_ERROR_FAILURE);
   NS_ENSURE_SUCCESS(mSessionHistory->GetCount(&count), NS_ERROR_FAILURE);

   if((index >= 0) && (index < (count - 1)))
      *aCanGoForward = PR_TRUE;
#else
  if (mSessionHistory) {
    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mSessionHistory));

    if (webNav) {
      return webNav->GetCanGoForward(aCanGoForward);
    }
  }
  else {
	nsCOMPtr<nsIDocShellTreeItem> root;
	GetSameTypeRootTreeItem(getter_AddRefs(root));
	NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
    nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));  
    if (rootAsWebnav) {
       rootAsWebnav->GetCanGoForward(aCanGoForward);
 	}
  }
#endif 
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GoBack()
{
#ifndef SH_IN_FRAMES
   if (mSessionHistory == nsnull) {
      return NS_OK;
   }
   nsCOMPtr<nsIDocShellTreeItem> root;
   GetSameTypeRootTreeItem(getter_AddRefs(root));
   if(root.get() != NS_STATIC_CAST(nsIDocShellTreeItem*, this))
      {
      nsCOMPtr<nsIWebNavigation> rootAsNav(do_QueryInterface(root));
      return rootAsNav->GoBack();
      }

   NS_ENSURE_STATE(mSessionHistory);

 
   UpdateCurrentSessionHistory();  

   nsCOMPtr<nsISHEntry> previousEntry;

   NS_ENSURE_SUCCESS(mSessionHistory->GetPreviousEntry(PR_TRUE, 
      getter_AddRefs(previousEntry)), NS_ERROR_FAILURE);
   NS_ENSURE_TRUE(previousEntry, NS_ERROR_FAILURE);

  
   NS_ENSURE_SUCCESS(LoadHistoryEntry(previousEntry), NS_ERROR_FAILURE);
#else
  if (mSessionHistory) {
    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mSessionHistory));

    if (webNav) {
      return webNav->GoBack();
    }
  }
  else {
	  	nsCOMPtr<nsIDocShellTreeItem> root;
	GetSameTypeRootTreeItem(getter_AddRefs(root));
	NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
    nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));  
    if (rootAsWebnav) {
       rootAsWebnav->GoBack();
 	}
  }
#endif 
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GoForward()
{
#ifndef SH_IN_FRAMES
   if (mSessionHistory == nsnull) {
      return NS_OK;
   }
   nsCOMPtr<nsIDocShellTreeItem> root;
   GetSameTypeRootTreeItem(getter_AddRefs(root));
   if(root.get() != NS_STATIC_CAST(nsIDocShellTreeItem*, this))
      {
      nsCOMPtr<nsIWebNavigation> rootAsNav(do_QueryInterface(root));
      return rootAsNav->GoForward();
      }

   NS_ENSURE_STATE(mSessionHistory);
   
   UpdateCurrentSessionHistory();  

   nsCOMPtr<nsISHEntry> nextEntry;

   NS_ENSURE_SUCCESS(mSessionHistory->GetNextEntry(PR_TRUE, 
      getter_AddRefs(nextEntry)), NS_ERROR_FAILURE);
   NS_ENSURE_TRUE(nextEntry, NS_ERROR_FAILURE);
   

   NS_ENSURE_SUCCESS(LoadHistoryEntry(nextEntry), NS_ERROR_FAILURE);
#else
  if (mSessionHistory) {
    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mSessionHistory));

    if (webNav) {
      return webNav->GoForward();
    }
  }
  else {
	  	nsCOMPtr<nsIDocShellTreeItem> root;
	GetSameTypeRootTreeItem(getter_AddRefs(root));
	NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
    nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));  
    if (rootAsWebnav) {
       rootAsWebnav->GoForward();
 	}
  }
#endif 
   return NS_OK;
}

NS_IMETHODIMP
nsDocShell::GotoIndex(PRInt32 aIndex)
{
  if (mSessionHistory) {
      nsCOMPtr<nsIWebNavigation>webNav(do_QueryInterface(mSessionHistory));
      if (webNav) {
          return webNav->GotoIndex(aIndex);
	  }
  }
  else {
 	nsCOMPtr<nsIDocShellTreeItem> root;
	GetSameTypeRootTreeItem(getter_AddRefs(root));
	NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
    nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));  
    if (rootAsWebnav) {
       rootAsWebnav->GotoIndex(aIndex);
 	}
  } 
	return NS_OK;
}


NS_IMETHODIMP nsDocShell::LoadURI(const PRUnichar* aURI)
{
   nsCOMPtr<nsIURI> uri;

   nsresult rv = CreateFixupURI(aURI, getter_AddRefs(uri));

   if(NS_ERROR_UNKNOWN_PROTOCOL == rv)
      {
      // we weren't able to find a protocol handler
      nsCOMPtr<nsIPrompt> prompter;
      nsCOMPtr<nsIStringBundle> stringBundle;
      GetPromptAndStringBundle(getter_AddRefs(prompter), 
         getter_AddRefs(stringBundle));

      NS_ENSURE_TRUE(stringBundle, NS_ERROR_FAILURE);

      nsXPIDLString messageStr;
      NS_ENSURE_SUCCESS(stringBundle->GetStringFromName(NS_ConvertASCIItoUCS2("protocolNotFound").GetUnicode(), 
         getter_Copies(messageStr)), NS_ERROR_FAILURE);

      nsAutoString uriString(aURI);
      PRInt32 colon = uriString.FindChar(':');
      // extract the scheme
      nsAutoString scheme;
      uriString.Left(scheme, colon);
      nsCAutoString cScheme;
      cScheme.AssignWithConversion(scheme);

      PRUnichar *msg = nsTextFormatter::smprintf(messageStr, cScheme.GetBuffer());
      if (!msg) return NS_ERROR_OUT_OF_MEMORY;

      prompter->Alert(nsnull, msg);
      nsTextFormatter::smprintf_free(msg);
      } // end unknown protocol

   if(!uri)
      return NS_ERROR_FAILURE;

   NS_ENSURE_SUCCESS(LoadURI(uri, nsnull), NS_ERROR_FAILURE);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::Reload(PRInt32 aReloadType)
{  
#ifdef SH_IN_FRAMES
   // XXX Honor the reload type
   //NS_ENSURE_STATE(mCurrentURI);

   // XXXTAB Convert reload type to our type
   nsDocShellInfoLoadType type = nsIDocShellLoadInfo::loadReloadNormal;
   if ( aReloadType == nsIWebNavigation::loadReloadBypassProxyAndCache )
   	type = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
#if 0
   nsCOMPtr<nsISHEntry> entry;
   if (OSHE) {
	   /* We should fall here in most cases including subframes & refreshes */
	   entry = OSHE;
   } else if (mSessionHistory) {
	   /* In case we fail above, as a last ditch effort, we 
	    * reload the whole page.
		*/
	  PRInt32 index = -1;
      NS_ENSURE_SUCCESS(mSessionHistory->GetIndex(&index), NS_ERROR_FAILURE);
      NS_ENSURE_SUCCESS(mSessionHistory->GetEntryAtIndex(index, PR_FALSE,
          getter_AddRefs(entry)), NS_ERROR_FAILURE);
      NS_ENSURE_TRUE(entry, NS_ERROR_FAILURE);
   }
   else {
	   //May be one of those <META> charset reloads in a composer or Messenger
      return InternalLoad(mCurrentURI, mReferrerURI, nsnull, nsnull, 
      nsnull, type);

   }
#else
   // OK. Atleast for the heck of it, pollmann says that he doesn't crash
   // in bug 45297 if he just did the following, instead of the one in #if 0.
   // If this really keeps the crash from re-occuring, may be this can stay. However 
   // there is no major difference between this one and the one inside #if 0
   
   return InternalLoad(mCurrentURI, mReferrerURI, nsnull, nsnull, 
      nsnull, type);
#endif /* 0 */

	   
  // return LoadHistoryEntry(entry, type);


#else

   // XXX Honor the reload type
   NS_ENSURE_STATE(mCurrentURI);

   // XXXTAB Convert reload type to our type
   nsDocShellInfoLoadType type = nsIDocShellLoadInfo::loadReloadNormal;
   if ( aReloadType == nsIWebNavigation::loadReloadBypassProxyAndCache )
   	type = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;

   UpdateCurrentSessionHistory();

   NS_ENSURE_SUCCESS(InternalLoad(mCurrentURI, mReferrerURI, nsnull, nsnull, 
      nsnull, type), NS_ERROR_FAILURE);
   return NS_OK;
#endif  /* SH_IN_FRAMES  */
 
}

NS_IMETHODIMP nsDocShell::Stop()
{
   // Cancel any timers that were set for this loader.
   CancelRefreshURITimers();

   if(mContentViewer)
      mContentViewer->Stop();

   if(mLoadCookie)
      {
      nsCOMPtr<nsIURILoader> uriLoader = do_GetService(NS_URI_LOADER_PROGID);
      if(uriLoader)
         uriLoader->Stop(mLoadCookie);
      }

   PRInt32 n;
   PRInt32 count = mChildren.Count();
   for(n = 0; n < count; n++)
      {
      nsIDocShellTreeItem* shell = (nsIDocShellTreeItem*)mChildren.ElementAt(n);
      nsCOMPtr<nsIWebNavigation> shellAsNav(do_QueryInterface(shell));
      if(shellAsNav)
         shellAsNav->Stop();
      }

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetDocument(nsIDOMDocument* aDocument,
   const PRUnichar* aContentType)
{
   //XXX First Checkin
   NS_ERROR("Not Yet Implemented");
   return NS_ERROR_FAILURE;
}

NS_IMETHODIMP nsDocShell::GetDocument(nsIDOMDocument** aDocument)
{
  NS_ENSURE_ARG_POINTER(aDocument);
  NS_ENSURE_STATE(mContentViewer);

  return mContentViewer->GetDOMDocument(aDocument);
}
	
NS_IMETHODIMP nsDocShell::GetCurrentURI(PRUnichar** aCurrentURI)
{
   NS_ENSURE_ARG_POINTER(aCurrentURI);
   
   if(!mCurrentURI)
      {
      *aCurrentURI = nsnull;
      return NS_OK;
      }

   char* spec;
   NS_ENSURE_SUCCESS(mCurrentURI->GetSpec(&spec), NS_ERROR_FAILURE);

   *aCurrentURI = NS_ConvertASCIItoUCS2(spec).ToNewUnicode();

   if(spec)
      nsCRT::free(spec);

   return NS_OK;
}
	
NS_IMETHODIMP nsDocShell::SetSessionHistory(nsISHistory* aSessionHistory)
{
   mSessionHistory = aSessionHistory;

#if defined(SH_IN_FRAMES)
   if (mSessionHistory) {
     mSessionHistory->SetRootDocShell(this);
   }
#endif /* SH_IN_FRAMES */

   return NS_OK;
}
	
NS_IMETHODIMP nsDocShell::GetSessionHistory(nsISHistory** aSessionHistory)
{
   NS_ENSURE_ARG_POINTER(aSessionHistory);
   if (mSessionHistory) {
     *aSessionHistory = mSessionHistory;
     NS_IF_ADDREF(*aSessionHistory);
   }
   else {
	nsCOMPtr<nsIDocShellTreeItem> root;
	GetSameTypeRootTreeItem(getter_AddRefs(root));
	NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
    nsCOMPtr<nsIWebNavigation> rootAsWebnav(do_QueryInterface(root));  
    if (rootAsWebnav) {
       rootAsWebnav->GetSessionHistory(aSessionHistory);
 	}
   }
   return NS_OK;
}
//*****************************************************************************
// nsDocShell::nsIBaseWindow
//*****************************************************************************   

NS_IMETHODIMP nsDocShell::InitWindow(nativeWindow parentNativeWindow,
   nsIWidget* parentWidget, PRInt32 x, PRInt32 y, PRInt32 cx, PRInt32 cy)   
{
   NS_ENSURE_ARG(parentWidget);  // DocShells must get a widget for a parent

   SetParentWidget(parentWidget);
   SetPositionAndSize(x, y, cx, cy, PR_FALSE);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::Create()
{
   NS_ENSURE_STATE(!mContentViewer);
   mPrefs = do_GetService(NS_PREF_PROGID);
   //GlobalHistory is now set in SetGlobalHistory
 //  mGlobalHistory = do_GetService(NS_GLOBALHISTORY_PROGID);

   // i don't want to read this pref in every time we load a url
   // so read it in once here and be done with it...
   mPrefs->GetBoolPref("network.protocols.useSystemDefaults", &mUseExternalProtocolHandler);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::Destroy()
{
   // Stop any URLs that are currently being loaded...
   Stop();
   if(mDocLoader)
      {
      mDocLoader->Destroy();
      mDocLoader->SetContainer(nsnull);
      }

   SetDocLoaderObserver(nsnull);

  // Save the state of the current document, before destroying the window.
  // This is needed to capture the state of a frameset when the new document
  // causes the frameset to be destroyed...
  PersistLayoutHistoryState();

   // Remove this docshell from its parent's child list
   nsCOMPtr<nsIDocShellTreeNode> docShellParentAsNode(do_QueryInterface(mParent));
   if(docShellParentAsNode)
      docShellParentAsNode->RemoveChild(this);

   mContentViewer = nsnull;

   DestroyChildren();

   mDocLoader = nsnull;
   mDocLoaderObserver = nsnull;
   mParentWidget = nsnull;
   mPrefs = nsnull;
   mCurrentURI = nsnull;

   if(mScriptGlobal)
      {
      mScriptGlobal->SetDocShell(nsnull);
      mScriptGlobal = nsnull;
      }
   if(mScriptContext)
      {
      mScriptContext->SetOwner(nsnull);
      mScriptContext = nsnull;
      }

   mScriptGlobal = nsnull;
   mScriptContext = nsnull;
   mSessionHistory = nsnull;
   SetTreeOwner(nsnull);

  SetLoadCookie(nsnull);

   if(mInitInfo)
      {
      delete mInitInfo;
      mInitInfo = nsnull;
      }

   if(mContentListener)
      {
      mContentListener->DocShell(nsnull);
      NS_RELEASE(mContentListener);
      }

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetPosition(PRInt32 x, PRInt32 y)
{
   if(mContentViewer)
      NS_ENSURE_SUCCESS(mContentViewer->Move(x, y), NS_ERROR_FAILURE);
   else if(InitInfo())
      {
      mInitInfo->x = x;
      mInitInfo->y = y;
      }
   else
      NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_FAILURE);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetPosition(PRInt32* aX, PRInt32* aY)
{
   PRInt32 dummyHolder;
   return GetPositionAndSize(aX, aY, &dummyHolder, &dummyHolder);
}

NS_IMETHODIMP nsDocShell::SetSize(PRInt32 aCX, PRInt32 aCY, PRBool aRepaint)
{
   PRInt32 x = 0, y = 0;
   GetPosition(&x, &y);
   return SetPositionAndSize(x, y, aCX, aCY, aRepaint);
}

NS_IMETHODIMP nsDocShell::GetSize(PRInt32* aCX, PRInt32* aCY)
{
   PRInt32 dummyHolder;
   return GetPositionAndSize(&dummyHolder, &dummyHolder, aCX, aCY);
}

NS_IMETHODIMP nsDocShell::SetPositionAndSize(PRInt32 x, PRInt32 y, PRInt32 cx,
   PRInt32 cy, PRBool fRepaint)
{
   if(mContentViewer)
      {
      //XXX Border figured in here or is that handled elsewhere?
      nsRect bounds(x, y, cx, cy);

      NS_ENSURE_SUCCESS(mContentViewer->SetBounds(bounds), NS_ERROR_FAILURE);
      }
   else if(InitInfo())
      {
      mInitInfo->x = x;
      mInitInfo->y = y;
      mInitInfo->cx = cx;
      mInitInfo->cy = cy;
      }
   else
      NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_FAILURE);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetPositionAndSize(PRInt32* x, PRInt32* y, PRInt32* cx,
   PRInt32* cy)
{
   if(mContentViewer)
      {
      nsRect bounds;

      NS_ENSURE_SUCCESS(mContentViewer->GetBounds(bounds), NS_ERROR_FAILURE);

      if(x)
         *x = bounds.x;
      if(y)
         *y = bounds.y;
      if(cx)
         *cx = bounds.width;
      if(cy)
         *cy = bounds.height;
      }
   else if(InitInfo())
      {
      if(x)
         *x = mInitInfo->x;
      if(y)
         *y = mInitInfo->y;
      if(cx)
         *cx = mInitInfo->cx;
      if(cy)
         *cy = mInitInfo->cy;
      }
   else
      NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_FAILURE);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::Repaint(PRBool aForce)
{
   nsCOMPtr<nsIDocumentViewer> docViewer(do_QueryInterface(mContentViewer));
   NS_ENSURE_TRUE(docViewer, NS_ERROR_FAILURE);

   nsCOMPtr<nsIPresContext> context;
   docViewer->GetPresContext(*getter_AddRefs(context));
   NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);

   nsCOMPtr<nsIPresShell> shell;
   context->GetShell(getter_AddRefs(shell));
   NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);

   nsCOMPtr<nsIViewManager> viewManager;
   shell->GetViewManager(getter_AddRefs(viewManager));
   NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);

   // what about aForce ?
   NS_ENSURE_SUCCESS(viewManager->UpdateAllViews(0), NS_ERROR_FAILURE);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetParentWidget(nsIWidget** parentWidget)
{
   NS_ENSURE_ARG_POINTER(parentWidget);

   *parentWidget = mParentWidget;
   NS_IF_ADDREF(*parentWidget);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetParentWidget(nsIWidget* aParentWidget)
{
   NS_ENSURE_STATE(!mContentViewer);

   mParentWidget = aParentWidget;

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetParentNativeWindow(nativeWindow* parentNativeWindow)
{
   NS_ENSURE_ARG_POINTER(parentNativeWindow);

   if(mParentWidget)
      *parentNativeWindow = mParentWidget->GetNativeData(NS_NATIVE_WIDGET);
   else
      *parentNativeWindow = nsnull;

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetParentNativeWindow(nativeWindow parentNativeWindow)
{
   return NS_ERROR_NOT_IMPLEMENTED;
}

NS_IMETHODIMP nsDocShell::GetVisibility(PRBool* aVisibility)
{
   NS_ENSURE_ARG_POINTER(aVisibility);
   if(!mContentViewer)
      {
      *aVisibility = PR_FALSE;
      return NS_OK;
      }

   // get the pres shell
   nsCOMPtr<nsIPresShell> presShell;
   NS_ENSURE_SUCCESS(GetPresShell(getter_AddRefs(presShell)), NS_ERROR_FAILURE);
   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);

   // get the view manager
   nsCOMPtr<nsIViewManager> vm;
   NS_ENSURE_SUCCESS(presShell->GetViewManager(getter_AddRefs(vm)), NS_ERROR_FAILURE);
   NS_ENSURE_TRUE(vm, NS_ERROR_FAILURE);

   // get the root view
   nsIView *rootView=nsnull; // views are not ref counted
   NS_ENSURE_SUCCESS(vm->GetRootView(rootView), NS_ERROR_FAILURE);
   NS_ENSURE_TRUE(rootView, NS_ERROR_FAILURE);

   // convert the view's visibility attribute to a bool
   nsViewVisibility vis;
   NS_ENSURE_TRUE(rootView->GetVisibility(vis), NS_ERROR_FAILURE);
   *aVisibility = nsViewVisibility_kHide==vis ? PR_FALSE : PR_TRUE;

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetVisibility(PRBool aVisibility)
{
   if(!mContentViewer)
      return NS_OK;
   if(aVisibility)
      {
      NS_ENSURE_SUCCESS(EnsureContentViewer(), NS_ERROR_FAILURE);
      mContentViewer->Show();
      }
   else if(mContentViewer)
      mContentViewer->Hide();

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetMainWidget(nsIWidget** aMainWidget)
{
   // We don't create our own widget, so simply return the parent one. 
   return GetParentWidget(aMainWidget);
}

NS_IMETHODIMP nsDocShell::SetFocus()
{
   nsCOMPtr<nsIWidget> mainWidget;
   GetMainWidget(getter_AddRefs(mainWidget));
   if(mainWidget)
      mainWidget->SetFocus();
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::FocusAvailable(nsIBaseWindow* aCurrentFocus, 
   PRBool* aTookFocus)
{
   NS_ENSURE_ARG_POINTER(aTookFocus);

   // Next person we should call is first the parent otherwise the 
   // docshell tree owner.
   nsCOMPtr<nsIBaseWindow> nextCallWin(do_QueryInterface(mParent));
   if(!nextCallWin)
      {
      nextCallWin = do_QueryInterface(mTreeOwner);
      }

   //If the current focus is us, offer it to the next owner.
   if(aCurrentFocus == NS_STATIC_CAST(nsIBaseWindow*, this))
      {
      if(nextCallWin)
         return nextCallWin->FocusAvailable(aCurrentFocus, aTookFocus);
      return NS_OK;
      }

   //Otherwise, check the chilren and offer it to the next sibling.
   PRInt32 i;
   PRInt32 n = mChildren.Count();
   for(i = 0; i < n; i++)
      {
      nsCOMPtr<nsIBaseWindow> 
         child(do_QueryInterface((nsISupports*)mChildren.ElementAt(i)));
      if(child.get() == aCurrentFocus)
         {
         while(++i < n)
            {
            child = do_QueryInterface((nsISupports*)mChildren.ElementAt(i));
            if(NS_SUCCEEDED(child->SetFocus()))
               {
               *aTookFocus = PR_TRUE;
               return NS_OK;
               }
            }
         }
      }
   if(nextCallWin)
      return nextCallWin->FocusAvailable(aCurrentFocus, aTookFocus);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetTitle(PRUnichar** aTitle)
{
   NS_ENSURE_ARG_POINTER(aTitle);

   *aTitle = mTitle.ToNewUnicode();
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetTitle(const PRUnichar* aTitle)
{
   // Store local title
   mTitle = aTitle;

   nsCOMPtr<nsIDocShellTreeItem> parent;
   GetSameTypeParent(getter_AddRefs(parent));

   // When title is set on the top object it should then be passed to the 
   // tree owner.
   if(!parent)
      {
      nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_QueryInterface(mTreeOwner));
      if (treeOwnerAsWin)
          treeOwnerAsWin->SetTitle(aTitle);
      }

   if(mGlobalHistory && mCurrentURI)
      {
      nsXPIDLCString url;
      mCurrentURI->GetSpec(getter_Copies(url));
      mGlobalHistory->SetPageTitle(url, aTitle);
      }


   // Update SessionHistory too with Title. Otherwise entry for current page
   // has previous page's title.
   if(mSessionHistory)
      {
      PRInt32 index = -1;
      mSessionHistory->GetIndex(&index);
      nsCOMPtr<nsISHEntry>   shEntry;
      mSessionHistory->GetEntryAtIndex(index, PR_FALSE, getter_AddRefs(shEntry));
      if(shEntry)
         shEntry->SetTitle(mTitle.GetUnicode());      
      }
   

   return NS_OK;
}

//*****************************************************************************
// nsDocShell::nsIScrollable
//*****************************************************************************   

NS_IMETHODIMP nsDocShell::GetCurScrollPos(PRInt32 scrollOrientation, 
   PRInt32* curPos)
{
   NS_ENSURE_ARG_POINTER(curPos);

   nsCOMPtr<nsIScrollableView> scrollView;
   NS_ENSURE_SUCCESS(GetRootScrollableView(getter_AddRefs(scrollView)), 
      NS_ERROR_FAILURE);
   if (!scrollView)
   {
       return NS_ERROR_FAILURE;
   }

   nscoord x, y;
   NS_ENSURE_SUCCESS(scrollView->GetScrollPosition(x, y), NS_ERROR_FAILURE);

   switch(scrollOrientation)
      {
      case ScrollOrientation_X:
         *curPos = x;
         return NS_OK;

      case ScrollOrientation_Y:
         *curPos = y;
         return NS_OK;

      default:
         NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
      }
   return NS_ERROR_FAILURE;
}

NS_IMETHODIMP nsDocShell::SetCurScrollPos(PRInt32 scrollOrientation, 
   PRInt32 curPos)
{
   nsCOMPtr<nsIScrollableView> scrollView;
   NS_ENSURE_SUCCESS(GetRootScrollableView(getter_AddRefs(scrollView)), 
      NS_ERROR_FAILURE);
   if (!scrollView)
   {
       return NS_ERROR_FAILURE;
   }

   PRInt32 other;
   PRInt32 x;
   PRInt32 y;

   GetCurScrollPos(scrollOrientation, &other);

   switch(scrollOrientation)
      {
      case ScrollOrientation_X:
         x = curPos;
         y = other;
         break;

      case ScrollOrientation_Y:
         x = other;
         y = curPos;
         break;

      default:
         NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
      }

   NS_ENSURE_SUCCESS(scrollView->ScrollTo(x, y, NS_VMREFRESH_IMMEDIATE),
      NS_ERROR_FAILURE);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetCurScrollPosEx(PRInt32 curHorizontalPos, 
   PRInt32 curVerticalPos)
{
   nsCOMPtr<nsIScrollableView> scrollView;
   NS_ENSURE_SUCCESS(GetRootScrollableView(getter_AddRefs(scrollView)), 
      NS_ERROR_FAILURE);
   if (!scrollView)
   {
       return NS_ERROR_FAILURE;
   }

   NS_ENSURE_SUCCESS(scrollView->ScrollTo(curHorizontalPos, curVerticalPos, 
      NS_VMREFRESH_IMMEDIATE), NS_ERROR_FAILURE);
   return NS_OK;
}

// XXX This is wrong
NS_IMETHODIMP nsDocShell::GetScrollRange(PRInt32 scrollOrientation,
   PRInt32* minPos, PRInt32* maxPos)
{
   NS_ENSURE_ARG_POINTER(minPos && maxPos);

   nsCOMPtr<nsIScrollableView> scrollView;
   NS_ENSURE_SUCCESS(GetRootScrollableView(getter_AddRefs(scrollView)), 
      NS_ERROR_FAILURE);
   if (!scrollView)
   {
       return NS_ERROR_FAILURE;
   }

   PRInt32 cx;
   PRInt32 cy;
   
   NS_ENSURE_SUCCESS(scrollView->GetContainerSize(&cx, &cy), NS_ERROR_FAILURE);
   *minPos = 0;
   
   switch(scrollOrientation)
      {
      case ScrollOrientation_X:
         *maxPos = cx;
         return NS_OK;

      case ScrollOrientation_Y:
         *maxPos = cy;
         return NS_OK;

      default:
         NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
      }

   return NS_ERROR_FAILURE;
}

NS_IMETHODIMP nsDocShell::SetScrollRange(PRInt32 scrollOrientation,
   PRInt32 minPos, PRInt32 maxPos)
{
   //XXX First Check
  /*
  Retrieves or Sets the valid ranges for the thumb.  When maxPos is set to 
  something less than the current thumb position, curPos is set = to maxPos.

  @return NS_OK - Setting or Getting completed successfully.
        NS_ERROR_INVALID_ARG - returned when curPos is not within the
          minPos and maxPos.
  */
   return NS_ERROR_FAILURE;
}

NS_IMETHODIMP nsDocShell::SetScrollRangeEx(PRInt32 minHorizontalPos,
   PRInt32 maxHorizontalPos, PRInt32 minVerticalPos, PRInt32 maxVerticalPos)
{
   //XXX First Check
  /*
  Retrieves or Sets the valid ranges for the thumb.  When maxPos is set to 
  something less than the current thumb position, curPos is set = to maxPos.

  @return NS_OK - Setting or Getting completed successfully.
        NS_ERROR_INVALID_ARG - returned when curPos is not within the
          minPos and maxPos.
  */
   return NS_ERROR_FAILURE;
}

// Get scroll setting for this document only
//
// One important client is nsCSSFrameConstructor::ConstructRootFrame()
NS_IMETHODIMP nsDocShell::GetCurrentScrollbarPreferences(PRInt32 scrollOrientation,
   PRInt32* scrollbarPref)
{
   NS_ENSURE_ARG_POINTER(scrollbarPref);
   switch(scrollOrientation) {
     case ScrollOrientation_X:
       *scrollbarPref = mCurrentScrollbarPref.x;
       return NS_OK;

     case ScrollOrientation_Y:
       *scrollbarPref = mCurrentScrollbarPref.y;
       return NS_OK;

     default:
       NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
   }
   return NS_ERROR_FAILURE;
}

// This returns setting for all documents in this webshell
NS_IMETHODIMP nsDocShell::GetDefaultScrollbarPreferences(PRInt32 scrollOrientation,
   PRInt32* scrollbarPref)
{
   NS_ENSURE_ARG_POINTER(scrollbarPref);
   switch(scrollOrientation) {
     case ScrollOrientation_X:
       *scrollbarPref = mDefaultScrollbarPref.x;
       return NS_OK;

     case ScrollOrientation_Y:
       *scrollbarPref = mDefaultScrollbarPref.y;
       return NS_OK;

     default:
       NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
   }
   return NS_ERROR_FAILURE;
}

// Set scrolling preference for this document only.
//
// There are three possible values stored in the shell:
//  1) NS_STYLE_OVERFLOW_HIDDEN = no scrollbars
//  2) NS_STYLE_OVERFLOW_AUTO = scrollbars appear if needed
//  3) NS_STYLE_OVERFLOW_SCROLL = scrollbars always
//
// XXX Currently OVERFLOW_SCROLL isn't honored,
//     as it is not implemented by Gfx scrollbars
// XXX setting has no effect after the root frame is created
//     as it is not implemented by Gfx scrollbars
//
// One important client is HTMLContentSink::StartLayout()
NS_IMETHODIMP nsDocShell::SetCurrentScrollbarPreferences(PRInt32 scrollOrientation,
   PRInt32 scrollbarPref)
{
   switch(scrollOrientation) {
     case ScrollOrientation_X:
       mCurrentScrollbarPref.x = scrollbarPref;
       return NS_OK;

     case ScrollOrientation_Y:
       mCurrentScrollbarPref.y = scrollbarPref;
       return NS_OK;

     default:
       NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
   }
   return NS_ERROR_FAILURE;
}

// Set scrolling preference for all documents in this shell
// One important client is nsHTMLFrameInnerFrame::CreateWebShell()
NS_IMETHODIMP nsDocShell::SetDefaultScrollbarPreferences(PRInt32 scrollOrientation,
   PRInt32 scrollbarPref)
{
   switch(scrollOrientation) {
     case ScrollOrientation_X:
       mDefaultScrollbarPref.x = scrollbarPref;
       return NS_OK;

     case ScrollOrientation_Y:
       mDefaultScrollbarPref.y = scrollbarPref;
       return NS_OK;

     default:
       NS_ENSURE_TRUE(PR_FALSE, NS_ERROR_INVALID_ARG);
   }
   return NS_ERROR_FAILURE;
}

// Reset 'current' scrollbar settings to 'default'.
// This must be called before every document load or else
// frameset scrollbar settings (e.g. <IFRAME SCROLLING="no">
// will not be preserved.
//
// One important client is HTMLContentSink::StartLayout()
NS_IMETHODIMP nsDocShell::ResetScrollbarPreferences()
{
  mCurrentScrollbarPref = mDefaultScrollbarPref;
  return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetScrollbarVisibility(PRBool* verticalVisible,
   PRBool* horizontalVisible)
{
   nsCOMPtr<nsIScrollableView> scrollView;
   NS_ENSURE_SUCCESS(GetRootScrollableView(getter_AddRefs(scrollView)), 
      NS_ERROR_FAILURE);
   if (!scrollView)
   {
       return NS_ERROR_FAILURE;
   }

   PRBool vertVisible;
   PRBool horizVisible;

   NS_ENSURE_SUCCESS(scrollView->GetScrollbarVisibility(&vertVisible,
      &horizVisible), NS_ERROR_FAILURE);

   if(verticalVisible)
      *verticalVisible = vertVisible;
   if(horizontalVisible)
      *horizontalVisible = horizVisible;

   return NS_OK;
}

//*****************************************************************************
// nsDocShell::nsITextScroll
//*****************************************************************************   

NS_IMETHODIMP nsDocShell::ScrollByLines(PRInt32 numLines)
{
   nsCOMPtr<nsIScrollableView> scrollView;

   NS_ENSURE_SUCCESS(GetRootScrollableView(getter_AddRefs(scrollView)),
      NS_ERROR_FAILURE);
   if (!scrollView)
   {
       return NS_ERROR_FAILURE;
   }

   NS_ENSURE_SUCCESS(scrollView->ScrollByLines(0, numLines), NS_ERROR_FAILURE);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::ScrollByPages(PRInt32 numPages)
{
   nsCOMPtr<nsIScrollableView> scrollView;

   NS_ENSURE_SUCCESS(GetRootScrollableView(getter_AddRefs(scrollView)),
      NS_ERROR_FAILURE);
   if (!scrollView)
   {
       return NS_ERROR_FAILURE;
   }

   NS_ENSURE_SUCCESS(scrollView->ScrollByPages(numPages), NS_ERROR_FAILURE);

   return NS_OK;
}

//*****************************************************************************
// nsDocShell::nsIScriptGlobalObjectOwner
//*****************************************************************************   

NS_IMETHODIMP nsDocShell::GetScriptGlobalObject(nsIScriptGlobalObject** aGlobal)
{
   NS_ENSURE_ARG_POINTER(aGlobal);
   NS_ENSURE_SUCCESS(EnsureScriptEnvironment(), NS_ERROR_FAILURE);

   *aGlobal = mScriptGlobal;
   NS_IF_ADDREF(*aGlobal);
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::ReportScriptError(nsIScriptError *errorObject)
{
   nsresult rv;

   if (errorObject == nsnull)
      return NS_ERROR_NULL_POINTER;

   // Get the console service, where we're going to register the error.
   nsCOMPtr<nsIConsoleService> consoleService
      (do_GetService("mozilla.consoleservice.1"));

   if (consoleService != nsnull)
      {
      rv = consoleService->LogMessage(errorObject);
      if (NS_SUCCEEDED(rv))
         {
         return NS_OK;
         }
      else
         {
         return rv;
         }
      }
   else
      {
      return NS_ERROR_NOT_AVAILABLE;
      }
}

//*****************************************************************************
// nsDocShell::nsIRefreshURI
//*****************************************************************************   

NS_IMETHODIMP nsDocShell::RefreshURI(nsIURI *aURI, PRInt32 aDelay, PRBool aRepeat)
{
   NS_ENSURE_ARG(aURI);

   nsRefreshTimer* refreshTimer = new nsRefreshTimer();
   NS_ENSURE_TRUE(refreshTimer, NS_ERROR_OUT_OF_MEMORY);

   nsCOMPtr<nsISupports> dataRef = refreshTimer; // Get the ref count to 1

   refreshTimer->mDocShell = this;
   refreshTimer->mURI = aURI;
   refreshTimer->mDelay = aDelay;
   refreshTimer->mRepeat = aRepeat;

   if (!mRefreshURIList)
   {
      NS_ENSURE_SUCCESS(NS_NewISupportsArray(getter_AddRefs(mRefreshURIList)),
         NS_ERROR_FAILURE);
   }
   
   nsCOMPtr<nsITimer> timer = do_CreateInstance("component://netscape/timer");
   NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
    
   mRefreshURIList->AppendElement(timer);    // owning timer ref
   timer->Init(refreshTimer, aDelay);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::CancelRefreshURITimers()
{
   if (!mRefreshURIList) return NS_OK;

   PRUint32 n;
   mRefreshURIList->Count(&n);

   while (n)
   {
      nsCOMPtr<nsISupports> element;
      mRefreshURIList->GetElementAt(0, getter_AddRefs(element));
      nsCOMPtr<nsITimer> timer(do_QueryInterface(element));

      mRefreshURIList->RemoveElementAt(0);    // bye bye owning timer ref

      if (timer)
         timer->Cancel();
      n--;
   }

   return NS_OK;
}

//*****************************************************************************
// nsDocShell::nsIContentViewerContainer
//*****************************************************************************   

NS_IMETHODIMP nsDocShell::Embed(nsIContentViewer* aContentViewer, 
                                    const char      * aCommand,
                                    nsISupports     * aExtraInfo)
{
#ifdef SH_IN_FRAMES
	// Save the LayoutHistoryState of the previous document, before
	// setting up new document
	PersistLayoutHistoryState();

   nsresult rv = SetupNewViewer(aContentViewer);

   // XXX What if SetupNewViewer fails?

   OSHE = LSHE;
/*   
   PRBool updateHistory = PR_TRUE;

    // Determine if this type of load should update history   
    switch(mLoadType)
    {
    case nsIDocShellLoadInfo::loadHistory:
    case nsIDocShellLoadInfo::loadReloadNormal:
    case nsIDocShellLoadInfo::loadReloadBypassCache:
    case nsIDocShellLoadInfo::loadReloadBypassProxy:
    case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
        updateHistory = PR_FALSE;
        break;
    default:
        break;
    } 
*/
  if (OSHE) {
    nsCOMPtr<nsILayoutHistoryState> layoutState;

    rv = OSHE->GetLayoutHistoryState(getter_AddRefs(layoutState));
    if (layoutState && (mLoadType != nsIDocShellLoadInfo::loadNormalReplace)) {
      // This is a SH load. That's why there is a LayoutHistoryState in OSHE
      nsCOMPtr<nsIPresShell> presShell;
      rv = GetPresShell(getter_AddRefs(presShell));
      if (NS_SUCCEEDED(rv) && presShell) {
        rv = presShell->SetHistoryState(layoutState);
      }
    }
  }
    return NS_OK;
#else
   return SetupNewViewer(aContentViewer);
#endif  /* SH_IN_FRAMES */
}

//*****************************************************************************
// nsDocShell::nsIWebProgressListener
//*****************************************************************************   

NS_IMETHODIMP
nsDocShell::OnProgressChange(nsIWebProgress *aProgress, nsIRequest *aRequest,
                             PRInt32 aCurSelfProgress, PRInt32 aMaxSelfProgress,
                             PRInt32 aCurTotalProgress, PRInt32 aMaxTotalProgress)
{
  return NS_OK;
}

NS_IMETHODIMP
nsDocShell::OnStateChange(nsIWebProgress *aProgress, nsIRequest *aRequest,
                          PRInt32 aStateFlags, nsresult aStatus)
{
  // Clear the LSHE reference to indicate document loading has finished
  // one way or another.
  if ((aStateFlags & flag_stop) && (aStateFlags & flag_is_network)) {
    LSHE = nsnull;
  }
  return NS_OK;
}

NS_IMETHODIMP
nsDocShell::OnLocationChange(nsIWebProgress *aProgress, 
                             nsIRequest *aRequest,
                             nsIURI *aURI)
{
  return NS_OK;
}

NS_IMETHODIMP 
nsDocShell::OnStatusChange(nsIWebProgress* aWebProgress,
                           nsIRequest* aRequest,
                           nsresult aStatus,
                           const PRUnichar* aMessage)
{
    return NS_OK;
}

//*****************************************************************************
// nsDocShell: Content Viewer Management
//*****************************************************************************   

NS_IMETHODIMP nsDocShell::EnsureContentViewer()
{
   if(mContentViewer)
      return NS_OK;

   return CreateAboutBlankContentViewer();
}

NS_IMETHODIMP nsDocShell::EnsureDeviceContext()
{
   if(mDeviceContext)
      return NS_OK;

   mDeviceContext = do_CreateInstance(kDeviceContextCID);
   NS_ENSURE_TRUE(mDeviceContext, NS_ERROR_FAILURE);

   nsCOMPtr<nsIWidget> widget;
   GetMainWidget(getter_AddRefs(widget));
   NS_ENSURE_TRUE(widget, NS_ERROR_FAILURE);

   mDeviceContext->Init(widget->GetNativeData(NS_NATIVE_WIDGET));
   float dev2twip;
   mDeviceContext->GetDevUnitsToTwips(dev2twip);
   mDeviceContext->SetDevUnitsToAppUnits(dev2twip);
   float twip2dev;
   mDeviceContext->GetTwipsToDevUnits(twip2dev);
   mDeviceContext->SetAppUnitsToDevUnits(twip2dev);
   mDeviceContext->SetGamma(1.0f);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::CreateAboutBlankContentViewer()
{
   // XXX
   NS_ERROR("Not Implemented yet");
   return NS_ERROR_FAILURE;
}

NS_IMETHODIMP nsDocShell::CreateContentViewer(const char* aContentType, 
   nsIChannel* aOpenedChannel, nsIStreamListener** aContentHandler)
{
   // Can we check the content type of the current content viewer
   // and reuse it without destroying it and re-creating it?

   nsCOMPtr<nsILoadGroup> loadGroup(do_GetInterface(mLoadCookie));
   NS_ENSURE_TRUE(loadGroup, NS_ERROR_FAILURE);

   // Instantiate the content viewer object
   nsCOMPtr<nsIContentViewer> viewer;
   if(NS_FAILED(NewContentViewerObj(aContentType, aOpenedChannel, loadGroup, 
      aContentHandler, getter_AddRefs(viewer))))
      return NS_ERROR_FAILURE;

   // let's try resetting the load group if we need to...
   nsCOMPtr<nsILoadGroup> currentLoadGroup;
   NS_ENSURE_SUCCESS(aOpenedChannel->GetLoadGroup(getter_AddRefs(currentLoadGroup)),
      NS_ERROR_FAILURE);

   if(currentLoadGroup.get() != loadGroup.get())
      {
      nsLoadFlags loadAttribs = 0;

      //Cancel any URIs that are currently loading...
      /// XXX: Need to do this eventually      Stop();
      //
      // Retarget the document to this loadgroup...
      //
      if(currentLoadGroup)
         currentLoadGroup->RemoveChannel(aOpenedChannel, nsnull, nsnull, nsnull);
      
      aOpenedChannel->SetLoadGroup(loadGroup);

      // Mark the channel as being a document URI...
      aOpenedChannel->GetLoadAttributes(&loadAttribs);
      loadAttribs |= nsIChannel::LOAD_DOCUMENT_URI;

      aOpenedChannel->SetLoadAttributes(loadAttribs);

      loadGroup->AddChannel(aOpenedChannel, nsnull);
      }
#ifdef SH_IN_FRAMES
   NS_ENSURE_SUCCESS(Embed(viewer, "", (nsISupports *) nsnull), NS_ERROR_FAILURE);
#else
   NS_ENSURE_SUCCESS(SetupNewViewer(viewer), NS_ERROR_FAILURE);
#endif  /* SH_IN_FRAMES */

   mEODForCurrentDocument = PR_FALSE; // clear the current flag
   return NS_OK;
}

nsresult nsDocShell::NewContentViewerObj(const char* aContentType,
   nsIChannel* aOpenedChannel, nsILoadGroup* aLoadGroup, 
   nsIStreamListener** aContentHandler, nsIContentViewer** aViewer)
{
   //XXX This should probably be some category thing....
   char id[256];
   PR_snprintf(id, sizeof(id), NS_DOCUMENT_LOADER_FACTORY_PROGID_PREFIX "%s/%s",
      (const char*)((viewSource == mViewMode) ? "view-source" : "view"),
      aContentType);

   // Create an instance of the document-loader-factory
   nsCOMPtr<nsIDocumentLoaderFactory> docLoaderFactory(do_CreateInstance(id));
   if(!docLoaderFactory)
      return NS_ERROR_FAILURE;

   // Now create an instance of the content viewer
   NS_ENSURE_SUCCESS(docLoaderFactory->CreateInstance(
      (viewSource == mViewMode) ? "view-source" : "view",
      aOpenedChannel, aLoadGroup, aContentType,
      NS_STATIC_CAST(nsIContentViewerContainer*, this), nsnull, 
      aContentHandler, aViewer), NS_ERROR_FAILURE);

   (*aViewer)->SetContainer(NS_STATIC_CAST(nsIContentViewerContainer*, this));

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer)
{
   //
   // Copy content viewer state from previous or parent content viewer.
   //
   // The following logic is mirrored in nsHTMLDocument::StartDocumentLoad!
   //
   // Do NOT to maintain a reference to the old content viewer outside
   // of this "copying" block, or it will not be destroyed until the end of
   // this routine and all <SCRIPT>s and event handlers fail! (bug 20315)
   //
   // 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.
   //

   PRInt32 x = 0;
   PRInt32 y = 0;
   PRInt32 cx = 0;
   PRInt32 cy = 0;

   // This will get the size from the current content viewer or from the
   // Init settings
   GetPositionAndSize(&x, &y, &cx, &cy);

   nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
   NS_ENSURE_SUCCESS(GetSameTypeParent(getter_AddRefs(parentAsItem)), 
      NS_ERROR_FAILURE);
   nsCOMPtr<nsIDocShell> parent(do_QueryInterface(parentAsItem));

   if(mContentViewer || parent)
      {  
      nsCOMPtr<nsIMarkupDocumentViewer> oldMUDV;
      if(mContentViewer)
         { 
         // Get any interesting state from old content viewer
         // XXX: it would be far better to just reuse the document viewer ,
         //      since we know we're just displaying the same document as before
         oldMUDV = do_QueryInterface(mContentViewer);
         }
      else
         { 
         // No old content viewer, so get state from parent's content viewer
         nsCOMPtr<nsIContentViewer> parentContentViewer;
         parent->GetContentViewer(getter_AddRefs(parentContentViewer));
         oldMUDV = do_QueryInterface(parentContentViewer);
         }

      nsXPIDLString defaultCharset;
      nsXPIDLString forceCharset;
      nsXPIDLString hintCharset;
      PRInt32 hintCharsetSource;

      nsCOMPtr<nsIMarkupDocumentViewer> newMUDV(do_QueryInterface(aNewViewer));
      if(oldMUDV && newMUDV)
         {
         NS_ENSURE_SUCCESS(oldMUDV->GetDefaultCharacterSet(getter_Copies(defaultCharset)),
            NS_ERROR_FAILURE);
         NS_ENSURE_SUCCESS(oldMUDV->GetForceCharacterSet(getter_Copies(forceCharset)),
            NS_ERROR_FAILURE);
         NS_ENSURE_SUCCESS(oldMUDV->GetHintCharacterSet(getter_Copies(hintCharset)),
            NS_ERROR_FAILURE);
         NS_ENSURE_SUCCESS(oldMUDV->GetHintCharacterSetSource(&hintCharsetSource),
            NS_ERROR_FAILURE);

         // set the old state onto the new content viewer
         NS_ENSURE_SUCCESS(newMUDV->SetDefaultCharacterSet(defaultCharset),
            NS_ERROR_FAILURE);
         NS_ENSURE_SUCCESS(newMUDV->SetForceCharacterSet(forceCharset), 
            NS_ERROR_FAILURE);
         NS_ENSURE_SUCCESS(newMUDV->SetHintCharacterSet(hintCharset),
            NS_ERROR_FAILURE);
         NS_ENSURE_SUCCESS(newMUDV->SetHintCharacterSetSource(hintCharsetSource),
            NS_ERROR_FAILURE);
         }
      }
   mContentViewer = nsnull;
   // End copying block (Don't hold content/document viewer ref beyond here!!)

   if(mScriptContext)
      mScriptContext->GC();

   mContentViewer = aNewViewer;

   nsCOMPtr<nsIWidget> widget;
   NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);

   nsRect bounds(x, y, cx, cy);
   NS_ENSURE_SUCCESS(EnsureDeviceContext(), NS_ERROR_FAILURE);
   if(NS_FAILED(mContentViewer->Init(widget,
      mDeviceContext, bounds)))
      {
      mContentViewer = nsnull;
      NS_ERROR("ContentViewer Initialization failed");
      return NS_ERROR_FAILURE;
      }

// XXX: It looks like the LayoutState gets restored again in Embed()
//      right after the call to SetupNewViewer(...)
#ifndef SH_IN_FRAMES
    // Restore up any HistoryLayoutState this page might have.
    nsresult rv = NS_OK;
	PRBool updateHistory = PR_TRUE;

    // Determine if this type of load should update history   
    switch(mLoadType)
    {
    case nsIDocShellLoadInfo::loadHistory:
    case nsIDocShellLoadInfo::loadReloadNormal:
    case nsIDocShellLoadInfo::loadReloadBypassCache:
    case nsIDocShellLoadInfo::loadReloadBypassProxy:
    case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
        updateHistory = PR_FALSE;
        break;
    default:
        break;
    } 
    if (mSessionHistory && !updateHistory) {
      PRInt32 index = 0;
      mSessionHistory->GetIndex(&index);
      if (-1 < index) {
 
        nsCOMPtr<nsISHEntry> entry;
        rv = mSessionHistory->GetEntryAtIndex(index, PR_FALSE, getter_AddRefs(entry));
        if (NS_SUCCEEDED(rv) && entry) {
 
          nsCOMPtr<nsILayoutHistoryState> layoutState;
          rv = entry->GetLayoutHistoryState(getter_AddRefs(layoutState));
          if (NS_SUCCEEDED(rv) && layoutState) {
 
            nsCOMPtr<nsIPresShell> presShell;
            rv = GetPresShell(getter_AddRefs(presShell));
            if (NS_SUCCEEDED(rv) && presShell) {
 
              rv = presShell->SetHistoryState(layoutState);
            }
          }
        }
      }
    }
#endif  /* SH_IN_FRAMES */

   mContentViewer->Show();

   // Now that we have switched documents, forget all of our children
   DestroyChildren();

   return NS_OK;
}

//*****************************************************************************
// nsDocShell: Site Loading
//*****************************************************************************   
#ifdef SH_IN_FRAMES
NS_IMETHODIMP nsDocShell::InternalLoad(nsIURI* aURI, nsIURI* aReferrer,
   nsISupports* aOwner, const char* aWindowTarget, nsIInputStream* aPostData, 
   nsDocShellInfoLoadType aLoadType, nsISHEntry * aSHEntry)
#else
NS_IMETHODIMP nsDocShell::InternalLoad(nsIURI* aURI, nsIURI* aReferrer,
   nsISupports* aOwner, const char* aWindowTarget, nsIInputStream* aPostData, 
   nsDocShellInfoLoadType aLoadType)
#endif
{
    // Check to see if the new URI is an anchor in the existing document.
  if (aLoadType == nsIDocShellLoadInfo::loadNormal ||
    aLoadType == nsIDocShellLoadInfo::loadNormalReplace ||
    aLoadType == nsIDocShellLoadInfo::loadHistory ||
    aLoadType == nsIDocShellLoadInfo::loadLink)
    {
        PRBool wasAnchor = PR_FALSE;
        NS_ENSURE_SUCCESS(ScrollIfAnchor(aURI, &wasAnchor), NS_ERROR_FAILURE);
        if(wasAnchor)
        {
            mLoadType = aLoadType;
            OnNewURI(aURI, nsnull, mLoadType);
            return NS_OK;
        }
    }

    NS_ENSURE_SUCCESS(StopLoad(), NS_ERROR_FAILURE);
    // Cancel any timers that were set for this loader.
    CancelRefreshURITimers();

    mLoadType = aLoadType;
#ifdef SH_IN_FRAMES
// XXX: I think that LSHE should *always* be set to the new Entry.
//      Even if it is null...
//   if (aSHEntry)
	   LSHE = aSHEntry;
#endif

    nsURILoadCommand  loadCmd = nsIURILoader::viewNormal;
    if(nsIDocShellLoadInfo::loadLink == aLoadType)
        loadCmd = nsIURILoader::viewUserClick;
    NS_ENSURE_SUCCESS(DoURILoad(aURI, aReferrer, aOwner, loadCmd, aWindowTarget, 
        aPostData), NS_ERROR_FAILURE);

    return NS_OK;
}

NS_IMETHODIMP nsDocShell::CreateFixupURI(const PRUnichar* aStringURI, 
   nsIURI** aURI)
{
   *aURI = nsnull;
   nsAutoString uriString(aStringURI);
   uriString.Trim(" ");  // Cleanup the empty spaces that might be on each end.

   // Just try to create an URL out of it
   NS_NewURI(aURI, uriString.GetUnicode(), nsnull);
   if(*aURI)
      return NS_OK;

   // Check for if it is a file URL
   FileURIFixup(uriString.GetUnicode(), aURI);
   if(*aURI)
      return NS_OK;

   // See if it is a keyword
   KeywordURIFixup(uriString.GetUnicode(), aURI);
   if(*aURI)
      return NS_OK;

   // See if a protocol needs to be added
   PRInt32 checkprotocol = uriString.Find("://",0);
   // if no scheme (protocol) is found, assume http or ftp.
   if (checkprotocol == -1) {
      // find host name
      PRInt32 hostPos = uriString.FindCharInSet("./:");
      if (hostPos == -1) 
         hostPos = uriString.Length();

      // extract host name
      nsAutoString hostSpec;
      uriString.Left(hostSpec, hostPos);

      // insert url spec corresponding to host name
      if (hostSpec.EqualsIgnoreCase("ftp")) 
         uriString.InsertWithConversion("ftp://", 0, 6);
      else 
         uriString.InsertWithConversion("http://", 0, 7);
   } // end if checkprotocol
   return NS_NewURI(aURI, uriString.GetUnicode(), nsnull);
}

NS_IMETHODIMP nsDocShell::FileURIFixup(const PRUnichar* aStringURI, 
   nsIURI** aURI)
{
   nsAutoString uriSpecIn(aStringURI);
   nsAutoString uriSpecOut(aStringURI);

   ConvertFileToStringURI(uriSpecIn, uriSpecOut);

   if(0 == uriSpecOut.Find("file:", 0))
      {
      // if this is file url, we need to  convert the URI
      // from Unicode to the FS charset
      nsCAutoString inFSCharset;
      NS_ENSURE_SUCCESS(ConvertStringURIToFileCharset(uriSpecOut, inFSCharset),
         NS_ERROR_FAILURE);

      if(NS_SUCCEEDED(NS_NewURI(aURI, inFSCharset.GetBuffer(), nsnull)))
         return NS_OK;
      } 
   return NS_ERROR_FAILURE;
}

#define FILE_PROTOCOL "file://"

NS_IMETHODIMP nsDocShell::ConvertFileToStringURI(nsString& aIn, nsString& aOut)
{
#ifdef XP_PC
   // Check for \ in the url-string or just a drive (PC)
   if(kNotFound != aIn.FindChar(PRUnichar('\\')) || ((aIn.Length() == 2 ) && (aIn.Last() == PRUnichar(':') || aIn.Last() == PRUnichar('|'))))
      {
#elif XP_UNIX
   // Check if it starts with / or \ (UNIX)
   const PRUnichar * up = aIn.GetUnicode();
   if((PRUnichar('/') == *up) || (PRUnichar('\\') == *up))
      {
#else
   if(0) 
      {  
      // Do nothing (All others for now) 
#endif

#ifdef XP_PC
      // Translate '\' to '/'
      aOut.ReplaceChar(PRUnichar('\\'), PRUnichar('/'));
      aOut.ReplaceChar(PRUnichar(':'), PRUnichar('|'));
#endif

      // Build the file URL
      aOut.InsertWithConversion(FILE_PROTOCOL,0);
      }

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::ConvertStringURIToFileCharset(nsString& aIn, 
   nsCString& aOut)
{
   aOut = "";
   // for file url, we need to convert the nsString to the file system
   // charset before we pass to NS_NewURI
   static nsAutoString fsCharset;
   // find out the file system charset first
   if(0 == fsCharset.Length())
      {
      fsCharset.AssignWithConversion("ISO-8859-1"); // set the fallback first.
      nsCOMPtr<nsIPlatformCharset> plat(do_GetService(kPlatformCharsetCID));
      NS_ENSURE_TRUE(plat, NS_ERROR_FAILURE);
      NS_ENSURE_SUCCESS(plat->GetCharset(kPlatformCharsetSel_FileName, fsCharset),
         NS_ERROR_FAILURE);
      }
   // We probably should cache ccm here.
   // get a charset converter from the manager
   nsCOMPtr<nsICharsetConverterManager> ccm(do_GetService(kCharsetConverterManagerCID));
   NS_ENSURE_TRUE(ccm, NS_ERROR_FAILURE);
   
   nsCOMPtr<nsIUnicodeEncoder> fsEncoder;
   NS_ENSURE_SUCCESS(ccm->GetUnicodeEncoder(&fsCharset, 
      getter_AddRefs(fsEncoder)), NS_ERROR_FAILURE);

   PRInt32 bufLen = 0;
   NS_ENSURE_SUCCESS(fsEncoder->GetMaxLength(aIn.GetUnicode(), aIn.Length(),
      &bufLen), NS_ERROR_FAILURE);
   aOut.SetCapacity(bufLen+1);
   PRInt32 srclen = aIn.Length();
   NS_ENSURE_SUCCESS(fsEncoder->Convert(aIn.GetUnicode(), &srclen, 
      (char*)aOut.GetBuffer(), &bufLen), NS_ERROR_FAILURE);

   ((char*)aOut.GetBuffer())[bufLen]='\0';
   aOut.SetLength(bufLen);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::KeywordURIFixup(const PRUnichar* aStringURI, 
   nsIURI** aURI)
{
   NS_ENSURE_STATE(mPrefs);

   PRBool keywordsEnabled = PR_FALSE;
   NS_ENSURE_SUCCESS(mPrefs->GetBoolPref("keyword.enabled", &keywordsEnabled),
      NS_ERROR_FAILURE);

   if(!keywordsEnabled)
      return NS_ERROR_FAILURE;

   // These are keyword formatted strings
   // "what is mozilla"
   // "what is mozilla?"
   // "?mozilla"
   // "?What is mozilla"

   // These are not keyword formatted strings
   // "www.blah.com" - anything with a dot in it 
   // "nonQualifiedHost:80" - anything with a colon in it
   // "nonQualifiedHost?"
   // "nonQualifiedHost?args"
   // "nonQualifiedHost?some args"

   nsAutoString uriString(aStringURI);
   if(uriString.FindChar('.') == -1 && uriString.FindChar(':') == -1)
      {
      PRInt32 qMarkLoc = uriString.FindChar('?');
      PRInt32 spaceLoc = uriString.FindChar(' ');

      PRBool keyword = PR_FALSE;
      if(qMarkLoc == 0)
         keyword = PR_TRUE;
      else if((spaceLoc > 0) && ((qMarkLoc == -1) || (spaceLoc < qMarkLoc)))
         keyword = PR_TRUE;

      if(keyword)
         {
         nsCAutoString keywordSpec("keyword:");
         char *utf8Spec = uriString.ToNewUTF8String();
         if(utf8Spec)
            {
            char* escapedUTF8Spec = nsEscape(utf8Spec, url_Path);
            if(escapedUTF8Spec) 
               {
               keywordSpec.Append(escapedUTF8Spec);
               NS_NewURI(aURI, keywordSpec.GetBuffer(), nsnull);
               nsMemory::Free(escapedUTF8Spec);
               } // escapedUTF8Spec
            nsMemory::Free(utf8Spec);
            } // utf8Spec
         } // keyword 
      } // FindChar

   if(*aURI)
      return NS_OK;

   return NS_ERROR_FAILURE;
}

NS_IMETHODIMP nsDocShell::GetCurrentDocumentOwner(nsISupports** aOwner)
{
    nsresult rv;
    *aOwner = nsnull;
    nsCOMPtr<nsIDocumentViewer> docv(do_QueryInterface(mContentViewer));
    if (!docv) return NS_ERROR_FAILURE;
    nsCOMPtr<nsIDocument> doc;
    rv = docv->GetDocument(*getter_AddRefs(doc));
    if (NS_FAILED(rv) || !doc) return NS_ERROR_FAILURE;
    nsCOMPtr<nsIPrincipal> principal;
    rv = doc->GetPrincipal(getter_AddRefs(principal));
    if (NS_FAILED(rv) || !principal) return NS_ERROR_FAILURE;
    rv = principal->QueryInterface(NS_GET_IID(nsISupports),(void**)aOwner);
    return rv;
}

NS_IMETHODIMP nsDocShell::DoURILoad(nsIURI* aURI, nsIURI* aReferrerURI,  
   nsISupports* aOwner, nsURILoadCommand aLoadCmd, const char* aWindowTarget, 
   nsIInputStream* aPostData)
{

  // if the load cmd is a user click....and we are supposed to try using
  // external default protocol handlers....then try to see if we have one for
  // this protocol
  if (mUseExternalProtocolHandler && aLoadCmd == nsIURILoader::viewUserClick)
  {
    nsXPIDLCString urlScheme;
    aURI->GetScheme(getter_Copies(urlScheme));
    nsCOMPtr<nsIExternalProtocolService> extProtService (do_GetService(NS_EXTERNALPROTOCOLSERVICE_PROGID));
    PRBool haveHandler = PR_FALSE;
    extProtService->ExternalProtocolHandlerExists(urlScheme, &haveHandler);
    if (haveHandler)
      return extProtService->LoadUrl(aURI);
  }
   nsCOMPtr<nsIURILoader> uriLoader(do_GetService(NS_URI_LOADER_PROGID));
   NS_ENSURE_TRUE(uriLoader, NS_ERROR_FAILURE);

   // we need to get the load group from our load cookie so we can pass it into open uri...
   nsCOMPtr<nsILoadGroup> loadGroup;
   NS_ENSURE_SUCCESS(
      uriLoader->GetLoadGroupForContext(NS_STATIC_CAST(nsIDocShell*, this),
      getter_AddRefs(loadGroup)), NS_ERROR_FAILURE);

   // open a channel for the url
   nsCOMPtr<nsIChannel> channel;
   nsresult rv;
   rv = NS_OpenURI(getter_AddRefs(channel), aURI, nsnull, loadGroup, 
                     NS_STATIC_CAST(nsIInterfaceRequestor*, this));
   if(NS_FAILED(rv))
      {
      if(NS_ERROR_DOM_RETVAL_UNDEFINED == rv) // if causing the channel changed the
         return NS_OK;                        // dom and there is nothing else to do
      else
         return NS_ERROR_FAILURE;
      }
   channel->SetOriginalURI(aURI);
   
   nsCOMPtr<nsIHTTPChannel> httpChannel(do_QueryInterface(channel));
   if(httpChannel)
      {
      // figure out if we need to set the post data stream on the channel...
      // right now, this is only done for http channels.....
      if(aPostData)
         {
         // XXX it's a bit of a hack to rewind the postdata stream here but
         // it has to be done in case the post data is being reused multiple
         // times.
         nsCOMPtr<nsIRandomAccessStore> postDataRandomAccess(do_QueryInterface(aPostData));
         if (postDataRandomAccess)
         {
             postDataRandomAccess->Seek(PR_SEEK_SET, 0);
         }

	     nsCOMPtr<nsIAtom> method = NS_NewAtom ("POST");
         httpChannel->SetRequestMethod(method);
         httpChannel->SetUploadStream(aPostData);
         }
      // Set the referrer explicitly
      if(aReferrerURI) // Referrer is currenly only set for link clicks here.
         httpChannel->SetReferrer(aReferrerURI, 
                                    nsIHTTPChannel::REFERRER_LINK_CLICK);
      }
      else
      {
        // If an owner was not provided, we want to inherit the principal from the current document iff we 
        // are dealing with a JS or a data url.
        nsCOMPtr<nsISupports> owner = aOwner;
        nsCOMPtr<nsIStreamIOChannel> ioChannel(do_QueryInterface(channel));
        if(ioChannel) // Might be a javascript: URL load, need to set owner
        {
           static const char jsSchemeName[] = "javascript";
           char* scheme;
           aURI->GetScheme(&scheme);
           if (PL_strcasecmp(scheme, jsSchemeName) == 0)
           {
             if (!owner)  // only try to call GetCurrentDocumentOwner if we are a JS url or a data url (hence the code duplication)
               GetCurrentDocumentOwner(getter_AddRefs(owner));
              channel->SetOwner(owner);
           }
           if (scheme)
               nsCRT::free(scheme);
        }
        else
        { // Also set owner for data: URLs
           nsCOMPtr<nsIDataChannel> dataChannel(do_QueryInterface(channel));
           if (dataChannel)
           {
             if (!owner)
               GetCurrentDocumentOwner(getter_AddRefs(owner));
             channel->SetOwner(owner);
           }
        }
      }

   NS_ENSURE_SUCCESS(DoChannelLoad(channel, aLoadCmd, aWindowTarget, uriLoader), NS_ERROR_FAILURE);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::DoChannelLoad(nsIChannel *aChannel, nsURILoadCommand aLoadCmd, 
                                       const char* aWindowTarget, nsIURILoader *aURILoader)
{
   // Mark the channel as being a document URI...
   nsLoadFlags loadAttribs = 0;
   (void)aChannel->GetLoadAttributes(&loadAttribs);
   loadAttribs |= nsIChannel::LOAD_DOCUMENT_URI;
  
  	switch ( mLoadType )
  	{
  	 case nsIDocShellLoadInfo::loadHistory:
  	 		loadAttribs |= nsIChannel::VALIDATE_NEVER;
  	 		break;
  	 		
  	 case nsIDocShellLoadInfo::loadReloadNormal:
  	 			loadAttribs |= nsIChannel::FORCE_VALIDATION;
  	 		break;
  	 		
  	 case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
  	 		loadAttribs |= nsIChannel::FORCE_RELOAD;
  	 		break;
  	 case nsIDocShellLoadInfo::loadRefresh:
  	 		loadAttribs |= nsIChannel::FORCE_RELOAD;
  	 		break;
     case nsIDocShellLoadInfo::loadNormal:
     case nsIDocShellLoadInfo::loadLink:
		   // Set cache checking flags
		   if ( mPrefs )
		   {
		   		PRInt32 prefSetting;
		   		if ( NS_SUCCEEDED( 	mPrefs->GetIntPref( "browser.cache.check_doc_frequency" , &prefSetting) ) )
		   		{
		   			switch ( prefSetting )
		   			{
		   				case 0:
		   					loadAttribs |= nsIChannel::VALIDATE_ONCE_PER_SESSION;
		   					break;
		   				case 1:
		   					loadAttribs |= nsIChannel::VALIDATE_ALWAYS;
		   					break;
		   				case 2:
		   					loadAttribs |= nsIChannel::VALIDATE_NEVER;
		   					break;
		   			}
		   		}
			   }
			break;
   }
   
   (void) aChannel->SetLoadAttributes(loadAttribs);

   NS_ENSURE_SUCCESS(aURILoader->OpenURI(aChannel, aLoadCmd,
      aWindowTarget, NS_STATIC_CAST(nsIDocShell*, this)), NS_ERROR_FAILURE);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::ScrollIfAnchor(nsIURI* aURI, PRBool* aWasAnchor)
{
    NS_ASSERTION(aURI, "null uri arg");
    NS_ASSERTION(aWasAnchor, "null anchor arg");

    if (aURI == nsnull || aWasAnchor == nsnull)
    {
        return NS_ERROR_FAILURE;
    }

    *aWasAnchor = PR_FALSE;
 
    if (!mCurrentURI)
    {
        return NS_OK;
    }

    nsresult rv;

    // NOTE: we assume URIs are absolute for comparison purposes

    nsXPIDLCString currentSpec;
    NS_ENSURE_SUCCESS(mCurrentURI->GetSpec(getter_Copies(currentSpec)), NS_ERROR_FAILURE);
 
    nsXPIDLCString newSpec;
    NS_ENSURE_SUCCESS(aURI->GetSpec(getter_Copies(newSpec)), NS_ERROR_FAILURE);

    // Search for hash marks in the current URI and the new URI and
    // take a copy of everything to the left of the hash for
    // comparison.

    const char kHash = '#';

    // Split the new URI into a left and right part
    nsAutoString sNew; sNew.AssignWithConversion(newSpec);
    nsAutoString sNewLeft;
    nsAutoString sNewRef;
    PRInt32 hashNew = sNew.FindChar(kHash);
    if (hashNew == 0)
    {
        return NS_OK; // Strange URI 
    }
    else if (hashNew > 0)
    {
        sNew.Left(sNewLeft, hashNew);
        sNew.Right(sNewRef, sNew.Length() - hashNew - 1);
    }
    else
    {
        sNewLeft = sNew;
    }

    // Split the current URI in a left and right part
    nsAutoString sCurrent; sCurrent.AssignWithConversion(currentSpec);
    nsAutoString sCurrentLeft;
    PRInt32 hashCurrent = sCurrent.FindChar(kHash);
    if (hashCurrent == 0)
    {
        return NS_OK; // Strange URI 
    }
    else if (hashCurrent > 0)
    {
        sCurrent.Left(sCurrentLeft, hashCurrent);
    }
    else
    {
        sCurrentLeft = sCurrent;
    }

    // Exit when there are no anchors
    if (hashNew <= 0 && hashCurrent <= 0)
    {
        return NS_OK;
    }

    // Compare the URIs.
    //
    // NOTE: this is a case sensitive comparison because some parts of the
    // URI are case sensitive, and some are not. i.e. the domain name
    // is case insensitive but the the paths are not.
    //
    // This means that comparing "http://www.ABC.com/" to "http://www.abc.com/"
    // will fail this test.
 
    if (sCurrentLeft.CompareWithConversion(sNewLeft, PR_FALSE, -1) != 0)
    {
        return NS_OK; // URIs not the same
    }

    // Both the new and current URIs refer to the same page. We can now
    // browse to the hash stored in the new URI.

    if (!sNewRef.IsEmpty())
    {
        nsCOMPtr<nsIPresShell> shell = nsnull;
        rv = GetPresShell(getter_AddRefs(shell));
        if (NS_SUCCEEDED(rv) && shell)
        {
            *aWasAnchor = PR_TRUE;
            rv = shell->GoToAnchor(sNewRef);
        }
    }
    else
    {
        // A bit of a hack - scroll to the top of the page.
        SetCurScrollPosEx(0, 0);
        *aWasAnchor = PR_TRUE;
    }

    return NS_OK;
}


NS_IMETHODIMP
nsDocShell::OnNewURI(nsIURI *aURI, nsIChannel *aChannel, nsDocShellInfoLoadType aLoadType)
{
    NS_ASSERTION(aURI, "uri is null");

    UpdateCurrentGlobalHistory();
    PRBool updateHistory = PR_TRUE;

    // Determine if this type of load should update history   
    switch(aLoadType)
    {
    case nsIDocShellLoadInfo::loadHistory:
    case nsIDocShellLoadInfo::loadReloadNormal:
    case nsIDocShellLoadInfo::loadReloadBypassCache:
    case nsIDocShellLoadInfo::loadReloadBypassProxy:
    case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
        updateHistory = PR_FALSE;
        break;

    case nsIDocShellLoadInfo::loadNormal:
    case nsIDocShellLoadInfo::loadRefresh:
    case nsIDocShellLoadInfo::loadNormalReplace:
    case nsIDocShellLoadInfo::loadLink:
        break;

    default:
        NS_ERROR("Need to update case");
        break;
    } 

  if (updateHistory) { // Page load not from SH
    // Update session history if necessary...
    if (!LSHE && (mItemType == typeContent)) {
      /* 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
       */
      (void) AddToSessionHistory(aURI, aChannel, getter_AddRefs(LSHE));
    }

    // Update Global history if necessary...
    updateHistory = PR_FALSE;
    ShouldAddToGlobalHistory(aURI, &updateHistory);
    if(updateHistory) {
      AddToGlobalHistory(aURI);
    }
  }

   SetCurrentURI(aURI);
   nsCOMPtr<nsIHTTPChannel> httpChannel(do_QueryInterface(aChannel));
   if(httpChannel)
   {
      nsCOMPtr<nsIURI> referrer;
      httpChannel->GetReferrer(getter_AddRefs(referrer));
      SetReferrerURI(referrer);

      nsXPIDLCString refreshHeader;
      nsCOMPtr<nsIAtom> refreshAtom ( dont_AddRef( NS_NewAtom("refresh") ) );

      httpChannel -> GetResponseHeader (refreshAtom, getter_Copies (refreshHeader));

      if (refreshHeader)
      {
        nsCOMPtr<nsIURI> baseURI = mCurrentURI;

        PRInt32 millis = -1;
        PRUnichar *uriAttrib = nsnull;
        nsString result; result.AssignWithConversion (refreshHeader);

        PRInt32 semiColon = result.FindCharInSet(";,");
        nsAutoString token;
        if (semiColon > -1)
            result.Left(token, semiColon);
        else
            token = result;
  
        PRBool done = PR_FALSE;
        while (!done && !token.IsEmpty()) {
            token.CompressWhitespace();
            if (millis == -1 && nsCRT::IsAsciiDigit(token.First())) {
                PRInt32 i = 0;
                PRUnichar value = nsnull;
                while ((value = token[i++])) {
                    if (!nsCRT::IsAsciiDigit(value)) {
                        i = -1;
                        break;
                     }
                }
            
                if (i > -1) {
                    PRInt32 err;
                    millis = token.ToInteger(&err) * 1000;
                } else {
                    done = PR_TRUE;
                }
            } else {
                   done = PR_TRUE;
            }
            if (done) {
                PRInt32 loc = token.FindChar('=');
                    if (loc > -1)
                        token.Cut(0, loc+1);
                     token.Trim(" \"'");
                     uriAttrib = token.ToNewUnicode();
            } else {
                // Increment to the next token.
                    if (semiColon > -1) {
                        semiColon++;
                        PRInt32 semiColon2 = result.FindCharInSet(";,", semiColon);
                        if (semiColon2 == -1) semiColon2 = result.Length();
                        result.Mid(token, semiColon, semiColon2 - semiColon);
                        semiColon = semiColon2;
                     } else {
                         done = PR_TRUE;
                    }
            }
        } // end while

        nsCOMPtr<nsIURI> uri;
        if (!uriAttrib) {
            uri = baseURI;
        } else {
            NS_NewURI(getter_AddRefs(uri), uriAttrib, baseURI);
            nsMemory::Free(uriAttrib);
        }

        RefreshURI (uri, millis, PR_FALSE);
      }
   }

   mInitialPageLoad = PR_FALSE;
   return NS_OK;
}

NS_IMETHODIMP nsDocShell::OnLoadingSite(nsIChannel* aChannel)
{
    nsCOMPtr<nsIURI> uri;
    aChannel->GetURI(getter_AddRefs(uri));
    NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);

    OnNewURI(uri, aChannel, mLoadType);

    return NS_OK;
}

void nsDocShell::SetCurrentURI(nsIURI* aURI)
{
   mCurrentURI = aURI; //This assignment addrefs
   
   nsCOMPtr<nsIDocumentLoader> loader(do_GetInterface(mLoadCookie));
   
   NS_ASSERTION(loader, "No document loader");
   if (loader) {
     loader->FireOnLocationChange(nsnull, nsnull, aURI);
   }
}

void nsDocShell::SetReferrerURI(nsIURI* aURI)
{
   mReferrerURI = aURI; // This assigment addrefs
}

//*****************************************************************************
// nsDocShell: Session History
//*****************************************************************************   
PRBool nsDocShell::ShouldAddToSessionHistory(nsIURI* aURI)
{
  nsresult rv;
  nsXPIDLCString buffer;
  nsCAutoString schemeStr;

  rv = aURI->GetScheme(getter_Copies(buffer));
  if (NS_FAILED(rv)) return PR_FALSE;

  schemeStr = buffer;
  if(schemeStr.Equals("about")) {
    rv = aURI->GetPath(getter_Copies(buffer));
    if (NS_FAILED(rv)) return PR_FALSE;

    schemeStr = buffer;
    if(schemeStr.Equals("blank")) {
      return PR_FALSE;
    }
  }
  return PR_TRUE;
}

nsresult nsDocShell::AddToSessionHistory(nsIURI *aURI,
                                         nsIChannel *aChannel,
                                         nsISHEntry **aNewEntry)
{
  nsresult rv;
  nsCOMPtr<nsISHEntry> entry;
  PRBool shouldPersist;

  shouldPersist = ShouldAddToSessionHistory(aURI);

  //
  // If the entry is being replaced in SH, then just use the
  // current entry...
  //
  if(mSessionHistory && nsIDocShellLoadInfo::loadNormalReplace == mLoadType) {
    PRInt32 index = 0;
    mSessionHistory->GetIndex(&index);
    mSessionHistory->GetEntryAtIndex(index, PR_FALSE, getter_AddRefs(entry));
  }

  // Create a new entry if necessary.
  if(!entry) {
    entry = do_CreateInstance(NS_SHENTRY_PROGID);

    if (!entry) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
  }

  // Get the post data
  nsCOMPtr<nsIInputStream> inputStream;
  if (aChannel) {
    nsCOMPtr<nsIHTTPChannel> httpChannel(do_QueryInterface(aChannel));

    if(httpChannel) {
      httpChannel->GetUploadStream(getter_AddRefs(inputStream));
    }
  }

  //Title is set in nsDocShell::SetTitle()
  entry->Create(aURI,           // uri
                nsnull,         // Title
                nsnull,         // DOMDocument
                inputStream,    // Post data stream
                nsnull);        // LayoutHistory state
  //
  // Add the new entry to session history.
  //
  // If no Session History component is available in the parent DocShell
  // heirarchy, then AddChildSHEntry(...) will fail and the new entry
  // will be deleted when it loses scope...
  //
  if (mLoadType != nsIDocShellLoadInfo::loadNormalReplace) {
     if (mSessionHistory) 	  
         rv = mSessionHistory->AddEntry(entry, shouldPersist); 
	 else
         rv = AddChildSHEntry(nsnull, entry, mChildOffset);
  }

  // Return the new SH entry...
  if (aNewEntry) {
    *aNewEntry = nsnull;
    if (NS_SUCCEEDED(rv)) {
      *aNewEntry = entry;
      NS_ADDREF(*aNewEntry);
    }
  }

  return rv;
}

 /* 
  * Save the HistoryLayoutState for this page before we leave it.
  */
NS_IMETHODIMP nsDocShell::UpdateCurrentSessionHistory()
{
   nsresult rv = NS_OK;
   if(!mInitialPageLoad && mSessionHistory) {
  
     PRInt32 index = 0;
     mSessionHistory->GetIndex(&index);
     if (-1 < index) {
 
       nsCOMPtr<nsISHEntry> entry;
       rv = mSessionHistory->GetEntryAtIndex(index, PR_FALSE, getter_AddRefs(entry));
       if (NS_SUCCEEDED(rv) && entry) {
 
         nsCOMPtr<nsIPresShell> shell;
         rv = GetPresShell(getter_AddRefs(shell));            
         if (NS_SUCCEEDED(rv) && shell) {
 
           nsCOMPtr<nsILayoutHistoryState> layoutState;
           rv = shell->CaptureHistoryState(getter_AddRefs(layoutState), PR_TRUE);
           if (NS_SUCCEEDED(rv) && layoutState) {
 
             rv = entry->SetLayoutHistoryState(layoutState);
           }
         }
       }
     }
   }
   return rv;
   
}

#ifdef SH_IN_FRAMES
NS_IMETHODIMP nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry, nsDocShellInfoLoadType aLoadType)
#else
NS_IMETHODIMP nsDocShell::LoadHistoryEntry(nsISHEntry* aEntry)
#endif
{
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsIInputStream> postData;
   PRBool repost = PR_TRUE;

   NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);

   NS_ENSURE_SUCCESS(aEntry->GetURI(getter_AddRefs(uri)), NS_ERROR_FAILURE);
   NS_ENSURE_SUCCESS(aEntry->GetPostData(getter_AddRefs(postData)),
      NS_ERROR_FAILURE);

   /* Ask whether to repost form post data */
   if (postData) {
       nsCOMPtr<nsIPrompt> prompter;
       nsCOMPtr<nsIStringBundle> stringBundle;
       GetPromptAndStringBundle(getter_AddRefs(prompter), 
          getter_AddRefs(stringBundle));
 
       if (stringBundle && prompter) {
          nsXPIDLString messageStr;
          nsresult rv = stringBundle->GetStringFromName(NS_ConvertASCIItoUCS2("repost").GetUnicode(), 
          getter_Copies(messageStr));
          
		  if (NS_SUCCEEDED(rv) && messageStr) {
             prompter->Confirm(nsnull, messageStr, &repost);
		     if (!repost)
                postData = nsnull;
		  }
	   }
    }
    

#ifdef SH_IN_FRAMES
   NS_ENSURE_SUCCESS(InternalLoad(uri, nsnull, nsnull, nsnull, postData, aLoadType, aEntry),
      NS_ERROR_FAILURE);
#else
   NS_ENSURE_SUCCESS(InternalLoad(uri, nsnull, nsnull, nsnull, postData, nsIDocShellLoadInfo::loadHistory),
      NS_ERROR_FAILURE);
#endif 

   return NS_OK;
}

/*
NS_IMETHODIMP
nsDocShell::GetSHEForChild(PRInt32 aChildOffset, nsISHEntry ** aResult)
{
    if (OSHE) {
		nsCOMPtr<nsISHContainer> container(do_QueryInterface(OSHE));
		if (container)
           return container->GetChildAt(aChildOffset, aResult);
	}
    return NS_ERROR_FAILURE;

}
*/
NS_IMETHODIMP
nsDocShell::PersistLayoutHistoryState()
{
	nsresult rv;
	if (OSHE) {
      nsCOMPtr<nsIPresShell> shell;

      rv = GetPresShell(getter_AddRefs(shell));            
      if (NS_SUCCEEDED(rv) && shell) {
         nsCOMPtr<nsILayoutHistoryState> layoutState;
         rv = shell->CaptureHistoryState(getter_AddRefs(layoutState), PR_TRUE);
         if (NS_SUCCEEDED(rv) && layoutState) {
             rv = OSHE->SetLayoutHistoryState(layoutState);
         }
      }

	}
	return rv;
}

#if 0
NS_IMETHODIMP
nsDocShell::CloneAndReplace(nsISHEntry * src, nsISHEntry * cloneRef,
							nsISHEntry * replaceEntry, nsISHEntry * dest)
{
	nsresult result;
	if (!src || !replaceEntry || !cloneRef || !dest)
		return NS_ERROR_FAILURE;
//    NS_ENSURE_ARG_POINTER(dest, NS_ERROR_FAILURE);
//	static  PRBool firstTime = PR_TRUE;
//	static nsISHEntry * rootSHEntry = nsnull;
  
	if (src == cloneRef) {
		// release the original object before assigning a new one.
	   NS_RELEASE(dest);
       dest = replaceEntry;
	}
	else {
    nsCOMPtr<nsIURI> uri;
	nsCOMPtr<nsIInputStream> postdata;
	nsCOMPtr<nsILayoutHistoryState> LHS;
	PRUnichar * title=nsnull;
	nsCOMPtr<nsISHEntry> parent;

	src->GetURI(getter_AddRefs(uri));
	src->GetPostData(getter_AddRefs(postdata));
	src->GetTitle(&title);
	src->GetLayoutHistoryState(getter_AddRefs(LHS));
	//XXX Is this correct? parent is a weak ref in nsISHEntry
	src->GetParent(getter_AddRefs(parent));

	// XXX do we care much about valid values for these uri, title etc....
	dest->SetURI(uri);
	dest->SetPostData(postdata);
	dest->SetLayoutHistoryState(LHS);
	dest->SetTitle(title);
	dest->SetParent(parent);
	}
	/*
	if (firstTime) {
		// Save the root of the hierarchy in the result parameter
		rootSHEntry = dest;
		firstTime = PR_FALSE;
	}
    */
	PRInt32 childCount= 0;

	nsCOMPtr<nsISHContainer> srcContainer(do_QueryInterface(src));
	if (!srcContainer)
		return NS_ERROR_FAILURE;
	nsCOMPtr<nsISHContainer> destContainer(do_QueryInterface(dest));
	if (!destContainer)
		return NS_ERROR_FAILURE;
	srcContainer->GetChildCount(&childCount);
	for(PRInt32 i = 0; i<childCount; i++) {
		nsCOMPtr<nsISHEntry> srcChild;
		srcContainer->GetChildAt(i, getter_AddRefs(srcChild));
		if (!srcChild)
			return NS_ERROR_FAILURE;
		nsCOMPtr<nsISHEntry>  destChild(do_CreateInstance(NS_SHENTRY_PROGID));
		if (!NS_SUCCEEDED(result))
			return result;
		result = CloneAndReplace(srcChild, cloneRef, replaceEntry, destChild);
		if (!NS_SUCCEEDED(result))
			return result;
		result = destContainer->AddChild(destChild, i);
		if (!NS_SUCCEEDED(result))
			return result;
	}

    
	return result;

}
#else
NS_IMETHODIMP
nsDocShell::CloneAndReplace(nsISHEntry * src, nsISHEntry * cloneRef,
							nsISHEntry * replaceEntry, nsISHEntry ** resultEntry)
{
	nsresult result = NS_OK;
	NS_ENSURE_ARG_POINTER(resultEntry);
	if (!src || !replaceEntry || !cloneRef)
		return NS_ERROR_FAILURE;
//    NS_ENSURE_ARG_POINTER(dest, NS_ERROR_FAILURE);
//	static  PRBool firstTime = PR_TRUE;
//	static nsISHEntry * rootSHEntry = nsnull;
    nsISHEntry * dest = *resultEntry;
	dest = (nsISHEntry *) nsnull;

	if (src == cloneRef) {
		// release the original object before assigning a new one.
	   //NS_RELEASE(dest);
		
      dest = replaceEntry;
	  *resultEntry = dest;
	  NS_IF_ADDREF(*resultEntry);
	}
	else {
    nsCOMPtr<nsIURI> uri;
	nsCOMPtr<nsIInputStream> postdata;
	nsCOMPtr<nsILayoutHistoryState> LHS;
	PRUnichar * title=nsnull;
	nsCOMPtr<nsISHEntry> parent;
	result = nsComponentManager::CreateInstance(NS_SHENTRY_PROGID, NULL,
			NS_GET_IID(nsISHEntry), (void **) &dest);
	if (!NS_SUCCEEDED(result))
      return result;

	src->GetURI(getter_AddRefs(uri));
	src->GetPostData(getter_AddRefs(postdata));
	src->GetTitle(&title);
	src->GetLayoutHistoryState(getter_AddRefs(LHS));
	//XXX Is this correct? parent is a weak ref in nsISHEntry
	src->GetParent(getter_AddRefs(parent));

	// XXX do we care much about valid values for these uri, title etc....
	dest->SetURI(uri);
	dest->SetPostData(postdata);
	dest->SetLayoutHistoryState(LHS);
	dest->SetTitle(title);
	dest->SetParent(parent);
	*resultEntry = dest;
	
	}
	*resultEntry = dest;
	/*
	if (firstTime) {
		// Save the root of the hierarchy in the result parameter
		rootSHEntry = dest;
		firstTime = PR_FALSE;
	}
    */
	PRInt32 childCount= 0;

	nsCOMPtr<nsISHContainer> srcContainer(do_QueryInterface(src));
	if (!srcContainer)
		return NS_ERROR_FAILURE;
	nsCOMPtr<nsISHContainer> destContainer(do_QueryInterface(dest));
	if (!destContainer)
		return NS_ERROR_FAILURE;
	srcContainer->GetChildCount(&childCount);
	for(PRInt32 i = 0; i<childCount; i++) {
		nsCOMPtr<nsISHEntry> srcChild;
		srcContainer->GetChildAt(i, getter_AddRefs(srcChild));
		if (!srcChild)
			return NS_ERROR_FAILURE;
		nsCOMPtr<nsISHEntry>  destChild;
		if (!NS_SUCCEEDED(result))
			return result;
		result = CloneAndReplace(srcChild, cloneRef, replaceEntry, getter_AddRefs(destChild));
		if (!NS_SUCCEEDED(result))
			return result;
		result = destContainer->AddChild(destChild, i);
		if (!NS_SUCCEEDED(result))
			return result;
	}

    
	return result;

}
#endif  /* 0 */

//*****************************************************************************
// nsDocShell: Global History
//*****************************************************************************   

NS_IMETHODIMP nsDocShell::ShouldAddToGlobalHistory(nsIURI* aURI, PRBool* aShouldAdd)
{
   *aShouldAdd = PR_FALSE;
   if(!mGlobalHistory || !aURI || (typeContent != mItemType))
      return NS_OK;

   nsXPIDLCString scheme;
   NS_ENSURE_SUCCESS(aURI->GetScheme(getter_Copies(scheme)), NS_ERROR_FAILURE);
   
   nsAutoString schemeStr; schemeStr.AssignWithConversion(scheme);

   // The model is really if we don't know differently then add which basically
   // means we are suppose to try all the things we know not to allow in and
   // then if we don't bail go on and allow it in.  But here lets compare
   // against the most common case we know to allow in and go on and say yes
   // to it.
   if(schemeStr.EqualsWithConversion("http") || schemeStr.EqualsWithConversion("https"))
      {
      *aShouldAdd = PR_TRUE;
      return NS_OK;
      }

   if(schemeStr.EqualsWithConversion("about") || schemeStr.EqualsWithConversion("imap") ||
      schemeStr.EqualsWithConversion("news") || schemeStr.EqualsWithConversion("mailbox"))
      return NS_OK;

   *aShouldAdd = PR_TRUE;

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::AddToGlobalHistory(nsIURI* aURI)
{
   NS_ENSURE_STATE(mGlobalHistory);

   nsXPIDLCString spec;
   NS_ENSURE_SUCCESS(aURI->GetSpec(getter_Copies(spec)), NS_ERROR_FAILURE);

   NS_ENSURE_SUCCESS(mGlobalHistory->AddPage(spec, nsnull, PR_Now()), 
      NS_ERROR_FAILURE);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::UpdateCurrentGlobalHistory()
{
   // XXX Add code here that needs to update the current history item
   return NS_OK;
}

//*****************************************************************************
// nsDocShell: Helper Routines
//*****************************************************************************   

nsresult nsDocShell::SetLoadCookie(nsISupports *aCookie)
{
  // Remove the DocShell as a listener of the old WebProgress...
  if (mLoadCookie) {
    nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(mLoadCookie));

    if (webProgress) {
      webProgress->RemoveProgressListener(this);
    }
  }

  mLoadCookie = aCookie;

  // Add the DocShell as a listener to the new WebProgress...
  if (mLoadCookie) {
    nsCOMPtr<nsIWebProgress> webProgress(do_QueryInterface(mLoadCookie));

    if (webProgress) {
      webProgress->AddProgressListener(this);
    }
  }
  return NS_OK;
}


nsresult nsDocShell::GetLoadCookie(nsISupports **aResult)
{
  *aResult = mLoadCookie;
  NS_IF_ADDREF(*aResult);

  return NS_OK;
}


nsDocShellInitInfo* nsDocShell::InitInfo()
{
   if(mInitInfo)
      return mInitInfo;
   return mInitInfo = new nsDocShellInitInfo();
}

#define DIALOG_STRING_URI "chrome://global/locale/appstrings.properties"

NS_IMETHODIMP nsDocShell::GetPromptAndStringBundle(nsIPrompt** aPrompt,
   nsIStringBundle** aStringBundle)
{
   NS_ENSURE_SUCCESS(GetInterface(NS_GET_IID(nsIPrompt), (void**)aPrompt), NS_ERROR_FAILURE);

   nsCOMPtr<nsILocaleService> localeService(do_GetService(NS_LOCALESERVICE_PROGID));
   NS_ENSURE_TRUE(localeService, NS_ERROR_FAILURE);

   nsCOMPtr<nsILocale> locale;
   localeService->GetSystemLocale(getter_AddRefs(locale));
   NS_ENSURE_TRUE(locale, NS_ERROR_FAILURE);

   nsCOMPtr<nsIStringBundleService> stringBundleService(do_GetService(NS_STRINGBUNDLE_PROGID));
   NS_ENSURE_TRUE(stringBundleService, NS_ERROR_FAILURE);

   NS_ENSURE_SUCCESS(stringBundleService->CreateBundle(DIALOG_STRING_URI, locale, 
      getter_AddRefs(aStringBundle)), NS_ERROR_FAILURE);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::GetChildOffset(nsIDOMNode *aChild, nsIDOMNode* aParent,
   PRInt32* aOffset)
{
   NS_ENSURE_ARG_POINTER(aChild || aParent);
   
   nsCOMPtr<nsIDOMNodeList> childNodes;
   NS_ENSURE_SUCCESS(aParent->GetChildNodes(getter_AddRefs(childNodes)),
      NS_ERROR_FAILURE);
   NS_ENSURE_TRUE(childNodes, NS_ERROR_FAILURE);

   PRInt32 i=0;

   for( ; PR_TRUE; i++)
      {
      nsCOMPtr<nsIDOMNode> childNode;
      NS_ENSURE_SUCCESS(childNodes->Item(i, getter_AddRefs(childNode)), 
         NS_ERROR_FAILURE);
      NS_ENSURE_TRUE(childNode, NS_ERROR_FAILURE);

      if(childNode.get() == aChild)
         {
         *aOffset = i;
         return NS_OK;
         }
      }

   return NS_ERROR_FAILURE;
}

NS_IMETHODIMP nsDocShell::GetRootScrollableView(nsIScrollableView** aOutScrollView)
{
   NS_ENSURE_ARG_POINTER(aOutScrollView);
   
   nsCOMPtr<nsIPresShell> shell;
   NS_ENSURE_SUCCESS(GetPresShell(getter_AddRefs(shell)), NS_ERROR_FAILURE);

   nsCOMPtr<nsIViewManager> viewManager;
   NS_ENSURE_SUCCESS(shell->GetViewManager(getter_AddRefs(viewManager)),
      NS_ERROR_FAILURE);

   NS_ENSURE_SUCCESS(viewManager->GetRootScrollableView(aOutScrollView),
      NS_ERROR_FAILURE);

   if (*aOutScrollView == nsnull)
   {
      return NS_ERROR_FAILURE;
   }
   return NS_OK;
} 

NS_IMETHODIMP nsDocShell::EnsureContentListener()
{
   if(mContentListener)
      return NS_OK;
   
   mContentListener = new nsDSURIContentListener();
   NS_ENSURE_TRUE(mContentListener, NS_ERROR_OUT_OF_MEMORY);

   NS_ADDREF(mContentListener);
   mContentListener->DocShell(this);

   return NS_OK;
}

NS_IMETHODIMP nsDocShell::EnsureScriptEnvironment()
{
   if(mScriptContext)
      return NS_OK;

   NS_NewScriptGlobalObject(getter_AddRefs(mScriptGlobal));
   NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);

   mScriptGlobal->SetDocShell(NS_STATIC_CAST(nsIDocShell*, this));
   mScriptGlobal->SetGlobalObjectOwner(
      NS_STATIC_CAST(nsIScriptGlobalObjectOwner*, this));

   NS_CreateScriptContext(mScriptGlobal, getter_AddRefs(mScriptContext));
   NS_ENSURE_TRUE(mScriptContext, NS_ERROR_FAILURE);

   return NS_OK;
}

PRBool nsDocShell::IsFrame()
{
   if(mParent)
      {
      PRInt32 parentType = ~mItemType;  // Not us
      mParent->GetItemType(&parentType);
      if(parentType == mItemType)   // This is a frame
         return PR_TRUE;
      }

   return PR_FALSE;
}

//*****************************************************************************
//***    nsRefreshTimer: Object Management
//*****************************************************************************

nsRefreshTimer::nsRefreshTimer() : mRepeat(PR_FALSE), mDelay(0)
{
  NS_INIT_REFCNT();
}

nsRefreshTimer::~nsRefreshTimer()
{
}

//*****************************************************************************
// nsRefreshTimer::nsISupports
//*****************************************************************************   

NS_IMPL_THREADSAFE_ADDREF(nsRefreshTimer)
NS_IMPL_THREADSAFE_RELEASE(nsRefreshTimer)

NS_INTERFACE_MAP_BEGIN(nsRefreshTimer)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
NS_INTERFACE_MAP_END_THREADSAFE

///*****************************************************************************
// nsRefreshTimer::nsITimerCallback
//*****************************************************************************   

NS_IMETHODIMP_(void) nsRefreshTimer::Notify(nsITimer *aTimer)
{
    NS_ASSERTION(mDocShell, "DocShell is somehow null");

    if(mDocShell)
    {
        nsCOMPtr<nsIDocShellLoadInfo> loadInfo;        
        mDocShell -> CreateLoadInfo (getter_AddRefs (loadInfo));

        loadInfo  -> SetLoadType(nsIDocShellLoadInfo::loadRefresh);
        mDocShell -> LoadURI(mURI, loadInfo);
    }

   /*
    * LoadURL(...) will cancel all refresh timers... This causes the Timer and
    * its refreshData instance to be released...
    */
}

