1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=78: */
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/. */
10 #include "mozilla/dom/HistoryBinding.h"
12 #include "nsPIDOMWindow.h"
13 #include "nsIDocument.h"
14 #include "nsIPresShell.h"
15 #include "nsPresContext.h"
16 #include "nsIDocShell.h"
17 #include "nsIWebNavigation.h"
19 #include "nsIInterfaceRequestorUtils.h"
20 #include "nsReadableUtils.h"
21 #include "nsContentUtils.h"
22 #include "nsISHistory.h"
23 #include "nsISHistoryInternal.h"
24 #include "mozilla/Preferences.h"
26 using namespace mozilla
;
27 using namespace mozilla::dom
;
29 static const char* sAllowPushStatePrefStr
=
30 "browser.history.allowPushState";
31 static const char* sAllowReplaceStatePrefStr
=
32 "browser.history.allowReplaceState";
35 // History class implementation
37 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsHistory
)
38 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHistory
)
39 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHistory
)
40 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHistory
)
41 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
42 NS_INTERFACE_MAP_ENTRY(nsISupports
)
43 NS_INTERFACE_MAP_ENTRY(nsIDOMHistory
) // Empty, needed for extension compat
46 nsHistory::nsHistory(nsPIDOMWindow
* aInnerWindow
)
47 : mInnerWindow(do_GetWeakReference(aInnerWindow
))
51 nsHistory::~nsHistory()
56 nsHistory::GetParentObject() const
58 nsCOMPtr
<nsPIDOMWindow
> win(do_QueryReferent(mInnerWindow
));
63 nsHistory::WrapObject(JSContext
* aCx
)
65 return HistoryBinding::Wrap(aCx
, this);
69 nsHistory::GetLength(ErrorResult
& aRv
) const
71 nsCOMPtr
<nsPIDOMWindow
> win(do_QueryReferent(mInnerWindow
));
72 if (!win
|| !win
->HasActiveDocument()) {
73 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
78 // Get session History from docshell
79 nsCOMPtr
<nsISHistory
> sHistory
= GetSessionHistory();
81 aRv
.Throw(NS_ERROR_FAILURE
);
87 nsresult rv
= sHistory
->GetCount(&len
);
95 return len
>= 0 ? len
: 0;
99 nsHistory::GetState(JSContext
* aCx
, JS::MutableHandle
<JS::Value
> aResult
,
100 ErrorResult
& aRv
) const
102 nsCOMPtr
<nsPIDOMWindow
> win(do_QueryReferent(mInnerWindow
));
104 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
108 if (!win
->HasActiveDocument()) {
109 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
113 nsCOMPtr
<nsIDocument
> doc
=
114 do_QueryInterface(win
->GetExtantDoc());
116 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
120 nsCOMPtr
<nsIVariant
> variant
;
121 doc
->GetStateObject(getter_AddRefs(variant
));
124 aRv
= variant
->GetAsJSVal(aResult
);
130 if (!JS_WrapValue(aCx
, aResult
)) {
131 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
141 nsHistory::Go(int32_t aDelta
, ErrorResult
& aRv
)
143 nsCOMPtr
<nsPIDOMWindow
> win(do_QueryReferent(mInnerWindow
));
144 if (!win
|| !win
->HasActiveDocument()) {
145 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
151 nsCOMPtr
<nsPIDOMWindow
> window
;
152 if (nsIDocShell
* docShell
= GetDocShell()) {
153 window
= docShell
->GetWindow();
156 if (window
&& window
->IsHandlingResizeEvent()) {
157 // history.go(0) (aka location.reload()) was called on a window
158 // that is handling a resize event. Sites do this since Netscape
159 // 4.x needed it, but we don't, and it's a horrible experience
160 // for nothing. In stead of reloading the page, just clear
161 // style data and reflow the page since some sites may use this
162 // trick to work around gecko reflow bugs, and this should have
165 nsCOMPtr
<nsIDocument
> doc
= window
->GetExtantDoc();
169 if (doc
&& (shell
= doc
->GetShell()) && (pcx
= shell
->GetPresContext())) {
170 pcx
->RebuildAllStyleData(NS_STYLE_HINT_REFLOW
, eRestyle_Subtree
);
177 nsCOMPtr
<nsISHistory
> session_history
= GetSessionHistory();
178 nsCOMPtr
<nsIWebNavigation
> webnav(do_QueryInterface(session_history
));
180 aRv
.Throw(NS_ERROR_FAILURE
);
185 int32_t curIndex
= -1;
187 session_history
->GetIndex(&curIndex
);
188 session_history
->GetCount(&len
);
190 int32_t index
= curIndex
+ aDelta
;
191 if (index
> -1 && index
< len
)
192 webnav
->GotoIndex(index
);
194 // Ignore the return value from GotoIndex(), since returning errors
195 // from GotoIndex() can lead to exceptions and a possible leak
200 nsHistory::Back(ErrorResult
& aRv
)
202 nsCOMPtr
<nsPIDOMWindow
> win(do_QueryReferent(mInnerWindow
));
203 if (!win
|| !win
->HasActiveDocument()) {
204 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
209 nsCOMPtr
<nsISHistory
> sHistory
= GetSessionHistory();
210 nsCOMPtr
<nsIWebNavigation
> webNav(do_QueryInterface(sHistory
));
212 aRv
.Throw(NS_ERROR_FAILURE
);
221 nsHistory::Forward(ErrorResult
& aRv
)
223 nsCOMPtr
<nsPIDOMWindow
> win(do_QueryReferent(mInnerWindow
));
224 if (!win
|| !win
->HasActiveDocument()) {
225 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
230 nsCOMPtr
<nsISHistory
> sHistory
= GetSessionHistory();
231 nsCOMPtr
<nsIWebNavigation
> webNav(do_QueryInterface(sHistory
));
233 aRv
.Throw(NS_ERROR_FAILURE
);
242 nsHistory::PushState(JSContext
* aCx
, JS::Handle
<JS::Value
> aData
,
243 const nsAString
& aTitle
, const nsAString
& aUrl
,
246 PushOrReplaceState(aCx
, aData
, aTitle
, aUrl
, aRv
, false);
250 nsHistory::ReplaceState(JSContext
* aCx
, JS::Handle
<JS::Value
> aData
,
251 const nsAString
& aTitle
, const nsAString
& aUrl
,
254 PushOrReplaceState(aCx
, aData
, aTitle
, aUrl
, aRv
, true);
258 nsHistory::PushOrReplaceState(JSContext
* aCx
, JS::Handle
<JS::Value
> aData
,
259 const nsAString
& aTitle
, const nsAString
& aUrl
,
260 ErrorResult
& aRv
, bool aReplace
)
262 nsCOMPtr
<nsPIDOMWindow
> win(do_QueryReferent(mInnerWindow
));
264 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
269 if (!win
->HasActiveDocument()) {
270 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
275 // Check that PushState hasn't been pref'ed off.
276 if (!Preferences::GetBool(aReplace
? sAllowReplaceStatePrefStr
:
277 sAllowPushStatePrefStr
, false)) {
281 // AddState might run scripts, so we need to hold a strong reference to the
282 // docShell here to keep it from going away.
283 nsCOMPtr
<nsIDocShell
> docShell
= win
->GetDocShell();
286 aRv
.Throw(NS_ERROR_FAILURE
);
291 // The "replace" argument tells the docshell to whether to add a new
292 // history entry or modify the current one.
294 aRv
= docShell
->AddState(aData
, aTitle
, aUrl
, aReplace
, aCx
);
298 nsHistory::GetDocShell() const
300 nsCOMPtr
<nsPIDOMWindow
> win
= do_QueryReferent(mInnerWindow
);
304 return win
->GetDocShell();
307 already_AddRefed
<nsISHistory
>
308 nsHistory::GetSessionHistory() const
310 nsIDocShell
*docShell
= GetDocShell();
311 NS_ENSURE_TRUE(docShell
, nullptr);
313 // Get the root DocShell from it
314 nsCOMPtr
<nsIDocShellTreeItem
> root
;
315 docShell
->GetSameTypeRootTreeItem(getter_AddRefs(root
));
316 nsCOMPtr
<nsIWebNavigation
> webNav(do_QueryInterface(root
));
317 NS_ENSURE_TRUE(webNav
, nullptr);
319 nsCOMPtr
<nsISHistory
> shistory
;
321 // Get SH from nsIWebNavigation
322 webNav
->GetSessionHistory(getter_AddRefs(shistory
));
324 return shistory
.forget();