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