LCOV - code coverage report
Current view: directory - docshell/shistory/src - nsSHistory.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 795 64 8.1 %
Date: 2012-06-02 Functions: 85 14 16.5 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  *
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is the Mozilla browser.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  * Netscape Communications, Inc.
      20                 :  * Portions created by the Initial Developer are Copyright (C) 1999
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Radha Kulkarni <radha@netscape.com>
      25                 :  *
      26                 :  * Alternatively, the contents of this file may be used under the terms of
      27                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      28                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      29                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      30                 :  * of those above. If you wish to allow use of your version of this file only
      31                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      32                 :  * use your version of this file under the terms of the MPL, indicate your
      33                 :  * decision by deleting the provisions above and replace them with the notice
      34                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      35                 :  * the provisions above, a recipient may use your version of this file under
      36                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      37                 :  *
      38                 :  * ***** END LICENSE BLOCK ***** */
      39                 : 
      40                 : // Local Includes 
      41                 : #include "nsSHistory.h"
      42                 : 
      43                 : // Helper Classes
      44                 : #include "nsXPIDLString.h"
      45                 : #include "nsReadableUtils.h"
      46                 : #include "mozilla/Preferences.h"
      47                 : 
      48                 : // Interfaces Needed
      49                 : #include "nsILayoutHistoryState.h"
      50                 : #include "nsIDocShell.h"
      51                 : #include "nsIDocShellLoadInfo.h"
      52                 : #include "nsISHContainer.h"
      53                 : #include "nsIDocShellTreeItem.h"
      54                 : #include "nsIDocShellTreeNode.h"
      55                 : #include "nsIDocShellLoadInfo.h"
      56                 : #include "nsIServiceManager.h"
      57                 : #include "nsIURI.h"
      58                 : #include "nsIContentViewer.h"
      59                 : #include "nsICacheService.h"
      60                 : #include "nsIObserverService.h"
      61                 : #include "prclist.h"
      62                 : #include "mozilla/Services.h"
      63                 : #include "nsTArray.h"
      64                 : #include "nsCOMArray.h"
      65                 : #include "nsDocShell.h"
      66                 : 
      67                 : // For calculating max history entries and max cachable contentviewers
      68                 : #include "nspr.h"
      69                 : #include <math.h>  // for log()
      70                 : 
      71                 : using namespace mozilla;
      72                 : 
      73                 : #define PREF_SHISTORY_SIZE "browser.sessionhistory.max_entries"
      74                 : #define PREF_SHISTORY_MAX_TOTAL_VIEWERS "browser.sessionhistory.max_total_viewers"
      75                 : 
      76                 : static const char* kObservedPrefs[] = {
      77                 :   PREF_SHISTORY_SIZE,
      78                 :   PREF_SHISTORY_MAX_TOTAL_VIEWERS,
      79                 :   nsnull
      80                 : };
      81                 : 
      82                 : static PRInt32  gHistoryMaxSize = 50;
      83                 : // Max viewers allowed per SHistory objects
      84                 : static const PRInt32  gHistoryMaxViewers = 3;
      85                 : // List of all SHistory objects, used for content viewer cache eviction
      86                 : static PRCList gSHistoryList;
      87                 : // Max viewers allowed total, across all SHistory objects - negative default
      88                 : // means we will calculate how many viewers to cache based on total memory
      89                 : PRInt32 nsSHistory::sHistoryMaxTotalViewers = -1;
      90                 : 
      91                 : // A counter that is used to be able to know the order in which
      92                 : // entries were touched, so that we can evict older entries first.
      93                 : static PRUint32 gTouchCounter = 0;
      94                 : 
      95            1464 : static PRLogModuleInfo* gLogModule = PR_LOG_DEFINE("nsSHistory");
      96                 : #define LOG(format) PR_LOG(gLogModule, PR_LOG_DEBUG, format)
      97                 : 
      98                 : // This macro makes it easier to print a log message which includes a URI's
      99                 : // spec.  Example use:
     100                 : //
     101                 : //  nsIURI *uri = [...];
     102                 : //  LOG_SPEC(("The URI is %s.", _spec), uri);
     103                 : //
     104                 : #define LOG_SPEC(format, uri)                              \
     105                 :   PR_BEGIN_MACRO                                           \
     106                 :     if (PR_LOG_TEST(gLogModule, PR_LOG_DEBUG)) {           \
     107                 :       nsCAutoString _specStr(NS_LITERAL_CSTRING("(null)"));\
     108                 :       if (uri) {                                           \
     109                 :         uri->GetSpec(_specStr);                            \
     110                 :       }                                                    \
     111                 :       const char* _spec = _specStr.get();                  \
     112                 :       LOG(format);                                         \
     113                 :     }                                                      \
     114                 :   PR_END_MACRO
     115                 : 
     116                 : // This macro makes it easy to log a message including an SHEntry's URI.
     117                 : // For example:
     118                 : //
     119                 : //  nsCOMPtr<nsISHEntry> shentry = [...];
     120                 : //  LOG_SHENTRY_SPEC(("shentry %p has uri %s.", shentry.get(), _spec), shentry);
     121                 : //
     122                 : #define LOG_SHENTRY_SPEC(format, shentry)                  \
     123                 :   PR_BEGIN_MACRO                                           \
     124                 :     if (PR_LOG_TEST(gLogModule, PR_LOG_DEBUG)) {           \
     125                 :       nsCOMPtr<nsIURI> uri;                                \
     126                 :       shentry->GetURI(getter_AddRefs(uri));                \
     127                 :       LOG_SPEC(format, uri);                               \
     128                 :     }                                                      \
     129                 :   PR_END_MACRO
     130                 : 
     131                 : enum HistCmd{
     132                 :   HIST_CMD_BACK,
     133                 :   HIST_CMD_FORWARD,
     134                 :   HIST_CMD_GOTOINDEX,
     135                 :   HIST_CMD_RELOAD
     136                 : } ;
     137                 : 
     138                 : //*****************************************************************************
     139                 : //***      nsSHistoryObserver
     140                 : //*****************************************************************************
     141                 : 
     142                 : class nsSHistoryObserver : public nsIObserver
     143                 : {
     144                 : 
     145                 : public:
     146                 :   NS_DECL_ISUPPORTS
     147                 :   NS_DECL_NSIOBSERVER
     148                 : 
     149            1404 :   nsSHistoryObserver() {}
     150                 : 
     151                 : protected:
     152            1404 :   ~nsSHistoryObserver() {}
     153                 : };
     154                 : 
     155                 : static nsSHistoryObserver* gObserver = nsnull;
     156                 : 
     157           38234 : NS_IMPL_ISUPPORTS1(nsSHistoryObserver, nsIObserver)
     158                 : 
     159                 : NS_IMETHODIMP
     160              81 : nsSHistoryObserver::Observe(nsISupports *aSubject, const char *aTopic,
     161                 :                             const PRUnichar *aData)
     162                 : {
     163              81 :   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     164               2 :     nsSHistory::UpdatePrefs();
     165               2 :     nsSHistory::GloballyEvictContentViewers();
     166              79 :   } else if (!strcmp(aTopic, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID) ||
     167               0 :              !strcmp(aTopic, "memory-pressure")) {
     168              79 :     nsSHistory::GloballyEvictAllContentViewers();
     169                 :   }
     170                 : 
     171              81 :   return NS_OK;
     172                 : }
     173                 : 
     174                 : namespace {
     175                 : 
     176                 : already_AddRefed<nsIContentViewer>
     177               0 : GetContentViewerForTransaction(nsISHTransaction *aTrans)
     178                 : {
     179               0 :   nsCOMPtr<nsISHEntry> entry;
     180               0 :   aTrans->GetSHEntry(getter_AddRefs(entry));
     181               0 :   if (!entry) {
     182               0 :     return nsnull;
     183                 :   }
     184                 : 
     185               0 :   nsCOMPtr<nsISHEntry> ownerEntry;
     186               0 :   nsCOMPtr<nsIContentViewer> viewer;
     187               0 :   entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
     188               0 :                              getter_AddRefs(viewer));
     189               0 :   return viewer.forget();
     190                 : }
     191                 : 
     192                 : void
     193               0 : EvictContentViewerForTransaction(nsISHTransaction *aTrans)
     194                 : {
     195               0 :   nsCOMPtr<nsISHEntry> entry;
     196               0 :   aTrans->GetSHEntry(getter_AddRefs(entry));
     197               0 :   nsCOMPtr<nsIContentViewer> viewer;
     198               0 :   nsCOMPtr<nsISHEntry> ownerEntry;
     199               0 :   entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
     200               0 :                              getter_AddRefs(viewer));
     201               0 :   if (viewer) {
     202               0 :     NS_ASSERTION(ownerEntry,
     203                 :                  "Content viewer exists but its SHEntry is null");
     204                 : 
     205               0 :     LOG_SHENTRY_SPEC(("Evicting content viewer 0x%p for "
     206                 :                       "owning SHEntry 0x%p at %s.",
     207                 :                       viewer.get(), ownerEntry.get(), _spec), ownerEntry);
     208                 : 
     209                 :     // Drop the presentation state before destroying the viewer, so that
     210                 :     // document teardown is able to correctly persist the state.
     211               0 :     ownerEntry->SetContentViewer(nsnull);
     212               0 :     ownerEntry->SyncPresentationState();
     213               0 :     viewer->Destroy();
     214                 :   }
     215               0 : }
     216                 : 
     217                 : } // anonymous namespace
     218                 : 
     219                 : //*****************************************************************************
     220                 : //***    nsSHistory: Object Management
     221                 : //*****************************************************************************
     222                 : 
     223               0 : nsSHistory::nsSHistory() : mListRoot(nsnull), mIndex(-1), mLength(0), mRequestedIndex(-1)
     224                 : {
     225                 :   // Add this new SHistory object to the list
     226               0 :   PR_APPEND_LINK(this, &gSHistoryList);
     227               0 : }
     228                 : 
     229                 : 
     230               0 : nsSHistory::~nsSHistory()
     231                 : {
     232                 :   // Remove this SHistory object from the list
     233               0 :   PR_REMOVE_LINK(this);
     234               0 : }
     235                 : 
     236                 : //*****************************************************************************
     237                 : //    nsSHistory: nsISupports
     238                 : //*****************************************************************************
     239                 : 
     240               0 : NS_IMPL_ADDREF(nsSHistory)
     241               0 : NS_IMPL_RELEASE(nsSHistory)
     242                 : 
     243               0 : NS_INTERFACE_MAP_BEGIN(nsSHistory)
     244               0 :    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISHistory)
     245               0 :    NS_INTERFACE_MAP_ENTRY(nsISHistory)
     246               0 :    NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
     247               0 :    NS_INTERFACE_MAP_ENTRY(nsISHistoryInternal)
     248               0 : NS_INTERFACE_MAP_END
     249                 : 
     250                 : //*****************************************************************************
     251                 : //    nsSHistory: nsISHistory
     252                 : //*****************************************************************************
     253                 : 
     254                 : // static
     255                 : PRUint32
     256            1406 : nsSHistory::CalcMaxTotalViewers()
     257                 : {
     258                 :   // Calculate an estimate of how many ContentViewers we should cache based
     259                 :   // on RAM.  This assumes that the average ContentViewer is 4MB (conservative)
     260                 :   // and caps the max at 8 ContentViewers
     261                 :   //
     262                 :   // TODO: Should we split the cache memory betw. ContentViewer caching and
     263                 :   // nsCacheService?
     264                 :   //
     265                 :   // RAM      ContentViewers
     266                 :   // -----------------------
     267                 :   // 32   Mb       0
     268                 :   // 64   Mb       1
     269                 :   // 128  Mb       2
     270                 :   // 256  Mb       3
     271                 :   // 512  Mb       5
     272                 :   // 1024 Mb       8
     273                 :   // 2048 Mb       8
     274                 :   // 4096 Mb       8
     275            1406 :   PRUint64 bytes = PR_GetPhysicalMemorySize();
     276                 : 
     277            1406 :   if (LL_IS_ZERO(bytes))
     278               0 :     return 0;
     279                 : 
     280                 :   // Conversion from unsigned int64 to double doesn't work on all platforms.
     281                 :   // We need to truncate the value at LL_MAXINT to make sure we don't
     282                 :   // overflow.
     283                 :   if (LL_CMP(bytes, >, LL_MAXINT))
     284                 :     bytes = LL_MAXINT;
     285                 : 
     286                 :   PRUint64 kbytes;
     287            1406 :   LL_SHR(kbytes, bytes, 10);
     288                 : 
     289                 :   double kBytesD;
     290            1406 :   LL_L2D(kBytesD, (PRInt64) kbytes);
     291                 : 
     292                 :   // This is essentially the same calculation as for nsCacheService,
     293                 :   // except that we divide the final memory calculation by 4, since
     294                 :   // we assume each ContentViewer takes on average 4MB
     295            1406 :   PRUint32 viewers = 0;
     296            1406 :   double x = log(kBytesD)/log(2.0) - 14;
     297            1406 :   if (x > 0) {
     298            1406 :     viewers    = (PRUint32)(x * x - x + 2.001); // add .001 for rounding
     299            1406 :     viewers   /= 4;
     300                 :   }
     301                 : 
     302                 :   // Cap it off at 8 max
     303            1406 :   if (viewers > 8) {
     304            1406 :     viewers = 8;
     305                 :   }
     306            1406 :   return viewers;
     307                 : }
     308                 : 
     309                 : // static
     310                 : void
     311            1406 : nsSHistory::UpdatePrefs()
     312                 : {
     313            1406 :   Preferences::GetInt(PREF_SHISTORY_SIZE, &gHistoryMaxSize);
     314                 :   Preferences::GetInt(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
     315            1406 :                       &sHistoryMaxTotalViewers);
     316                 :   // If the pref is negative, that means we calculate how many viewers
     317                 :   // we think we should cache, based on total memory
     318            1406 :   if (sHistoryMaxTotalViewers < 0) {
     319            1406 :     sHistoryMaxTotalViewers = CalcMaxTotalViewers();
     320                 :   }
     321            1406 : }
     322                 : 
     323                 : // static
     324                 : nsresult
     325            1404 : nsSHistory::Startup()
     326                 : {
     327            1404 :   UpdatePrefs();
     328                 : 
     329                 :   // The goal of this is to unbreak users who have inadvertently set their
     330                 :   // session history size to less than the default value.
     331                 :   PRInt32 defaultHistoryMaxSize =
     332            1404 :     Preferences::GetDefaultInt(PREF_SHISTORY_SIZE, 50);
     333            1404 :   if (gHistoryMaxSize < defaultHistoryMaxSize) {
     334               0 :     gHistoryMaxSize = defaultHistoryMaxSize;
     335                 :   }
     336                 :   
     337                 :   // Allow the user to override the max total number of cached viewers,
     338                 :   // but keep the per SHistory cached viewer limit constant
     339            1404 :   if (!gObserver) {
     340            1404 :     gObserver = new nsSHistoryObserver();
     341            1404 :     NS_ADDREF(gObserver);
     342            1404 :     Preferences::AddStrongObservers(gObserver, kObservedPrefs);
     343                 : 
     344                 :     nsCOMPtr<nsIObserverService> obsSvc =
     345            2808 :       mozilla::services::GetObserverService();
     346            1404 :     if (obsSvc) {
     347                 :       // Observe empty-cache notifications so tahat clearing the disk/memory
     348                 :       // cache will also evict all content viewers.
     349            1404 :       obsSvc->AddObserver(gObserver,
     350            1404 :                           NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID, false);
     351                 : 
     352                 :       // Same for memory-pressure notifications
     353            1404 :       obsSvc->AddObserver(gObserver, "memory-pressure", false);
     354                 :     }
     355                 :   }
     356                 : 
     357                 :   // Initialize the global list of all SHistory objects
     358            1404 :   PR_INIT_CLIST(&gSHistoryList);
     359            1404 :   return NS_OK;
     360                 : }
     361                 : 
     362                 : // static
     363                 : void
     364            1404 : nsSHistory::Shutdown()
     365                 : {
     366            1404 :   if (gObserver) {
     367            1404 :     Preferences::RemoveObservers(gObserver, kObservedPrefs);
     368                 :     nsCOMPtr<nsIObserverService> obsSvc =
     369            2808 :       mozilla::services::GetObserverService();
     370            1404 :     if (obsSvc) {
     371               0 :       obsSvc->RemoveObserver(gObserver, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID);
     372               0 :       obsSvc->RemoveObserver(gObserver, "memory-pressure");
     373                 :     }
     374            1404 :     NS_RELEASE(gObserver);
     375                 :   }
     376            1404 : }
     377                 : 
     378                 : /* Add an entry to the History list at mIndex and 
     379                 :  * increment the index to point to the new entry
     380                 :  */
     381                 : NS_IMETHODIMP
     382               0 : nsSHistory::AddEntry(nsISHEntry * aSHEntry, bool aPersist)
     383                 : {
     384               0 :   NS_ENSURE_ARG(aSHEntry);
     385                 : 
     386               0 :   nsCOMPtr<nsISHTransaction> currentTxn;
     387                 : 
     388               0 :   if(mListRoot)
     389               0 :     GetTransactionAtIndex(mIndex, getter_AddRefs(currentTxn));
     390                 : 
     391               0 :   bool currentPersist = true;
     392               0 :   if(currentTxn)
     393               0 :     currentTxn->GetPersist(&currentPersist);
     394                 : 
     395               0 :   if(!currentPersist)
     396                 :   {
     397               0 :     NS_ENSURE_SUCCESS(currentTxn->SetSHEntry(aSHEntry),NS_ERROR_FAILURE);
     398               0 :     currentTxn->SetPersist(aPersist);
     399               0 :     return NS_OK;
     400                 :   }
     401                 : 
     402               0 :   nsCOMPtr<nsISHTransaction> txn(do_CreateInstance(NS_SHTRANSACTION_CONTRACTID));
     403               0 :   NS_ENSURE_TRUE(txn, NS_ERROR_FAILURE);
     404                 : 
     405                 :   // Notify any listener about the new addition
     406               0 :   if (mListener) {
     407               0 :     nsCOMPtr<nsISHistoryListener> listener(do_QueryReferent(mListener));
     408               0 :     if (listener) {
     409               0 :       nsCOMPtr<nsIURI> uri;
     410               0 :       nsCOMPtr<nsIHistoryEntry> hEntry(do_QueryInterface(aSHEntry));
     411               0 :       if (hEntry) {
     412               0 :         PRInt32 currentIndex = mIndex;
     413               0 :         hEntry->GetURI(getter_AddRefs(uri));
     414               0 :         listener->OnHistoryNewEntry(uri);
     415                 : 
     416                 :         // If a listener has changed mIndex, we need to get currentTxn again,
     417                 :         // otherwise we'll be left at an inconsistent state (see bug 320742)
     418               0 :         if (currentIndex != mIndex)
     419               0 :           GetTransactionAtIndex(mIndex, getter_AddRefs(currentTxn));
     420                 :       }
     421                 :     }
     422                 :   }
     423                 : 
     424                 :   // Set the ShEntry and parent for the transaction. setting the 
     425                 :   // parent will properly set the parent child relationship
     426               0 :   txn->SetPersist(aPersist);
     427               0 :   NS_ENSURE_SUCCESS(txn->Create(aSHEntry, currentTxn), NS_ERROR_FAILURE);
     428                 :    
     429                 :   // A little tricky math here...  Basically when adding an object regardless of
     430                 :   // what the length was before, it should always be set back to the current and
     431                 :   // lop off the forward.
     432               0 :   mLength = (++mIndex + 1);
     433                 : 
     434                 :   // If this is the very first transaction, initialize the list
     435               0 :   if(!mListRoot)
     436               0 :     mListRoot = txn;
     437                 : 
     438                 :   // Purge History list if it is too long
     439               0 :   if ((gHistoryMaxSize >= 0) && (mLength > gHistoryMaxSize))
     440               0 :     PurgeHistory(mLength-gHistoryMaxSize);
     441                 :   
     442               0 :   RemoveDynEntries(mIndex - 1, mIndex);
     443               0 :   return NS_OK;
     444                 : }
     445                 : 
     446                 : /* Get size of the history list */
     447                 : NS_IMETHODIMP
     448               0 : nsSHistory::GetCount(PRInt32 * aResult)
     449                 : {
     450               0 :   NS_ENSURE_ARG_POINTER(aResult);
     451               0 :   *aResult = mLength;
     452               0 :   return NS_OK;
     453                 : }
     454                 : 
     455                 : /* Get index of the history list */
     456                 : NS_IMETHODIMP
     457               0 : nsSHistory::GetIndex(PRInt32 * aResult)
     458                 : {
     459               0 :   NS_PRECONDITION(aResult, "null out param?");
     460               0 :   *aResult = mIndex;
     461               0 :   return NS_OK;
     462                 : }
     463                 : 
     464                 : /* Get the requestedIndex */
     465                 : NS_IMETHODIMP
     466               0 : nsSHistory::GetRequestedIndex(PRInt32 * aResult)
     467                 : {
     468               0 :   NS_PRECONDITION(aResult, "null out param?");
     469               0 :   *aResult = mRequestedIndex;
     470               0 :   return NS_OK;
     471                 : }
     472                 : 
     473                 : NS_IMETHODIMP
     474               0 : nsSHistory::GetEntryAtIndex(PRInt32 aIndex, bool aModifyIndex, nsISHEntry** aResult)
     475                 : {
     476                 :   nsresult rv;
     477               0 :   nsCOMPtr<nsISHTransaction> txn;
     478                 : 
     479                 :   /* GetTransactionAtIndex ensures aResult is valid and validates aIndex */
     480               0 :   rv = GetTransactionAtIndex(aIndex, getter_AddRefs(txn));
     481               0 :   if (NS_SUCCEEDED(rv) && txn) {
     482                 :     //Get the Entry from the transaction
     483               0 :     rv = txn->GetSHEntry(aResult);
     484               0 :     if (NS_SUCCEEDED(rv) && (*aResult)) {
     485                 :       // Set mIndex to the requested index, if asked to do so..
     486               0 :       if (aModifyIndex) {
     487               0 :         mIndex = aIndex;
     488                 :       }
     489                 :     } //entry
     490                 :   }  //Transaction
     491               0 :   return rv;
     492                 : }
     493                 : 
     494                 : 
     495                 : /* Get the entry at a given index */
     496                 : NS_IMETHODIMP
     497               0 : nsSHistory::GetEntryAtIndex(PRInt32 aIndex, bool aModifyIndex, nsIHistoryEntry** aResult)
     498                 : {
     499                 :   nsresult rv;
     500               0 :   nsCOMPtr<nsISHEntry> shEntry;
     501               0 :   rv = GetEntryAtIndex(aIndex, aModifyIndex, getter_AddRefs(shEntry));
     502               0 :   if (NS_SUCCEEDED(rv) && shEntry) 
     503               0 :     rv = CallQueryInterface(shEntry, aResult);
     504                 :  
     505               0 :   return rv;
     506                 : }
     507                 : 
     508                 : /* Get the transaction at a given index */
     509                 : NS_IMETHODIMP
     510               0 : nsSHistory::GetTransactionAtIndex(PRInt32 aIndex, nsISHTransaction ** aResult)
     511                 : {
     512                 :   nsresult rv;
     513               0 :   NS_ENSURE_ARG_POINTER(aResult);
     514                 : 
     515               0 :   if ((mLength <= 0) || (aIndex < 0) || (aIndex >= mLength))
     516               0 :     return NS_ERROR_FAILURE;
     517                 : 
     518               0 :   if (!mListRoot) 
     519               0 :     return NS_ERROR_FAILURE;
     520                 : 
     521               0 :   if (aIndex == 0)
     522                 :   {
     523               0 :     *aResult = mListRoot;
     524               0 :     NS_ADDREF(*aResult);
     525               0 :     return NS_OK;
     526                 :   } 
     527               0 :   PRInt32   cnt=0;
     528               0 :   nsCOMPtr<nsISHTransaction>  tempPtr;
     529                 : 
     530               0 :   rv = GetRootTransaction(getter_AddRefs(tempPtr));
     531               0 :   if (NS_FAILED(rv) || !tempPtr)
     532               0 :     return NS_ERROR_FAILURE;
     533                 : 
     534               0 :   while(1) {
     535               0 :     nsCOMPtr<nsISHTransaction> ptr;
     536               0 :     rv = tempPtr->GetNext(getter_AddRefs(ptr));
     537               0 :     if (NS_SUCCEEDED(rv) && ptr) {
     538               0 :       cnt++;
     539               0 :       if (cnt == aIndex) {
     540               0 :         *aResult = ptr;
     541               0 :         NS_ADDREF(*aResult);
     542                 :         break;
     543                 :       }
     544                 :       else {
     545               0 :         tempPtr = ptr;
     546               0 :         continue;
     547                 :       }
     548                 :     }  //NS_SUCCEEDED
     549                 :     else 
     550               0 :       return NS_ERROR_FAILURE;
     551                 :   }  // while 
     552                 :   
     553               0 :   return NS_OK;
     554                 : }
     555                 : 
     556                 : #ifdef DEBUG
     557                 : nsresult
     558               0 : nsSHistory::PrintHistory()
     559                 : {
     560                 : 
     561               0 :   nsCOMPtr<nsISHTransaction>   txn;
     562               0 :   PRInt32 index = 0;
     563                 :   nsresult rv;
     564                 : 
     565               0 :   if (!mListRoot) 
     566               0 :     return NS_ERROR_FAILURE;
     567                 : 
     568               0 :   txn = mListRoot;
     569                 :     
     570               0 :   while (1) {
     571               0 :     if (!txn)
     572               0 :       break;
     573               0 :     nsCOMPtr<nsISHEntry>  entry;
     574               0 :     rv = txn->GetSHEntry(getter_AddRefs(entry));
     575               0 :     if (NS_FAILED(rv) && !entry)
     576               0 :       return NS_ERROR_FAILURE;
     577                 : 
     578               0 :     nsCOMPtr<nsILayoutHistoryState> layoutHistoryState;
     579               0 :     nsCOMPtr<nsIURI>  uri;
     580               0 :     nsXPIDLString title;
     581                 : 
     582               0 :     entry->GetLayoutHistoryState(getter_AddRefs(layoutHistoryState));
     583               0 :     nsCOMPtr<nsIHistoryEntry> hEntry(do_QueryInterface(entry));
     584               0 :     if (hEntry) {
     585               0 :       hEntry->GetURI(getter_AddRefs(uri));
     586               0 :       hEntry->GetTitle(getter_Copies(title));              
     587                 :     }
     588                 : 
     589                 : #if 0
     590                 :     nsCAutoString url;
     591                 :     if (uri)
     592                 :      uri->GetSpec(url);
     593                 : 
     594                 :     printf("**** SH Transaction #%d, Entry = %x\n", index, entry.get());
     595                 :     printf("\t\t URL = %s\n", url.get());
     596                 : 
     597                 :     printf("\t\t Title = %s\n", NS_LossyConvertUTF16toASCII(title).get());
     598                 :     printf("\t\t layout History Data = %x\n", layoutHistoryState.get());
     599                 : #endif
     600                 : 
     601               0 :     nsCOMPtr<nsISHTransaction> next;
     602               0 :     rv = txn->GetNext(getter_AddRefs(next));
     603               0 :     if (NS_SUCCEEDED(rv) && next) {
     604               0 :       txn = next;
     605               0 :       index++;
     606               0 :       continue;
     607                 :     }
     608                 :     else
     609               0 :       break;
     610                 :   }
     611                 : 
     612               0 :   return NS_OK;
     613                 : }
     614                 : #endif
     615                 : 
     616                 : 
     617                 : NS_IMETHODIMP
     618               0 : nsSHistory::GetRootTransaction(nsISHTransaction ** aResult)
     619                 : {
     620               0 :   NS_ENSURE_ARG_POINTER(aResult);
     621               0 :   *aResult=mListRoot;
     622               0 :   NS_IF_ADDREF(*aResult);
     623               0 :   return NS_OK;
     624                 : }
     625                 : 
     626                 : /* Get the max size of the history list */
     627                 : NS_IMETHODIMP
     628               0 : nsSHistory::GetMaxLength(PRInt32 * aResult)
     629                 : {
     630               0 :   NS_ENSURE_ARG_POINTER(aResult);
     631               0 :   *aResult = gHistoryMaxSize;
     632               0 :   return NS_OK;
     633                 : }
     634                 : 
     635                 : /* Set the max size of the history list */
     636                 : NS_IMETHODIMP
     637               0 : nsSHistory::SetMaxLength(PRInt32 aMaxSize)
     638                 : {
     639               0 :   if (aMaxSize < 0)
     640               0 :     return NS_ERROR_ILLEGAL_VALUE;
     641                 : 
     642               0 :   gHistoryMaxSize = aMaxSize;
     643               0 :   if (mLength > aMaxSize)
     644               0 :     PurgeHistory(mLength-aMaxSize);
     645               0 :   return NS_OK;
     646                 : }
     647                 : 
     648                 : NS_IMETHODIMP
     649               0 : nsSHistory::PurgeHistory(PRInt32 aEntries)
     650                 : {
     651               0 :   if (mLength <= 0 || aEntries <= 0)
     652               0 :     return NS_ERROR_FAILURE;
     653                 : 
     654               0 :   aEntries = NS_MIN(aEntries, mLength);
     655                 :   
     656               0 :   bool purgeHistory = true;
     657                 :   // Notify the listener about the history purge
     658               0 :   if (mListener) {
     659               0 :     nsCOMPtr<nsISHistoryListener> listener(do_QueryReferent(mListener));
     660               0 :     if (listener) {
     661               0 :       listener->OnHistoryPurge(aEntries, &purgeHistory);
     662                 :     } 
     663                 :   }
     664                 : 
     665               0 :   if (!purgeHistory) {
     666                 :     // Listener asked us not to purge
     667               0 :     return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
     668                 :   }
     669                 : 
     670               0 :   PRInt32 cnt = 0;
     671               0 :   while (cnt < aEntries) {
     672               0 :     nsCOMPtr<nsISHTransaction> nextTxn;
     673               0 :     if (mListRoot) {
     674               0 :       mListRoot->GetNext(getter_AddRefs(nextTxn));
     675               0 :       mListRoot->SetNext(nsnull);
     676                 :     }
     677               0 :     mListRoot = nextTxn;
     678               0 :     if (mListRoot) {
     679               0 :       mListRoot->SetPrev(nsnull);
     680                 :     }
     681               0 :     cnt++;        
     682                 :   }
     683               0 :   mLength -= cnt;
     684               0 :   mIndex -= cnt;
     685                 : 
     686                 :   // Now if we were not at the end of the history, mIndex could have
     687                 :   // become far too negative.  If so, just set it to -1.
     688               0 :   if (mIndex < -1) {
     689               0 :     mIndex = -1;
     690                 :   }
     691                 : 
     692               0 :   if (mRootDocShell)
     693               0 :     mRootDocShell->HistoryPurged(cnt);
     694                 : 
     695               0 :   return NS_OK;
     696                 : }
     697                 : 
     698                 : 
     699                 : NS_IMETHODIMP
     700               0 : nsSHistory::AddSHistoryListener(nsISHistoryListener * aListener)
     701                 : {
     702               0 :   NS_ENSURE_ARG_POINTER(aListener);
     703                 : 
     704                 :   // Check if the listener supports Weak Reference. This is a must.
     705                 :   // This listener functionality is used by embedders and we want to 
     706                 :   // have the right ownership with who ever listens to SHistory
     707               0 :   nsWeakPtr listener = do_GetWeakReference(aListener);
     708               0 :   if (!listener) return NS_ERROR_FAILURE;
     709               0 :   mListener = listener;
     710               0 :   return NS_OK;
     711                 : }
     712                 : 
     713                 : 
     714                 : NS_IMETHODIMP
     715               0 : nsSHistory::RemoveSHistoryListener(nsISHistoryListener * aListener)
     716                 : {
     717                 :   // Make sure the listener that wants to be removed is the
     718                 :   // one we have in store. 
     719               0 :   nsWeakPtr listener = do_GetWeakReference(aListener);  
     720               0 :   if (listener == mListener) {
     721               0 :     mListener = nsnull;
     722               0 :     return NS_OK;
     723                 :   }
     724               0 :   return NS_ERROR_FAILURE;
     725                 : }
     726                 : 
     727                 : 
     728                 : /* Replace an entry in the History list at a particular index.
     729                 :  * Do not update index or count.
     730                 :  */
     731                 : NS_IMETHODIMP
     732               0 : nsSHistory::ReplaceEntry(PRInt32 aIndex, nsISHEntry * aReplaceEntry)
     733                 : {
     734               0 :   NS_ENSURE_ARG(aReplaceEntry);
     735                 :   nsresult rv;
     736               0 :   nsCOMPtr<nsISHTransaction> currentTxn;
     737                 : 
     738               0 :   if (!mListRoot) // Session History is not initialised.
     739               0 :     return NS_ERROR_FAILURE;
     740                 : 
     741               0 :   rv = GetTransactionAtIndex(aIndex, getter_AddRefs(currentTxn));
     742                 : 
     743               0 :   if(currentTxn)
     744                 :   {
     745                 :     // Set the replacement entry in the transaction
     746               0 :     rv = currentTxn->SetSHEntry(aReplaceEntry);
     747               0 :     rv = currentTxn->SetPersist(true);
     748                 :   }
     749               0 :   return rv;
     750                 : }
     751                 : 
     752                 : /* Get a handle to the Session history listener */
     753                 : NS_IMETHODIMP
     754               0 : nsSHistory::GetListener(nsISHistoryListener ** aListener)
     755                 : {
     756               0 :   NS_ENSURE_ARG_POINTER(aListener);
     757               0 :   if (mListener) 
     758               0 :     CallQueryReferent(mListener.get(),  aListener);
     759                 :   // Don't addref aListener. It is a weak pointer.
     760               0 :   return NS_OK;
     761                 : }
     762                 : 
     763                 : NS_IMETHODIMP
     764               0 : nsSHistory::EvictOutOfRangeContentViewers(PRInt32 aIndex)
     765                 : {
     766                 :   // Check our per SHistory object limit in the currently navigated SHistory
     767               0 :   EvictOutOfRangeWindowContentViewers(aIndex);
     768                 :   // Check our total limit across all SHistory objects
     769               0 :   GloballyEvictContentViewers();
     770               0 :   return NS_OK;
     771                 : }
     772                 : 
     773                 : NS_IMETHODIMP
     774               0 : nsSHistory::EvictAllContentViewers()
     775                 : {
     776                 :   // XXXbz we don't actually do a good job of evicting things as we should, so
     777                 :   // we might have viewers quite far from mIndex.  So just evict everything.
     778               0 :   nsCOMPtr<nsISHTransaction> trans = mListRoot;
     779               0 :   while (trans) {
     780               0 :     EvictContentViewerForTransaction(trans);
     781                 : 
     782               0 :     nsISHTransaction *temp = trans;
     783               0 :     temp->GetNext(getter_AddRefs(trans));
     784                 :   }
     785                 : 
     786               0 :   return NS_OK;
     787                 : }
     788                 : 
     789                 : 
     790                 : 
     791                 : //*****************************************************************************
     792                 : //    nsSHistory: nsIWebNavigation
     793                 : //*****************************************************************************
     794                 : 
     795                 : NS_IMETHODIMP
     796               0 : nsSHistory::GetCanGoBack(bool * aCanGoBack)
     797                 : {
     798               0 :   NS_ENSURE_ARG_POINTER(aCanGoBack);
     799               0 :   *aCanGoBack = false;
     800                 : 
     801               0 :   PRInt32 index = -1;
     802               0 :   NS_ENSURE_SUCCESS(GetIndex(&index), NS_ERROR_FAILURE);
     803               0 :   if(index > 0)
     804               0 :      *aCanGoBack = true;
     805                 : 
     806               0 :   return NS_OK;
     807                 : }
     808                 : 
     809                 : NS_IMETHODIMP
     810               0 : nsSHistory::GetCanGoForward(bool * aCanGoForward)
     811                 : {
     812               0 :   NS_ENSURE_ARG_POINTER(aCanGoForward);
     813               0 :   *aCanGoForward = false;
     814                 : 
     815               0 :   PRInt32 index = -1;
     816               0 :   PRInt32 count = -1;
     817                 : 
     818               0 :   NS_ENSURE_SUCCESS(GetIndex(&index), NS_ERROR_FAILURE);
     819               0 :   NS_ENSURE_SUCCESS(GetCount(&count), NS_ERROR_FAILURE);
     820                 : 
     821               0 :   if((index >= 0) && (index < (count - 1)))
     822               0 :     *aCanGoForward = true;
     823                 : 
     824               0 :   return NS_OK;
     825                 : }
     826                 : 
     827                 : NS_IMETHODIMP
     828               0 : nsSHistory::GoBack()
     829                 : {
     830               0 :   bool canGoBack = false;
     831                 : 
     832               0 :   GetCanGoBack(&canGoBack);
     833               0 :   if (!canGoBack)  // Can't go back
     834               0 :     return NS_ERROR_UNEXPECTED;
     835               0 :   return LoadEntry(mIndex-1, nsIDocShellLoadInfo::loadHistory, HIST_CMD_BACK);
     836                 : }
     837                 : 
     838                 : 
     839                 : NS_IMETHODIMP
     840               0 : nsSHistory::GoForward()
     841                 : {
     842               0 :   bool canGoForward = false;
     843                 : 
     844               0 :   GetCanGoForward(&canGoForward);
     845               0 :   if (!canGoForward)  // Can't go forward
     846               0 :     return NS_ERROR_UNEXPECTED;
     847               0 :   return LoadEntry(mIndex+1, nsIDocShellLoadInfo::loadHistory, HIST_CMD_FORWARD);
     848                 : }
     849                 : 
     850                 : NS_IMETHODIMP
     851               0 : nsSHistory::Reload(PRUint32 aReloadFlags)
     852                 : {
     853                 :   nsresult rv;
     854                 :   nsDocShellInfoLoadType loadType;
     855               0 :   if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY && 
     856                 :       aReloadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)
     857                 :   {
     858               0 :     loadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
     859                 :   }
     860               0 :   else if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY)
     861                 :   {
     862               0 :     loadType = nsIDocShellLoadInfo::loadReloadBypassProxy;
     863                 :   }
     864               0 :   else if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)
     865                 :   {
     866               0 :     loadType = nsIDocShellLoadInfo::loadReloadBypassCache;
     867                 :   }
     868               0 :   else if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_CHARSET_CHANGE)
     869                 :   {
     870               0 :     loadType = nsIDocShellLoadInfo::loadReloadCharsetChange;
     871                 :   }
     872                 :   else
     873                 :   {
     874               0 :     loadType = nsIDocShellLoadInfo::loadReloadNormal;
     875                 :   }
     876                 :   
     877                 :   // Notify listeners
     878               0 :   bool canNavigate = true;
     879               0 :   if (mListener) {
     880               0 :     nsCOMPtr<nsISHistoryListener> listener(do_QueryReferent(mListener));
     881                 :     // We are reloading. Send Reload notifications. 
     882                 :     // nsDocShellLoadFlagType is not public, where as nsIWebNavigation
     883                 :     // is public. So send the reload notifications with the
     884                 :     // nsIWebNavigation flags. 
     885               0 :     if (listener) {
     886               0 :       nsCOMPtr<nsIURI> currentURI;
     887               0 :       rv = GetCurrentURI(getter_AddRefs(currentURI));
     888               0 :       listener->OnHistoryReload(currentURI, aReloadFlags, &canNavigate);
     889                 :     }
     890                 :   }
     891               0 :   if (!canNavigate)
     892               0 :     return NS_OK;
     893                 : 
     894               0 :   return LoadEntry(mIndex, loadType, HIST_CMD_RELOAD);
     895                 : }
     896                 : 
     897                 : NS_IMETHODIMP
     898               0 : nsSHistory::ReloadCurrentEntry()
     899                 : {
     900                 :   // Notify listeners
     901               0 :   bool canNavigate = true;
     902               0 :   if (mListener) {
     903               0 :     nsCOMPtr<nsISHistoryListener> listener(do_QueryReferent(mListener));
     904               0 :     if (listener) {
     905               0 :       nsCOMPtr<nsIURI> currentURI;
     906               0 :       GetCurrentURI(getter_AddRefs(currentURI));
     907               0 :       listener->OnHistoryGotoIndex(mIndex, currentURI, &canNavigate);
     908                 :     }
     909                 :   }
     910               0 :   if (!canNavigate)
     911               0 :     return NS_OK;
     912                 : 
     913               0 :   return LoadEntry(mIndex, nsIDocShellLoadInfo::loadHistory, HIST_CMD_RELOAD);
     914                 : }
     915                 : 
     916                 : void
     917               0 : nsSHistory::EvictOutOfRangeWindowContentViewers(PRInt32 aIndex)
     918                 : {
     919                 :   // XXX rename method to EvictContentViewersExceptAroundIndex, or something.
     920                 : 
     921                 :   // We need to release all content viewers that are no longer in the range
     922                 :   //
     923                 :   //  aIndex - gHistoryMaxViewers to aIndex + gHistoryMaxViewers
     924                 :   //
     925                 :   // to ensure that this SHistory object isn't responsible for more than
     926                 :   // gHistoryMaxViewers content viewers.  But our job is complicated by the
     927                 :   // fact that two transactions which are related by either hash navigations or
     928                 :   // history.pushState will have the same content viewer.
     929                 :   //
     930                 :   // To illustrate the issue, suppose gHistoryMaxViewers = 3 and we have four
     931                 :   // linked transactions in our history.  Suppose we then add a new content
     932                 :   // viewer and call into this function.  So the history looks like:
     933                 :   //
     934                 :   //   A A A A B
     935                 :   //     +     *
     936                 :   //
     937                 :   // where the letters are content viewers and + and * denote the beginning and
     938                 :   // end of the range aIndex +/- gHistoryMaxViewers.
     939                 :   //
     940                 :   // Although one copy of the content viewer A exists outside the range, we
     941                 :   // don't want to evict A, because it has other copies in range!
     942                 :   //
     943                 :   // We therefore adjust our eviction strategy to read:
     944                 :   //
     945                 :   //   Evict each content viewer outside the range aIndex -/+
     946                 :   //   gHistoryMaxViewers, unless that content viewer also appears within the
     947                 :   //   range.
     948                 :   //
     949                 :   // (Note that it's entirely legal to have two copies of one content viewer
     950                 :   // separated by a different content viewer -- call pushState twice, go back
     951                 :   // once, and refresh -- so we can't rely on identical viewers only appearing
     952                 :   // adjacent to one another.)
     953                 : 
     954               0 :   if (aIndex < 0) {
     955               0 :     return;
     956                 :   }
     957               0 :   NS_ASSERTION(aIndex < mLength, "aIndex is out of range");
     958               0 :   if (aIndex >= mLength) {
     959               0 :     return;
     960                 :   }
     961                 : 
     962                 :   // Calculate the range that's safe from eviction.
     963               0 :   PRInt32 startSafeIndex = NS_MAX(0, aIndex - gHistoryMaxViewers);
     964               0 :   PRInt32 endSafeIndex = NS_MIN(mLength, aIndex + gHistoryMaxViewers);
     965                 : 
     966               0 :   LOG(("EvictOutOfRangeWindowContentViewers(index=%d), "
     967                 :        "mLength=%d. Safe range [%d, %d]",
     968                 :        aIndex, mLength, startSafeIndex, endSafeIndex)); 
     969                 : 
     970                 :   // The content viewers in range aIndex -/+ gHistoryMaxViewers will not be
     971                 :   // evicted.  Collect a set of them so we don't accidentally evict one of them
     972                 :   // if it appears outside this range.
     973               0 :   nsCOMArray<nsIContentViewer> safeViewers;
     974               0 :   nsCOMPtr<nsISHTransaction> trans;
     975               0 :   GetTransactionAtIndex(startSafeIndex, getter_AddRefs(trans));
     976               0 :   for (PRUint32 i = startSafeIndex; trans && i <= endSafeIndex; i++) {
     977               0 :     nsCOMPtr<nsIContentViewer> viewer = GetContentViewerForTransaction(trans);
     978               0 :     safeViewers.AppendObject(viewer);
     979               0 :     nsISHTransaction *temp = trans;
     980               0 :     temp->GetNext(getter_AddRefs(trans));
     981                 :   }
     982                 : 
     983                 :   // Walk the SHistory list and evict any content viewers that aren't safe.
     984               0 :   GetTransactionAtIndex(0, getter_AddRefs(trans));
     985               0 :   while (trans) {
     986               0 :     nsCOMPtr<nsIContentViewer> viewer = GetContentViewerForTransaction(trans);
     987               0 :     if (safeViewers.IndexOf(viewer) == -1) {
     988               0 :       EvictContentViewerForTransaction(trans);
     989                 :     }
     990                 : 
     991               0 :     nsISHTransaction *temp = trans;
     992               0 :     temp->GetNext(getter_AddRefs(trans));
     993                 :   }
     994                 : }
     995                 : 
     996                 : namespace {
     997                 : 
     998                 : class TransactionAndDistance
     999               0 : {
    1000                 : public:
    1001               0 :   TransactionAndDistance(nsISHTransaction *aTrans, PRUint32 aDist)
    1002                 :     : mTransaction(aTrans)
    1003               0 :     , mDistance(aDist)
    1004                 :   {
    1005               0 :     mViewer = GetContentViewerForTransaction(aTrans);
    1006               0 :     NS_ASSERTION(mViewer, "Transaction should have a content viewer");
    1007                 : 
    1008               0 :     nsCOMPtr<nsISHEntry> shentry;
    1009               0 :     mTransaction->GetSHEntry(getter_AddRefs(shentry));
    1010                 : 
    1011               0 :     nsCOMPtr<nsISHEntryInternal> shentryInternal = do_QueryInterface(shentry);
    1012               0 :     if (shentryInternal) {
    1013               0 :       shentryInternal->GetLastTouched(&mLastTouched);
    1014                 :     } else {
    1015               0 :       NS_WARNING("Can't cast to nsISHEntryInternal?");
    1016               0 :       mLastTouched = 0;
    1017                 :     }
    1018               0 :   }
    1019                 : 
    1020               0 :   bool operator<(const TransactionAndDistance &aOther) const
    1021                 :   {
    1022                 :     // Compare distances first, and fall back to last-accessed times.
    1023               0 :     if (aOther.mDistance != this->mDistance) {
    1024               0 :       return this->mDistance < aOther.mDistance;
    1025                 :     }
    1026                 : 
    1027               0 :     return this->mLastTouched < aOther.mLastTouched;
    1028                 :   }
    1029                 : 
    1030               0 :   bool operator==(const TransactionAndDistance &aOther) const
    1031                 :   {
    1032                 :     // This is a little silly; we need == so the default comaprator can be
    1033                 :     // instantiated, but this function is never actually called when we sort
    1034                 :     // the list of TransactionAndDistance objects.
    1035                 :     return aOther.mDistance == this->mDistance &&
    1036               0 :            aOther.mLastTouched == this->mLastTouched;
    1037                 :   }
    1038                 : 
    1039                 :   nsCOMPtr<nsISHTransaction> mTransaction;
    1040                 :   nsCOMPtr<nsIContentViewer> mViewer;
    1041                 :   PRUint32 mLastTouched;
    1042                 :   PRInt32 mDistance;
    1043                 : };
    1044                 : 
    1045                 : } // anonymous namespace
    1046                 : 
    1047                 : //static
    1048                 : void
    1049              81 : nsSHistory::GloballyEvictContentViewers()
    1050                 : {
    1051                 :   // First, collect from each SHistory object the transactions which have a
    1052                 :   // cached content viewer.  Associate with each transaction its distance from
    1053                 :   // its SHistory's current index.
    1054                 : 
    1055             162 :   nsTArray<TransactionAndDistance> transactions;
    1056                 : 
    1057              81 :   nsSHistory *shist = static_cast<nsSHistory*>(PR_LIST_HEAD(&gSHistoryList));
    1058             162 :   while (shist != &gSHistoryList) {
    1059                 : 
    1060                 :     // Maintain a list of the transactions which have viewers and belong to
    1061                 :     // this particular shist object.  We'll add this list to the global list,
    1062                 :     // |transactions|, eventually.
    1063               0 :     nsTArray<TransactionAndDistance> shTransactions;
    1064                 : 
    1065                 :     // Content viewers are likely to exist only within shist->mIndex -/+
    1066                 :     // gHistoryMaxViewers, so only search within that range.
    1067                 :     //
    1068                 :     // A content viewer might exist outside that range due to either:
    1069                 :     //
    1070                 :     //   * history.pushState or hash navigations, in which case a copy of the
    1071                 :     //     content viewer should exist within the range, or
    1072                 :     //
    1073                 :     //   * bugs which cause us not to call nsSHistory::EvictContentViewers()
    1074                 :     //     often enough.  Once we do call EvictContentViewers() for the
    1075                 :     //     SHistory object in question, we'll do a full search of its history
    1076                 :     //     and evict the out-of-range content viewers, so we don't bother here.
    1077                 :     //
    1078               0 :     PRInt32 startIndex = NS_MAX(0, shist->mIndex - gHistoryMaxViewers);
    1079                 :     PRInt32 endIndex = NS_MIN(shist->mLength - 1,
    1080               0 :                               shist->mIndex + gHistoryMaxViewers);
    1081               0 :     nsCOMPtr<nsISHTransaction> trans;
    1082               0 :     shist->GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
    1083               0 :     for (PRInt32 i = startIndex; trans && i <= endIndex; i++) {
    1084                 :       nsCOMPtr<nsIContentViewer> contentViewer =
    1085               0 :         GetContentViewerForTransaction(trans);
    1086                 : 
    1087               0 :       if (contentViewer) {
    1088                 :         // Because one content viewer might belong to multiple SHEntries, we
    1089                 :         // have to search through shTransactions to see if we already know
    1090                 :         // about this content viewer.  If we find the viewer, update its
    1091                 :         // distance from the SHistory's index and continue.
    1092               0 :         bool found = false;
    1093               0 :         for (PRUint32 j = 0; j < shTransactions.Length(); j++) {
    1094               0 :           TransactionAndDistance &container = shTransactions[j];
    1095               0 :           if (container.mViewer == contentViewer) {
    1096                 :             container.mDistance = NS_MIN(container.mDistance,
    1097               0 :                                          NS_ABS(i - shist->mIndex));
    1098               0 :             found = true;
    1099               0 :             break;
    1100                 :           }
    1101                 :         }
    1102                 : 
    1103                 :         // If we didn't find a TransactionAndDistance for this content viewer, make a new
    1104                 :         // one.
    1105               0 :         if (!found) {
    1106               0 :           TransactionAndDistance container(trans, NS_ABS(i - shist->mIndex));
    1107               0 :           shTransactions.AppendElement(container);
    1108                 :         }
    1109                 :       }
    1110                 : 
    1111               0 :       nsISHTransaction *temp = trans;
    1112               0 :       temp->GetNext(getter_AddRefs(trans));
    1113                 :     }
    1114                 : 
    1115                 :     // We've found all the transactions belonging to shist which have viewers.
    1116                 :     // Add those transactions to our global list and move on.
    1117               0 :     transactions.AppendElements(shTransactions);
    1118               0 :     shist = static_cast<nsSHistory*>(PR_NEXT_LINK(shist));
    1119                 :   }
    1120                 : 
    1121                 :   // We now have collected all cached content viewers.  First check that we
    1122                 :   // have enough that we actually need to evict some.
    1123              81 :   if ((PRInt32)transactions.Length() <= sHistoryMaxTotalViewers) {
    1124                 :     return;
    1125                 :   }
    1126                 : 
    1127                 :   // If we need to evict, sort our list of transactions and evict the largest
    1128                 :   // ones.  (We could of course get better algorithmic complexity here by using
    1129                 :   // a heap or something more clever.  But sHistoryMaxTotalViewers isn't large,
    1130                 :   // so let's not worry about it.)
    1131               0 :   transactions.Sort();
    1132                 : 
    1133               0 :   for (PRInt32 i = transactions.Length() - 1;
    1134                 :        i >= sHistoryMaxTotalViewers; --i) {
    1135                 : 
    1136               0 :     EvictContentViewerForTransaction(transactions[i].mTransaction);
    1137                 : 
    1138                 :   }
    1139                 : }
    1140                 : 
    1141                 : nsresult
    1142               0 : nsSHistory::EvictExpiredContentViewerForEntry(nsIBFCacheEntry *aEntry)
    1143                 : {
    1144               0 :   PRInt32 startIndex = NS_MAX(0, mIndex - gHistoryMaxViewers);
    1145                 :   PRInt32 endIndex = NS_MIN(mLength - 1,
    1146               0 :                             mIndex + gHistoryMaxViewers);
    1147               0 :   nsCOMPtr<nsISHTransaction> trans;
    1148               0 :   GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
    1149                 : 
    1150                 :   PRInt32 i;
    1151               0 :   for (i = startIndex; trans && i <= endIndex; ++i) {
    1152               0 :     nsCOMPtr<nsISHEntry> entry;
    1153               0 :     trans->GetSHEntry(getter_AddRefs(entry));
    1154                 : 
    1155                 :     // Does entry have the same BFCacheEntry as the argument to this method?
    1156               0 :     if (entry->HasBFCacheEntry(aEntry)) {
    1157                 :       break;
    1158                 :     }
    1159                 : 
    1160               0 :     nsISHTransaction *temp = trans;
    1161               0 :     temp->GetNext(getter_AddRefs(trans));
    1162                 :   }
    1163               0 :   if (i > endIndex)
    1164               0 :     return NS_OK;
    1165                 :   
    1166               0 :   if (i == mIndex) {
    1167               0 :     NS_WARNING("How did the current SHEntry expire?");
    1168               0 :     return NS_OK;
    1169                 :   }
    1170                 : 
    1171               0 :   EvictContentViewerForTransaction(trans);
    1172                 : 
    1173               0 :   return NS_OK;
    1174                 : }
    1175                 : 
    1176                 : // Evicts all content viewers in all history objects.  This is very
    1177                 : // inefficient, because it requires a linear search through all SHistory
    1178                 : // objects for each viewer to be evicted.  However, this method is called
    1179                 : // infrequently -- only when the disk or memory cache is cleared.
    1180                 : 
    1181                 : //static
    1182                 : void
    1183              79 : nsSHistory::GloballyEvictAllContentViewers()
    1184                 : {
    1185              79 :   PRInt32 maxViewers = sHistoryMaxTotalViewers;
    1186              79 :   sHistoryMaxTotalViewers = 0;
    1187              79 :   GloballyEvictContentViewers();
    1188              79 :   sHistoryMaxTotalViewers = maxViewers;
    1189              79 : }
    1190                 : 
    1191               0 : void GetDynamicChildren(nsISHContainer* aContainer,
    1192                 :                         nsTArray<PRUint64>& aDocshellIDs,
    1193                 :                         bool aOnlyTopLevelDynamic)
    1194                 : {
    1195               0 :   PRInt32 count = 0;
    1196               0 :   aContainer->GetChildCount(&count);
    1197               0 :   for (PRInt32 i = 0; i < count; ++i) {
    1198               0 :     nsCOMPtr<nsISHEntry> child;
    1199               0 :     aContainer->GetChildAt(i, getter_AddRefs(child));
    1200               0 :     if (child) {
    1201               0 :       bool dynAdded = false;
    1202               0 :       child->IsDynamicallyAdded(&dynAdded);
    1203               0 :       if (dynAdded) {
    1204               0 :         PRUint64 docshellID = 0;
    1205               0 :         child->GetDocshellID(&docshellID);
    1206               0 :         aDocshellIDs.AppendElement(docshellID);
    1207                 :       }
    1208               0 :       if (!dynAdded || !aOnlyTopLevelDynamic) {
    1209               0 :         nsCOMPtr<nsISHContainer> childAsContainer = do_QueryInterface(child);
    1210               0 :         if (childAsContainer) {
    1211                 :           GetDynamicChildren(childAsContainer, aDocshellIDs,
    1212               0 :                              aOnlyTopLevelDynamic);
    1213                 :         }
    1214                 :       }
    1215                 :     }
    1216                 :   }
    1217               0 : }
    1218                 : 
    1219                 : bool
    1220               0 : RemoveFromSessionHistoryContainer(nsISHContainer* aContainer,
    1221                 :                                   nsTArray<PRUint64>& aDocshellIDs)
    1222                 : {
    1223               0 :   nsCOMPtr<nsISHEntry> root = do_QueryInterface(aContainer);
    1224               0 :   NS_ENSURE_TRUE(root, false);
    1225                 : 
    1226               0 :   bool didRemove = false;
    1227               0 :   PRInt32 childCount = 0;
    1228               0 :   aContainer->GetChildCount(&childCount);
    1229               0 :   for (PRInt32 i = childCount - 1; i >= 0; --i) {
    1230               0 :     nsCOMPtr<nsISHEntry> child;
    1231               0 :     aContainer->GetChildAt(i, getter_AddRefs(child));
    1232               0 :     if (child) {
    1233               0 :       PRUint64 docshelldID = 0;
    1234               0 :       child->GetDocshellID(&docshelldID);
    1235               0 :       if (aDocshellIDs.Contains(docshelldID)) {
    1236               0 :         didRemove = true;
    1237               0 :         aContainer->RemoveChild(child);
    1238                 :       } else {
    1239               0 :         nsCOMPtr<nsISHContainer> container = do_QueryInterface(child);
    1240               0 :         if (container) {
    1241                 :           bool childRemoved =
    1242               0 :             RemoveFromSessionHistoryContainer(container, aDocshellIDs);
    1243               0 :           if (childRemoved) {
    1244               0 :             didRemove = true;
    1245                 :           }
    1246                 :         }
    1247                 :       }
    1248                 :     }
    1249                 :   }
    1250               0 :   return didRemove;
    1251                 : }
    1252                 : 
    1253               0 : bool RemoveChildEntries(nsISHistory* aHistory, PRInt32 aIndex,
    1254                 :                           nsTArray<PRUint64>& aEntryIDs)
    1255                 : {
    1256               0 :   nsCOMPtr<nsIHistoryEntry> rootHE;
    1257               0 :   aHistory->GetEntryAtIndex(aIndex, false, getter_AddRefs(rootHE));
    1258               0 :   nsCOMPtr<nsISHContainer> root = do_QueryInterface(rootHE);
    1259               0 :   return root ? RemoveFromSessionHistoryContainer(root, aEntryIDs) : false;
    1260                 : }
    1261                 : 
    1262               0 : bool IsSameTree(nsISHEntry* aEntry1, nsISHEntry* aEntry2)
    1263                 : {
    1264               0 :   if (!aEntry1 && !aEntry2) {
    1265               0 :     return true;
    1266                 :   }
    1267               0 :   if ((!aEntry1 && aEntry2) || (aEntry1 && !aEntry2)) {
    1268               0 :     return false;
    1269                 :   }
    1270                 :   PRUint32 id1, id2;
    1271               0 :   aEntry1->GetID(&id1);
    1272               0 :   aEntry2->GetID(&id2);
    1273               0 :   if (id1 != id2) {
    1274               0 :     return false;
    1275                 :   }
    1276                 : 
    1277               0 :   nsCOMPtr<nsISHContainer> container1 = do_QueryInterface(aEntry1);
    1278               0 :   nsCOMPtr<nsISHContainer> container2 = do_QueryInterface(aEntry2);
    1279                 :   PRInt32 count1, count2;
    1280               0 :   container1->GetChildCount(&count1);
    1281               0 :   container2->GetChildCount(&count2);
    1282                 :   // We allow null entries in the end of the child list.
    1283               0 :   PRInt32 count = NS_MAX(count1, count2);
    1284               0 :   for (PRInt32 i = 0; i < count; ++i) {
    1285               0 :     nsCOMPtr<nsISHEntry> child1, child2;
    1286               0 :     container1->GetChildAt(i, getter_AddRefs(child1));
    1287               0 :     container2->GetChildAt(i, getter_AddRefs(child2));
    1288               0 :     if (!IsSameTree(child1, child2)) {
    1289               0 :       return false;
    1290                 :     }
    1291                 :   }
    1292                 :   
    1293               0 :   return true;
    1294                 : }
    1295                 : 
    1296                 : bool
    1297               0 : nsSHistory::RemoveDuplicate(PRInt32 aIndex, bool aKeepNext)
    1298                 : {
    1299               0 :   NS_ASSERTION(aIndex >= 0, "aIndex must be >= 0!");
    1300               0 :   NS_ASSERTION(aIndex != 0 || aKeepNext,
    1301                 :                "If we're removing index 0 we must be keeping the next");
    1302               0 :   NS_ASSERTION(aIndex != mIndex, "Shouldn't remove mIndex!");
    1303               0 :   PRInt32 compareIndex = aKeepNext ? aIndex + 1 : aIndex - 1;
    1304               0 :   nsCOMPtr<nsIHistoryEntry> rootHE1, rootHE2;
    1305               0 :   GetEntryAtIndex(aIndex, false, getter_AddRefs(rootHE1));
    1306               0 :   GetEntryAtIndex(compareIndex, false, getter_AddRefs(rootHE2));
    1307               0 :   nsCOMPtr<nsISHEntry> root1 = do_QueryInterface(rootHE1);
    1308               0 :   nsCOMPtr<nsISHEntry> root2 = do_QueryInterface(rootHE2);
    1309               0 :   if (IsSameTree(root1, root2)) {
    1310               0 :     nsCOMPtr<nsISHTransaction> txToRemove, txToKeep, txNext, txPrev;
    1311               0 :     GetTransactionAtIndex(aIndex, getter_AddRefs(txToRemove));
    1312               0 :     GetTransactionAtIndex(compareIndex, getter_AddRefs(txToKeep));
    1313               0 :     NS_ENSURE_TRUE(txToRemove, false);
    1314               0 :     NS_ENSURE_TRUE(txToKeep, false);
    1315               0 :     txToRemove->GetNext(getter_AddRefs(txNext));
    1316               0 :     txToRemove->GetPrev(getter_AddRefs(txPrev));
    1317               0 :     txToRemove->SetNext(nsnull);
    1318               0 :     txToRemove->SetPrev(nsnull);
    1319               0 :     if (aKeepNext) {
    1320               0 :       if (txPrev) {
    1321               0 :         txPrev->SetNext(txToKeep);
    1322                 :       } else {
    1323               0 :         txToKeep->SetPrev(nsnull);
    1324                 :       }
    1325                 :     } else {
    1326               0 :       txToKeep->SetNext(txNext);
    1327                 :     }
    1328                 : 
    1329               0 :     if (aIndex == 0 && aKeepNext) {
    1330               0 :       NS_ASSERTION(txToRemove == mListRoot,
    1331                 :                    "Transaction at index 0 should be mListRoot!");
    1332                 :       // We're removing the very first session history transaction!
    1333               0 :       mListRoot = txToKeep;
    1334                 :     }
    1335               0 :     if (mRootDocShell) {
    1336               0 :       static_cast<nsDocShell*>(mRootDocShell)->HistoryTransactionRemoved(aIndex);
    1337                 :     }
    1338                 : 
    1339                 :     // Adjust our indices to reflect the removed transaction
    1340               0 :     if (mIndex > aIndex) {
    1341               0 :       mIndex = mIndex - 1;
    1342                 :     }
    1343                 : 
    1344                 :     // NB: If the transaction we are removing is the transaction currently
    1345                 :     // being navigated to (mRequestedIndex) then we adjust the index
    1346                 :     // only if we're not keeping the next entry (because if we are keeping
    1347                 :     // the next entry (because the current is a duplicate of the next), then
    1348                 :     // that entry slides into the spot that we're currently pointing to.
    1349                 :     // We don't do this adjustment for mIndex because mIndex cannot equal
    1350                 :     // aIndex.
    1351                 : 
    1352                 :     // NB: We don't need to guard on mRequestedIndex being nonzero here,
    1353                 :     // because either they're strictly greater than aIndex which is at least
    1354                 :     // zero, or they are equal to aIndex in which case aKeepNext must be true
    1355                 :     // if aIndex is zero.
    1356               0 :     if (mRequestedIndex > aIndex || (mRequestedIndex == aIndex && !aKeepNext)) {
    1357               0 :       mRequestedIndex = mRequestedIndex - 1;
    1358                 :     }
    1359               0 :     --mLength;
    1360               0 :     return true;
    1361                 :   }
    1362               0 :   return false;
    1363                 : }
    1364                 : 
    1365                 : NS_IMETHODIMP_(void)
    1366               0 : nsSHistory::RemoveEntries(nsTArray<PRUint64>& aIDs, PRInt32 aStartIndex)
    1367                 : {
    1368               0 :   PRInt32 index = aStartIndex;
    1369               0 :   while(index >= 0 && RemoveChildEntries(this, --index, aIDs));
    1370               0 :   PRInt32 minIndex = index;
    1371               0 :   index = aStartIndex;
    1372               0 :   while(index >= 0 && RemoveChildEntries(this, index++, aIDs));
    1373                 :   
    1374                 :   // We need to remove duplicate nsSHEntry trees.
    1375               0 :   bool didRemove = false;
    1376               0 :   while (index > minIndex) {
    1377               0 :     if (index != mIndex) {
    1378               0 :       didRemove = RemoveDuplicate(index, index < mIndex) || didRemove;
    1379                 :     }
    1380               0 :     --index;
    1381                 :   }
    1382               0 :   if (didRemove && mRootDocShell) {
    1383                 :     nsRefPtr<nsIRunnable> ev =
    1384               0 :       NS_NewRunnableMethod(static_cast<nsDocShell*>(mRootDocShell),
    1385               0 :                            &nsDocShell::FireDummyOnLocationChange);
    1386               0 :     NS_DispatchToCurrentThread(ev);
    1387                 :   }
    1388               0 : }
    1389                 : 
    1390                 : void
    1391               0 : nsSHistory::RemoveDynEntries(PRInt32 aOldIndex, PRInt32 aNewIndex)
    1392                 : {
    1393                 :   // Search for the entries which are in the current index,
    1394                 :   // but not in the new one.
    1395               0 :   nsCOMPtr<nsISHEntry> originalSH;
    1396               0 :   GetEntryAtIndex(aOldIndex, false, getter_AddRefs(originalSH));
    1397               0 :   nsCOMPtr<nsISHContainer> originalContainer = do_QueryInterface(originalSH);
    1398               0 :   nsAutoTArray<PRUint64, 16> toBeRemovedEntries;
    1399               0 :   if (originalContainer) {
    1400               0 :     nsTArray<PRUint64> originalDynDocShellIDs;
    1401               0 :     GetDynamicChildren(originalContainer, originalDynDocShellIDs, true);
    1402               0 :     if (originalDynDocShellIDs.Length()) {
    1403               0 :       nsCOMPtr<nsISHEntry> currentSH;
    1404               0 :       GetEntryAtIndex(aNewIndex, false, getter_AddRefs(currentSH));
    1405               0 :       nsCOMPtr<nsISHContainer> newContainer = do_QueryInterface(currentSH);
    1406               0 :       if (newContainer) {
    1407               0 :         nsTArray<PRUint64> newDynDocShellIDs;
    1408               0 :         GetDynamicChildren(newContainer, newDynDocShellIDs, false);
    1409               0 :         for (PRUint32 i = 0; i < originalDynDocShellIDs.Length(); ++i) {
    1410               0 :           if (!newDynDocShellIDs.Contains(originalDynDocShellIDs[i])) {
    1411               0 :             toBeRemovedEntries.AppendElement(originalDynDocShellIDs[i]);
    1412                 :           }
    1413                 :         }
    1414                 :       }
    1415                 :     }
    1416                 :   }
    1417               0 :   if (toBeRemovedEntries.Length()) {
    1418               0 :     RemoveEntries(toBeRemovedEntries, aOldIndex);
    1419                 :   }
    1420               0 : }
    1421                 : 
    1422                 : NS_IMETHODIMP
    1423               0 : nsSHistory::UpdateIndex()
    1424                 : {
    1425                 :   // Update the actual index with the right value. 
    1426               0 :   if (mIndex != mRequestedIndex && mRequestedIndex != -1) {
    1427               0 :     RemoveDynEntries(mIndex, mRequestedIndex);
    1428               0 :     mIndex = mRequestedIndex;
    1429                 :   }
    1430                 : 
    1431               0 :   mRequestedIndex = -1;
    1432               0 :   return NS_OK;
    1433                 : }
    1434                 : 
    1435                 : NS_IMETHODIMP
    1436               0 : nsSHistory::Stop(PRUint32 aStopFlags)
    1437                 : {
    1438                 :   //Not implemented
    1439               0 :   return NS_OK;
    1440                 : }
    1441                 : 
    1442                 : 
    1443                 : NS_IMETHODIMP
    1444               0 : nsSHistory::GetDocument(nsIDOMDocument** aDocument)
    1445                 : {
    1446                 :   // Not implemented
    1447               0 :   return NS_OK;
    1448                 : }
    1449                 : 
    1450                 : 
    1451                 : NS_IMETHODIMP
    1452               0 : nsSHistory::GetCurrentURI(nsIURI** aResultURI)
    1453                 : {
    1454               0 :   NS_ENSURE_ARG_POINTER(aResultURI);
    1455                 :   nsresult rv;
    1456                 : 
    1457               0 :   nsCOMPtr<nsIHistoryEntry> currentEntry;
    1458               0 :   rv = GetEntryAtIndex(mIndex, false, getter_AddRefs(currentEntry));
    1459               0 :   if (NS_FAILED(rv) && !currentEntry) return rv;
    1460               0 :   rv = currentEntry->GetURI(aResultURI);
    1461               0 :   return rv;
    1462                 : }
    1463                 : 
    1464                 : 
    1465                 : NS_IMETHODIMP
    1466               0 : nsSHistory::GetReferringURI(nsIURI** aURI)
    1467                 : {
    1468               0 :   *aURI = nsnull;
    1469                 :   // Not implemented
    1470               0 :   return NS_OK;
    1471                 : }
    1472                 : 
    1473                 : 
    1474                 : NS_IMETHODIMP
    1475               0 : nsSHistory::SetSessionHistory(nsISHistory* aSessionHistory)
    1476                 : {
    1477                 :   // Not implemented
    1478               0 :   return NS_OK;
    1479                 : }
    1480                 : 
    1481                 :         
    1482                 : NS_IMETHODIMP
    1483               0 : nsSHistory::GetSessionHistory(nsISHistory** aSessionHistory)
    1484                 : {
    1485                 :   // Not implemented
    1486               0 :   return NS_OK;
    1487                 : }
    1488                 : 
    1489                 : 
    1490                 : NS_IMETHODIMP
    1491               0 : nsSHistory::LoadURI(const PRUnichar* aURI,
    1492                 :                     PRUint32 aLoadFlags,
    1493                 :                     nsIURI* aReferringURI,
    1494                 :                     nsIInputStream* aPostStream,
    1495                 :                     nsIInputStream* aExtraHeaderStream)
    1496                 : {
    1497               0 :   return NS_OK;
    1498                 : }
    1499                 : 
    1500                 : NS_IMETHODIMP
    1501               0 : nsSHistory::GotoIndex(PRInt32 aIndex)
    1502                 : {
    1503               0 :   return LoadEntry(aIndex, nsIDocShellLoadInfo::loadHistory, HIST_CMD_GOTOINDEX);
    1504                 : }
    1505                 : 
    1506                 : nsresult
    1507               0 : nsSHistory::LoadNextPossibleEntry(PRInt32 aNewIndex, long aLoadType, PRUint32 aHistCmd)
    1508                 : {
    1509               0 :   mRequestedIndex = -1;
    1510               0 :   if (aNewIndex < mIndex) {
    1511               0 :     return LoadEntry(aNewIndex - 1, aLoadType, aHistCmd);
    1512                 :   }
    1513               0 :   if (aNewIndex > mIndex) {
    1514               0 :     return LoadEntry(aNewIndex + 1, aLoadType, aHistCmd);
    1515                 :   }
    1516               0 :   return NS_ERROR_FAILURE;
    1517                 : }
    1518                 : 
    1519                 : NS_IMETHODIMP
    1520               0 : nsSHistory::LoadEntry(PRInt32 aIndex, long aLoadType, PRUint32 aHistCmd)
    1521                 : {
    1522               0 :   nsCOMPtr<nsIDocShell> docShell;
    1523                 :   // Keep note of requested history index in mRequestedIndex.
    1524               0 :   mRequestedIndex = aIndex;
    1525                 : 
    1526               0 :   nsCOMPtr<nsISHEntry> prevEntry;
    1527               0 :   GetEntryAtIndex(mIndex, false, getter_AddRefs(prevEntry));
    1528                 : 
    1529               0 :   nsCOMPtr<nsISHEntry> nextEntry;
    1530               0 :   GetEntryAtIndex(mRequestedIndex, false, getter_AddRefs(nextEntry));
    1531               0 :   nsCOMPtr<nsIHistoryEntry> nHEntry(do_QueryInterface(nextEntry));
    1532               0 :   if (!nextEntry || !prevEntry || !nHEntry) {
    1533               0 :     mRequestedIndex = -1;
    1534               0 :     return NS_ERROR_FAILURE;
    1535                 :   }
    1536                 : 
    1537                 :   // Remember that this entry is getting loaded at this point in the sequence
    1538               0 :   nsCOMPtr<nsISHEntryInternal> entryInternal = do_QueryInterface(nextEntry);
    1539                 : 
    1540               0 :   if (entryInternal) {
    1541               0 :     entryInternal->SetLastTouched(++gTouchCounter);
    1542                 :   }
    1543                 : 
    1544                 :   // Send appropriate listener notifications
    1545               0 :   bool canNavigate = true;
    1546                 :   // Get the uri for the entry we are about to visit
    1547               0 :   nsCOMPtr<nsIURI> nextURI;
    1548               0 :   nHEntry->GetURI(getter_AddRefs(nextURI));
    1549                 : 
    1550               0 :   if(mListener) {
    1551               0 :     nsCOMPtr<nsISHistoryListener> listener(do_QueryReferent(mListener));
    1552               0 :     if (listener) {
    1553               0 :       if (aHistCmd == HIST_CMD_BACK) {
    1554                 :         // We are going back one entry. Send GoBack notifications
    1555               0 :         listener->OnHistoryGoBack(nextURI, &canNavigate);
    1556                 :       }
    1557               0 :       else if (aHistCmd == HIST_CMD_FORWARD) {
    1558                 :         // We are going forward. Send GoForward notification
    1559               0 :         listener->OnHistoryGoForward(nextURI, &canNavigate);
    1560                 :       }
    1561               0 :       else if (aHistCmd == HIST_CMD_GOTOINDEX) {
    1562                 :         // We are going somewhere else. This is not reload either
    1563               0 :         listener->OnHistoryGotoIndex(aIndex, nextURI, &canNavigate);
    1564                 :       }
    1565                 :     }
    1566                 :   }
    1567                 : 
    1568               0 :   if (!canNavigate) {
    1569                 :     // If the listener asked us not to proceed with 
    1570                 :     // the operation, simply return.    
    1571               0 :     mRequestedIndex = -1;
    1572               0 :     return NS_OK;  // XXX Maybe I can return some other error code?
    1573                 :   }
    1574                 : 
    1575               0 :   nsCOMPtr<nsIURI> nexturi;
    1576               0 :   PRInt32 pCount=0, nCount=0;
    1577               0 :   nsCOMPtr<nsISHContainer> prevAsContainer(do_QueryInterface(prevEntry));
    1578               0 :   nsCOMPtr<nsISHContainer> nextAsContainer(do_QueryInterface(nextEntry));
    1579               0 :   if (prevAsContainer && nextAsContainer) {
    1580               0 :     prevAsContainer->GetChildCount(&pCount);
    1581               0 :     nextAsContainer->GetChildCount(&nCount);
    1582                 :   }
    1583                 :   
    1584               0 :   nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
    1585               0 :   if (mRequestedIndex == mIndex) {
    1586                 :     // Possibly a reload case 
    1587               0 :     docShell = mRootDocShell;
    1588                 :   }
    1589                 :   else {
    1590                 :     // Going back or forward.
    1591               0 :     if ((pCount > 0) && (nCount > 0)) {
    1592                 :       /* THis is a subframe navigation. Go find 
    1593                 :        * the docshell in which load should happen
    1594                 :        */
    1595               0 :       bool frameFound = false;
    1596               0 :       nsresult rv = CompareFrames(prevEntry, nextEntry, mRootDocShell, aLoadType, &frameFound);
    1597               0 :       if (!frameFound) {
    1598                 :         // We did not successfully find the subframe in which
    1599                 :         // the new url was to be loaded. Go further in the history.
    1600               0 :         return LoadNextPossibleEntry(aIndex, aLoadType, aHistCmd);
    1601                 :       }
    1602               0 :       return rv;
    1603                 :     }   // (pCount >0)
    1604                 :     else {
    1605                 :       // Loading top level page.
    1606               0 :       PRUint32 prevID = 0;
    1607               0 :       PRUint32 nextID = 0;
    1608               0 :       prevEntry->GetID(&prevID);
    1609               0 :       nextEntry->GetID(&nextID);
    1610               0 :       if (prevID == nextID) {
    1611                 :         // Try harder to find something new to load.
    1612                 :         // This may happen for example if some page removed iframes dynamically.
    1613               0 :         return LoadNextPossibleEntry(aIndex, aLoadType, aHistCmd);
    1614                 :       }
    1615               0 :       docShell = mRootDocShell;
    1616                 :     }
    1617                 :   }
    1618                 : 
    1619               0 :   if (!docShell) {
    1620                 :     // we did not successfully go to the proper index.
    1621                 :     // return error.
    1622               0 :     mRequestedIndex = -1;
    1623               0 :     return NS_ERROR_FAILURE;
    1624                 :   }
    1625                 : 
    1626                 :   // Start the load on the appropriate docshell
    1627               0 :   return InitiateLoad(nextEntry, docShell, aLoadType);
    1628                 : }
    1629                 : 
    1630                 : nsresult
    1631               0 : nsSHistory::CompareFrames(nsISHEntry * aPrevEntry, nsISHEntry * aNextEntry, nsIDocShell * aParent, long aLoadType, bool * aIsFrameFound)
    1632                 : {
    1633               0 :   if (!aPrevEntry || !aNextEntry || !aParent)
    1634               0 :     return NS_ERROR_FAILURE;
    1635                 : 
    1636                 :   // We should be comparing only entries which were created for the
    1637                 :   // same docshell. This is here to just prevent anything strange happening.
    1638                 :   // This check could be possibly an assertion.
    1639                 :   PRUint64 prevdID, nextdID;
    1640               0 :   aPrevEntry->GetDocshellID(&prevdID);
    1641               0 :   aNextEntry->GetDocshellID(&nextdID);
    1642               0 :   NS_ENSURE_STATE(prevdID == nextdID);
    1643                 : 
    1644               0 :   nsresult result = NS_OK;
    1645                 :   PRUint32 prevID, nextID;
    1646                 : 
    1647               0 :   aPrevEntry->GetID(&prevID);
    1648               0 :   aNextEntry->GetID(&nextID);
    1649                 :  
    1650                 :   // Check the IDs to verify if the pages are different.
    1651               0 :   if (prevID != nextID) {
    1652               0 :     if (aIsFrameFound)
    1653               0 :       *aIsFrameFound = true;
    1654                 :     // Set the Subframe flag of the entry to indicate that
    1655                 :     // it is subframe navigation        
    1656               0 :     aNextEntry->SetIsSubFrame(true);
    1657               0 :     InitiateLoad(aNextEntry, aParent, aLoadType);
    1658               0 :     return NS_OK;
    1659                 :   }
    1660                 : 
    1661                 :   /* The root entries are the same, so compare any child frames */
    1662               0 :   PRInt32 pcnt=0, ncnt=0, dsCount=0;
    1663               0 :   nsCOMPtr<nsISHContainer>  prevContainer(do_QueryInterface(aPrevEntry));
    1664               0 :   nsCOMPtr<nsISHContainer>  nextContainer(do_QueryInterface(aNextEntry));
    1665               0 :   nsCOMPtr<nsIDocShellTreeNode> dsTreeNode(do_QueryInterface(aParent));
    1666                 : 
    1667               0 :   if (!dsTreeNode)
    1668               0 :     return NS_ERROR_FAILURE;
    1669               0 :   if (!prevContainer || !nextContainer)
    1670               0 :     return NS_ERROR_FAILURE;
    1671                 : 
    1672               0 :   prevContainer->GetChildCount(&pcnt);
    1673               0 :   nextContainer->GetChildCount(&ncnt);
    1674               0 :   dsTreeNode->GetChildCount(&dsCount);
    1675                 : 
    1676                 :   // Create an array for child docshells.
    1677               0 :   nsCOMArray<nsIDocShell> docshells;
    1678               0 :   for (PRInt32 i = 0; i < dsCount; ++i) {
    1679               0 :     nsCOMPtr<nsIDocShellTreeItem> treeItem;
    1680               0 :     dsTreeNode->GetChildAt(i, getter_AddRefs(treeItem));
    1681               0 :     nsCOMPtr<nsIDocShell> shell = do_QueryInterface(treeItem);
    1682               0 :     if (shell) {
    1683               0 :       docshells.AppendObject(shell);
    1684                 :     }
    1685                 :   }
    1686                 : 
    1687                 :   // Search for something to load next.
    1688               0 :   for (PRInt32 i = 0; i < ncnt; ++i) {
    1689                 :     // First get an entry which may cause a new page to be loaded.
    1690               0 :     nsCOMPtr<nsISHEntry> nChild;
    1691               0 :     nextContainer->GetChildAt(i, getter_AddRefs(nChild));
    1692               0 :     if (!nChild) {
    1693               0 :       continue;
    1694                 :     }
    1695               0 :     PRUint64 docshellID = 0;
    1696               0 :     nChild->GetDocshellID(&docshellID);
    1697                 : 
    1698                 :     // Then find the associated docshell.
    1699               0 :     nsIDocShell* dsChild = nsnull;
    1700               0 :     PRInt32 count = docshells.Count();
    1701               0 :     for (PRInt32 j = 0; j < count; ++j) {
    1702               0 :       PRUint64 shellID = 0;
    1703               0 :       nsIDocShell* shell = docshells[j];
    1704               0 :       shell->GetHistoryID(&shellID);
    1705               0 :       if (shellID == docshellID) {
    1706               0 :         dsChild = shell;
    1707               0 :         break;
    1708                 :       }
    1709                 :     }
    1710               0 :     if (!dsChild) {
    1711               0 :       continue;
    1712                 :     }
    1713                 : 
    1714                 :     // Then look at the previous entries to see if there was
    1715                 :     // an entry for the docshell.
    1716               0 :     nsCOMPtr<nsISHEntry> pChild;
    1717               0 :     for (PRInt32 k = 0; k < pcnt; ++k) {
    1718               0 :       nsCOMPtr<nsISHEntry> child;
    1719               0 :       prevContainer->GetChildAt(k, getter_AddRefs(child));
    1720               0 :       if (child) {
    1721               0 :         PRUint64 dID = 0;
    1722               0 :         child->GetDocshellID(&dID);
    1723               0 :         if (dID == docshellID) {
    1724               0 :           pChild = child;
    1725                 :           break;
    1726                 :         }
    1727                 :       }
    1728                 :     }
    1729                 : 
    1730                 :     // Finally recursively call this method.
    1731                 :     // This will either load a new page to shell or some subshell or
    1732                 :     // do nothing.
    1733               0 :     CompareFrames(pChild, nChild, dsChild, aLoadType, aIsFrameFound);
    1734                 :   }     
    1735               0 :   return result;
    1736                 : }
    1737                 : 
    1738                 : 
    1739                 : nsresult 
    1740               0 : nsSHistory::InitiateLoad(nsISHEntry * aFrameEntry, nsIDocShell * aFrameDS, long aLoadType)
    1741                 : {
    1742               0 :   NS_ENSURE_STATE(aFrameDS && aFrameEntry);
    1743                 : 
    1744               0 :   nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
    1745                 : 
    1746                 :   /* Set the loadType in the SHEntry too to  what was passed on.
    1747                 :    * This will be passed on to child subframes later in nsDocShell,
    1748                 :    * so that proper loadType is maintained through out a frameset
    1749                 :    */
    1750               0 :   aFrameEntry->SetLoadType(aLoadType);    
    1751               0 :   aFrameDS->CreateLoadInfo (getter_AddRefs(loadInfo));
    1752                 : 
    1753               0 :   loadInfo->SetLoadType(aLoadType);
    1754               0 :   loadInfo->SetSHEntry(aFrameEntry);
    1755                 : 
    1756               0 :   nsCOMPtr<nsIURI> nextURI;
    1757               0 :   nsCOMPtr<nsIHistoryEntry> hEntry(do_QueryInterface(aFrameEntry));
    1758               0 :   hEntry->GetURI(getter_AddRefs(nextURI));
    1759                 :   // Time   to initiate a document load
    1760               0 :   return aFrameDS->LoadURI(nextURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, false);
    1761                 : 
    1762                 : }
    1763                 : 
    1764                 : 
    1765                 : 
    1766                 : NS_IMETHODIMP
    1767               0 : nsSHistory::SetRootDocShell(nsIDocShell * aDocShell)
    1768                 : {
    1769               0 :   mRootDocShell = aDocShell;
    1770               0 :   return NS_OK;
    1771                 : }
    1772                 : 
    1773                 : NS_IMETHODIMP
    1774               0 : nsSHistory::GetRootDocShell(nsIDocShell ** aDocShell)
    1775                 : {
    1776               0 :   NS_ENSURE_ARG_POINTER(aDocShell);
    1777                 : 
    1778               0 :   *aDocShell = mRootDocShell;
    1779                 :   //Not refcounted. May this method should not be available for public
    1780                 :   // NS_IF_ADDREF(*aDocShell);
    1781               0 :   return NS_OK;
    1782                 : }
    1783                 : 
    1784                 : 
    1785                 : NS_IMETHODIMP
    1786               0 : nsSHistory::GetSHistoryEnumerator(nsISimpleEnumerator** aEnumerator)
    1787                 : {
    1788               0 :   nsresult status = NS_OK;
    1789                 : 
    1790               0 :   NS_ENSURE_ARG_POINTER(aEnumerator);
    1791               0 :   nsSHEnumerator * iterator = new nsSHEnumerator(this);
    1792               0 :   if (iterator && NS_FAILED(status = CallQueryInterface(iterator, aEnumerator)))
    1793               0 :     delete iterator;
    1794               0 :   return status;
    1795                 : }
    1796                 : 
    1797                 : 
    1798                 : //*****************************************************************************
    1799                 : //***    nsSHEnumerator: Object Management
    1800                 : //*****************************************************************************
    1801                 : 
    1802               0 : nsSHEnumerator::nsSHEnumerator(nsSHistory * aSHistory):mIndex(-1)
    1803                 : {
    1804               0 :   mSHistory = aSHistory;
    1805               0 : }
    1806                 : 
    1807               0 : nsSHEnumerator::~nsSHEnumerator()
    1808                 : {
    1809               0 :   mSHistory = nsnull;
    1810               0 : }
    1811                 : 
    1812               0 : NS_IMPL_ISUPPORTS1(nsSHEnumerator, nsISimpleEnumerator)
    1813                 : 
    1814                 : NS_IMETHODIMP
    1815               0 : nsSHEnumerator::HasMoreElements(bool * aReturn)
    1816                 : {
    1817                 :   PRInt32 cnt;
    1818               0 :   *aReturn = false;
    1819               0 :   mSHistory->GetCount(&cnt);
    1820               0 :   if (mIndex >= -1 && mIndex < (cnt-1) ) { 
    1821               0 :     *aReturn = true;
    1822                 :   }
    1823               0 :   return NS_OK;
    1824                 : }
    1825                 : 
    1826                 : 
    1827                 : NS_IMETHODIMP 
    1828               0 : nsSHEnumerator::GetNext(nsISupports **aItem)
    1829                 : {
    1830               0 :   NS_ENSURE_ARG_POINTER(aItem);
    1831               0 :   PRInt32 cnt= 0;
    1832                 : 
    1833               0 :   nsresult  result = NS_ERROR_FAILURE;
    1834               0 :   mSHistory->GetCount(&cnt);
    1835               0 :   if (mIndex < (cnt-1)) {
    1836               0 :     mIndex++;
    1837               0 :     nsCOMPtr<nsIHistoryEntry> hEntry;
    1838               0 :     result = mSHistory->GetEntryAtIndex(mIndex, false, getter_AddRefs(hEntry));
    1839               0 :     if (hEntry)
    1840               0 :       result = CallQueryInterface(hEntry, aItem);
    1841                 :   }
    1842               0 :   return result;
    1843            4392 : }

Generated by: LCOV version 1.7