gecko-dev/layout/base/nsFrameManager.cpp

264 строки
8.3 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/. */
/* storage of the frame tree and information about it */
#include "nsFrameManager.h"
#include "nscore.h"
#include "nsIPresShell.h"
#include "nsCOMPtr.h"
#include "plhash.h"
#include "nsPlaceholderFrame.h"
#include "nsGkAtoms.h"
#include "nsILayoutHistoryState.h"
#include "mozilla/PresState.h"
#include "mozilla/ComputedStyle.h"
#include "mozilla/dom/Element.h"
#include "nsIDocument.h"
#include "nsError.h"
#include "nsAbsoluteContainingBlock.h"
#include "ChildIterator.h"
#include "GeckoProfiler.h"
#include "nsIStatefulFrame.h"
#include "nsContainerFrame.h"
#include "nsWindowSizes.h"
#include "mozilla/MemoryReporting.h"
// #define DEBUG_UNDISPLAYED_MAP
// #define DEBUG_DISPLAY_CONTENTS_MAP
using namespace mozilla;
using namespace mozilla::dom;
//----------------------------------------------------------------------
nsFrameManager::~nsFrameManager()
{
NS_ASSERTION(!mPresShell, "nsFrameManager::Destroy never called");
}
void
nsFrameManager::Destroy()
{
NS_ASSERTION(mPresShell, "Frame manager already shut down.");
// Destroy the frame hierarchy.
mPresShell->SetIgnoreFrameDestruction(true);
if (mRootFrame) {
mRootFrame->Destroy();
mRootFrame = nullptr;
}
mPresShell = nullptr;
}
//----------------------------------------------------------------------
void
nsFrameManager::AppendFrames(nsContainerFrame* aParentFrame,
ChildListID aListID,
nsFrameList& aFrameList)
{
if (aParentFrame->IsAbsoluteContainer() &&
aListID == aParentFrame->GetAbsoluteListID()) {
aParentFrame->GetAbsoluteContainingBlock()->
AppendFrames(aParentFrame, aListID, aFrameList);
} else {
aParentFrame->AppendFrames(aListID, aFrameList);
}
}
void
nsFrameManager::InsertFrames(nsContainerFrame* aParentFrame,
ChildListID aListID,
nsIFrame* aPrevFrame,
nsFrameList& aFrameList)
{
MOZ_ASSERT(!aPrevFrame || (!aPrevFrame->GetNextContinuation()
|| (((aPrevFrame->GetNextContinuation()->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER))
&& !(aPrevFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER))),
"aPrevFrame must be the last continuation in its chain!");
if (aParentFrame->IsAbsoluteContainer() &&
aListID == aParentFrame->GetAbsoluteListID()) {
aParentFrame->GetAbsoluteContainingBlock()->
InsertFrames(aParentFrame, aListID, aPrevFrame, aFrameList);
} else {
aParentFrame->InsertFrames(aListID, aPrevFrame, aFrameList);
}
}
void
nsFrameManager::RemoveFrame(ChildListID aListID,
nsIFrame* aOldFrame)
{
// In case the reflow doesn't invalidate anything since it just leaves
// a gap where the old frame was, we invalidate it here. (This is
// reasonably likely to happen when removing a last child in a way
// that doesn't change the size of the parent.)
// This has to sure to invalidate the entire overflow rect; this
// is important in the presence of absolute positioning
aOldFrame->InvalidateFrameForRemoval();
NS_ASSERTION(!aOldFrame->GetPrevContinuation() ||
// exception for nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames
aOldFrame->IsTextFrame(),
"Must remove first continuation.");
NS_ASSERTION(!(aOldFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW &&
aOldFrame->GetPlaceholderFrame()),
"Must call RemoveFrame on placeholder for out-of-flows.");
nsContainerFrame* parentFrame = aOldFrame->GetParent();
if (parentFrame->IsAbsoluteContainer() &&
aListID == parentFrame->GetAbsoluteListID()) {
parentFrame->GetAbsoluteContainingBlock()->
RemoveFrame(parentFrame, aListID, aOldFrame);
} else {
parentFrame->RemoveFrame(aListID, aOldFrame);
}
}
//----------------------------------------------------------------------
// Capture state for a given frame.
// Accept a content id here, in some cases we may not have content (scroll position)
void
nsFrameManager::CaptureFrameStateFor(nsIFrame* aFrame,
nsILayoutHistoryState* aState)
{
if (!aFrame || !aState) {
NS_WARNING("null frame, or state");
return;
}
// Only capture state for stateful frames
nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame);
if (!statefulFrame) {
return;
}
// Capture the state, exit early if we get null (nothing to save)
UniquePtr<PresState> frameState = statefulFrame->SaveState();
if (!frameState) {
return;
}
// Generate the hash key to store the state under
// Exit early if we get empty key
nsAutoCString stateKey;
nsIContent* content = aFrame->GetContent();
nsIDocument* doc = content ? content->GetUncomposedDoc() : nullptr;
nsresult rv = statefulFrame->GenerateStateKey(content, doc, stateKey);
if(NS_FAILED(rv) || stateKey.IsEmpty()) {
return;
}
// Store the state. aState owns frameState now.
aState->AddState(stateKey, std::move(frameState));
}
void
nsFrameManager::CaptureFrameState(nsIFrame* aFrame,
nsILayoutHistoryState* aState)
{
MOZ_ASSERT(nullptr != aFrame && nullptr != aState, "null parameters passed in");
CaptureFrameStateFor(aFrame, aState);
// Now capture state recursively for the frame hierarchy rooted at aFrame
nsIFrame::ChildListIterator lists(aFrame);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
nsIFrame* child = childFrames.get();
if (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
// We'll pick it up when we get to its placeholder
continue;
}
// Make sure to walk through placeholders as needed, so that we
// save state for out-of-flows which may not be our descendants
// themselves but whose placeholders are our descendants.
CaptureFrameState(nsPlaceholderFrame::GetRealFrameFor(child), aState);
}
}
}
// Restore state for a given frame.
// Accept a content id here, in some cases we may not have content (scroll position)
void
nsFrameManager::RestoreFrameStateFor(nsIFrame* aFrame,
nsILayoutHistoryState* aState)
{
if (!aFrame || !aState) {
NS_WARNING("null frame or state");
return;
}
// Only restore state for stateful frames
nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame);
if (!statefulFrame) {
return;
}
// Generate the hash key the state was stored under
// Exit early if we get empty key
nsIContent* content = aFrame->GetContent();
// If we don't have content, we can't generate a hash
// key and there's probably no state information for us.
if (!content) {
return;
}
nsAutoCString stateKey;
nsIDocument* doc = content->GetUncomposedDoc();
nsresult rv = statefulFrame->GenerateStateKey(content, doc, stateKey);
if (NS_FAILED(rv) || stateKey.IsEmpty()) {
return;
}
// Get the state from the hash
PresState* frameState = aState->GetState(stateKey);
if (!frameState) {
return;
}
// Restore it
rv = statefulFrame->RestoreState(frameState);
if (NS_FAILED(rv)) {
return;
}
// If we restore ok, remove the state from the state table
aState->RemoveState(stateKey);
}
void
nsFrameManager::RestoreFrameState(nsIFrame* aFrame,
nsILayoutHistoryState* aState)
{
MOZ_ASSERT(nullptr != aFrame && nullptr != aState, "null parameters passed in");
RestoreFrameStateFor(aFrame, aState);
// Now restore state recursively for the frame hierarchy rooted at aFrame
nsIFrame::ChildListIterator lists(aFrame);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
RestoreFrameState(childFrames.get(), aState);
}
}
}
void
nsFrameManager::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const
{
aSizes.mLayoutPresShellSize += aSizes.mState.mMallocSizeOf(this);
}