docshell/shistory/src/nsSHistory.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/docshell/shistory/src/nsSHistory.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1872 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +// Local Includes 
    1.11 +#include "nsSHistory.h"
    1.12 +#include <algorithm>
    1.13 +
    1.14 +// Helper Classes
    1.15 +#include "mozilla/Preferences.h"
    1.16 +
    1.17 +// Interfaces Needed
    1.18 +#include "nsILayoutHistoryState.h"
    1.19 +#include "nsIDocShell.h"
    1.20 +#include "nsIDocShellLoadInfo.h"
    1.21 +#include "nsISHContainer.h"
    1.22 +#include "nsIDocShellTreeItem.h"
    1.23 +#include "nsIURI.h"
    1.24 +#include "nsIContentViewer.h"
    1.25 +#include "nsICacheService.h"
    1.26 +#include "nsIObserverService.h"
    1.27 +#include "prclist.h"
    1.28 +#include "mozilla/Services.h"
    1.29 +#include "nsTArray.h"
    1.30 +#include "nsCOMArray.h"
    1.31 +#include "nsDocShell.h"
    1.32 +#include "mozilla/Attributes.h"
    1.33 +#include "nsISHEntry.h"
    1.34 +#include "nsISHTransaction.h"
    1.35 +#include "nsISHistoryListener.h"
    1.36 +#include "nsComponentManagerUtils.h"
    1.37 +
    1.38 +// For calculating max history entries and max cachable contentviewers
    1.39 +#include "prsystem.h"
    1.40 +#include "mozilla/MathAlgorithms.h"
    1.41 +
    1.42 +using namespace mozilla;
    1.43 +
    1.44 +#define PREF_SHISTORY_SIZE "browser.sessionhistory.max_entries"
    1.45 +#define PREF_SHISTORY_MAX_TOTAL_VIEWERS "browser.sessionhistory.max_total_viewers"
    1.46 +
    1.47 +static const char* kObservedPrefs[] = {
    1.48 +  PREF_SHISTORY_SIZE,
    1.49 +  PREF_SHISTORY_MAX_TOTAL_VIEWERS,
    1.50 +  nullptr
    1.51 +};
    1.52 +
    1.53 +static int32_t  gHistoryMaxSize = 50;
    1.54 +// Max viewers allowed per SHistory objects
    1.55 +static const int32_t  gHistoryMaxViewers = 3;
    1.56 +// List of all SHistory objects, used for content viewer cache eviction
    1.57 +static PRCList gSHistoryList;
    1.58 +// Max viewers allowed total, across all SHistory objects - negative default
    1.59 +// means we will calculate how many viewers to cache based on total memory
    1.60 +int32_t nsSHistory::sHistoryMaxTotalViewers = -1;
    1.61 +
    1.62 +// A counter that is used to be able to know the order in which
    1.63 +// entries were touched, so that we can evict older entries first.
    1.64 +static uint32_t gTouchCounter = 0;
    1.65 +
    1.66 +#ifdef PR_LOGGING
    1.67 +
    1.68 +static PRLogModuleInfo*
    1.69 +GetSHistoryLog()
    1.70 +{
    1.71 +  static PRLogModuleInfo *sLog;
    1.72 +  if (!sLog)
    1.73 +    sLog = PR_NewLogModule("nsSHistory");
    1.74 +  return sLog;
    1.75 +}
    1.76 +#define LOG(format) PR_LOG(GetSHistoryLog(), PR_LOG_DEBUG, format)
    1.77 +
    1.78 +// This macro makes it easier to print a log message which includes a URI's
    1.79 +// spec.  Example use:
    1.80 +//
    1.81 +//  nsIURI *uri = [...];
    1.82 +//  LOG_SPEC(("The URI is %s.", _spec), uri);
    1.83 +//
    1.84 +#define LOG_SPEC(format, uri)                              \
    1.85 +  PR_BEGIN_MACRO                                           \
    1.86 +    if (PR_LOG_TEST(GetSHistoryLog(), PR_LOG_DEBUG)) {     \
    1.87 +      nsAutoCString _specStr(NS_LITERAL_CSTRING("(null)"));\
    1.88 +      if (uri) {                                           \
    1.89 +        uri->GetSpec(_specStr);                            \
    1.90 +      }                                                    \
    1.91 +      const char* _spec = _specStr.get();                  \
    1.92 +      LOG(format);                                         \
    1.93 +    }                                                      \
    1.94 +  PR_END_MACRO
    1.95 +
    1.96 +// This macro makes it easy to log a message including an SHEntry's URI.
    1.97 +// For example:
    1.98 +//
    1.99 +//  nsCOMPtr<nsISHEntry> shentry = [...];
   1.100 +//  LOG_SHENTRY_SPEC(("shentry %p has uri %s.", shentry.get(), _spec), shentry);
   1.101 +//
   1.102 +#define LOG_SHENTRY_SPEC(format, shentry)                  \
   1.103 +  PR_BEGIN_MACRO                                           \
   1.104 +    if (PR_LOG_TEST(GetSHistoryLog(), PR_LOG_DEBUG)) {     \
   1.105 +      nsCOMPtr<nsIURI> uri;                                \
   1.106 +      shentry->GetURI(getter_AddRefs(uri));                \
   1.107 +      LOG_SPEC(format, uri);                               \
   1.108 +    }                                                      \
   1.109 +  PR_END_MACRO
   1.110 +
   1.111 +#else // !PR_LOGGING
   1.112 +
   1.113 +#define LOG(format)
   1.114 +#define LOG_SPEC(format, uri)
   1.115 +#define LOG_SHENTRY_SPEC(format, shentry)
   1.116 +
   1.117 +#endif // PR_LOGGING
   1.118 +
   1.119 +// Iterates over all registered session history listeners.
   1.120 +#define ITERATE_LISTENERS(body)                            \
   1.121 +  PR_BEGIN_MACRO                                           \
   1.122 +  {                                                        \
   1.123 +    nsAutoTObserverArray<nsWeakPtr, 2>::EndLimitedIterator \
   1.124 +      iter(mListeners);                                    \
   1.125 +    while (iter.HasMore()) {                               \
   1.126 +      nsCOMPtr<nsISHistoryListener> listener =             \
   1.127 +        do_QueryReferent(iter.GetNext());                  \
   1.128 +      if (listener) {                                      \
   1.129 +        body                                               \
   1.130 +      }                                                    \
   1.131 +    }                                                      \
   1.132 +  }                                                        \
   1.133 +  PR_END_MACRO
   1.134 +
   1.135 +// Calls a given method on all registered session history listeners.
   1.136 +#define NOTIFY_LISTENERS(method, args)                     \
   1.137 +  ITERATE_LISTENERS(                                       \
   1.138 +    listener->method args;                                 \
   1.139 +  );
   1.140 +
   1.141 +// Calls a given method on all registered session history listeners.
   1.142 +// Listeners may return 'false' to cancel an action so make sure that we
   1.143 +// set the return value to 'false' if one of the listeners wants to cancel.
   1.144 +#define NOTIFY_LISTENERS_CANCELABLE(method, retval, args)  \
   1.145 +  PR_BEGIN_MACRO                                           \
   1.146 +  {                                                        \
   1.147 +    bool canceled = false;                                 \
   1.148 +    retval = true;                                         \
   1.149 +    ITERATE_LISTENERS(                                     \
   1.150 +      listener->method args;                               \
   1.151 +      if (!retval) {                                       \
   1.152 +        canceled = true;                                   \
   1.153 +      }                                                    \
   1.154 +    );                                                     \
   1.155 +    if (canceled) {                                        \
   1.156 +      retval = false;                                      \
   1.157 +    }                                                      \
   1.158 +  }                                                        \
   1.159 +  PR_END_MACRO
   1.160 +
   1.161 +enum HistCmd{
   1.162 +  HIST_CMD_BACK,
   1.163 +  HIST_CMD_FORWARD,
   1.164 +  HIST_CMD_GOTOINDEX,
   1.165 +  HIST_CMD_RELOAD
   1.166 +} ;
   1.167 +
   1.168 +//*****************************************************************************
   1.169 +//***      nsSHistoryObserver
   1.170 +//*****************************************************************************
   1.171 +
   1.172 +class nsSHistoryObserver MOZ_FINAL : public nsIObserver
   1.173 +{
   1.174 +
   1.175 +public:
   1.176 +  NS_DECL_ISUPPORTS
   1.177 +  NS_DECL_NSIOBSERVER
   1.178 +
   1.179 +  nsSHistoryObserver() {}
   1.180 +
   1.181 +protected:
   1.182 +  ~nsSHistoryObserver() {}
   1.183 +};
   1.184 +
   1.185 +static nsSHistoryObserver* gObserver = nullptr;
   1.186 +
   1.187 +NS_IMPL_ISUPPORTS(nsSHistoryObserver, nsIObserver)
   1.188 +
   1.189 +NS_IMETHODIMP
   1.190 +nsSHistoryObserver::Observe(nsISupports *aSubject, const char *aTopic,
   1.191 +                            const char16_t *aData)
   1.192 +{
   1.193 +  if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
   1.194 +    nsSHistory::UpdatePrefs();
   1.195 +    nsSHistory::GloballyEvictContentViewers();
   1.196 +  } else if (!strcmp(aTopic, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID) ||
   1.197 +             !strcmp(aTopic, "memory-pressure")) {
   1.198 +    nsSHistory::GloballyEvictAllContentViewers();
   1.199 +  }
   1.200 +
   1.201 +  return NS_OK;
   1.202 +}
   1.203 +
   1.204 +namespace {
   1.205 +
   1.206 +already_AddRefed<nsIContentViewer>
   1.207 +GetContentViewerForTransaction(nsISHTransaction *aTrans)
   1.208 +{
   1.209 +  nsCOMPtr<nsISHEntry> entry;
   1.210 +  aTrans->GetSHEntry(getter_AddRefs(entry));
   1.211 +  if (!entry) {
   1.212 +    return nullptr;
   1.213 +  }
   1.214 +
   1.215 +  nsCOMPtr<nsISHEntry> ownerEntry;
   1.216 +  nsCOMPtr<nsIContentViewer> viewer;
   1.217 +  entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
   1.218 +                             getter_AddRefs(viewer));
   1.219 +  return viewer.forget();
   1.220 +}
   1.221 +
   1.222 +void
   1.223 +EvictContentViewerForTransaction(nsISHTransaction *aTrans)
   1.224 +{
   1.225 +  nsCOMPtr<nsISHEntry> entry;
   1.226 +  aTrans->GetSHEntry(getter_AddRefs(entry));
   1.227 +  nsCOMPtr<nsIContentViewer> viewer;
   1.228 +  nsCOMPtr<nsISHEntry> ownerEntry;
   1.229 +  entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
   1.230 +                             getter_AddRefs(viewer));
   1.231 +  if (viewer) {
   1.232 +    NS_ASSERTION(ownerEntry,
   1.233 +                 "Content viewer exists but its SHEntry is null");
   1.234 +
   1.235 +    LOG_SHENTRY_SPEC(("Evicting content viewer 0x%p for "
   1.236 +                      "owning SHEntry 0x%p at %s.",
   1.237 +                      viewer.get(), ownerEntry.get(), _spec), ownerEntry);
   1.238 +
   1.239 +    // Drop the presentation state before destroying the viewer, so that
   1.240 +    // document teardown is able to correctly persist the state.
   1.241 +    ownerEntry->SetContentViewer(nullptr);
   1.242 +    ownerEntry->SyncPresentationState();
   1.243 +    viewer->Destroy();
   1.244 +  }
   1.245 +}
   1.246 +
   1.247 +} // anonymous namespace
   1.248 +
   1.249 +//*****************************************************************************
   1.250 +//***    nsSHistory: Object Management
   1.251 +//*****************************************************************************
   1.252 +
   1.253 +nsSHistory::nsSHistory() : mListRoot(nullptr), mIndex(-1), mLength(0), mRequestedIndex(-1)
   1.254 +{
   1.255 +  // Add this new SHistory object to the list
   1.256 +  PR_APPEND_LINK(this, &gSHistoryList);
   1.257 +}
   1.258 +
   1.259 +
   1.260 +nsSHistory::~nsSHistory()
   1.261 +{
   1.262 +  // Remove this SHistory object from the list
   1.263 +  PR_REMOVE_LINK(this);
   1.264 +}
   1.265 +
   1.266 +//*****************************************************************************
   1.267 +//    nsSHistory: nsISupports
   1.268 +//*****************************************************************************
   1.269 +
   1.270 +NS_IMPL_ADDREF(nsSHistory)
   1.271 +NS_IMPL_RELEASE(nsSHistory)
   1.272 +
   1.273 +NS_INTERFACE_MAP_BEGIN(nsSHistory)
   1.274 +   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISHistory)
   1.275 +   NS_INTERFACE_MAP_ENTRY(nsISHistory)
   1.276 +   NS_INTERFACE_MAP_ENTRY(nsIWebNavigation)
   1.277 +   NS_INTERFACE_MAP_ENTRY(nsISHistoryInternal)
   1.278 +NS_INTERFACE_MAP_END
   1.279 +
   1.280 +//*****************************************************************************
   1.281 +//    nsSHistory: nsISHistory
   1.282 +//*****************************************************************************
   1.283 +
   1.284 +// static
   1.285 +uint32_t
   1.286 +nsSHistory::CalcMaxTotalViewers()
   1.287 +{
   1.288 +  // Calculate an estimate of how many ContentViewers we should cache based
   1.289 +  // on RAM.  This assumes that the average ContentViewer is 4MB (conservative)
   1.290 +  // and caps the max at 8 ContentViewers
   1.291 +  //
   1.292 +  // TODO: Should we split the cache memory betw. ContentViewer caching and
   1.293 +  // nsCacheService?
   1.294 +  //
   1.295 +  // RAM      ContentViewers
   1.296 +  // -----------------------
   1.297 +  // 32   Mb       0
   1.298 +  // 64   Mb       1
   1.299 +  // 128  Mb       2
   1.300 +  // 256  Mb       3
   1.301 +  // 512  Mb       5
   1.302 +  // 1024 Mb       8
   1.303 +  // 2048 Mb       8
   1.304 +  // 4096 Mb       8
   1.305 +  uint64_t bytes = PR_GetPhysicalMemorySize();
   1.306 +
   1.307 +  if (bytes == 0)
   1.308 +    return 0;
   1.309 +
   1.310 +  // Conversion from unsigned int64_t to double doesn't work on all platforms.
   1.311 +  // We need to truncate the value at INT64_MAX to make sure we don't
   1.312 +  // overflow.
   1.313 +  if (bytes > INT64_MAX)
   1.314 +    bytes = INT64_MAX;
   1.315 +
   1.316 +  double kBytesD = (double)(bytes >> 10);
   1.317 +
   1.318 +  // This is essentially the same calculation as for nsCacheService,
   1.319 +  // except that we divide the final memory calculation by 4, since
   1.320 +  // we assume each ContentViewer takes on average 4MB
   1.321 +  uint32_t viewers = 0;
   1.322 +  double x = std::log(kBytesD)/std::log(2.0) - 14;
   1.323 +  if (x > 0) {
   1.324 +    viewers    = (uint32_t)(x * x - x + 2.001); // add .001 for rounding
   1.325 +    viewers   /= 4;
   1.326 +  }
   1.327 +
   1.328 +  // Cap it off at 8 max
   1.329 +  if (viewers > 8) {
   1.330 +    viewers = 8;
   1.331 +  }
   1.332 +  return viewers;
   1.333 +}
   1.334 +
   1.335 +// static
   1.336 +void
   1.337 +nsSHistory::UpdatePrefs()
   1.338 +{
   1.339 +  Preferences::GetInt(PREF_SHISTORY_SIZE, &gHistoryMaxSize);
   1.340 +  Preferences::GetInt(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
   1.341 +                      &sHistoryMaxTotalViewers);
   1.342 +  // If the pref is negative, that means we calculate how many viewers
   1.343 +  // we think we should cache, based on total memory
   1.344 +  if (sHistoryMaxTotalViewers < 0) {
   1.345 +    sHistoryMaxTotalViewers = CalcMaxTotalViewers();
   1.346 +  }
   1.347 +}
   1.348 +
   1.349 +// static
   1.350 +nsresult
   1.351 +nsSHistory::Startup()
   1.352 +{
   1.353 +  UpdatePrefs();
   1.354 +
   1.355 +  // The goal of this is to unbreak users who have inadvertently set their
   1.356 +  // session history size to less than the default value.
   1.357 +  int32_t defaultHistoryMaxSize =
   1.358 +    Preferences::GetDefaultInt(PREF_SHISTORY_SIZE, 50);
   1.359 +  if (gHistoryMaxSize < defaultHistoryMaxSize) {
   1.360 +    gHistoryMaxSize = defaultHistoryMaxSize;
   1.361 +  }
   1.362 +  
   1.363 +  // Allow the user to override the max total number of cached viewers,
   1.364 +  // but keep the per SHistory cached viewer limit constant
   1.365 +  if (!gObserver) {
   1.366 +    gObserver = new nsSHistoryObserver();
   1.367 +    NS_ADDREF(gObserver);
   1.368 +    Preferences::AddStrongObservers(gObserver, kObservedPrefs);
   1.369 +
   1.370 +    nsCOMPtr<nsIObserverService> obsSvc =
   1.371 +      mozilla::services::GetObserverService();
   1.372 +    if (obsSvc) {
   1.373 +      // Observe empty-cache notifications so tahat clearing the disk/memory
   1.374 +      // cache will also evict all content viewers.
   1.375 +      obsSvc->AddObserver(gObserver,
   1.376 +                          NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID, false);
   1.377 +
   1.378 +      // Same for memory-pressure notifications
   1.379 +      obsSvc->AddObserver(gObserver, "memory-pressure", false);
   1.380 +    }
   1.381 +  }
   1.382 +
   1.383 +  // Initialize the global list of all SHistory objects
   1.384 +  PR_INIT_CLIST(&gSHistoryList);
   1.385 +  return NS_OK;
   1.386 +}
   1.387 +
   1.388 +// static
   1.389 +void
   1.390 +nsSHistory::Shutdown()
   1.391 +{
   1.392 +  if (gObserver) {
   1.393 +    Preferences::RemoveObservers(gObserver, kObservedPrefs);
   1.394 +    nsCOMPtr<nsIObserverService> obsSvc =
   1.395 +      mozilla::services::GetObserverService();
   1.396 +    if (obsSvc) {
   1.397 +      obsSvc->RemoveObserver(gObserver, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID);
   1.398 +      obsSvc->RemoveObserver(gObserver, "memory-pressure");
   1.399 +    }
   1.400 +    NS_RELEASE(gObserver);
   1.401 +  }
   1.402 +}
   1.403 +
   1.404 +/* Add an entry to the History list at mIndex and 
   1.405 + * increment the index to point to the new entry
   1.406 + */
   1.407 +NS_IMETHODIMP
   1.408 +nsSHistory::AddEntry(nsISHEntry * aSHEntry, bool aPersist)
   1.409 +{
   1.410 +  NS_ENSURE_ARG(aSHEntry);
   1.411 +
   1.412 +  nsCOMPtr<nsISHTransaction> currentTxn;
   1.413 +
   1.414 +  if(mListRoot)
   1.415 +    GetTransactionAtIndex(mIndex, getter_AddRefs(currentTxn));
   1.416 +
   1.417 +  bool currentPersist = true;
   1.418 +  if(currentTxn)
   1.419 +    currentTxn->GetPersist(&currentPersist);
   1.420 +
   1.421 +  int32_t currentIndex = mIndex;
   1.422 +
   1.423 +  if(!currentPersist)
   1.424 +  {
   1.425 +    NOTIFY_LISTENERS(OnHistoryReplaceEntry, (currentIndex));
   1.426 +    NS_ENSURE_SUCCESS(currentTxn->SetSHEntry(aSHEntry),NS_ERROR_FAILURE);
   1.427 +    currentTxn->SetPersist(aPersist);
   1.428 +    return NS_OK;
   1.429 +  }
   1.430 +
   1.431 +  nsCOMPtr<nsISHTransaction> txn(do_CreateInstance(NS_SHTRANSACTION_CONTRACTID));
   1.432 +  NS_ENSURE_TRUE(txn, NS_ERROR_FAILURE);
   1.433 +
   1.434 +  nsCOMPtr<nsIURI> uri;
   1.435 +  aSHEntry->GetURI(getter_AddRefs(uri));
   1.436 +  NOTIFY_LISTENERS(OnHistoryNewEntry, (uri));
   1.437 +
   1.438 +  // If a listener has changed mIndex, we need to get currentTxn again,
   1.439 +  // otherwise we'll be left at an inconsistent state (see bug 320742)
   1.440 +  if (currentIndex != mIndex) {
   1.441 +    GetTransactionAtIndex(mIndex, getter_AddRefs(currentTxn));
   1.442 +  }
   1.443 +
   1.444 +  // Set the ShEntry and parent for the transaction. setting the 
   1.445 +  // parent will properly set the parent child relationship
   1.446 +  txn->SetPersist(aPersist);
   1.447 +  NS_ENSURE_SUCCESS(txn->Create(aSHEntry, currentTxn), NS_ERROR_FAILURE);
   1.448 +   
   1.449 +  // A little tricky math here...  Basically when adding an object regardless of
   1.450 +  // what the length was before, it should always be set back to the current and
   1.451 +  // lop off the forward.
   1.452 +  mLength = (++mIndex + 1);
   1.453 +
   1.454 +  // If this is the very first transaction, initialize the list
   1.455 +  if(!mListRoot)
   1.456 +    mListRoot = txn;
   1.457 +
   1.458 +  // Purge History list if it is too long
   1.459 +  if ((gHistoryMaxSize >= 0) && (mLength > gHistoryMaxSize))
   1.460 +    PurgeHistory(mLength-gHistoryMaxSize);
   1.461 +  
   1.462 +  RemoveDynEntries(mIndex - 1, mIndex);
   1.463 +  return NS_OK;
   1.464 +}
   1.465 +
   1.466 +/* Get size of the history list */
   1.467 +NS_IMETHODIMP
   1.468 +nsSHistory::GetCount(int32_t * aResult)
   1.469 +{
   1.470 +  NS_ENSURE_ARG_POINTER(aResult);
   1.471 +  *aResult = mLength;
   1.472 +  return NS_OK;
   1.473 +}
   1.474 +
   1.475 +/* Get index of the history list */
   1.476 +NS_IMETHODIMP
   1.477 +nsSHistory::GetIndex(int32_t * aResult)
   1.478 +{
   1.479 +  NS_PRECONDITION(aResult, "null out param?");
   1.480 +  *aResult = mIndex;
   1.481 +  return NS_OK;
   1.482 +}
   1.483 +
   1.484 +/* Get the requestedIndex */
   1.485 +NS_IMETHODIMP
   1.486 +nsSHistory::GetRequestedIndex(int32_t * aResult)
   1.487 +{
   1.488 +  NS_PRECONDITION(aResult, "null out param?");
   1.489 +  *aResult = mRequestedIndex;
   1.490 +  return NS_OK;
   1.491 +}
   1.492 +
   1.493 +/* Get the entry at a given index */
   1.494 +NS_IMETHODIMP
   1.495 +nsSHistory::GetEntryAtIndex(int32_t aIndex, bool aModifyIndex, nsISHEntry** aResult)
   1.496 +{
   1.497 +  nsresult rv;
   1.498 +  nsCOMPtr<nsISHTransaction> txn;
   1.499 +
   1.500 +  /* GetTransactionAtIndex ensures aResult is valid and validates aIndex */
   1.501 +  rv = GetTransactionAtIndex(aIndex, getter_AddRefs(txn));
   1.502 +  if (NS_SUCCEEDED(rv) && txn) {
   1.503 +    //Get the Entry from the transaction
   1.504 +    rv = txn->GetSHEntry(aResult);
   1.505 +    if (NS_SUCCEEDED(rv) && (*aResult)) {
   1.506 +      // Set mIndex to the requested index, if asked to do so..
   1.507 +      if (aModifyIndex) {
   1.508 +        mIndex = aIndex;
   1.509 +      }
   1.510 +    } //entry
   1.511 +  }  //Transaction
   1.512 +  return rv;
   1.513 +}
   1.514 +
   1.515 +/* Get the transaction at a given index */
   1.516 +NS_IMETHODIMP
   1.517 +nsSHistory::GetTransactionAtIndex(int32_t aIndex, nsISHTransaction ** aResult)
   1.518 +{
   1.519 +  nsresult rv;
   1.520 +  NS_ENSURE_ARG_POINTER(aResult);
   1.521 +
   1.522 +  if ((mLength <= 0) || (aIndex < 0) || (aIndex >= mLength))
   1.523 +    return NS_ERROR_FAILURE;
   1.524 +
   1.525 +  if (!mListRoot) 
   1.526 +    return NS_ERROR_FAILURE;
   1.527 +
   1.528 +  if (aIndex == 0)
   1.529 +  {
   1.530 +    *aResult = mListRoot;
   1.531 +    NS_ADDREF(*aResult);
   1.532 +    return NS_OK;
   1.533 +  } 
   1.534 +  int32_t   cnt=0;
   1.535 +  nsCOMPtr<nsISHTransaction>  tempPtr;
   1.536 +
   1.537 +  rv = GetRootTransaction(getter_AddRefs(tempPtr));
   1.538 +  if (NS_FAILED(rv) || !tempPtr)
   1.539 +    return NS_ERROR_FAILURE;
   1.540 +
   1.541 +  while(1) {
   1.542 +    nsCOMPtr<nsISHTransaction> ptr;
   1.543 +    rv = tempPtr->GetNext(getter_AddRefs(ptr));
   1.544 +    if (NS_SUCCEEDED(rv) && ptr) {
   1.545 +      cnt++;
   1.546 +      if (cnt == aIndex) {
   1.547 +        *aResult = ptr;
   1.548 +        NS_ADDREF(*aResult);
   1.549 +        break;
   1.550 +      }
   1.551 +      else {
   1.552 +        tempPtr = ptr;
   1.553 +        continue;
   1.554 +      }
   1.555 +    }  //NS_SUCCEEDED
   1.556 +    else 
   1.557 +      return NS_ERROR_FAILURE;
   1.558 +  }  // while 
   1.559 +  
   1.560 +  return NS_OK;
   1.561 +}
   1.562 +
   1.563 +
   1.564 +/* Get the index of a given entry */
   1.565 +NS_IMETHODIMP
   1.566 +nsSHistory::GetIndexOfEntry(nsISHEntry* aSHEntry, int32_t* aResult) {
   1.567 +  NS_ENSURE_ARG(aSHEntry);
   1.568 +  NS_ENSURE_ARG_POINTER(aResult);
   1.569 +  *aResult = -1;
   1.570 +
   1.571 +  if (mLength <= 0) {
   1.572 +    return NS_ERROR_FAILURE;
   1.573 +  }
   1.574 +
   1.575 +  nsCOMPtr<nsISHTransaction> currentTxn;
   1.576 +  int32_t cnt = 0;
   1.577 +
   1.578 +  nsresult rv = GetRootTransaction(getter_AddRefs(currentTxn));
   1.579 +  if (NS_FAILED(rv) || !currentTxn) {
   1.580 +    return NS_ERROR_FAILURE;
   1.581 +  }
   1.582 +
   1.583 +  while (true) {
   1.584 +    nsCOMPtr<nsISHEntry> entry;
   1.585 +    rv = currentTxn->GetSHEntry(getter_AddRefs(entry));
   1.586 +    if (NS_FAILED(rv) || !entry) {
   1.587 +      return NS_ERROR_FAILURE;
   1.588 +    }
   1.589 +
   1.590 +    if (aSHEntry == entry) {
   1.591 +      *aResult = cnt;
   1.592 +      break;
   1.593 +    }
   1.594 +
   1.595 +    rv = currentTxn->GetNext(getter_AddRefs(currentTxn));
   1.596 +    if (NS_FAILED(rv) || !currentTxn) {
   1.597 +      return NS_ERROR_FAILURE;
   1.598 +    }
   1.599 +
   1.600 +    cnt++;
   1.601 +  }
   1.602 +
   1.603 +  return NS_OK;
   1.604 +}
   1.605 +
   1.606 +
   1.607 +#ifdef DEBUG
   1.608 +nsresult
   1.609 +nsSHistory::PrintHistory()
   1.610 +{
   1.611 +
   1.612 +  nsCOMPtr<nsISHTransaction>   txn;
   1.613 +  int32_t index = 0;
   1.614 +  nsresult rv;
   1.615 +
   1.616 +  if (!mListRoot) 
   1.617 +    return NS_ERROR_FAILURE;
   1.618 +
   1.619 +  txn = mListRoot;
   1.620 +    
   1.621 +  while (1) {
   1.622 +    if (!txn)
   1.623 +      break;
   1.624 +    nsCOMPtr<nsISHEntry>  entry;
   1.625 +    rv = txn->GetSHEntry(getter_AddRefs(entry));
   1.626 +    if (NS_FAILED(rv) && !entry)
   1.627 +      return NS_ERROR_FAILURE;
   1.628 +
   1.629 +    nsCOMPtr<nsILayoutHistoryState> layoutHistoryState;
   1.630 +    nsCOMPtr<nsIURI>  uri;
   1.631 +    nsXPIDLString title;
   1.632 +
   1.633 +    entry->GetLayoutHistoryState(getter_AddRefs(layoutHistoryState));
   1.634 +    entry->GetURI(getter_AddRefs(uri));
   1.635 +    entry->GetTitle(getter_Copies(title));
   1.636 +
   1.637 +#if 0
   1.638 +    nsAutoCString url;
   1.639 +    if (uri)
   1.640 +     uri->GetSpec(url);
   1.641 +
   1.642 +    printf("**** SH Transaction #%d, Entry = %x\n", index, entry.get());
   1.643 +    printf("\t\t URL = %s\n", url.get());
   1.644 +
   1.645 +    printf("\t\t Title = %s\n", NS_LossyConvertUTF16toASCII(title).get());
   1.646 +    printf("\t\t layout History Data = %x\n", layoutHistoryState.get());
   1.647 +#endif
   1.648 +
   1.649 +    nsCOMPtr<nsISHTransaction> next;
   1.650 +    rv = txn->GetNext(getter_AddRefs(next));
   1.651 +    if (NS_SUCCEEDED(rv) && next) {
   1.652 +      txn = next;
   1.653 +      index++;
   1.654 +      continue;
   1.655 +    }
   1.656 +    else
   1.657 +      break;
   1.658 +  }
   1.659 +
   1.660 +  return NS_OK;
   1.661 +}
   1.662 +#endif
   1.663 +
   1.664 +
   1.665 +NS_IMETHODIMP
   1.666 +nsSHistory::GetRootTransaction(nsISHTransaction ** aResult)
   1.667 +{
   1.668 +  NS_ENSURE_ARG_POINTER(aResult);
   1.669 +  *aResult=mListRoot;
   1.670 +  NS_IF_ADDREF(*aResult);
   1.671 +  return NS_OK;
   1.672 +}
   1.673 +
   1.674 +/* Get the max size of the history list */
   1.675 +NS_IMETHODIMP
   1.676 +nsSHistory::GetMaxLength(int32_t * aResult)
   1.677 +{
   1.678 +  NS_ENSURE_ARG_POINTER(aResult);
   1.679 +  *aResult = gHistoryMaxSize;
   1.680 +  return NS_OK;
   1.681 +}
   1.682 +
   1.683 +/* Set the max size of the history list */
   1.684 +NS_IMETHODIMP
   1.685 +nsSHistory::SetMaxLength(int32_t aMaxSize)
   1.686 +{
   1.687 +  if (aMaxSize < 0)
   1.688 +    return NS_ERROR_ILLEGAL_VALUE;
   1.689 +
   1.690 +  gHistoryMaxSize = aMaxSize;
   1.691 +  if (mLength > aMaxSize)
   1.692 +    PurgeHistory(mLength-aMaxSize);
   1.693 +  return NS_OK;
   1.694 +}
   1.695 +
   1.696 +NS_IMETHODIMP
   1.697 +nsSHistory::PurgeHistory(int32_t aEntries)
   1.698 +{
   1.699 +  if (mLength <= 0 || aEntries <= 0)
   1.700 +    return NS_ERROR_FAILURE;
   1.701 +
   1.702 +  aEntries = std::min(aEntries, mLength);
   1.703 +  
   1.704 +  bool purgeHistory = true;
   1.705 +  NOTIFY_LISTENERS_CANCELABLE(OnHistoryPurge, purgeHistory,
   1.706 +                              (aEntries, &purgeHistory));
   1.707 +
   1.708 +  if (!purgeHistory) {
   1.709 +    // Listener asked us not to purge
   1.710 +    return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA;
   1.711 +  }
   1.712 +
   1.713 +  int32_t cnt = 0;
   1.714 +  while (cnt < aEntries) {
   1.715 +    nsCOMPtr<nsISHTransaction> nextTxn;
   1.716 +    if (mListRoot) {
   1.717 +      mListRoot->GetNext(getter_AddRefs(nextTxn));
   1.718 +      mListRoot->SetNext(nullptr);
   1.719 +    }
   1.720 +    mListRoot = nextTxn;
   1.721 +    if (mListRoot) {
   1.722 +      mListRoot->SetPrev(nullptr);
   1.723 +    }
   1.724 +    cnt++;        
   1.725 +  }
   1.726 +  mLength -= cnt;
   1.727 +  mIndex -= cnt;
   1.728 +
   1.729 +  // Now if we were not at the end of the history, mIndex could have
   1.730 +  // become far too negative.  If so, just set it to -1.
   1.731 +  if (mIndex < -1) {
   1.732 +    mIndex = -1;
   1.733 +  }
   1.734 +
   1.735 +  if (mRootDocShell)
   1.736 +    mRootDocShell->HistoryPurged(cnt);
   1.737 +
   1.738 +  return NS_OK;
   1.739 +}
   1.740 +
   1.741 +
   1.742 +NS_IMETHODIMP
   1.743 +nsSHistory::AddSHistoryListener(nsISHistoryListener * aListener)
   1.744 +{
   1.745 +  NS_ENSURE_ARG_POINTER(aListener);
   1.746 +
   1.747 +  // Check if the listener supports Weak Reference. This is a must.
   1.748 +  // This listener functionality is used by embedders and we want to 
   1.749 +  // have the right ownership with who ever listens to SHistory
   1.750 +  nsWeakPtr listener = do_GetWeakReference(aListener);
   1.751 +  if (!listener) return NS_ERROR_FAILURE;
   1.752 +
   1.753 +  return mListeners.AppendElementUnlessExists(listener) ?
   1.754 +    NS_OK : NS_ERROR_OUT_OF_MEMORY;
   1.755 +}
   1.756 +
   1.757 +
   1.758 +NS_IMETHODIMP
   1.759 +nsSHistory::RemoveSHistoryListener(nsISHistoryListener * aListener)
   1.760 +{
   1.761 +  // Make sure the listener that wants to be removed is the
   1.762 +  // one we have in store. 
   1.763 +  nsWeakPtr listener = do_GetWeakReference(aListener);
   1.764 +  mListeners.RemoveElement(listener);
   1.765 +  return NS_OK;
   1.766 +}
   1.767 +
   1.768 +
   1.769 +/* Replace an entry in the History list at a particular index.
   1.770 + * Do not update index or count.
   1.771 + */
   1.772 +NS_IMETHODIMP
   1.773 +nsSHistory::ReplaceEntry(int32_t aIndex, nsISHEntry * aReplaceEntry)
   1.774 +{
   1.775 +  NS_ENSURE_ARG(aReplaceEntry);
   1.776 +  nsresult rv;
   1.777 +  nsCOMPtr<nsISHTransaction> currentTxn;
   1.778 +
   1.779 +  if (!mListRoot) // Session History is not initialised.
   1.780 +    return NS_ERROR_FAILURE;
   1.781 +
   1.782 +  rv = GetTransactionAtIndex(aIndex, getter_AddRefs(currentTxn));
   1.783 +
   1.784 +  if(currentTxn)
   1.785 +  {
   1.786 +    NOTIFY_LISTENERS(OnHistoryReplaceEntry, (aIndex));
   1.787 +
   1.788 +    // Set the replacement entry in the transaction
   1.789 +    rv = currentTxn->SetSHEntry(aReplaceEntry);
   1.790 +    rv = currentTxn->SetPersist(true);
   1.791 +  }
   1.792 +  return rv;
   1.793 +}
   1.794 +
   1.795 +NS_IMETHODIMP
   1.796 +nsSHistory::NotifyOnHistoryReload(nsIURI* aReloadURI, uint32_t aReloadFlags,
   1.797 +                                  bool* aCanReload)
   1.798 +{
   1.799 +  NOTIFY_LISTENERS_CANCELABLE(OnHistoryReload, *aCanReload,
   1.800 +                              (aReloadURI, aReloadFlags, aCanReload));
   1.801 +  return NS_OK;
   1.802 +}
   1.803 +
   1.804 +NS_IMETHODIMP
   1.805 +nsSHistory::EvictOutOfRangeContentViewers(int32_t aIndex)
   1.806 +{
   1.807 +  // Check our per SHistory object limit in the currently navigated SHistory
   1.808 +  EvictOutOfRangeWindowContentViewers(aIndex);
   1.809 +  // Check our total limit across all SHistory objects
   1.810 +  GloballyEvictContentViewers();
   1.811 +  return NS_OK;
   1.812 +}
   1.813 +
   1.814 +NS_IMETHODIMP
   1.815 +nsSHistory::EvictAllContentViewers()
   1.816 +{
   1.817 +  // XXXbz we don't actually do a good job of evicting things as we should, so
   1.818 +  // we might have viewers quite far from mIndex.  So just evict everything.
   1.819 +  nsCOMPtr<nsISHTransaction> trans = mListRoot;
   1.820 +  while (trans) {
   1.821 +    EvictContentViewerForTransaction(trans);
   1.822 +
   1.823 +    nsISHTransaction *temp = trans;
   1.824 +    temp->GetNext(getter_AddRefs(trans));
   1.825 +  }
   1.826 +
   1.827 +  return NS_OK;
   1.828 +}
   1.829 +
   1.830 +
   1.831 +
   1.832 +//*****************************************************************************
   1.833 +//    nsSHistory: nsIWebNavigation
   1.834 +//*****************************************************************************
   1.835 +
   1.836 +NS_IMETHODIMP
   1.837 +nsSHistory::GetCanGoBack(bool * aCanGoBack)
   1.838 +{
   1.839 +  NS_ENSURE_ARG_POINTER(aCanGoBack);
   1.840 +  *aCanGoBack = false;
   1.841 +
   1.842 +  int32_t index = -1;
   1.843 +  NS_ENSURE_SUCCESS(GetIndex(&index), NS_ERROR_FAILURE);
   1.844 +  if(index > 0)
   1.845 +     *aCanGoBack = true;
   1.846 +
   1.847 +  return NS_OK;
   1.848 +}
   1.849 +
   1.850 +NS_IMETHODIMP
   1.851 +nsSHistory::GetCanGoForward(bool * aCanGoForward)
   1.852 +{
   1.853 +  NS_ENSURE_ARG_POINTER(aCanGoForward);
   1.854 +  *aCanGoForward = false;
   1.855 +
   1.856 +  int32_t index = -1;
   1.857 +  int32_t count = -1;
   1.858 +
   1.859 +  NS_ENSURE_SUCCESS(GetIndex(&index), NS_ERROR_FAILURE);
   1.860 +  NS_ENSURE_SUCCESS(GetCount(&count), NS_ERROR_FAILURE);
   1.861 +
   1.862 +  if((index >= 0) && (index < (count - 1)))
   1.863 +    *aCanGoForward = true;
   1.864 +
   1.865 +  return NS_OK;
   1.866 +}
   1.867 +
   1.868 +NS_IMETHODIMP
   1.869 +nsSHistory::GoBack()
   1.870 +{
   1.871 +  bool canGoBack = false;
   1.872 +
   1.873 +  GetCanGoBack(&canGoBack);
   1.874 +  if (!canGoBack)  // Can't go back
   1.875 +    return NS_ERROR_UNEXPECTED;
   1.876 +  return LoadEntry(mIndex-1, nsIDocShellLoadInfo::loadHistory, HIST_CMD_BACK);
   1.877 +}
   1.878 +
   1.879 +
   1.880 +NS_IMETHODIMP
   1.881 +nsSHistory::GoForward()
   1.882 +{
   1.883 +  bool canGoForward = false;
   1.884 +
   1.885 +  GetCanGoForward(&canGoForward);
   1.886 +  if (!canGoForward)  // Can't go forward
   1.887 +    return NS_ERROR_UNEXPECTED;
   1.888 +  return LoadEntry(mIndex+1, nsIDocShellLoadInfo::loadHistory, HIST_CMD_FORWARD);
   1.889 +}
   1.890 +
   1.891 +NS_IMETHODIMP
   1.892 +nsSHistory::Reload(uint32_t aReloadFlags)
   1.893 +{
   1.894 +  nsDocShellInfoLoadType loadType;
   1.895 +  if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY && 
   1.896 +      aReloadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)
   1.897 +  {
   1.898 +    loadType = nsIDocShellLoadInfo::loadReloadBypassProxyAndCache;
   1.899 +  }
   1.900 +  else if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY)
   1.901 +  {
   1.902 +    loadType = nsIDocShellLoadInfo::loadReloadBypassProxy;
   1.903 +  }
   1.904 +  else if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE)
   1.905 +  {
   1.906 +    loadType = nsIDocShellLoadInfo::loadReloadBypassCache;
   1.907 +  }
   1.908 +  else if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_CHARSET_CHANGE)
   1.909 +  {
   1.910 +    loadType = nsIDocShellLoadInfo::loadReloadCharsetChange;
   1.911 +  }
   1.912 +  else if (aReloadFlags & nsIWebNavigation::LOAD_FLAGS_ALLOW_MIXED_CONTENT)
   1.913 +  {
   1.914 +    loadType = nsIDocShellLoadInfo::loadReloadMixedContent;
   1.915 +  }
   1.916 +  else
   1.917 +  {
   1.918 +    loadType = nsIDocShellLoadInfo::loadReloadNormal;
   1.919 +  }
   1.920 +
   1.921 +  // We are reloading. Send Reload notifications.
   1.922 +  // nsDocShellLoadFlagType is not public, where as nsIWebNavigation
   1.923 +  // is public. So send the reload notifications with the
   1.924 +  // nsIWebNavigation flags.
   1.925 +  bool canNavigate = true;
   1.926 +  nsCOMPtr<nsIURI> currentURI;
   1.927 +  GetCurrentURI(getter_AddRefs(currentURI));
   1.928 +  NOTIFY_LISTENERS_CANCELABLE(OnHistoryReload, canNavigate,
   1.929 +                              (currentURI, aReloadFlags, &canNavigate));
   1.930 +  if (!canNavigate)
   1.931 +    return NS_OK;
   1.932 +
   1.933 +  return LoadEntry(mIndex, loadType, HIST_CMD_RELOAD);
   1.934 +}
   1.935 +
   1.936 +NS_IMETHODIMP
   1.937 +nsSHistory::ReloadCurrentEntry()
   1.938 +{
   1.939 +  // Notify listeners
   1.940 +  bool canNavigate = true;
   1.941 +  nsCOMPtr<nsIURI> currentURI;
   1.942 +  GetCurrentURI(getter_AddRefs(currentURI));
   1.943 +  NOTIFY_LISTENERS_CANCELABLE(OnHistoryGotoIndex, canNavigate,
   1.944 +                              (mIndex, currentURI, &canNavigate));
   1.945 +  if (!canNavigate)
   1.946 +    return NS_OK;
   1.947 +
   1.948 +  return LoadEntry(mIndex, nsIDocShellLoadInfo::loadHistory, HIST_CMD_RELOAD);
   1.949 +}
   1.950 +
   1.951 +void
   1.952 +nsSHistory::EvictOutOfRangeWindowContentViewers(int32_t aIndex)
   1.953 +{
   1.954 +  // XXX rename method to EvictContentViewersExceptAroundIndex, or something.
   1.955 +
   1.956 +  // We need to release all content viewers that are no longer in the range
   1.957 +  //
   1.958 +  //  aIndex - gHistoryMaxViewers to aIndex + gHistoryMaxViewers
   1.959 +  //
   1.960 +  // to ensure that this SHistory object isn't responsible for more than
   1.961 +  // gHistoryMaxViewers content viewers.  But our job is complicated by the
   1.962 +  // fact that two transactions which are related by either hash navigations or
   1.963 +  // history.pushState will have the same content viewer.
   1.964 +  //
   1.965 +  // To illustrate the issue, suppose gHistoryMaxViewers = 3 and we have four
   1.966 +  // linked transactions in our history.  Suppose we then add a new content
   1.967 +  // viewer and call into this function.  So the history looks like:
   1.968 +  //
   1.969 +  //   A A A A B
   1.970 +  //     +     *
   1.971 +  //
   1.972 +  // where the letters are content viewers and + and * denote the beginning and
   1.973 +  // end of the range aIndex +/- gHistoryMaxViewers.
   1.974 +  //
   1.975 +  // Although one copy of the content viewer A exists outside the range, we
   1.976 +  // don't want to evict A, because it has other copies in range!
   1.977 +  //
   1.978 +  // We therefore adjust our eviction strategy to read:
   1.979 +  //
   1.980 +  //   Evict each content viewer outside the range aIndex -/+
   1.981 +  //   gHistoryMaxViewers, unless that content viewer also appears within the
   1.982 +  //   range.
   1.983 +  //
   1.984 +  // (Note that it's entirely legal to have two copies of one content viewer
   1.985 +  // separated by a different content viewer -- call pushState twice, go back
   1.986 +  // once, and refresh -- so we can't rely on identical viewers only appearing
   1.987 +  // adjacent to one another.)
   1.988 +
   1.989 +  if (aIndex < 0) {
   1.990 +    return;
   1.991 +  }
   1.992 +  NS_ENSURE_TRUE_VOID(aIndex < mLength);
   1.993 +
   1.994 +  // Calculate the range that's safe from eviction.
   1.995 +  int32_t startSafeIndex = std::max(0, aIndex - gHistoryMaxViewers);
   1.996 +  int32_t endSafeIndex = std::min(mLength, aIndex + gHistoryMaxViewers);
   1.997 +
   1.998 +  LOG(("EvictOutOfRangeWindowContentViewers(index=%d), "
   1.999 +       "mLength=%d. Safe range [%d, %d]",
  1.1000 +       aIndex, mLength, startSafeIndex, endSafeIndex)); 
  1.1001 +
  1.1002 +  // The content viewers in range aIndex -/+ gHistoryMaxViewers will not be
  1.1003 +  // evicted.  Collect a set of them so we don't accidentally evict one of them
  1.1004 +  // if it appears outside this range.
  1.1005 +  nsCOMArray<nsIContentViewer> safeViewers;
  1.1006 +  nsCOMPtr<nsISHTransaction> trans;
  1.1007 +  GetTransactionAtIndex(startSafeIndex, getter_AddRefs(trans));
  1.1008 +  for (int32_t i = startSafeIndex; trans && i <= endSafeIndex; i++) {
  1.1009 +    nsCOMPtr<nsIContentViewer> viewer = GetContentViewerForTransaction(trans);
  1.1010 +    safeViewers.AppendObject(viewer);
  1.1011 +    nsISHTransaction *temp = trans;
  1.1012 +    temp->GetNext(getter_AddRefs(trans));
  1.1013 +  }
  1.1014 +
  1.1015 +  // Walk the SHistory list and evict any content viewers that aren't safe.
  1.1016 +  GetTransactionAtIndex(0, getter_AddRefs(trans));
  1.1017 +  while (trans) {
  1.1018 +    nsCOMPtr<nsIContentViewer> viewer = GetContentViewerForTransaction(trans);
  1.1019 +    if (safeViewers.IndexOf(viewer) == -1) {
  1.1020 +      EvictContentViewerForTransaction(trans);
  1.1021 +    }
  1.1022 +
  1.1023 +    nsISHTransaction *temp = trans;
  1.1024 +    temp->GetNext(getter_AddRefs(trans));
  1.1025 +  }
  1.1026 +}
  1.1027 +
  1.1028 +namespace {
  1.1029 +
  1.1030 +class TransactionAndDistance
  1.1031 +{
  1.1032 +public:
  1.1033 +  TransactionAndDistance(nsISHTransaction *aTrans, uint32_t aDist)
  1.1034 +    : mTransaction(aTrans)
  1.1035 +    , mDistance(aDist)
  1.1036 +  {
  1.1037 +    mViewer = GetContentViewerForTransaction(aTrans);
  1.1038 +    NS_ASSERTION(mViewer, "Transaction should have a content viewer");
  1.1039 +
  1.1040 +    nsCOMPtr<nsISHEntry> shentry;
  1.1041 +    mTransaction->GetSHEntry(getter_AddRefs(shentry));
  1.1042 +
  1.1043 +    nsCOMPtr<nsISHEntryInternal> shentryInternal = do_QueryInterface(shentry);
  1.1044 +    if (shentryInternal) {
  1.1045 +      shentryInternal->GetLastTouched(&mLastTouched);
  1.1046 +    } else {
  1.1047 +      NS_WARNING("Can't cast to nsISHEntryInternal?");
  1.1048 +      mLastTouched = 0;
  1.1049 +    }
  1.1050 +  }
  1.1051 +
  1.1052 +  bool operator<(const TransactionAndDistance &aOther) const
  1.1053 +  {
  1.1054 +    // Compare distances first, and fall back to last-accessed times.
  1.1055 +    if (aOther.mDistance != this->mDistance) {
  1.1056 +      return this->mDistance < aOther.mDistance;
  1.1057 +    }
  1.1058 +
  1.1059 +    return this->mLastTouched < aOther.mLastTouched;
  1.1060 +  }
  1.1061 +
  1.1062 +  bool operator==(const TransactionAndDistance &aOther) const
  1.1063 +  {
  1.1064 +    // This is a little silly; we need == so the default comaprator can be
  1.1065 +    // instantiated, but this function is never actually called when we sort
  1.1066 +    // the list of TransactionAndDistance objects.
  1.1067 +    return aOther.mDistance == this->mDistance &&
  1.1068 +           aOther.mLastTouched == this->mLastTouched;
  1.1069 +  }
  1.1070 +
  1.1071 +  nsCOMPtr<nsISHTransaction> mTransaction;
  1.1072 +  nsCOMPtr<nsIContentViewer> mViewer;
  1.1073 +  uint32_t mLastTouched;
  1.1074 +  int32_t mDistance;
  1.1075 +};
  1.1076 +
  1.1077 +} // anonymous namespace
  1.1078 +
  1.1079 +//static
  1.1080 +void
  1.1081 +nsSHistory::GloballyEvictContentViewers()
  1.1082 +{
  1.1083 +  // First, collect from each SHistory object the transactions which have a
  1.1084 +  // cached content viewer.  Associate with each transaction its distance from
  1.1085 +  // its SHistory's current index.
  1.1086 +
  1.1087 +  nsTArray<TransactionAndDistance> transactions;
  1.1088 +
  1.1089 +  nsSHistory *shist = static_cast<nsSHistory*>(PR_LIST_HEAD(&gSHistoryList));
  1.1090 +  while (shist != &gSHistoryList) {
  1.1091 +
  1.1092 +    // Maintain a list of the transactions which have viewers and belong to
  1.1093 +    // this particular shist object.  We'll add this list to the global list,
  1.1094 +    // |transactions|, eventually.
  1.1095 +    nsTArray<TransactionAndDistance> shTransactions;
  1.1096 +
  1.1097 +    // Content viewers are likely to exist only within shist->mIndex -/+
  1.1098 +    // gHistoryMaxViewers, so only search within that range.
  1.1099 +    //
  1.1100 +    // A content viewer might exist outside that range due to either:
  1.1101 +    //
  1.1102 +    //   * history.pushState or hash navigations, in which case a copy of the
  1.1103 +    //     content viewer should exist within the range, or
  1.1104 +    //
  1.1105 +    //   * bugs which cause us not to call nsSHistory::EvictContentViewers()
  1.1106 +    //     often enough.  Once we do call EvictContentViewers() for the
  1.1107 +    //     SHistory object in question, we'll do a full search of its history
  1.1108 +    //     and evict the out-of-range content viewers, so we don't bother here.
  1.1109 +    //
  1.1110 +    int32_t startIndex = std::max(0, shist->mIndex - gHistoryMaxViewers);
  1.1111 +    int32_t endIndex = std::min(shist->mLength - 1,
  1.1112 +                              shist->mIndex + gHistoryMaxViewers);
  1.1113 +    nsCOMPtr<nsISHTransaction> trans;
  1.1114 +    shist->GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
  1.1115 +    for (int32_t i = startIndex; trans && i <= endIndex; i++) {
  1.1116 +      nsCOMPtr<nsIContentViewer> contentViewer =
  1.1117 +        GetContentViewerForTransaction(trans);
  1.1118 +
  1.1119 +      if (contentViewer) {
  1.1120 +        // Because one content viewer might belong to multiple SHEntries, we
  1.1121 +        // have to search through shTransactions to see if we already know
  1.1122 +        // about this content viewer.  If we find the viewer, update its
  1.1123 +        // distance from the SHistory's index and continue.
  1.1124 +        bool found = false;
  1.1125 +        for (uint32_t j = 0; j < shTransactions.Length(); j++) {
  1.1126 +          TransactionAndDistance &container = shTransactions[j];
  1.1127 +          if (container.mViewer == contentViewer) {
  1.1128 +            container.mDistance = std::min(container.mDistance, DeprecatedAbs(i - shist->mIndex));
  1.1129 +            found = true;
  1.1130 +            break;
  1.1131 +          }
  1.1132 +        }
  1.1133 +
  1.1134 +        // If we didn't find a TransactionAndDistance for this content viewer, make a new
  1.1135 +        // one.
  1.1136 +        if (!found) {
  1.1137 +          TransactionAndDistance container(trans, DeprecatedAbs(i - shist->mIndex));
  1.1138 +          shTransactions.AppendElement(container);
  1.1139 +        }
  1.1140 +      }
  1.1141 +
  1.1142 +      nsISHTransaction *temp = trans;
  1.1143 +      temp->GetNext(getter_AddRefs(trans));
  1.1144 +    }
  1.1145 +
  1.1146 +    // We've found all the transactions belonging to shist which have viewers.
  1.1147 +    // Add those transactions to our global list and move on.
  1.1148 +    transactions.AppendElements(shTransactions);
  1.1149 +    shist = static_cast<nsSHistory*>(PR_NEXT_LINK(shist));
  1.1150 +  }
  1.1151 +
  1.1152 +  // We now have collected all cached content viewers.  First check that we
  1.1153 +  // have enough that we actually need to evict some.
  1.1154 +  if ((int32_t)transactions.Length() <= sHistoryMaxTotalViewers) {
  1.1155 +    return;
  1.1156 +  }
  1.1157 +
  1.1158 +  // If we need to evict, sort our list of transactions and evict the largest
  1.1159 +  // ones.  (We could of course get better algorithmic complexity here by using
  1.1160 +  // a heap or something more clever.  But sHistoryMaxTotalViewers isn't large,
  1.1161 +  // so let's not worry about it.)
  1.1162 +  transactions.Sort();
  1.1163 +
  1.1164 +  for (int32_t i = transactions.Length() - 1;
  1.1165 +       i >= sHistoryMaxTotalViewers; --i) {
  1.1166 +
  1.1167 +    EvictContentViewerForTransaction(transactions[i].mTransaction);
  1.1168 +
  1.1169 +  }
  1.1170 +}
  1.1171 +
  1.1172 +nsresult
  1.1173 +nsSHistory::EvictExpiredContentViewerForEntry(nsIBFCacheEntry *aEntry)
  1.1174 +{
  1.1175 +  int32_t startIndex = std::max(0, mIndex - gHistoryMaxViewers);
  1.1176 +  int32_t endIndex = std::min(mLength - 1,
  1.1177 +                            mIndex + gHistoryMaxViewers);
  1.1178 +  nsCOMPtr<nsISHTransaction> trans;
  1.1179 +  GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
  1.1180 +
  1.1181 +  int32_t i;
  1.1182 +  for (i = startIndex; trans && i <= endIndex; ++i) {
  1.1183 +    nsCOMPtr<nsISHEntry> entry;
  1.1184 +    trans->GetSHEntry(getter_AddRefs(entry));
  1.1185 +
  1.1186 +    // Does entry have the same BFCacheEntry as the argument to this method?
  1.1187 +    if (entry->HasBFCacheEntry(aEntry)) {
  1.1188 +      break;
  1.1189 +    }
  1.1190 +
  1.1191 +    nsISHTransaction *temp = trans;
  1.1192 +    temp->GetNext(getter_AddRefs(trans));
  1.1193 +  }
  1.1194 +  if (i > endIndex)
  1.1195 +    return NS_OK;
  1.1196 +  
  1.1197 +  if (i == mIndex) {
  1.1198 +    NS_WARNING("How did the current SHEntry expire?");
  1.1199 +    return NS_OK;
  1.1200 +  }
  1.1201 +
  1.1202 +  EvictContentViewerForTransaction(trans);
  1.1203 +
  1.1204 +  return NS_OK;
  1.1205 +}
  1.1206 +
  1.1207 +// Evicts all content viewers in all history objects.  This is very
  1.1208 +// inefficient, because it requires a linear search through all SHistory
  1.1209 +// objects for each viewer to be evicted.  However, this method is called
  1.1210 +// infrequently -- only when the disk or memory cache is cleared.
  1.1211 +
  1.1212 +//static
  1.1213 +void
  1.1214 +nsSHistory::GloballyEvictAllContentViewers()
  1.1215 +{
  1.1216 +  int32_t maxViewers = sHistoryMaxTotalViewers;
  1.1217 +  sHistoryMaxTotalViewers = 0;
  1.1218 +  GloballyEvictContentViewers();
  1.1219 +  sHistoryMaxTotalViewers = maxViewers;
  1.1220 +}
  1.1221 +
  1.1222 +void GetDynamicChildren(nsISHContainer* aContainer,
  1.1223 +                        nsTArray<uint64_t>& aDocshellIDs,
  1.1224 +                        bool aOnlyTopLevelDynamic)
  1.1225 +{
  1.1226 +  int32_t count = 0;
  1.1227 +  aContainer->GetChildCount(&count);
  1.1228 +  for (int32_t i = 0; i < count; ++i) {
  1.1229 +    nsCOMPtr<nsISHEntry> child;
  1.1230 +    aContainer->GetChildAt(i, getter_AddRefs(child));
  1.1231 +    if (child) {
  1.1232 +      bool dynAdded = false;
  1.1233 +      child->IsDynamicallyAdded(&dynAdded);
  1.1234 +      if (dynAdded) {
  1.1235 +        uint64_t docshellID = 0;
  1.1236 +        child->GetDocshellID(&docshellID);
  1.1237 +        aDocshellIDs.AppendElement(docshellID);
  1.1238 +      }
  1.1239 +      if (!dynAdded || !aOnlyTopLevelDynamic) {
  1.1240 +        nsCOMPtr<nsISHContainer> childAsContainer = do_QueryInterface(child);
  1.1241 +        if (childAsContainer) {
  1.1242 +          GetDynamicChildren(childAsContainer, aDocshellIDs,
  1.1243 +                             aOnlyTopLevelDynamic);
  1.1244 +        }
  1.1245 +      }
  1.1246 +    }
  1.1247 +  }
  1.1248 +}
  1.1249 +
  1.1250 +bool
  1.1251 +RemoveFromSessionHistoryContainer(nsISHContainer* aContainer,
  1.1252 +                                  nsTArray<uint64_t>& aDocshellIDs)
  1.1253 +{
  1.1254 +  nsCOMPtr<nsISHEntry> root = do_QueryInterface(aContainer);
  1.1255 +  NS_ENSURE_TRUE(root, false);
  1.1256 +
  1.1257 +  bool didRemove = false;
  1.1258 +  int32_t childCount = 0;
  1.1259 +  aContainer->GetChildCount(&childCount);
  1.1260 +  for (int32_t i = childCount - 1; i >= 0; --i) {
  1.1261 +    nsCOMPtr<nsISHEntry> child;
  1.1262 +    aContainer->GetChildAt(i, getter_AddRefs(child));
  1.1263 +    if (child) {
  1.1264 +      uint64_t docshelldID = 0;
  1.1265 +      child->GetDocshellID(&docshelldID);
  1.1266 +      if (aDocshellIDs.Contains(docshelldID)) {
  1.1267 +        didRemove = true;
  1.1268 +        aContainer->RemoveChild(child);
  1.1269 +      } else {
  1.1270 +        nsCOMPtr<nsISHContainer> container = do_QueryInterface(child);
  1.1271 +        if (container) {
  1.1272 +          bool childRemoved =
  1.1273 +            RemoveFromSessionHistoryContainer(container, aDocshellIDs);
  1.1274 +          if (childRemoved) {
  1.1275 +            didRemove = true;
  1.1276 +          }
  1.1277 +        }
  1.1278 +      }
  1.1279 +    }
  1.1280 +  }
  1.1281 +  return didRemove;
  1.1282 +}
  1.1283 +
  1.1284 +bool RemoveChildEntries(nsISHistory* aHistory, int32_t aIndex,
  1.1285 +                          nsTArray<uint64_t>& aEntryIDs)
  1.1286 +{
  1.1287 +  nsCOMPtr<nsISHEntry> rootHE;
  1.1288 +  aHistory->GetEntryAtIndex(aIndex, false, getter_AddRefs(rootHE));
  1.1289 +  nsCOMPtr<nsISHContainer> root = do_QueryInterface(rootHE);
  1.1290 +  return root ? RemoveFromSessionHistoryContainer(root, aEntryIDs) : false;
  1.1291 +}
  1.1292 +
  1.1293 +bool IsSameTree(nsISHEntry* aEntry1, nsISHEntry* aEntry2)
  1.1294 +{
  1.1295 +  if (!aEntry1 && !aEntry2) {
  1.1296 +    return true;
  1.1297 +  }
  1.1298 +  if ((!aEntry1 && aEntry2) || (aEntry1 && !aEntry2)) {
  1.1299 +    return false;
  1.1300 +  }
  1.1301 +  uint32_t id1, id2;
  1.1302 +  aEntry1->GetID(&id1);
  1.1303 +  aEntry2->GetID(&id2);
  1.1304 +  if (id1 != id2) {
  1.1305 +    return false;
  1.1306 +  }
  1.1307 +
  1.1308 +  nsCOMPtr<nsISHContainer> container1 = do_QueryInterface(aEntry1);
  1.1309 +  nsCOMPtr<nsISHContainer> container2 = do_QueryInterface(aEntry2);
  1.1310 +  int32_t count1, count2;
  1.1311 +  container1->GetChildCount(&count1);
  1.1312 +  container2->GetChildCount(&count2);
  1.1313 +  // We allow null entries in the end of the child list.
  1.1314 +  int32_t count = std::max(count1, count2);
  1.1315 +  for (int32_t i = 0; i < count; ++i) {
  1.1316 +    nsCOMPtr<nsISHEntry> child1, child2;
  1.1317 +    container1->GetChildAt(i, getter_AddRefs(child1));
  1.1318 +    container2->GetChildAt(i, getter_AddRefs(child2));
  1.1319 +    if (!IsSameTree(child1, child2)) {
  1.1320 +      return false;
  1.1321 +    }
  1.1322 +  }
  1.1323 +  
  1.1324 +  return true;
  1.1325 +}
  1.1326 +
  1.1327 +bool
  1.1328 +nsSHistory::RemoveDuplicate(int32_t aIndex, bool aKeepNext)
  1.1329 +{
  1.1330 +  NS_ASSERTION(aIndex >= 0, "aIndex must be >= 0!");
  1.1331 +  NS_ASSERTION(aIndex != 0 || aKeepNext,
  1.1332 +               "If we're removing index 0 we must be keeping the next");
  1.1333 +  NS_ASSERTION(aIndex != mIndex, "Shouldn't remove mIndex!");
  1.1334 +  int32_t compareIndex = aKeepNext ? aIndex + 1 : aIndex - 1;
  1.1335 +  nsCOMPtr<nsISHEntry> root1, root2;
  1.1336 +  GetEntryAtIndex(aIndex, false, getter_AddRefs(root1));
  1.1337 +  GetEntryAtIndex(compareIndex, false, getter_AddRefs(root2));
  1.1338 +  if (IsSameTree(root1, root2)) {
  1.1339 +    nsCOMPtr<nsISHTransaction> txToRemove, txToKeep, txNext, txPrev;
  1.1340 +    GetTransactionAtIndex(aIndex, getter_AddRefs(txToRemove));
  1.1341 +    GetTransactionAtIndex(compareIndex, getter_AddRefs(txToKeep));
  1.1342 +    NS_ENSURE_TRUE(txToRemove, false);
  1.1343 +    NS_ENSURE_TRUE(txToKeep, false);
  1.1344 +    txToRemove->GetNext(getter_AddRefs(txNext));
  1.1345 +    txToRemove->GetPrev(getter_AddRefs(txPrev));
  1.1346 +    txToRemove->SetNext(nullptr);
  1.1347 +    txToRemove->SetPrev(nullptr);
  1.1348 +    if (aKeepNext) {
  1.1349 +      if (txPrev) {
  1.1350 +        txPrev->SetNext(txToKeep);
  1.1351 +      } else {
  1.1352 +        txToKeep->SetPrev(nullptr);
  1.1353 +      }
  1.1354 +    } else {
  1.1355 +      txToKeep->SetNext(txNext);
  1.1356 +    }
  1.1357 +
  1.1358 +    if (aIndex == 0 && aKeepNext) {
  1.1359 +      NS_ASSERTION(txToRemove == mListRoot,
  1.1360 +                   "Transaction at index 0 should be mListRoot!");
  1.1361 +      // We're removing the very first session history transaction!
  1.1362 +      mListRoot = txToKeep;
  1.1363 +    }
  1.1364 +    if (mRootDocShell) {
  1.1365 +      static_cast<nsDocShell*>(mRootDocShell)->HistoryTransactionRemoved(aIndex);
  1.1366 +    }
  1.1367 +
  1.1368 +    // Adjust our indices to reflect the removed transaction
  1.1369 +    if (mIndex > aIndex) {
  1.1370 +      mIndex = mIndex - 1;
  1.1371 +    }
  1.1372 +
  1.1373 +    // NB: If the transaction we are removing is the transaction currently
  1.1374 +    // being navigated to (mRequestedIndex) then we adjust the index
  1.1375 +    // only if we're not keeping the next entry (because if we are keeping
  1.1376 +    // the next entry (because the current is a duplicate of the next), then
  1.1377 +    // that entry slides into the spot that we're currently pointing to.
  1.1378 +    // We don't do this adjustment for mIndex because mIndex cannot equal
  1.1379 +    // aIndex.
  1.1380 +
  1.1381 +    // NB: We don't need to guard on mRequestedIndex being nonzero here,
  1.1382 +    // because either they're strictly greater than aIndex which is at least
  1.1383 +    // zero, or they are equal to aIndex in which case aKeepNext must be true
  1.1384 +    // if aIndex is zero.
  1.1385 +    if (mRequestedIndex > aIndex || (mRequestedIndex == aIndex && !aKeepNext)) {
  1.1386 +      mRequestedIndex = mRequestedIndex - 1;
  1.1387 +    }
  1.1388 +    --mLength;
  1.1389 +    return true;
  1.1390 +  }
  1.1391 +  return false;
  1.1392 +}
  1.1393 +
  1.1394 +NS_IMETHODIMP_(void)
  1.1395 +nsSHistory::RemoveEntries(nsTArray<uint64_t>& aIDs, int32_t aStartIndex)
  1.1396 +{
  1.1397 +  int32_t index = aStartIndex;
  1.1398 +  while(index >= 0 && RemoveChildEntries(this, --index, aIDs));
  1.1399 +  int32_t minIndex = index;
  1.1400 +  index = aStartIndex;
  1.1401 +  while(index >= 0 && RemoveChildEntries(this, index++, aIDs));
  1.1402 +  
  1.1403 +  // We need to remove duplicate nsSHEntry trees.
  1.1404 +  bool didRemove = false;
  1.1405 +  while (index > minIndex) {
  1.1406 +    if (index != mIndex) {
  1.1407 +      didRemove = RemoveDuplicate(index, index < mIndex) || didRemove;
  1.1408 +    }
  1.1409 +    --index;
  1.1410 +  }
  1.1411 +  if (didRemove && mRootDocShell) {
  1.1412 +    nsRefPtr<nsIRunnable> ev =
  1.1413 +      NS_NewRunnableMethod(static_cast<nsDocShell*>(mRootDocShell),
  1.1414 +                           &nsDocShell::FireDummyOnLocationChange);
  1.1415 +    NS_DispatchToCurrentThread(ev);
  1.1416 +  }
  1.1417 +}
  1.1418 +
  1.1419 +void
  1.1420 +nsSHistory::RemoveDynEntries(int32_t aOldIndex, int32_t aNewIndex)
  1.1421 +{
  1.1422 +  // Search for the entries which are in the current index,
  1.1423 +  // but not in the new one.
  1.1424 +  nsCOMPtr<nsISHEntry> originalSH;
  1.1425 +  GetEntryAtIndex(aOldIndex, false, getter_AddRefs(originalSH));
  1.1426 +  nsCOMPtr<nsISHContainer> originalContainer = do_QueryInterface(originalSH);
  1.1427 +  nsAutoTArray<uint64_t, 16> toBeRemovedEntries;
  1.1428 +  if (originalContainer) {
  1.1429 +    nsTArray<uint64_t> originalDynDocShellIDs;
  1.1430 +    GetDynamicChildren(originalContainer, originalDynDocShellIDs, true);
  1.1431 +    if (originalDynDocShellIDs.Length()) {
  1.1432 +      nsCOMPtr<nsISHEntry> currentSH;
  1.1433 +      GetEntryAtIndex(aNewIndex, false, getter_AddRefs(currentSH));
  1.1434 +      nsCOMPtr<nsISHContainer> newContainer = do_QueryInterface(currentSH);
  1.1435 +      if (newContainer) {
  1.1436 +        nsTArray<uint64_t> newDynDocShellIDs;
  1.1437 +        GetDynamicChildren(newContainer, newDynDocShellIDs, false);
  1.1438 +        for (uint32_t i = 0; i < originalDynDocShellIDs.Length(); ++i) {
  1.1439 +          if (!newDynDocShellIDs.Contains(originalDynDocShellIDs[i])) {
  1.1440 +            toBeRemovedEntries.AppendElement(originalDynDocShellIDs[i]);
  1.1441 +          }
  1.1442 +        }
  1.1443 +      }
  1.1444 +    }
  1.1445 +  }
  1.1446 +  if (toBeRemovedEntries.Length()) {
  1.1447 +    RemoveEntries(toBeRemovedEntries, aOldIndex);
  1.1448 +  }
  1.1449 +}
  1.1450 +
  1.1451 +NS_IMETHODIMP
  1.1452 +nsSHistory::UpdateIndex()
  1.1453 +{
  1.1454 +  // Update the actual index with the right value. 
  1.1455 +  if (mIndex != mRequestedIndex && mRequestedIndex != -1) {
  1.1456 +    RemoveDynEntries(mIndex, mRequestedIndex);
  1.1457 +    mIndex = mRequestedIndex;
  1.1458 +  }
  1.1459 +
  1.1460 +  mRequestedIndex = -1;
  1.1461 +  return NS_OK;
  1.1462 +}
  1.1463 +
  1.1464 +NS_IMETHODIMP
  1.1465 +nsSHistory::Stop(uint32_t aStopFlags)
  1.1466 +{
  1.1467 +  //Not implemented
  1.1468 +  return NS_OK;
  1.1469 +}
  1.1470 +
  1.1471 +
  1.1472 +NS_IMETHODIMP
  1.1473 +nsSHistory::GetDocument(nsIDOMDocument** aDocument)
  1.1474 +{
  1.1475 +  // Not implemented
  1.1476 +  return NS_OK;
  1.1477 +}
  1.1478 +
  1.1479 +
  1.1480 +NS_IMETHODIMP
  1.1481 +nsSHistory::GetCurrentURI(nsIURI** aResultURI)
  1.1482 +{
  1.1483 +  NS_ENSURE_ARG_POINTER(aResultURI);
  1.1484 +  nsresult rv;
  1.1485 +
  1.1486 +  nsCOMPtr<nsISHEntry> currentEntry;
  1.1487 +  rv = GetEntryAtIndex(mIndex, false, getter_AddRefs(currentEntry));
  1.1488 +  if (NS_FAILED(rv) && !currentEntry) return rv;
  1.1489 +  rv = currentEntry->GetURI(aResultURI);
  1.1490 +  return rv;
  1.1491 +}
  1.1492 +
  1.1493 +
  1.1494 +NS_IMETHODIMP
  1.1495 +nsSHistory::GetReferringURI(nsIURI** aURI)
  1.1496 +{
  1.1497 +  *aURI = nullptr;
  1.1498 +  // Not implemented
  1.1499 +  return NS_OK;
  1.1500 +}
  1.1501 +
  1.1502 +
  1.1503 +NS_IMETHODIMP
  1.1504 +nsSHistory::SetSessionHistory(nsISHistory* aSessionHistory)
  1.1505 +{
  1.1506 +  // Not implemented
  1.1507 +  return NS_OK;
  1.1508 +}
  1.1509 +
  1.1510 +	
  1.1511 +NS_IMETHODIMP
  1.1512 +nsSHistory::GetSessionHistory(nsISHistory** aSessionHistory)
  1.1513 +{
  1.1514 +  // Not implemented
  1.1515 +  return NS_OK;
  1.1516 +}
  1.1517 +
  1.1518 +NS_IMETHODIMP
  1.1519 +nsSHistory::LoadURIWithBase(const char16_t* aURI,
  1.1520 +                            uint32_t aLoadFlags,
  1.1521 +                            nsIURI* aReferringURI,
  1.1522 +                            nsIInputStream* aPostStream,
  1.1523 +                            nsIInputStream* aExtraHeaderStream,
  1.1524 +                            nsIURI* aBaseURI)
  1.1525 +{
  1.1526 +  return NS_OK;
  1.1527 +}
  1.1528 +
  1.1529 +NS_IMETHODIMP
  1.1530 +nsSHistory::LoadURI(const char16_t* aURI,
  1.1531 +                    uint32_t aLoadFlags,
  1.1532 +                    nsIURI* aReferringURI,
  1.1533 +                    nsIInputStream* aPostStream,
  1.1534 +                    nsIInputStream* aExtraHeaderStream)
  1.1535 +{
  1.1536 +  return NS_OK;
  1.1537 +}
  1.1538 +
  1.1539 +NS_IMETHODIMP
  1.1540 +nsSHistory::GotoIndex(int32_t aIndex)
  1.1541 +{
  1.1542 +  return LoadEntry(aIndex, nsIDocShellLoadInfo::loadHistory, HIST_CMD_GOTOINDEX);
  1.1543 +}
  1.1544 +
  1.1545 +nsresult
  1.1546 +nsSHistory::LoadNextPossibleEntry(int32_t aNewIndex, long aLoadType, uint32_t aHistCmd)
  1.1547 +{
  1.1548 +  mRequestedIndex = -1;
  1.1549 +  if (aNewIndex < mIndex) {
  1.1550 +    return LoadEntry(aNewIndex - 1, aLoadType, aHistCmd);
  1.1551 +  }
  1.1552 +  if (aNewIndex > mIndex) {
  1.1553 +    return LoadEntry(aNewIndex + 1, aLoadType, aHistCmd);
  1.1554 +  }
  1.1555 +  return NS_ERROR_FAILURE;
  1.1556 +}
  1.1557 +
  1.1558 +NS_IMETHODIMP
  1.1559 +nsSHistory::LoadEntry(int32_t aIndex, long aLoadType, uint32_t aHistCmd)
  1.1560 +{
  1.1561 +  nsCOMPtr<nsIDocShell> docShell;
  1.1562 +  // Keep note of requested history index in mRequestedIndex.
  1.1563 +  mRequestedIndex = aIndex;
  1.1564 +
  1.1565 +  nsCOMPtr<nsISHEntry> prevEntry;
  1.1566 +  GetEntryAtIndex(mIndex, false, getter_AddRefs(prevEntry));
  1.1567 +
  1.1568 +  nsCOMPtr<nsISHEntry> nextEntry;
  1.1569 +  GetEntryAtIndex(mRequestedIndex, false, getter_AddRefs(nextEntry));
  1.1570 +  if (!nextEntry || !prevEntry) {
  1.1571 +    mRequestedIndex = -1;
  1.1572 +    return NS_ERROR_FAILURE;
  1.1573 +  }
  1.1574 +
  1.1575 +  // Remember that this entry is getting loaded at this point in the sequence
  1.1576 +  nsCOMPtr<nsISHEntryInternal> entryInternal = do_QueryInterface(nextEntry);
  1.1577 +
  1.1578 +  if (entryInternal) {
  1.1579 +    entryInternal->SetLastTouched(++gTouchCounter);
  1.1580 +  }
  1.1581 +
  1.1582 +  // Send appropriate listener notifications
  1.1583 +  bool canNavigate = true;
  1.1584 +  // Get the uri for the entry we are about to visit
  1.1585 +  nsCOMPtr<nsIURI> nextURI;
  1.1586 +  nextEntry->GetURI(getter_AddRefs(nextURI));
  1.1587 +
  1.1588 +  if (aHistCmd == HIST_CMD_BACK) {
  1.1589 +    // We are going back one entry. Send GoBack notifications
  1.1590 +    NOTIFY_LISTENERS_CANCELABLE(OnHistoryGoBack, canNavigate,
  1.1591 +                                (nextURI, &canNavigate));
  1.1592 +  } else if (aHistCmd == HIST_CMD_FORWARD) {
  1.1593 +    // We are going forward. Send GoForward notification
  1.1594 +    NOTIFY_LISTENERS_CANCELABLE(OnHistoryGoForward, canNavigate,
  1.1595 +                                (nextURI, &canNavigate));
  1.1596 +  } else if (aHistCmd == HIST_CMD_GOTOINDEX) {
  1.1597 +    // We are going somewhere else. This is not reload either
  1.1598 +    NOTIFY_LISTENERS_CANCELABLE(OnHistoryGotoIndex, canNavigate,
  1.1599 +                                (aIndex, nextURI, &canNavigate));
  1.1600 +  }
  1.1601 +
  1.1602 +  if (!canNavigate) {
  1.1603 +    // If the listener asked us not to proceed with 
  1.1604 +    // the operation, simply return.    
  1.1605 +    mRequestedIndex = -1;
  1.1606 +    return NS_OK;  // XXX Maybe I can return some other error code?
  1.1607 +  }
  1.1608 +
  1.1609 +  nsCOMPtr<nsIURI> nexturi;
  1.1610 +  int32_t pCount=0, nCount=0;
  1.1611 +  nsCOMPtr<nsISHContainer> prevAsContainer(do_QueryInterface(prevEntry));
  1.1612 +  nsCOMPtr<nsISHContainer> nextAsContainer(do_QueryInterface(nextEntry));
  1.1613 +  if (prevAsContainer && nextAsContainer) {
  1.1614 +    prevAsContainer->GetChildCount(&pCount);
  1.1615 +    nextAsContainer->GetChildCount(&nCount);
  1.1616 +  }
  1.1617 +  
  1.1618 +  nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
  1.1619 +  if (mRequestedIndex == mIndex) {
  1.1620 +    // Possibly a reload case 
  1.1621 +    docShell = mRootDocShell;
  1.1622 +  }
  1.1623 +  else {
  1.1624 +    // Going back or forward.
  1.1625 +    if ((pCount > 0) && (nCount > 0)) {
  1.1626 +      /* THis is a subframe navigation. Go find 
  1.1627 +       * the docshell in which load should happen
  1.1628 +       */
  1.1629 +      bool frameFound = false;
  1.1630 +      nsresult rv = CompareFrames(prevEntry, nextEntry, mRootDocShell, aLoadType, &frameFound);
  1.1631 +      if (!frameFound) {
  1.1632 +        // We did not successfully find the subframe in which
  1.1633 +        // the new url was to be loaded. Go further in the history.
  1.1634 +        return LoadNextPossibleEntry(aIndex, aLoadType, aHistCmd);
  1.1635 +      }
  1.1636 +      return rv;
  1.1637 +    }   // (pCount >0)
  1.1638 +    else {
  1.1639 +      // Loading top level page.
  1.1640 +      uint32_t prevID = 0;
  1.1641 +      uint32_t nextID = 0;
  1.1642 +      prevEntry->GetID(&prevID);
  1.1643 +      nextEntry->GetID(&nextID);
  1.1644 +      if (prevID == nextID) {
  1.1645 +        // Try harder to find something new to load.
  1.1646 +        // This may happen for example if some page removed iframes dynamically.
  1.1647 +        return LoadNextPossibleEntry(aIndex, aLoadType, aHistCmd);
  1.1648 +      }
  1.1649 +      docShell = mRootDocShell;
  1.1650 +    }
  1.1651 +  }
  1.1652 +
  1.1653 +  if (!docShell) {
  1.1654 +    // we did not successfully go to the proper index.
  1.1655 +    // return error.
  1.1656 +    mRequestedIndex = -1;
  1.1657 +    return NS_ERROR_FAILURE;
  1.1658 +  }
  1.1659 +
  1.1660 +  // Start the load on the appropriate docshell
  1.1661 +  return InitiateLoad(nextEntry, docShell, aLoadType);
  1.1662 +}
  1.1663 +
  1.1664 +nsresult
  1.1665 +nsSHistory::CompareFrames(nsISHEntry * aPrevEntry, nsISHEntry * aNextEntry, nsIDocShell * aParent, long aLoadType, bool * aIsFrameFound)
  1.1666 +{
  1.1667 +  if (!aPrevEntry || !aNextEntry || !aParent)
  1.1668 +    return NS_ERROR_FAILURE;
  1.1669 +
  1.1670 +  // We should be comparing only entries which were created for the
  1.1671 +  // same docshell. This is here to just prevent anything strange happening.
  1.1672 +  // This check could be possibly an assertion.
  1.1673 +  uint64_t prevdID, nextdID;
  1.1674 +  aPrevEntry->GetDocshellID(&prevdID);
  1.1675 +  aNextEntry->GetDocshellID(&nextdID);
  1.1676 +  NS_ENSURE_STATE(prevdID == nextdID);
  1.1677 +
  1.1678 +  nsresult result = NS_OK;
  1.1679 +  uint32_t prevID, nextID;
  1.1680 +
  1.1681 +  aPrevEntry->GetID(&prevID);
  1.1682 +  aNextEntry->GetID(&nextID);
  1.1683 + 
  1.1684 +  // Check the IDs to verify if the pages are different.
  1.1685 +  if (prevID != nextID) {
  1.1686 +    if (aIsFrameFound)
  1.1687 +      *aIsFrameFound = true;
  1.1688 +    // Set the Subframe flag of the entry to indicate that
  1.1689 +    // it is subframe navigation        
  1.1690 +    aNextEntry->SetIsSubFrame(true);
  1.1691 +    InitiateLoad(aNextEntry, aParent, aLoadType);
  1.1692 +    return NS_OK;
  1.1693 +  }
  1.1694 +
  1.1695 +  /* The root entries are the same, so compare any child frames */
  1.1696 +  int32_t pcnt=0, ncnt=0, dsCount=0;
  1.1697 +  nsCOMPtr<nsISHContainer>  prevContainer(do_QueryInterface(aPrevEntry));
  1.1698 +  nsCOMPtr<nsISHContainer>  nextContainer(do_QueryInterface(aNextEntry));
  1.1699 +
  1.1700 +  if (!aParent)
  1.1701 +    return NS_ERROR_FAILURE;
  1.1702 +  if (!prevContainer || !nextContainer)
  1.1703 +    return NS_ERROR_FAILURE;
  1.1704 +
  1.1705 +  prevContainer->GetChildCount(&pcnt);
  1.1706 +  nextContainer->GetChildCount(&ncnt);
  1.1707 +  aParent->GetChildCount(&dsCount);
  1.1708 +
  1.1709 +  // Create an array for child docshells.
  1.1710 +  nsCOMArray<nsIDocShell> docshells;
  1.1711 +  for (int32_t i = 0; i < dsCount; ++i) {
  1.1712 +    nsCOMPtr<nsIDocShellTreeItem> treeItem;
  1.1713 +    aParent->GetChildAt(i, getter_AddRefs(treeItem));
  1.1714 +    nsCOMPtr<nsIDocShell> shell = do_QueryInterface(treeItem);
  1.1715 +    if (shell) {
  1.1716 +      docshells.AppendObject(shell);
  1.1717 +    }
  1.1718 +  }
  1.1719 +
  1.1720 +  // Search for something to load next.
  1.1721 +  for (int32_t i = 0; i < ncnt; ++i) {
  1.1722 +    // First get an entry which may cause a new page to be loaded.
  1.1723 +    nsCOMPtr<nsISHEntry> nChild;
  1.1724 +    nextContainer->GetChildAt(i, getter_AddRefs(nChild));
  1.1725 +    if (!nChild) {
  1.1726 +      continue;
  1.1727 +    }
  1.1728 +    uint64_t docshellID = 0;
  1.1729 +    nChild->GetDocshellID(&docshellID);
  1.1730 +
  1.1731 +    // Then find the associated docshell.
  1.1732 +    nsIDocShell* dsChild = nullptr;
  1.1733 +    int32_t count = docshells.Count();
  1.1734 +    for (int32_t j = 0; j < count; ++j) {
  1.1735 +      uint64_t shellID = 0;
  1.1736 +      nsIDocShell* shell = docshells[j];
  1.1737 +      shell->GetHistoryID(&shellID);
  1.1738 +      if (shellID == docshellID) {
  1.1739 +        dsChild = shell;
  1.1740 +        break;
  1.1741 +      }
  1.1742 +    }
  1.1743 +    if (!dsChild) {
  1.1744 +      continue;
  1.1745 +    }
  1.1746 +
  1.1747 +    // Then look at the previous entries to see if there was
  1.1748 +    // an entry for the docshell.
  1.1749 +    nsCOMPtr<nsISHEntry> pChild;
  1.1750 +    for (int32_t k = 0; k < pcnt; ++k) {
  1.1751 +      nsCOMPtr<nsISHEntry> child;
  1.1752 +      prevContainer->GetChildAt(k, getter_AddRefs(child));
  1.1753 +      if (child) {
  1.1754 +        uint64_t dID = 0;
  1.1755 +        child->GetDocshellID(&dID);
  1.1756 +        if (dID == docshellID) {
  1.1757 +          pChild = child;
  1.1758 +          break;
  1.1759 +        }
  1.1760 +      }
  1.1761 +    }
  1.1762 +
  1.1763 +    // Finally recursively call this method.
  1.1764 +    // This will either load a new page to shell or some subshell or
  1.1765 +    // do nothing.
  1.1766 +    CompareFrames(pChild, nChild, dsChild, aLoadType, aIsFrameFound);
  1.1767 +  }     
  1.1768 +  return result;
  1.1769 +}
  1.1770 +
  1.1771 +
  1.1772 +nsresult 
  1.1773 +nsSHistory::InitiateLoad(nsISHEntry * aFrameEntry, nsIDocShell * aFrameDS, long aLoadType)
  1.1774 +{
  1.1775 +  NS_ENSURE_STATE(aFrameDS && aFrameEntry);
  1.1776 +
  1.1777 +  nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
  1.1778 +
  1.1779 +  /* Set the loadType in the SHEntry too to  what was passed on.
  1.1780 +   * This will be passed on to child subframes later in nsDocShell,
  1.1781 +   * so that proper loadType is maintained through out a frameset
  1.1782 +   */
  1.1783 +  aFrameEntry->SetLoadType(aLoadType);    
  1.1784 +  aFrameDS->CreateLoadInfo (getter_AddRefs(loadInfo));
  1.1785 +
  1.1786 +  loadInfo->SetLoadType(aLoadType);
  1.1787 +  loadInfo->SetSHEntry(aFrameEntry);
  1.1788 +
  1.1789 +  nsCOMPtr<nsIURI> nextURI;
  1.1790 +  aFrameEntry->GetURI(getter_AddRefs(nextURI));
  1.1791 +  // Time   to initiate a document load
  1.1792 +  return aFrameDS->LoadURI(nextURI, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, false);
  1.1793 +
  1.1794 +}
  1.1795 +
  1.1796 +
  1.1797 +
  1.1798 +NS_IMETHODIMP
  1.1799 +nsSHistory::SetRootDocShell(nsIDocShell * aDocShell)
  1.1800 +{
  1.1801 +  mRootDocShell = aDocShell;
  1.1802 +  return NS_OK;
  1.1803 +}
  1.1804 +
  1.1805 +NS_IMETHODIMP
  1.1806 +nsSHistory::GetRootDocShell(nsIDocShell ** aDocShell)
  1.1807 +{
  1.1808 +  NS_ENSURE_ARG_POINTER(aDocShell);
  1.1809 +
  1.1810 +  *aDocShell = mRootDocShell;
  1.1811 +  //Not refcounted. May this method should not be available for public
  1.1812 +  // NS_IF_ADDREF(*aDocShell);
  1.1813 +  return NS_OK;
  1.1814 +}
  1.1815 +
  1.1816 +
  1.1817 +NS_IMETHODIMP
  1.1818 +nsSHistory::GetSHistoryEnumerator(nsISimpleEnumerator** aEnumerator)
  1.1819 +{
  1.1820 +  nsresult status = NS_OK;
  1.1821 +
  1.1822 +  NS_ENSURE_ARG_POINTER(aEnumerator);
  1.1823 +  nsSHEnumerator * iterator = new nsSHEnumerator(this);
  1.1824 +  if (iterator && NS_FAILED(status = CallQueryInterface(iterator, aEnumerator)))
  1.1825 +    delete iterator;
  1.1826 +  return status;
  1.1827 +}
  1.1828 +
  1.1829 +
  1.1830 +//*****************************************************************************
  1.1831 +//***    nsSHEnumerator: Object Management
  1.1832 +//*****************************************************************************
  1.1833 +
  1.1834 +nsSHEnumerator::nsSHEnumerator(nsSHistory * aSHistory):mIndex(-1)
  1.1835 +{
  1.1836 +  mSHistory = aSHistory;
  1.1837 +}
  1.1838 +
  1.1839 +nsSHEnumerator::~nsSHEnumerator()
  1.1840 +{
  1.1841 +  mSHistory = nullptr;
  1.1842 +}
  1.1843 +
  1.1844 +NS_IMPL_ISUPPORTS(nsSHEnumerator, nsISimpleEnumerator)
  1.1845 +
  1.1846 +NS_IMETHODIMP
  1.1847 +nsSHEnumerator::HasMoreElements(bool * aReturn)
  1.1848 +{
  1.1849 +  int32_t cnt;
  1.1850 +  *aReturn = false;
  1.1851 +  mSHistory->GetCount(&cnt);
  1.1852 +  if (mIndex >= -1 && mIndex < (cnt-1) ) { 
  1.1853 +    *aReturn = true;
  1.1854 +  }
  1.1855 +  return NS_OK;
  1.1856 +}
  1.1857 +
  1.1858 +
  1.1859 +NS_IMETHODIMP 
  1.1860 +nsSHEnumerator::GetNext(nsISupports **aItem)
  1.1861 +{
  1.1862 +  NS_ENSURE_ARG_POINTER(aItem);
  1.1863 +  int32_t cnt= 0;
  1.1864 +
  1.1865 +  nsresult  result = NS_ERROR_FAILURE;
  1.1866 +  mSHistory->GetCount(&cnt);
  1.1867 +  if (mIndex < (cnt-1)) {
  1.1868 +    mIndex++;
  1.1869 +    nsCOMPtr<nsISHEntry> hEntry;
  1.1870 +    result = mSHistory->GetEntryAtIndex(mIndex, false, getter_AddRefs(hEntry));
  1.1871 +    if (hEntry)
  1.1872 +      result = CallQueryInterface(hEntry, aItem);
  1.1873 +  }
  1.1874 +  return result;
  1.1875 +}

mercurial