LCOV - code coverage report
Current view: top level - docshell/shistory - nsSHistory.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 202 893 22.6 %
Date: 2017-07-14 16:53:18 Functions: 30 96 31.2 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13