gecko-dev/dom/base/GroupedSHistory.cpp

355 строки
11 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 "GroupedSHistory.h"
#include "mozilla/dom/Promise.h"
#include "TabParent.h"
#include "PartialSHistory.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_CLASS(GroupedSHistory)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(GroupedSHistory)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPartialHistories)
tmp->mPrerenderingHistories.Clear();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(GroupedSHistory)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPartialHistories)
for (GroupedSHistory::PrerenderingHistory& h : tmp->mPrerenderingHistories) {
ImplCycleCollectionTraverse(cb, h.mPartialHistory, "mPrerenderingHistories[i]->mPartialHistory", 0);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(GroupedSHistory)
NS_IMPL_CYCLE_COLLECTING_RELEASE(GroupedSHistory)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GroupedSHistory)
NS_INTERFACE_MAP_ENTRY(nsIGroupedSHistory)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIGroupedSHistory)
NS_INTERFACE_MAP_END
GroupedSHistory::GroupedSHistory()
: mCount(0),
mIndexOfActivePartialHistory(-1)
{
}
NS_IMETHODIMP
GroupedSHistory::GetCount(uint32_t* aResult)
{
MOZ_ASSERT(aResult);
*aResult = mCount;
return NS_OK;
}
NS_IMETHODIMP
GroupedSHistory::AppendPartialSHistory(nsIPartialSHistory* aPartialHistory)
{
if (!aPartialHistory) {
return NS_ERROR_INVALID_POINTER;
}
nsCOMPtr<nsIPartialSHistory> partialHistory(aPartialHistory);
if (!partialHistory || mPartialHistories.Contains(partialHistory)) {
return NS_ERROR_FAILURE;
}
// Remove all items after active one and deactive it, unless it's the first
// call and no active partial history has been set yet.
if (mIndexOfActivePartialHistory >= 0) {
PurgePartialHistories(mIndexOfActivePartialHistory);
nsCOMPtr<nsIPartialSHistory> prevPartialHistory =
mPartialHistories[mIndexOfActivePartialHistory];
if (NS_WARN_IF(!prevPartialHistory)) {
// Cycle collected?
return NS_ERROR_UNEXPECTED;
}
prevPartialHistory->OnDeactive();
}
// Attach the partial history.
uint32_t offset = mCount;
mCount += partialHistory->GetCount();
mPartialHistories.AppendElement(partialHistory);
partialHistory->OnAttachGroupedSHistory(this, offset);
mIndexOfActivePartialHistory = mPartialHistories.Count() - 1;
// Remove the prerendered documents, as there was a history navigation
PurgePrerendering();
return NS_OK;
}
NS_IMETHODIMP
GroupedSHistory::HandleSHistoryUpdate(nsIPartialSHistory* aPartial, bool aTruncate)
{
if (!aPartial) {
return NS_ERROR_INVALID_POINTER;
}
nsCOMPtr<nsIPartialSHistory> partialHistory = aPartial;
int32_t index = partialHistory->GetGlobalIndex();
// Get the lower and upper bounds for the viewer window
int32_t lower = index - nsISHistory::VIEWER_WINDOW;
int32_t upper = index + nsISHistory::VIEWER_WINDOW;
for (uint32_t i = 0; i < mPartialHistories.Length(); ++i) {
nsIPartialSHistory* pHistory = mPartialHistories[i];
// Skip the active partial history.
if (pHistory == partialHistory) {
continue;
}
// Check if the given partialshistory entry is too far away in history, and
// if it is, close it.
int32_t thisCount = pHistory->GetCount();
int32_t thisOffset = pHistory->GetGlobalIndexOffset();
if ((thisOffset > upper) || ((thisCount + thisOffset) < lower)) {
nsCOMPtr<nsIFrameLoader> loader;
pHistory->GetOwnerFrameLoader(getter_AddRefs(loader));
if (loader && !loader->GetIsDead()) {
loader->RequestFrameLoaderClose();
}
}
}
// Remove the prerendered documents, as there was a history navigation
PurgePrerendering();
// If we should be truncating, make sure to purge any partialSHistories which
// follow the one being updated.
if (aTruncate) {
int32_t index = mPartialHistories.IndexOf(partialHistory);
if (NS_WARN_IF(index != mIndexOfActivePartialHistory) ||
NS_WARN_IF(index < 0)) {
// Non-active or not attached partialHistory
return NS_ERROR_UNEXPECTED;
}
PurgePartialHistories(index);
// Update global count.
uint32_t count = partialHistory->GetCount();
uint32_t offset = partialHistory->GetGlobalIndexOffset();
mCount = count + offset;
}
return NS_OK;
}
NS_IMETHODIMP
GroupedSHistory::GotoIndex(uint32_t aGlobalIndex,
nsIFrameLoader** aTargetLoaderToSwap)
{
MOZ_ASSERT(aTargetLoaderToSwap);
*aTargetLoaderToSwap = nullptr;
nsCOMPtr<nsIPartialSHistory> currentPartialHistory =
mPartialHistories[mIndexOfActivePartialHistory];
if (!currentPartialHistory) {
// Cycle collected?
return NS_ERROR_UNEXPECTED;
}
for (uint32_t i = 0; i < mPartialHistories.Length(); i++) {
nsCOMPtr<nsIPartialSHistory> partialHistory = mPartialHistories[i];
if (NS_WARN_IF(!partialHistory)) {
// Cycle collected?
return NS_ERROR_UNEXPECTED;
}
// Examine index range.
uint32_t offset = partialHistory->GetGlobalIndexOffset();
uint32_t count = partialHistory->GetCount();
if (offset <= aGlobalIndex && (offset + count) > aGlobalIndex) {
uint32_t targetIndex = aGlobalIndex - offset;
// Check if we are trying to swap to a dead frameloader, and return
// NS_ERROR_NOT_AVAILABLE if we are.
nsCOMPtr<nsIFrameLoader> frameLoader;
partialHistory->GetOwnerFrameLoader(getter_AddRefs(frameLoader));
if (!frameLoader || frameLoader->GetIsDead()) {
return NS_ERROR_NOT_AVAILABLE;
}
if ((size_t)mIndexOfActivePartialHistory == i) {
return NS_OK;
}
mIndexOfActivePartialHistory = i;
if (NS_FAILED(currentPartialHistory->OnDeactive()) ||
NS_FAILED(partialHistory->OnActive(mCount, targetIndex))) {
return NS_ERROR_FAILURE;
}
// Return the target frameloader to the caller.
frameLoader.forget(aTargetLoaderToSwap);
return NS_OK;
}
}
// Index not found.
NS_WARNING("Out of index request!");
return NS_ERROR_FAILURE;
}
void
GroupedSHistory::PurgePartialHistories(uint32_t aLastPartialIndexToKeep)
{
uint32_t lastIndex = mPartialHistories.Length() - 1;
if (aLastPartialIndexToKeep >= lastIndex) {
// Nothing to remove.
return;
}
// Close tabs.
for (uint32_t i = lastIndex; i > aLastPartialIndexToKeep; i--) {
nsCOMPtr<nsIPartialSHistory> partialHistory = mPartialHistories[i];
if (!partialHistory) {
// Cycle collected?
return;
}
nsCOMPtr<nsIFrameLoader> loader;
partialHistory->GetOwnerFrameLoader(getter_AddRefs(loader));
loader->RequestFrameLoaderClose();
}
// Remove references.
mPartialHistories.RemoveElementsAt(aLastPartialIndexToKeep + 1,
lastIndex - aLastPartialIndexToKeep);
}
/* static */ bool
GroupedSHistory::GroupedHistoryEnabled() {
static bool sGroupedSHistoryEnabled = false;
static bool sGroupedSHistoryPrefCached = false;
if (!sGroupedSHistoryPrefCached) {
sGroupedSHistoryPrefCached = true;
Preferences::AddBoolVarCache(&sGroupedSHistoryEnabled,
"browser.groupedhistory.enabled",
false);
}
return sGroupedSHistoryEnabled;
}
void
GroupedSHistory::PurgePrerendering()
{
nsTArray<PrerenderingHistory> histories = Move(mPrerenderingHistories);
// Remove the frameloaders which are owned by the prerendering history, and
// remove them from mPrerenderingHistories.
for (uint32_t i = 0; i < histories.Length(); ++i) {
nsCOMPtr<nsIFrameLoader> loader;
histories[i].mPartialHistory->GetOwnerFrameLoader(getter_AddRefs(loader));
if (loader) {
loader->RequestFrameLoaderClose();
}
}
MOZ_ASSERT(mPrerenderingHistories.IsEmpty());
}
NS_IMETHODIMP
GroupedSHistory::CloseInactiveFrameLoaderOwners()
{
MOZ_ASSERT(mIndexOfActivePartialHistory >= 0);
// Remove inactive frameloaders which are participating in the grouped shistory
for (uint32_t i = 0; i < mPartialHistories.Length(); ++i) {
if (i != static_cast<uint32_t>(mIndexOfActivePartialHistory)) {
nsCOMPtr<nsIFrameLoader> loader;
mPartialHistories[i]->GetOwnerFrameLoader(getter_AddRefs(loader));
loader->RequestFrameLoaderClose();
}
}
PurgePrerendering();
return NS_OK;
}
NS_IMETHODIMP
GroupedSHistory::AddPrerenderingPartialSHistory(nsIPartialSHistory* aPrerendering, int32_t aId)
{
NS_ENSURE_TRUE(aPrerendering && aId, NS_ERROR_UNEXPECTED);
aPrerendering->SetActiveState(nsIPartialSHistory::STATE_PRERENDER);
PrerenderingHistory history = { aPrerendering, aId };
mPrerenderingHistories.AppendElement(history);
return NS_OK;
}
NS_IMETHODIMP
GroupedSHistory::GetActiveFrameLoader(nsIFrameLoader** aFrameLoader)
{
if (mIndexOfActivePartialHistory >= 0) {
return mPartialHistories[mIndexOfActivePartialHistory]->GetOwnerFrameLoader(aFrameLoader);
}
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
GroupedSHistory::ActivatePrerendering(int32_t aId, JSContext* aCx, nsISupports** aPromise)
{
NS_ENSURE_TRUE(aId && aCx && aPromise, NS_ERROR_UNEXPECTED);
// Look for an entry with the given aId in mPrerenderingHistories.
for (uint32_t i = 0; i < mPrerenderingHistories.Length(); ++i) {
if (mPrerenderingHistories[i].mId == aId) {
nsCOMPtr<nsIPartialSHistory> partialHistory = mPrerenderingHistories[i].mPartialHistory;
mPrerenderingHistories.RemoveElementAt(i);
nsCOMPtr<nsIFrameLoader> fl;
partialHistory->GetOwnerFrameLoader(getter_AddRefs(fl));
NS_ENSURE_TRUE(fl, NS_ERROR_FAILURE);
nsCOMPtr<nsIFrameLoader> activeFl;
GetActiveFrameLoader(getter_AddRefs(activeFl));
NS_ENSURE_TRUE(activeFl, NS_ERROR_FAILURE);
nsresult rv = fl->MakePrerenderedLoaderActive();
NS_ENSURE_SUCCESS(rv, rv);
return activeFl->AppendPartialSHistoryAndSwap(fl, aPromise);
}
}
// Generate a rejected promise as the entry was not found.
nsCOMPtr<nsIGlobalObject> go = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
if (NS_WARN_IF(!go)) {
return NS_ERROR_FAILURE;
}
ErrorResult rv;
RefPtr<Promise> promise = Promise::Reject(go, aCx, JS::UndefinedHandleValue, rv);
if (NS_WARN_IF(rv.Failed())) {
return NS_ERROR_FAILURE;
}
promise.forget(aPromise);
return NS_OK;
}
NS_IMETHODIMP
GroupedSHistory::CancelPrerendering(int32_t aId)
{
for (uint32_t i = 0; i < mPrerenderingHistories.Length(); ++i) {
if (mPrerenderingHistories[i].mId == aId) {
nsCOMPtr<nsIPartialSHistory> partialHistory = mPrerenderingHistories[i].mPartialHistory;
nsCOMPtr<nsIFrameLoader> fl;
partialHistory->GetOwnerFrameLoader(getter_AddRefs(fl));
if (fl) {
fl->RequestFrameLoaderClose();
}
mPrerenderingHistories.RemoveElementAt(i);
}
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla