tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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      &currentScrollRestorationIsManual);
     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 }