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
) {
93 nsContentUtils::GenerateUUIDInPlace(changeID
);
94 PendingSHistoryChange change
= {changeID
, aIndexDelta
, aLengthDelta
};
95 mPendingSHistoryChanges
.AppendElement(change
);
99 void ChildSHistory::SetIndexAndLength(uint32_t aIndex
, uint32_t aLength
,
100 const nsID
& aChangeID
) {
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();
115 aRv
= shistory
->Reload(aReloadFlags
);
118 ContentChild::GetSingleton()->SendHistoryReload(mBrowsingContext
,
124 aRv
= mHistory
->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 (!aRequireUserInteraction
|| index
.value() >= Count() - 1 ||
160 index
.value() <= 0) {
163 if (mHistory
&& mHistory
->HasUserInteractionAtIndex(index
.value())) {
168 GotoIndex(index
.value(), aOffset
, aRequireUserInteraction
, aUserActivation
,
172 void ChildSHistory::AsyncGo(int32_t aOffset
, bool aRequireUserInteraction
,
173 bool aUserActivation
, CallerType aCallerType
,
175 CheckedInt
<int32_t> index
= Index();
176 MOZ_LOG(gSHLog
, LogLevel::Debug
,
177 ("ChildSHistory::AsyncGo(%d), current index = %d", aOffset
,
179 nsresult rv
= mBrowsingContext
->CheckLocationChangeRateLimit(aCallerType
);
181 MOZ_LOG(gSHLog
, LogLevel::Debug
, ("Rejected"));
186 RefPtr
<PendingAsyncHistoryNavigation
> asyncNav
=
187 new PendingAsyncHistoryNavigation(this, aOffset
, aRequireUserInteraction
,
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
,
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;
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.
216 shistory
->InternalSetRequestedIndex(aRequestedIndex
);
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.");
249 MOZ_RELEASE_ASSERT(mHistory
);
253 nsISHistory
* ChildSHistory::LegacySHistory() {
254 IgnoredErrorResult ignore
;
255 nsISHistory
* shistory
= GetLegacySHistory(ignore
);
256 MOZ_RELEASE_ASSERT(shistory
);
260 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChildSHistory
)
261 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
262 NS_INTERFACE_MAP_ENTRY(nsISupports
)
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
)
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());
294 } // namespace mozilla