forked from mirrors/gecko-dev
		
	As of the prior patch, these are no longer needed. I removed these with a script, then ran clang-format on the files, then manually reverted a few unrelated changed from the formatter. Differential Revision: https://phabricator.services.mozilla.com/D164829
		
			
				
	
	
		
			292 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			292 lines
		
	
	
	
		
			8.9 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/BrowserSessionStore.h"
 | 
						|
#include <algorithm>
 | 
						|
#include <cstdint>
 | 
						|
#include <functional>
 | 
						|
 | 
						|
#include "mozilla/AlreadyAddRefed.h"
 | 
						|
#include "mozilla/Assertions.h"
 | 
						|
#include "mozilla/ClearOnShutdown.h"
 | 
						|
#include "mozilla/IntegerRange.h"
 | 
						|
#include "mozilla/RefPtr.h"
 | 
						|
#include "mozilla/ScopeExit.h"
 | 
						|
#include "mozilla/StaticPtr.h"
 | 
						|
 | 
						|
#include "mozilla/dom/BrowserSessionStoreBinding.h"
 | 
						|
#include "mozilla/dom/CanonicalBrowsingContext.h"
 | 
						|
#include "mozilla/dom/SessionStoreFormData.h"
 | 
						|
#include "mozilla/dom/SessionStoreScrollData.h"
 | 
						|
#include "mozilla/dom/WindowGlobalParent.h"
 | 
						|
 | 
						|
#include "nsTHashMap.h"
 | 
						|
#include "nsHashtablesFwd.h"
 | 
						|
 | 
						|
#include "js/RootingAPI.h"
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
using namespace mozilla::dom;
 | 
						|
 | 
						|
static StaticAutoPtr<nsTHashMap<nsUint64HashKey, BrowserSessionStore*>>
 | 
						|
    sSessionStore;
 | 
						|
 | 
						|
NS_IMPL_CYCLE_COLLECTION(BrowserSessionStore, mBrowsingContext, mFormData,
 | 
						|
                         mScrollData)
 | 
						|
 | 
						|
/* static */
 | 
						|
already_AddRefed<BrowserSessionStore> BrowserSessionStore::GetOrCreate(
 | 
						|
    CanonicalBrowsingContext* aBrowsingContext) {
 | 
						|
  if (!aBrowsingContext->IsTop()) {
 | 
						|
    return nullptr;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!sSessionStore) {
 | 
						|
    sSessionStore = new nsTHashMap<nsUint64HashKey, BrowserSessionStore*>();
 | 
						|
    ClearOnShutdown(&sSessionStore);
 | 
						|
  }
 | 
						|
 | 
						|
  return do_AddRef(sSessionStore->LookupOrInsertWith(
 | 
						|
      aBrowsingContext->Id(),
 | 
						|
      [&] { return new BrowserSessionStore(aBrowsingContext); }));
 | 
						|
}
 | 
						|
 | 
						|
BrowserSessionStore::BrowserSessionStore(
 | 
						|
    CanonicalBrowsingContext* aBrowsingContext)
 | 
						|
    : mBrowsingContext(aBrowsingContext) {}
 | 
						|
 | 
						|
SessionStoreFormData* BrowserSessionStore::GetFormdata() { return mFormData; }
 | 
						|
 | 
						|
SessionStoreScrollData* BrowserSessionStore::GetScroll() { return mScrollData; }
 | 
						|
 | 
						|
static bool ShouldUpdateSessionStore(CanonicalBrowsingContext* aBrowsingContext,
 | 
						|
                                     uint32_t aEpoch) {
 | 
						|
  if (!aBrowsingContext) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aBrowsingContext->Top()->GetSessionStoreEpoch() != aEpoch) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aBrowsingContext->IsReplaced()) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aBrowsingContext->IsDynamic()) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
 | 
						|
  return true;
 | 
						|
}
 | 
						|
 | 
						|
// With GetOrCreate we can create either of the weak fields:
 | 
						|
//   WeakPtr<SessionStoreFormData> mFormdata;
 | 
						|
//   WeakPtr<SessionStoreScrollData> mScroll;
 | 
						|
// in CanonicalBrowsingContext. If one already exists, then we return that.
 | 
						|
template <typename T, WeakPtr<T>& (CanonicalBrowsingContext::*GetWeakRef)()>
 | 
						|
static already_AddRefed<T> GetOrCreateEntry(
 | 
						|
    CanonicalBrowsingContext* aBrowsingContext) {
 | 
						|
  typename T::LocationType& location = (aBrowsingContext->*GetWeakRef)();
 | 
						|
  RefPtr<T> entry = location.get();
 | 
						|
  if (!entry) {
 | 
						|
    entry = MakeRefPtr<T>();
 | 
						|
    location = entry;
 | 
						|
  }
 | 
						|
 | 
						|
  return entry.forget();
 | 
						|
}
 | 
						|
 | 
						|
// With InsertEntry we can insert an entry in the session store data tree in
 | 
						|
// either of the weak fields:
 | 
						|
//   WeakPtr<SessionStoreFormData> mFormdata;
 | 
						|
//   WeakPtr<SessionStoreScrollData> mScroll;
 | 
						|
// in CanonicalBrowsingContext. If an entry is inserted where there is no parent
 | 
						|
// entry, a spine of entries will be created until one is found, or we reach the
 | 
						|
// top browsing context.
 | 
						|
template <typename T>
 | 
						|
void InsertEntry(BrowsingContext* aBrowsingContext, T* aParent, T* aUpdate) {
 | 
						|
  int32_t offset = aBrowsingContext->ChildOffset();
 | 
						|
  if (offset < 0) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  aParent->ClearCachedChildren();
 | 
						|
 | 
						|
  auto& children = aParent->Children();
 | 
						|
 | 
						|
  children.EnsureLengthAtLeast(offset + 1);
 | 
						|
  if (children[offset] && !aBrowsingContext->Children().IsEmpty()) {
 | 
						|
    children[offset]->ClearCachedChildren();
 | 
						|
    aUpdate->ClearCachedChildren();
 | 
						|
  }
 | 
						|
 | 
						|
  children[offset] = aUpdate;
 | 
						|
}
 | 
						|
 | 
						|
// With RemoveEntry we can remove an entry in the session store data tree in
 | 
						|
// either of the weak fields:
 | 
						|
//   WeakPtr<SessionStoreFormData> mFormdata;
 | 
						|
//   WeakPtr<SessionStoreScrollData> mScroll;
 | 
						|
// in CanonicalBrowsingContext. If an entry is removed, where its parent doesn't
 | 
						|
// contain data, we'll remove the parent and repeat until we either find an
 | 
						|
// entry with data or reach the top browsing context.
 | 
						|
template <typename T>
 | 
						|
void RemoveEntry(BrowsingContext* aBrowsingContext, T* aParent) {
 | 
						|
  int32_t offset = aBrowsingContext->ChildOffset();
 | 
						|
  if (offset < 0) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!aParent) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  aParent->ClearCachedChildren();
 | 
						|
 | 
						|
  auto& children = aParent->Children();
 | 
						|
  size_t length = children.Length();
 | 
						|
  if (children.Length() <= static_cast<size_t>(offset)) {
 | 
						|
    // The children array doesn't extend to offset.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (static_cast<size_t>(offset) < length - 1) {
 | 
						|
    // offset is before the last item in the children array.
 | 
						|
    children[offset] = nullptr;
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // offset is the last item, find the first non-null item before it
 | 
						|
  // and remove anything after that item.
 | 
						|
  while (offset > 0 && !children[offset - 1]) {
 | 
						|
    --offset;
 | 
						|
  }
 | 
						|
 | 
						|
  children.TruncateLength(offset);
 | 
						|
}
 | 
						|
 | 
						|
// With UpdateSessionStoreField we can update an entry in the session store
 | 
						|
// data tree in either of the weak fields:
 | 
						|
//   WeakPtr<SessionStoreFormData> mFormdata;
 | 
						|
//   WeakPtr<SessionStoreScrollData> mScroll;
 | 
						|
// in CanonicalBrowsingContext. UpdateSessionStoreField uses the above
 | 
						|
// functions, `GetOrCreateEntry`, `InsertEntry` and `RemoveEntry` to operate on
 | 
						|
// the weak fields. We return the top-level entry attached to the top browsing
 | 
						|
// context through `aEntry`. If the entire browsing context tree contains no
 | 
						|
// session store data this will be set to nullptr.
 | 
						|
template <typename T, WeakPtr<T>& (CanonicalBrowsingContext::*GetWeakRef)()>
 | 
						|
void UpdateSessionStoreField(CanonicalBrowsingContext* aBrowsingContext,
 | 
						|
                             const typename T::CollectedType& aUpdate,
 | 
						|
                             T** aEntry) {
 | 
						|
  RefPtr<T> currentEntry;
 | 
						|
 | 
						|
  if (T::HasData(aUpdate)) {
 | 
						|
    currentEntry = GetOrCreateEntry<T, GetWeakRef>(aBrowsingContext);
 | 
						|
    currentEntry->Update(aUpdate);
 | 
						|
 | 
						|
    CanonicalBrowsingContext* currentBrowsingContext = aBrowsingContext;
 | 
						|
    while (CanonicalBrowsingContext* parent =
 | 
						|
               currentBrowsingContext->GetParent()) {
 | 
						|
      WeakPtr<T>& parentEntry = (parent->*GetWeakRef)();
 | 
						|
      if (parentEntry) {
 | 
						|
        InsertEntry(aBrowsingContext, parentEntry.get(), currentEntry.get());
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      RefPtr<T> entry = GetOrCreateEntry<T, GetWeakRef>(parent);
 | 
						|
      InsertEntry(currentBrowsingContext, entry.get(), currentEntry.get());
 | 
						|
 | 
						|
      currentEntry = entry;
 | 
						|
      currentBrowsingContext = parent;
 | 
						|
    }
 | 
						|
 | 
						|
    currentEntry = (aBrowsingContext->Top()->*GetWeakRef)().get();
 | 
						|
  } else if ((currentEntry = (aBrowsingContext->*GetWeakRef)())) {
 | 
						|
    currentEntry->Update(aUpdate);
 | 
						|
 | 
						|
    CanonicalBrowsingContext* currentBrowsingContext = aBrowsingContext;
 | 
						|
    while (CanonicalBrowsingContext* parent =
 | 
						|
               currentBrowsingContext->GetParent()) {
 | 
						|
      if (!currentEntry || !currentEntry->IsEmpty()) {
 | 
						|
        break;
 | 
						|
      }
 | 
						|
 | 
						|
      T* parentEntry = (parent->*GetWeakRef)().get();
 | 
						|
      RemoveEntry(currentBrowsingContext, parentEntry);
 | 
						|
 | 
						|
      currentEntry = parentEntry;
 | 
						|
      currentBrowsingContext = parent;
 | 
						|
    }
 | 
						|
 | 
						|
    if (currentEntry && currentEntry->IsEmpty()) {
 | 
						|
      currentEntry = nullptr;
 | 
						|
    } else {
 | 
						|
      currentEntry = (aBrowsingContext->Top()->*GetWeakRef)().get();
 | 
						|
    }
 | 
						|
  } else {
 | 
						|
    currentEntry = (aBrowsingContext->Top()->*GetWeakRef)().get();
 | 
						|
  }
 | 
						|
 | 
						|
  *aEntry = currentEntry.forget().take();
 | 
						|
}
 | 
						|
 | 
						|
void BrowserSessionStore::UpdateSessionStore(
 | 
						|
    CanonicalBrowsingContext* aBrowsingContext,
 | 
						|
    const Maybe<sessionstore::FormData>& aFormData,
 | 
						|
    const Maybe<nsPoint>& aScrollPosition, uint32_t aEpoch) {
 | 
						|
  if (!aFormData && !aScrollPosition) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!ShouldUpdateSessionStore(aBrowsingContext, aEpoch)) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aFormData) {
 | 
						|
    UpdateSessionStoreField<
 | 
						|
        SessionStoreFormData,
 | 
						|
        &CanonicalBrowsingContext::GetSessionStoreFormDataRef>(
 | 
						|
        aBrowsingContext, *aFormData, getter_AddRefs(mFormData));
 | 
						|
  }
 | 
						|
 | 
						|
  if (aScrollPosition) {
 | 
						|
    UpdateSessionStoreField<
 | 
						|
        SessionStoreScrollData,
 | 
						|
        &CanonicalBrowsingContext::GetSessionStoreScrollDataRef>(
 | 
						|
        aBrowsingContext, *aScrollPosition, getter_AddRefs(mScrollData));
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void BrowserSessionStore::RemoveSessionStore(
 | 
						|
    CanonicalBrowsingContext* aBrowsingContext) {
 | 
						|
  if (!aBrowsingContext) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  CanonicalBrowsingContext* parentContext = aBrowsingContext->GetParent();
 | 
						|
 | 
						|
  if (parentContext) {
 | 
						|
    RemoveEntry(aBrowsingContext,
 | 
						|
                parentContext->GetSessionStoreFormDataRef().get());
 | 
						|
 | 
						|
    RemoveEntry(aBrowsingContext,
 | 
						|
                parentContext->GetSessionStoreScrollDataRef().get());
 | 
						|
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  if (aBrowsingContext->IsTop()) {
 | 
						|
    mFormData = nullptr;
 | 
						|
    mScrollData = nullptr;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
BrowserSessionStore::~BrowserSessionStore() {
 | 
						|
  if (sSessionStore) {
 | 
						|
    sSessionStore->Remove(mBrowsingContext->Id());
 | 
						|
  }
 | 
						|
}
 |