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