Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/docshell/shistory/nsSHEntryShared.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsILayoutHistoryState.h"
16
#include "nsISHistory.h"
17
#include "nsIWebNavigation.h"
18
#include "nsThreadUtils.h"
19
20
#include "mozilla/Attributes.h"
21
#include "mozilla/Preferences.h"
22
23
namespace dom = mozilla::dom;
24
25
namespace {
26
27
uint64_t gSHEntrySharedID = 0;
28
29
} // namespace
30
31
void
32
nsSHEntryShared::Shutdown()
33
0
{
34
0
}
35
36
nsSHEntryShared::nsSHEntryShared()
37
  : mDocShellID({0})
38
  , mCacheKey(0)
39
  , mLastTouched(0)
40
  , mID(gSHEntrySharedID++)
41
  , mViewerBounds(0, 0, 0, 0)
42
  , mIsFrameNavigation(false)
43
  , mSaveLayoutState(true)
44
  , mSticky(true)
45
  , mDynamicallyCreated(false)
46
  , mExpired(false)
47
0
{
48
0
}
49
50
nsSHEntryShared::~nsSHEntryShared()
51
0
{
52
0
  // The destruction can be caused by either the entry is removed from session
53
0
  // history and no one holds the reference, or the whole session history is on
54
0
  // destruction. We want to ensure that we invoke
55
0
  // shistory->RemoveFromExpirationTracker for the former case.
56
0
  RemoveFromExpirationTracker();
57
0
58
0
  // Calling RemoveDynEntriesForBFCacheEntry on destruction is unnecessary since
59
0
  // there couldn't be any SHEntry holding this shared entry, and we noticed
60
0
  // that calling RemoveDynEntriesForBFCacheEntry in the middle of
61
0
  // nsSHistory::Release can cause a crash, so set mSHistory to null explicitly
62
0
  // before RemoveFromBFCacheSync.
63
0
  mSHistory = nullptr;
64
0
  if (mContentViewer) {
65
0
    RemoveFromBFCacheSync();
66
0
  }
67
0
}
68
69
NS_IMPL_ISUPPORTS(nsSHEntryShared, nsIBFCacheEntry, nsIMutationObserver)
70
71
already_AddRefed<nsSHEntryShared>
72
nsSHEntryShared::Duplicate(nsSHEntryShared* aEntry)
73
0
{
74
0
  RefPtr<nsSHEntryShared> newEntry = new nsSHEntryShared();
75
0
76
0
  newEntry->mDocShellID = aEntry->mDocShellID;
77
0
  newEntry->mChildShells.AppendObjects(aEntry->mChildShells);
78
0
  newEntry->mTriggeringPrincipal = aEntry->mTriggeringPrincipal;
79
0
  newEntry->mPrincipalToInherit = aEntry->mPrincipalToInherit;
80
0
  newEntry->mContentType.Assign(aEntry->mContentType);
81
0
  newEntry->mIsFrameNavigation = aEntry->mIsFrameNavigation;
82
0
  newEntry->mSaveLayoutState = aEntry->mSaveLayoutState;
83
0
  newEntry->mSticky = aEntry->mSticky;
84
0
  newEntry->mDynamicallyCreated = aEntry->mDynamicallyCreated;
85
0
  newEntry->mCacheKey = aEntry->mCacheKey;
86
0
  newEntry->mLastTouched = aEntry->mLastTouched;
87
0
88
0
  return newEntry.forget();
89
0
}
90
91
void
92
nsSHEntryShared::RemoveFromExpirationTracker()
93
0
{
94
0
  nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory);
95
0
  if (shistory && GetExpirationState()->IsTracked()) {
96
0
    shistory->RemoveFromExpirationTracker(this);
97
0
  }
98
0
}
99
100
void
101
nsSHEntryShared::SyncPresentationState()
102
0
{
103
0
  if (mContentViewer && mWindowState) {
104
0
    // If we have a content viewer and a window state, we should be ok.
105
0
    return;
106
0
  }
107
0
108
0
  DropPresentationState();
109
0
}
110
111
void
112
nsSHEntryShared::DropPresentationState()
113
0
{
114
0
  RefPtr<nsSHEntryShared> kungFuDeathGrip = this;
115
0
116
0
  if (mDocument) {
117
0
    mDocument->SetBFCacheEntry(nullptr);
118
0
    mDocument->RemoveMutationObserver(this);
119
0
    mDocument = nullptr;
120
0
  }
121
0
  if (mContentViewer) {
122
0
    mContentViewer->ClearHistoryEntry();
123
0
  }
124
0
125
0
  RemoveFromExpirationTracker();
126
0
  mContentViewer = nullptr;
127
0
  mSticky = true;
128
0
  mWindowState = nullptr;
129
0
  mViewerBounds.SetRect(0, 0, 0, 0);
130
0
  mChildShells.Clear();
131
0
  mRefreshURIList = nullptr;
132
0
  mEditorData = nullptr;
133
0
}
134
135
nsresult
136
nsSHEntryShared::SetContentViewer(nsIContentViewer* aViewer)
137
0
{
138
0
  MOZ_ASSERT(!aViewer || !mContentViewer,
139
0
             "SHEntryShared already contains viewer");
140
0
141
0
  if (mContentViewer || !aViewer) {
142
0
    DropPresentationState();
143
0
  }
144
0
145
0
  // If we're setting mContentViewer to null, state should already be cleared
146
0
  // in the DropPresentationState() call above; If we're setting it to a
147
0
  // non-null content viewer, the entry shouldn't have been tracked either.
148
0
  MOZ_ASSERT(!GetExpirationState()->IsTracked());
149
0
  mContentViewer = aViewer;
150
0
151
0
  if (mContentViewer) {
152
0
    // mSHistory is only set for root entries, but in general bfcache only
153
0
    // applies to root entries as well. BFCache for subframe navigation has been
154
0
    // disabled since 2005 in bug 304860.
155
0
    if (nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory)) {
156
0
      shistory->AddToExpirationTracker(this);
157
0
    }
158
0
159
0
    // Store observed document in strong pointer in case it is removed from
160
0
    // the contentviewer
161
0
    mDocument = mContentViewer->GetDocument();
162
0
    if (mDocument) {
163
0
      mDocument->SetBFCacheEntry(this);
164
0
      mDocument->AddMutationObserver(this);
165
0
    }
166
0
  }
167
0
168
0
  return NS_OK;
169
0
}
170
171
nsresult
172
nsSHEntryShared::RemoveFromBFCacheSync()
173
0
{
174
0
  MOZ_ASSERT(mContentViewer && mDocument, "we're not in the bfcache!");
175
0
176
0
  // The call to DropPresentationState could drop the last reference, so hold
177
0
  // |this| until RemoveDynEntriesForBFCacheEntry finishes.
178
0
  RefPtr<nsSHEntryShared> kungFuDeathGrip = this;
179
0
180
0
  // DropPresentationState would clear mContentViewer.
181
0
  nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
182
0
  DropPresentationState();
183
0
184
0
  if (viewer) {
185
0
    viewer->Destroy();
186
0
  }
187
0
188
0
  // Now that we've dropped the viewer, we have to clear associated dynamic
189
0
  // subframe entries.
190
0
  nsCOMPtr<nsISHistory> shistory = do_QueryReferent(mSHistory);
191
0
  if (shistory) {
192
0
    shistory->RemoveDynEntriesForBFCacheEntry(this);
193
0
  }
194
0
195
0
  return NS_OK;
196
0
}
197
198
nsresult
199
nsSHEntryShared::RemoveFromBFCacheAsync()
200
0
{
201
0
  MOZ_ASSERT(mContentViewer && mDocument, "we're not in the bfcache!");
202
0
203
0
  // Check it again to play safe in release builds.
204
0
  if (!mDocument) {
205
0
    return NS_ERROR_UNEXPECTED;
206
0
  }
207
0
208
0
  // DropPresentationState would clear mContentViewer & mDocument. Capture and
209
0
  // release the references asynchronously so that the document doesn't get
210
0
  // nuked mid-mutation.
211
0
  nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
212
0
  nsCOMPtr<nsIDocument> document = mDocument;
213
0
  RefPtr<nsSHEntryShared> self = this;
214
0
  nsresult rv = mDocument->Dispatch(mozilla::TaskCategory::Other,
215
0
    NS_NewRunnableFunction("nsSHEntryShared::RemoveFromBFCacheAsync",
216
0
    [self, viewer, document]() {
217
0
      if (viewer) {
218
0
        viewer->Destroy();
219
0
      }
220
0
221
0
      nsCOMPtr<nsISHistory> shistory = do_QueryReferent(self->mSHistory);
222
0
      if (shistory) {
223
0
        shistory->RemoveDynEntriesForBFCacheEntry(self);
224
0
      }
225
0
    }));
226
0
227
0
  if (NS_FAILED(rv)) {
228
0
    NS_WARNING("Failed to dispatch RemoveFromBFCacheAsync runnable.");
229
0
  } else {
230
0
    // Drop presentation. Only do this if we succeeded in posting the event
231
0
    // since otherwise the document could be torn down mid-mutation, causing
232
0
    // crashes.
233
0
    DropPresentationState();
234
0
  }
235
0
236
0
  return NS_OK;
237
0
}
238
239
nsresult
240
nsSHEntryShared::GetID(uint64_t* aID)
241
0
{
242
0
  *aID = mID;
243
0
  return NS_OK;
244
0
}
245
246
void
247
nsSHEntryShared::CharacterDataChanged(nsIContent* aContent,
248
                                      const CharacterDataChangeInfo&)
249
0
{
250
0
  RemoveFromBFCacheAsync();
251
0
}
252
253
void
254
nsSHEntryShared::AttributeChanged(dom::Element* aElement,
255
                                  int32_t aNameSpaceID,
256
                                  nsAtom* aAttribute,
257
                                  int32_t aModType,
258
                                  const nsAttrValue* aOldValue)
259
0
{
260
0
  RemoveFromBFCacheAsync();
261
0
}
262
263
void
264
nsSHEntryShared::ContentAppended(nsIContent* aFirstNewContent)
265
0
{
266
0
  RemoveFromBFCacheAsync();
267
0
}
268
269
void
270
nsSHEntryShared::ContentInserted(nsIContent* aChild)
271
0
{
272
0
  RemoveFromBFCacheAsync();
273
0
}
274
275
void
276
nsSHEntryShared::ContentRemoved(nsIContent* aChild,
277
                                nsIContent* aPreviousSibling)
278
0
{
279
0
  RemoveFromBFCacheAsync();
280
0
}