1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/docshell/shistory/src/nsSHEntryShared.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,368 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "nsSHEntryShared.h" 1.9 + 1.10 +#include "nsIDOMDocument.h" 1.11 +#include "nsISHistory.h" 1.12 +#include "nsISHistoryInternal.h" 1.13 +#include "nsIDocument.h" 1.14 +#include "nsIWebNavigation.h" 1.15 +#include "nsIContentViewer.h" 1.16 +#include "nsIDocShell.h" 1.17 +#include "nsIDocShellTreeItem.h" 1.18 +#include "nsDocShellEditorData.h" 1.19 +#include "nsThreadUtils.h" 1.20 +#include "nsILayoutHistoryState.h" 1.21 +#include "mozilla/Attributes.h" 1.22 +#include "nsISupportsArray.h" 1.23 + 1.24 +namespace dom = mozilla::dom; 1.25 + 1.26 +namespace { 1.27 + 1.28 +uint64_t gSHEntrySharedID = 0; 1.29 + 1.30 +} // anonymous namespace 1.31 + 1.32 +// Hardcode this to time out unused content viewers after 30 minutes 1.33 +// XXX jlebar shouldn't this be a pref? 1.34 +#define CONTENT_VIEWER_TIMEOUT_SECONDS (30*60) 1.35 + 1.36 +typedef nsExpirationTracker<nsSHEntryShared, 3> HistoryTrackerBase; 1.37 +class HistoryTracker MOZ_FINAL : public HistoryTrackerBase { 1.38 +public: 1.39 + // Expire cached contentviewers after 20-30 minutes in the cache. 1.40 + HistoryTracker() 1.41 + : HistoryTrackerBase(1000 * CONTENT_VIEWER_TIMEOUT_SECONDS / 2) 1.42 + { 1.43 + } 1.44 + 1.45 +protected: 1.46 + virtual void NotifyExpired(nsSHEntryShared *aObj) { 1.47 + RemoveObject(aObj); 1.48 + aObj->Expire(); 1.49 + } 1.50 +}; 1.51 + 1.52 +static HistoryTracker *gHistoryTracker = nullptr; 1.53 + 1.54 +void 1.55 +nsSHEntryShared::Startup() 1.56 +{ 1.57 + gHistoryTracker = new HistoryTracker(); 1.58 +} 1.59 + 1.60 +void 1.61 +nsSHEntryShared::Shutdown() 1.62 +{ 1.63 + delete gHistoryTracker; 1.64 + gHistoryTracker = nullptr; 1.65 +} 1.66 + 1.67 +nsSHEntryShared::nsSHEntryShared() 1.68 + : mDocShellID(0) 1.69 + , mIsFrameNavigation(false) 1.70 + , mSaveLayoutState(true) 1.71 + , mSticky(true) 1.72 + , mDynamicallyCreated(false) 1.73 + , mLastTouched(0) 1.74 + , mID(gSHEntrySharedID++) 1.75 + , mExpired(false) 1.76 + , mViewerBounds(0, 0, 0, 0) 1.77 +{ 1.78 +} 1.79 + 1.80 +nsSHEntryShared::~nsSHEntryShared() 1.81 +{ 1.82 + RemoveFromExpirationTracker(); 1.83 + 1.84 +#ifdef DEBUG 1.85 + // Check that we're not still on track to expire. We shouldn't be, because 1.86 + // we just removed ourselves! 1.87 + nsExpirationTracker<nsSHEntryShared, 3>::Iterator 1.88 + iterator(gHistoryTracker); 1.89 + 1.90 + nsSHEntryShared *elem; 1.91 + while ((elem = iterator.Next()) != nullptr) { 1.92 + NS_ASSERTION(elem != this, "Found dead entry still in the tracker!"); 1.93 + } 1.94 +#endif 1.95 + 1.96 + if (mContentViewer) { 1.97 + RemoveFromBFCacheSync(); 1.98 + } 1.99 +} 1.100 + 1.101 +NS_IMPL_ISUPPORTS(nsSHEntryShared, nsIBFCacheEntry, nsIMutationObserver) 1.102 + 1.103 +already_AddRefed<nsSHEntryShared> 1.104 +nsSHEntryShared::Duplicate(nsSHEntryShared *aEntry) 1.105 +{ 1.106 + nsRefPtr<nsSHEntryShared> newEntry = new nsSHEntryShared(); 1.107 + 1.108 + newEntry->mDocShellID = aEntry->mDocShellID; 1.109 + newEntry->mChildShells.AppendObjects(aEntry->mChildShells); 1.110 + newEntry->mOwner = aEntry->mOwner; 1.111 + newEntry->mContentType.Assign(aEntry->mContentType); 1.112 + newEntry->mIsFrameNavigation = aEntry->mIsFrameNavigation; 1.113 + newEntry->mSaveLayoutState = aEntry->mSaveLayoutState; 1.114 + newEntry->mSticky = aEntry->mSticky; 1.115 + newEntry->mDynamicallyCreated = aEntry->mDynamicallyCreated; 1.116 + newEntry->mCacheKey = aEntry->mCacheKey; 1.117 + newEntry->mLastTouched = aEntry->mLastTouched; 1.118 + 1.119 + return newEntry.forget(); 1.120 +} 1.121 + 1.122 +void nsSHEntryShared::RemoveFromExpirationTracker() 1.123 +{ 1.124 + if (GetExpirationState()->IsTracked()) { 1.125 + gHistoryTracker->RemoveObject(this); 1.126 + } 1.127 +} 1.128 + 1.129 +nsresult 1.130 +nsSHEntryShared::SyncPresentationState() 1.131 +{ 1.132 + if (mContentViewer && mWindowState) { 1.133 + // If we have a content viewer and a window state, we should be ok. 1.134 + return NS_OK; 1.135 + } 1.136 + 1.137 + DropPresentationState(); 1.138 + 1.139 + return NS_OK; 1.140 +} 1.141 + 1.142 +void 1.143 +nsSHEntryShared::DropPresentationState() 1.144 +{ 1.145 + nsRefPtr<nsSHEntryShared> kungFuDeathGrip = this; 1.146 + 1.147 + if (mDocument) { 1.148 + mDocument->SetBFCacheEntry(nullptr); 1.149 + mDocument->RemoveMutationObserver(this); 1.150 + mDocument = nullptr; 1.151 + } 1.152 + if (mContentViewer) { 1.153 + mContentViewer->ClearHistoryEntry(); 1.154 + } 1.155 + 1.156 + RemoveFromExpirationTracker(); 1.157 + mContentViewer = nullptr; 1.158 + mSticky = true; 1.159 + mWindowState = nullptr; 1.160 + mViewerBounds.SetRect(0, 0, 0, 0); 1.161 + mChildShells.Clear(); 1.162 + mRefreshURIList = nullptr; 1.163 + mEditorData = nullptr; 1.164 +} 1.165 + 1.166 +void 1.167 +nsSHEntryShared::Expire() 1.168 +{ 1.169 + // This entry has timed out. If we still have a content viewer, we need to 1.170 + // evict it. 1.171 + if (!mContentViewer) { 1.172 + return; 1.173 + } 1.174 + nsCOMPtr<nsIDocShell> container; 1.175 + mContentViewer->GetContainer(getter_AddRefs(container)); 1.176 + nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container); 1.177 + if (!treeItem) { 1.178 + return; 1.179 + } 1.180 + // We need to find the root DocShell since only that object has an 1.181 + // SHistory and we need the SHistory to evict content viewers 1.182 + nsCOMPtr<nsIDocShellTreeItem> root; 1.183 + treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root)); 1.184 + nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root); 1.185 + nsCOMPtr<nsISHistory> history; 1.186 + webNav->GetSessionHistory(getter_AddRefs(history)); 1.187 + nsCOMPtr<nsISHistoryInternal> historyInt = do_QueryInterface(history); 1.188 + if (!historyInt) { 1.189 + return; 1.190 + } 1.191 + historyInt->EvictExpiredContentViewerForEntry(this); 1.192 +} 1.193 + 1.194 +nsresult 1.195 +nsSHEntryShared::SetContentViewer(nsIContentViewer *aViewer) 1.196 +{ 1.197 + NS_PRECONDITION(!aViewer || !mContentViewer, 1.198 + "SHEntryShared already contains viewer"); 1.199 + 1.200 + if (mContentViewer || !aViewer) { 1.201 + DropPresentationState(); 1.202 + } 1.203 + 1.204 + mContentViewer = aViewer; 1.205 + 1.206 + if (mContentViewer) { 1.207 + gHistoryTracker->AddObject(this); 1.208 + 1.209 + nsCOMPtr<nsIDOMDocument> domDoc; 1.210 + mContentViewer->GetDOMDocument(getter_AddRefs(domDoc)); 1.211 + // Store observed document in strong pointer in case it is removed from 1.212 + // the contentviewer 1.213 + mDocument = do_QueryInterface(domDoc); 1.214 + if (mDocument) { 1.215 + mDocument->SetBFCacheEntry(this); 1.216 + mDocument->AddMutationObserver(this); 1.217 + } 1.218 + } 1.219 + 1.220 + return NS_OK; 1.221 +} 1.222 + 1.223 +nsresult 1.224 +nsSHEntryShared::RemoveFromBFCacheSync() 1.225 +{ 1.226 + NS_ASSERTION(mContentViewer && mDocument, 1.227 + "we're not in the bfcache!"); 1.228 + 1.229 + nsCOMPtr<nsIContentViewer> viewer = mContentViewer; 1.230 + DropPresentationState(); 1.231 + 1.232 + // Warning! The call to DropPresentationState could have dropped the last 1.233 + // reference to this object, so don't access members beyond here. 1.234 + 1.235 + if (viewer) { 1.236 + viewer->Destroy(); 1.237 + } 1.238 + 1.239 + return NS_OK; 1.240 +} 1.241 + 1.242 +class DestroyViewerEvent : public nsRunnable 1.243 +{ 1.244 +public: 1.245 + DestroyViewerEvent(nsIContentViewer* aViewer, nsIDocument* aDocument) 1.246 + : mViewer(aViewer), 1.247 + mDocument(aDocument) 1.248 + {} 1.249 + 1.250 + NS_IMETHOD Run() 1.251 + { 1.252 + if (mViewer) { 1.253 + mViewer->Destroy(); 1.254 + } 1.255 + return NS_OK; 1.256 + } 1.257 + 1.258 + nsCOMPtr<nsIContentViewer> mViewer; 1.259 + nsCOMPtr<nsIDocument> mDocument; 1.260 +}; 1.261 + 1.262 +nsresult 1.263 +nsSHEntryShared::RemoveFromBFCacheAsync() 1.264 +{ 1.265 + NS_ASSERTION(mContentViewer && mDocument, 1.266 + "we're not in the bfcache!"); 1.267 + 1.268 + // Release the reference to the contentviewer asynchronously so that the 1.269 + // document doesn't get nuked mid-mutation. 1.270 + 1.271 + nsCOMPtr<nsIRunnable> evt = 1.272 + new DestroyViewerEvent(mContentViewer, mDocument); 1.273 + nsresult rv = NS_DispatchToCurrentThread(evt); 1.274 + if (NS_FAILED(rv)) { 1.275 + NS_WARNING("failed to dispatch DestroyViewerEvent"); 1.276 + } else { 1.277 + // Drop presentation. Only do this if we succeeded in posting the event 1.278 + // since otherwise the document could be torn down mid-mutation, causing 1.279 + // crashes. 1.280 + DropPresentationState(); 1.281 + } 1.282 + 1.283 + // Careful! The call to DropPresentationState could have dropped the last 1.284 + // reference to this nsSHEntryShared, so don't access members beyond here. 1.285 + 1.286 + return NS_OK; 1.287 +} 1.288 + 1.289 +nsresult 1.290 +nsSHEntryShared::GetID(uint64_t *aID) 1.291 +{ 1.292 + *aID = mID; 1.293 + return NS_OK; 1.294 +} 1.295 + 1.296 +//***************************************************************************** 1.297 +// nsSHEntryShared: nsIMutationObserver 1.298 +//***************************************************************************** 1.299 + 1.300 +void 1.301 +nsSHEntryShared::NodeWillBeDestroyed(const nsINode* aNode) 1.302 +{ 1.303 + NS_NOTREACHED("Document destroyed while we're holding a strong ref to it"); 1.304 +} 1.305 + 1.306 +void 1.307 +nsSHEntryShared::CharacterDataWillChange(nsIDocument* aDocument, 1.308 + nsIContent* aContent, 1.309 + CharacterDataChangeInfo* aInfo) 1.310 +{ 1.311 +} 1.312 + 1.313 +void 1.314 +nsSHEntryShared::CharacterDataChanged(nsIDocument* aDocument, 1.315 + nsIContent* aContent, 1.316 + CharacterDataChangeInfo* aInfo) 1.317 +{ 1.318 + RemoveFromBFCacheAsync(); 1.319 +} 1.320 + 1.321 +void 1.322 +nsSHEntryShared::AttributeWillChange(nsIDocument* aDocument, 1.323 + dom::Element* aContent, 1.324 + int32_t aNameSpaceID, 1.325 + nsIAtom* aAttribute, 1.326 + int32_t aModType) 1.327 +{ 1.328 +} 1.329 + 1.330 +void 1.331 +nsSHEntryShared::AttributeChanged(nsIDocument* aDocument, 1.332 + dom::Element* aElement, 1.333 + int32_t aNameSpaceID, 1.334 + nsIAtom* aAttribute, 1.335 + int32_t aModType) 1.336 +{ 1.337 + RemoveFromBFCacheAsync(); 1.338 +} 1.339 + 1.340 +void 1.341 +nsSHEntryShared::ContentAppended(nsIDocument* aDocument, 1.342 + nsIContent* aContainer, 1.343 + nsIContent* aFirstNewContent, 1.344 + int32_t /* unused */) 1.345 +{ 1.346 + RemoveFromBFCacheAsync(); 1.347 +} 1.348 + 1.349 +void 1.350 +nsSHEntryShared::ContentInserted(nsIDocument* aDocument, 1.351 + nsIContent* aContainer, 1.352 + nsIContent* aChild, 1.353 + int32_t /* unused */) 1.354 +{ 1.355 + RemoveFromBFCacheAsync(); 1.356 +} 1.357 + 1.358 +void 1.359 +nsSHEntryShared::ContentRemoved(nsIDocument* aDocument, 1.360 + nsIContent* aContainer, 1.361 + nsIContent* aChild, 1.362 + int32_t aIndexInContainer, 1.363 + nsIContent* aPreviousSibling) 1.364 +{ 1.365 + RemoveFromBFCacheAsync(); 1.366 +} 1.367 + 1.368 +void 1.369 +nsSHEntryShared::ParentChainChanged(nsIContent *aContent) 1.370 +{ 1.371 +}