Bug 1550519 - Show a translucent parent highlight when a subgrid is highlighted....
[gecko.git] / dom / base / nsHistory.cpp
blob7d573e7ad696402c9039336b104e661f0ea3443d
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 "nsHistory.h"
9 #include "jsapi.h"
10 #include "nsCOMPtr.h"
11 #include "nsPIDOMWindow.h"
12 #include "mozilla/dom/Document.h"
13 #include "mozilla/dom/DocumentInlines.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 "mozilla/dom/Location.h"
23 #include "mozilla/Preferences.h"
24 #include "mozilla/RefPtr.h"
26 using namespace mozilla;
27 using namespace mozilla::dom;
30 // History class implementation
32 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsHistory)
33 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHistory)
34 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHistory)
35 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHistory)
36 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
37 NS_INTERFACE_MAP_ENTRY(nsISupports)
38 NS_INTERFACE_MAP_END
40 nsHistory::nsHistory(nsPIDOMWindowInner* aInnerWindow)
41 : mInnerWindow(do_GetWeakReference(aInnerWindow)) {}
43 nsHistory::~nsHistory() {}
45 nsPIDOMWindowInner* nsHistory::GetParentObject() const {
46 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
47 return win;
50 JSObject* nsHistory::WrapObject(JSContext* aCx,
51 JS::Handle<JSObject*> aGivenProto) {
52 return History_Binding::Wrap(aCx, this, aGivenProto);
55 uint32_t nsHistory::GetLength(ErrorResult& aRv) const {
56 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
57 if (!win || !win->HasActiveDocument()) {
58 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
60 return 0;
63 // Get session History from docshell
64 RefPtr<ChildSHistory> sHistory = GetSessionHistory();
65 if (!sHistory) {
66 aRv.Throw(NS_ERROR_FAILURE);
68 return 0;
71 int32_t len = sHistory->Count();
73 return len >= 0 ? len : 0;
76 ScrollRestoration nsHistory::GetScrollRestoration(mozilla::ErrorResult& aRv) {
77 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
78 if (!win || !win->HasActiveDocument() || !win->GetDocShell()) {
79 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
80 return mozilla::dom::ScrollRestoration::Auto;
83 bool currentScrollRestorationIsManual = false;
84 win->GetDocShell()->GetCurrentScrollRestorationIsManual(
85 &currentScrollRestorationIsManual);
86 return currentScrollRestorationIsManual
87 ? mozilla::dom::ScrollRestoration::Manual
88 : mozilla::dom::ScrollRestoration::Auto;
91 void nsHistory::SetScrollRestoration(mozilla::dom::ScrollRestoration aMode,
92 mozilla::ErrorResult& aRv) {
93 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
94 if (!win || !win->HasActiveDocument() || !win->GetDocShell()) {
95 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
96 return;
99 win->GetDocShell()->SetCurrentScrollRestorationIsManual(
100 aMode == mozilla::dom::ScrollRestoration::Manual);
103 void nsHistory::GetState(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
104 ErrorResult& aRv) const {
105 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
106 if (!win) {
107 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
108 return;
111 if (!win->HasActiveDocument()) {
112 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
113 return;
116 nsCOMPtr<Document> doc = win->GetExtantDoc();
117 if (!doc) {
118 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
119 return;
122 nsCOMPtr<nsIVariant> variant;
123 doc->GetStateObject(getter_AddRefs(variant));
125 if (variant) {
126 aRv = variant->GetAsJSVal(aResult);
128 if (aRv.Failed()) {
129 return;
132 if (!JS_WrapValue(aCx, aResult)) {
133 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
136 return;
139 aResult.setNull();
142 void nsHistory::Go(int32_t aDelta, ErrorResult& aRv) {
143 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
144 if (!win || !win->HasActiveDocument()) {
145 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
147 return;
150 if (!aDelta) {
151 nsCOMPtr<nsPIDOMWindowOuter> 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
163 // the same effect.
165 nsCOMPtr<Document> doc = window->GetExtantDoc();
167 nsPresContext* pcx;
168 if (doc && (pcx = doc->GetPresContext())) {
169 pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW,
170 RestyleHint::RestyleSubtree());
173 return;
176 // https://html.spec.whatwg.org/multipage/history.html#the-history-interface
177 // "When the go(delta) method is invoked, if delta is zero, the user agent
178 // must act as if the location.reload() method was called instead."
179 RefPtr<Location> location = window ? window->GetLocation() : nullptr;
181 if (location) {
182 nsresult rv = location->Reload(false);
184 if (NS_FAILED(rv)) {
185 aRv.Throw(NS_ERROR_FAILURE);
188 return;
192 RefPtr<ChildSHistory> session_history = GetSessionHistory();
193 if (!session_history) {
194 aRv.Throw(NS_ERROR_FAILURE);
196 return;
199 // Ignore the return value from Go(), since returning errors from Go() can
200 // lead to exceptions and a possible leak of history length
201 session_history->Go(aDelta, IgnoreErrors());
204 void nsHistory::Back(ErrorResult& aRv) {
205 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
206 if (!win || !win->HasActiveDocument()) {
207 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
209 return;
212 RefPtr<ChildSHistory> sHistory = GetSessionHistory();
213 if (!sHistory) {
214 aRv.Throw(NS_ERROR_FAILURE);
216 return;
219 sHistory->Go(-1, IgnoreErrors());
222 void nsHistory::Forward(ErrorResult& aRv) {
223 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
224 if (!win || !win->HasActiveDocument()) {
225 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
227 return;
230 RefPtr<ChildSHistory> sHistory = GetSessionHistory();
231 if (!sHistory) {
232 aRv.Throw(NS_ERROR_FAILURE);
234 return;
237 sHistory->Go(1, IgnoreErrors());
240 void nsHistory::PushState(JSContext* aCx, JS::Handle<JS::Value> aData,
241 const nsAString& aTitle, const nsAString& aUrl,
242 ErrorResult& aRv) {
243 PushOrReplaceState(aCx, aData, aTitle, aUrl, aRv, false);
246 void nsHistory::ReplaceState(JSContext* aCx, JS::Handle<JS::Value> aData,
247 const nsAString& aTitle, const nsAString& aUrl,
248 ErrorResult& aRv) {
249 PushOrReplaceState(aCx, aData, aTitle, aUrl, aRv, true);
252 void nsHistory::PushOrReplaceState(JSContext* aCx, JS::Handle<JS::Value> aData,
253 const nsAString& aTitle,
254 const nsAString& aUrl, ErrorResult& aRv,
255 bool aReplace) {
256 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow));
257 if (!win) {
258 aRv.Throw(NS_ERROR_NOT_AVAILABLE);
260 return;
263 if (!win->HasActiveDocument()) {
264 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
266 return;
269 // AddState might run scripts, so we need to hold a strong reference to the
270 // docShell here to keep it from going away.
271 nsCOMPtr<nsIDocShell> docShell = win->GetDocShell();
273 if (!docShell) {
274 aRv.Throw(NS_ERROR_FAILURE);
276 return;
279 // The "replace" argument tells the docshell to whether to add a new
280 // history entry or modify the current one.
282 aRv = docShell->AddState(aData, aTitle, aUrl, aReplace, aCx);
285 nsIDocShell* nsHistory::GetDocShell() const {
286 nsCOMPtr<nsPIDOMWindowInner> win = do_QueryReferent(mInnerWindow);
287 if (!win) {
288 return nullptr;
290 return win->GetDocShell();
293 already_AddRefed<ChildSHistory> nsHistory::GetSessionHistory() const {
294 nsIDocShell* docShell = GetDocShell();
295 NS_ENSURE_TRUE(docShell, nullptr);
297 // Get the root DocShell from it
298 nsCOMPtr<nsIDocShellTreeItem> root;
299 docShell->GetSameTypeRootTreeItem(getter_AddRefs(root));
300 nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(root));
301 NS_ENSURE_TRUE(webNav, nullptr);
303 // Get SH from nsIWebNavigation
304 return webNav->GetSessionHistory();