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
;
24 ChildSHistory::ChildSHistory(BrowsingContext
* aBrowsingContext
)
25 : mBrowsingContext(aBrowsingContext
) {}
27 ChildSHistory::~ChildSHistory() {
29 static_cast<nsSHistory
*>(mHistory
.get())->SetBrowsingContext(nullptr);
33 void ChildSHistory::SetBrowsingContext(BrowsingContext
* aBrowsingContext
) {
34 mBrowsingContext
= aBrowsingContext
;
37 void ChildSHistory::SetIsInProcess(bool aIsInProcess
) {
39 MOZ_ASSERT_IF(mozilla::SessionHistoryInParent(), !mHistory
);
40 if (!mozilla::SessionHistoryInParent()) {
41 RemovePendingHistoryNavigations();
43 static_cast<nsSHistory
*>(mHistory
.get())->SetBrowsingContext(nullptr);
51 if (mHistory
|| mozilla::SessionHistoryInParent()) {
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
;
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
;
80 mHistory
->GetIndex(&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
= nsID::GenerateUUID();
93 PendingSHistoryChange change
= {changeID
, aIndexDelta
, aLengthDelta
};
94 mPendingSHistoryChanges
.AppendElement(change
);
98 void ChildSHistory::SetIndexAndLength(uint32_t aIndex
, uint32_t aLength
,
99 const nsID
& aChangeID
) {
102 mPendingSHistoryChanges
.RemoveElementsBy(
103 [aChangeID
](const PendingSHistoryChange
& aChange
) {
104 return aChange
.mChangeID
== aChangeID
;
108 void ChildSHistory::Reload(uint32_t aReloadFlags
, ErrorResult
& aRv
) {
109 if (mozilla::SessionHistoryInParent()) {
110 if (XRE_IsParentProcess()) {
111 nsCOMPtr
<nsISHistory
> shistory
=
112 mBrowsingContext
->Canonical()->GetSessionHistory();
114 aRv
= shistory
->Reload(aReloadFlags
);
117 ContentChild::GetSingleton()->SendHistoryReload(mBrowsingContext
,
123 nsCOMPtr
<nsISHistory
> shistory
= mHistory
;
124 aRv
= shistory
->Reload(aReloadFlags
);
127 bool ChildSHistory::CanGo(int32_t aOffset
) {
128 CheckedInt
<int32_t> index
= Index();
130 if (!index
.isValid()) {
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();
140 gSHLog
, LogLevel::Debug
,
141 ("ChildSHistory::Go(%d), current index = %d", aOffset
, index
.value()));
142 if (aRequireUserInteraction
&& aOffset
!= -1 && aOffset
!= 1) {
144 "aRequireUserInteraction may only be used with an offset of -1 or 1");
145 aRv
.Throw(NS_ERROR_INVALID_ARG
);
151 if (!index
.isValid()) {
152 aRv
.Throw(NS_ERROR_FAILURE
);
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 (!StaticPrefs::browser_navigation_requireUserInteraction() ||
160 !aRequireUserInteraction
|| index
.value() >= Count() - 1 ||
161 index
.value() <= 0) {
164 if (mHistory
&& mHistory
->HasUserInteractionAtIndex(index
.value())) {
169 GotoIndex(index
.value(), aOffset
, aRequireUserInteraction
, aUserActivation
,
173 void ChildSHistory::AsyncGo(int32_t aOffset
, bool aRequireUserInteraction
,
174 bool aUserActivation
, CallerType aCallerType
,
176 CheckedInt
<int32_t> index
= Index();
177 MOZ_LOG(gSHLog
, LogLevel::Debug
,
178 ("ChildSHistory::AsyncGo(%d), current index = %d", aOffset
,
180 nsresult rv
= mBrowsingContext
->CheckLocationChangeRateLimit(aCallerType
);
182 MOZ_LOG(gSHLog
, LogLevel::Debug
, ("Rejected"));
187 RefPtr
<PendingAsyncHistoryNavigation
> asyncNav
=
188 new PendingAsyncHistoryNavigation(this, aOffset
, aRequireUserInteraction
,
190 mPendingNavigations
.insertBack(asyncNav
);
191 NS_DispatchToCurrentThread(asyncNav
.forget());
194 void ChildSHistory::GotoIndex(int32_t aIndex
, int32_t aOffset
,
195 bool aRequireUserInteraction
,
196 bool aUserActivation
, ErrorResult
& aRv
) {
197 MOZ_LOG(gSHLog
, LogLevel::Debug
,
198 ("ChildSHistory::GotoIndex(%d, %d), epoch %" PRIu64
, aIndex
, aOffset
,
200 if (mozilla::SessionHistoryInParent()) {
201 if (!mPendingEpoch
) {
202 mPendingEpoch
= true;
203 RefPtr
<ChildSHistory
> self(this);
204 NS_DispatchToCurrentThread(
205 NS_NewRunnableFunction("UpdateEpochRunnable", [self
] {
206 self
->mHistoryEpoch
++;
207 self
->mPendingEpoch
= false;
211 nsCOMPtr
<nsISHistory
> shistory
= mHistory
;
212 RefPtr
<BrowsingContext
> bc
= mBrowsingContext
;
214 aOffset
, mHistoryEpoch
, aRequireUserInteraction
, aUserActivation
,
215 [shistory
](Maybe
<int32_t>&& aRequestedIndex
) {
216 // FIXME Should probably only do this for non-fission.
217 if (aRequestedIndex
.isSome() && shistory
) {
218 shistory
->InternalSetRequestedIndex(aRequestedIndex
.value());
222 nsCOMPtr
<nsISHistory
> shistory
= mHistory
;
223 aRv
= shistory
->GotoIndex(aIndex
, aUserActivation
);
227 void ChildSHistory::RemovePendingHistoryNavigations() {
228 // Per the spec, this generally shouldn't remove all navigations - it
229 // depends if they're in the same document family or not. We don't do
230 // that. Also with SessionHistoryInParent, this can only abort AsyncGo's
231 // that have not yet been sent to the parent - see discussion of point
232 // 2.2 in comments in nsDocShell::UpdateURLAndHistory()
233 MOZ_LOG(gSHLog
, LogLevel::Debug
,
234 ("ChildSHistory::RemovePendingHistoryNavigations: %zu",
235 mPendingNavigations
.length()));
236 mPendingNavigations
.clear();
239 void ChildSHistory::EvictLocalDocumentViewers() {
240 if (!mozilla::SessionHistoryInParent()) {
241 mHistory
->EvictAllDocumentViewers();
245 nsISHistory
* ChildSHistory::GetLegacySHistory(ErrorResult
& aError
) {
246 if (mozilla::SessionHistoryInParent()) {
247 aError
.ThrowTypeError(
248 "legacySHistory is not available with session history in the parent.");
252 MOZ_RELEASE_ASSERT(mHistory
);
256 nsISHistory
* ChildSHistory::LegacySHistory() {
257 IgnoredErrorResult ignore
;
258 nsISHistory
* shistory
= GetLegacySHistory(ignore
);
259 MOZ_RELEASE_ASSERT(shistory
);
263 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChildSHistory
)
264 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
265 NS_INTERFACE_MAP_ENTRY(nsISupports
)
268 NS_IMPL_CYCLE_COLLECTING_ADDREF(ChildSHistory
)
269 NS_IMPL_CYCLE_COLLECTING_RELEASE(ChildSHistory
)
271 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ChildSHistory
)
273 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ChildSHistory
)
275 static_cast<nsSHistory
*>(tmp
->mHistory
.get())->SetBrowsingContext(nullptr);
277 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext
, mHistory
)
278 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
279 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
281 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ChildSHistory
)
282 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext
, mHistory
)
283 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
285 JSObject
* ChildSHistory::WrapObject(JSContext
* cx
,
286 JS::Handle
<JSObject
*> aGivenProto
) {
287 return ChildSHistory_Binding::Wrap(cx
, this, aGivenProto
);
290 nsISupports
* ChildSHistory::GetParentObject() const {
291 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
295 } // namespace mozilla