Files
palemoon27/layout/base/nsFrameManager.cpp
T
roytam1 86eaa55e65 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1251999 - [css-grid] Update <fixed-size> parsing to the latest spec. r=dholbert (07f68007b7)
- Bug 1151243 part 5 - [css-grid] Reftests for abs. pos. static-position. (f9a3b24508)
- Bug 1242053 part 1 - [css-grid] Fix parsing / serialization of grid-template subgrid values. r=dholbert (39576c2f6d)
- Bug 1242053 part 2 - [css-grid] Enable subgrid support for tests. r=dholbert (6da4aeb35a)
- Bug 1240956 part 2 - [css-grid] Update tests due to the changed column/row value order for 'grid', 'grid-template' and 'grid-gap' properties. (16d2258d3a)
- Bug 1253529 part 1 - [css-grid] Replace uses of the 'grid-template' shorthand with 'grid'. (f847bfa554)
- Bug 1253529 part 2 - [css-grid] Remove the 'grid-template' shorthand. r=dholbert (d553c2f623)
- Bug 594933 - Part 1: Let calc() supports number. r=dbaron (e4bc39b5f0)
- Bug 594933 - Part 2: Add support for calc() to line-height. r=dbaron (901227bacb)
- Bug 1253189 part 5 - Import reftests of css-values. r=dbaron (4599431827)
- Bug 1253189 part 4 - Add skip.list for skipping imported tests. r=dbaron (014f7ca7de)
- Bug 1253189 part 1 - Make import-tests.py compatible with Windows. r=dbaron (a22c3e4f7f)
- Bug 1253189 part 2 - Update import-tests.py script to work on the current repo. r=dbaron (7535b38b71)
- Bug 1253189 part 3 - Update existing received w3c reftests. r=dbaron (0fa2d6e2de)
- Bug 1253189 followup - Update skip.list to match the directory name on CLOSED TREE (019b55c3c9)
- Bug 968761 - Treat nested calc() as plain parenthesis. r=dbaron (537506f4e5)
- Bug 1248371 - [css-grid] Require that each track has a definite min- or max-sizing function when the track list contains a repeat(auto-fill/fit) track. r=dholbert (330dd37427)
- Bug 1244638 - Part 1:implement AnimationEffectTiming iterationStart. r=birtles, r=smaug (d0217068ac)
- Bug 1244638 - Part 2: Throw TypeError if iterationStart is NaN, negative value or Infinity. r=birtles, r=smaug (f631e261a0)
- Bug 1238845 - Implement UUID helper - dom/bluetooth & dom/webidl & layout/build change. r=jocelyn, r=bz (3ddfa8cccf)
- Bug 1252790 - Remove RuleHashTableOps. r=dbaron. (b52d9e2613)
- Bug 1252375 - Fix up jemalloc stats reporting. r=erahm. (dbecb54598)
- Bug 1249503 - Make WindowsAddressSpaceReporter bloat memory reports less. r=erahm. (3bfb6932cb)
- Bug 1253085 - Remove the |PLDHashTable*| argument from PLDHash{HashKey,MatchEntry}. r=froydnj. (1331fd9ea0)
- Bug 1251127 - Gracefully handle multiple calls to nsMemoryReporterManager::Init(). r=erahm. (c51f57907e)
- Bug 1234540 - verify the validity of hashEntry, if it's null return with NS_ERROR_FAILURE. r=michal (f44bd269db)
- Bug 1112499 - Set up shutdown pipe before new thread starts. r=keeler (73b9a56196)
- Bug 1245059 - Disable NotifyIpInterfaceChange use for now. r=mcmanus (ab11fd8507)
- Bug 1251873 - Store the trimmed referrer URL on HTTP channel if a trimming referrer policy is in effect; r=mcmanus (3f43d5b351)
- more bits of bug 366559 - patch 7, content-encoding brotli for https (f127a97450)
- Bug 1250115 - initialized pollInterval with 0 in order to prevent garbage value if Poll(..) doesn't get called. r=dragana (af55e06789)
- Bug 1251493 - Remove remaining references to MOZILLA_XPCOMRT_API from netwerk. r=mcmanus (43d711d2d6)
- Bug 1242452 - only if PR_GetIdentitiesLayer return valid pointer continue and pass it's result to PR_GetIdentitiesLayer. r=mcmanus (61a2785337)
- Bug 1251595 - Check if mNetAddr is set before sending ipv4/6 telemetry. r=mcmanus (9d081bc0b9)
- Bug 1252755: increase maximum UDP packet size to 9216 bytes for jumbo frames. r=jesup (3d629473ea)
- Bug 1194259: warnings-as-errors bustage fix rs=bustagn a CLOSED TREE (5b8288ff6d)
- Bug 1227781 - Fix crash with bogus STUN parameters. r=bwc (7d132a37ff)
- Bug 1231196: Fix init of PeerConnectionImpl::mThread for unit-tests and similar. r+drno (60f11ed191)
- Bug 1230184 - plumb setParameters down to JsepTrack. r=bwc, r=mrbkap (fdeaafb737)
- Bug 1231507 - Part 1: selectSsrc chrome-only API for SSRC-based filtering of receive tracks. r=drno, r=smaug (c24ce1a76e)
- Bug 1221837 - Accept hard coded codec numbers without rtpmaps. r=bwc (89b81c861c)
- Bug 1231507 - Part 2: added mochitest SimulCast offer test case r=jib (05d838960f)
- Bug 1230184 - setParameters webidl. r=smaug (6319cdfca7)
- Bug 1230184 - add input parameter validation to setParameters. r=bwc (c0740c142a)
- Bug 1230184 - add media.peerconnection.simulcast pref. r=bwc (99b1a4978d)
- Bug 1247619 - add console warning about OAuth for STUN not yet supported. r=jesup,smaug (801eaa66e1)
- Bug 1241153: Stop blocking addTrack on GMP init, and update codecs on existing tracks when GMP init finishes. r=jesup (3473dfe0ce)
- Bug 1251737 - Remove remaining references to MOZILLA_XPCOMRT_API from media. r=jesup (7b32d9d191)
- Bug 1218326: Prevent datachannel operations on closed PeerConnections. r=jesup a=lizzard (5d20d7dd7f)
- Bug 1211091 - use upper boundary for streams per data channel. r=bwc (7f9462c434)
- Bug 1188391 - separate immediate ICE failures from failures after checking. r=bwc, r=vladan (9095c58656)
- Bug 1227708 - Differentiate ICE_SUCCESS_RATE telemetry for loop vs webrtc r=jesup (399406c872)
- Bug 1232082 - fix removal of remote tracks to update receivers. r=jesup (81998ed355)
- Bug 1244913 - Add test_peerConnection_scaleResolution.html r=bwc,jesup (8ac2d8a363)
- Bug 1249860 - Add simulcast test for resolution scaling. r=bwc (c7753bf02b)
- Bug 1247622 - Make RTCRtpSender.setParameters return a promise. r=jesup,smaug (4c2504adc8)
2024-02-29 23:07:00 +08:00

838 lines
26 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:cindent:ts=2:et:sw=2:
*
* 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/.
*
* This Original Code has been modified by IBM Corporation. Modifications made by IBM
* described herein are Copyright (c) International Business Machines Corporation, 2000.
* Modifications to Mozilla code or documentation identified per MPL Section 3.3
*
* Date Modified by Description of modification
* 04/20/2000 IBM Corp. OS/2 VisualAge build.
*/
/* storage of the frame tree and information about it */
#include "nscore.h"
#include "nsIPresShell.h"
#include "nsStyleContext.h"
#include "nsCOMPtr.h"
#include "plhash.h"
#include "nsPlaceholderFrame.h"
#include "nsGkAtoms.h"
#include "nsILayoutHistoryState.h"
#include "nsPresState.h"
#include "mozilla/dom/Element.h"
#include "nsIDocument.h"
#include "nsContentUtils.h"
#include "nsError.h"
#include "nsAutoPtr.h"
#include "nsAbsoluteContainingBlock.h"
#include "ChildIterator.h"
#include "nsFrameManager.h"
#include "GeckoProfiler.h"
#include "nsIStatefulFrame.h"
#include "nsContainerFrame.h"
#ifdef DEBUG
//#define DEBUG_UNDISPLAYED_MAP
//#define DEBUG_DISPLAY_CONTENTS_MAP
#else
#undef DEBUG_UNDISPLAYED_MAP
#undef DEBUG_DISPLAY_CONTENTS_MAP
#endif
using namespace mozilla;
using namespace mozilla::dom;
//----------------------------------------------------------------------
struct PlaceholderMapEntry : public PLDHashEntryHdr {
// key (the out of flow frame) can be obtained through placeholder frame
nsPlaceholderFrame *placeholderFrame;
};
static bool
PlaceholderMapMatchEntry(const PLDHashEntryHdr *hdr, const void *key)
{
const PlaceholderMapEntry *entry =
static_cast<const PlaceholderMapEntry*>(hdr);
NS_ASSERTION(entry->placeholderFrame->GetOutOfFlowFrame() !=
(void*)0xdddddddd,
"Dead placeholder in placeholder map");
return entry->placeholderFrame->GetOutOfFlowFrame() == key;
}
static const PLDHashTableOps PlaceholderMapOps = {
PLDHashTable::HashVoidPtrKeyStub,
PlaceholderMapMatchEntry,
PLDHashTable::MoveEntryStub,
PLDHashTable::ClearEntryStub,
nullptr
};
nsFrameManagerBase::nsFrameManagerBase()
: mPresShell(nullptr)
, mRootFrame(nullptr)
, mPlaceholderMap(&PlaceholderMapOps, sizeof(PlaceholderMapEntry))
, mUndisplayedMap(nullptr)
, mDisplayContentsMap(nullptr)
, mIsDestroyingFrames(false)
{
}
//----------------------------------------------------------------------
// XXXldb This seems too complicated for what I think it's doing, and it
// should also be using PLDHashTable rather than plhash to use less memory.
class nsFrameManagerBase::UndisplayedMap {
public:
explicit UndisplayedMap(uint32_t aNumBuckets = 16);
~UndisplayedMap(void);
UndisplayedNode* GetFirstNode(nsIContent* aParentContent);
nsresult AddNodeFor(nsIContent* aParentContent,
nsIContent* aChild, nsStyleContext* aStyle);
void RemoveNodeFor(nsIContent* aParentContent,
UndisplayedNode* aNode);
void RemoveNodesFor(nsIContent* aParentContent);
UndisplayedNode* UnlinkNodesFor(nsIContent* aParentContent);
// Removes all entries from the hash table
void Clear(void);
protected:
/**
* Gets the entry for the provided parent content. If the content
* is a <xbl:children> element, |**aParentContent| is set to
* the parent of the children element.
*/
PLHashEntry** GetEntryFor(nsIContent** aParentContent);
void AppendNodeFor(UndisplayedNode* aNode,
nsIContent* aParentContent);
PLHashTable* mTable;
PLHashEntry** mLastLookup;
};
//----------------------------------------------------------------------
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);
// Unregister all placeholders before tearing down the frame tree
nsFrameManager::ClearPlaceholderFrameMap();
if (mRootFrame) {
mRootFrame->Destroy();
mRootFrame = nullptr;
}
delete mUndisplayedMap;
mUndisplayedMap = nullptr;
delete mDisplayContentsMap;
mDisplayContentsMap = nullptr;
mPresShell = nullptr;
}
//----------------------------------------------------------------------
// Placeholder frame functions
nsPlaceholderFrame*
nsFrameManager::GetPlaceholderFrameFor(const nsIFrame* aFrame)
{
NS_PRECONDITION(aFrame, "null param unexpected");
auto entry = static_cast<PlaceholderMapEntry*>
(const_cast<PLDHashTable*>(&mPlaceholderMap)->Search(aFrame));
if (entry) {
return entry->placeholderFrame;
}
return nullptr;
}
nsresult
nsFrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
{
NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
"unexpected frame type");
auto entry = static_cast<PlaceholderMapEntry*>
(mPlaceholderMap.Add(aPlaceholderFrame->GetOutOfFlowFrame(), fallible));
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
NS_ASSERTION(!entry->placeholderFrame, "Registering a placeholder for a frame that already has a placeholder!");
entry->placeholderFrame = aPlaceholderFrame;
return NS_OK;
}
void
nsFrameManager::UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
{
NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
NS_PRECONDITION(nsGkAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
"unexpected frame type");
mPlaceholderMap.Remove(aPlaceholderFrame->GetOutOfFlowFrame());
}
void
nsFrameManager::ClearPlaceholderFrameMap()
{
for (auto iter = mPlaceholderMap.Iter(); !iter.Done(); iter.Next()) {
auto entry = static_cast<PlaceholderMapEntry*>(iter.Get());
entry->placeholderFrame->SetOutOfFlowFrame(nullptr);
}
mPlaceholderMap.Clear();
}
//----------------------------------------------------------------------
/* static */ nsStyleContext*
nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap, nsIContent* aContent)
{
if (!aContent) {
return nullptr;
}
nsIContent* parent = aContent->GetParent();
for (UndisplayedNode* node = aMap->GetFirstNode(parent);
node; node = node->mNext) {
if (node->mContent == aContent)
return node->mStyle;
}
return nullptr;
}
/* static */ UndisplayedNode*
nsFrameManager::GetAllUndisplayedNodesInMapFor(UndisplayedMap* aMap,
nsIContent* aParentContent)
{
return aMap ? aMap->GetFirstNode(aParentContent) : nullptr;
}
UndisplayedNode*
nsFrameManager::GetAllUndisplayedContentIn(nsIContent* aParentContent)
{
return GetAllUndisplayedNodesInMapFor(mUndisplayedMap, aParentContent);
}
/* static */ void
nsFrameManager::SetStyleContextInMap(UndisplayedMap* aMap,
nsIContent* aContent,
nsStyleContext* aStyleContext)
{
NS_PRECONDITION(!aStyleContext->GetPseudo(),
"Should only have actual elements here");
#if defined(DEBUG_UNDISPLAYED_MAP) || defined(DEBUG_DISPLAY_BOX_CONTENTS_MAP)
static int i = 0;
printf("SetStyleContextInMap(%d): p=%p \n", i++, (void *)aContent);
#endif
NS_ASSERTION(!GetStyleContextInMap(aMap, aContent),
"Already have an entry for aContent");
nsIContent* parent = aContent->GetParent();
#ifdef DEBUG
nsIPresShell* shell = aStyleContext->PresContext()->PresShell();
NS_ASSERTION(parent || (shell && shell->GetDocument() &&
shell->GetDocument()->GetRootElement() == aContent),
"undisplayed content must have a parent, unless it's the root "
"element");
#endif
aMap->AddNodeFor(parent, aContent, aStyleContext);
}
void
nsFrameManager::SetUndisplayedContent(nsIContent* aContent,
nsStyleContext* aStyleContext)
{
if (!mUndisplayedMap) {
mUndisplayedMap = new UndisplayedMap;
}
SetStyleContextInMap(mUndisplayedMap, aContent, aStyleContext);
}
/* static */ void
nsFrameManager::ChangeStyleContextInMap(UndisplayedMap* aMap,
nsIContent* aContent,
nsStyleContext* aStyleContext)
{
MOZ_ASSERT(aMap, "expecting a map");
#if defined(DEBUG_UNDISPLAYED_MAP) || defined(DEBUG_DISPLAY_BOX_CONTENTS_MAP)
static int i = 0;
printf("ChangeStyleContextInMap(%d): p=%p \n", i++, (void *)aContent);
#endif
for (UndisplayedNode* node = aMap->GetFirstNode(aContent->GetParent());
node; node = node->mNext) {
if (node->mContent == aContent) {
node->mStyle = aStyleContext;
return;
}
}
MOZ_CRASH("couldn't find the entry to change");
}
void
nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent,
nsIContent* aParentContent)
{
#ifdef DEBUG_UNDISPLAYED_MAP
static int i = 0;
printf("ClearUndisplayedContent(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent);
#endif
if (mUndisplayedMap) {
UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent);
while (node) {
if (node->mContent == aContent) {
mUndisplayedMap->RemoveNodeFor(aParentContent, node);
#ifdef DEBUG_UNDISPLAYED_MAP
printf( "REMOVED!\n");
#endif
#ifdef DEBUG
// make sure that there are no more entries for the same content
nsStyleContext *context = GetUndisplayedContent(aContent);
NS_ASSERTION(context == nullptr, "Found more undisplayed content data after removal");
#endif
return;
}
node = node->mNext;
}
}
#ifdef DEBUG_UNDISPLAYED_MAP
printf( "not found.\n");
#endif
}
void
nsFrameManager::ClearAllUndisplayedContentIn(nsIContent* aParentContent)
{
#ifdef DEBUG_UNDISPLAYED_MAP
static int i = 0;
printf("ClearAllUndisplayedContentIn(%d): parent=%p \n", i++, (void*)aParentContent);
#endif
if (mUndisplayedMap) {
mUndisplayedMap->RemoveNodesFor(aParentContent);
}
// Need to look at aParentContent's content list due to XBL insertions.
// Nodes in aParentContent's content list do not have aParentContent as a
// parent, but are treated as children of aParentContent. We iterate over
// the flattened content list and just ignore any nodes we don't care about.
FlattenedChildIterator iter(aParentContent);
for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
if (child->GetParent() != aParentContent) {
ClearUndisplayedContentIn(child, child->GetParent());
}
}
}
//----------------------------------------------------------------------
void
nsFrameManager::SetDisplayContents(nsIContent* aContent,
nsStyleContext* aStyleContext)
{
if (!mDisplayContentsMap) {
mDisplayContentsMap = new UndisplayedMap;
}
SetStyleContextInMap(mDisplayContentsMap, aContent, aStyleContext);
}
UndisplayedNode*
nsFrameManager::GetAllDisplayContentsIn(nsIContent* aParentContent)
{
return GetAllUndisplayedNodesInMapFor(mDisplayContentsMap, aParentContent);
}
void
nsFrameManager::ClearDisplayContentsIn(nsIContent* aContent,
nsIContent* aParentContent)
{
#ifdef DEBUG_DISPLAY_CONTENTS_MAP
static int i = 0;
printf("ClearDisplayContents(%d): content=%p parent=%p --> ", i++, (void *)aContent, (void*)aParentContent);
#endif
if (mDisplayContentsMap) {
UndisplayedNode* node = mDisplayContentsMap->GetFirstNode(aParentContent);
while (node) {
if (node->mContent == aContent) {
mDisplayContentsMap->RemoveNodeFor(aParentContent, node);
#ifdef DEBUG_DISPLAY_CONTENTS_MAP
printf( "REMOVED!\n");
#endif
#ifdef DEBUG
// make sure that there are no more entries for the same content
nsStyleContext* context = GetDisplayContentsStyleFor(aContent);
NS_ASSERTION(context == nullptr, "Found more entries for aContent after removal");
#endif
ClearAllDisplayContentsIn(aContent);
ClearAllUndisplayedContentIn(aContent);
return;
}
node = node->mNext;
}
}
#ifdef DEBUG_DISPLAY_CONTENTS_MAP
printf( "not found.\n");
#endif
}
void
nsFrameManager::ClearAllDisplayContentsIn(nsIContent* aParentContent)
{
#ifdef DEBUG_DISPLAY_CONTENTS_MAP
static int i = 0;
printf("ClearAllDisplayContentsIn(%d): parent=%p \n", i++, (void*)aParentContent);
#endif
if (mDisplayContentsMap) {
UndisplayedNode* cur = mDisplayContentsMap->UnlinkNodesFor(aParentContent);
while (cur) {
UndisplayedNode* next = cur->mNext;
cur->mNext = nullptr;
ClearAllDisplayContentsIn(cur->mContent);
ClearAllUndisplayedContentIn(cur->mContent);
delete cur;
cur = next;
}
}
// Need to look at aParentContent's content list due to XBL insertions.
// Nodes in aParentContent's content list do not have aParentContent as a
// parent, but are treated as children of aParentContent. We iterate over
// the flattened content list and just ignore any nodes we don't care about.
FlattenedChildIterator iter(aParentContent);
for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
if (child->GetParent() != aParentContent) {
ClearDisplayContentsIn(child, child->GetParent());
ClearUndisplayedContentIn(child, child->GetParent());
}
}
}
//----------------------------------------------------------------------
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)
{
NS_PRECONDITION(!aPrevFrame || (!aPrevFrame->GetNextContinuation()
|| (((aPrevFrame->GetNextContinuation()->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER))
&& !(aPrevFrame->GetStateBits() & 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, aFrameList);
}
}
void
nsFrameManager::RemoveFrame(ChildListID aListID,
nsIFrame* aOldFrame)
{
bool wasDestroyingFrames = mIsDestroyingFrames;
mIsDestroyingFrames = true;
// 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->GetType() == nsGkAtoms::textFrame,
"Must remove first continuation.");
NS_ASSERTION(!(aOldFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW &&
GetPlaceholderFrameFor(aOldFrame)),
"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);
}
mIsDestroyingFrames = wasDestroyingFrames;
}
//----------------------------------------------------------------------
void
nsFrameManager::NotifyDestroyingFrame(nsIFrame* aFrame)
{
nsIContent* content = aFrame->GetContent();
if (content && content->GetPrimaryFrame() == aFrame) {
ClearAllUndisplayedContentIn(content);
ClearAllDisplayContentsIn(content);
}
}
// 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)
nsAutoPtr<nsPresState> frameState;
nsresult rv = statefulFrame->SaveState(getter_Transfers(frameState));
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();
nsIDocument* doc = content ? content->GetCurrentDoc() : nullptr;
rv = nsContentUtils::GenerateStateKey(content, doc, stateKey);
if(NS_FAILED(rv) || stateKey.IsEmpty()) {
return;
}
// Store the state. aState owns frameState now.
aState->AddState(stateKey, frameState.forget());
}
void
nsFrameManager::CaptureFrameState(nsIFrame* aFrame,
nsILayoutHistoryState* aState)
{
NS_PRECONDITION(nullptr != aFrame && nullptr != aState, "null parameters passed in");
CaptureFrameStateFor(aFrame, aState);
// Now capture state recursively for the frame hierarchy rooted at aFrame
nsIFrame::ChildListIterator lists(aFrame);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
nsIFrame* child = childFrames.get();
if (child->GetStateBits() & 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;
nsIDocument* doc = content->GetCurrentDoc();
nsresult rv = nsContentUtils::GenerateStateKey(content, doc, stateKey);
if (NS_FAILED(rv) || stateKey.IsEmpty()) {
return;
}
// Get the state from the hash
nsPresState* frameState = aState->GetState(stateKey);
if (!frameState) {
return;
}
// Restore it
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)
{
NS_PRECONDITION(nullptr != aFrame && nullptr != aState, "null parameters passed in");
RestoreFrameStateFor(aFrame, aState);
// Now restore state recursively for the frame hierarchy rooted at aFrame
nsIFrame::ChildListIterator lists(aFrame);
for (; !lists.IsDone(); lists.Next()) {
nsFrameList::Enumerator childFrames(lists.CurrentList());
for (; !childFrames.AtEnd(); childFrames.Next()) {
RestoreFrameState(childFrames.get(), aState);
}
}
}
//----------------------------------------------------------------------
static PLHashNumber
HashKey(void* key)
{
return NS_PTR_TO_INT32(key);
}
static int
CompareKeys(void* key1, void* key2)
{
return key1 == key2;
}
//----------------------------------------------------------------------
nsFrameManagerBase::UndisplayedMap::UndisplayedMap(uint32_t aNumBuckets)
{
MOZ_COUNT_CTOR(nsFrameManagerBase::UndisplayedMap);
mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey,
(PLHashComparator)CompareKeys,
(PLHashComparator)nullptr,
nullptr, nullptr);
mLastLookup = nullptr;
}
nsFrameManagerBase::UndisplayedMap::~UndisplayedMap(void)
{
MOZ_COUNT_DTOR(nsFrameManagerBase::UndisplayedMap);
Clear();
PL_HashTableDestroy(mTable);
}
PLHashEntry**
nsFrameManagerBase::UndisplayedMap::GetEntryFor(nsIContent** aParentContent)
{
nsIContent* parentContent = *aParentContent;
if (mLastLookup && (parentContent == (*mLastLookup)->key)) {
return mLastLookup;
}
// In the case of XBL default content, <xbl:children> elements do not get a
// frame causing a mismatch between the content tree and the frame tree.
// |GetEntryFor| is sometimes called with the content tree parent (which may
// be a <xbl:children> element) but the parent in the frame tree would be the
// insertion parent (parent of the <xbl:children> element). Here the children
// elements are normalized to the insertion parent to correct for the mismatch.
if (parentContent && nsContentUtils::IsContentInsertionPoint(parentContent)) {
parentContent = parentContent->GetParent();
// Change the caller's pointer for the parent content to be the insertion parent.
*aParentContent = parentContent;
}
PLHashNumber hashCode = NS_PTR_TO_INT32(parentContent);
PLHashEntry** entry = PL_HashTableRawLookup(mTable, hashCode, parentContent);
if (*entry) {
mLastLookup = entry;
}
return entry;
}
UndisplayedNode*
nsFrameManagerBase::UndisplayedMap::GetFirstNode(nsIContent* aParentContent)
{
PLHashEntry** entry = GetEntryFor(&aParentContent);
if (*entry) {
return (UndisplayedNode*)((*entry)->value);
}
return nullptr;
}
void
nsFrameManagerBase::UndisplayedMap::AppendNodeFor(UndisplayedNode* aNode,
nsIContent* aParentContent)
{
PLHashEntry** entry = GetEntryFor(&aParentContent);
if (*entry) {
UndisplayedNode* node = (UndisplayedNode*)((*entry)->value);
while (node->mNext) {
if (node->mContent == aNode->mContent) {
// We actually need to check this in optimized builds because
// there are some callers that do this. See bug 118014, bug
// 136704, etc.
NS_NOTREACHED("node in map twice");
delete aNode;
return;
}
node = node->mNext;
}
node->mNext = aNode;
}
else {
PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent);
PL_HashTableRawAdd(mTable, entry, hashCode, aParentContent, aNode);
mLastLookup = nullptr; // hashtable may have shifted bucket out from under us
}
}
nsresult
nsFrameManagerBase::UndisplayedMap::AddNodeFor(nsIContent* aParentContent,
nsIContent* aChild,
nsStyleContext* aStyle)
{
UndisplayedNode* node = new UndisplayedNode(aChild, aStyle);
AppendNodeFor(node, aParentContent);
return NS_OK;
}
void
nsFrameManagerBase::UndisplayedMap::RemoveNodeFor(nsIContent* aParentContent,
UndisplayedNode* aNode)
{
PLHashEntry** entry = GetEntryFor(&aParentContent);
NS_ASSERTION(*entry, "content not in map");
if (*entry) {
if ((UndisplayedNode*)((*entry)->value) == aNode) { // first node
if (aNode->mNext) {
(*entry)->value = aNode->mNext;
aNode->mNext = nullptr;
}
else {
PL_HashTableRawRemove(mTable, entry, *entry);
mLastLookup = nullptr; // hashtable may have shifted bucket out from under us
}
}
else {
UndisplayedNode* node = (UndisplayedNode*)((*entry)->value);
while (node->mNext) {
if (node->mNext == aNode) {
node->mNext = aNode->mNext;
aNode->mNext = nullptr;
break;
}
node = node->mNext;
}
}
}
delete aNode;
}
UndisplayedNode*
nsFrameManagerBase::UndisplayedMap::UnlinkNodesFor(nsIContent* aParentContent)
{
PLHashEntry** entry = GetEntryFor(&aParentContent);
NS_ASSERTION(entry, "content not in map");
if (*entry) {
UndisplayedNode* node = (UndisplayedNode*)((*entry)->value);
NS_ASSERTION(node, "null node for non-null entry in UndisplayedMap");
PL_HashTableRawRemove(mTable, entry, *entry);
mLastLookup = nullptr; // hashtable may have shifted bucket out from under us
return node;
}
return nullptr;
}
void
nsFrameManagerBase::UndisplayedMap::RemoveNodesFor(nsIContent* aParentContent)
{
delete UnlinkNodesFor(aParentContent);
}
static int
RemoveUndisplayedEntry(PLHashEntry* he, int i, void* arg)
{
UndisplayedNode* node = (UndisplayedNode*)(he->value);
delete node;
// Remove and free this entry and continue enumerating
return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT;
}
void
nsFrameManagerBase::UndisplayedMap::Clear(void)
{
mLastLookup = nullptr;
PL_HashTableEnumerateEntries(mTable, RemoveUndisplayedEntry, 0);
}
uint32_t nsFrameManagerBase::sGlobalGenerationNumber;