mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
d64e96b4a0
- Bug 448064 - Add crashtest. (d7e9a3f9bb)
- Minor cleanup (cbab3e1ede)
- Bug 1222829: Remove URIChecker. r=mcmanus (74c16ae58c)
- Bug 1220682 - Clear exceptions on single-arg init. r=bz # Please enter the commit message for your changes. Lines starting (4736e0fb57)
- Bug 1229237 (part 1) - Make nsIWidget::{Create,CreateChildren}() take a LayoutDeviceIntRect. r=botond. (992ec123ff)
- Bug 1229237 (part 2) - Make nsIWidget::DrawWindowUnderlay() take a LayoutDeviceIntRect. r=botond. (ad4f13440d)
- Bug 1229237 (part 3) - Make nsIWidget::Invalidate() take a LayoutDeviceIntRect. r=botond. (19f10846a3)
- Bug 1229237 (part 4) - Make ThemeGeometry::mRect a LayoutDeviceIntRect. r=botond. (5f17f6be14)
- Bug 1228125 (part 1) - Remove nsIWidget::GetBoundsUntyped(). r=botond. (5e4d0811e7)
- Bug 1228125 (part 2) - Remove nsIWidget::GetScreenBoundsUntyped(). r=botond. (3ac4b3271b)
- Bug 1228125 (part 3) - Remove nsIWidget::GetClientBoundsUntyped(). r=botond. (7163a1a6e6)
- Bug 1229237 (part 5) - Make Update{Opaque,WindowDragging}Region() take a LayoutDeviceIntRegion. r=botond. (907aafd4c0)
- missing bit Bug 1229237 (part 1) (c160f227b3)
- missing bit Bug 1229237 (part 1) (edc4237028)
- Bug 1215959 - (GeckoCaret2) update experimental prefs, r=snorp (df3a19e142)
- Bug 1168881 - Enabling AccessibleCarets crashes Firefox when trying to input text, r=tylin (abbd401672)
- Bug 1215959 - (GeckoCaret2) Remove experimental bits added to Touch/Selection carets, r=tylin (291385ad0e)
- Bug 1219236 - Part 1: Flush throttle styles for all descendant sub documents and *root* document itself. r=roc (b6c4d4c815)
- Bug 1153130 - Prevent FireFox crashes when pointer events are enabled. r=smaug (a7093fae15)
- Bug 1226041 - Remove mFirstPaint hack for JPZC when the C++ APZ is enabled. r=kats (6af6102f1f)
- Bug 1223228 - Remove unused nsFrameManagerBase::mStyleSet. r=dholbert (9145f9cb38)
- Bug 1189353 - Change NS_ macro prefix to MOZ_ in AccessibleCaretEventHub. r=mtseng (820920546e)
- Bug 1210315 - Use preference to control whether to use long tap injector. r=roc (00ee7e8874)
- Bug 1206545 - Store nsIPresShell pointer in constructor instead of Init(). r=roc (6a9f8216f7)
- Bug 1194063 - Update link to point to the diagram directly. r=mtseng (3d627f37da)
- Bug 1225701 - Update comments in AccessibleCaret files. r=mtseng (fe1d296655)
- Bug 1211365 - Remove NS_IMPL_STATE_UTILITIES. r=mtseng (79713764b9)
- Bug 1211365 - Make deleted functions in class State public. r=mtseng (da2df347b3)
- Bug 1211365 - Delete wheel event handling. r=mtseng (7bacb7e8f5)
- Bug 1200194 - Tweak formatting and debug logging. r=roc (148a22ce3c)
- Bug 1082425 - Commit composition string before changing focus by long tap. r=masayuki (1452e46511)
- Bug 1196176 - No need to test mLastUpdateCaretMode. r=mtseng (fc93c83698)
- Bug 1196176 - Hide carets for mouse down reason. r=mtseng (3c13762b86)
- Bug 1209841 - Merge test_selectioncarets2.py into test_selectioncarets.py. r=automatedtester (35575914b9)
- Bug 1209841 - Simplify functions related to select word. r=automatedtester (56bf6052e1)
- Bug 1209841 - Send synthesized mouse long tap to gecko. r=automatedtester (e83509d49b)
- Bug 1210315 - Inline selectors in carets tests. r=automatedtester (cebad9b688)
- Bug 1163490 - Remove SpecialPowers from marionette carets test. r=jgriffin (fd1e4b6442)
- Bug 1210315 - Use pref utilities in test_selectioncarets.py. r=automatedtester (09efee2a80)
- Bug 1207934 - Add marionette test for appearance changing. r=automatedtester (6e1c5aea60)
- Bug 1210315 - Use pref utilities in test_touchcaret.py. r=automatedtester (a17a92808e)
- Bug 1200364 - Fix AccessibleCaret jumps when dragging. r=roc (66c36347f7)
- Bug 1203074: Move caret to end of textual input field before sending keys (1a8cf55bd7)
- Bug 1164233 - Enlarge the timeout margin from 1.5x to 3x. r=mtseng (188b17550d)
- Bug 1196176 - Fix CaretStateChanged not dispatch on empty content. r=mtseng (5d6930c1e1)
- Bug 1196176 - Do not fire extra CaretStateChanged event when typing. r=mtseng (93f4ccdc97)
- Bug 1211365 - Add test case for scrolling in selection mode. r=mtseng (bfd69f4337)
- Bug 1212732 - Update caret in cursor mode on scroll-end if it was logically visible. r=roc (07ddba2c15)
- Bug 1215959 - (GeckoCaret2) Upgrade Core and AccessibleCaret, r=smaug (fe388a4b37)
- Bug 1216857 - Notify selection dialog to update its position if carets are logically visible. r=TYLin (e759cbe5cd)
- Bug 1215798 nsContentIterator::Init(nsIDOMRange*) should not skip empty start node when mPre is true r=smaug (013857d99d)
- Bug 1215816 nsContentIterator::Init(nsIDOMRange*) should not include end node if it's an empty element and the end offset is 0 when mPre is true r=smaug (5276c84afb)
839 lines
26 KiB
C++
839 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(PLDHashTable *table, 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;
|