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/. */
11 #include "nsPIDOMWindow.h"
12 #include "mozilla/dom/Document.h"
13 #include "mozilla/dom/DocumentInlines.h"
14 #include "nsIDocShell.h"
15 #include "nsIWebNavigation.h"
16 #include "nsReadableUtils.h"
17 #include "nsContentUtils.h"
18 #include "mozilla/dom/WindowContext.h"
19 #include "mozilla/dom/Location.h"
20 #include "mozilla/RefPtr.h"
21 #include "mozilla/StaticPrefs_dom.h"
22 #include "mozilla/BasePrincipal.h"
24 using namespace mozilla
;
25 using namespace mozilla::dom
;
27 extern LazyLogModule gSHistoryLog
;
29 #define LOG(format) MOZ_LOG(gSHistoryLog, mozilla::LogLevel::Debug, format)
32 // History class implementation
34 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsHistory
)
35 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHistory
)
36 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHistory
)
37 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHistory
)
38 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
39 NS_INTERFACE_MAP_ENTRY(nsISupports
)
42 nsHistory::nsHistory(nsPIDOMWindowInner
* aInnerWindow
)
43 : mInnerWindow(do_GetWeakReference(aInnerWindow
)) {}
45 nsHistory::~nsHistory() = default;
47 nsPIDOMWindowInner
* nsHistory::GetParentObject() const {
48 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryReferent(mInnerWindow
));
52 JSObject
* nsHistory::WrapObject(JSContext
* aCx
,
53 JS::Handle
<JSObject
*> aGivenProto
) {
54 return History_Binding::Wrap(aCx
, this, aGivenProto
);
57 uint32_t nsHistory::GetLength(ErrorResult
& aRv
) const {
58 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryReferent(mInnerWindow
));
59 if (!win
|| !win
->HasActiveDocument()) {
60 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
65 // Get session History from docshell
66 RefPtr
<ChildSHistory
> sHistory
= GetSessionHistory();
71 int32_t len
= sHistory
->Count();
72 return len
>= 0 ? len
: 0;
75 ScrollRestoration
nsHistory::GetScrollRestoration(mozilla::ErrorResult
& aRv
) {
76 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryReferent(mInnerWindow
));
77 if (!win
|| !win
->HasActiveDocument() || !win
->GetDocShell()) {
78 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
79 return mozilla::dom::ScrollRestoration::Auto
;
82 bool currentScrollRestorationIsManual
= false;
83 win
->GetDocShell()->GetCurrentScrollRestorationIsManual(
84 ¤tScrollRestorationIsManual
);
85 return currentScrollRestorationIsManual
86 ? mozilla::dom::ScrollRestoration::Manual
87 : mozilla::dom::ScrollRestoration::Auto
;
90 void nsHistory::SetScrollRestoration(mozilla::dom::ScrollRestoration aMode
,
91 mozilla::ErrorResult
& aRv
) {
92 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryReferent(mInnerWindow
));
93 if (!win
|| !win
->HasActiveDocument() || !win
->GetDocShell()) {
94 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
98 win
->GetDocShell()->SetCurrentScrollRestorationIsManual(
99 aMode
== mozilla::dom::ScrollRestoration::Manual
);
102 void nsHistory::GetState(JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aResult
,
103 ErrorResult
& aRv
) const {
104 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryReferent(mInnerWindow
));
106 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
110 if (!win
->HasActiveDocument()) {
111 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
115 nsCOMPtr
<Document
> doc
= win
->GetExtantDoc();
117 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
121 aRv
= doc
->GetStateObject(aResult
);
124 void nsHistory::Go(int32_t aDelta
, nsIPrincipal
& aSubjectPrincipal
,
126 LOG(("nsHistory::Go(%d)", aDelta
));
127 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryReferent(mInnerWindow
));
128 if (!win
|| !win
->HasActiveDocument()) {
129 return aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
133 // https://html.spec.whatwg.org/multipage/history.html#the-history-interface
134 // "When the go(delta) method is invoked, if delta is zero, the user agent
135 // must act as if the location.reload() method was called instead."
136 RefPtr
<Location
> location
= win
->Location();
137 return location
->Reload(false, aSubjectPrincipal
, aRv
);
140 RefPtr
<ChildSHistory
> session_history
= GetSessionHistory();
141 if (!session_history
) {
142 aRv
.Throw(NS_ERROR_FAILURE
);
146 bool userActivation
=
147 win
->GetWindowContext()
148 ? win
->GetWindowContext()->HasValidTransientUserGestureActivation()
151 CallerType callerType
= aSubjectPrincipal
.IsSystemPrincipal()
153 : CallerType::NonSystem
;
155 // AsyncGo throws if we hit the location change rate limit.
156 session_history
->AsyncGo(aDelta
, /* aRequireUserInteraction = */ false,
157 userActivation
, callerType
, aRv
);
160 void nsHistory::Back(CallerType aCallerType
, ErrorResult
& aRv
) {
161 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryReferent(mInnerWindow
));
162 if (!win
|| !win
->HasActiveDocument()) {
163 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
168 RefPtr
<ChildSHistory
> sHistory
= GetSessionHistory();
170 aRv
.Throw(NS_ERROR_FAILURE
);
175 bool userActivation
=
176 win
->GetWindowContext()
177 ? win
->GetWindowContext()->HasValidTransientUserGestureActivation()
180 sHistory
->AsyncGo(-1, /* aRequireUserInteraction = */ false, userActivation
,
184 void nsHistory::Forward(CallerType aCallerType
, ErrorResult
& aRv
) {
185 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryReferent(mInnerWindow
));
186 if (!win
|| !win
->HasActiveDocument()) {
187 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
192 RefPtr
<ChildSHistory
> sHistory
= GetSessionHistory();
194 aRv
.Throw(NS_ERROR_FAILURE
);
199 bool userActivation
=
200 win
->GetWindowContext()
201 ? win
->GetWindowContext()->HasValidTransientUserGestureActivation()
204 sHistory
->AsyncGo(1, /* aRequireUserInteraction = */ false, userActivation
,
208 void nsHistory::PushState(JSContext
* aCx
, JS::Handle
<JS::Value
> aData
,
209 const nsAString
& aTitle
, const nsAString
& aUrl
,
210 CallerType aCallerType
, ErrorResult
& aRv
) {
211 PushOrReplaceState(aCx
, aData
, aTitle
, aUrl
, aCallerType
, aRv
, false);
214 void nsHistory::ReplaceState(JSContext
* aCx
, JS::Handle
<JS::Value
> aData
,
215 const nsAString
& aTitle
, const nsAString
& aUrl
,
216 CallerType aCallerType
, ErrorResult
& aRv
) {
217 PushOrReplaceState(aCx
, aData
, aTitle
, aUrl
, aCallerType
, aRv
, true);
220 void nsHistory::PushOrReplaceState(JSContext
* aCx
, JS::Handle
<JS::Value
> aData
,
221 const nsAString
& aTitle
,
222 const nsAString
& aUrl
,
223 CallerType aCallerType
, ErrorResult
& aRv
,
225 nsCOMPtr
<nsPIDOMWindowInner
> win(do_QueryReferent(mInnerWindow
));
227 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
232 if (!win
->HasActiveDocument()) {
233 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
238 BrowsingContext
* bc
= win
->GetBrowsingContext();
240 nsresult rv
= bc
->CheckLocationChangeRateLimit(aCallerType
);
247 // AddState might run scripts, so we need to hold a strong reference to the
248 // docShell here to keep it from going away.
249 nsCOMPtr
<nsIDocShell
> docShell
= win
->GetDocShell();
252 aRv
.Throw(NS_ERROR_FAILURE
);
257 // The "replace" argument tells the docshell to whether to add a new
258 // history entry or modify the current one.
260 aRv
= docShell
->AddState(aData
, aTitle
, aUrl
, aReplace
, aCx
);
263 already_AddRefed
<ChildSHistory
> nsHistory::GetSessionHistory() const {
264 nsCOMPtr
<nsPIDOMWindowInner
> win
= do_QueryReferent(mInnerWindow
);
265 NS_ENSURE_TRUE(win
, nullptr);
267 BrowsingContext
* bc
= win
->GetBrowsingContext();
268 NS_ENSURE_TRUE(bc
, nullptr);
270 RefPtr
<ChildSHistory
> childSHistory
= bc
->Top()->GetChildSessionHistory();
271 return childSHistory
.forget();