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(¤tPersist); 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 +}