nsHistory.cpp (8287B)
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 "mozilla/RefPtr.h" 11 #include "mozilla/dom/Document.h" 12 #include "mozilla/dom/WindowContext.h" 13 #include "nsCOMPtr.h" 14 #include "nsContentUtils.h" 15 #include "nsDocShell.h" 16 #include "nsIDocShell.h" 17 #include "nsIWebNavigation.h" 18 #include "nsPIDOMWindow.h" 19 20 using namespace mozilla; 21 using namespace mozilla::dom; 22 23 extern LazyLogModule gSHistoryLog; 24 25 #define LOG(format) MOZ_LOG(gSHistoryLog, mozilla::LogLevel::Debug, format) 26 27 static bool CheckNavigationRateLimit(BrowsingContext* aContext, 28 CallerType aCallerType, ErrorResult& aRv) { 29 if (aContext) { 30 nsresult rv = aContext->CheckNavigationRateLimit(aCallerType); 31 if (NS_FAILED(rv)) { 32 aRv.Throw(rv); 33 return false; 34 } 35 } 36 37 return true; 38 } 39 40 // 41 // History class implementation 42 // 43 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsHistory) 44 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHistory) 45 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHistory) 46 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHistory) 47 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 48 NS_INTERFACE_MAP_ENTRY(nsISupports) 49 NS_INTERFACE_MAP_END 50 51 nsHistory::nsHistory(nsPIDOMWindowInner* aInnerWindow) 52 : mInnerWindow(do_GetWeakReference(aInnerWindow)) {} 53 54 nsHistory::~nsHistory() = default; 55 56 nsPIDOMWindowInner* nsHistory::GetParentObject() const { 57 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); 58 return win; 59 } 60 61 JSObject* nsHistory::WrapObject(JSContext* aCx, 62 JS::Handle<JSObject*> aGivenProto) { 63 return History_Binding::Wrap(aCx, this, aGivenProto); 64 } 65 66 uint32_t nsHistory::GetLength(ErrorResult& aRv) const { 67 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); 68 if (!win || !win->HasActiveDocument()) { 69 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 70 71 return 0; 72 } 73 74 // Get session History from docshell 75 RefPtr<ChildSHistory> sHistory = GetSessionHistory(); 76 if (!sHistory) { 77 return 1; 78 } 79 80 int32_t len = sHistory->Count(); 81 return len >= 0 ? len : 0; 82 } 83 84 ScrollRestoration nsHistory::GetScrollRestoration( 85 mozilla::dom::CallerType aCallerType, mozilla::ErrorResult& aRv) { 86 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); 87 if (!win || !win->HasActiveDocument() || !win->GetDocShell()) { 88 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 89 return mozilla::dom::ScrollRestoration::Auto; 90 } 91 92 bool currentScrollRestorationIsManual = false; 93 win->GetDocShell()->GetCurrentScrollRestorationIsManual( 94 ¤tScrollRestorationIsManual); 95 return currentScrollRestorationIsManual 96 ? mozilla::dom::ScrollRestoration::Manual 97 : mozilla::dom::ScrollRestoration::Auto; 98 } 99 100 void nsHistory::SetScrollRestoration(mozilla::dom::ScrollRestoration aMode, 101 mozilla::dom::CallerType aCallerType, 102 mozilla::ErrorResult& aRv) { 103 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); 104 if (!win || !win->HasActiveDocument() || !win->GetDocShell()) { 105 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 106 return; 107 } 108 109 if (!CheckNavigationRateLimit(win->GetBrowsingContext(), aCallerType, aRv)) { 110 return; 111 } 112 113 win->GetDocShell()->SetCurrentScrollRestorationIsManual( 114 aMode == mozilla::dom::ScrollRestoration::Manual); 115 } 116 117 void nsHistory::GetState(JSContext* aCx, JS::MutableHandle<JS::Value> aResult, 118 ErrorResult& aRv) const { 119 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); 120 if (!win) { 121 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 122 return; 123 } 124 125 if (!win->HasActiveDocument()) { 126 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 127 return; 128 } 129 130 nsCOMPtr<Document> doc = win->GetExtantDoc(); 131 if (!doc) { 132 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 133 return; 134 } 135 136 aRv = doc->GetStateObject(aResult); 137 } 138 139 // https://html.spec.whatwg.org/#dom-history-go 140 void nsHistory::Go(JSContext* aCx, int32_t aDelta, CallerType aCallerType, 141 ErrorResult& aRv) { 142 DeltaTraverse(Some(WrapNotNull(aCx)), aDelta, aCallerType, aRv); 143 } 144 145 // https://html.spec.whatwg.org/#dom-history-back 146 void nsHistory::Back(CallerType aCallerType, ErrorResult& aRv) { 147 DeltaTraverse(Nothing(), -1, aCallerType, aRv); 148 } 149 150 // https://html.spec.whatwg.org/#dom-history-forward 151 void nsHistory::Forward(CallerType aCallerType, ErrorResult& aRv) { 152 DeltaTraverse(Nothing(), 1, aCallerType, aRv); 153 } 154 155 void nsHistory::PushState(JSContext* aCx, JS::Handle<JS::Value> aData, 156 const nsAString& aTitle, const nsAString& aUrl, 157 CallerType aCallerType, ErrorResult& aRv) { 158 PushOrReplaceState(aCx, aData, aTitle, aUrl, aCallerType, aRv, false); 159 } 160 161 void nsHistory::ReplaceState(JSContext* aCx, JS::Handle<JS::Value> aData, 162 const nsAString& aTitle, const nsAString& aUrl, 163 CallerType aCallerType, ErrorResult& aRv) { 164 PushOrReplaceState(aCx, aData, aTitle, aUrl, aCallerType, aRv, true); 165 } 166 167 void nsHistory::PushOrReplaceState(JSContext* aCx, JS::Handle<JS::Value> aData, 168 const nsAString& aTitle, 169 const nsAString& aUrl, 170 CallerType aCallerType, ErrorResult& aRv, 171 bool aReplace) { 172 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); 173 if (!win) { 174 aRv.Throw(NS_ERROR_NOT_AVAILABLE); 175 176 return; 177 } 178 179 if (!win->HasActiveDocument()) { 180 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 181 182 return; 183 } 184 185 if (!CheckNavigationRateLimit(win->GetBrowsingContext(), aCallerType, aRv)) { 186 return; 187 } 188 189 // AddState might run scripts, so we need to hold a strong reference to the 190 // docShell here to keep it from going away. 191 nsCOMPtr<nsIDocShell> docShell = win->GetDocShell(); 192 193 if (!docShell) { 194 aRv.Throw(NS_ERROR_FAILURE); 195 196 return; 197 } 198 199 // The "replace" argument tells the docshell to whether to add a new 200 // history entry or modify the current one. 201 202 aRv = docShell->AddState(aData, aTitle, aUrl, aReplace, aCx); 203 } 204 205 already_AddRefed<ChildSHistory> nsHistory::GetSessionHistory() const { 206 nsCOMPtr<nsPIDOMWindowInner> win = do_QueryReferent(mInnerWindow); 207 NS_ENSURE_TRUE(win, nullptr); 208 209 BrowsingContext* bc = win->GetBrowsingContext(); 210 NS_ENSURE_TRUE(bc, nullptr); 211 212 RefPtr<ChildSHistory> childSHistory = bc->Top()->GetChildSessionHistory(); 213 return childSHistory.forget(); 214 } 215 216 // https://html.spec.whatwg.org/#delta-traverse 217 void nsHistory::DeltaTraverse(mozilla::Maybe<NotNull<JSContext*>> aCx, 218 int32_t aDelta, CallerType aCallerType, 219 ErrorResult& aRv) { 220 LOG(("nsHistory::Go(%d)", aDelta)); 221 // Step 1, but instead of Document we operate on the inner window in this and 222 // following steps. 223 nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); 224 // Step 2 225 if (!win || !win->IsFullyActive()) { 226 aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); 227 return; 228 } 229 230 if (!CheckNavigationRateLimit(win->GetBrowsingContext(), aCallerType, aRv)) { 231 MOZ_LOG(gSHistoryLog, LogLevel::Debug, ("Rejected")); 232 return; 233 } 234 235 // Step 3 236 if (!aDelta) { 237 MOZ_DIAGNOSTIC_ASSERT(aCx); 238 RefPtr<nsDocShell> docShell = nsDocShell::Cast(win->GetDocShell()); 239 240 nsresult rv = docShell->ReloadNavigable(WrapNotNull(aCx), 241 nsIWebNavigation::LOAD_FLAGS_NONE); 242 if (NS_FAILED(rv)) { 243 aRv.Throw(rv); 244 } 245 return; 246 } 247 248 // Step 4 is the remainder of this method. 249 RefPtr<ChildSHistory> session_history = GetSessionHistory(); 250 if (!session_history) { 251 aRv.Throw(NS_ERROR_FAILURE); 252 return; 253 } 254 255 bool userActivation = 256 win->GetWindowContext() 257 ? win->GetWindowContext()->HasValidTransientUserGestureActivation() 258 : false; 259 260 session_history->AsyncGo(aDelta, /* aRequireUserInteraction = */ false, 261 userActivation); 262 }