summaryrefslogtreecommitdiff
path: root/dom/base/nsHistory.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/nsHistory.cpp')
-rw-r--r--dom/base/nsHistory.cpp346
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(&currentScrollRestorationIsManual);
+ 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();
+}