layout/base/nsFrameManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/base/nsFrameManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,735 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + * vim:cindent:ts=2:et:sw=2:
     1.6 + *
     1.7 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.8 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.9 + * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    1.10 + *
    1.11 + * This Original Code has been modified by IBM Corporation. Modifications made by IBM 
    1.12 + * described herein are Copyright (c) International Business Machines Corporation, 2000.
    1.13 + * Modifications to Mozilla code or documentation identified per MPL Section 3.3
    1.14 + *
    1.15 + * Date             Modified by     Description of modification
    1.16 + * 04/20/2000       IBM Corp.      OS/2 VisualAge build.
    1.17 + */
    1.18 +
    1.19 +/* storage of the frame tree and information about it */
    1.20 +
    1.21 +#include "nscore.h"
    1.22 +#include "nsIPresShell.h"
    1.23 +#include "nsStyleContext.h"
    1.24 +#include "nsCOMPtr.h"
    1.25 +#include "plhash.h"
    1.26 +#include "nsPlaceholderFrame.h"
    1.27 +#include "nsGkAtoms.h"
    1.28 +#include "nsILayoutHistoryState.h"
    1.29 +#include "nsPresState.h"
    1.30 +#include "mozilla/dom/Element.h"
    1.31 +#include "nsIDocument.h"
    1.32 +
    1.33 +#include "nsContentUtils.h"
    1.34 +#include "nsError.h"
    1.35 +#include "nsAutoPtr.h"
    1.36 +#include "nsAbsoluteContainingBlock.h"
    1.37 +#include "ChildIterator.h"
    1.38 +
    1.39 +#include "nsFrameManager.h"
    1.40 +#include "GeckoProfiler.h"
    1.41 +#include "nsIStatefulFrame.h"
    1.42 +
    1.43 +  #ifdef DEBUG
    1.44 +    //#define DEBUG_UNDISPLAYED_MAP
    1.45 +  #else
    1.46 +    #undef DEBUG_UNDISPLAYED_MAP
    1.47 +  #endif
    1.48 +
    1.49 +using namespace mozilla;
    1.50 +using namespace mozilla::dom;
    1.51 +
    1.52 +//----------------------------------------------------------------------
    1.53 +
    1.54 +struct PlaceholderMapEntry : public PLDHashEntryHdr {
    1.55 +  // key (the out of flow frame) can be obtained through placeholder frame
    1.56 +  nsPlaceholderFrame *placeholderFrame;
    1.57 +};
    1.58 +
    1.59 +static bool
    1.60 +PlaceholderMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
    1.61 +                         const void *key)
    1.62 +{
    1.63 +  const PlaceholderMapEntry *entry =
    1.64 +    static_cast<const PlaceholderMapEntry*>(hdr);
    1.65 +  NS_ASSERTION(entry->placeholderFrame->GetOutOfFlowFrame() !=
    1.66 +               (void*)0xdddddddd,
    1.67 +               "Dead placeholder in placeholder map");
    1.68 +  return entry->placeholderFrame->GetOutOfFlowFrame() == key;
    1.69 +}
    1.70 +
    1.71 +static const PLDHashTableOps PlaceholderMapOps = {
    1.72 +  PL_DHashAllocTable,
    1.73 +  PL_DHashFreeTable,
    1.74 +  PL_DHashVoidPtrKeyStub,
    1.75 +  PlaceholderMapMatchEntry,
    1.76 +  PL_DHashMoveEntryStub,
    1.77 +  PL_DHashClearEntryStub,
    1.78 +  PL_DHashFinalizeStub,
    1.79 +  nullptr
    1.80 +};
    1.81 +
    1.82 +//----------------------------------------------------------------------
    1.83 +
    1.84 +// XXXldb This seems too complicated for what I think it's doing, and it
    1.85 +// should also be using pldhash rather than plhash to use less memory.
    1.86 +
    1.87 +class nsFrameManagerBase::UndisplayedMap {
    1.88 +public:
    1.89 +  UndisplayedMap(uint32_t aNumBuckets = 16) NS_HIDDEN;
    1.90 +  ~UndisplayedMap(void) NS_HIDDEN;
    1.91 +
    1.92 +  NS_HIDDEN_(UndisplayedNode*) GetFirstNode(nsIContent* aParentContent);
    1.93 +
    1.94 +  NS_HIDDEN_(nsresult) AddNodeFor(nsIContent* aParentContent,
    1.95 +                                  nsIContent* aChild, nsStyleContext* aStyle);
    1.96 +
    1.97 +  NS_HIDDEN_(void) RemoveNodeFor(nsIContent* aParentContent,
    1.98 +                                 UndisplayedNode* aNode);
    1.99 +
   1.100 +  NS_HIDDEN_(void) RemoveNodesFor(nsIContent* aParentContent);
   1.101 +
   1.102 +  // Removes all entries from the hash table
   1.103 +  NS_HIDDEN_(void)  Clear(void);
   1.104 +
   1.105 +protected:
   1.106 +  /**
   1.107 +   * Gets the entry for the provided parent content. If the content
   1.108 +   * is a <xbl:children> element, |**aParentContent| is set to
   1.109 +   * the parent of the children element.
   1.110 +   */
   1.111 +  NS_HIDDEN_(PLHashEntry**) GetEntryFor(nsIContent** aParentContent);
   1.112 +  NS_HIDDEN_(void)          AppendNodeFor(UndisplayedNode* aNode,
   1.113 +                                          nsIContent* aParentContent);
   1.114 +
   1.115 +  PLHashTable*  mTable;
   1.116 +  PLHashEntry** mLastLookup;
   1.117 +};
   1.118 +
   1.119 +//----------------------------------------------------------------------
   1.120 +
   1.121 +nsFrameManager::~nsFrameManager()
   1.122 +{
   1.123 +  NS_ASSERTION(!mPresShell, "nsFrameManager::Destroy never called");
   1.124 +}
   1.125 +
   1.126 +void
   1.127 +nsFrameManager::Destroy()
   1.128 +{
   1.129 +  NS_ASSERTION(mPresShell, "Frame manager already shut down.");
   1.130 +
   1.131 +  // Destroy the frame hierarchy.
   1.132 +  mPresShell->SetIgnoreFrameDestruction(true);
   1.133 +
   1.134 +  // Unregister all placeholders before tearing down the frame tree
   1.135 +  nsFrameManager::ClearPlaceholderFrameMap();
   1.136 +
   1.137 +  if (mRootFrame) {
   1.138 +    mRootFrame->Destroy();
   1.139 +    mRootFrame = nullptr;
   1.140 +  }
   1.141 +  
   1.142 +  delete mUndisplayedMap;
   1.143 +  mUndisplayedMap = nullptr;
   1.144 +
   1.145 +  mPresShell = nullptr;
   1.146 +}
   1.147 +
   1.148 +//----------------------------------------------------------------------
   1.149 +
   1.150 +// Placeholder frame functions
   1.151 +nsPlaceholderFrame*
   1.152 +nsFrameManager::GetPlaceholderFrameFor(const nsIFrame* aFrame)
   1.153 +{
   1.154 +  NS_PRECONDITION(aFrame, "null param unexpected");
   1.155 +
   1.156 +  if (mPlaceholderMap.ops) {
   1.157 +    PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>
   1.158 +                                            (PL_DHashTableOperate(const_cast<PLDHashTable*>(&mPlaceholderMap),
   1.159 +                                aFrame, PL_DHASH_LOOKUP));
   1.160 +    if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
   1.161 +      return entry->placeholderFrame;
   1.162 +    }
   1.163 +  }
   1.164 +
   1.165 +  return nullptr;
   1.166 +}
   1.167 +
   1.168 +nsresult
   1.169 +nsFrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
   1.170 +{
   1.171 +  NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
   1.172 +  NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
   1.173 +                  "unexpected frame type");
   1.174 +  if (!mPlaceholderMap.ops) {
   1.175 +    PL_DHashTableInit(&mPlaceholderMap, &PlaceholderMapOps, nullptr,
   1.176 +                      sizeof(PlaceholderMapEntry), 16);
   1.177 +  }
   1.178 +  PlaceholderMapEntry *entry = static_cast<PlaceholderMapEntry*>(PL_DHashTableOperate(&mPlaceholderMap,
   1.179 +                              aPlaceholderFrame->GetOutOfFlowFrame(),
   1.180 +                              PL_DHASH_ADD));
   1.181 +  if (!entry)
   1.182 +    return NS_ERROR_OUT_OF_MEMORY;
   1.183 +
   1.184 +  NS_ASSERTION(!entry->placeholderFrame, "Registering a placeholder for a frame that already has a placeholder!");
   1.185 +  entry->placeholderFrame = aPlaceholderFrame;
   1.186 +
   1.187 +  return NS_OK;
   1.188 +}
   1.189 +
   1.190 +void
   1.191 +nsFrameManager::UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
   1.192 +{
   1.193 +  NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
   1.194 +  NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
   1.195 +                  "unexpected frame type");
   1.196 +
   1.197 +  if (mPlaceholderMap.ops) {
   1.198 +    PL_DHashTableOperate(&mPlaceholderMap,
   1.199 +                         aPlaceholderFrame->GetOutOfFlowFrame(),
   1.200 +                         PL_DHASH_REMOVE);
   1.201 +  }
   1.202 +}
   1.203 +
   1.204 +static PLDHashOperator
   1.205 +UnregisterPlaceholders(PLDHashTable* table, PLDHashEntryHdr* hdr,
   1.206 +                       uint32_t number, void* arg)
   1.207 +{
   1.208 +  PlaceholderMapEntry* entry = static_cast<PlaceholderMapEntry*>(hdr);
   1.209 +  entry->placeholderFrame->SetOutOfFlowFrame(nullptr);
   1.210 +  return PL_DHASH_NEXT;
   1.211 +}
   1.212 +
   1.213 +void
   1.214 +nsFrameManager::ClearPlaceholderFrameMap()
   1.215 +{
   1.216 +  if (mPlaceholderMap.ops) {
   1.217 +    PL_DHashTableEnumerate(&mPlaceholderMap, UnregisterPlaceholders, nullptr);
   1.218 +    PL_DHashTableFinish(&mPlaceholderMap);
   1.219 +    mPlaceholderMap.ops = nullptr;
   1.220 +  }
   1.221 +}
   1.222 +
   1.223 +//----------------------------------------------------------------------
   1.224 +
   1.225 +nsStyleContext*
   1.226 +nsFrameManager::GetUndisplayedContent(nsIContent* aContent)
   1.227 +{
   1.228 +  if (!aContent || !mUndisplayedMap)
   1.229 +    return nullptr;
   1.230 +
   1.231 +  nsIContent* parent = aContent->GetParent();
   1.232 +  for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(parent);
   1.233 +         node; node = node->mNext) {
   1.234 +    if (node->mContent == aContent)
   1.235 +      return node->mStyle;
   1.236 +  }
   1.237 +
   1.238 +  return nullptr;
   1.239 +}
   1.240 +
   1.241 +UndisplayedNode*
   1.242 +nsFrameManager::GetAllUndisplayedContentIn(nsIContent* aParentContent)
   1.243 +{
   1.244 +  if (!mUndisplayedMap)
   1.245 +    return nullptr;
   1.246 +
   1.247 +  return mUndisplayedMap->GetFirstNode(aParentContent);
   1.248 +}
   1.249 +
   1.250 +void
   1.251 +nsFrameManager::SetUndisplayedContent(nsIContent* aContent, 
   1.252 +                                      nsStyleContext* aStyleContext)
   1.253 +{
   1.254 +  NS_PRECONDITION(!aStyleContext->GetPseudo(),
   1.255 +                  "Should only have actual elements here");
   1.256 +
   1.257 +#ifdef DEBUG_UNDISPLAYED_MAP
   1.258 +  static int i = 0;
   1.259 +  printf("SetUndisplayedContent(%d): p=%p \n", i++, (void *)aContent);
   1.260 +#endif
   1.261 +
   1.262 +  NS_ASSERTION(!GetUndisplayedContent(aContent),
   1.263 +               "Already have an undisplayed context entry for aContent");
   1.264 +
   1.265 +  if (! mUndisplayedMap) {
   1.266 +    mUndisplayedMap = new UndisplayedMap;
   1.267 +  }
   1.268 +  nsIContent* parent = aContent->GetParent();
   1.269 +  NS_ASSERTION(parent || (mPresShell && mPresShell->GetDocument() &&
   1.270 +               mPresShell->GetDocument()->GetRootElement() == aContent),
   1.271 +               "undisplayed content must have a parent, unless it's the root "
   1.272 +               "element");
   1.273 +  mUndisplayedMap->AddNodeFor(parent, aContent, aStyleContext);
   1.274 +}
   1.275 +
   1.276 +void
   1.277 +nsFrameManager::ChangeUndisplayedContent(nsIContent* aContent, 
   1.278 +                                         nsStyleContext* aStyleContext)
   1.279 +{
   1.280 +  NS_ASSERTION(mUndisplayedMap, "no existing undisplayed content");
   1.281 +  
   1.282 +#ifdef DEBUG_UNDISPLAYED_MAP
   1.283 +   static int i = 0;
   1.284 +   printf("ChangeUndisplayedContent(%d): p=%p \n", i++, (void *)aContent);
   1.285 +#endif
   1.286 +
   1.287 +  for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aContent->GetParent());
   1.288 +         node; node = node->mNext) {
   1.289 +    if (node->mContent == aContent) {
   1.290 +      node->mStyle = aStyleContext;
   1.291 +      return;
   1.292 +    }
   1.293 +  }
   1.294 +
   1.295 +  NS_NOTREACHED("no existing undisplayed content");
   1.296 +}
   1.297 +
   1.298 +void
   1.299 +nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent,
   1.300 +                                          nsIContent* aParentContent)
   1.301 +{
   1.302 +#ifdef DEBUG_UNDISPLAYED_MAP
   1.303 +  static int i = 0;
   1.304 +  printf("ClearUndisplayedContent(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent);
   1.305 +#endif
   1.306 +  
   1.307 +  if (mUndisplayedMap) {
   1.308 +    UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent);
   1.309 +    while (node) {
   1.310 +      if (node->mContent == aContent) {
   1.311 +        mUndisplayedMap->RemoveNodeFor(aParentContent, node);
   1.312 +
   1.313 +#ifdef DEBUG_UNDISPLAYED_MAP
   1.314 +        printf( "REMOVED!\n");
   1.315 +#endif
   1.316 +#ifdef DEBUG
   1.317 +        // make sure that there are no more entries for the same content
   1.318 +        nsStyleContext *context = GetUndisplayedContent(aContent);
   1.319 +        NS_ASSERTION(context == nullptr, "Found more undisplayed content data after removal");
   1.320 +#endif
   1.321 +        return;
   1.322 +      }
   1.323 +      node = node->mNext;
   1.324 +    }
   1.325 +  }
   1.326 +}
   1.327 +
   1.328 +void
   1.329 +nsFrameManager::ClearAllUndisplayedContentIn(nsIContent* aParentContent)
   1.330 +{
   1.331 +#ifdef DEBUG_UNDISPLAYED_MAP
   1.332 +  static int i = 0;
   1.333 +  printf("ClearAllUndisplayedContentIn(%d): parent=%p \n", i++, (void*)aParentContent);
   1.334 +#endif
   1.335 +
   1.336 +  if (mUndisplayedMap) {
   1.337 +    mUndisplayedMap->RemoveNodesFor(aParentContent);
   1.338 +  }
   1.339 +
   1.340 +  // Need to look at aParentContent's content list due to XBL insertions.
   1.341 +  // Nodes in aParentContent's content list do not have aParentContent as a
   1.342 +  // parent, but are treated as children of aParentContent. We iterate over
   1.343 +  // the flattened content list and just ignore any nodes we don't care about.
   1.344 +  FlattenedChildIterator iter(aParentContent);
   1.345 +  for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
   1.346 +    if (child->GetParent() != aParentContent) {
   1.347 +      ClearUndisplayedContentIn(child, child->GetParent());
   1.348 +    }
   1.349 +  }
   1.350 +}
   1.351 +
   1.352 +//----------------------------------------------------------------------
   1.353 +nsresult
   1.354 +nsFrameManager::AppendFrames(nsIFrame*       aParentFrame,
   1.355 +                             ChildListID     aListID,
   1.356 +                             nsFrameList&    aFrameList)
   1.357 +{
   1.358 +  if (aParentFrame->IsAbsoluteContainer() &&
   1.359 +      aListID == aParentFrame->GetAbsoluteListID()) {
   1.360 +    return aParentFrame->GetAbsoluteContainingBlock()->
   1.361 +           AppendFrames(aParentFrame, aListID, aFrameList);
   1.362 +  } else {
   1.363 +    return aParentFrame->AppendFrames(aListID, aFrameList);
   1.364 +  }
   1.365 +}
   1.366 +
   1.367 +nsresult
   1.368 +nsFrameManager::InsertFrames(nsIFrame*       aParentFrame,
   1.369 +                             ChildListID     aListID,
   1.370 +                             nsIFrame*       aPrevFrame,
   1.371 +                             nsFrameList&    aFrameList)
   1.372 +{
   1.373 +  NS_PRECONDITION(!aPrevFrame || (!aPrevFrame->GetNextContinuation()
   1.374 +                  || (((aPrevFrame->GetNextContinuation()->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER))
   1.375 +                  && !(aPrevFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER))),
   1.376 +                  "aPrevFrame must be the last continuation in its chain!");
   1.377 +
   1.378 +  if (aParentFrame->IsAbsoluteContainer() &&
   1.379 +      aListID == aParentFrame->GetAbsoluteListID()) {
   1.380 +    return aParentFrame->GetAbsoluteContainingBlock()->
   1.381 +           InsertFrames(aParentFrame, aListID, aPrevFrame, aFrameList);
   1.382 +  } else {
   1.383 +    return aParentFrame->InsertFrames(aListID, aPrevFrame, aFrameList);
   1.384 +  }
   1.385 +}
   1.386 +
   1.387 +nsresult
   1.388 +nsFrameManager::RemoveFrame(ChildListID     aListID,
   1.389 +                            nsIFrame*       aOldFrame)
   1.390 +{
   1.391 +  bool wasDestroyingFrames = mIsDestroyingFrames;
   1.392 +  mIsDestroyingFrames = true;
   1.393 +
   1.394 +  // In case the reflow doesn't invalidate anything since it just leaves
   1.395 +  // a gap where the old frame was, we invalidate it here.  (This is
   1.396 +  // reasonably likely to happen when removing a last child in a way
   1.397 +  // that doesn't change the size of the parent.)
   1.398 +  // This has to sure to invalidate the entire overflow rect; this
   1.399 +  // is important in the presence of absolute positioning
   1.400 +  aOldFrame->InvalidateFrameForRemoval();
   1.401 +
   1.402 +  NS_ASSERTION(!aOldFrame->GetPrevContinuation() ||
   1.403 +               // exception for nsCSSFrameConstructor::RemoveFloatingFirstLetterFrames
   1.404 +               aOldFrame->GetType() == nsGkAtoms::textFrame,
   1.405 +               "Must remove first continuation.");
   1.406 +  NS_ASSERTION(!(aOldFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW &&
   1.407 +                 GetPlaceholderFrameFor(aOldFrame)),
   1.408 +               "Must call RemoveFrame on placeholder for out-of-flows.");
   1.409 +  nsresult rv = NS_OK;
   1.410 +  nsIFrame* parentFrame = aOldFrame->GetParent();
   1.411 +  if (parentFrame->IsAbsoluteContainer() &&
   1.412 +      aListID == parentFrame->GetAbsoluteListID()) {
   1.413 +    parentFrame->GetAbsoluteContainingBlock()->
   1.414 +      RemoveFrame(parentFrame, aListID, aOldFrame);
   1.415 +  } else {
   1.416 +    rv = parentFrame->RemoveFrame(aListID, aOldFrame);
   1.417 +  }
   1.418 +
   1.419 +  mIsDestroyingFrames = wasDestroyingFrames;
   1.420 +
   1.421 +  return rv;
   1.422 +}
   1.423 +
   1.424 +//----------------------------------------------------------------------
   1.425 +
   1.426 +void
   1.427 +nsFrameManager::NotifyDestroyingFrame(nsIFrame* aFrame)
   1.428 +{
   1.429 +  nsIContent* content = aFrame->GetContent();
   1.430 +  if (content && content->GetPrimaryFrame() == aFrame) {
   1.431 +    ClearAllUndisplayedContentIn(content);
   1.432 +  }
   1.433 +}
   1.434 +
   1.435 +// Capture state for a given frame.
   1.436 +// Accept a content id here, in some cases we may not have content (scroll position)
   1.437 +void
   1.438 +nsFrameManager::CaptureFrameStateFor(nsIFrame* aFrame,
   1.439 +                                     nsILayoutHistoryState* aState)
   1.440 +{
   1.441 +  if (!aFrame || !aState) {
   1.442 +    NS_WARNING("null frame, or state");
   1.443 +    return;
   1.444 +  }
   1.445 +
   1.446 +  // Only capture state for stateful frames
   1.447 +  nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame);
   1.448 +  if (!statefulFrame) {
   1.449 +    return;
   1.450 +  }
   1.451 +
   1.452 +  // Capture the state, exit early if we get null (nothing to save)
   1.453 +  nsAutoPtr<nsPresState> frameState;
   1.454 +  nsresult rv = statefulFrame->SaveState(getter_Transfers(frameState));
   1.455 +  if (!frameState) {
   1.456 +    return;
   1.457 +  }
   1.458 +
   1.459 +  // Generate the hash key to store the state under
   1.460 +  // Exit early if we get empty key
   1.461 +  nsAutoCString stateKey;
   1.462 +  nsIContent* content = aFrame->GetContent();
   1.463 +  nsIDocument* doc = content ? content->GetCurrentDoc() : nullptr;
   1.464 +  rv = nsContentUtils::GenerateStateKey(content, doc, stateKey);
   1.465 +  if(NS_FAILED(rv) || stateKey.IsEmpty()) {
   1.466 +    return;
   1.467 +  }
   1.468 +
   1.469 +  // Store the state. aState owns frameState now.
   1.470 +  aState->AddState(stateKey, frameState.forget());
   1.471 +}
   1.472 +
   1.473 +void
   1.474 +nsFrameManager::CaptureFrameState(nsIFrame* aFrame,
   1.475 +                                  nsILayoutHistoryState* aState)
   1.476 +{
   1.477 +  NS_PRECONDITION(nullptr != aFrame && nullptr != aState, "null parameters passed in");
   1.478 +
   1.479 +  CaptureFrameStateFor(aFrame, aState);
   1.480 +
   1.481 +  // Now capture state recursively for the frame hierarchy rooted at aFrame
   1.482 +  nsIFrame::ChildListIterator lists(aFrame);
   1.483 +  for (; !lists.IsDone(); lists.Next()) {
   1.484 +    nsFrameList::Enumerator childFrames(lists.CurrentList());
   1.485 +    for (; !childFrames.AtEnd(); childFrames.Next()) {
   1.486 +      nsIFrame* child = childFrames.get();
   1.487 +      if (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
   1.488 +        // We'll pick it up when we get to its placeholder
   1.489 +        continue;
   1.490 +      }
   1.491 +      // Make sure to walk through placeholders as needed, so that we
   1.492 +      // save state for out-of-flows which may not be our descendants
   1.493 +      // themselves but whose placeholders are our descendants.
   1.494 +      CaptureFrameState(nsPlaceholderFrame::GetRealFrameFor(child), aState);
   1.495 +    }
   1.496 +  }
   1.497 +}
   1.498 +
   1.499 +// Restore state for a given frame.
   1.500 +// Accept a content id here, in some cases we may not have content (scroll position)
   1.501 +void
   1.502 +nsFrameManager::RestoreFrameStateFor(nsIFrame* aFrame,
   1.503 +                                     nsILayoutHistoryState* aState)
   1.504 +{
   1.505 +  if (!aFrame || !aState) {
   1.506 +    NS_WARNING("null frame or state");
   1.507 +    return;
   1.508 +  }
   1.509 +
   1.510 +  // Only restore state for stateful frames
   1.511 +  nsIStatefulFrame* statefulFrame = do_QueryFrame(aFrame);
   1.512 +  if (!statefulFrame) {
   1.513 +    return;
   1.514 +  }
   1.515 +
   1.516 +  // Generate the hash key the state was stored under
   1.517 +  // Exit early if we get empty key
   1.518 +  nsIContent* content = aFrame->GetContent();
   1.519 +  // If we don't have content, we can't generate a hash
   1.520 +  // key and there's probably no state information for us.
   1.521 +  if (!content) {
   1.522 +    return;
   1.523 +  }
   1.524 +
   1.525 +  nsAutoCString stateKey;
   1.526 +  nsIDocument* doc = content->GetCurrentDoc();
   1.527 +  nsresult rv = nsContentUtils::GenerateStateKey(content, doc, stateKey);
   1.528 +  if (NS_FAILED(rv) || stateKey.IsEmpty()) {
   1.529 +    return;
   1.530 +  }
   1.531 +
   1.532 +  // Get the state from the hash
   1.533 +  nsPresState* frameState = aState->GetState(stateKey);
   1.534 +  if (!frameState) {
   1.535 +    return;
   1.536 +  }
   1.537 +
   1.538 +  // Restore it
   1.539 +  rv = statefulFrame->RestoreState(frameState);
   1.540 +  if (NS_FAILED(rv)) {
   1.541 +    return;
   1.542 +  }
   1.543 +
   1.544 +  // If we restore ok, remove the state from the state table
   1.545 +  aState->RemoveState(stateKey);
   1.546 +}
   1.547 +
   1.548 +void
   1.549 +nsFrameManager::RestoreFrameState(nsIFrame* aFrame,
   1.550 +                                  nsILayoutHistoryState* aState)
   1.551 +{
   1.552 +  NS_PRECONDITION(nullptr != aFrame && nullptr != aState, "null parameters passed in");
   1.553 +  
   1.554 +  RestoreFrameStateFor(aFrame, aState);
   1.555 +
   1.556 +  // Now restore state recursively for the frame hierarchy rooted at aFrame
   1.557 +  nsIFrame::ChildListIterator lists(aFrame);
   1.558 +  for (; !lists.IsDone(); lists.Next()) {
   1.559 +    nsFrameList::Enumerator childFrames(lists.CurrentList());
   1.560 +    for (; !childFrames.AtEnd(); childFrames.Next()) {
   1.561 +      RestoreFrameState(childFrames.get(), aState);
   1.562 +    }
   1.563 +  }
   1.564 +}
   1.565 +
   1.566 +//----------------------------------------------------------------------
   1.567 +
   1.568 +static PLHashNumber
   1.569 +HashKey(void* key)
   1.570 +{
   1.571 +  return NS_PTR_TO_INT32(key);
   1.572 +}
   1.573 +
   1.574 +static int
   1.575 +CompareKeys(void* key1, void* key2)
   1.576 +{
   1.577 +  return key1 == key2;
   1.578 +}
   1.579 +
   1.580 +//----------------------------------------------------------------------
   1.581 +
   1.582 +nsFrameManagerBase::UndisplayedMap::UndisplayedMap(uint32_t aNumBuckets)
   1.583 +{
   1.584 +  MOZ_COUNT_CTOR(nsFrameManagerBase::UndisplayedMap);
   1.585 +  mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey,
   1.586 +                           (PLHashComparator)CompareKeys,
   1.587 +                           (PLHashComparator)nullptr,
   1.588 +                           nullptr, nullptr);
   1.589 +  mLastLookup = nullptr;
   1.590 +}
   1.591 +
   1.592 +nsFrameManagerBase::UndisplayedMap::~UndisplayedMap(void)
   1.593 +{
   1.594 +  MOZ_COUNT_DTOR(nsFrameManagerBase::UndisplayedMap);
   1.595 +  Clear();
   1.596 +  PL_HashTableDestroy(mTable);
   1.597 +}
   1.598 +
   1.599 +PLHashEntry**  
   1.600 +nsFrameManagerBase::UndisplayedMap::GetEntryFor(nsIContent** aParentContent)
   1.601 +{
   1.602 +  nsIContent* parentContent = *aParentContent;
   1.603 +
   1.604 +  if (mLastLookup && (parentContent == (*mLastLookup)->key)) {
   1.605 +    return mLastLookup;
   1.606 +  }
   1.607 +
   1.608 +  // In the case of XBL default content, <xbl:children> elements do not get a
   1.609 +  // frame causing a mismatch between the content tree and the frame tree.
   1.610 +  // |GetEntryFor| is sometimes called with the content tree parent (which may
   1.611 +  // be a <xbl:children> element) but the parent in the frame tree would be the
   1.612 +  // insertion parent (parent of the <xbl:children> element). Here the children
   1.613 +  // elements are normalized to the insertion parent to correct for the mismatch.
   1.614 +  if (parentContent && nsContentUtils::IsContentInsertionPoint(parentContent)) {
   1.615 +    parentContent = parentContent->GetParent();
   1.616 +    // Change the caller's pointer for the parent content to be the insertion parent.
   1.617 +    *aParentContent = parentContent;
   1.618 +  }
   1.619 +
   1.620 +  PLHashNumber hashCode = NS_PTR_TO_INT32(parentContent);
   1.621 +  PLHashEntry** entry = PL_HashTableRawLookup(mTable, hashCode, parentContent);
   1.622 +  if (*entry) {
   1.623 +    mLastLookup = entry;
   1.624 +  }
   1.625 +  return entry;
   1.626 +}
   1.627 +
   1.628 +UndisplayedNode* 
   1.629 +nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent)
   1.630 +{
   1.631 +  PLHashEntry** entry = GetEntryFor(&aParentContent);
   1.632 +  if (*entry) {
   1.633 +    return (UndisplayedNode*)((*entry)->value);
   1.634 +  }
   1.635 +  return nullptr;
   1.636 +}
   1.637 +
   1.638 +void
   1.639 +nsFrameManagerBase::UndisplayedMap::AppendNodeFor(UndisplayedNode* aNode,
   1.640 +                                                  nsIContent* aParentContent)
   1.641 +{
   1.642 +  PLHashEntry** entry = GetEntryFor(&aParentContent);
   1.643 +  if (*entry) {
   1.644 +    UndisplayedNode*  node = (UndisplayedNode*)((*entry)->value);
   1.645 +    while (node->mNext) {
   1.646 +      if (node->mContent == aNode->mContent) {
   1.647 +        // We actually need to check this in optimized builds because
   1.648 +        // there are some callers that do this.  See bug 118014, bug
   1.649 +        // 136704, etc.
   1.650 +        NS_NOTREACHED("node in map twice");
   1.651 +        delete aNode;
   1.652 +        return;
   1.653 +      }
   1.654 +      node = node->mNext;
   1.655 +    }
   1.656 +    node->mNext = aNode;
   1.657 +  }
   1.658 +  else {
   1.659 +    PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent);
   1.660 +    PL_HashTableRawAdd(mTable, entry, hashCode, aParentContent, aNode);
   1.661 +    mLastLookup = nullptr; // hashtable may have shifted bucket out from under us
   1.662 +  }
   1.663 +}
   1.664 +
   1.665 +nsresult 
   1.666 +nsFrameManagerBase::UndisplayedMap::AddNodeFor(nsIContent* aParentContent,
   1.667 +                                               nsIContent* aChild, 
   1.668 +                                               nsStyleContext* aStyle)
   1.669 +{
   1.670 +  UndisplayedNode*  node = new UndisplayedNode(aChild, aStyle);
   1.671 +
   1.672 +  AppendNodeFor(node, aParentContent);
   1.673 +  return NS_OK;
   1.674 +}
   1.675 +
   1.676 +void
   1.677 +nsFrameManagerBase::UndisplayedMap::RemoveNodeFor(nsIContent* aParentContent,
   1.678 +                                                  UndisplayedNode* aNode)
   1.679 +{
   1.680 +  PLHashEntry** entry = GetEntryFor(&aParentContent);
   1.681 +  NS_ASSERTION(*entry, "content not in map");
   1.682 +  if (*entry) {
   1.683 +    if ((UndisplayedNode*)((*entry)->value) == aNode) {  // first node
   1.684 +      if (aNode->mNext) {
   1.685 +        (*entry)->value = aNode->mNext;
   1.686 +        aNode->mNext = nullptr;
   1.687 +      }
   1.688 +      else {
   1.689 +        PL_HashTableRawRemove(mTable, entry, *entry);
   1.690 +        mLastLookup = nullptr; // hashtable may have shifted bucket out from under us
   1.691 +      }
   1.692 +    }
   1.693 +    else {
   1.694 +      UndisplayedNode*  node = (UndisplayedNode*)((*entry)->value);
   1.695 +      while (node->mNext) {
   1.696 +        if (node->mNext == aNode) {
   1.697 +          node->mNext = aNode->mNext;
   1.698 +          aNode->mNext = nullptr;
   1.699 +          break;
   1.700 +        }
   1.701 +        node = node->mNext;
   1.702 +      }
   1.703 +    }
   1.704 +  }
   1.705 +  delete aNode;
   1.706 +}
   1.707 +
   1.708 +void
   1.709 +nsFrameManagerBase::UndisplayedMap::RemoveNodesFor(nsIContent* aParentContent)
   1.710 +{
   1.711 +  PLHashEntry** entry = GetEntryFor(&aParentContent);
   1.712 +  NS_ASSERTION(entry, "content not in map");
   1.713 +  if (*entry) {
   1.714 +    UndisplayedNode*  node = (UndisplayedNode*)((*entry)->value);
   1.715 +    NS_ASSERTION(node, "null node for non-null entry in UndisplayedMap");
   1.716 +    delete node;
   1.717 +    PL_HashTableRawRemove(mTable, entry, *entry);
   1.718 +    mLastLookup = nullptr; // hashtable may have shifted bucket out from under us
   1.719 +  }
   1.720 +}
   1.721 +
   1.722 +static int
   1.723 +RemoveUndisplayedEntry(PLHashEntry* he, int i, void* arg)
   1.724 +{
   1.725 +  UndisplayedNode*  node = (UndisplayedNode*)(he->value);
   1.726 +  delete node;
   1.727 +  // Remove and free this entry and continue enumerating
   1.728 +  return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT;
   1.729 +}
   1.730 +
   1.731 +void
   1.732 +nsFrameManagerBase::UndisplayedMap::Clear(void)
   1.733 +{
   1.734 +  mLastLookup = nullptr;
   1.735 +  PL_HashTableEnumerateEntries(mTable, RemoveUndisplayedEntry, 0);
   1.736 +}
   1.737 +
   1.738 +uint32_t nsFrameManagerBase::sGlobalGenerationNumber;

mercurial