forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			291 lines
		
	
	
	
		
			9.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			291 lines
		
	
	
	
		
			9.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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 "mozilla/dom/ChildSHistory.h"
 | |
| #include "mozilla/dom/ChildSHistoryBinding.h"
 | |
| #include "mozilla/dom/CanonicalBrowsingContext.h"
 | |
| #include "mozilla/dom/ContentChild.h"
 | |
| #include "mozilla/dom/ContentFrameMessageManager.h"
 | |
| #include "nsIXULRuntime.h"
 | |
| #include "nsComponentManagerUtils.h"
 | |
| #include "nsSHEntry.h"
 | |
| #include "nsSHistory.h"
 | |
| #include "nsDocShell.h"
 | |
| #include "nsXULAppAPI.h"
 | |
| 
 | |
| extern mozilla::LazyLogModule gSHLog;
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace dom {
 | |
| 
 | |
| ChildSHistory::ChildSHistory(BrowsingContext* aBrowsingContext)
 | |
|     : mBrowsingContext(aBrowsingContext) {}
 | |
| 
 | |
| ChildSHistory::~ChildSHistory() {
 | |
|   if (mHistory) {
 | |
|     static_cast<nsSHistory*>(mHistory.get())->SetBrowsingContext(nullptr);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ChildSHistory::SetBrowsingContext(BrowsingContext* aBrowsingContext) {
 | |
|   mBrowsingContext = aBrowsingContext;
 | |
| }
 | |
| 
 | |
| void ChildSHistory::SetIsInProcess(bool aIsInProcess) {
 | |
|   if (!aIsInProcess) {
 | |
|     MOZ_ASSERT_IF(mozilla::SessionHistoryInParent(), !mHistory);
 | |
|     if (!mozilla::SessionHistoryInParent()) {
 | |
|       RemovePendingHistoryNavigations();
 | |
|       if (mHistory) {
 | |
|         static_cast<nsSHistory*>(mHistory.get())->SetBrowsingContext(nullptr);
 | |
|         mHistory = nullptr;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (mHistory || mozilla::SessionHistoryInParent()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mHistory = new nsSHistory(mBrowsingContext);
 | |
| }
 | |
| 
 | |
| int32_t ChildSHistory::Count() {
 | |
|   if (mozilla::SessionHistoryInParent()) {
 | |
|     uint32_t length = mLength;
 | |
|     for (uint32_t i = 0; i < mPendingSHistoryChanges.Length(); ++i) {
 | |
|       length += mPendingSHistoryChanges[i].mLengthDelta;
 | |
|     }
 | |
| 
 | |
|     return length;
 | |
|   }
 | |
|   return mHistory->GetCount();
 | |
| }
 | |
| 
 | |
| int32_t ChildSHistory::Index() {
 | |
|   if (mozilla::SessionHistoryInParent()) {
 | |
|     uint32_t index = mIndex;
 | |
|     for (uint32_t i = 0; i < mPendingSHistoryChanges.Length(); ++i) {
 | |
|       index += mPendingSHistoryChanges[i].mIndexDelta;
 | |
|     }
 | |
| 
 | |
|     return index;
 | |
|   }
 | |
|   int32_t index;
 | |
|   mHistory->GetIndex(&index);
 | |
|   return index;
 | |
| }
 | |
| 
 | |
| nsID ChildSHistory::AddPendingHistoryChange() {
 | |
|   int32_t indexDelta = 1;
 | |
|   int32_t lengthDelta = (Index() + indexDelta) - (Count() - 1);
 | |
|   return AddPendingHistoryChange(indexDelta, lengthDelta);
 | |
| }
 | |
| 
 | |
| nsID ChildSHistory::AddPendingHistoryChange(int32_t aIndexDelta,
 | |
|                                             int32_t aLengthDelta) {
 | |
|   nsID changeID = nsID::GenerateUUID();
 | |
|   PendingSHistoryChange change = {changeID, aIndexDelta, aLengthDelta};
 | |
|   mPendingSHistoryChanges.AppendElement(change);
 | |
|   return changeID;
 | |
| }
 | |
| 
 | |
| void ChildSHistory::SetIndexAndLength(uint32_t aIndex, uint32_t aLength,
 | |
|                                       const nsID& aChangeID) {
 | |
|   mIndex = aIndex;
 | |
|   mLength = aLength;
 | |
|   mPendingSHistoryChanges.RemoveElementsBy(
 | |
|       [aChangeID](const PendingSHistoryChange& aChange) {
 | |
|         return aChange.mChangeID == aChangeID;
 | |
|       });
 | |
| }
 | |
| 
 | |
| void ChildSHistory::Reload(uint32_t aReloadFlags, ErrorResult& aRv) {
 | |
|   if (mozilla::SessionHistoryInParent()) {
 | |
|     if (XRE_IsParentProcess()) {
 | |
|       nsISHistory* shistory =
 | |
|           mBrowsingContext->Canonical()->GetSessionHistory();
 | |
|       if (shistory) {
 | |
|         aRv = shistory->Reload(aReloadFlags);
 | |
|       }
 | |
|     } else {
 | |
|       ContentChild::GetSingleton()->SendHistoryReload(mBrowsingContext,
 | |
|                                                       aReloadFlags);
 | |
|     }
 | |
| 
 | |
|     return;
 | |
|   }
 | |
|   aRv = mHistory->Reload(aReloadFlags);
 | |
| }
 | |
| 
 | |
| bool ChildSHistory::CanGo(int32_t aOffset) {
 | |
|   CheckedInt<int32_t> index = Index();
 | |
|   index += aOffset;
 | |
|   if (!index.isValid()) {
 | |
|     return false;
 | |
|   }
 | |
|   return index.value() < Count() && index.value() >= 0;
 | |
| }
 | |
| 
 | |
| void ChildSHistory::Go(int32_t aOffset, bool aRequireUserInteraction,
 | |
|                        bool aUserActivation, ErrorResult& aRv) {
 | |
|   CheckedInt<int32_t> index = Index();
 | |
|   MOZ_LOG(
 | |
|       gSHLog, LogLevel::Debug,
 | |
|       ("ChildSHistory::Go(%d), current index = %d", aOffset, index.value()));
 | |
|   if (aRequireUserInteraction && aOffset != -1 && aOffset != 1) {
 | |
|     NS_ERROR(
 | |
|         "aRequireUserInteraction may only be used with an offset of -1 or 1");
 | |
|     aRv.Throw(NS_ERROR_INVALID_ARG);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   while (true) {
 | |
|     index += aOffset;
 | |
|     if (!index.isValid()) {
 | |
|       aRv.Throw(NS_ERROR_FAILURE);
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     // Check for user interaction if desired, except for the first and last
 | |
|     // history entries. We compare with >= to account for the case where
 | |
|     // aOffset >= Count().
 | |
|     if (!aRequireUserInteraction || index.value() >= Count() - 1 ||
 | |
|         index.value() <= 0) {
 | |
|       break;
 | |
|     }
 | |
|     if (mHistory && mHistory->HasUserInteractionAtIndex(index.value())) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   GotoIndex(index.value(), aOffset, aRequireUserInteraction, aUserActivation,
 | |
|             aRv);
 | |
| }
 | |
| 
 | |
| void ChildSHistory::AsyncGo(int32_t aOffset, bool aRequireUserInteraction,
 | |
|                             bool aUserActivation, CallerType aCallerType,
 | |
|                             ErrorResult& aRv) {
 | |
|   CheckedInt<int32_t> index = Index();
 | |
|   MOZ_LOG(gSHLog, LogLevel::Debug,
 | |
|           ("ChildSHistory::AsyncGo(%d), current index = %d", aOffset,
 | |
|            index.value()));
 | |
|   nsresult rv = mBrowsingContext->CheckLocationChangeRateLimit(aCallerType);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     MOZ_LOG(gSHLog, LogLevel::Debug, ("Rejected"));
 | |
|     aRv.Throw(rv);
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   RefPtr<PendingAsyncHistoryNavigation> asyncNav =
 | |
|       new PendingAsyncHistoryNavigation(this, aOffset, aRequireUserInteraction,
 | |
|                                         aUserActivation);
 | |
|   mPendingNavigations.insertBack(asyncNav);
 | |
|   NS_DispatchToCurrentThread(asyncNav.forget());
 | |
| }
 | |
| 
 | |
| void ChildSHistory::GotoIndex(int32_t aIndex, int32_t aOffset,
 | |
|                               bool aRequireUserInteraction,
 | |
|                               bool aUserActivation, ErrorResult& aRv) {
 | |
|   MOZ_LOG(gSHLog, LogLevel::Debug,
 | |
|           ("ChildSHistory::GotoIndex(%d, %d), epoch %" PRIu64, aIndex, aOffset,
 | |
|            mHistoryEpoch));
 | |
|   if (mozilla::SessionHistoryInParent()) {
 | |
|     if (!mPendingEpoch) {
 | |
|       mPendingEpoch = true;
 | |
|       RefPtr<ChildSHistory> self(this);
 | |
|       NS_DispatchToCurrentThread(
 | |
|           NS_NewRunnableFunction("UpdateEpochRunnable", [self] {
 | |
|             self->mHistoryEpoch++;
 | |
|             self->mPendingEpoch = false;
 | |
|           }));
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsISHistory> shistory = mHistory;
 | |
|     mBrowsingContext->HistoryGo(
 | |
|         aOffset, mHistoryEpoch, aRequireUserInteraction, aUserActivation,
 | |
|         [shistory](Maybe<int32_t>&& aRequestedIndex) {
 | |
|           // FIXME Should probably only do this for non-fission.
 | |
|           if (aRequestedIndex.isSome() && shistory) {
 | |
|             shistory->InternalSetRequestedIndex(aRequestedIndex.value());
 | |
|           }
 | |
|         });
 | |
|   } else {
 | |
|     aRv = mHistory->GotoIndex(aIndex, aUserActivation);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ChildSHistory::RemovePendingHistoryNavigations() {
 | |
|   // Per the spec, this generally shouldn't remove all navigations - it
 | |
|   // depends if they're in the same document family or not.  We don't do
 | |
|   // that.  Also with SessionHistoryInParent, this can only abort AsyncGo's
 | |
|   // that have not yet been sent to the parent - see discussion of point
 | |
|   // 2.2 in comments in nsDocShell::UpdateURLAndHistory()
 | |
|   MOZ_LOG(gSHLog, LogLevel::Debug,
 | |
|           ("ChildSHistory::RemovePendingHistoryNavigations: %zu",
 | |
|            mPendingNavigations.length()));
 | |
|   mPendingNavigations.clear();
 | |
| }
 | |
| 
 | |
| void ChildSHistory::EvictLocalContentViewers() {
 | |
|   if (!mozilla::SessionHistoryInParent()) {
 | |
|     mHistory->EvictAllContentViewers();
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsISHistory* ChildSHistory::GetLegacySHistory(ErrorResult& aError) {
 | |
|   if (mozilla::SessionHistoryInParent()) {
 | |
|     aError.ThrowTypeError(
 | |
|         "legacySHistory is not available with session history in the parent.");
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   MOZ_RELEASE_ASSERT(mHistory);
 | |
|   return mHistory;
 | |
| }
 | |
| 
 | |
| nsISHistory* ChildSHistory::LegacySHistory() {
 | |
|   IgnoredErrorResult ignore;
 | |
|   nsISHistory* shistory = GetLegacySHistory(ignore);
 | |
|   MOZ_RELEASE_ASSERT(shistory);
 | |
|   return shistory;
 | |
| }
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ChildSHistory)
 | |
|   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
 | |
|   NS_INTERFACE_MAP_ENTRY(nsISupports)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTING_ADDREF(ChildSHistory)
 | |
| NS_IMPL_CYCLE_COLLECTING_RELEASE(ChildSHistory)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(ChildSHistory)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ChildSHistory)
 | |
|   if (tmp->mHistory) {
 | |
|     static_cast<nsSHistory*>(tmp->mHistory.get())->SetBrowsingContext(nullptr);
 | |
|   }
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext, mHistory)
 | |
|   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 | |
| NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ChildSHistory)
 | |
|   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext, mHistory)
 | |
| NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 | |
| 
 | |
| JSObject* ChildSHistory::WrapObject(JSContext* cx,
 | |
|                                     JS::Handle<JSObject*> aGivenProto) {
 | |
|   return ChildSHistory_Binding::Wrap(cx, this, aGivenProto);
 | |
| }
 | |
| 
 | |
| nsISupports* ChildSHistory::GetParentObject() const {
 | |
|   return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
 | |
| }
 | |
| 
 | |
| }  // namespace dom
 | |
| }  // namespace mozilla
 | 
