LCOV - app.info - docshell/shistory/src/nsSHistory.cpp

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-04-21 | 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            1396 : 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            1365 :   nsSHistoryObserver() {}
         150                 :
         151                 : protected:
         152            1365 :   ~nsSHistoryObserver() {}
         153                 : };
         154                 :
         155                 : static nsSHistoryObserver* gObserver = nsnull;
         156                 :
         157           37181 : 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            1367 : 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            1367 :   PRUint64 bytes = PR_GetPhysicalMemorySize();
         276                 :
         277            1367 :   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            1367 :   LL_SHR(kbytes, bytes, 10);
         288                 :
         289                 :   double kBytesD;
         290            1367 :   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            1367 :   PRUint32 viewers = 0;
         296            1367 :   double x = log(kBytesD)/log(2.0) - 14;
         297            1367 :   if (x > 0) {
         298            1367 :     viewers    = (PRUint32)(x * x - x + 2.001); // add .001 for rounding
         299            1367 :     viewers   /= 4;
         300                 :   }
         301                 :
         302                 :   // Cap it off at 8 max
         303            1367 :   if (viewers > 8) {
         304            1367 :     viewers = 8;
         305                 :   }
         306            1367 :   return viewers;
         307                 : }
         308                 :
         309                 : // static
         310                 : void
         311            1367 : nsSHistory::UpdatePrefs()
         312                 : {
         313            1367 :   Preferences::GetInt(PREF_SHISTORY_SIZE, &gHistoryMaxSize);
         314                 :   Preferences::GetInt(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
         315            1367 :                       &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            1367 :   if (sHistoryMaxTotalViewers < 0) {
         319            1367 :     sHistoryMaxTotalViewers = CalcMaxTotalViewers();
         320                 :   }
         321            1367 : }
         322                 :
         323                 : // static
         324                 : nsresult
         325            1365 : nsSHistory::Startup()
         326                 : {
         327            1365 :   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            1365 :     Preferences::GetDefaultInt(PREF_SHISTORY_SIZE, 50);
         333            1365 :   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            1365 :   if (!gObserver) {
         340            1365 :     gObserver = new nsSHistoryObserver();
         341            1365 :     NS_ADDREF(gObserver);
         342            1365 :     Preferences::AddStrongObservers(gObserver, kObservedPrefs);
         343                 :
         344                 :     nsCOMPtr<nsIObserverService> obsSvc =
         345            2730 :       mozilla::services::GetObserverService();
         346            1365 :     if (obsSvc) {
         347                 :       // Observe empty-cache notifications so tahat clearing the disk/memory
         348                 :       // cache will also evict all content viewers.
         349            1365 :       obsSvc->AddObserver(gObserver,
         350            1365 :                           NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID, false);
         351                 :
         352                 :       // Same for memory-pressure notifications
         353            1365 :       obsSvc->AddObserver(gObserver, "memory-pressure", false);
         354                 :     }
         355                 :   }
         356                 :
         357                 :   // Initialize the global list of all SHistory objects
         358            1365 :   PR_INIT_CLIST(&gSHistoryList);
         359            1365 :   return NS_OK;
         360                 : }
         361                 :
         362                 : // static
         363                 : void
         364            1365 : nsSHistory::Shutdown()
         365                 : {
         366            1365 :   if (gObserver) {
         367            1365 :     Preferences::RemoveObservers(gObserver, kObservedPrefs);
         368                 :     nsCOMPtr<nsIObserverService> obsSvc =
         369            2730 :       mozilla::services::GetObserverService();
         370            1365 :     if (obsSvc) {
         371               0 :       obsSvc->RemoveObserver(gObserver, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID);
         372               0 :       obsSvc->RemoveObserver(gObserver, "memory-pressure");
         373                 :     }
         374            1365 :     NS_RELEASE(gObserver);
         375                 :   }
         376            1365 : }
         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            4188 : }

Generated by: LCOV version 1.7