Back to index

lightning-sunbird  0.9+nobinonly
nsSHistory.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Mozilla browser.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications, Inc.
00020  * Portions created by the Initial Developer are Copyright (C) 1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Radha Kulkarni <radha@netscape.com>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 // Local Includes 
00041 #include "nsSHistory.h"
00042 
00043 // Helper Classes
00044 #include "nsXPIDLString.h"
00045 #include "nsReadableUtils.h"
00046 
00047 // Interfaces Needed
00048 #include "nsILayoutHistoryState.h"
00049 #include "nsIDocShell.h"
00050 #include "nsIDocShellLoadInfo.h"
00051 #include "nsISHContainer.h"
00052 #include "nsIDocShellTreeItem.h"
00053 #include "nsIDocShellTreeNode.h"
00054 #include "nsIDocShellLoadInfo.h"
00055 #include "nsIServiceManager.h"
00056 #include "nsIPrefService.h"
00057 #include "nsIURI.h"
00058 #include "nsIContentViewer.h"
00059 #include "nsIPrefBranch2.h"
00060 #include "nsICacheService.h"
00061 #include "nsIObserverService.h"
00062 #include "prclist.h"
00063 
00064 // For calculating max history entries and max cachable contentviewers
00065 #include "nspr.h"
00066 #include <math.h>  // for log()
00067 
00068 #define PREF_SHISTORY_SIZE "browser.sessionhistory.max_entries"
00069 #define PREF_SHISTORY_MAX_TOTAL_VIEWERS "browser.sessionhistory.max_total_viewers"
00070 
00071 static PRInt32  gHistoryMaxSize = 50;
00072 // Max viewers allowed per SHistory objects
00073 static const PRInt32  gHistoryMaxViewers = 3;
00074 // List of all SHistory objects, used for content viewer cache eviction
00075 static PRCList gSHistoryList;
00076 // Max viewers allowed total, across all SHistory objects - negative default
00077 // means we will calculate how many viewers to cache based on total memory
00078 PRInt32 nsSHistory::sHistoryMaxTotalViewers = -1;
00079 
00080 enum HistCmd{
00081   HIST_CMD_BACK,
00082   HIST_CMD_FORWARD,
00083   HIST_CMD_GOTOINDEX,
00084   HIST_CMD_RELOAD
00085 } ;
00086 
00087 //*****************************************************************************
00088 //***      nsSHistoryObserver
00089 //*****************************************************************************
00090 
00091 class nsSHistoryObserver : public nsIObserver
00092 {
00093 
00094 public:
00095   NS_DECL_ISUPPORTS
00096   NS_DECL_NSIOBSERVER
00097 
00098   nsSHistoryObserver() {}
00099 
00100 protected:
00101   ~nsSHistoryObserver() {}
00102 };
00103 
00104 NS_IMPL_ISUPPORTS1(nsSHistoryObserver, nsIObserver)
00105 
00106 NS_IMETHODIMP
00107 nsSHistoryObserver::Observe(nsISupports *aSubject, const char *aTopic,
00108                             const PRUnichar *aData)
00109 {
00110   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
00111     nsCOMPtr<nsIPrefBranch> prefs = do_QueryInterface(aSubject);
00112     prefs->GetIntPref(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
00113                       &nsSHistory::sHistoryMaxTotalViewers);
00114     if (nsSHistory::sHistoryMaxTotalViewers < 0) {
00115       nsSHistory::sHistoryMaxTotalViewers = nsSHistory::GetMaxTotalViewers();
00116     }
00117 
00118     nsSHistory::EvictGlobalContentViewer();
00119   } else if (!strcmp(aTopic, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID) ||
00120              !strcmp(aTopic, "memory-pressure")) {
00121     nsSHistory::EvictAllContentViewers();
00122   }
00123 
00124   return NS_OK;
00125 }
00126 
00127 //*****************************************************************************
00128 //***    nsSHistory: Object Management
00129 //*****************************************************************************
00130 
00131 nsSHistory::nsSHistory() : mListRoot(nsnull), mIndex(-1), mLength(0), mRequestedIndex(-1)
00132 {
00133   // Add this new SHistory object to the list
00134   PR_APPEND_LINK(this, &gSHistoryList);
00135 }
00136 
00137 
00138 nsSHistory::~nsSHistory()
00139 {
00140   // Remove this SHistory object from the list
00141   PR_REMOVE_LINK(this);
00142 }
00143 
00144 //*****************************************************************************
00145 //    nsSHistory: nsISupports
00146 //*****************************************************************************
00147 
00148 NS_IMPL_ADDREF(nsSHistory)
00149 NS_IMPL_RELEASE(nsSHistory)
00150 
00151 NS_INTERFACE_MAP_BEGIN(nsSHistory)
00152    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISHistory)
00153    NS_INTERFACE_MAP_ENTRY(nsISHistory)
00154    NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
00155    NS_INTERFACE_MAP_ENTRY(nsISHistoryInternal)
00156 NS_INTERFACE_MAP_END
00157 
00158 //*****************************************************************************
00159 //    nsSHistory: nsISHistory
00160 //*****************************************************************************
00161 
00162 NS_IMETHODIMP
00163 nsSHistory::GetHistoryMaxTotalViewers(PRInt32 *max)
00164 {
00165     *max = sHistoryMaxTotalViewers;
00166     return NS_OK;
00167 }
00168 
00169 // static
00170 PRUint32
00171 nsSHistory::GetMaxTotalViewers()
00172 {
00173   // Calculate an estimate of how many ContentViewers we should cache based
00174   // on RAM.  This assumes that the average ContentViewer is 4MB (conservative)
00175   // and caps the max at 8 ContentViewers
00176   //
00177   // TODO: Should we split the cache memory betw. ContentViewer caching and
00178   // nsCacheService?
00179   //
00180   // RAM      ContentViewers
00181   // -----------------------
00182   // 32   Mb       0
00183   // 64   Mb       1
00184   // 128  Mb       2
00185   // 256  Mb       3
00186   // 512  Mb       5
00187   // 1024 Mb       8
00188   // 2048 Mb       8
00189   // 4096 Mb       8
00190   PRUint64 bytes = PR_GetPhysicalMemorySize();
00191 
00192   if (LL_IS_ZERO(bytes))
00193     return 0;
00194 
00195   // Conversion from unsigned int64 to double doesn't work on all platforms.
00196   // We need to truncate the value at LL_MAXINT to make sure we don't
00197   // overflow.
00198   if (LL_CMP(bytes, >, LL_MAXINT))
00199     bytes = LL_MAXINT;
00200 
00201   PRUint64 kbytes;
00202   LL_SHR(kbytes, bytes, 10);
00203 
00204   double kBytesD;
00205   LL_L2D(kBytesD, (PRInt64) kbytes);
00206 
00207   // This is essentially the same calculation as for nsCacheService,
00208   // except that we divide the final memory calculation by 4, since
00209   // we assume each ContentViewer takes on average 4MB
00210   PRUint32 viewers = 0;
00211   double x = log(kBytesD)/log(2.0) - 14;
00212   if (x > 0) {
00213     viewers    = (PRUint32)(x * x - x + 2.001); // add .001 for rounding
00214     viewers   /= 4;
00215   }
00216 
00217   // Cap it off at 8 max
00218   if (viewers > 8) {
00219     viewers = 8;
00220   }
00221   return viewers;
00222 }
00223 
00224 // static
00225 nsresult
00226 nsSHistory::Startup()
00227 {
00228   nsCOMPtr<nsIPrefService> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
00229   if (prefs) {
00230     // Session history size is only taken from the default prefs branch.
00231     // This means that it's only configurable on a per-application basis.
00232     // The goal of this is to unbreak users who have inadvertently set their
00233     // session history size to -1.
00234     nsCOMPtr<nsIPrefBranch> defaultBranch;
00235     prefs->GetDefaultBranch(nsnull, getter_AddRefs(defaultBranch));
00236     if (defaultBranch) {
00237       defaultBranch->GetIntPref(PREF_SHISTORY_SIZE, &gHistoryMaxSize);
00238     }
00239     
00240     // Allow the user to override the max total number of cached viewers,
00241     // but keep the per SHistory cached viewer limit constant
00242     nsCOMPtr<nsIPrefBranch2> branch = do_QueryInterface(prefs);
00243     if (branch) {
00244       branch->GetIntPref(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
00245                          &sHistoryMaxTotalViewers);
00246       nsSHistoryObserver* obs = new nsSHistoryObserver();
00247       if (!obs) {
00248         return NS_ERROR_OUT_OF_MEMORY;
00249       }
00250       branch->AddObserver(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
00251                           obs, PR_FALSE);
00252 
00253       nsCOMPtr<nsIObserverService> obsSvc =
00254         do_GetService("@mozilla.org/observer-service;1");
00255       if (obsSvc) {
00256         // Observe empty-cache notifications so tahat clearing the disk/memory
00257         // cache will also evict all content viewers.
00258         obsSvc->AddObserver(obs,
00259                             NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID, PR_FALSE);
00260 
00261         // Same for memory-pressure notifications
00262         obsSvc->AddObserver(obs, "memory-pressure", PR_FALSE);
00263       }
00264     }
00265   }
00266   // If the pref is negative, that means we calculate how many viewers
00267   // we think we should cache, based on total memory
00268   if (sHistoryMaxTotalViewers < 0) {
00269     sHistoryMaxTotalViewers = GetMaxTotalViewers();
00270   }
00271 
00272   // Initialize the global list of all SHistory objects
00273   PR_INIT_CLIST(&gSHistoryList);
00274   return NS_OK;
00275 }
00276 
00277 /* Add an entry to the History list at mIndex and 
00278  * increment the index to point to the new entry
00279  */
00280 NS_IMETHODIMP
00281 nsSHistory::AddEntry(nsISHEntry * aSHEntry, PRBool aPersist)
00282 {
00283   NS_ENSURE_ARG(aSHEntry);
00284 
00285   nsCOMPtr<nsISHTransaction> currentTxn;
00286 
00287   if(mListRoot)
00288     GetTransactionAtIndex(mIndex, getter_AddRefs(currentTxn));
00289 
00290   PRBool currentPersist = PR_TRUE;
00291   if(currentTxn)
00292     currentTxn->GetPersist(&currentPersist);
00293 
00294   if(!currentPersist)
00295   {
00296     NS_ENSURE_SUCCESS(currentTxn->SetSHEntry(aSHEntry),NS_ERROR_FAILURE);
00297     currentTxn->SetPersist(aPersist);
00298     return NS_OK;
00299   }
00300 
00301   nsCOMPtr<nsISHTransaction> txn(do_CreateInstance(NS_SHTRANSACTION_CONTRACTID));
00302   NS_ENSURE_TRUE(txn, NS_ERROR_FAILURE);
00303 
00304   // Notify any listener about the new addition
00305   if (mListener) {
00306     nsCOMPtr<nsISHistoryListener> listener(do_QueryReferent(mListener));
00307     if (listener) {
00308       nsCOMPtr<nsIURI> uri;
00309       nsCOMPtr<nsIHistoryEntry> hEntry(do_QueryInterface(aSHEntry));
00310       if (hEntry) {
00311         PRInt32 currentIndex = mIndex;
00312         hEntry->GetURI(getter_AddRefs(uri));
00313         listener->OnHistoryNewEntry(uri);
00314 
00315         // If a listener has changed mIndex, we need to get currentTxn again,
00316         // otherwise we'll be left at an inconsistent state (see bug 320742)
00317         if (currentIndex != mIndex)
00318           GetTransactionAtIndex(mIndex, getter_AddRefs(currentTxn));
00319       }
00320     }
00321   }
00322 
00323   // Set the ShEntry and parent for the transaction. setting the 
00324   // parent will properly set the parent child relationship
00325   txn->SetPersist(aPersist);
00326   NS_ENSURE_SUCCESS(txn->Create(aSHEntry, currentTxn), NS_ERROR_FAILURE);
00327    
00328   // A little tricky math here...  Basically when adding an object regardless of
00329   // what the length was before, it should always be set back to the current and
00330   // lop off the forward.
00331   mLength = (++mIndex + 1);
00332 
00333   // If this is the very first transaction, initialize the list
00334   if(!mListRoot)
00335     mListRoot = txn;
00336 
00337   // Purge History list if it is too long
00338   if ((gHistoryMaxSize >= 0) && (mLength > gHistoryMaxSize))
00339     PurgeHistory(mLength-gHistoryMaxSize);
00340   
00341   return NS_OK;
00342 }
00343 
00344 /* Get size of the history list */
00345 NS_IMETHODIMP
00346 nsSHistory::GetCount(PRInt32 * aResult)
00347 {
00348     NS_ENSURE_ARG_POINTER(aResult);
00349        *aResult = mLength;
00350        return NS_OK;
00351 }
00352 
00353 /* Get index of the history list */
00354 NS_IMETHODIMP
00355 nsSHistory::GetIndex(PRInt32 * aResult)
00356 {
00357     NS_ENSURE_ARG_POINTER(aResult);
00358        *aResult = mIndex;
00359        return NS_OK;
00360 }
00361 
00362 NS_IMETHODIMP
00363 nsSHistory::GetEntryAtIndex(PRInt32 aIndex, PRBool aModifyIndex, nsISHEntry** aResult)
00364 {
00365   nsresult rv;
00366   nsCOMPtr<nsISHTransaction> txn;
00367 
00368   /* GetTransactionAtIndex ensures aResult is valid and validates aIndex */
00369   rv = GetTransactionAtIndex(aIndex, getter_AddRefs(txn));
00370   if (NS_SUCCEEDED(rv) && txn) {
00371     //Get the Entry from the transaction
00372     rv = txn->GetSHEntry(aResult);
00373     if (NS_SUCCEEDED(rv) && (*aResult)) {
00374       // Set mIndex to the requested index, if asked to do so..
00375       if (aModifyIndex) {
00376         mIndex = aIndex;
00377       }
00378     } //entry
00379   }  //Transaction
00380   return rv;
00381 }
00382 
00383 
00384 /* Get the entry at a given index */
00385 NS_IMETHODIMP
00386 nsSHistory::GetEntryAtIndex(PRInt32 aIndex, PRBool aModifyIndex, nsIHistoryEntry** aResult)
00387 {
00388   nsresult rv;
00389   nsCOMPtr<nsISHEntry> shEntry;
00390   rv = GetEntryAtIndex(aIndex, aModifyIndex, getter_AddRefs(shEntry));
00391   if (NS_SUCCEEDED(rv) && shEntry) 
00392     rv = CallQueryInterface(shEntry, aResult);
00393  
00394   return rv;
00395 }
00396 
00397 /* Get the transaction at a given index */
00398 NS_IMETHODIMP
00399 nsSHistory::GetTransactionAtIndex(PRInt32 aIndex, nsISHTransaction ** aResult)
00400 {
00401      nsresult rv;
00402      NS_ENSURE_ARG_POINTER(aResult);
00403 
00404      if ((mLength <= 0) || (aIndex < 0) || (aIndex >= mLength))
00405           return NS_ERROR_FAILURE;
00406 
00407      if (!mListRoot) 
00408          return NS_ERROR_FAILURE;
00409 
00410      if (aIndex == 0)
00411         {
00412            *aResult = mListRoot;
00413            NS_ADDREF(*aResult);
00414            return NS_OK;
00415         } 
00416         PRInt32   cnt=0;
00417         nsCOMPtr<nsISHTransaction>  tempPtr;
00418        
00419        rv = GetRootTransaction(getter_AddRefs(tempPtr));
00420        if (NS_FAILED(rv) || !tempPtr)
00421                return NS_ERROR_FAILURE;
00422 
00423      while(1) {
00424        nsCOMPtr<nsISHTransaction> ptr;
00425           rv = tempPtr->GetNext(getter_AddRefs(ptr));
00426           if (NS_SUCCEEDED(rv) && ptr) {
00427           cnt++;
00428                 if (cnt == aIndex) {
00429                        *aResult = ptr;
00430                        NS_ADDREF(*aResult);
00431                        break;
00432                 }
00433                 else {
00434             tempPtr = ptr;
00435             continue;
00436                 }
00437           }  //NS_SUCCEEDED
00438           else 
00439                  return NS_ERROR_FAILURE;
00440        }  // while 
00441   
00442    return NS_OK;
00443 }
00444 
00445 #ifdef DEBUG
00446 nsresult
00447 nsSHistory::PrintHistory()
00448 {
00449 
00450       nsCOMPtr<nsISHTransaction>   txn;
00451       PRInt32 index = 0;
00452       nsresult rv;
00453 
00454       if (!mListRoot) 
00455               return NS_ERROR_FAILURE;
00456 
00457       txn = mListRoot;
00458     
00459       while (1) {
00460                     if (!txn)
00461                           break;
00462               nsCOMPtr<nsISHEntry>  entry;
00463               rv = txn->GetSHEntry(getter_AddRefs(entry));
00464               if (NS_FAILED(rv) && !entry)
00465                       return NS_ERROR_FAILURE;
00466 
00467               nsCOMPtr<nsILayoutHistoryState> layoutHistoryState;
00468               nsCOMPtr<nsIURI>  uri;
00469               PRUnichar *  title;
00470               
00471               entry->GetLayoutHistoryState(getter_AddRefs(layoutHistoryState));
00472               nsCOMPtr<nsIHistoryEntry> hEntry(do_QueryInterface(entry));
00473               if (hEntry) {
00474                 hEntry->GetURI(getter_AddRefs(uri));
00475                 hEntry->GetTitle(&title);              
00476               }
00477               
00478 #if 0
00479               nsCAutoString url;
00480                        if (uri)
00481                  uri->GetSpec(url);
00482 
00483               printf("**** SH Transaction #%d, Entry = %x\n", index, entry.get());
00484               printf("\t\t URL = %s\n", url);
00485               printf("\t\t Title = %s\n", NS_LossyConvertUCS2toASCII(title).get());
00486               printf("\t\t layout History Data = %x\n", layoutHistoryState);
00487 #endif
00488       
00489               nsMemory::Free(title);
00490               
00491 
00492               nsCOMPtr<nsISHTransaction> next;
00493               rv = txn->GetNext(getter_AddRefs(next));
00494               if (NS_SUCCEEDED(rv) && next) {
00495                       txn = next;
00496                       index++;
00497                       continue;
00498               }
00499               else
00500                       break;
00501       }
00502       
00503   return NS_OK;
00504 }
00505 #endif
00506 
00507 
00508 NS_IMETHODIMP
00509 nsSHistory::GetRootTransaction(nsISHTransaction ** aResult)
00510 {
00511     NS_ENSURE_ARG_POINTER(aResult);
00512     *aResult=mListRoot;
00513       NS_IF_ADDREF(*aResult);
00514       return NS_OK;
00515 }
00516 
00517 /* Get the max size of the history list */
00518 NS_IMETHODIMP
00519 nsSHistory::GetMaxLength(PRInt32 * aResult)
00520 {
00521   NS_ENSURE_ARG_POINTER(aResult);
00522   *aResult = gHistoryMaxSize;
00523   return NS_OK;
00524 }
00525 
00526 /* Set the max size of the history list */
00527 NS_IMETHODIMP
00528 nsSHistory::SetMaxLength(PRInt32 aMaxSize)
00529 {
00530   if (aMaxSize < 0)
00531     return NS_ERROR_ILLEGAL_VALUE;
00532 
00533   gHistoryMaxSize = aMaxSize;
00534   if (mLength > aMaxSize)
00535     PurgeHistory(mLength-aMaxSize);
00536   return NS_OK;
00537 }
00538 
00539 NS_IMETHODIMP
00540 nsSHistory::PurgeHistory(PRInt32 aEntries)
00541 {
00542   if (mLength <= 0 || aEntries <= 0)
00543     return NS_ERROR_FAILURE;
00544 
00545   aEntries = PR_MIN(aEntries, mLength);
00546   
00547   PRBool purgeHistory = PR_TRUE;
00548   // Notify the listener about the history purge
00549   if (mListener) {
00550     nsCOMPtr<nsISHistoryListener> listener(do_QueryReferent(mListener));
00551     if (listener) {
00552       listener->OnHistoryPurge(aEntries, &purgeHistory);
00553     } 
00554   }
00555 
00556   if (!purgeHistory) {
00557     // Listener asked us not to purge
00558     return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
00559   }
00560 
00561   PRInt32 cnt = 0;
00562   while (cnt < aEntries) {
00563     nsCOMPtr<nsISHTransaction> nextTxn;
00564     if (mListRoot)
00565       mListRoot->GetNext(getter_AddRefs(nextTxn));
00566     mListRoot = nextTxn;
00567     cnt++;        
00568   }
00569   mLength -= cnt;
00570   mIndex -= cnt;
00571 
00572   // Now if we were not at the end of the history, mIndex could have
00573   // become far too negative.  If so, just set it to -1.
00574   if (mIndex < -1) {
00575     mIndex = -1;
00576   }
00577 
00578   if (mRootDocShell)
00579     mRootDocShell->HistoryPurged(cnt);
00580 
00581   return NS_OK;
00582 }
00583 
00584 
00585 NS_IMETHODIMP
00586 nsSHistory::AddSHistoryListener(nsISHistoryListener * aListener)
00587 {
00588   NS_ENSURE_ARG_POINTER(aListener);
00589 
00590   // Check if the listener supports Weak Reference. This is a must.
00591   // This listener functionality is used by embedders and we want to 
00592   // have the right ownership with who ever listens to SHistory
00593   nsWeakPtr listener = do_GetWeakReference(aListener);
00594   if (!listener) return NS_ERROR_FAILURE;
00595   mListener = listener;
00596   return NS_OK;
00597 }
00598 
00599 
00600 NS_IMETHODIMP
00601 nsSHistory::RemoveSHistoryListener(nsISHistoryListener * aListener)
00602 {
00603   // Make sure the listener that wants to be removed is the
00604   // one we have in store. 
00605   nsWeakPtr listener = do_GetWeakReference(aListener);  
00606   if (listener == mListener) {
00607     mListener = nsnull;
00608     return NS_OK;
00609   }
00610   return NS_ERROR_FAILURE;
00611 }
00612 
00613 
00614 /* Replace an entry in the History list at a particular index.
00615  * Do not update index or count.
00616  */
00617 NS_IMETHODIMP
00618 nsSHistory::ReplaceEntry(PRInt32 aIndex, nsISHEntry * aReplaceEntry)
00619 {
00620   NS_ENSURE_ARG(aReplaceEntry);
00621   nsresult rv;
00622   nsCOMPtr<nsISHTransaction> currentTxn;
00623 
00624   if (!mListRoot) // Session History is not initialised.
00625     return NS_ERROR_FAILURE;
00626 
00627   rv = GetTransactionAtIndex(aIndex, getter_AddRefs(currentTxn));
00628 
00629   if(currentTxn)
00630   {
00631     // Set the replacement entry in the transaction
00632     rv = currentTxn->SetSHEntry(aReplaceEntry);
00633     rv = currentTxn->SetPersist(PR_TRUE);
00634   }
00635   return rv;
00636 }
00637 
00638 /* Get a handle to the Session history listener */
00639 NS_IMETHODIMP
00640 nsSHistory::GetListener(nsISHistoryListener ** aListener)
00641 {
00642   NS_ENSURE_ARG_POINTER(aListener);
00643   if (mListener) 
00644     CallQueryReferent(mListener.get(),  aListener);
00645   // Don't addref aListener. It is a weak pointer.
00646   return NS_OK;
00647 }
00648 
00649 NS_IMETHODIMP
00650 nsSHistory::EvictContentViewers(PRInt32 aPreviousIndex, PRInt32 aIndex)
00651 {
00652   // Check our per SHistory object limit in the currently navigated SHistory
00653   EvictWindowContentViewers(aPreviousIndex, aIndex);
00654   // Check our total limit across all SHistory objects
00655   EvictGlobalContentViewer();
00656   return NS_OK;
00657 }
00658 
00659 //*****************************************************************************
00660 //    nsSHistory: nsIWebNavigation
00661 //*****************************************************************************
00662 
00663 NS_IMETHODIMP
00664 nsSHistory::GetCanGoBack(PRBool * aCanGoBack)
00665 {
00666    NS_ENSURE_ARG_POINTER(aCanGoBack);
00667    *aCanGoBack = PR_FALSE;
00668 
00669    PRInt32 index = -1;
00670    NS_ENSURE_SUCCESS(GetIndex(&index), NS_ERROR_FAILURE);
00671    if(index > 0)
00672       *aCanGoBack = PR_TRUE;
00673 
00674    return NS_OK;
00675 }
00676 
00677 NS_IMETHODIMP
00678 nsSHistory::GetCanGoForward(PRBool * aCanGoForward)
00679 {
00680     NS_ENSURE_ARG_POINTER(aCanGoForward);
00681    *aCanGoForward = PR_FALSE;
00682 
00683    PRInt32 index = -1;
00684    PRInt32 count = -1;
00685 
00686    NS_ENSURE_SUCCESS(GetIndex(&index), NS_ERROR_FAILURE);
00687    NS_ENSURE_SUCCESS(GetCount(&count), NS_ERROR_FAILURE);
00688 
00689    if((index >= 0) && (index < (count - 1)))
00690       *aCanGoForward = PR_TRUE;
00691 
00692    return NS_OK;
00693 }
00694 
00695 NS_IMETHODIMP
00696 nsSHistory::GoBack()
00697 {
00698        PRBool canGoBack = PR_FALSE;
00699 
00700        GetCanGoBack(&canGoBack);
00701        if (!canGoBack)  // Can't go back
00702               return NS_ERROR_UNEXPECTED;
00703   return LoadEntry(mIndex-1, nsIDocShellLoadInfo::loadHistory, HIST_CMD_BACK);
00704 }
00705 
00706 
00707 NS_IMETHODIMP
00708 nsSHistory::GoForward()
00709 {
00710        PRBool canGoForward = PR_FALSE;
00711 
00712        GetCanGoForward(&canGoForward);
00713        if (!canGoForward)  // Can't go forward
00714               return NS_ERROR_UNEXPECTED;
00715   return LoadEntry(mIndex+1, nsIDocShellLoadInfo::loadHistory, HIST_CMD_FORWARD);
00716 
00717 }
00718 
00719 NS_IMETHODIMP
00720 nsSHistory::Reload(PRUint32 aReloadFlags)
00721 {
00722   nsresult rv;
00723        nsDocShellInfoLoadType loadType;
00724        if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY && 
00725            aReloadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)
00726        {
00727               loadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
00728        }
00729        else if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY)
00730        {
00731               loadType = nsIDocShellLoadInfo::loadReloadBypassProxy;
00732        }
00733        else if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)
00734        {
00735               loadType = nsIDocShellLoadInfo::loadReloadBypassCache;
00736        }
00737   else if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_CHARSET_CHANGE)
00738   {
00739               loadType = nsIDocShellLoadInfo::loadReloadCharsetChange;
00740   }
00741   else
00742        {
00743               loadType = nsIDocShellLoadInfo::loadReloadNormal;
00744        }
00745   
00746   // Notify listeners
00747   PRBool canNavigate = PR_TRUE;
00748   if (mListener) {
00749     nsCOMPtr<nsISHistoryListener> listener(do_QueryReferent(mListener));
00750     // We are reloading. Send Reload notifications. 
00751     // nsDocShellLoadFlagType is not public, where as nsIWebNavigation
00752     // is public. So send the reload notifications with the
00753     // nsIWebNavigation flags. 
00754     if (listener) {
00755       nsCOMPtr<nsIURI> currentURI;
00756       rv = GetCurrentURI(getter_AddRefs(currentURI));
00757       listener->OnHistoryReload(currentURI, aReloadFlags, &canNavigate);
00758     }
00759   }
00760   if (!canNavigate)
00761     return NS_OK;
00762 
00763        return LoadEntry(mIndex, loadType, HIST_CMD_RELOAD);
00764 }
00765 
00766 void
00767 nsSHistory::EvictWindowContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex)
00768 {
00769   // To enforce the per SHistory object limit on cached content viewers, we
00770   // need to release all of the content viewers that are no longer in the
00771   // "window" that now ends/begins at aToIndex.
00772 
00773   // This can happen on the first load of a page in a particular window
00774   if (aFromIndex < 0 || aToIndex < 0) {
00775     return;
00776   }
00777 
00778   // These indices give the range of SHEntries whose content viewers will be
00779   // evicted
00780   PRInt32 startIndex, endIndex;
00781   if (aToIndex > aFromIndex) { // going forward
00782     endIndex = aToIndex - gHistoryMaxViewers;
00783     if (endIndex <= 0) {
00784       return;
00785     }
00786     startIndex = PR_MAX(0, aFromIndex - gHistoryMaxViewers);
00787   } else { // going backward
00788     startIndex = aToIndex + gHistoryMaxViewers + 1;
00789     if (startIndex >= mLength) {
00790       return;
00791     }
00792     endIndex = PR_MIN(mLength, aFromIndex + gHistoryMaxViewers);
00793   }
00794 
00795   nsCOMPtr<nsISHTransaction> trans;
00796   GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
00797 
00798   for (PRInt32 i = startIndex; i < endIndex; ++i) {
00799     nsCOMPtr<nsISHEntry> entry;
00800     trans->GetSHEntry(getter_AddRefs(entry));
00801     nsCOMPtr<nsIContentViewer> viewer;
00802     nsCOMPtr<nsISHEntry> ownerEntry;
00803     entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
00804                                getter_AddRefs(viewer));
00805     if (viewer) {
00806       NS_ASSERTION(ownerEntry,
00807                    "ContentViewer exists but its SHEntry is null");
00808 #ifdef DEBUG_PAGE_CACHE 
00809       nsCOMPtr<nsIURI> uri;
00810       ownerEntry->GetURI(getter_AddRefs(uri));
00811       nsCAutoString spec;
00812       if (uri)
00813         uri->GetSpec(spec);
00814 
00815       printf("per SHistory limit: evicting content viewer: %s\n", spec.get());
00816 #endif
00817 
00818       viewer->Destroy();
00819       ownerEntry->SetContentViewer(nsnull);
00820       ownerEntry->SyncPresentationState();
00821     }
00822 
00823     nsISHTransaction *temp = trans;
00824     temp->GetNext(getter_AddRefs(trans));
00825   }
00826 }
00827 
00828 // static
00829 void
00830 nsSHistory::EvictGlobalContentViewer()
00831 {
00832   // true until the total number of content viewers is <= total max
00833   // The usual case is that we only need to evict one content viewer.
00834   // However, if somebody resets the pref value, we might occasionally
00835   // need to evict more than one.
00836   PRBool shouldTryEviction = PR_TRUE;
00837   while (shouldTryEviction) {
00838     // Walk through our list of SHistory objects, looking for content
00839     // viewers in the possible active window of all of the SHEntry objects.
00840     // Keep track of the SHEntry object that has a ContentViewer and is
00841     // farthest from the current focus in any SHistory object.  The
00842     // ContentViewer associated with that SHEntry will be evicted
00843     PRInt32 distanceFromFocus = 0;
00844     nsCOMPtr<nsISHEntry> evictFromSHE;
00845     nsCOMPtr<nsIContentViewer> evictViewer;
00846     PRInt32 totalContentViewers = 0;
00847     nsSHistory* shist = NS_STATIC_CAST(nsSHistory*,
00848                                        PR_LIST_HEAD(&gSHistoryList));
00849     while (shist != &gSHistoryList) {
00850       // Calculate the window of SHEntries that could possibly have a content
00851       // viewer.  There could be up to gHistoryMaxViewers content viewers,
00852       // but we don't know whether they are before or after the mIndex position
00853       // in the SHEntry list.  Just check both sides, to be safe.
00854       PRInt32 startIndex = PR_MAX(0, shist->mIndex - gHistoryMaxViewers);
00855       PRInt32 endIndex = PR_MIN(shist->mLength - 1,
00856                                 shist->mIndex + gHistoryMaxViewers);
00857       nsCOMPtr<nsISHTransaction> trans;
00858       shist->GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
00859       
00860       for (PRInt32 i = startIndex; i <= endIndex; ++i) {
00861         nsCOMPtr<nsISHEntry> entry;
00862         trans->GetSHEntry(getter_AddRefs(entry));
00863         nsCOMPtr<nsIContentViewer> viewer;
00864         nsCOMPtr<nsISHEntry> ownerEntry;
00865         entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
00866                                    getter_AddRefs(viewer));
00867 
00868 #ifdef DEBUG_PAGE_CACHE
00869         nsCOMPtr<nsIURI> uri;
00870         if (ownerEntry) {
00871           ownerEntry->GetURI(getter_AddRefs(uri));
00872         } else {
00873           entry->GetURI(getter_AddRefs(uri));
00874         }
00875         nsCAutoString spec;
00876         if (uri) {
00877           uri->GetSpec(spec);
00878           printf("Considering for eviction: %s\n", spec.get());
00879         }
00880 #endif
00881         
00882         // This SHEntry has a ContentViewer, so check how far away it is from
00883         // the currently used SHEntry within this SHistory object
00884         if (viewer) {
00885           PRInt32 distance = PR_ABS(shist->mIndex - i);
00886           
00887 #ifdef DEBUG_PAGE_CACHE
00888           printf("Has a cached content viewer: %s\n", spec.get());
00889           printf("mIndex: %d i: %d\n", shist->mIndex, i);
00890 #endif
00891           totalContentViewers++;
00892           if (distance > distanceFromFocus) {
00893             
00894 #ifdef DEBUG_PAGE_CACHE
00895             printf("Choosing as new eviction candidate: %s\n", spec.get());
00896 #endif
00897 
00898             distanceFromFocus = distance;
00899             evictFromSHE = ownerEntry;
00900             evictViewer = viewer;
00901           }
00902         }
00903         nsISHTransaction* temp = trans;
00904         temp->GetNext(getter_AddRefs(trans));
00905       }
00906       shist = NS_STATIC_CAST(nsSHistory*, PR_NEXT_LINK(shist));
00907     }
00908 
00909 #ifdef DEBUG_PAGE_CACHE
00910     printf("Distance from focus: %d\n", distanceFromFocus);
00911     printf("Total max viewers: %d\n", sHistoryMaxTotalViewers);
00912     printf("Total number of viewers: %d\n", totalContentViewers);
00913 #endif
00914 
00915     if (totalContentViewers > sHistoryMaxTotalViewers && evictViewer) {
00916 #ifdef DEBUG_PAGE_CACHE
00917       nsCOMPtr<nsIURI> uri;
00918       evictFromSHE->GetURI(getter_AddRefs(uri));
00919       nsCAutoString spec;
00920       if (uri) {
00921         uri->GetSpec(spec);
00922         printf("Evicting content viewer: %s\n", spec.get());
00923       }
00924 #endif
00925 
00926       evictViewer->Destroy();
00927       evictFromSHE->SetContentViewer(nsnull);
00928       evictFromSHE->SyncPresentationState();
00929 
00930       // If we only needed to evict one content viewer, then we are done.
00931       // Otherwise, continue evicting until we reach the max total limit.
00932       if (totalContentViewers - sHistoryMaxTotalViewers == 1) {
00933         shouldTryEviction = PR_FALSE;
00934       }
00935     } else {
00936       // couldn't find a content viewer to evict, so we are done
00937       shouldTryEviction = PR_FALSE;
00938     }
00939   }  // while shouldTryEviction
00940 }
00941 
00942 // Evicts all content viewers in all history objects.  This is very
00943 // inefficient, because it requires a linear search through all SHistory
00944 // objcets for each viewer to be evicted.  However, this method is called
00945 // infrequently -- only when the disk or memory cache is cleared.
00946 
00947 //static
00948 void
00949 nsSHistory::EvictAllContentViewers()
00950 {
00951   PRInt32 maxViewers = sHistoryMaxTotalViewers;
00952   sHistoryMaxTotalViewers = 0;
00953   EvictGlobalContentViewer();
00954   sHistoryMaxTotalViewers = maxViewers;
00955 }
00956 
00957 NS_IMETHODIMP
00958 nsSHistory::UpdateIndex()
00959 {
00960   // Update the actual index with the right value. 
00961   if (mIndex != mRequestedIndex && mRequestedIndex != -1) {
00962     mIndex = mRequestedIndex;
00963   }
00964 
00965   return NS_OK;
00966 }
00967 
00968 NS_IMETHODIMP
00969 nsSHistory::Stop(PRUint32 aStopFlags)
00970 {
00971        //Not implemented
00972    return NS_OK;
00973 }
00974 
00975 
00976 NS_IMETHODIMP
00977 nsSHistory::GetDocument(nsIDOMDocument** aDocument)
00978 {
00979 
00980        // Not implemented
00981   return NS_OK;
00982 }
00983 
00984 
00985 NS_IMETHODIMP
00986 nsSHistory::GetCurrentURI(nsIURI** aResultURI)
00987 {
00988   NS_ENSURE_ARG_POINTER(aResultURI);
00989   nsresult rv;
00990 
00991   nsCOMPtr<nsIHistoryEntry> currentEntry;
00992   rv = GetEntryAtIndex(mIndex, PR_FALSE, getter_AddRefs(currentEntry));
00993   if (NS_FAILED(rv) && !currentEntry) return rv;
00994   rv = currentEntry->GetURI(aResultURI);
00995   return rv;
00996 }
00997 
00998 
00999 NS_IMETHODIMP
01000 nsSHistory::GetReferringURI(nsIURI** aURI)
01001 {
01002     *aURI = nsnull;
01003     // Not implemented
01004     return NS_OK;
01005 }
01006 
01007 
01008 NS_IMETHODIMP
01009 nsSHistory::SetSessionHistory(nsISHistory* aSessionHistory)
01010 {
01011    // Not implemented
01012    return NS_OK;
01013 }
01014 
01015        
01016 NS_IMETHODIMP
01017 nsSHistory::GetSessionHistory(nsISHistory** aSessionHistory)
01018 {
01019   // Not implemented
01020    return NS_OK;
01021 }
01022 
01023 
01024 NS_IMETHODIMP
01025 nsSHistory::LoadURI(const PRUnichar* aURI,
01026                     PRUint32 aLoadFlags,
01027                     nsIURI* aReferringURI,
01028                     nsIInputStream* aPostStream,
01029                     nsIInputStream* aExtraHeaderStream)
01030 {
01031   return NS_OK;
01032 }
01033 
01034 NS_IMETHODIMP
01035 nsSHistory::GotoIndex(PRInt32 aIndex)
01036 {
01037        return LoadEntry(aIndex, nsIDocShellLoadInfo::loadHistory, HIST_CMD_GOTOINDEX);
01038 }
01039 
01040 NS_IMETHODIMP
01041 nsSHistory::LoadEntry(PRInt32 aIndex, long aLoadType, PRUint32 aHistCmd)
01042 {
01043   nsCOMPtr<nsIDocShell> docShell;
01044   nsCOMPtr<nsISHEntry> shEntry;
01045   // Keep note of requested history index in mRequestedIndex.
01046   mRequestedIndex = aIndex;
01047 
01048   nsCOMPtr<nsISHEntry> prevEntry;
01049   GetEntryAtIndex(mIndex, PR_FALSE, getter_AddRefs(prevEntry));  
01050    
01051   nsCOMPtr<nsISHEntry> nextEntry;   
01052   GetEntryAtIndex(mRequestedIndex, PR_FALSE, getter_AddRefs(nextEntry));
01053   nsCOMPtr<nsIHistoryEntry> nHEntry(do_QueryInterface(nextEntry));
01054   if (!nextEntry || !prevEntry || !nHEntry) {    
01055     mRequestedIndex = -1;
01056     return NS_ERROR_FAILURE;
01057   }
01058   
01059   // Send appropriate listener notifications
01060   PRBool canNavigate = PR_TRUE;
01061   // Get the uri for the entry we are about to visit
01062   nsCOMPtr<nsIURI> nextURI;
01063   nHEntry->GetURI(getter_AddRefs(nextURI));
01064  
01065   if(mListener) {    
01066     nsCOMPtr<nsISHistoryListener> listener(do_QueryReferent(mListener));
01067     if (listener) {
01068       if (aHistCmd == HIST_CMD_BACK) {
01069         // We are going back one entry. Send GoBack notifications
01070         listener->OnHistoryGoBack(nextURI, &canNavigate);
01071       }
01072       else if (aHistCmd == HIST_CMD_FORWARD) {
01073         // We are going forward. Send GoForward notification
01074         listener->OnHistoryGoForward(nextURI, &canNavigate);
01075       }
01076       else if (aHistCmd == HIST_CMD_GOTOINDEX) {
01077         // We are going somewhere else. This is not reload either
01078         listener->OnHistoryGotoIndex(aIndex, nextURI, &canNavigate);
01079       }
01080     }
01081   }
01082 
01083   if (!canNavigate) {
01084     // If the listener asked us not to proceed with 
01085     // the operation, simply return.    
01086     return NS_OK;  // XXX Maybe I can return some other error code?
01087   }
01088 
01089   nsCOMPtr<nsIURI> nexturi;
01090   PRInt32 pCount=0, nCount=0;
01091   nsCOMPtr<nsISHContainer> prevAsContainer(do_QueryInterface(prevEntry));
01092   nsCOMPtr<nsISHContainer> nextAsContainer(do_QueryInterface(nextEntry));
01093   if (prevAsContainer && nextAsContainer) {
01094     prevAsContainer->GetChildCount(&pCount);
01095     nextAsContainer->GetChildCount(&nCount);
01096   }
01097   
01098   nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
01099   if (mRequestedIndex == mIndex) {
01100     // Possibly a reload case 
01101     docShell = mRootDocShell;
01102   }
01103   else {
01104     // Going back or forward.
01105     if ((pCount > 0) && (nCount > 0)) {
01106       /* THis is a subframe navigation. Go find 
01107        * the docshell in which load should happen
01108        */
01109       PRBool frameFound = PR_FALSE;
01110       nsresult rv = CompareFrames(prevEntry, nextEntry, mRootDocShell, aLoadType, &frameFound);
01111       if (!frameFound) {
01112         // we did not successfully find the subframe in which
01113         // the new url was to be loaded. return error.
01114         mRequestedIndex = -1;
01115         return NS_ERROR_FAILURE; 
01116       }
01117       return rv;
01118     }   // (pCount >0)
01119     else
01120       docShell = mRootDocShell;
01121     }
01122   
01123 
01124   if (!docShell) {
01125      // we did not successfully go to the proper index.
01126      // return error.
01127       mRequestedIndex = -1;
01128       return NS_ERROR_FAILURE;
01129   }
01130 
01131   // Start the load on the appropriate docshell
01132   return InitiateLoad(nextEntry, docShell, aLoadType);
01133 }
01134 
01135 
01136 
01137 nsresult
01138 nsSHistory::CompareFrames(nsISHEntry * aPrevEntry, nsISHEntry * aNextEntry, nsIDocShell * aParent, long aLoadType, PRBool * aIsFrameFound)
01139 {
01140   if (!aPrevEntry || !aNextEntry || !aParent)
01141     return PR_FALSE;
01142 
01143   nsresult result = NS_OK;
01144   PRUint32 prevID, nextID;
01145 
01146   aPrevEntry->GetID(&prevID);
01147   aNextEntry->GetID(&nextID);
01148  
01149   // Check the IDs to verify if the pages are different.
01150   if (prevID != nextID) {
01151     if (aIsFrameFound)
01152       *aIsFrameFound = PR_TRUE;
01153     // Set the Subframe flag of the entry to indicate that
01154     // it is subframe navigation        
01155     aNextEntry->SetIsSubFrame(PR_TRUE);
01156     InitiateLoad(aNextEntry, aParent, aLoadType);
01157     return NS_OK;
01158   }
01159 
01160   /* The root entries are the same, so compare any child frames */
01161   PRInt32 pcnt=0, ncnt=0, dsCount=0;
01162   nsCOMPtr<nsISHContainer>  prevContainer(do_QueryInterface(aPrevEntry));
01163   nsCOMPtr<nsISHContainer>  nextContainer(do_QueryInterface(aNextEntry));
01164   nsCOMPtr<nsIDocShellTreeNode> dsTreeNode(do_QueryInterface(aParent));
01165 
01166   if (!dsTreeNode)
01167     return NS_ERROR_FAILURE;
01168   if (!prevContainer || !nextContainer)
01169     return NS_ERROR_FAILURE;
01170 
01171   prevContainer->GetChildCount(&pcnt);
01172   nextContainer->GetChildCount(&ncnt);
01173   dsTreeNode->GetChildCount(&dsCount);
01174 
01175   //XXX What to do if the children count don't match
01176     
01177   for (PRInt32 i=0; i<ncnt; i++){
01178          nsCOMPtr<nsISHEntry> pChild, nChild;
01179     nsCOMPtr<nsIDocShellTreeItem> dsTreeItemChild;
01180          
01181     prevContainer->GetChildAt(i, getter_AddRefs(pChild));
01182          nextContainer->GetChildAt(i, getter_AddRefs(nChild));
01183     if (dsCount > 0)
01184            dsTreeNode->GetChildAt(i, getter_AddRefs(dsTreeItemChild));
01185 
01186          if (!dsTreeItemChild)
01187       return NS_ERROR_FAILURE;
01188 
01189     nsCOMPtr<nsIDocShell> dsChild(do_QueryInterface(dsTreeItemChild));
01190 
01191          CompareFrames(pChild, nChild, dsChild, aLoadType, aIsFrameFound);
01192        }     
01193   return result;
01194 }
01195 
01196 
01197 nsresult 
01198 nsSHistory::InitiateLoad(nsISHEntry * aFrameEntry, nsIDocShell * aFrameDS, long aLoadType)
01199 {
01200   nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
01201 
01202   /* Set the loadType in the SHEntry too to  what was passed on.
01203    * This will be passed on to child subframes later in nsDocShell,
01204    * so that proper loadType is maintained through out a frameset
01205    */
01206   aFrameEntry->SetLoadType(aLoadType);    
01207   aFrameDS->CreateLoadInfo (getter_AddRefs(loadInfo));
01208 
01209   loadInfo->SetLoadType(aLoadType);
01210   loadInfo->SetSHEntry(aFrameEntry);
01211 
01212   nsCOMPtr<nsIURI> nextURI;
01213   nsCOMPtr<nsIHistoryEntry> hEntry(do_QueryInterface(aFrameEntry));
01214   hEntry->GetURI(getter_AddRefs(nextURI));
01215   // Time   to initiate a document load
01216   return aFrameDS->LoadURI(nextURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, PR_FALSE);
01217 
01218 }
01219 
01220 
01221 
01222 NS_IMETHODIMP
01223 nsSHistory::SetRootDocShell(nsIDocShell * aDocShell)
01224 {
01225   mRootDocShell = aDocShell;
01226   return NS_OK;
01227 }
01228 
01229 NS_IMETHODIMP
01230 nsSHistory::GetRootDocShell(nsIDocShell ** aDocShell)
01231 {
01232    NS_ENSURE_ARG_POINTER(aDocShell);
01233 
01234    *aDocShell = mRootDocShell;
01235    //Not refcounted. May this method should not be available for public
01236   // NS_IF_ADDREF(*aDocShell);
01237    return NS_OK;
01238 }
01239 
01240 
01241 NS_IMETHODIMP
01242 nsSHistory::GetSHistoryEnumerator(nsISimpleEnumerator** aEnumerator)
01243 {
01244   nsresult status = NS_OK;
01245 
01246   NS_ENSURE_ARG_POINTER(aEnumerator);
01247   nsSHEnumerator * iterator = new nsSHEnumerator(this);
01248   if (iterator && NS_FAILED(status = CallQueryInterface(iterator, aEnumerator)))
01249     delete iterator;
01250   return status;
01251 }
01252 
01253 
01254 //*****************************************************************************
01255 //***    nsSHEnumerator: Object Management
01256 //*****************************************************************************
01257 
01258 nsSHEnumerator::nsSHEnumerator(nsSHistory * aSHistory):mIndex(-1)
01259 {
01260   mSHistory = aSHistory;
01261 }
01262 
01263 nsSHEnumerator::~nsSHEnumerator()
01264 {
01265   mSHistory = nsnull;
01266 }
01267 
01268 NS_IMPL_ISUPPORTS1(nsSHEnumerator, nsISimpleEnumerator)
01269 
01270 NS_IMETHODIMP
01271 nsSHEnumerator::HasMoreElements(PRBool * aReturn)
01272 {
01273   PRInt32 cnt;
01274   *aReturn = PR_FALSE;
01275   mSHistory->GetCount(&cnt);
01276   if (mIndex >= -1 && mIndex < (cnt-1) ) { 
01277     *aReturn = PR_TRUE;
01278   }
01279   return NS_OK;
01280 }
01281 
01282 
01283 NS_IMETHODIMP 
01284 nsSHEnumerator::GetNext(nsISupports **aItem)
01285 {
01286   NS_ENSURE_ARG_POINTER(aItem);
01287   PRInt32 cnt= 0;
01288 
01289   nsresult  result = NS_ERROR_FAILURE;
01290   mSHistory->GetCount(&cnt);
01291   if (mIndex < (cnt-1)) {
01292     mIndex++;
01293     nsCOMPtr<nsIHistoryEntry> hEntry;
01294     result = mSHistory->GetEntryAtIndex(mIndex, PR_FALSE, getter_AddRefs(hEntry));
01295        if (hEntry)
01296       result = CallQueryInterface(hEntry, aItem);
01297   }
01298   return result;
01299 }