nsFrameManager.cpp (9227B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* storage of the frame tree and information about it */ 8 9 #include "nsFrameManager.h" 10 11 #include "ChildIterator.h" 12 #include "GeckoProfiler.h" 13 #include "mozilla/AbsoluteContainingBlock.h" 14 #include "mozilla/ComputedStyle.h" 15 #include "mozilla/PresShell.h" 16 #include "mozilla/PresState.h" 17 #include "mozilla/ViewportFrame.h" 18 #include "mozilla/dom/Document.h" 19 #include "mozilla/dom/Element.h" 20 #include "nsCOMPtr.h" 21 #include "nsContainerFrame.h" 22 #include "nsError.h" 23 #include "nsGkAtoms.h" 24 #include "nsILayoutHistoryState.h" 25 #include "nsIStatefulFrame.h" 26 #include "nsPlaceholderFrame.h" 27 #include "nsWindowSizes.h" 28 #include "nscore.h" 29 #include "plhash.h" 30 31 using namespace mozilla; 32 using namespace mozilla::dom; 33 34 //---------------------------------------------------------------------- 35 36 nsFrameManager::~nsFrameManager() { 37 NS_ASSERTION(!mPresShell, "nsFrameManager::Destroy never called"); 38 } 39 40 void nsFrameManager::SetRootFrame(ViewportFrame* aRootFrame) { 41 MOZ_ASSERT(aRootFrame, "The root frame should be valid!"); 42 MOZ_ASSERT(!mRootFrame, "We should set a root frame only once!"); 43 mRootFrame = aRootFrame; 44 } 45 46 void nsFrameManager::Destroy() { 47 NS_ASSERTION(mPresShell, "Frame manager already shut down."); 48 49 // Destroy the frame hierarchy. 50 mPresShell->SetIgnoreFrameDestruction(true); 51 52 if (mRootFrame) { 53 FrameDestroyContext context(mPresShell); 54 mRootFrame->Destroy(context); 55 mRootFrame = nullptr; 56 } 57 58 mPresShell = nullptr; 59 } 60 61 //---------------------------------------------------------------------- 62 void nsFrameManager::AppendFrames(nsContainerFrame* aParentFrame, 63 FrameChildListID aListID, 64 nsFrameList&& aFrameList) { 65 if (aParentFrame->IsAbsoluteContainer() && 66 aListID == aParentFrame->GetAbsoluteListID()) { 67 aParentFrame->GetAbsoluteContainingBlock()->AppendFrames( 68 aParentFrame, aListID, std::move(aFrameList)); 69 } else { 70 aParentFrame->AppendFrames(aListID, std::move(aFrameList)); 71 } 72 } 73 74 void nsFrameManager::InsertFrames(nsContainerFrame* aParentFrame, 75 FrameChildListID aListID, 76 nsIFrame* aPrevFrame, 77 nsFrameList&& aFrameList) { 78 MOZ_ASSERT( 79 !aPrevFrame || 80 (!aPrevFrame->GetNextContinuation() || 81 (aPrevFrame->GetNextContinuation()->HasAnyStateBits( 82 NS_FRAME_IS_OVERFLOW_CONTAINER) && 83 !aPrevFrame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER))), 84 "aPrevFrame must be the last continuation in its chain!"); 85 86 if (aParentFrame->IsAbsoluteContainer() && 87 aListID == aParentFrame->GetAbsoluteListID()) { 88 aParentFrame->GetAbsoluteContainingBlock()->InsertFrames( 89 aParentFrame, aListID, aPrevFrame, std::move(aFrameList)); 90 } else { 91 aParentFrame->InsertFrames(aListID, aPrevFrame, nullptr, 92 std::move(aFrameList)); 93 } 94 } 95 96 void nsFrameManager::RemoveFrame(DestroyContext& aContext, 97 FrameChildListID aListID, 98 nsIFrame* aOldFrame) { 99 // In case the reflow doesn't invalidate anything since it just leaves 100 // a gap where the old frame was, we invalidate it here. (This is 101 // reasonably likely to happen when removing a last child in a way 102 // that doesn't change the size of the parent.) 103 // This has to sure to invalidate the entire overflow rect; this 104 // is important in the presence of absolute positioning 105 aOldFrame->InvalidateFrameForRemoval(); 106 107 NS_ASSERTION(!aOldFrame->GetPrevContinuation() || 108 // exception for 109 // nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames 110 aOldFrame->IsTextFrame(), 111 "Must remove first continuation."); 112 NS_ASSERTION(!(aOldFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) && 113 aOldFrame->GetPlaceholderFrame()), 114 "Must call RemoveFrame on placeholder for out-of-flows."); 115 nsContainerFrame* parentFrame = aOldFrame->GetParent(); 116 if (parentFrame->IsAbsoluteContainer() && 117 aListID == parentFrame->GetAbsoluteListID()) { 118 parentFrame->GetAbsoluteContainingBlock()->RemoveFrame(aContext, aListID, 119 aOldFrame); 120 } else { 121 parentFrame->RemoveFrame(aContext, aListID, aOldFrame); 122 } 123 } 124 125 //---------------------------------------------------------------------- 126 127 // Capture state for a given frame. 128 // Accept a content id here, in some cases we may not have content (scroll 129 // position) 130 void nsFrameManager::CaptureFrameStateFor(nsIFrame* aFrame, 131 nsILayoutHistoryState* aState) { 132 if (!aFrame || !aState) { 133 NS_WARNING("null frame, or state"); 134 return; 135 } 136 137 // Only capture state for stateful frames 138 nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame); 139 if (!statefulFrame) { 140 return; 141 } 142 143 // Capture the state, exit early if we get null (nothing to save) 144 UniquePtr<PresState> frameState = statefulFrame->SaveState(); 145 if (!frameState) { 146 return; 147 } 148 149 // Generate the hash key to store the state under 150 // Exit early if we get empty key 151 nsAutoCString stateKey; 152 nsIContent* content = aFrame->GetContent(); 153 Document* doc = content ? content->GetUncomposedDoc() : nullptr; 154 statefulFrame->GenerateStateKey(content, doc, stateKey); 155 if (stateKey.IsEmpty()) { 156 return; 157 } 158 159 // Store the state. aState owns frameState now. 160 aState->AddState(stateKey, std::move(frameState)); 161 } 162 163 void nsFrameManager::CaptureFrameState(nsIFrame* aFrame, 164 nsILayoutHistoryState* aState) { 165 MOZ_ASSERT(nullptr != aFrame && nullptr != aState, 166 "null parameters passed in"); 167 168 CaptureFrameStateFor(aFrame, aState); 169 170 // Now capture state recursively for the frame hierarchy rooted at aFrame 171 for (const auto& childList : aFrame->ChildLists()) { 172 for (nsIFrame* child : childList.mList) { 173 if (child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) { 174 // We'll pick it up when we get to its placeholder 175 continue; 176 } 177 // Make sure to walk through placeholders as needed, so that we 178 // save state for out-of-flows which may not be our descendants 179 // themselves but whose placeholders are our descendants. 180 nsIFrame* realChild = nsPlaceholderFrame::GetRealFrameFor(child); 181 // GetRealFrameFor should theoretically never return null here (and its 182 // helper has an assertion to enforce this); but we've got known fuzzer 183 // testcases where it does return null (in non-debug builds that make it 184 // past the aforementioned assertion) due to weird situations with 185 // out-of-flows and fragmentation. We handle that unexpected situation by 186 // silently skipping this frame, rather than crashing. 187 if (MOZ_LIKELY(realChild)) { 188 CaptureFrameState(realChild, aState); 189 } 190 } 191 } 192 } 193 194 // Restore state for a given frame. 195 // Accept a content id here, in some cases we may not have content (scroll 196 // position) 197 void nsFrameManager::RestoreFrameStateFor(nsIFrame* aFrame, 198 nsILayoutHistoryState* aState) { 199 if (!aFrame || !aState) { 200 NS_WARNING("null frame or state"); 201 return; 202 } 203 204 // Only restore state for stateful frames 205 nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame); 206 if (!statefulFrame) { 207 return; 208 } 209 210 // Generate the hash key the state was stored under 211 // Exit early if we get empty key 212 nsIContent* content = aFrame->GetContent(); 213 // If we don't have content, we can't generate a hash 214 // key and there's probably no state information for us. 215 if (!content) { 216 return; 217 } 218 219 nsAutoCString stateKey; 220 Document* doc = content->GetUncomposedDoc(); 221 statefulFrame->GenerateStateKey(content, doc, stateKey); 222 if (stateKey.IsEmpty()) { 223 return; 224 } 225 226 // Get the state from the hash 227 PresState* frameState = aState->GetState(stateKey); 228 if (!frameState) { 229 return; 230 } 231 232 // Restore it 233 nsresult rv = statefulFrame->RestoreState(frameState); 234 if (NS_FAILED(rv)) { 235 return; 236 } 237 238 // If we restore ok, remove the state from the state table 239 aState->RemoveState(stateKey); 240 } 241 242 void nsFrameManager::RestoreFrameState(nsIFrame* aFrame, 243 nsILayoutHistoryState* aState) { 244 MOZ_ASSERT(nullptr != aFrame && nullptr != aState, 245 "null parameters passed in"); 246 247 RestoreFrameStateFor(aFrame, aState); 248 249 // Now restore state recursively for the frame hierarchy rooted at aFrame 250 for (const auto& childList : aFrame->ChildLists()) { 251 for (nsIFrame* child : childList.mList) { 252 RestoreFrameState(child, aState); 253 } 254 } 255 } 256 257 void nsFrameManager::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const { 258 aSizes.mLayoutPresShellSize += aSizes.mState.mMallocSizeOf(this); 259 }