tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }