Bug 1719035 [wpt PR 29572] - Address flakiness in import-css-module-basic.html, a...
[gecko.git] / docshell / shistory / ChildSHistory.cpp
blobb3218b742215f29f5d91f01df8f8188f15b017b9
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 "mozilla/dom/ChildSHistory.h"
8 #include "mozilla/dom/ChildSHistoryBinding.h"
9 #include "mozilla/dom/CanonicalBrowsingContext.h"
10 #include "mozilla/dom/ContentChild.h"
11 #include "mozilla/dom/ContentFrameMessageManager.h"
12 #include "nsIXULRuntime.h"
13 #include "nsComponentManagerUtils.h"
14 #include "nsSHEntry.h"
15 #include "nsSHistory.h"
16 #include "nsDocShell.h"
17 #include "nsXULAppAPI.h"
19 extern mozilla::LazyLogModule gSHLog;
21 namespace mozilla {
22 namespace dom {
24 ChildSHistory::ChildSHistory(BrowsingContext* aBrowsingContext)
25 : mBrowsingContext(aBrowsingContext) {}
27 ChildSHistory::~ChildSHistory() {
28 if (mHistory) {
29 static_cast<nsSHistory*>(mHistory.get())->SetBrowsingContext(nullptr);
33 void ChildSHistory::SetBrowsingContext(BrowsingContext* aBrowsingContext) {
34 mBrowsingContext = aBrowsingContext;
37 void ChildSHistory::SetIsInProcess(bool aIsInProcess) {
38 if (!aIsInProcess) {
39 MOZ_ASSERT_IF(mozilla::SessionHistoryInParent(), !mHistory);
40 if (!mozilla::SessionHistoryInParent()) {
41 RemovePendingHistoryNavigations();
42 if (mHistory) {
43 static_cast<nsSHistory*>(mHistory.get())->SetBrowsingContext(nullptr);
44 mHistory = nullptr;
48 return;
51 if (mHistory || mozilla::SessionHistoryInParent()) {
52 return;
55 mHistory = new nsSHistory(mBrowsingContext);
58 int32_t ChildSHistory::Count() {
59 if (mozilla::SessionHistoryInParent()) {
60 uint32_t length = mLength;
61 for (uint32_t i = 0; i < mPendingSHistoryChanges.Length(); ++i) {
62 length += mPendingSHistoryChanges[i].mLengthDelta;
65 return length;
67 return mHistory->GetCount();
70 int32_t ChildSHistory::Index() {
71 if (mozilla::SessionHistoryInParent()) {
72 uint32_t index = mIndex;
73 for (uint32_t i = 0; i < mPendingSHistoryChanges.Length(); ++i) {
74 index += mPendingSHistoryChanges[i].mIndexDelta;
77 return index;
79 int32_t index;
80 mHistory->GetIndex(&index);
81 return index;
84 nsID ChildSHistory::AddPendingHistoryChange() {
85 int32_t indexDelta = 1;
86 int32_t lengthDelta = (Index() + indexDelta) - (Count() - 1);
87 return AddPendingHistoryChange(indexDelta, lengthDelta);
90 nsID ChildSHistory::AddPendingHistoryChange(int32_t aIndexDelta,
91 int32_t aLengthDelta) {
92 nsID changeID = {};
93 nsContentUtils::GenerateUUIDInPlace(changeID);
94 PendingSHistoryChange change = {changeID, aIndexDelta, aLengthDelta};
95 mPendingSHistoryChanges.AppendElement(change);
96 return changeID;
99 void ChildSHistory::SetIndexAndLength(uint32_t aIndex, uint32_t aLength,
100 const nsID& aChangeID) {
101 mIndex = aIndex;
102 mLength = aLength;
103 mPendingSHistoryChanges.RemoveElementsBy(
104 [aChangeID](const PendingSHistoryChange& aChange) {
105 return aChange.mChangeID == aChangeID;
109 void ChildSHistory::Reload(uint32_t aReloadFlags, ErrorResult& aRv) {
110 if (mozilla::SessionHistoryInParent()) {
111 if (XRE_IsParentProcess()) {
112 nsISHistory* shistory =
113 mBrowsingContext->Canonical()->GetSessionHistory();
114 if (shistory) {
115 aRv = shistory->Reload(aReloadFlags);
117 } else {
118 ContentChild::GetSingleton()->SendHistoryReload(mBrowsingContext,
119 aReloadFlags);
122 return;
124 aRv = mHistory->Reload(aReloadFlags);
127 bool ChildSHistory::CanGo(int32_t aOffset) {
128 CheckedInt<int32_t> index = Index();
129 index += aOffset;
130 if (!index.isValid()) {
131 return false;
133 return index.value() < Count() && index.value() >= 0;
136 void ChildSHistory::Go(int32_t aOffset, bool aRequireUserInteraction,
137 bool aUserActivation, ErrorResult& aRv) {
138 CheckedInt<int32_t> index = Index();
139 MOZ_LOG(
140 gSHLog, LogLevel::Debug,
141 ("ChildSHistory::Go(%d), current index = %d", aOffset, index.value()));
142 if (aRequireUserInteraction && aOffset != -1 && aOffset != 1) {
143 NS_ERROR(
144 "aRequireUserInteraction may only be used with an offset of -1 or 1");
145 aRv.Throw(NS_ERROR_INVALID_ARG);
146 return;
149 while (true) {
150 index += aOffset;
151 if (!index.isValid()) {
152 aRv.Throw(NS_ERROR_FAILURE);
153 return;
156 // Check for user interaction if desired, except for the first and last
157 // history entries. We compare with >= to account for the case where
158 // aOffset >= Count().
159 if (!aRequireUserInteraction || index.value() >= Count() - 1 ||
160 index.value() <= 0) {
161 break;
163 if (mHistory && mHistory->HasUserInteractionAtIndex(index.value())) {
164 break;
168 GotoIndex(index.value(), aOffset, aRequireUserInteraction, aUserActivation,
169 aRv);
172 void ChildSHistory::AsyncGo(int32_t aOffset, bool aRequireUserInteraction,
173 bool aUserActivation, CallerType aCallerType,
174 ErrorResult& aRv) {
175 CheckedInt<int32_t> index = Index();
176 MOZ_LOG(gSHLog, LogLevel::Debug,
177 ("ChildSHistory::AsyncGo(%d), current index = %d", aOffset,
178 index.value()));
179 nsresult rv = mBrowsingContext->CheckLocationChangeRateLimit(aCallerType);
180 if (NS_FAILED(rv)) {
181 MOZ_LOG(gSHLog, LogLevel::Debug, ("Rejected"));
182 aRv.Throw(rv);
183 return;
186 RefPtr<PendingAsyncHistoryNavigation> asyncNav =
187 new PendingAsyncHistoryNavigation(this, aOffset, aRequireUserInteraction,
188 aUserActivation);
189 mPendingNavigations.insertBack(asyncNav);
190 NS_DispatchToCurrentThread(asyncNav.forget());
193 void ChildSHistory::GotoIndex(int32_t aIndex, int32_t aOffset,
194 bool aRequireUserInteraction,
195 bool aUserActivation, ErrorResult& aRv) {
196 MOZ_LOG(gSHLog, LogLevel::Debug,
197 ("ChildSHistory::GotoIndex(%d, %d), epoch %" PRIu64, aIndex, aOffset,
198 mHistoryEpoch));
199 if (mozilla::SessionHistoryInParent()) {
200 if (!mPendingEpoch) {
201 mPendingEpoch = true;
202 RefPtr<ChildSHistory> self(this);
203 NS_DispatchToCurrentThread(
204 NS_NewRunnableFunction("UpdateEpochRunnable", [self] {
205 self->mHistoryEpoch++;
206 self->mPendingEpoch = false;
207 }));
210 nsCOMPtr<nsISHistory> shistory = mHistory;
211 mBrowsingContext->HistoryGo(
212 aOffset, mHistoryEpoch, aRequireUserInteraction, aUserActivation,
213 [shistory](int32_t&& aRequestedIndex) {
214 // FIXME Should probably only do this for non-fission.
215 if (shistory) {
216 shistory->InternalSetRequestedIndex(aRequestedIndex);
219 } else {
220 aRv = mHistory->GotoIndex(aIndex, aUserActivation);
224 void ChildSHistory::RemovePendingHistoryNavigations() {
225 // Per the spec, this generally shouldn't remove all navigations - it
226 // depends if they're in the same document family or not. We don't do
227 // that. Also with SessionHistoryInParent, this can only abort AsyncGo's
228 // that have not yet been sent to the parent - see discussion of point
229 // 2.2 in comments in nsDocShell::UpdateURLAndHistory()
230 MOZ_LOG(gSHLog, LogLevel::Debug,
231 ("ChildSHistory::RemovePendingHistoryNavigations: %zu",
232 mPendingNavigations.length()));
233 mPendingNavigations.clear();
236 void ChildSHistory::EvictLocalContentViewers() {
237 if (!mozilla::SessionHistoryInParent()) {
238 mHistory->EvictAllContentViewers();
242 nsISHistory* ChildSHistory::GetLegacySHistory(ErrorResult& aError) {
243 if (mozilla::SessionHistoryInParent()) {
244 aError.ThrowTypeError(
245 "legacySHistory is not available with session history in the parent.");
246 return nullptr;
249 MOZ_RELEASE_ASSERT(mHistory);
250 return mHistory;
253 nsISHistory* ChildSHistory::LegacySHistory() {
254 IgnoredErrorResult ignore;
255 nsISHistory* shistory = GetLegacySHistory(ignore);
256 MOZ_RELEASE_ASSERT(shistory);
257 return shistory;
260 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChildSHistory)
261 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
262 NS_INTERFACE_MAP_ENTRY(nsISupports)
263 NS_INTERFACE_MAP_END
265 NS_IMPL_CYCLE_COLLECTING_ADDREF(ChildSHistory)
266 NS_IMPL_CYCLE_COLLECTING_RELEASE(ChildSHistory)
268 NS_IMPL_CYCLE_COLLECTION_CLASS(ChildSHistory)
270 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ChildSHistory)
271 if (tmp->mHistory) {
272 static_cast<nsSHistory*>(tmp->mHistory.get())->SetBrowsingContext(nullptr);
274 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext, mHistory)
275 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
276 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
278 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ChildSHistory)
279 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext, mHistory)
280 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
282 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(ChildSHistory)
284 JSObject* ChildSHistory::WrapObject(JSContext* cx,
285 JS::Handle<JSObject*> aGivenProto) {
286 return ChildSHistory_Binding::Wrap(cx, this, aGivenProto);
289 nsISupports* ChildSHistory::GetParentObject() const {
290 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
293 } // namespace dom
294 } // namespace mozilla