tor-browser

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

ChildSHistory.cpp (12847B)


      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 "mozilla/dom/ChildSHistory.h"
      8 #include "mozilla/dom/ChildSHistoryBinding.h"
      9 #include "mozilla/dom/CanonicalBrowsingContext.h"
     10 #include "mozilla/dom/ContentChild.h"
     11 #include "mozilla/dom/ContentFrameMessageManager.h"
     12 #include "mozilla/StaticPrefs_browser.h"
     13 #include "nsIXULRuntime.h"
     14 #include "nsComponentManagerUtils.h"
     15 #include "nsSHEntry.h"
     16 #include "nsSHistory.h"
     17 #include "nsDocShell.h"
     18 #include "nsXULAppAPI.h"
     19 
     20 extern mozilla::LazyLogModule gSHLog;
     21 
     22 namespace mozilla {
     23 namespace dom {
     24 
     25 ChildSHistory::PendingAsyncHistoryNavigation::PendingAsyncHistoryNavigation(
     26    ChildSHistory* aHistory, int32_t aOffset, bool aRequireUserInteraction,
     27    bool aUserActivation)
     28    : Runnable("PendingAsyncHistoryNavigation"),
     29      mHistory(aHistory),
     30      mRequireUserInteraction(aRequireUserInteraction),
     31      mUserActivation(aUserActivation),
     32      mOffset(aOffset) {}
     33 ChildSHistory::PendingAsyncHistoryNavigation::PendingAsyncHistoryNavigation(
     34    ChildSHistory* aHistory, const nsID& aKey,
     35    BrowsingContext* aBrowsingContext, bool aRequireUserInteraction,
     36    bool aUserActivation, bool aCheckForCancelation,
     37    std::function<void(nsresult)>&& aResolver)
     38    : Runnable("PendingAsyncHistoryNavigation"),
     39      mHistory(aHistory),
     40      mRequireUserInteraction(aRequireUserInteraction),
     41      mUserActivation(aUserActivation),
     42      mCheckForCancelation(aCheckForCancelation),
     43      mOffset(std::numeric_limits<int32_t>::max()),
     44      mKey(Some(aKey)),
     45      mBrowsingContext(aBrowsingContext),
     46      mResolver(Some(aResolver)) {}
     47 
     48 nsresult ChildSHistory::PendingAsyncHistoryNavigation::Run() {
     49  if (isInList()) {
     50    remove();
     51    if (mKey) {
     52      RefPtr browsingContext = mBrowsingContext;
     53      mHistory->GotoKey(*mKey, browsingContext, mRequireUserInteraction,
     54                        mUserActivation, mCheckForCancelation, *mResolver,
     55                        IgnoreErrors());
     56    } else {
     57      mHistory->Go(mOffset, mRequireUserInteraction, mUserActivation,
     58                   IgnoreErrors());
     59    }
     60  }
     61  return NS_OK;
     62 }
     63 
     64 ChildSHistory::ChildSHistory(BrowsingContext* aBrowsingContext)
     65    : mBrowsingContext(aBrowsingContext) {}
     66 
     67 ChildSHistory::~ChildSHistory() {
     68  if (mHistory) {
     69    static_cast<nsSHistory*>(mHistory.get())->SetBrowsingContext(nullptr);
     70  }
     71 }
     72 
     73 void ChildSHistory::SetBrowsingContext(BrowsingContext* aBrowsingContext) {
     74  mBrowsingContext = aBrowsingContext;
     75 }
     76 
     77 void ChildSHistory::SetIsInProcess(bool aIsInProcess) {
     78  if (!aIsInProcess) {
     79    MOZ_ASSERT_IF(mozilla::SessionHistoryInParent(), !mHistory);
     80    if (!mozilla::SessionHistoryInParent()) {
     81      RemovePendingHistoryNavigations();
     82      if (mHistory) {
     83        static_cast<nsSHistory*>(mHistory.get())->SetBrowsingContext(nullptr);
     84        mHistory = nullptr;
     85      }
     86    }
     87 
     88    return;
     89  }
     90 
     91  if (mHistory || mozilla::SessionHistoryInParent()) {
     92    return;
     93  }
     94 
     95  mHistory = new nsSHistory(mBrowsingContext);
     96 }
     97 
     98 int32_t ChildSHistory::Count() {
     99  if (mozilla::SessionHistoryInParent()) {
    100    uint32_t length = mLength;
    101    for (uint32_t i = 0; i < mPendingSHistoryChanges.Length(); ++i) {
    102      length += mPendingSHistoryChanges[i].mLengthDelta;
    103    }
    104 
    105    return length;
    106  }
    107  return mHistory->GetCount();
    108 }
    109 
    110 int32_t ChildSHistory::Index() {
    111  if (mozilla::SessionHistoryInParent()) {
    112    uint32_t index = mIndex;
    113    for (uint32_t i = 0; i < mPendingSHistoryChanges.Length(); ++i) {
    114      index += mPendingSHistoryChanges[i].mIndexDelta;
    115    }
    116 
    117    return index;
    118  }
    119  int32_t index;
    120  mHistory->GetIndex(&index);
    121  return index;
    122 }
    123 
    124 nsID ChildSHistory::AddPendingHistoryChange() {
    125  int32_t indexDelta = 1;
    126  int32_t lengthDelta = (Index() + indexDelta) - (Count() - 1);
    127  return AddPendingHistoryChange(indexDelta, lengthDelta);
    128 }
    129 
    130 nsID ChildSHistory::AddPendingHistoryChange(int32_t aIndexDelta,
    131                                            int32_t aLengthDelta) {
    132  nsID changeID = nsID::GenerateUUID();
    133  PendingSHistoryChange change = {changeID, aIndexDelta, aLengthDelta};
    134  mPendingSHistoryChanges.AppendElement(change);
    135  return changeID;
    136 }
    137 
    138 void ChildSHistory::SetIndexAndLength(uint32_t aIndex, uint32_t aLength,
    139                                      const nsID& aChangeID) {
    140  mIndex = aIndex;
    141  mLength = aLength;
    142  mPendingSHistoryChanges.RemoveElementsBy(
    143      [aChangeID](const PendingSHistoryChange& aChange) {
    144        return aChange.mChangeID == aChangeID;
    145      });
    146 }
    147 
    148 void ChildSHistory::Reload(uint32_t aReloadFlags, ErrorResult& aRv) {
    149  if (mozilla::SessionHistoryInParent()) {
    150    if (XRE_IsParentProcess()) {
    151      nsCOMPtr<nsISHistory> shistory =
    152          mBrowsingContext->Canonical()->GetSessionHistory();
    153      if (shistory) {
    154        aRv = shistory->Reload(aReloadFlags);
    155      }
    156    } else {
    157      ContentChild::GetSingleton()->SendHistoryReload(mBrowsingContext,
    158                                                      aReloadFlags);
    159    }
    160 
    161    return;
    162  }
    163  nsCOMPtr<nsISHistory> shistory = mHistory;
    164  aRv = shistory->Reload(aReloadFlags);
    165 }
    166 
    167 bool ChildSHistory::CanGo(int32_t aOffset, bool aRequireUserInteraction) {
    168  CheckedInt<int32_t> index = Index();
    169  index += aOffset;
    170  if (!index.isValid()) {
    171    return false;
    172  }
    173 
    174  if (!mHistory || aOffset >= 0) {
    175    return index.value() < Count() && index.value() >= 0;
    176  }
    177 
    178  if (!aRequireUserInteraction) {
    179    return index.value() >= 0;
    180  }
    181 
    182  bool canGoBack;
    183  mHistory->CanGoBackFromEntryAtIndex(Index(), &canGoBack);
    184  return canGoBack;
    185 }
    186 
    187 void ChildSHistory::Go(int32_t aOffset, bool aRequireUserInteraction,
    188                       bool aUserActivation, ErrorResult& aRv) {
    189  CheckedInt<int32_t> index = Index();
    190  MOZ_LOG(
    191      gSHLog, LogLevel::Debug,
    192      ("ChildSHistory::Go(%d), current index = %d", aOffset, index.value()));
    193  if (aRequireUserInteraction && aOffset != -1 && aOffset != 1) {
    194    NS_ERROR(
    195        "aRequireUserInteraction may only be used with an offset of -1 or 1");
    196    aRv.Throw(NS_ERROR_INVALID_ARG);
    197    return;
    198  }
    199 
    200  while (true) {
    201    index += aOffset;
    202    if (!index.isValid()) {
    203      aRv.Throw(NS_ERROR_FAILURE);
    204      return;
    205    }
    206 
    207    // Check for user interaction if desired, except for the first and last
    208    // history entries. We compare with >= to account for the case where
    209    // aOffset >= Count().
    210    if (!StaticPrefs::browser_navigation_requireUserInteraction() ||
    211        !aRequireUserInteraction || index.value() >= Count() - 1 ||
    212        index.value() <= 0) {
    213      break;
    214    }
    215    if (mHistory && mHistory->HasUserInteractionAtIndex(index.value())) {
    216      break;
    217    }
    218  }
    219 
    220  GotoIndex(index.value(), aOffset, aRequireUserInteraction, aUserActivation,
    221            aRv);
    222 }
    223 
    224 void ChildSHistory::AsyncGo(int32_t aOffset, bool aRequireUserInteraction,
    225                            bool aUserActivation) {
    226  CheckedInt<int32_t> index = Index();
    227  MOZ_LOG(gSHLog, LogLevel::Debug,
    228          ("ChildSHistory::AsyncGo(%d), current index = %d", aOffset,
    229           index.value()));
    230 
    231  RefPtr<PendingAsyncHistoryNavigation> asyncNav =
    232      new PendingAsyncHistoryNavigation(this, aOffset, aRequireUserInteraction,
    233                                        aUserActivation);
    234  mPendingNavigations.insertBack(asyncNav);
    235  NS_DispatchToCurrentThread(asyncNav.forget());
    236 }
    237 
    238 void ChildSHistory::AsyncGo(const nsID& aKey, BrowsingContext* aNavigable,
    239                            bool aRequireUserInteraction, bool aUserActivation,
    240                            bool aCheckForCancelation,
    241                            std::function<void(nsresult)>&& aResolver) {
    242  CheckedInt<int32_t> index = Index();
    243  MOZ_LOG_FMT(gSHLog, LogLevel::Debug,
    244              "ChildSHistory::AsyncGo({}), current index = {}",
    245              aKey.ToString().get(), index.value());
    246 
    247  RefPtr<PendingAsyncHistoryNavigation> asyncNav =
    248      new PendingAsyncHistoryNavigation(
    249          this, aKey, aNavigable, aRequireUserInteraction, aUserActivation,
    250          aCheckForCancelation, std::move(aResolver));
    251  mPendingNavigations.insertBack(asyncNav);
    252  NS_DispatchToCurrentThread(asyncNav.forget());
    253 }
    254 
    255 void ChildSHistory::GotoIndex(int32_t aIndex, int32_t aOffset,
    256                              bool aRequireUserInteraction,
    257                              bool aUserActivation, ErrorResult& aRv) {
    258  MOZ_LOG(gSHLog, LogLevel::Debug,
    259          ("ChildSHistory::GotoIndex(%d, %d), epoch %" PRIu64, aIndex, aOffset,
    260           mHistoryEpoch));
    261  if (mozilla::SessionHistoryInParent()) {
    262    if (!mPendingEpoch) {
    263      mPendingEpoch = true;
    264      RefPtr<ChildSHistory> self(this);
    265      NS_DispatchToCurrentThread(
    266          NS_NewRunnableFunction("UpdateEpochRunnable", [self] {
    267            self->mHistoryEpoch++;
    268            self->mPendingEpoch = false;
    269          }));
    270    }
    271 
    272    nsCOMPtr<nsISHistory> shistory = mHistory;
    273    RefPtr<BrowsingContext> bc = mBrowsingContext;
    274    bc->HistoryGo(
    275        aOffset, mHistoryEpoch, aRequireUserInteraction, aUserActivation,
    276        [shistory](Maybe<int32_t>&& aRequestedIndex) {
    277          // FIXME Should probably only do this for non-fission.
    278          if (aRequestedIndex.isSome() && shistory) {
    279            shistory->InternalSetRequestedIndex(aRequestedIndex.value());
    280          }
    281        });
    282  } else {
    283    nsCOMPtr<nsISHistory> shistory = mHistory;
    284    aRv = shistory->GotoIndex(aIndex, aUserActivation);
    285  }
    286 }
    287 
    288 void ChildSHistory::GotoKey(const nsID& aKey, BrowsingContext* aNavigable,
    289                            bool aRequireUserInteraction, bool aUserActivation,
    290                            bool aCheckForCancelation,
    291                            const std::function<void(nsresult)>& aResolver,
    292                            ErrorResult& aRv) {
    293  MOZ_DIAGNOSTIC_ASSERT(mozilla::SessionHistoryInParent());
    294 
    295  if (!mPendingEpoch) {
    296    mPendingEpoch = true;
    297    RefPtr<ChildSHistory> self(this);
    298    NS_DispatchToCurrentThread(
    299        NS_NewRunnableFunction("UpdateEpochRunnable", [self] {
    300          self->mHistoryEpoch++;
    301          self->mPendingEpoch = false;
    302        }));
    303  }
    304 
    305  nsCOMPtr<nsISHistory> shistory = mHistory;
    306  aNavigable->NavigationTraverse(
    307      aKey, mHistoryEpoch, aUserActivation,
    308      /* aCheckForCancelation */ aCheckForCancelation,
    309      [shistory, aResolver](nsresult aResult) { aResolver(aResult); });
    310 }
    311 
    312 void ChildSHistory::RemovePendingHistoryNavigations() {
    313  // Per the spec, this generally shouldn't remove all navigations - it
    314  // depends if they're in the same document family or not.  We don't do
    315  // that.  Also with SessionHistoryInParent, this can only abort AsyncGo's
    316  // that have not yet been sent to the parent - see discussion of point
    317  // 2.2 in comments in nsDocShell::UpdateURLAndHistory()
    318  MOZ_LOG(gSHLog, LogLevel::Debug,
    319          ("ChildSHistory::RemovePendingHistoryNavigations: %zu",
    320           mPendingNavigations.length()));
    321  mPendingNavigations.clear();
    322 }
    323 
    324 void ChildSHistory::EvictLocalDocumentViewers() {
    325  if (!mozilla::SessionHistoryInParent()) {
    326    mHistory->EvictAllDocumentViewers();
    327  }
    328 }
    329 
    330 nsISHistory* ChildSHistory::GetLegacySHistory(ErrorResult& aError) {
    331  if (mozilla::SessionHistoryInParent()) {
    332    aError.ThrowTypeError(
    333        "legacySHistory is not available with session history in the parent.");
    334    return nullptr;
    335  }
    336 
    337  MOZ_RELEASE_ASSERT(mHistory);
    338  return mHistory;
    339 }
    340 
    341 nsISHistory* ChildSHistory::LegacySHistory() {
    342  IgnoredErrorResult ignore;
    343  nsISHistory* shistory = GetLegacySHistory(ignore);
    344  MOZ_RELEASE_ASSERT(shistory);
    345  return shistory;
    346 }
    347 
    348 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChildSHistory)
    349  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    350  NS_INTERFACE_MAP_ENTRY(nsISupports)
    351 NS_INTERFACE_MAP_END
    352 
    353 NS_IMPL_CYCLE_COLLECTING_ADDREF(ChildSHistory)
    354 NS_IMPL_CYCLE_COLLECTING_RELEASE(ChildSHistory)
    355 
    356 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ChildSHistory)
    357 
    358 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ChildSHistory)
    359  if (tmp->mHistory) {
    360    static_cast<nsSHistory*>(tmp->mHistory.get())->SetBrowsingContext(nullptr);
    361  }
    362  NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext, mHistory)
    363  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    364 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    365 
    366 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ChildSHistory)
    367  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext, mHistory)
    368 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    369 
    370 JSObject* ChildSHistory::WrapObject(JSContext* cx,
    371                                    JS::Handle<JSObject*> aGivenProto) {
    372  return ChildSHistory_Binding::Wrap(cx, this, aGivenProto);
    373 }
    374 
    375 nsISupports* ChildSHistory::GetParentObject() const {
    376  return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
    377 }
    378 
    379 }  // namespace dom
    380 }  // namespace mozilla