Line data Source code
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/. */
6 :
7 : #include "nsHistory.h"
8 :
9 : #include "jsapi.h"
10 : #include "nsCOMPtr.h"
11 : #include "nsPIDOMWindow.h"
12 : #include "nsIDocument.h"
13 : #include "nsIPresShell.h"
14 : #include "nsPresContext.h"
15 : #include "nsIDocShell.h"
16 : #include "nsIWebNavigation.h"
17 : #include "nsIURI.h"
18 : #include "nsIInterfaceRequestorUtils.h"
19 : #include "nsReadableUtils.h"
20 : #include "nsContentUtils.h"
21 : #include "nsISHistory.h"
22 : #include "nsISHistoryInternal.h"
23 : #include "mozilla/Preferences.h"
24 :
25 : using namespace mozilla;
26 : using namespace mozilla::dom;
27 :
28 : //
29 : // History class implementation
30 : //
31 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsHistory)
32 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHistory)
33 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHistory)
34 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHistory)
35 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
36 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
37 0 : NS_INTERFACE_MAP_ENTRY(nsIDOMHistory) // Empty, needed for extension compat
38 0 : NS_INTERFACE_MAP_END
39 :
40 0 : nsHistory::nsHistory(nsPIDOMWindowInner* aInnerWindow)
41 0 : : mInnerWindow(do_GetWeakReference(aInnerWindow))
42 : {
43 0 : MOZ_ASSERT(aInnerWindow->IsInnerWindow());
44 0 : }
45 :
46 0 : nsHistory::~nsHistory()
47 : {
48 0 : }
49 :
50 : nsPIDOMWindowInner*
51 0 : nsHistory::GetParentObject() const
52 : {
53 0 : nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
54 0 : return win;
55 : }
56 :
57 : JSObject*
58 0 : nsHistory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
59 : {
60 0 : return HistoryBinding::Wrap(aCx, this, aGivenProto);
61 : }
62 :
63 : uint32_t
64 0 : nsHistory::GetLength(ErrorResult& aRv) const
65 : {
66 0 : nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
67 0 : if (!win || !win->HasActiveDocument()) {
68 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
69 :
70 0 : return 0;
71 : }
72 :
73 : // Get session History from docshell
74 0 : nsCOMPtr<nsISHistory> sHistory = GetSessionHistory();
75 0 : if (!sHistory) {
76 0 : aRv.Throw(NS_ERROR_FAILURE);
77 :
78 0 : return 0;
79 : }
80 :
81 : int32_t len;
82 0 : nsresult rv = sHistory->GetGlobalCount(&len);
83 :
84 0 : if (NS_FAILED(rv)) {
85 0 : aRv.Throw(rv);
86 :
87 0 : return 0;
88 : }
89 :
90 0 : return len >= 0 ? len : 0;
91 : }
92 :
93 : ScrollRestoration
94 0 : nsHistory::GetScrollRestoration(mozilla::ErrorResult& aRv)
95 : {
96 0 : nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
97 0 : if (!win || !win->HasActiveDocument() || !win->GetDocShell()) {
98 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
99 0 : return mozilla::dom::ScrollRestoration::Auto;
100 : }
101 :
102 0 : bool currentScrollRestorationIsManual = false;
103 0 : win->GetDocShell()->
104 0 : GetCurrentScrollRestorationIsManual(¤tScrollRestorationIsManual);
105 0 : return currentScrollRestorationIsManual ?
106 : mozilla::dom::ScrollRestoration::Manual :
107 0 : mozilla::dom::ScrollRestoration::Auto;
108 : }
109 :
110 : void
111 0 : nsHistory::SetScrollRestoration(mozilla::dom::ScrollRestoration aMode,
112 : mozilla::ErrorResult& aRv)
113 : {
114 0 : nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
115 0 : if (!win || !win->HasActiveDocument() || !win->GetDocShell()) {
116 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
117 0 : return;
118 : }
119 :
120 0 : win->GetDocShell()->
121 0 : SetCurrentScrollRestorationIsManual(
122 0 : aMode == mozilla::dom::ScrollRestoration::Manual);
123 : }
124 :
125 : void
126 0 : nsHistory::GetState(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
127 : ErrorResult& aRv) const
128 : {
129 0 : nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
130 0 : if (!win) {
131 0 : aRv.Throw(NS_ERROR_NOT_AVAILABLE);
132 0 : return;
133 : }
134 :
135 0 : if (!win->HasActiveDocument()) {
136 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
137 0 : return;
138 : }
139 :
140 : nsCOMPtr<nsIDocument> doc =
141 0 : do_QueryInterface(win->GetExtantDoc());
142 0 : if (!doc) {
143 0 : aRv.Throw(NS_ERROR_NOT_AVAILABLE);
144 0 : return;
145 : }
146 :
147 0 : nsCOMPtr<nsIVariant> variant;
148 0 : doc->GetStateObject(getter_AddRefs(variant));
149 :
150 0 : if (variant) {
151 0 : aRv = variant->GetAsJSVal(aResult);
152 :
153 0 : if (aRv.Failed()) {
154 0 : return;
155 : }
156 :
157 0 : if (!JS_WrapValue(aCx, aResult)) {
158 0 : aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
159 : }
160 :
161 0 : return;
162 : }
163 :
164 0 : aResult.setNull();
165 : }
166 :
167 : void
168 0 : nsHistory::Go(int32_t aDelta, ErrorResult& aRv)
169 : {
170 0 : nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
171 0 : if (!win || !win->HasActiveDocument()) {
172 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
173 :
174 0 : return;
175 : }
176 :
177 0 : if (!aDelta) {
178 0 : nsCOMPtr<nsPIDOMWindowOuter> window;
179 0 : if (nsIDocShell* docShell = GetDocShell()) {
180 0 : window = docShell->GetWindow();
181 : }
182 :
183 0 : if (window && window->IsHandlingResizeEvent()) {
184 : // history.go(0) (aka location.reload()) was called on a window
185 : // that is handling a resize event. Sites do this since Netscape
186 : // 4.x needed it, but we don't, and it's a horrible experience
187 : // for nothing. In stead of reloading the page, just clear
188 : // style data and reflow the page since some sites may use this
189 : // trick to work around gecko reflow bugs, and this should have
190 : // the same effect.
191 :
192 0 : nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
193 :
194 : nsIPresShell *shell;
195 : nsPresContext *pcx;
196 0 : if (doc && (shell = doc->GetShell()) && (pcx = shell->GetPresContext())) {
197 0 : pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW, eRestyle_Subtree);
198 : }
199 :
200 0 : return;
201 : }
202 : }
203 :
204 0 : nsCOMPtr<nsISHistory> session_history = GetSessionHistory();
205 0 : nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(session_history));
206 0 : if (!webnav) {
207 0 : aRv.Throw(NS_ERROR_FAILURE);
208 :
209 0 : return;
210 : }
211 :
212 0 : int32_t curIndex = -1;
213 0 : int32_t len = 0;
214 0 : session_history->GetGlobalIndex(&curIndex);
215 0 : session_history->GetGlobalCount(&len);
216 :
217 0 : int32_t index = curIndex + aDelta;
218 0 : if (index > -1 && index < len)
219 0 : webnav->GotoIndex(index);
220 :
221 : // Ignore the return value from GotoIndex(), since returning errors
222 : // from GotoIndex() can lead to exceptions and a possible leak
223 : // of history length
224 : }
225 :
226 : void
227 0 : nsHistory::Back(ErrorResult& aRv)
228 : {
229 0 : nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
230 0 : if (!win || !win->HasActiveDocument()) {
231 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
232 :
233 0 : return;
234 : }
235 :
236 0 : nsCOMPtr<nsISHistory> sHistory = GetSessionHistory();
237 0 : nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(sHistory));
238 0 : if (!webNav) {
239 0 : aRv.Throw(NS_ERROR_FAILURE);
240 :
241 0 : return;
242 : }
243 :
244 0 : webNav->GoBack();
245 : }
246 :
247 : void
248 0 : nsHistory::Forward(ErrorResult& aRv)
249 : {
250 0 : nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
251 0 : if (!win || !win->HasActiveDocument()) {
252 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
253 :
254 0 : return;
255 : }
256 :
257 0 : nsCOMPtr<nsISHistory> sHistory = GetSessionHistory();
258 0 : nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(sHistory));
259 0 : if (!webNav) {
260 0 : aRv.Throw(NS_ERROR_FAILURE);
261 :
262 0 : return;
263 : }
264 :
265 0 : webNav->GoForward();
266 : }
267 :
268 : void
269 0 : nsHistory::PushState(JSContext* aCx, JS::Handle<JS::Value> aData,
270 : const nsAString& aTitle, const nsAString& aUrl,
271 : ErrorResult& aRv)
272 : {
273 0 : PushOrReplaceState(aCx, aData, aTitle, aUrl, aRv, false);
274 0 : }
275 :
276 : void
277 0 : nsHistory::ReplaceState(JSContext* aCx, JS::Handle<JS::Value> aData,
278 : const nsAString& aTitle, const nsAString& aUrl,
279 : ErrorResult& aRv)
280 : {
281 0 : PushOrReplaceState(aCx, aData, aTitle, aUrl, aRv, true);
282 0 : }
283 :
284 : void
285 0 : nsHistory::PushOrReplaceState(JSContext* aCx, JS::Handle<JS::Value> aData,
286 : const nsAString& aTitle, const nsAString& aUrl,
287 : ErrorResult& aRv, bool aReplace)
288 : {
289 0 : nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
290 0 : if (!win) {
291 0 : aRv.Throw(NS_ERROR_NOT_AVAILABLE);
292 :
293 0 : return;
294 : }
295 :
296 0 : if (!win->HasActiveDocument()) {
297 0 : aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
298 :
299 0 : return;
300 : }
301 :
302 : // AddState might run scripts, so we need to hold a strong reference to the
303 : // docShell here to keep it from going away.
304 0 : nsCOMPtr<nsIDocShell> docShell = win->GetDocShell();
305 :
306 0 : if (!docShell) {
307 0 : aRv.Throw(NS_ERROR_FAILURE);
308 :
309 0 : return;
310 : }
311 :
312 : // The "replace" argument tells the docshell to whether to add a new
313 : // history entry or modify the current one.
314 :
315 0 : aRv = docShell->AddState(aData, aTitle, aUrl, aReplace, aCx);
316 : }
317 :
318 : nsIDocShell*
319 0 : nsHistory::GetDocShell() const
320 : {
321 0 : nsCOMPtr<nsPIDOMWindowInner> win = do_QueryReferent(mInnerWindow);
322 0 : if (!win) {
323 0 : return nullptr;
324 : }
325 0 : return win->GetDocShell();
326 : }
327 :
328 : already_AddRefed<nsISHistory>
329 0 : nsHistory::GetSessionHistory() const
330 : {
331 0 : nsIDocShell *docShell = GetDocShell();
332 0 : NS_ENSURE_TRUE(docShell, nullptr);
333 :
334 : // Get the root DocShell from it
335 0 : nsCOMPtr<nsIDocShellTreeItem> root;
336 0 : docShell->GetSameTypeRootTreeItem(getter_AddRefs(root));
337 0 : nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(root));
338 0 : NS_ENSURE_TRUE(webNav, nullptr);
339 :
340 0 : nsCOMPtr<nsISHistory> shistory;
341 :
342 : // Get SH from nsIWebNavigation
343 0 : webNav->GetSessionHistory(getter_AddRefs(shistory));
344 :
345 0 : return shistory.forget();
346 : }
|