Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "nsSHEntryShared.h"
8 :
9 : #include "nsArray.h"
10 : #include "nsDocShellEditorData.h"
11 : #include "nsIContentViewer.h"
12 : #include "nsIDocShell.h"
13 : #include "nsIDocShellTreeItem.h"
14 : #include "nsIDocument.h"
15 : #include "nsIDOMDocument.h"
16 : #include "nsILayoutHistoryState.h"
17 : #include "nsISHistory.h"
18 : #include "nsISHistoryInternal.h"
19 : #include "nsIWebNavigation.h"
20 : #include "nsThreadUtils.h"
21 :
22 : #include "mozilla/Attributes.h"
23 : #include "mozilla/Preferences.h"
24 :
25 : namespace dom = mozilla::dom;
26 :
27 : namespace {
28 :
29 : uint64_t gSHEntrySharedID = 0;
30 :
31 : } // namespace
32 :
33 : void
34 0 : nsSHEntryShared::Shutdown()
35 : {
36 0 : }
37 :
38 1 : nsSHEntryShared::nsSHEntryShared()
39 : : mDocShellID({0})
40 : , mLastTouched(0)
41 1 : , mID(gSHEntrySharedID++)
42 : , mViewerBounds(0, 0, 0, 0)
43 : , mIsFrameNavigation(false)
44 : , mSaveLayoutState(true)
45 : , mSticky(true)
46 : , mDynamicallyCreated(false)
47 2 : , mExpired(false)
48 : {
49 1 : }
50 :
51 0 : nsSHEntryShared::~nsSHEntryShared()
52 : {
53 0 : RemoveFromExpirationTracker();
54 0 : if (mContentViewer) {
55 0 : RemoveFromBFCacheSync();
56 : }
57 0 : }
58 :
59 22 : NS_IMPL_ISUPPORTS(nsSHEntryShared, nsIBFCacheEntry, nsIMutationObserver)
60 :
61 : already_AddRefed<nsSHEntryShared>
62 0 : nsSHEntryShared::Duplicate(nsSHEntryShared* aEntry)
63 : {
64 0 : RefPtr<nsSHEntryShared> newEntry = new nsSHEntryShared();
65 :
66 0 : newEntry->mDocShellID = aEntry->mDocShellID;
67 0 : newEntry->mChildShells.AppendObjects(aEntry->mChildShells);
68 0 : newEntry->mTriggeringPrincipal = aEntry->mTriggeringPrincipal;
69 0 : newEntry->mPrincipalToInherit = aEntry->mPrincipalToInherit;
70 0 : newEntry->mContentType.Assign(aEntry->mContentType);
71 0 : newEntry->mIsFrameNavigation = aEntry->mIsFrameNavigation;
72 0 : newEntry->mSaveLayoutState = aEntry->mSaveLayoutState;
73 0 : newEntry->mSticky = aEntry->mSticky;
74 0 : newEntry->mDynamicallyCreated = aEntry->mDynamicallyCreated;
75 0 : newEntry->mCacheKey = aEntry->mCacheKey;
76 0 : newEntry->mLastTouched = aEntry->mLastTouched;
77 :
78 0 : return newEntry.forget();
79 : }
80 :
81 : void
82 0 : nsSHEntryShared::RemoveFromExpirationTracker()
83 : {
84 0 : nsCOMPtr<nsISHistoryInternal> shistory = do_QueryReferent(mSHistory);
85 0 : if (shistory && GetExpirationState()->IsTracked()) {
86 0 : shistory->RemoveFromExpirationTracker(this);
87 : }
88 0 : }
89 :
90 : nsresult
91 0 : nsSHEntryShared::SyncPresentationState()
92 : {
93 0 : if (mContentViewer && mWindowState) {
94 : // If we have a content viewer and a window state, we should be ok.
95 0 : return NS_OK;
96 : }
97 :
98 0 : DropPresentationState();
99 :
100 0 : return NS_OK;
101 : }
102 :
103 : void
104 0 : nsSHEntryShared::DropPresentationState()
105 : {
106 0 : RefPtr<nsSHEntryShared> kungFuDeathGrip = this;
107 :
108 0 : if (mDocument) {
109 0 : mDocument->SetBFCacheEntry(nullptr);
110 0 : mDocument->RemoveMutationObserver(this);
111 0 : mDocument = nullptr;
112 : }
113 0 : if (mContentViewer) {
114 0 : mContentViewer->ClearHistoryEntry();
115 : }
116 :
117 0 : RemoveFromExpirationTracker();
118 0 : mContentViewer = nullptr;
119 0 : mSticky = true;
120 0 : mWindowState = nullptr;
121 0 : mViewerBounds.SetRect(0, 0, 0, 0);
122 0 : mChildShells.Clear();
123 0 : mRefreshURIList = nullptr;
124 0 : mEditorData = nullptr;
125 0 : }
126 :
127 : nsresult
128 0 : nsSHEntryShared::SetContentViewer(nsIContentViewer* aViewer)
129 : {
130 0 : NS_PRECONDITION(!aViewer || !mContentViewer,
131 : "SHEntryShared already contains viewer");
132 :
133 0 : if (mContentViewer || !aViewer) {
134 0 : DropPresentationState();
135 : }
136 :
137 : // If we're setting mContentViewer to null, state should already be cleared
138 : // in the DropPresentationState() call above; If we're setting it to a
139 : // non-null content viewer, the entry shouldn't have been tracked either.
140 0 : MOZ_ASSERT(!GetExpirationState()->IsTracked());
141 0 : mContentViewer = aViewer;
142 :
143 0 : if (mContentViewer) {
144 : // mSHistory is only set for root entries, but in general bfcache only
145 : // applies to root entries as well. BFCache for subframe navigation has been
146 : // disabled since 2005 in bug 304860.
147 0 : nsCOMPtr<nsISHistoryInternal> shistory = do_QueryReferent(mSHistory);
148 0 : if (shistory) {
149 0 : shistory->AddToExpirationTracker(this);
150 : }
151 :
152 0 : nsCOMPtr<nsIDOMDocument> domDoc;
153 0 : mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
154 : // Store observed document in strong pointer in case it is removed from
155 : // the contentviewer
156 0 : mDocument = do_QueryInterface(domDoc);
157 0 : if (mDocument) {
158 0 : mDocument->SetBFCacheEntry(this);
159 0 : mDocument->AddMutationObserver(this);
160 : }
161 : }
162 :
163 0 : return NS_OK;
164 : }
165 :
166 : nsresult
167 0 : nsSHEntryShared::RemoveFromBFCacheSync()
168 : {
169 0 : MOZ_ASSERT(mContentViewer && mDocument, "we're not in the bfcache!");
170 :
171 0 : nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
172 0 : DropPresentationState();
173 :
174 : // Warning! The call to DropPresentationState could have dropped the last
175 : // reference to this object, so don't access members beyond here.
176 :
177 0 : if (viewer) {
178 0 : viewer->Destroy();
179 : }
180 :
181 0 : return NS_OK;
182 : }
183 :
184 0 : class DestroyViewerEvent : public mozilla::Runnable
185 : {
186 : public:
187 0 : DestroyViewerEvent(nsIContentViewer* aViewer, nsIDocument* aDocument)
188 0 : : mozilla::Runnable("DestroyViewerEvent")
189 : , mViewer(aViewer)
190 0 : , mDocument(aDocument)
191 : {
192 0 : }
193 :
194 0 : NS_IMETHOD Run() override
195 : {
196 0 : if (mViewer) {
197 0 : mViewer->Destroy();
198 : }
199 0 : return NS_OK;
200 : }
201 :
202 : nsCOMPtr<nsIContentViewer> mViewer;
203 : nsCOMPtr<nsIDocument> mDocument;
204 : };
205 :
206 : nsresult
207 0 : nsSHEntryShared::RemoveFromBFCacheAsync()
208 : {
209 0 : MOZ_ASSERT(mContentViewer && mDocument, "we're not in the bfcache!");
210 :
211 : // Release the reference to the contentviewer asynchronously so that the
212 : // document doesn't get nuked mid-mutation.
213 :
214 0 : if (!mDocument) {
215 0 : return NS_ERROR_UNEXPECTED;
216 : }
217 0 : nsCOMPtr<nsIRunnable> evt = new DestroyViewerEvent(mContentViewer, mDocument);
218 0 : nsresult rv = mDocument->Dispatch("nsSHEntryShared::DestroyViewerEvent",
219 0 : mozilla::TaskCategory::Other, evt.forget());
220 0 : if (NS_FAILED(rv)) {
221 0 : NS_WARNING("failed to dispatch DestroyViewerEvent");
222 : } else {
223 : // Drop presentation. Only do this if we succeeded in posting the event
224 : // since otherwise the document could be torn down mid-mutation, causing
225 : // crashes.
226 0 : DropPresentationState();
227 : }
228 :
229 : // Careful! The call to DropPresentationState could have dropped the last
230 : // reference to this nsSHEntryShared, so don't access members beyond here.
231 :
232 0 : return NS_OK;
233 : }
234 :
235 : nsresult
236 2 : nsSHEntryShared::GetID(uint64_t* aID)
237 : {
238 2 : *aID = mID;
239 2 : return NS_OK;
240 : }
241 :
242 : void
243 0 : nsSHEntryShared::NodeWillBeDestroyed(const nsINode* aNode)
244 : {
245 0 : NS_NOTREACHED("Document destroyed while we're holding a strong ref to it");
246 0 : }
247 :
248 : void
249 0 : nsSHEntryShared::CharacterDataWillChange(nsIDocument* aDocument,
250 : nsIContent* aContent,
251 : CharacterDataChangeInfo* aInfo)
252 : {
253 0 : }
254 :
255 : void
256 0 : nsSHEntryShared::CharacterDataChanged(nsIDocument* aDocument,
257 : nsIContent* aContent,
258 : CharacterDataChangeInfo* aInfo)
259 : {
260 0 : RemoveFromBFCacheAsync();
261 0 : }
262 :
263 : void
264 0 : nsSHEntryShared::AttributeWillChange(nsIDocument* aDocument,
265 : dom::Element* aContent,
266 : int32_t aNameSpaceID,
267 : nsIAtom* aAttribute,
268 : int32_t aModType,
269 : const nsAttrValue* aNewValue)
270 : {
271 0 : }
272 :
273 : void
274 0 : nsSHEntryShared::NativeAnonymousChildListChange(nsIDocument* aDocument,
275 : nsIContent* aContent,
276 : bool aIsRemove)
277 : {
278 0 : }
279 :
280 : void
281 0 : nsSHEntryShared::AttributeChanged(nsIDocument* aDocument,
282 : dom::Element* aElement,
283 : int32_t aNameSpaceID,
284 : nsIAtom* aAttribute,
285 : int32_t aModType,
286 : const nsAttrValue* aOldValue)
287 : {
288 0 : RemoveFromBFCacheAsync();
289 0 : }
290 :
291 : void
292 0 : nsSHEntryShared::ContentAppended(nsIDocument* aDocument,
293 : nsIContent* aContainer,
294 : nsIContent* aFirstNewContent,
295 : int32_t /* unused */)
296 : {
297 0 : RemoveFromBFCacheAsync();
298 0 : }
299 :
300 : void
301 0 : nsSHEntryShared::ContentInserted(nsIDocument* aDocument,
302 : nsIContent* aContainer,
303 : nsIContent* aChild,
304 : int32_t /* unused */)
305 : {
306 0 : RemoveFromBFCacheAsync();
307 0 : }
308 :
309 : void
310 0 : nsSHEntryShared::ContentRemoved(nsIDocument* aDocument,
311 : nsIContent* aContainer,
312 : nsIContent* aChild,
313 : int32_t aIndexInContainer,
314 : nsIContent* aPreviousSibling)
315 : {
316 0 : RemoveFromBFCacheAsync();
317 0 : }
318 :
319 : void
320 0 : nsSHEntryShared::ParentChainChanged(nsIContent* aContent)
321 : {
322 0 : }
|