dom/base/nsHistory.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial