CLOSED TREE: TraceMonkey merge head. (a=blockers)
[mozilla-central.git] / docshell / shistory / src / nsSHEntry.cpp
blob86379440b04dc1e90ff79286d3911e662f0e9cda
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is the Mozilla browser.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications, Inc.
19 * Portions created by the Initial Developer are Copyright (C) 1999
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Radha Kulkarni <radha@netscape.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #ifdef DEBUG_bryner
40 #define DEBUG_PAGE_CACHE
41 #endif
43 // Local Includes
44 #include "nsSHEntry.h"
45 #include "nsXPIDLString.h"
46 #include "nsReadableUtils.h"
47 #include "nsIDocShellLoadInfo.h"
48 #include "nsIDocShellTreeItem.h"
49 #include "nsIDocument.h"
50 #include "nsIDOMDocument.h"
51 #include "nsAutoPtr.h"
52 #include "nsThreadUtils.h"
53 #include "nsIWebNavigation.h"
54 #include "nsISHistory.h"
55 #include "nsISHistoryInternal.h"
56 #include "nsDocShellEditorData.h"
57 #include "nsIDocShell.h"
59 namespace dom = mozilla::dom;
61 // Hardcode this to time out unused content viewers after 30 minutes
62 #define CONTENT_VIEWER_TIMEOUT_SECONDS 30*60
64 typedef nsExpirationTracker<nsSHEntry,3> HistoryTrackerBase;
65 class HistoryTracker : public HistoryTrackerBase {
66 public:
67 // Expire cached contentviewers after 20-30 minutes in the cache.
68 HistoryTracker() : HistoryTrackerBase((CONTENT_VIEWER_TIMEOUT_SECONDS/2)*1000) {}
70 protected:
71 virtual void NotifyExpired(nsSHEntry* aObj) {
72 RemoveObject(aObj);
73 aObj->Expire();
77 static HistoryTracker *gHistoryTracker = nsnull;
78 static PRUint32 gEntryID = 0;
79 static PRUint64 gEntryDocIdentifier = 0;
81 nsresult nsSHEntry::Startup()
83 gHistoryTracker = new HistoryTracker();
84 return gHistoryTracker ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
87 void nsSHEntry::Shutdown()
89 delete gHistoryTracker;
90 gHistoryTracker = nsnull;
93 static void StopTrackingEntry(nsSHEntry *aEntry)
95 if (aEntry->GetExpirationState()->IsTracked()) {
96 gHistoryTracker->RemoveObject(aEntry);
100 //*****************************************************************************
101 //*** nsSHEntry: Object Management
102 //*****************************************************************************
105 nsSHEntry::nsSHEntry()
106 : mLoadType(0)
107 , mID(gEntryID++)
108 , mPageIdentifier(mID)
109 , mDocIdentifier(gEntryDocIdentifier++)
110 , mScrollPositionX(0)
111 , mScrollPositionY(0)
112 , mIsFrameNavigation(PR_FALSE)
113 , mSaveLayoutState(PR_TRUE)
114 , mExpired(PR_FALSE)
115 , mSticky(PR_TRUE)
116 , mDynamicallyCreated(PR_FALSE)
117 , mParent(nsnull)
118 , mViewerBounds(0, 0, 0, 0)
119 , mDocShellID(0)
120 , mLastTouched(0)
124 nsSHEntry::nsSHEntry(const nsSHEntry &other)
125 : mURI(other.mURI)
126 , mReferrerURI(other.mReferrerURI)
127 // XXX why not copy mDocument?
128 , mTitle(other.mTitle)
129 , mPostData(other.mPostData)
130 , mLayoutHistoryState(other.mLayoutHistoryState)
131 , mLoadType(0) // XXX why not copy?
132 , mID(other.mID)
133 , mPageIdentifier(other.mPageIdentifier)
134 , mDocIdentifier(other.mDocIdentifier)
135 , mScrollPositionX(0) // XXX why not copy?
136 , mScrollPositionY(0) // XXX why not copy?
137 , mIsFrameNavigation(other.mIsFrameNavigation)
138 , mSaveLayoutState(other.mSaveLayoutState)
139 , mExpired(other.mExpired)
140 , mSticky(PR_TRUE)
141 , mDynamicallyCreated(other.mDynamicallyCreated)
142 // XXX why not copy mContentType?
143 , mCacheKey(other.mCacheKey)
144 , mParent(other.mParent)
145 , mViewerBounds(0, 0, 0, 0)
146 , mOwner(other.mOwner)
147 , mStateData(other.mStateData)
148 , mDocShellID(other.mDocShellID)
152 static PRBool
153 ClearParentPtr(nsISHEntry* aEntry, void* /* aData */)
155 if (aEntry) {
156 aEntry->SetParent(nsnull);
158 return PR_TRUE;
161 nsSHEntry::~nsSHEntry()
163 StopTrackingEntry(this);
165 // Since we never really remove kids from SHEntrys, we need to null
166 // out the mParent pointers on all our kids.
167 mChildren.EnumerateForwards(ClearParentPtr, nsnull);
168 mChildren.Clear();
170 if (mContentViewer) {
171 // RemoveFromBFCacheSync is virtual, so call the nsSHEntry version
172 // explicitly
173 nsSHEntry::RemoveFromBFCacheSync();
176 mEditorData = nsnull;
178 #ifdef DEBUG
179 // This is not happening as far as I can tell from breakpad as of early November 2007
180 nsExpirationTracker<nsSHEntry,3>::Iterator iterator(gHistoryTracker);
181 nsSHEntry* elem;
182 while ((elem = iterator.Next()) != nsnull) {
183 NS_ASSERTION(elem != this, "Found dead entry still in the tracker!");
185 #endif
188 //*****************************************************************************
189 // nsSHEntry: nsISupports
190 //*****************************************************************************
192 NS_IMPL_ISUPPORTS5(nsSHEntry, nsISHContainer, nsISHEntry, nsIHistoryEntry,
193 nsIMutationObserver, nsISHEntryInternal)
195 //*****************************************************************************
196 // nsSHEntry: nsISHEntry
197 //*****************************************************************************
199 NS_IMETHODIMP nsSHEntry::SetScrollPosition(PRInt32 x, PRInt32 y)
201 mScrollPositionX = x;
202 mScrollPositionY = y;
203 return NS_OK;
206 NS_IMETHODIMP nsSHEntry::GetScrollPosition(PRInt32 *x, PRInt32 *y)
208 *x = mScrollPositionX;
209 *y = mScrollPositionY;
210 return NS_OK;
213 NS_IMETHODIMP nsSHEntry::GetURI(nsIURI** aURI)
215 *aURI = mURI;
216 NS_IF_ADDREF(*aURI);
217 return NS_OK;
220 NS_IMETHODIMP nsSHEntry::SetURI(nsIURI* aURI)
222 mURI = aURI;
223 return NS_OK;
226 NS_IMETHODIMP nsSHEntry::GetReferrerURI(nsIURI **aReferrerURI)
228 *aReferrerURI = mReferrerURI;
229 NS_IF_ADDREF(*aReferrerURI);
230 return NS_OK;
233 NS_IMETHODIMP nsSHEntry::SetReferrerURI(nsIURI *aReferrerURI)
235 mReferrerURI = aReferrerURI;
236 return NS_OK;
239 NS_IMETHODIMP
240 nsSHEntry::SetContentViewer(nsIContentViewer *aViewer)
242 NS_PRECONDITION(!aViewer || !mContentViewer, "SHEntry already contains viewer");
244 if (mContentViewer || !aViewer) {
245 DropPresentationState();
248 mContentViewer = aViewer;
250 if (mContentViewer) {
251 gHistoryTracker->AddObject(this);
253 nsCOMPtr<nsIDOMDocument> domDoc;
254 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
255 // Store observed document in strong pointer in case it is removed from
256 // the contentviewer
257 mDocument = do_QueryInterface(domDoc);
258 if (mDocument) {
259 mDocument->SetBFCacheEntry(this);
260 mDocument->AddMutationObserver(this);
264 return NS_OK;
267 NS_IMETHODIMP
268 nsSHEntry::GetContentViewer(nsIContentViewer **aResult)
270 *aResult = mContentViewer;
271 NS_IF_ADDREF(*aResult);
272 return NS_OK;
275 NS_IMETHODIMP
276 nsSHEntry::GetAnyContentViewer(nsISHEntry **aOwnerEntry,
277 nsIContentViewer **aResult)
279 // Find a content viewer in the root node or any of its children,
280 // assuming that there is only one content viewer total in any one
281 // nsSHEntry tree
282 GetContentViewer(aResult);
283 if (*aResult) {
284 #ifdef DEBUG_PAGE_CACHE
285 printf("Found content viewer\n");
286 #endif
287 *aOwnerEntry = this;
288 NS_ADDREF(*aOwnerEntry);
289 return NS_OK;
291 // The root SHEntry doesn't have a ContentViewer, so check child nodes
292 for (PRInt32 i = 0; i < mChildren.Count(); i++) {
293 nsISHEntry* child = mChildren[i];
294 if (child) {
295 #ifdef DEBUG_PAGE_CACHE
296 printf("Evaluating SHEntry child %d\n", i);
297 #endif
298 child->GetAnyContentViewer(aOwnerEntry, aResult);
299 if (*aResult) {
300 return NS_OK;
304 return NS_OK;
307 NS_IMETHODIMP
308 nsSHEntry::SetSticky(PRBool aSticky)
310 mSticky = aSticky;
311 return NS_OK;
314 NS_IMETHODIMP
315 nsSHEntry::GetSticky(PRBool *aSticky)
317 *aSticky = mSticky;
318 return NS_OK;
321 NS_IMETHODIMP nsSHEntry::GetTitle(PRUnichar** aTitle)
323 // Check for empty title...
324 if (mTitle.IsEmpty() && mURI) {
325 // Default title is the URL.
326 nsCAutoString spec;
327 if (NS_SUCCEEDED(mURI->GetSpec(spec)))
328 AppendUTF8toUTF16(spec, mTitle);
331 *aTitle = ToNewUnicode(mTitle);
332 return NS_OK;
335 NS_IMETHODIMP nsSHEntry::SetTitle(const nsAString &aTitle)
337 mTitle = aTitle;
338 return NS_OK;
341 NS_IMETHODIMP nsSHEntry::GetPostData(nsIInputStream** aResult)
343 *aResult = mPostData;
344 NS_IF_ADDREF(*aResult);
345 return NS_OK;
348 NS_IMETHODIMP nsSHEntry::SetPostData(nsIInputStream* aPostData)
350 mPostData = aPostData;
351 return NS_OK;
354 NS_IMETHODIMP nsSHEntry::GetLayoutHistoryState(nsILayoutHistoryState** aResult)
356 *aResult = mLayoutHistoryState;
357 NS_IF_ADDREF(*aResult);
358 return NS_OK;
361 NS_IMETHODIMP nsSHEntry::SetLayoutHistoryState(nsILayoutHistoryState* aState)
363 mLayoutHistoryState = aState;
364 if (mLayoutHistoryState)
365 mLayoutHistoryState->SetScrollPositionOnly(!mSaveLayoutState);
367 return NS_OK;
370 NS_IMETHODIMP nsSHEntry::GetLoadType(PRUint32 * aResult)
372 *aResult = mLoadType;
373 return NS_OK;
376 NS_IMETHODIMP nsSHEntry::SetLoadType(PRUint32 aLoadType)
378 mLoadType = aLoadType;
379 return NS_OK;
382 NS_IMETHODIMP nsSHEntry::GetID(PRUint32 * aResult)
384 *aResult = mID;
385 return NS_OK;
388 NS_IMETHODIMP nsSHEntry::SetID(PRUint32 aID)
390 mID = aID;
391 return NS_OK;
394 NS_IMETHODIMP nsSHEntry::GetPageIdentifier(PRUint32 * aResult)
396 *aResult = mPageIdentifier;
397 return NS_OK;
400 NS_IMETHODIMP nsSHEntry::SetPageIdentifier(PRUint32 aPageIdentifier)
402 mPageIdentifier = aPageIdentifier;
403 return NS_OK;
406 NS_IMETHODIMP nsSHEntry::GetDocIdentifier(PRUint64 * aResult)
408 *aResult = mDocIdentifier;
409 return NS_OK;
412 NS_IMETHODIMP nsSHEntry::SetDocIdentifier(PRUint64 aDocIdentifier)
414 // This ensures that after a session restore, gEntryDocIdentifier is greater
415 // than all SHEntries' docIdentifiers, which ensures that we'll never repeat
416 // a doc identifier.
417 if (aDocIdentifier >= gEntryDocIdentifier)
418 gEntryDocIdentifier = aDocIdentifier + 1;
420 mDocIdentifier = aDocIdentifier;
421 return NS_OK;
424 NS_IMETHODIMP nsSHEntry::SetUniqueDocIdentifier()
426 mDocIdentifier = gEntryDocIdentifier++;
427 return NS_OK;
430 NS_IMETHODIMP nsSHEntry::GetIsSubFrame(PRBool * aFlag)
432 *aFlag = mIsFrameNavigation;
433 return NS_OK;
436 NS_IMETHODIMP nsSHEntry::SetIsSubFrame(PRBool aFlag)
438 mIsFrameNavigation = aFlag;
439 return NS_OK;
442 NS_IMETHODIMP nsSHEntry::GetCacheKey(nsISupports** aResult)
444 *aResult = mCacheKey;
445 NS_IF_ADDREF(*aResult);
446 return NS_OK;
449 NS_IMETHODIMP nsSHEntry::SetCacheKey(nsISupports* aCacheKey)
451 mCacheKey = aCacheKey;
452 return NS_OK;
455 NS_IMETHODIMP nsSHEntry::GetSaveLayoutStateFlag(PRBool * aFlag)
457 *aFlag = mSaveLayoutState;
458 return NS_OK;
461 NS_IMETHODIMP nsSHEntry::SetSaveLayoutStateFlag(PRBool aFlag)
463 mSaveLayoutState = aFlag;
464 if (mLayoutHistoryState)
465 mLayoutHistoryState->SetScrollPositionOnly(!aFlag);
467 return NS_OK;
470 NS_IMETHODIMP nsSHEntry::GetExpirationStatus(PRBool * aFlag)
472 *aFlag = mExpired;
473 return NS_OK;
476 NS_IMETHODIMP nsSHEntry::SetExpirationStatus(PRBool aFlag)
478 mExpired = aFlag;
479 return NS_OK;
482 NS_IMETHODIMP nsSHEntry::GetContentType(nsACString& aContentType)
484 aContentType = mContentType;
485 return NS_OK;
488 NS_IMETHODIMP nsSHEntry::SetContentType(const nsACString& aContentType)
490 mContentType = aContentType;
491 return NS_OK;
494 NS_IMETHODIMP
495 nsSHEntry::Create(nsIURI * aURI, const nsAString &aTitle,
496 nsIInputStream * aInputStream,
497 nsILayoutHistoryState * aLayoutHistoryState,
498 nsISupports * aCacheKey, const nsACString& aContentType,
499 nsISupports* aOwner,
500 PRUint64 aDocShellID, PRBool aDynamicCreation)
502 mURI = aURI;
503 mTitle = aTitle;
504 mPostData = aInputStream;
505 mCacheKey = aCacheKey;
506 mContentType = aContentType;
507 mOwner = aOwner;
508 mDocShellID = aDocShellID;
509 mDynamicallyCreated = aDynamicCreation;
511 // Set the LoadType by default to loadHistory during creation
512 mLoadType = (PRUint32) nsIDocShellLoadInfo::loadHistory;
514 // By default all entries are set false for subframe flag.
515 // nsDocShell::CloneAndReplace() which creates entries for
516 // all subframe navigations, sets the flag to true.
517 mIsFrameNavigation = PR_FALSE;
519 // By default we save LayoutHistoryState
520 mSaveLayoutState = PR_TRUE;
521 mLayoutHistoryState = aLayoutHistoryState;
523 //By default the page is not expired
524 mExpired = PR_FALSE;
526 return NS_OK;
529 NS_IMETHODIMP
530 nsSHEntry::Clone(nsISHEntry ** aResult)
532 *aResult = new nsSHEntry(*this);
533 if (!*aResult)
534 return NS_ERROR_OUT_OF_MEMORY;
535 NS_ADDREF(*aResult);
536 return NS_OK;
539 NS_IMETHODIMP
540 nsSHEntry::GetParent(nsISHEntry ** aResult)
542 NS_ENSURE_ARG_POINTER(aResult);
543 *aResult = mParent;
544 NS_IF_ADDREF(*aResult);
545 return NS_OK;
548 NS_IMETHODIMP
549 nsSHEntry::SetParent(nsISHEntry * aParent)
551 /* parent not Addrefed on purpose to avoid cyclic reference
552 * Null parent is OK
554 * XXX this method should not be scriptable if this is the case!!
556 mParent = aParent;
557 return NS_OK;
560 NS_IMETHODIMP
561 nsSHEntry::SetWindowState(nsISupports *aState)
563 mWindowState = aState;
564 return NS_OK;
567 NS_IMETHODIMP
568 nsSHEntry::GetWindowState(nsISupports **aState)
570 NS_IF_ADDREF(*aState = mWindowState);
571 return NS_OK;
574 NS_IMETHODIMP
575 nsSHEntry::SetViewerBounds(const nsIntRect &aBounds)
577 mViewerBounds = aBounds;
578 return NS_OK;
581 NS_IMETHODIMP
582 nsSHEntry::GetViewerBounds(nsIntRect &aBounds)
584 aBounds = mViewerBounds;
585 return NS_OK;
588 NS_IMETHODIMP
589 nsSHEntry::GetOwner(nsISupports **aOwner)
591 NS_IF_ADDREF(*aOwner = mOwner);
592 return NS_OK;
595 NS_IMETHODIMP
596 nsSHEntry::SetOwner(nsISupports *aOwner)
598 mOwner = aOwner;
599 return NS_OK;
602 //*****************************************************************************
603 // nsSHEntry: nsISHContainer
604 //*****************************************************************************
606 NS_IMETHODIMP
607 nsSHEntry::GetChildCount(PRInt32 * aCount)
609 *aCount = mChildren.Count();
610 return NS_OK;
613 NS_IMETHODIMP
614 nsSHEntry::AddChild(nsISHEntry * aChild, PRInt32 aOffset)
616 if (aChild) {
617 NS_ENSURE_SUCCESS(aChild->SetParent(this), NS_ERROR_FAILURE);
620 if (aOffset < 0) {
621 mChildren.AppendObject(aChild);
622 return NS_OK;
626 // Bug 52670: Ensure children are added in order.
628 // Later frames in the child list may load faster and get appended
629 // before earlier frames, causing session history to be scrambled.
630 // By growing the list here, they are added to the right position.
632 // Assert that aOffset will not be so high as to grow us a lot.
634 NS_ASSERTION(aOffset < (mChildren.Count()+1023), "Large frames array!\n");
636 #ifdef DEBUG
637 if (aOffset < mChildren.Count()) {
638 nsISHEntry* oldChild = mChildren.ObjectAt(aOffset);
639 if (aChild && oldChild && oldChild != aChild) {
640 PRBool dyn = PR_FALSE;
641 oldChild->IsDynamicallyAdded(&dyn);
642 NS_WARN_IF_FALSE(dyn, "Adding child where we already have a child? "
643 "This may misbehave");
646 #endif
648 // InsertObjectAt allows only appending one object.
649 // If aOffset is larger than Count(), we must first manually
650 // set the capacity.
651 if (aOffset > mChildren.Count()) {
652 mChildren.SetCount(aOffset);
654 if (!mChildren.InsertObjectAt(aChild, aOffset)) {
655 NS_WARNING("Adding a child failed!");
656 aChild->SetParent(nsnull);
657 return NS_ERROR_FAILURE;
660 return NS_OK;
663 NS_IMETHODIMP
664 nsSHEntry::RemoveChild(nsISHEntry * aChild)
666 NS_ENSURE_TRUE(aChild, NS_ERROR_FAILURE);
667 PRBool childRemoved = PR_FALSE;
668 PRBool dynamic = PR_FALSE;
669 aChild->IsDynamicallyAdded(&dynamic);
670 if (dynamic) {
671 childRemoved = mChildren.RemoveObject(aChild);
672 } else {
673 PRInt32 index = mChildren.IndexOfObject(aChild);
674 if (index >= 0) {
675 childRemoved = mChildren.ReplaceObjectAt(nsnull, index);
678 if (childRemoved)
679 aChild->SetParent(nsnull);
680 return NS_OK;
683 NS_IMETHODIMP
684 nsSHEntry::GetChildAt(PRInt32 aIndex, nsISHEntry ** aResult)
686 if (aIndex >= 0 && aIndex < mChildren.Count()) {
687 *aResult = mChildren[aIndex];
688 // yes, mChildren can have holes in it. AddChild's offset parameter makes
689 // that possible.
690 NS_IF_ADDREF(*aResult);
691 } else {
692 *aResult = nsnull;
694 return NS_OK;
697 NS_IMETHODIMP
698 nsSHEntry::AddChildShell(nsIDocShellTreeItem *aShell)
700 NS_ASSERTION(aShell, "Null child shell added to history entry");
701 mChildShells.AppendObject(aShell);
702 return NS_OK;
705 NS_IMETHODIMP
706 nsSHEntry::ChildShellAt(PRInt32 aIndex, nsIDocShellTreeItem **aShell)
708 NS_IF_ADDREF(*aShell = mChildShells.SafeObjectAt(aIndex));
709 return NS_OK;
712 NS_IMETHODIMP
713 nsSHEntry::ClearChildShells()
715 mChildShells.Clear();
716 return NS_OK;
719 NS_IMETHODIMP
720 nsSHEntry::GetRefreshURIList(nsISupportsArray **aList)
722 NS_IF_ADDREF(*aList = mRefreshURIList);
723 return NS_OK;
726 NS_IMETHODIMP
727 nsSHEntry::SetRefreshURIList(nsISupportsArray *aList)
729 mRefreshURIList = aList;
730 return NS_OK;
733 NS_IMETHODIMP
734 nsSHEntry::SyncPresentationState()
736 if (mContentViewer && mWindowState) {
737 // If we have a content viewer and a window state, we should be ok.
738 return NS_OK;
741 DropPresentationState();
743 return NS_OK;
746 void
747 nsSHEntry::DropPresentationState()
749 nsRefPtr<nsSHEntry> kungFuDeathGrip = this;
751 if (mDocument) {
752 mDocument->SetBFCacheEntry(nsnull);
753 mDocument->RemoveMutationObserver(this);
754 mDocument = nsnull;
756 if (mContentViewer)
757 mContentViewer->ClearHistoryEntry();
759 StopTrackingEntry(this);
760 mContentViewer = nsnull;
761 mSticky = PR_TRUE;
762 mWindowState = nsnull;
763 mViewerBounds.SetRect(0, 0, 0, 0);
764 mChildShells.Clear();
765 mRefreshURIList = nsnull;
768 void
769 nsSHEntry::Expire()
771 // This entry has timed out. If we still have a content viewer, we need to
772 // get it evicted.
773 if (!mContentViewer)
774 return;
775 nsCOMPtr<nsISupports> container;
776 mContentViewer->GetContainer(getter_AddRefs(container));
777 nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container);
778 if (!treeItem)
779 return;
780 // We need to find the root DocShell since only that object has an
781 // SHistory and we need the SHistory to evict content viewers
782 nsCOMPtr<nsIDocShellTreeItem> root;
783 treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
784 nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
785 nsCOMPtr<nsISHistory> history;
786 webNav->GetSessionHistory(getter_AddRefs(history));
787 nsCOMPtr<nsISHistoryInternal> historyInt = do_QueryInterface(history);
788 if (!historyInt)
789 return;
790 historyInt->EvictExpiredContentViewerForEntry(this);
793 //*****************************************************************************
794 // nsSHEntry: nsIMutationObserver
795 //*****************************************************************************
797 void
798 nsSHEntry::NodeWillBeDestroyed(const nsINode* aNode)
800 NS_NOTREACHED("Document destroyed while we're holding a strong ref to it");
803 void
804 nsSHEntry::CharacterDataWillChange(nsIDocument* aDocument,
805 nsIContent* aContent,
806 CharacterDataChangeInfo* aInfo)
810 void
811 nsSHEntry::CharacterDataChanged(nsIDocument* aDocument,
812 nsIContent* aContent,
813 CharacterDataChangeInfo* aInfo)
815 RemoveFromBFCacheAsync();
818 void
819 nsSHEntry::AttributeWillChange(nsIDocument* aDocument,
820 dom::Element* aContent,
821 PRInt32 aNameSpaceID,
822 nsIAtom* aAttribute,
823 PRInt32 aModType)
827 void
828 nsSHEntry::AttributeChanged(nsIDocument* aDocument,
829 dom::Element* aElement,
830 PRInt32 aNameSpaceID,
831 nsIAtom* aAttribute,
832 PRInt32 aModType)
834 RemoveFromBFCacheAsync();
837 void
838 nsSHEntry::ContentAppended(nsIDocument* aDocument,
839 nsIContent* aContainer,
840 nsIContent* aFirstNewContent,
841 PRInt32 /* unused */)
843 RemoveFromBFCacheAsync();
846 void
847 nsSHEntry::ContentInserted(nsIDocument* aDocument,
848 nsIContent* aContainer,
849 nsIContent* aChild,
850 PRInt32 /* unused */)
852 RemoveFromBFCacheAsync();
855 void
856 nsSHEntry::ContentRemoved(nsIDocument* aDocument,
857 nsIContent* aContainer,
858 nsIContent* aChild,
859 PRInt32 aIndexInContainer,
860 nsIContent* aPreviousSibling)
862 RemoveFromBFCacheAsync();
865 void
866 nsSHEntry::ParentChainChanged(nsIContent *aContent)
870 class DestroyViewerEvent : public nsRunnable
872 public:
873 DestroyViewerEvent(nsIContentViewer* aViewer, nsIDocument* aDocument)
874 : mViewer(aViewer),
875 mDocument(aDocument)
878 NS_IMETHOD Run()
880 if (mViewer)
881 mViewer->Destroy();
882 return NS_OK;
885 nsCOMPtr<nsIContentViewer> mViewer;
886 nsCOMPtr<nsIDocument> mDocument;
889 void
890 nsSHEntry::RemoveFromBFCacheSync()
892 NS_ASSERTION(mContentViewer && mDocument,
893 "we're not in the bfcache!");
895 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
896 DropPresentationState();
898 // Warning! The call to DropPresentationState could have dropped the last
899 // reference to this nsSHEntry, so no accessing members beyond here.
901 if (viewer) {
902 viewer->Destroy();
906 void
907 nsSHEntry::RemoveFromBFCacheAsync()
909 NS_ASSERTION(mContentViewer && mDocument,
910 "we're not in the bfcache!");
912 // Release the reference to the contentviewer asynchronously so that the
913 // document doesn't get nuked mid-mutation.
915 nsCOMPtr<nsIRunnable> evt =
916 new DestroyViewerEvent(mContentViewer, mDocument);
917 nsresult rv = NS_DispatchToCurrentThread(evt);
918 if (NS_FAILED(rv)) {
919 NS_WARNING("failed to dispatch DestroyViewerEvent");
921 else {
922 // Drop presentation. Also ensures that we don't post more then one
923 // PLEvent. Only do this if we succeeded in posting the event since
924 // otherwise the document could be torn down mid mutation causing crashes.
925 DropPresentationState();
927 // Warning! The call to DropPresentationState could have dropped the last
928 // reference to this nsSHEntry, so no accessing members beyond here.
931 nsDocShellEditorData*
932 nsSHEntry::ForgetEditorData()
934 return mEditorData.forget();
937 void
938 nsSHEntry::SetEditorData(nsDocShellEditorData* aData)
940 NS_ASSERTION(!(aData && mEditorData),
941 "We're going to overwrite an owning ref!");
942 if (mEditorData != aData)
943 mEditorData = aData;
946 PRBool
947 nsSHEntry::HasDetachedEditor()
949 return mEditorData != nsnull;
952 NS_IMETHODIMP
953 nsSHEntry::GetStateData(nsAString &aStateData)
955 aStateData.Assign(mStateData);
956 return NS_OK;
959 NS_IMETHODIMP
960 nsSHEntry::SetStateData(const nsAString &aDataStr)
962 mStateData.Assign(aDataStr);
963 return NS_OK;
966 NS_IMETHODIMP
967 nsSHEntry::IsDynamicallyAdded(PRBool* aAdded)
969 *aAdded = mDynamicallyCreated;
970 return NS_OK;
973 NS_IMETHODIMP
974 nsSHEntry::HasDynamicallyAddedChild(PRBool* aAdded)
976 *aAdded = PR_FALSE;
977 for (PRInt32 i = 0; i < mChildren.Count(); ++i) {
978 nsISHEntry* entry = mChildren[i];
979 if (entry) {
980 entry->IsDynamicallyAdded(aAdded);
981 if (*aAdded) {
982 break;
986 return NS_OK;
989 NS_IMETHODIMP
990 nsSHEntry::GetDocshellID(PRUint64* aID)
992 *aID = mDocShellID;
993 return NS_OK;
996 NS_IMETHODIMP
997 nsSHEntry::SetDocshellID(PRUint64 aID)
999 mDocShellID = aID;
1000 return NS_OK;
1004 NS_IMETHODIMP
1005 nsSHEntry::GetLastTouched(PRUint32 *aLastTouched)
1007 *aLastTouched = mLastTouched;
1008 return NS_OK;
1011 NS_IMETHODIMP
1012 nsSHEntry::SetLastTouched(PRUint32 aLastTouched)
1014 mLastTouched = aLastTouched;
1015 return NS_OK;