Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "nsSHEntryShared.h"
7 #include "nsIDOMDocument.h"
8 #include "nsISHistory.h"
9 #include "nsISHistoryInternal.h"
10 #include "nsIDocument.h"
11 #include "nsIWebNavigation.h"
12 #include "nsIContentViewer.h"
13 #include "nsIDocShell.h"
14 #include "nsIDocShellTreeItem.h"
15 #include "nsDocShellEditorData.h"
16 #include "nsThreadUtils.h"
17 #include "nsILayoutHistoryState.h"
18 #include "mozilla/Attributes.h"
19 #include "nsISupportsArray.h"
21 namespace dom = mozilla::dom;
23 namespace {
25 uint64_t gSHEntrySharedID = 0;
27 } // anonymous namespace
29 // Hardcode this to time out unused content viewers after 30 minutes
30 // XXX jlebar shouldn't this be a pref?
31 #define CONTENT_VIEWER_TIMEOUT_SECONDS (30*60)
33 typedef nsExpirationTracker<nsSHEntryShared, 3> HistoryTrackerBase;
34 class HistoryTracker MOZ_FINAL : public HistoryTrackerBase {
35 public:
36 // Expire cached contentviewers after 20-30 minutes in the cache.
37 HistoryTracker()
38 : HistoryTrackerBase(1000 * CONTENT_VIEWER_TIMEOUT_SECONDS / 2)
39 {
40 }
42 protected:
43 virtual void NotifyExpired(nsSHEntryShared *aObj) {
44 RemoveObject(aObj);
45 aObj->Expire();
46 }
47 };
49 static HistoryTracker *gHistoryTracker = nullptr;
51 void
52 nsSHEntryShared::Startup()
53 {
54 gHistoryTracker = new HistoryTracker();
55 }
57 void
58 nsSHEntryShared::Shutdown()
59 {
60 delete gHistoryTracker;
61 gHistoryTracker = nullptr;
62 }
64 nsSHEntryShared::nsSHEntryShared()
65 : mDocShellID(0)
66 , mIsFrameNavigation(false)
67 , mSaveLayoutState(true)
68 , mSticky(true)
69 , mDynamicallyCreated(false)
70 , mLastTouched(0)
71 , mID(gSHEntrySharedID++)
72 , mExpired(false)
73 , mViewerBounds(0, 0, 0, 0)
74 {
75 }
77 nsSHEntryShared::~nsSHEntryShared()
78 {
79 RemoveFromExpirationTracker();
81 #ifdef DEBUG
82 // Check that we're not still on track to expire. We shouldn't be, because
83 // we just removed ourselves!
84 nsExpirationTracker<nsSHEntryShared, 3>::Iterator
85 iterator(gHistoryTracker);
87 nsSHEntryShared *elem;
88 while ((elem = iterator.Next()) != nullptr) {
89 NS_ASSERTION(elem != this, "Found dead entry still in the tracker!");
90 }
91 #endif
93 if (mContentViewer) {
94 RemoveFromBFCacheSync();
95 }
96 }
98 NS_IMPL_ISUPPORTS(nsSHEntryShared, nsIBFCacheEntry, nsIMutationObserver)
100 already_AddRefed<nsSHEntryShared>
101 nsSHEntryShared::Duplicate(nsSHEntryShared *aEntry)
102 {
103 nsRefPtr<nsSHEntryShared> newEntry = new nsSHEntryShared();
105 newEntry->mDocShellID = aEntry->mDocShellID;
106 newEntry->mChildShells.AppendObjects(aEntry->mChildShells);
107 newEntry->mOwner = aEntry->mOwner;
108 newEntry->mContentType.Assign(aEntry->mContentType);
109 newEntry->mIsFrameNavigation = aEntry->mIsFrameNavigation;
110 newEntry->mSaveLayoutState = aEntry->mSaveLayoutState;
111 newEntry->mSticky = aEntry->mSticky;
112 newEntry->mDynamicallyCreated = aEntry->mDynamicallyCreated;
113 newEntry->mCacheKey = aEntry->mCacheKey;
114 newEntry->mLastTouched = aEntry->mLastTouched;
116 return newEntry.forget();
117 }
119 void nsSHEntryShared::RemoveFromExpirationTracker()
120 {
121 if (GetExpirationState()->IsTracked()) {
122 gHistoryTracker->RemoveObject(this);
123 }
124 }
126 nsresult
127 nsSHEntryShared::SyncPresentationState()
128 {
129 if (mContentViewer && mWindowState) {
130 // If we have a content viewer and a window state, we should be ok.
131 return NS_OK;
132 }
134 DropPresentationState();
136 return NS_OK;
137 }
139 void
140 nsSHEntryShared::DropPresentationState()
141 {
142 nsRefPtr<nsSHEntryShared> kungFuDeathGrip = this;
144 if (mDocument) {
145 mDocument->SetBFCacheEntry(nullptr);
146 mDocument->RemoveMutationObserver(this);
147 mDocument = nullptr;
148 }
149 if (mContentViewer) {
150 mContentViewer->ClearHistoryEntry();
151 }
153 RemoveFromExpirationTracker();
154 mContentViewer = nullptr;
155 mSticky = true;
156 mWindowState = nullptr;
157 mViewerBounds.SetRect(0, 0, 0, 0);
158 mChildShells.Clear();
159 mRefreshURIList = nullptr;
160 mEditorData = nullptr;
161 }
163 void
164 nsSHEntryShared::Expire()
165 {
166 // This entry has timed out. If we still have a content viewer, we need to
167 // evict it.
168 if (!mContentViewer) {
169 return;
170 }
171 nsCOMPtr<nsIDocShell> container;
172 mContentViewer->GetContainer(getter_AddRefs(container));
173 nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container);
174 if (!treeItem) {
175 return;
176 }
177 // We need to find the root DocShell since only that object has an
178 // SHistory and we need the SHistory to evict content viewers
179 nsCOMPtr<nsIDocShellTreeItem> root;
180 treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
181 nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
182 nsCOMPtr<nsISHistory> history;
183 webNav->GetSessionHistory(getter_AddRefs(history));
184 nsCOMPtr<nsISHistoryInternal> historyInt = do_QueryInterface(history);
185 if (!historyInt) {
186 return;
187 }
188 historyInt->EvictExpiredContentViewerForEntry(this);
189 }
191 nsresult
192 nsSHEntryShared::SetContentViewer(nsIContentViewer *aViewer)
193 {
194 NS_PRECONDITION(!aViewer || !mContentViewer,
195 "SHEntryShared already contains viewer");
197 if (mContentViewer || !aViewer) {
198 DropPresentationState();
199 }
201 mContentViewer = aViewer;
203 if (mContentViewer) {
204 gHistoryTracker->AddObject(this);
206 nsCOMPtr<nsIDOMDocument> domDoc;
207 mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
208 // Store observed document in strong pointer in case it is removed from
209 // the contentviewer
210 mDocument = do_QueryInterface(domDoc);
211 if (mDocument) {
212 mDocument->SetBFCacheEntry(this);
213 mDocument->AddMutationObserver(this);
214 }
215 }
217 return NS_OK;
218 }
220 nsresult
221 nsSHEntryShared::RemoveFromBFCacheSync()
222 {
223 NS_ASSERTION(mContentViewer && mDocument,
224 "we're not in the bfcache!");
226 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
227 DropPresentationState();
229 // Warning! The call to DropPresentationState could have dropped the last
230 // reference to this object, so don't access members beyond here.
232 if (viewer) {
233 viewer->Destroy();
234 }
236 return NS_OK;
237 }
239 class DestroyViewerEvent : public nsRunnable
240 {
241 public:
242 DestroyViewerEvent(nsIContentViewer* aViewer, nsIDocument* aDocument)
243 : mViewer(aViewer),
244 mDocument(aDocument)
245 {}
247 NS_IMETHOD Run()
248 {
249 if (mViewer) {
250 mViewer->Destroy();
251 }
252 return NS_OK;
253 }
255 nsCOMPtr<nsIContentViewer> mViewer;
256 nsCOMPtr<nsIDocument> mDocument;
257 };
259 nsresult
260 nsSHEntryShared::RemoveFromBFCacheAsync()
261 {
262 NS_ASSERTION(mContentViewer && mDocument,
263 "we're not in the bfcache!");
265 // Release the reference to the contentviewer asynchronously so that the
266 // document doesn't get nuked mid-mutation.
268 nsCOMPtr<nsIRunnable> evt =
269 new DestroyViewerEvent(mContentViewer, mDocument);
270 nsresult rv = NS_DispatchToCurrentThread(evt);
271 if (NS_FAILED(rv)) {
272 NS_WARNING("failed to dispatch DestroyViewerEvent");
273 } else {
274 // Drop presentation. Only do this if we succeeded in posting the event
275 // since otherwise the document could be torn down mid-mutation, causing
276 // crashes.
277 DropPresentationState();
278 }
280 // Careful! The call to DropPresentationState could have dropped the last
281 // reference to this nsSHEntryShared, so don't access members beyond here.
283 return NS_OK;
284 }
286 nsresult
287 nsSHEntryShared::GetID(uint64_t *aID)
288 {
289 *aID = mID;
290 return NS_OK;
291 }
293 //*****************************************************************************
294 // nsSHEntryShared: nsIMutationObserver
295 //*****************************************************************************
297 void
298 nsSHEntryShared::NodeWillBeDestroyed(const nsINode* aNode)
299 {
300 NS_NOTREACHED("Document destroyed while we're holding a strong ref to it");
301 }
303 void
304 nsSHEntryShared::CharacterDataWillChange(nsIDocument* aDocument,
305 nsIContent* aContent,
306 CharacterDataChangeInfo* aInfo)
307 {
308 }
310 void
311 nsSHEntryShared::CharacterDataChanged(nsIDocument* aDocument,
312 nsIContent* aContent,
313 CharacterDataChangeInfo* aInfo)
314 {
315 RemoveFromBFCacheAsync();
316 }
318 void
319 nsSHEntryShared::AttributeWillChange(nsIDocument* aDocument,
320 dom::Element* aContent,
321 int32_t aNameSpaceID,
322 nsIAtom* aAttribute,
323 int32_t aModType)
324 {
325 }
327 void
328 nsSHEntryShared::AttributeChanged(nsIDocument* aDocument,
329 dom::Element* aElement,
330 int32_t aNameSpaceID,
331 nsIAtom* aAttribute,
332 int32_t aModType)
333 {
334 RemoveFromBFCacheAsync();
335 }
337 void
338 nsSHEntryShared::ContentAppended(nsIDocument* aDocument,
339 nsIContent* aContainer,
340 nsIContent* aFirstNewContent,
341 int32_t /* unused */)
342 {
343 RemoveFromBFCacheAsync();
344 }
346 void
347 nsSHEntryShared::ContentInserted(nsIDocument* aDocument,
348 nsIContent* aContainer,
349 nsIContent* aChild,
350 int32_t /* unused */)
351 {
352 RemoveFromBFCacheAsync();
353 }
355 void
356 nsSHEntryShared::ContentRemoved(nsIDocument* aDocument,
357 nsIContent* aContainer,
358 nsIContent* aChild,
359 int32_t aIndexInContainer,
360 nsIContent* aPreviousSibling)
361 {
362 RemoveFromBFCacheAsync();
363 }
365 void
366 nsSHEntryShared::ParentChainChanged(nsIContent *aContent)
367 {
368 }