diff options
Diffstat (limited to 'layout/base/nsFrameManager.cpp')
-rw-r--r-- | layout/base/nsFrameManager.cpp | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp new file mode 100644 index 0000000000..87328ccb80 --- /dev/null +++ b/layout/base/nsFrameManager.cpp @@ -0,0 +1,250 @@ +/* -*- 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 "nsCOMPtr.h" +#include "plhash.h" +#include "nsPlaceholderFrame.h" +#include "nsGkAtoms.h" +#include "nsILayoutHistoryState.h" +#include "mozilla/PresShell.h" +#include "mozilla/PresState.h" +#include "mozilla/ComputedStyle.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/Document.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) { + FrameDestroyContext context(mPresShell); + mRootFrame->Destroy(context); + mRootFrame = nullptr; + } + + mPresShell = nullptr; +} + +//---------------------------------------------------------------------- +void nsFrameManager::AppendFrames(nsContainerFrame* aParentFrame, + FrameChildListID aListID, + nsFrameList&& aFrameList) { + if (aParentFrame->IsAbsoluteContainer() && + aListID == aParentFrame->GetAbsoluteListID()) { + aParentFrame->GetAbsoluteContainingBlock()->AppendFrames( + aParentFrame, aListID, std::move(aFrameList)); + } else { + aParentFrame->AppendFrames(aListID, std::move(aFrameList)); + } +} + +void nsFrameManager::InsertFrames(nsContainerFrame* aParentFrame, + FrameChildListID aListID, + nsIFrame* aPrevFrame, + nsFrameList&& aFrameList) { + MOZ_ASSERT( + !aPrevFrame || + (!aPrevFrame->GetNextContinuation() || + (aPrevFrame->GetNextContinuation()->HasAnyStateBits( + NS_FRAME_IS_OVERFLOW_CONTAINER) && + !aPrevFrame->HasAnyStateBits(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, std::move(aFrameList)); + } else { + aParentFrame->InsertFrames(aListID, aPrevFrame, nullptr, + std::move(aFrameList)); + } +} + +void nsFrameManager::RemoveFrame(DestroyContext& aContext, + FrameChildListID 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->HasAnyStateBits(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(aContext, aListID, + aOldFrame); + } else { + parentFrame->RemoveFrame(aContext, 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(); + Document* doc = content ? content->GetUncomposedDoc() : nullptr; + statefulFrame->GenerateStateKey(content, doc, stateKey); + if (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 + for (const auto& childList : aFrame->ChildLists()) { + for (nsIFrame* child : childList.mList) { + if (child->HasAnyStateBits(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; + Document* doc = content->GetUncomposedDoc(); + statefulFrame->GenerateStateKey(content, doc, stateKey); + if (stateKey.IsEmpty()) { + return; + } + + // Get the state from the hash + PresState* frameState = aState->GetState(stateKey); + if (!frameState) { + return; + } + + // Restore it + nsresult 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 + for (const auto& childList : aFrame->ChildLists()) { + for (nsIFrame* child : childList.mList) { + RestoreFrameState(child, aState); + } + } +} + +void nsFrameManager::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const { + aSizes.mLayoutPresShellSize += aSizes.mState.mMallocSizeOf(this); +} |