Bug 1733133 [wpt PR 30931] - Fix transform-rotate-006.html., a=testonly
[gecko.git] / docshell / shistory / nsSHEntryShared.cpp
blob06c7ab337333dbc0faace052ac9b51d546706d3d
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/. */
7 #include "nsSHEntryShared.h"
9 #include "nsArray.h"
10 #include "nsContentUtils.h"
11 #include "nsDocShellEditorData.h"
12 #include "nsIContentViewer.h"
13 #include "nsISHistory.h"
14 #include "mozilla/dom/Document.h"
15 #include "nsILayoutHistoryState.h"
16 #include "nsIWebNavigation.h"
17 #include "nsSHistory.h"
18 #include "nsThreadUtils.h"
19 #include "nsFrameLoader.h"
20 #include "mozilla/Attributes.h"
21 #include "mozilla/Preferences.h"
23 namespace dom = mozilla::dom;
25 namespace {
26 uint64_t gSHEntrySharedID = 0;
27 nsTHashMap<nsUint64HashKey, mozilla::dom::SHEntrySharedParentState*>*
28 sIdToSharedState = nullptr;
29 } // namespace
31 namespace mozilla {
32 namespace dom {
34 /* static */
35 uint64_t SHEntrySharedState::GenerateId() {
36 return nsContentUtils::GenerateProcessSpecificId(++gSHEntrySharedID);
39 /* static */
40 SHEntrySharedParentState* SHEntrySharedParentState::Lookup(uint64_t aId) {
41 MOZ_ASSERT(aId != 0);
43 return sIdToSharedState ? sIdToSharedState->Get(aId) : nullptr;
46 static void AddSHEntrySharedParentState(
47 SHEntrySharedParentState* aSharedState) {
48 MOZ_ASSERT(aSharedState->mId != 0);
50 if (!sIdToSharedState) {
51 sIdToSharedState =
52 new nsTHashMap<nsUint64HashKey, SHEntrySharedParentState*>();
54 sIdToSharedState->InsertOrUpdate(aSharedState->mId, aSharedState);
57 SHEntrySharedParentState::SHEntrySharedParentState() {
58 AddSHEntrySharedParentState(this);
61 SHEntrySharedParentState::SHEntrySharedParentState(
62 nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit,
63 nsIPrincipal* aPartitionedPrincipalToInherit,
64 nsIContentSecurityPolicy* aCsp, const nsACString& aContentType)
65 : SHEntrySharedState(aTriggeringPrincipal, aPrincipalToInherit,
66 aPartitionedPrincipalToInherit, aCsp, aContentType) {
67 AddSHEntrySharedParentState(this);
70 SHEntrySharedParentState::~SHEntrySharedParentState() {
71 MOZ_ASSERT(mId != 0);
73 RefPtr<nsFrameLoader> loader = mFrameLoader;
74 SetFrameLoader(nullptr);
75 if (loader) {
76 loader->Destroy();
79 sIdToSharedState->Remove(mId);
80 if (sIdToSharedState->IsEmpty()) {
81 delete sIdToSharedState;
82 sIdToSharedState = nullptr;
86 void SHEntrySharedParentState::ChangeId(uint64_t aId) {
87 MOZ_ASSERT(aId != 0);
89 sIdToSharedState->Remove(mId);
90 mId = aId;
91 sIdToSharedState->InsertOrUpdate(mId, this);
94 void SHEntrySharedParentState::CopyFrom(SHEntrySharedParentState* aEntry) {
95 mDocShellID = aEntry->mDocShellID;
96 mTriggeringPrincipal = aEntry->mTriggeringPrincipal;
97 mPrincipalToInherit = aEntry->mPrincipalToInherit;
98 mPartitionedPrincipalToInherit = aEntry->mPartitionedPrincipalToInherit;
99 mCsp = aEntry->mCsp;
100 mSaveLayoutState = aEntry->mSaveLayoutState;
101 mContentType.Assign(aEntry->mContentType);
102 mIsFrameNavigation = aEntry->mIsFrameNavigation;
103 mSticky = aEntry->mSticky;
104 mDynamicallyCreated = aEntry->mDynamicallyCreated;
105 mCacheKey = aEntry->mCacheKey;
106 mLastTouched = aEntry->mLastTouched;
109 void dom::SHEntrySharedParentState::NotifyListenersContentViewerEvicted() {
110 if (nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory)) {
111 RefPtr<nsSHistory> nsshistory = static_cast<nsSHistory*>(shistory.get());
112 nsshistory->NotifyListenersContentViewerEvicted(1);
116 void SHEntrySharedChildState::CopyFrom(SHEntrySharedChildState* aEntry) {
117 mChildShells.AppendObjects(aEntry->mChildShells);
120 void SHEntrySharedParentState::SetFrameLoader(nsFrameLoader* aFrameLoader) {
121 // If expiration tracker is removing this object, IsTracked() returns false.
122 if (GetExpirationState()->IsTracked() && mFrameLoader) {
123 if (nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory)) {
124 shistory->RemoveFromExpirationTracker(this);
128 mFrameLoader = aFrameLoader;
130 if (mFrameLoader) {
131 if (nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory)) {
132 shistory->AddToExpirationTracker(this);
137 nsFrameLoader* SHEntrySharedParentState::GetFrameLoader() {
138 return mFrameLoader;
141 } // namespace dom
142 } // namespace mozilla
144 void nsSHEntryShared::Shutdown() {}
146 nsSHEntryShared::~nsSHEntryShared() {
147 // The destruction can be caused by either the entry is removed from session
148 // history and no one holds the reference, or the whole session history is on
149 // destruction. We want to ensure that we invoke
150 // shistory->RemoveFromExpirationTracker for the former case.
151 RemoveFromExpirationTracker();
153 // Calling RemoveDynEntriesForBFCacheEntry on destruction is unnecessary since
154 // there couldn't be any SHEntry holding this shared entry, and we noticed
155 // that calling RemoveDynEntriesForBFCacheEntry in the middle of
156 // nsSHistory::Release can cause a crash, so set mSHistory to null explicitly
157 // before RemoveFromBFCacheSync.
158 mSHistory = nullptr;
159 if (mContentViewer) {
160 RemoveFromBFCacheSync();
164 NS_IMPL_QUERY_INTERFACE(nsSHEntryShared, nsIBFCacheEntry, nsIMutationObserver)
165 NS_IMPL_ADDREF_INHERITED(nsSHEntryShared, dom::SHEntrySharedParentState)
166 NS_IMPL_RELEASE_INHERITED(nsSHEntryShared, dom::SHEntrySharedParentState)
168 already_AddRefed<nsSHEntryShared> nsSHEntryShared::Duplicate() {
169 RefPtr<nsSHEntryShared> newEntry = new nsSHEntryShared();
171 newEntry->dom::SHEntrySharedParentState::CopyFrom(this);
172 newEntry->dom::SHEntrySharedChildState::CopyFrom(this);
174 return newEntry.forget();
177 void nsSHEntryShared::RemoveFromExpirationTracker() {
178 nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory);
179 if (shistory && GetExpirationState()->IsTracked()) {
180 shistory->RemoveFromExpirationTracker(this);
184 void nsSHEntryShared::SyncPresentationState() {
185 if (mContentViewer && mWindowState) {
186 // If we have a content viewer and a window state, we should be ok.
187 return;
190 DropPresentationState();
193 void nsSHEntryShared::DropPresentationState() {
194 RefPtr<nsSHEntryShared> kungFuDeathGrip = this;
196 if (mDocument) {
197 mDocument->SetBFCacheEntry(nullptr);
198 mDocument->RemoveMutationObserver(this);
199 mDocument = nullptr;
201 if (mContentViewer) {
202 mContentViewer->ClearHistoryEntry();
205 RemoveFromExpirationTracker();
206 mContentViewer = nullptr;
207 mSticky = true;
208 mWindowState = nullptr;
209 mViewerBounds.SetRect(0, 0, 0, 0);
210 mChildShells.Clear();
211 mRefreshURIList = nullptr;
212 mEditorData = nullptr;
215 nsresult nsSHEntryShared::SetContentViewer(nsIContentViewer* aViewer) {
216 MOZ_ASSERT(!aViewer || !mContentViewer,
217 "SHEntryShared already contains viewer");
219 if (mContentViewer || !aViewer) {
220 DropPresentationState();
223 // If we're setting mContentViewer to null, state should already be cleared
224 // in the DropPresentationState() call above; If we're setting it to a
225 // non-null content viewer, the entry shouldn't have been tracked either.
226 MOZ_ASSERT(!GetExpirationState()->IsTracked());
227 mContentViewer = aViewer;
229 if (mContentViewer) {
230 // mSHistory is only set for root entries, but in general bfcache only
231 // applies to root entries as well. BFCache for subframe navigation has been
232 // disabled since 2005 in bug 304860.
233 if (nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory)) {
234 shistory->AddToExpirationTracker(this);
237 // Store observed document in strong pointer in case it is removed from
238 // the contentviewer
239 mDocument = mContentViewer->GetDocument();
240 if (mDocument) {
241 mDocument->SetBFCacheEntry(this);
242 mDocument->AddMutationObserver(this);
246 return NS_OK;
249 nsresult nsSHEntryShared::RemoveFromBFCacheSync() {
250 MOZ_ASSERT(mContentViewer && mDocument, "we're not in the bfcache!");
252 // The call to DropPresentationState could drop the last reference, so hold
253 // |this| until RemoveDynEntriesForBFCacheEntry finishes.
254 RefPtr<nsSHEntryShared> kungFuDeathGrip = this;
256 // DropPresentationState would clear mContentViewer.
257 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
258 DropPresentationState();
260 if (viewer) {
261 viewer->Destroy();
264 // Now that we've dropped the viewer, we have to clear associated dynamic
265 // subframe entries.
266 nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory);
267 if (shistory) {
268 shistory->RemoveDynEntriesForBFCacheEntry(this);
271 return NS_OK;
274 nsresult nsSHEntryShared::RemoveFromBFCacheAsync() {
275 MOZ_ASSERT(mContentViewer && mDocument, "we're not in the bfcache!");
277 // Check it again to play safe in release builds.
278 if (!mDocument) {
279 return NS_ERROR_UNEXPECTED;
282 // DropPresentationState would clear mContentViewer & mDocument. Capture and
283 // release the references asynchronously so that the document doesn't get
284 // nuked mid-mutation.
285 nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
286 RefPtr<dom::Document> document = mDocument;
287 RefPtr<nsSHEntryShared> self = this;
288 nsresult rv = mDocument->Dispatch(
289 mozilla::TaskCategory::Other,
290 NS_NewRunnableFunction(
291 "nsSHEntryShared::RemoveFromBFCacheAsync",
292 [self, viewer, document]() {
293 if (viewer) {
294 viewer->Destroy();
297 nsCOMPtr<nsISHistory> shistory = do_QueryReferent(self->mSHistory);
298 if (shistory) {
299 shistory->RemoveDynEntriesForBFCacheEntry(self);
301 }));
303 if (NS_FAILED(rv)) {
304 NS_WARNING("Failed to dispatch RemoveFromBFCacheAsync runnable.");
305 } else {
306 // Drop presentation. Only do this if we succeeded in posting the event
307 // since otherwise the document could be torn down mid-mutation, causing
308 // crashes.
309 DropPresentationState();
312 return NS_OK;
315 void nsSHEntryShared::CharacterDataChanged(nsIContent* aContent,
316 const CharacterDataChangeInfo&) {
317 RemoveFromBFCacheAsync();
320 void nsSHEntryShared::AttributeChanged(dom::Element* aElement,
321 int32_t aNameSpaceID, nsAtom* aAttribute,
322 int32_t aModType,
323 const nsAttrValue* aOldValue) {
324 RemoveFromBFCacheAsync();
327 void nsSHEntryShared::ContentAppended(nsIContent* aFirstNewContent) {
328 RemoveFromBFCacheAsync();
331 void nsSHEntryShared::ContentInserted(nsIContent* aChild) {
332 RemoveFromBFCacheAsync();
335 void nsSHEntryShared::ContentRemoved(nsIContent* aChild,
336 nsIContent* aPreviousSibling) {
337 RemoveFromBFCacheAsync();