forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			245 lines
		
	
	
	
		
			8.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
	
		
			8.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/. */
 | |
| 
 | |
| /* 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) {
 | |
|     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()->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, aFrameList);
 | |
|   } else {
 | |
|     aParentFrame->InsertFrames(aListID, aPrevFrame, nullptr, 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->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(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();
 | |
|   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);
 | |
| }
 | 
