diff options
Diffstat (limited to 'dom/base/nsHistory.cpp')
-rw-r--r-- | dom/base/nsHistory.cpp | 346 |
1 files changed, 346 insertions, 0 deletions
diff --git a/dom/base/nsHistory.cpp b/dom/base/nsHistory.cpp new file mode 100644 index 0000000000..3459df4b0c --- /dev/null +++ b/dom/base/nsHistory.cpp @@ -0,0 +1,346 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsHistory.h" + +#include "jsapi.h" +#include "nsCOMPtr.h" +#include "nsPIDOMWindow.h" +#include "nsIDocument.h" +#include "nsIPresShell.h" +#include "nsPresContext.h" +#include "nsIDocShell.h" +#include "nsIWebNavigation.h" +#include "nsIURI.h" +#include "nsIInterfaceRequestorUtils.h" +#include "nsReadableUtils.h" +#include "nsContentUtils.h" +#include "nsISHistory.h" +#include "nsISHistoryInternal.h" +#include "mozilla/Preferences.h" + +using namespace mozilla; +using namespace mozilla::dom; + +// +// History class implementation +// +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsHistory) +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsHistory) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsHistory) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsHistory) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) + NS_INTERFACE_MAP_ENTRY(nsIDOMHistory) // Empty, needed for extension compat +NS_INTERFACE_MAP_END + +nsHistory::nsHistory(nsPIDOMWindowInner* aInnerWindow) + : mInnerWindow(do_GetWeakReference(aInnerWindow)) +{ + MOZ_ASSERT(aInnerWindow->IsInnerWindow()); +} + +nsHistory::~nsHistory() +{ +} + +nsPIDOMWindowInner* +nsHistory::GetParentObject() const +{ + nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); + return win; +} + +JSObject* +nsHistory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) +{ + return HistoryBinding::Wrap(aCx, this, aGivenProto); +} + +uint32_t +nsHistory::GetLength(ErrorResult& aRv) const +{ + nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); + if (!win || !win->HasActiveDocument()) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + + return 0; + } + + // Get session History from docshell + nsCOMPtr<nsISHistory> sHistory = GetSessionHistory(); + if (!sHistory) { + aRv.Throw(NS_ERROR_FAILURE); + + return 0; + } + + int32_t len; + nsresult rv = sHistory->GetCount(&len); + + if (NS_FAILED(rv)) { + aRv.Throw(rv); + + return 0; + } + + return len >= 0 ? len : 0; +} + +ScrollRestoration +nsHistory::GetScrollRestoration(mozilla::ErrorResult& aRv) +{ + nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); + if (!win || !win->HasActiveDocument() || !win->GetDocShell()) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return mozilla::dom::ScrollRestoration::Auto; + } + + bool currentScrollRestorationIsManual = false; + win->GetDocShell()-> + GetCurrentScrollRestorationIsManual(¤tScrollRestorationIsManual); + return currentScrollRestorationIsManual ? + mozilla::dom::ScrollRestoration::Manual : + mozilla::dom::ScrollRestoration::Auto; +} + +void +nsHistory::SetScrollRestoration(mozilla::dom::ScrollRestoration aMode, + mozilla::ErrorResult& aRv) +{ + nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); + if (!win || !win->HasActiveDocument() || !win->GetDocShell()) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + + win->GetDocShell()-> + SetCurrentScrollRestorationIsManual( + aMode == mozilla::dom::ScrollRestoration::Manual); +} + +void +nsHistory::GetState(JSContext* aCx, JS::MutableHandle<JS::Value> aResult, + ErrorResult& aRv) const +{ + nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); + if (!win) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + return; + } + + if (!win->HasActiveDocument()) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + return; + } + + nsCOMPtr<nsIDocument> doc = + do_QueryInterface(win->GetExtantDoc()); + if (!doc) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + return; + } + + nsCOMPtr<nsIVariant> variant; + doc->GetStateObject(getter_AddRefs(variant)); + + if (variant) { + aRv = variant->GetAsJSVal(aResult); + + if (aRv.Failed()) { + return; + } + + if (!JS_WrapValue(aCx, aResult)) { + aRv.Throw(NS_ERROR_OUT_OF_MEMORY); + } + + return; + } + + aResult.setNull(); +} + +void +nsHistory::Go(int32_t aDelta, ErrorResult& aRv) +{ + nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); + if (!win || !win->HasActiveDocument()) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + + return; + } + + if (!aDelta) { + nsCOMPtr<nsPIDOMWindowOuter> window; + if (nsIDocShell* docShell = GetDocShell()) { + window = docShell->GetWindow(); + } + + if (window && window->IsHandlingResizeEvent()) { + // history.go(0) (aka location.reload()) was called on a window + // that is handling a resize event. Sites do this since Netscape + // 4.x needed it, but we don't, and it's a horrible experience + // for nothing. In stead of reloading the page, just clear + // style data and reflow the page since some sites may use this + // trick to work around gecko reflow bugs, and this should have + // the same effect. + + nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); + + nsIPresShell *shell; + nsPresContext *pcx; + if (doc && (shell = doc->GetShell()) && (pcx = shell->GetPresContext())) { + pcx->RebuildAllStyleData(NS_STYLE_HINT_REFLOW, eRestyle_Subtree); + } + + return; + } + } + + nsCOMPtr<nsISHistory> session_history = GetSessionHistory(); + nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(session_history)); + if (!webnav) { + aRv.Throw(NS_ERROR_FAILURE); + + return; + } + + int32_t curIndex = -1; + int32_t len = 0; + session_history->GetIndex(&curIndex); + session_history->GetCount(&len); + + int32_t index = curIndex + aDelta; + if (index > -1 && index < len) + webnav->GotoIndex(index); + + // Ignore the return value from GotoIndex(), since returning errors + // from GotoIndex() can lead to exceptions and a possible leak + // of history length +} + +void +nsHistory::Back(ErrorResult& aRv) +{ + nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); + if (!win || !win->HasActiveDocument()) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + + return; + } + + nsCOMPtr<nsISHistory> sHistory = GetSessionHistory(); + nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(sHistory)); + if (!webNav) { + aRv.Throw(NS_ERROR_FAILURE); + + return; + } + + webNav->GoBack(); +} + +void +nsHistory::Forward(ErrorResult& aRv) +{ + nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); + if (!win || !win->HasActiveDocument()) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + + return; + } + + nsCOMPtr<nsISHistory> sHistory = GetSessionHistory(); + nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(sHistory)); + if (!webNav) { + aRv.Throw(NS_ERROR_FAILURE); + + return; + } + + webNav->GoForward(); +} + +void +nsHistory::PushState(JSContext* aCx, JS::Handle<JS::Value> aData, + const nsAString& aTitle, const nsAString& aUrl, + ErrorResult& aRv) +{ + PushOrReplaceState(aCx, aData, aTitle, aUrl, aRv, false); +} + +void +nsHistory::ReplaceState(JSContext* aCx, JS::Handle<JS::Value> aData, + const nsAString& aTitle, const nsAString& aUrl, + ErrorResult& aRv) +{ + PushOrReplaceState(aCx, aData, aTitle, aUrl, aRv, true); +} + +void +nsHistory::PushOrReplaceState(JSContext* aCx, JS::Handle<JS::Value> aData, + const nsAString& aTitle, const nsAString& aUrl, + ErrorResult& aRv, bool aReplace) +{ + nsCOMPtr<nsPIDOMWindowInner> win(do_QueryReferent(mInnerWindow)); + if (!win) { + aRv.Throw(NS_ERROR_NOT_AVAILABLE); + + return; + } + + if (!win->HasActiveDocument()) { + aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); + + return; + } + + // AddState might run scripts, so we need to hold a strong reference to the + // docShell here to keep it from going away. + nsCOMPtr<nsIDocShell> docShell = win->GetDocShell(); + + if (!docShell) { + aRv.Throw(NS_ERROR_FAILURE); + + return; + } + + // The "replace" argument tells the docshell to whether to add a new + // history entry or modify the current one. + + aRv = docShell->AddState(aData, aTitle, aUrl, aReplace, aCx); +} + +nsIDocShell* +nsHistory::GetDocShell() const +{ + nsCOMPtr<nsPIDOMWindowInner> win = do_QueryReferent(mInnerWindow); + if (!win) { + return nullptr; + } + return win->GetDocShell(); +} + +already_AddRefed<nsISHistory> +nsHistory::GetSessionHistory() const +{ + nsIDocShell *docShell = GetDocShell(); + NS_ENSURE_TRUE(docShell, nullptr); + + // Get the root DocShell from it + nsCOMPtr<nsIDocShellTreeItem> root; + docShell->GetSameTypeRootTreeItem(getter_AddRefs(root)); + nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(root)); + NS_ENSURE_TRUE(webNav, nullptr); + + nsCOMPtr<nsISHistory> shistory; + + // Get SH from nsIWebNavigation + webNav->GetSessionHistory(getter_AddRefs(shistory)); + + return shistory.forget(); +} |