pjs/layout/base/nsFrameManager.cpp

445 строки
12 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsIFrameManager.h"
#include "nsIFrame.h"
#include "nsIPresContext.h"
#include "nsIPresShell.h"
#include "nsIStyleSet.h"
#include "nsCOMPtr.h"
#include "plhash.h"
#include "nsDST.h"
#include "nsPlaceholderFrame.h"
#ifdef NS_DEBUG
#include "nsLayoutAtoms.h"
#endif
//----------------------------------------------------------------------
static PLHashNumber
HashKey(void* key)
{
return (PLHashNumber)key;
}
static PRIntn
CompareKeys(void* key1, void* key2)
{
return key1 == key2;
}
// Thin veneer around an NSPR hash table. Provides a map from a key that's
// a pointer to a value that's also a pointer.
class FrameHashTable {
public:
FrameHashTable(PRUint32 aNumBuckets = 16);
~FrameHashTable();
// Gets the value associated with the specified key
void* Get(void* aKey);
// Creates an association between the key and value. Returns the previous
// value associated with the key, or NULL if there was none
void* Put(void* aKey, void* aValue);
// Removes association for the key, and returns its associated value
void* Remove(void* aKey);
// Removes all entries from the hash table
void Clear();
#ifdef NS_DEBUG
void Dump(FILE* fp);
#endif
protected:
PLHashTable* mTable;
};
FrameHashTable::FrameHashTable(PRUint32 aNumBuckets)
{
mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey,
(PLHashComparator)CompareKeys,
(PLHashComparator)nsnull,
nsnull, nsnull);
}
FrameHashTable::~FrameHashTable()
{
PL_HashTableDestroy(mTable);
}
void*
FrameHashTable::Get(void* aKey)
{
PRInt32 hashCode = (PRInt32) aKey;
PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey);
PLHashEntry* he = *hep;
if (nsnull != he) {
return he->value;
}
return nsnull;
}
void*
FrameHashTable::Put(void* aKey, void* aData)
{
PRInt32 hashCode = (PRInt32) aKey;
PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey);
PLHashEntry* he = *hep;
if (nsnull != he) {
void* oldValue = he->value;
he->value = aData;
return oldValue;
}
PL_HashTableRawAdd(mTable, hep, hashCode, aKey, aData);
return nsnull;
}
void*
FrameHashTable::Remove(void* aKey)
{
PRInt32 hashCode = (PRInt32) aKey;
PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey);
PLHashEntry* he = *hep;
void* oldValue = nsnull;
if (nsnull != he) {
oldValue = he->value;
PL_HashTableRawRemove(mTable, hep, he);
}
return oldValue;
}
static PRIntn
RemoveEntry(PLHashEntry* he, PRIntn i, void* arg)
{
// Remove and free this entry and continue enumerating
return HT_ENUMERATE_REMOVE | HT_ENUMERATE_NEXT;
}
void
FrameHashTable::Clear()
{
PL_HashTableEnumerateEntries(mTable, RemoveEntry, 0);
}
#ifdef NS_DEBUG
static PRIntn
EnumEntries(PLHashEntry* he, PRIntn i, void* arg)
{
// Continue enumerating
return HT_ENUMERATE_NEXT;
}
void
FrameHashTable::Dump(FILE* fp)
{
PL_HashTableDump(mTable, EnumEntries, fp);
}
#endif
//----------------------------------------------------------------------
class FrameManager : public nsIFrameManager
{
public:
FrameManager();
virtual ~FrameManager();
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
// nsISupports
NS_DECL_ISUPPORTS
// nsIFrameManager
NS_IMETHOD Init(nsIPresShell* aPresShell);
// Primary frame functions
NS_IMETHOD GetPrimaryFrameFor(nsIContent* aContent,
nsIFrame** aPrimaryFrame) const;
NS_IMETHOD SetPrimaryFrameFor(nsIContent* aContent,
nsIFrame* aPrimaryFrame);
NS_IMETHOD ClearPrimaryFrameMap();
// Placeholder frame functions
NS_IMETHOD GetPlaceholderFrameFor(nsIFrame* aFrame,
nsIFrame** aPlaceholderFrame) const;
NS_IMETHOD SetPlaceholderFrameFor(nsIFrame* aFrame,
nsIFrame* aPlaceholderFrame);
NS_IMETHOD ClearPlaceholderFrameMap();
// Functions for manipulating the frame model
NS_IMETHOD AppendFrames(nsIPresContext& aPresContext,
nsIPresShell& aPresShell,
nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aFrameList);
NS_IMETHOD InsertFrames(nsIPresContext& aPresContext,
nsIPresShell& aPresShell,
nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aPrevFrame,
nsIFrame* aFrameList);
NS_IMETHOD RemoveFrame(nsIPresContext& aPresContext,
nsIPresShell& aPresShell,
nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aOldFrame);
NS_IMETHOD ReplaceFrame(nsIPresContext& aPresContext,
nsIPresShell& aPresShell,
nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aOldFrame,
nsIFrame* aNewFrame);
private:
nsIPresShell* mPresShell; // weak link
nsDST* mPrimaryFrameMap;
FrameHashTable* mPlaceholderMap;
};
//----------------------------------------------------------------------
NS_LAYOUT nsresult
NS_NewFrameManager(nsIFrameManager** aInstancePtrResult)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
if (nsnull == aInstancePtrResult) {
return NS_ERROR_NULL_POINTER;
}
FrameManager* it = new FrameManager;
if (nsnull == it) {
return NS_ERROR_OUT_OF_MEMORY;
}
return it->QueryInterface(nsIFrameManager::GetIID(), (void **)aInstancePtrResult);
}
FrameManager::FrameManager()
{
}
NS_IMPL_ADDREF(FrameManager)
NS_IMPL_RELEASE(FrameManager)
FrameManager::~FrameManager()
{
delete mPrimaryFrameMap;
delete mPlaceholderMap;
}
nsresult
FrameManager::QueryInterface(const nsIID& aIID, void** aInstancePtr)
{
if (aIID.Equals(GetIID())) {
*aInstancePtr = (void*)(nsIFrameManager*)this;
NS_ADDREF_THIS();
return NS_OK;
}
return NS_NOINTERFACE;
}
NS_IMETHODIMP
FrameManager::Init(nsIPresShell* aPresShell)
{
mPresShell = aPresShell;
return NS_OK;
}
// Primary frame functions
NS_IMETHODIMP
FrameManager::GetPrimaryFrameFor(nsIContent* aContent,
nsIFrame** aResult) const
{
NS_PRECONDITION(nsnull != aResult, "null ptr");
NS_PRECONDITION(nsnull != aContent, "no content object");
if (nsnull == aResult) {
return NS_ERROR_NULL_POINTER;
}
if (nsnull == mPrimaryFrameMap) {
*aResult = nsnull;
} else {
*aResult = (nsIFrame*)mPrimaryFrameMap->Search(aContent);
if (!*aResult) {
nsCOMPtr<nsIStyleSet> styleSet;
nsCOMPtr<nsIPresContext> presContext;
// Give the frame construction code the opportunity to return the
// frame that maps the content object
mPresShell->GetStyleSet(getter_AddRefs(styleSet));
mPresShell->GetPresContext(getter_AddRefs(presContext));
styleSet->FindPrimaryFrameFor(presContext, aContent, aResult);
}
}
return NS_OK;
}
NS_IMETHODIMP
FrameManager::SetPrimaryFrameFor(nsIContent* aContent,
nsIFrame* aPrimaryFrame)
{
NS_PRECONDITION(aContent, "no content object");
// If aPrimaryFrame is NULL, then remove the mapping
if (!aPrimaryFrame) {
if (mPrimaryFrameMap) {
mPrimaryFrameMap->Remove(aContent);
}
} else {
// Create a new DST if necessary
if (!mPrimaryFrameMap) {
mPrimaryFrameMap = new nsDST;
if (!mPrimaryFrameMap) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
// Add a mapping to the hash table
nsIFrame* oldPrimaryFrame;
oldPrimaryFrame = (nsIFrame*)mPrimaryFrameMap->Insert(aContent, (void*)aPrimaryFrame);
#ifdef NS_DEBUG
if (oldPrimaryFrame && (oldPrimaryFrame != aPrimaryFrame)) {
NS_WARNING("overwriting current primary frame");
}
#endif
}
return NS_OK;
}
NS_IMETHODIMP
FrameManager::ClearPrimaryFrameMap()
{
if (mPrimaryFrameMap) {
mPrimaryFrameMap->Clear();
}
return NS_OK;
}
// Placeholder frame functions
NS_IMETHODIMP
FrameManager::GetPlaceholderFrameFor(nsIFrame* aFrame,
nsIFrame** aResult) const
{
NS_PRECONDITION(nsnull != aResult, "null ptr");
NS_PRECONDITION(nsnull != aFrame, "no frame");
if ((nsnull == aResult) || (nsnull == aFrame)) {
return NS_ERROR_NULL_POINTER;
}
if (nsnull == mPlaceholderMap) {
*aResult = nsnull;
} else {
*aResult = (nsIFrame*)mPlaceholderMap->Get(aFrame);
}
return NS_OK;
}
NS_IMETHODIMP
FrameManager::SetPlaceholderFrameFor(nsIFrame* aFrame,
nsIFrame* aPlaceholderFrame)
{
NS_PRECONDITION(aFrame, "no frame");
#ifdef NS_DEBUG
// Verify that the placeholder frame is of the correct type
if (aPlaceholderFrame) {
nsIAtom* frameType;
aPlaceholderFrame->GetFrameType(&frameType);
NS_PRECONDITION(nsLayoutAtoms::placeholderFrame == frameType, "unexpected frame type");
NS_IF_RELEASE(frameType);
}
#endif
// If aPlaceholderFrame is NULL, then remove the mapping
if (!aPlaceholderFrame) {
if (mPlaceholderMap) {
mPlaceholderMap->Remove(aFrame);
}
} else {
// Create a new hash table if necessary
if (!mPlaceholderMap) {
mPlaceholderMap = new FrameHashTable;
if (!mPlaceholderMap) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
// Add a mapping to the hash table
mPlaceholderMap->Put(aFrame, (void*)aPlaceholderFrame);
}
return NS_OK;
}
NS_IMETHODIMP
FrameManager::ClearPlaceholderFrameMap()
{
if (mPlaceholderMap) {
mPlaceholderMap->Clear();
}
return NS_OK;
}
NS_IMETHODIMP
FrameManager::AppendFrames(nsIPresContext& aPresContext,
nsIPresShell& aPresShell,
nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aFrameList)
{
return aParentFrame->AppendFrames(aPresContext, aPresShell, aListName,
aFrameList);
}
NS_IMETHODIMP
FrameManager::InsertFrames(nsIPresContext& aPresContext,
nsIPresShell& aPresShell,
nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aPrevFrame,
nsIFrame* aFrameList)
{
return aParentFrame->InsertFrames(aPresContext, aPresShell, aListName,
aPrevFrame, aFrameList);
}
NS_IMETHODIMP
FrameManager::RemoveFrame(nsIPresContext& aPresContext,
nsIPresShell& aPresShell,
nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aOldFrame)
{
return aParentFrame->RemoveFrame(aPresContext, aPresShell, aListName,
aOldFrame);
}
NS_IMETHODIMP
FrameManager::ReplaceFrame(nsIPresContext& aPresContext,
nsIPresShell& aPresShell,
nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aOldFrame,
nsIFrame* aNewFrame)
{
return aParentFrame->ReplaceFrame(aPresContext, aPresShell, aListName,
aOldFrame, aNewFrame);
}