Mozilla/mozilla/layout/base/nsFrameManager.cpp
bzbarsky%mit.edu 38a6e197cd Make SVG use the normal attribute mapping codepath (via HasAttributeDependence)
by pushing the relevant helpers up to nsGenericElement..  Remove referrer code
in nsSVGDocument, since nsDocument handles that.  Bug 233370,
r=alex@croczilla.com, sr=jst


git-svn-id: svn://10.0.0.236/trunk@152562 18797224-902f-48f8-a5cc-f745e15eee43
2004-02-09 20:48:20 +00:00

2417 lines
80 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:cindent:ts=2:et:sw=2:
*
* The contents of this file are subject to the Netscape Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
*
* 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.
*/
#include "nscore.h"
#include "nsIFrameManager.h"
#include "nsIFrame.h"
#include "nsIPresContext.h"
#include "nsIPresShell.h"
#include "nsStyleSet.h"
#include "nsCSSFrameConstructor.h"
#include "nsStyleContext.h"
#include "nsStyleChangeList.h"
#include "nsIEventQueueService.h"
#include "nsIServiceManager.h"
#include "nsCOMPtr.h"
#include "prthread.h"
#include "plhash.h"
#include "pldhash.h"
#include "nsPlaceholderFrame.h"
#include "nsLayoutAtoms.h"
#include "nsCSSAnonBoxes.h"
#include "nsCSSPseudoElements.h"
#include "nsHTMLAtoms.h"
#ifdef NS_DEBUG
#include "nsISupportsArray.h"
#include "nsIStyleRule.h"
#endif
#include "nsILayoutHistoryState.h"
#include "nsIStatefulFrame.h"
#include "nsIPresState.h"
#include "nsIContent.h"
#include "nsINameSpaceManager.h"
#include "nsIXBLBinding.h"
#include "nsIDocument.h"
#include "nsIBindingManager.h"
#include "nsIScrollableFrame.h"
#include "nsIHTMLDocument.h"
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMHTMLCollection.h"
#include "nsIFormControl.h"
#include "nsIDOMElement.h"
#include "nsIDOMHTMLFormElement.h"
#include "nsIForm.h"
#include "nsContentUtils.h"
#include "nsReadableUtils.h"
#include "nsUnicharUtils.h"
#include "nsPrintfCString.h"
#include "nsDummyLayoutRequest.h"
#include "nsLayoutErrors.h"
#include "nsLayoutUtils.h"
#include "nsAutoPtr.h"
#ifdef DEBUG
//#define NOISY_DEBUG
//#define DEBUG_UNDISPLAYED_MAP
#else
#undef NOISY_DEBUG
#undef DEBUG_UNDISPLAYED_MAP
#endif
#ifdef NOISY_DEBUG
#define NOISY_TRACE(_msg) \
printf("%s",_msg);
#define NOISY_TRACE_FRAME(_msg,_frame) \
printf("%s ",_msg); nsFrame::ListTag(stdout,_frame); printf("\n");
#else
#define NOISY_TRACE(_msg);
#define NOISY_TRACE_FRAME(_msg,_frame);
#endif
// Class IID's
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
// IID's
//----------------------------------------------------------------------
struct PlaceholderMapEntry : public PLDHashEntryHdr {
// key (the out of flow frame) can be obtained through placeholder frame
nsPlaceholderFrame *placeholderFrame;
};
PR_STATIC_CALLBACK(const void *)
PlaceholderMapGetKey(PLDHashTable *table, PLDHashEntryHdr *hdr)
{
PlaceholderMapEntry *entry = NS_STATIC_CAST(PlaceholderMapEntry*, hdr);
return entry->placeholderFrame->GetOutOfFlowFrame();
}
PR_STATIC_CALLBACK(PRBool)
PlaceholderMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
const void *key)
{
const PlaceholderMapEntry *entry =
NS_STATIC_CAST(const PlaceholderMapEntry*, hdr);
return entry->placeholderFrame->GetOutOfFlowFrame() == key;
}
static PLDHashTableOps PlaceholderMapOps = {
PL_DHashAllocTable,
PL_DHashFreeTable,
PlaceholderMapGetKey,
PL_DHashVoidPtrKeyStub,
PlaceholderMapMatchEntry,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
PL_DHashFinalizeStub,
NULL
};
//----------------------------------------------------------------------
struct PropertyListMapEntry : public PLDHashEntryHdr {
nsIFrame *key;
void *value;
};
//----------------------------------------------------------------------
struct PrimaryFrameMapEntry : public PLDHashEntryHdr {
// key (the content node) can almost always be obtained through the
// frame. If it weren't for the way image maps (mis)used the primary
// frame map, we'd be able to have a 2 word entry instead of a 3 word
// entry.
nsIContent *content;
nsIFrame *frame;
};
// These ops should be used if/when we switch back to a 2-word entry.
// See comment in |PrimaryFrameMapEntry| above.
#if 0
PR_STATIC_CALLBACK(const void *)
PrimaryFrameMapGetKey(PLDHashTable *table, PLDHashEntryHdr *hdr)
{
PrimaryFrameMapEntry *entry = NS_STATIC_CAST(PrimaryFrameMapEntry*, hdr);
return entry->frame->GetContent();
}
PR_STATIC_CALLBACK(PRBool)
PrimaryFrameMapMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *hdr,
const void *key)
{
const PrimaryFrameMapEntry *entry =
NS_STATIC_CAST(const PrimaryFrameMapEntry*, hdr);
return entry->frame->GetContent() == key;
}
static PLDHashTableOps PrimaryFrameMapOps = {
PL_DHashAllocTable,
PL_DHashFreeTable,
PrimaryFrameMapGetKey,
PL_DHashVoidPtrKeyStub,
PrimaryFrameMapMatchEntry,
PL_DHashMoveEntryStub,
PL_DHashClearEntryStub,
PL_DHashFinalizeStub,
NULL
};
#endif /* 0 */
//----------------------------------------------------------------------
// XXXldb This seems too complicated for what I think it's doing, and it
// should also be using pldhash rather than plhash to use less memory.
MOZ_DECL_CTOR_COUNTER(UndisplayedNode)
class UndisplayedNode {
public:
UndisplayedNode(nsIContent* aContent, nsStyleContext* aStyle)
: mContent(aContent),
mStyle(aStyle),
mNext(nsnull)
{
MOZ_COUNT_CTOR(UndisplayedNode);
}
~UndisplayedNode()
{
MOZ_COUNT_DTOR(UndisplayedNode);
delete mNext;
}
nsCOMPtr<nsIContent> mContent;
nsRefPtr<nsStyleContext> mStyle;
UndisplayedNode* mNext;
};
class UndisplayedMap {
public:
UndisplayedMap(PRUint32 aNumBuckets = 16);
~UndisplayedMap(void);
UndisplayedNode* GetFirstNode(nsIContent* aParentContent);
nsresult AddNodeFor(nsIContent* aParentContent, nsIContent* aChild, nsStyleContext* aStyle);
nsresult RemoveNodeFor(nsIContent* aParentContent, UndisplayedNode* aNode);
nsresult RemoveNodesFor(nsIContent* aParentContent);
// Removes all entries from the hash table
void Clear(void);
protected:
PLHashEntry** GetEntryFor(nsIContent* aParentContent);
nsresult AppendNodeFor(UndisplayedNode* aNode, nsIContent* aParentContent);
PLHashTable* mTable;
PLHashEntry** mLastLookup;
};
//----------------------------------------------------------------------
class FrameManager;
// A CantRenderReplacedElementEvent has a weak pointer to the frame
// manager, and the frame manager has a weak pointer to the event.
// The event queue owns the event and the FrameManager will delete
// the event if it's going to go away.
struct CantRenderReplacedElementEvent : public PLEvent {
CantRenderReplacedElementEvent(FrameManager* aFrameManager, nsIFrame* aFrame, nsIPresShell* aPresShell);
~CantRenderReplacedElementEvent();
// XXXldb Should the pres shell maintain a reference count on a single
// dummy layout request instead of doing creation of a separate one
// here (and per-event!)?
nsresult AddLoadGroupRequest(nsIPresShell* aPresShell);
nsresult RemoveLoadGroupRequest();
nsIFrame* mFrame; // the frame that can't be rendered
CantRenderReplacedElementEvent* mNext; // next event in the list
nsCOMPtr<nsIRequest> mDummyLayoutRequest; // load group request
nsWeakPtr mPresShell; // for removing load group request later
};
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, nsStyleSet* aStyleSet);
NS_IMETHOD Destroy();
// Gets and sets the root frame
NS_IMETHOD GetRootFrame(nsIFrame** aRootFrame) const;
NS_IMETHOD SetRootFrame(nsIFrame* aRootFrame);
// Get the canvas frame: searches from the Root frame down, may be null
NS_IMETHOD GetCanvasFrame(nsIFrame** aCanvasFrame) const;
// Primary frame functions
NS_IMETHOD GetPrimaryFrameFor(nsIContent* aContent,
nsIFrame** aPrimaryFrame);
NS_IMETHOD SetPrimaryFrameFor(nsIContent* aContent,
nsIFrame* aPrimaryFrame);
NS_IMETHOD ClearPrimaryFrameMap();
// Placeholder frame functions
NS_IMETHOD GetPlaceholderFrameFor(nsIFrame* aFrame,
nsIFrame** aPlaceholderFrame) const;
NS_IMETHOD RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame);
NS_IMETHOD UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame);
NS_IMETHOD ClearPlaceholderFrameMap();
// Undisplayed content functions
virtual nsStyleContext* GetUndisplayedContent(nsIContent* aContent);
virtual void SetUndisplayedContent(nsIContent* aContent,
nsStyleContext* aStyleContext);
virtual void ChangeUndisplayedContent(nsIContent* aContent,
nsStyleContext* aStyleContext);
NS_IMETHOD ClearUndisplayedContentIn(nsIContent* aContent,
nsIContent* aParentContent);
NS_IMETHOD ClearAllUndisplayedContentIn(nsIContent* aParentContent);
NS_IMETHOD ClearUndisplayedContentMap();
// Functions for manipulating the frame model
NS_IMETHOD AppendFrames(nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aFrameList);
NS_IMETHOD InsertFrames(nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aPrevFrame,
nsIFrame* aFrameList);
NS_IMETHOD RemoveFrame(nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aOldFrame);
NS_IMETHOD ReplaceFrame(nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aOldFrame,
nsIFrame* aNewFrame);
NS_IMETHOD CantRenderReplacedElement(nsIFrame* aFrame);
NS_IMETHOD NotifyDestroyingFrame(nsIFrame* aFrame);
NS_IMETHOD ReParentStyleContext(nsIFrame* aFrame,
nsStyleContext* aNewParentContext);
NS_IMETHOD ComputeStyleChangeFor(nsIFrame* aFrame,
nsStyleChangeList& aChangeList,
nsChangeHint aMinChange,
nsChangeHint& aTopLevelChange);
NS_IMETHOD HasAttributeDependentStyle(nsIContent *aContent,
nsIAtom *aAttribute,
PRInt32 aModType,
nsReStyleHint *aResult);
// Capture state from the entire frame heirarchy and store in aState
NS_IMETHOD CaptureFrameState(nsIFrame* aFrame,
nsILayoutHistoryState* aState);
NS_IMETHOD RestoreFrameState(nsIFrame* aFrame,
nsILayoutHistoryState* aState);
// Add/restore state for one frame (special, global type, like scroll position)
NS_IMETHOD CaptureFrameStateFor(nsIFrame* aFrame,
nsILayoutHistoryState* aState,
nsIStatefulFrame::SpecialStateID aID = nsIStatefulFrame::eNoID);
NS_IMETHOD RestoreFrameStateFor(nsIFrame* aFrame,
nsILayoutHistoryState* aState,
nsIStatefulFrame::SpecialStateID aID = nsIStatefulFrame::eNoID);
// Gets and sets properties on a given frame
NS_IMETHOD GetFrameProperty(nsIFrame* aFrame,
nsIAtom* aPropertyName,
PRUint32 aOptions,
void** aPropertyValue);
NS_IMETHOD SetFrameProperty(nsIFrame* aFrame,
nsIAtom* aPropertyName,
void* aPropertyValue,
NSFramePropertyDtorFunc aPropDtorFunc);
NS_IMETHOD RemoveFrameProperty(nsIFrame* aFrame,
nsIAtom* aPropertyName);
#ifdef NS_DEBUG
NS_IMETHOD DebugVerifyStyleTree(nsIFrame* aFrame);
#endif
struct PropertyList {
nsCOMPtr<nsIAtom> mName; // property name
PLDHashTable mFrameValueMap; // map of frame/value pairs
NSFramePropertyDtorFunc mDtorFunc; // property specific value dtor function
PropertyList* mNext;
PropertyList(nsIAtom* aName,
NSFramePropertyDtorFunc aDtorFunc);
~PropertyList();
// Removes the property associated with the given frame, and destroys
// the property value
PRBool RemovePropertyForFrame(nsIPresContext* aPresContext, nsIFrame* aFrame);
// Destroy all remaining properties (without removing them)
void Destroy(nsIPresContext* aPresContext);
};
private:
nsIPresShell* GetPresShell() const { return mPresShell; }
nsIPresContext* GetPresContext() const {
return mPresShell->GetPresContext();
}
nsIPresShell* mPresShell; // weak link, because the pres shell owns us
nsStyleSet* mStyleSet; // weak link. pres shell holds a reference
nsIFrame* mRootFrame;
PLDHashTable mPrimaryFrameMap;
PLDHashTable mPlaceholderMap;
UndisplayedMap* mUndisplayedMap;
CantRenderReplacedElementEvent* mPostedEvents;
PropertyList* mPropertyList;
PRBool mIsDestroyingFrames;
void ReResolveStyleContext(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIContent* aParentContent,
nsStyleChangeList& aChangeList,
nsChangeHint aMinChange,
nsChangeHint& aResultChange);
nsresult RevokePostedEvents();
CantRenderReplacedElementEvent** FindPostedEventFor(nsIFrame* aFrame);
void DequeuePostedEventFor(nsIFrame* aFrame);
void DestroyPropertyList(nsIPresContext* aPresContext);
PropertyList* GetPropertyListFor(nsIAtom* aPropertyName) const;
void RemoveAllPropertiesFor(nsIPresContext* aPresContext, nsIFrame* aFrame);
friend struct CantRenderReplacedElementEvent;
static void HandlePLEvent(CantRenderReplacedElementEvent* aEvent);
static void DestroyPLEvent(CantRenderReplacedElementEvent* aEvent);
};
//----------------------------------------------------------------------
nsresult
NS_NewFrameManager(nsIFrameManager** aInstancePtrResult)
{
NS_ENSURE_ARG_POINTER(aInstancePtrResult);
if (!aInstancePtrResult) {
return NS_ERROR_NULL_POINTER;
}
FrameManager* it = new FrameManager;
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
return it->QueryInterface(NS_GET_IID(nsIFrameManager), (void **)aInstancePtrResult);
}
FrameManager::FrameManager()
{
}
FrameManager::~FrameManager()
{
NS_ASSERTION(!mPresShell, "FrameManager::Destroy never called");
}
NS_IMPL_ISUPPORTS1(FrameManager, nsIFrameManager)
NS_IMETHODIMP
FrameManager::Init(nsIPresShell* aPresShell,
nsStyleSet* aStyleSet)
{
NS_ASSERTION(aPresShell, "null aPresShell");
NS_ASSERTION(aStyleSet, "null aStyleSet");
mPresShell = aPresShell;
mStyleSet = aStyleSet;
return NS_OK;
}
NS_IMETHODIMP
FrameManager::Destroy()
{
NS_ASSERTION(mPresShell, "Frame manager already shut down.");
nsCOMPtr<nsIPresContext> presContext;
mPresShell->GetPresContext(getter_AddRefs(presContext));
// Destroy the frame hierarchy. Don't destroy the property lists until after
// we've destroyed the frame hierarchy because some frames may expect to be
// able to retrieve their properties during destruction
mPresShell->SetIgnoreFrameDestruction(PR_TRUE);
mIsDestroyingFrames = PR_TRUE; // This flag prevents GetPrimaryFrameFor from returning pointers to destroyed frames
if (mRootFrame) {
mRootFrame->Destroy(presContext);
mRootFrame = nsnull;
}
if (mPrimaryFrameMap.ops) {
PL_DHashTableFinish(&mPrimaryFrameMap);
mPrimaryFrameMap.ops = nsnull;
}
if (mPlaceholderMap.ops) {
PL_DHashTableFinish(&mPlaceholderMap);
mPlaceholderMap.ops = nsnull;
}
delete mUndisplayedMap;
DestroyPropertyList(presContext);
// If we're not going to be used anymore, we should revoke the
// pending |CantRenderReplacedElementEvent|s being sent to us.
nsresult rv = RevokePostedEvents();
NS_ASSERTION(NS_SUCCEEDED(rv), "RevokePostedEvents failed: might crash");
mPresShell = nsnull; // mPresShell isn't valid anymore. We
// won't use it, either, but we check it
// at the start of every function so that we'll
// be OK when nsIPresShell is converted to IDL.
return rv;
}
NS_IMETHODIMP
FrameManager::GetRootFrame(nsIFrame** aRootFrame) const
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_ARG_POINTER(aRootFrame);
*aRootFrame = mRootFrame;
return NS_OK;
}
NS_IMETHODIMP
FrameManager::SetRootFrame(nsIFrame* aRootFrame)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_PRECONDITION(!mRootFrame, "already have a root frame");
if (mRootFrame) {
return NS_ERROR_UNEXPECTED;
}
mRootFrame = aRootFrame;
return NS_OK;
}
NS_IMETHODIMP
FrameManager::GetCanvasFrame(nsIFrame** aCanvasFrame) const
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_PRECONDITION(aCanvasFrame, "aCanvasFrame argument cannot be null");
*aCanvasFrame = nsnull;
if (mRootFrame) {
// walk the children of the root frame looking for a frame with type==canvas
// start at the root
nsIFrame* childFrame = mRootFrame;
while (childFrame) {
// get each sibling of the child and check them, startig at the child
nsIFrame *siblingFrame = childFrame;
while (siblingFrame) {
if (siblingFrame->GetType() == nsLayoutAtoms::canvasFrame) {
// this is it: set the out-arg and stop looking
*aCanvasFrame = siblingFrame;
break;
} else {
siblingFrame = siblingFrame->GetNextSibling();
}
}
// move on to the child's child
childFrame = childFrame->GetFirstChild(nsnull);
}
}
return NS_OK;
}
//----------------------------------------------------------------------
// Primary frame functions
NS_IMETHODIMP
FrameManager::GetPrimaryFrameFor(nsIContent* aContent, nsIFrame** aResult)
{
NS_ASSERTION(aResult, "null out-param not supported");
*aResult = nsnull; // initialize out param (before possibly returning due to null args/members)
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_ARG_POINTER(aContent);
if (mIsDestroyingFrames) {
#ifdef DEBUG
printf("GetPrimaryFrameFor() called while FrameManager is being destroyed!\n");
#endif
return NS_ERROR_FAILURE;
}
if (mPrimaryFrameMap.ops) {
PrimaryFrameMapEntry *entry = NS_STATIC_CAST(PrimaryFrameMapEntry*,
PL_DHashTableOperate(&mPrimaryFrameMap, aContent, PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
*aResult = entry->frame;
} else {
// XXX: todo: Add a lookup into the undisplay map to skip searches
// if we already know the content has no frame.
// nsCSSFrameConstructor calls SetUndisplayedContent() for every
// content node that has display: none.
// Today, the undisplay map doesn't quite support what we need.
// We need to see if we can add a method to make a search for aContent
// very fast in the embedded hash table.
// This would almost completely remove the lookup penalty for things
// like <SCRIPT> and comments in very large documents.
nsCOMPtr<nsIPresContext> presContext;
// Give the frame construction code the opportunity to return the
// frame that maps the content object
mPresShell->GetPresContext(getter_AddRefs(presContext));
NS_ASSERTION(presContext, "bad presContext");
if (!presContext) {
return NS_ERROR_NULL_POINTER;
}
// if the prev sibling of aContent has a cached primary frame,
// pass that data in to the style set to speed things up
// if any methods in here fail, don't report that failure
// we're just trying to enhance performance here, not test for correctness
nsFindFrameHint hint;
nsIContent* parent = aContent->GetParent();
if (parent)
{
PRInt32 index = parent->IndexOf(aContent);
if (index > 0) // no use looking if it's the first child
{
nsIContent *prevSibling;
nsIAtom *tag;
do {
prevSibling = parent->GetChildAt(--index);
tag = prevSibling->Tag();
} while (index &&
(tag == nsLayoutAtoms::textTagName ||
tag == nsLayoutAtoms::commentTagName ||
tag == nsLayoutAtoms::processingInstructionTagName));
if (prevSibling) {
entry = NS_STATIC_CAST(PrimaryFrameMapEntry*,
PL_DHashTableOperate(&mPrimaryFrameMap, prevSibling,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(entry))
hint.mPrimaryFrameForPrevSibling = entry->frame;
}
}
}
// walk the frame tree to find the frame that maps aContent.
// Use the hint if we have it.
mPresShell->FrameConstructor()->
FindPrimaryFrameFor(presContext, this, aContent, aResult,
hint.mPrimaryFrameForPrevSibling ? &hint : nsnull);
}
}
return NS_OK;
}
NS_IMETHODIMP
FrameManager::SetPrimaryFrameFor(nsIContent* aContent,
nsIFrame* aPrimaryFrame)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_ARG_POINTER(aContent);
// it's ok if aPrimaryFrame is null
// If aPrimaryFrame is NULL, then remove the mapping
if (!aPrimaryFrame) {
if (mPrimaryFrameMap.ops) {
PL_DHashTableOperate(&mPrimaryFrameMap, aContent, PL_DHASH_REMOVE);
}
} else {
// This code should be used if/when we switch back to a 2-word entry
// in the primary frame map.
#if 0
NS_PRECONDITION(aPrimaryFrame->GetContent() == aContent, "wrong content");
#endif
// Create a new hashtable if necessary
if (!mPrimaryFrameMap.ops) {
if (!PL_DHashTableInit(&mPrimaryFrameMap, PL_DHashGetStubOps(), nsnull,
sizeof(PrimaryFrameMapEntry), 16)) {
mPrimaryFrameMap.ops = nsnull;
return NS_ERROR_OUT_OF_MEMORY;
}
}
// Add a mapping to the hash table
PrimaryFrameMapEntry *entry = NS_STATIC_CAST(PrimaryFrameMapEntry*,
PL_DHashTableOperate(&mPrimaryFrameMap, aContent, PL_DHASH_ADD));
#ifdef DEBUG_dbaron
if (entry->frame) {
NS_WARNING("already have primary frame for content");
}
#endif
entry->frame = aPrimaryFrame;
entry->content = aContent;
}
return NS_OK;
}
NS_IMETHODIMP
FrameManager::ClearPrimaryFrameMap()
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
if (mPrimaryFrameMap.ops) {
PL_DHashTableFinish(&mPrimaryFrameMap);
mPrimaryFrameMap.ops = nsnull;
}
return NS_OK;
}
// Placeholder frame functions
NS_IMETHODIMP
FrameManager::GetPlaceholderFrameFor(nsIFrame* aFrame,
nsIFrame** aResult) const
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_PRECONDITION(aFrame, "null param unexpected");
NS_PRECONDITION(aResult, "null out param unexpected");
if (mPlaceholderMap.ops) {
PlaceholderMapEntry *entry = NS_STATIC_CAST(PlaceholderMapEntry*,
PL_DHashTableOperate(NS_CONST_CAST(PLDHashTable*, &mPlaceholderMap),
aFrame, PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
*aResult = entry->placeholderFrame;
return NS_OK;
}
}
*aResult = nsnull;
return NS_OK;
}
NS_IMETHODIMP
FrameManager::RegisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
NS_PRECONDITION(nsLayoutAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
"unexpected frame type");
if (!mPlaceholderMap.ops) {
if (!PL_DHashTableInit(&mPlaceholderMap, &PlaceholderMapOps, nsnull,
sizeof(PlaceholderMapEntry), 16)) {
mPlaceholderMap.ops = nsnull;
return NS_ERROR_OUT_OF_MEMORY;
}
}
PlaceholderMapEntry *entry = NS_STATIC_CAST(PlaceholderMapEntry*,
PL_DHashTableOperate(&mPlaceholderMap,
aPlaceholderFrame->GetOutOfFlowFrame(),
PL_DHASH_ADD));
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;
}
NS_IMETHODIMP
FrameManager::UnregisterPlaceholderFrame(nsPlaceholderFrame* aPlaceholderFrame)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_PRECONDITION(aPlaceholderFrame, "null param unexpected");
NS_PRECONDITION(nsLayoutAtoms::placeholderFrame == aPlaceholderFrame->GetType(),
"unexpected frame type");
/*
* nsCSSFrameConstructor::ReconstructDocElementHierarchy calls
* ClearPlaceholderFrameMap and _then_ removes the fixed-positioned
* frames one by one. As these are removed they call
* UnregisterPlaceholderFrame on their placeholders, but this is all
* happening when mPlaceholderMap is already finished, so there is
* nothing to do here. See bug 144479.
*/
if (mPlaceholderMap.ops) {
PL_DHashTableOperate(&mPlaceholderMap,
aPlaceholderFrame->GetOutOfFlowFrame(),
PL_DHASH_REMOVE);
}
return NS_OK;
}
NS_IMETHODIMP
FrameManager::ClearPlaceholderFrameMap()
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
if (mPlaceholderMap.ops) {
PL_DHashTableFinish(&mPlaceholderMap);
mPlaceholderMap.ops = nsnull;
}
return NS_OK;
}
//----------------------------------------------------------------------
nsStyleContext*
FrameManager::GetUndisplayedContent(nsIContent* aContent)
{
if (!aContent || !mUndisplayedMap)
return nsnull;
nsIContent* parent = aContent->GetParent();
if (!parent)
return nsnull;
for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(parent);
node; node = node->mNext) {
if (node->mContent == aContent)
return node->mStyle;
}
return nsnull;
}
void
FrameManager::SetUndisplayedContent(nsIContent* aContent,
nsStyleContext* aStyleContext)
{
if (!mPresShell)
return;
#ifdef DEBUG_UNDISPLAYED_MAP
static int i = 0;
printf("SetUndisplayedContent(%d): p=%p \n", i++, (void *)aContent);
#endif
if (! mUndisplayedMap) {
mUndisplayedMap = new UndisplayedMap;
}
if (mUndisplayedMap) {
nsIContent* parent = aContent->GetParent();
NS_ASSERTION(parent, "undisplayed content must have a parent");
if (parent) {
mUndisplayedMap->AddNodeFor(parent, aContent, aStyleContext);
}
}
}
void
FrameManager::ChangeUndisplayedContent(nsIContent* aContent,
nsStyleContext* aStyleContext)
{
if (!mPresShell)
return;
NS_ASSERTION(mUndisplayedMap, "no existing undisplayed content");
#ifdef DEBUG_UNDISPLAYED_MAP
static int i = 0;
printf("ChangeUndisplayedContent(%d): p=%p \n", i++, (void *)aContent);
#endif
for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aContent->GetParent());
node; node = node->mNext) {
if (node->mContent == aContent) {
node->mStyle = aStyleContext;
return;
}
}
NS_NOTREACHED("no existing undisplayed content");
}
NS_IMETHODIMP
FrameManager::ClearUndisplayedContentIn(nsIContent* aContent, nsIContent* aParentContent)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
#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) {
nsresult rv = mUndisplayedMap->RemoveNodeFor(aParentContent, node);
#ifdef DEBUG_UNDISPLAYED_MAP
printf( "REMOVED! (rv=%d)\n", (int)rv);
#endif
#ifdef DEBUG
// make sure that there are no more entries for the same content
nsStyleContext *context = GetUndisplayedContent(aContent);
NS_ASSERTION(context == nsnull, "Found more undisplayed content data after removal");
#endif
return rv;
}
node = node->mNext;
}
}
return NS_OK;
}
NS_IMETHODIMP
FrameManager::ClearAllUndisplayedContentIn(nsIContent* aParentContent)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
#ifdef DEBUG_UNDISPLAYED_MAP
static int i = 0;
printf("ClearAllUndisplayedContentIn(%d): parent=%p \n", i++, (void*)aParentContent);
#endif
if (mUndisplayedMap) {
return mUndisplayedMap->RemoveNodesFor(aParentContent);
}
return NS_OK;
}
NS_IMETHODIMP
FrameManager::ClearUndisplayedContentMap()
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
#ifdef DEBUG_UNDISPLAYED_MAP
static int i = 0;
printf("ClearUndisplayedContentMap(%d)\n", i++);
#endif
if (mUndisplayedMap) {
mUndisplayedMap->Clear();
}
return NS_OK;
}
//----------------------------------------------------------------------
NS_IMETHODIMP
FrameManager::AppendFrames(nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aFrameList)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
return aParentFrame->AppendFrames(GetPresContext(), *GetPresShell(),
aListName, aFrameList);
}
NS_IMETHODIMP
FrameManager::InsertFrames(nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aPrevFrame,
nsIFrame* aFrameList)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
#ifdef IBMBIDI
if (aPrevFrame) {
// Insert aFrameList after the last bidi continuation of aPrevFrame.
nsIFrame* nextBidi;
for (; ;) {
GetFrameProperty(aPrevFrame, nsLayoutAtoms::nextBidi, 0, (void**) &nextBidi);
if (!nextBidi) {
break;
}
aPrevFrame = nextBidi;
}
}
#endif // IBMBIDI
return aParentFrame->InsertFrames(GetPresContext(), *GetPresShell(),
aListName, aPrevFrame, aFrameList);
}
NS_IMETHODIMP
FrameManager::RemoveFrame(nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aOldFrame)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
#ifdef IBMBIDI
// Don't let the parent remove next bidi. In the other cases the it should NOT be removed.
nsIFrame* nextBidi;
GetFrameProperty(aOldFrame, nsLayoutAtoms::nextBidi, 0, (void**) &nextBidi);
if (nextBidi) {
RemoveFrame(aParentFrame, aListName, nextBidi);
}
#endif // IBMBIDI
return aParentFrame->RemoveFrame(GetPresContext(), *GetPresShell(),
aListName, aOldFrame);
}
NS_IMETHODIMP
FrameManager::ReplaceFrame(nsIFrame* aParentFrame,
nsIAtom* aListName,
nsIFrame* aOldFrame,
nsIFrame* aNewFrame)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
return aParentFrame->ReplaceFrame(GetPresContext(), *GetPresShell(),
aListName, aOldFrame, aNewFrame);
}
//----------------------------------------------------------------------
NS_IMETHODIMP
FrameManager::NotifyDestroyingFrame(nsIFrame* aFrame)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
// Dequeue and destroy and posted events for this frame
DequeuePostedEventFor(aFrame);
// Remove all properties associated with the frame
nsCOMPtr<nsIPresContext> presContext;
mPresShell->GetPresContext(getter_AddRefs(presContext));
RemoveAllPropertiesFor(presContext, aFrame);
#ifdef DEBUG
if (mPrimaryFrameMap.ops) {
PrimaryFrameMapEntry *entry = NS_STATIC_CAST(PrimaryFrameMapEntry*,
PL_DHashTableOperate(&mPrimaryFrameMap, aFrame->GetContent(), PL_DHASH_LOOKUP));
NS_ASSERTION(!PL_DHASH_ENTRY_IS_BUSY(entry) || entry->frame != aFrame,
"frame was not removed from primary frame map before "
"destruction or was readded to map after being removed");
}
#endif
return NS_OK;
}
nsresult
FrameManager::RevokePostedEvents()
{
nsresult rv = NS_OK;
#ifdef NOISY_EVENTS
printf("%p ~RevokePostedEvents() start\n", this);
#endif
if (mPostedEvents) {
mPostedEvents = nsnull;
// Revoke any events in the event queue that are owned by us
nsIEventQueueService* eventService;
rv = nsServiceManager::GetService(kEventQueueServiceCID,
NS_GET_IID(nsIEventQueueService),
(nsISupports **)&eventService);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIEventQueue> eventQueue;
rv = eventService->GetThreadEventQueue(NS_CURRENT_THREAD,
getter_AddRefs(eventQueue));
nsServiceManager::ReleaseService(kEventQueueServiceCID, eventService);
if (NS_SUCCEEDED(rv) && eventQueue) {
rv = eventQueue->RevokeEvents(this);
}
}
}
#ifdef NOISY_EVENTS
printf("%p ~RevokePostedEvents() end\n", this);
#endif
return rv;
}
CantRenderReplacedElementEvent**
FrameManager::FindPostedEventFor(nsIFrame* aFrame)
{
CantRenderReplacedElementEvent** event = &mPostedEvents;
while (*event) {
if ((*event)->mFrame == aFrame) {
return event;
}
event = &(*event)->mNext;
}
return event;
}
void
FrameManager::DequeuePostedEventFor(nsIFrame* aFrame)
{
// If there's a posted event for this frame, then remove it
CantRenderReplacedElementEvent** event = FindPostedEventFor(aFrame);
if (*event) {
CantRenderReplacedElementEvent* tmp = *event;
// Remove it from our linked list of posted events
*event = (*event)->mNext;
// Dequeue it from the event queue
nsIEventQueueService* eventService;
nsresult rv;
rv = nsServiceManager::GetService(kEventQueueServiceCID,
NS_GET_IID(nsIEventQueueService),
(nsISupports **)&eventService);
NS_ASSERTION(NS_SUCCEEDED(rv),
"will crash soon due to event holding dangling pointer to frame");
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIEventQueue> eventQueue;
rv = eventService->GetThreadEventQueue(NS_CURRENT_THREAD,
getter_AddRefs(eventQueue));
nsServiceManager::ReleaseService(kEventQueueServiceCID, eventService);
NS_ASSERTION(NS_SUCCEEDED(rv) && eventQueue,
"will crash soon due to event holding dangling pointer to frame");
if (NS_SUCCEEDED(rv) && eventQueue) {
PLEventQueue* plqueue;
eventQueue->GetPLEventQueue(&plqueue);
NS_ASSERTION(plqueue,
"will crash soon due to event holding dangling pointer to frame");
if (plqueue) {
// Remove the event and then destroy it
PL_DequeueEvent(tmp, plqueue);
PL_DestroyEvent(tmp);
}
}
}
}
}
void
FrameManager::HandlePLEvent(CantRenderReplacedElementEvent* aEvent)
{
#ifdef NOISY_EVENTS
printf("FrameManager::HandlePLEvent() start for FM %p\n", aEvent->owner);
#endif
FrameManager* frameManager = (FrameManager*)aEvent->owner;
NS_ASSERTION(frameManager, "null frame manager");
if (!frameManager->mPresShell) {
NS_ASSERTION(frameManager->mPresShell,
"event not removed from queue on shutdown");
return;
}
// Remove the posted event from the linked list
CantRenderReplacedElementEvent** events = &frameManager->mPostedEvents;
while (*events) {
if (*events == aEvent) {
*events = (*events)->mNext;
break;
}
events = &(*events)->mNext;
NS_ASSERTION(*events, "event not in queue");
}
// Notify the style system and then process any reflow commands that
// are generated
nsCOMPtr<nsIPresContext> presContext;
frameManager->mPresShell->GetPresContext(getter_AddRefs(presContext));
frameManager->mPresShell->FrameConstructor()->
CantRenderReplacedElement(frameManager->mPresShell, presContext,
aEvent->mFrame);
#ifdef NOISY_EVENTS
printf("FrameManager::HandlePLEvent() end for FM %p\n", aEvent->owner);
#endif
}
void
FrameManager::DestroyPLEvent(CantRenderReplacedElementEvent* aEvent)
{
delete aEvent;
}
CantRenderReplacedElementEvent::CantRenderReplacedElementEvent(FrameManager* aFrameManager,
nsIFrame* aFrame,
nsIPresShell* aPresShell)
{
PL_InitEvent(this, aFrameManager,
(PLHandleEventProc)&FrameManager::HandlePLEvent,
(PLDestroyEventProc)&FrameManager::DestroyPLEvent);
mFrame = aFrame;
if (nsLayoutAtoms::objectFrame == aFrame->GetType()) {
AddLoadGroupRequest(aPresShell);
}
}
CantRenderReplacedElementEvent::~CantRenderReplacedElementEvent()
{
RemoveLoadGroupRequest();
}
// Add a load group request in order to delay the onLoad handler when we have
// pending replacements
nsresult CantRenderReplacedElementEvent::AddLoadGroupRequest(nsIPresShell* aPresShell)
{
nsCOMPtr<nsIDocument> doc;
aPresShell->GetDocument(getter_AddRefs(doc));
if (!doc) return NS_ERROR_FAILURE;
nsresult rv = nsDummyLayoutRequest::Create(getter_AddRefs(mDummyLayoutRequest), aPresShell);
if (NS_FAILED(rv)) return rv;
if (!mDummyLayoutRequest) return NS_ERROR_FAILURE;
nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
if (!loadGroup) return NS_ERROR_FAILURE;
rv = mDummyLayoutRequest->SetLoadGroup(loadGroup);
if (NS_FAILED(rv)) return rv;
mPresShell = do_GetWeakReference(aPresShell);
return loadGroup->AddRequest(mDummyLayoutRequest, nsnull);
}
// Remove the load group request added above
nsresult CantRenderReplacedElementEvent::RemoveLoadGroupRequest()
{
nsresult rv = NS_OK;
if (mDummyLayoutRequest) {
nsCOMPtr<nsIRequest> request = mDummyLayoutRequest;
mDummyLayoutRequest = nsnull;
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
if (!presShell) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDocument> doc;
presShell->GetDocument(getter_AddRefs(doc));
if (!doc) return NS_ERROR_FAILURE;;
nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup();
if (!loadGroup) return NS_ERROR_FAILURE;
rv = loadGroup->RemoveRequest(request, nsnull, NS_OK);
}
return rv;
}
NS_IMETHODIMP
FrameManager::CantRenderReplacedElement(nsIFrame* aFrame)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
#ifdef NOISY_EVENTS
printf("%p FrameManager::CantRenderReplacedElement called\n", this);
#endif
// We need to notify the style stystem, but post the notification so it
// doesn't happen now
nsresult rv;
nsCOMPtr<nsIEventQueueService> eventService = do_GetService(kEventQueueServiceCID, &rv);
if (eventService) {
nsCOMPtr<nsIEventQueue> eventQueue;
rv = eventService->GetThreadEventQueue(NS_CURRENT_THREAD,
getter_AddRefs(eventQueue));
if (NS_SUCCEEDED(rv) && eventQueue) {
// Verify that there isn't already a posted event associated with
// this frame.
if (*FindPostedEventFor(aFrame))
return NS_OK;
CantRenderReplacedElementEvent* ev;
// Create a new event
ev = new CantRenderReplacedElementEvent(this, aFrame, mPresShell);
// Post the event
rv = eventQueue->PostEvent(ev);
if (NS_FAILED(rv)) {
NS_ERROR("failed to post event");
PL_DestroyEvent(ev);
}
else {
// Add the event to our linked list of posted events
ev->mNext = mPostedEvents;
mPostedEvents = ev;
}
}
}
return rv;
}
#ifdef NS_DEBUG
static void
DumpContext(nsIFrame* aFrame, nsStyleContext* aContext)
{
if (aFrame) {
fputs("frame: ", stdout);
nsAutoString name;
nsIFrameDebug* frameDebug;
if (NS_SUCCEEDED(aFrame->QueryInterface(NS_GET_IID(nsIFrameDebug), (void**)&frameDebug))) {
frameDebug->GetFrameName(name);
fputs(NS_LossyConvertUCS2toASCII(name).get(), stdout);
}
fprintf(stdout, " (%p)", NS_STATIC_CAST(void*, aFrame));
}
if (aContext) {
fprintf(stdout, " style: %p ", NS_STATIC_CAST(void*, aContext));
nsIAtom* pseudoTag = aContext->GetPseudoType();
if (pseudoTag) {
nsAutoString buffer;
pseudoTag->ToString(buffer);
fputs(NS_LossyConvertUCS2toASCII(buffer).get(), stdout);
fputs(" ", stdout);
}
/* XXXdwh fix debugging here. Need to add a List method to nsRuleNode
and have the context call list on its rule node.
PRInt32 count = aContext->GetStyleRuleCount();
if (0 < count) {
fputs("{\n", stdout);
nsISupportsArray* rules = aContext->GetStyleRules();
PRInt32 ix;
for (ix = 0; ix < count; ix++) {
nsIStyleRule* rule = (nsIStyleRule*)rules->ElementAt(ix);
rule->List(stdout, 1);
NS_RELEASE(rule);
}
NS_RELEASE(rules);
fputs("}\n", stdout);
}
else
*/
{
fputs("{}\n", stdout);
}
}
}
static void
VerifySameTree(nsStyleContext* aContext1, nsStyleContext* aContext2)
{
nsStyleContext* top1 = aContext1;
nsStyleContext* top2 = aContext2;
nsStyleContext* parent;
for (;;) {
parent = top1->GetParent();
if (!parent)
break;
top1 = parent;
}
for (;;) {
parent = top2->GetParent();
if (!parent)
break;
top2 = parent;
}
if (top1 != top2)
printf("Style contexts are not in the same style context tree.\n");
}
static void
VerifyContextParent(nsIPresContext* aPresContext, nsIFrame* aFrame,
nsStyleContext* aContext, nsStyleContext* aParentContext)
{
// get the contexts not provided
if (!aContext) {
aContext = aFrame->GetStyleContext();
}
if (!aParentContext) {
// Get the correct parent context from the frame
// - if the frame is a placeholder, we get the out of flow frame's context
// as the parent context instead of asking the frame
// get the parent context from the frame (indirectly)
nsIFrame* providerFrame = nsnull;
PRBool providerIsChild;
aFrame->GetParentStyleContextFrame(aPresContext,
&providerFrame, &providerIsChild);
if (providerFrame)
aParentContext = providerFrame->GetStyleContext();
// aParentContext could still be null
}
NS_ASSERTION(aContext, "Failure to get required contexts");
nsStyleContext* actualParentContext = aContext->GetParent();
if (aParentContext) {
if (aParentContext != actualParentContext) {
DumpContext(aFrame, aContext);
if (aContext == aParentContext) {
fputs("Using parent's style context\n\n", stdout);
}
else {
fputs("Wrong parent style context: ", stdout);
DumpContext(nsnull, actualParentContext);
fputs("should be using: ", stdout);
DumpContext(nsnull, aParentContext);
VerifySameTree(actualParentContext, aParentContext);
fputs("\n", stdout);
}
}
}
else {
if (actualParentContext) {
DumpContext(aFrame, aContext);
fputs("Has parent context: ", stdout);
DumpContext(nsnull, actualParentContext);
fputs("Should be null\n\n", stdout);
}
}
}
static void
VerifyStyleTree(nsIPresContext* aPresContext, nsIFrame* aFrame, nsStyleContext* aParentContext)
{
nsStyleContext* context = aFrame->GetStyleContext();
VerifyContextParent(aPresContext, aFrame, context, nsnull);
PRInt32 listIndex = 0;
nsIAtom* childList = nsnull;
nsIFrame* child;
do {
child = aFrame->GetFirstChild(childList);
while (child) {
if (NS_FRAME_OUT_OF_FLOW != (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
// only do frames that are in flow
if (nsLayoutAtoms::placeholderFrame == child->GetType()) {
// placeholder: first recirse and verify the out of flow frame,
// then verify the placeholder's context
nsIFrame* outOfFlowFrame = ((nsPlaceholderFrame*)child)->GetOutOfFlowFrame();
NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
// recurse to out of flow frame, letting the parent context get resolved
VerifyStyleTree(aPresContext, outOfFlowFrame, nsnull);
// verify placeholder using the parent frame's context as
// parent context
VerifyContextParent(aPresContext, child, nsnull, nsnull);
}
else { // regular frame
VerifyStyleTree(aPresContext, child, nsnull);
}
}
child = child->GetNextSibling();
}
childList = aFrame->GetAdditionalChildListName(listIndex++);
} while (childList);
// do additional contexts
PRInt32 contextIndex = -1;
while (1) {
nsStyleContext* extraContext = aFrame->GetAdditionalStyleContext(++contextIndex);
if (extraContext) {
VerifyContextParent(aPresContext, aFrame, extraContext, context);
}
else {
break;
}
}
}
NS_IMETHODIMP
FrameManager::DebugVerifyStyleTree(nsIFrame* aFrame)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
if (aFrame) {
nsStyleContext* context = aFrame->GetStyleContext();
nsStyleContext* parentContext = context->GetParent();
VerifyStyleTree(GetPresContext(), aFrame, parentContext);
}
return NS_OK;
}
#endif // DEBUG
NS_IMETHODIMP
FrameManager::ReParentStyleContext(nsIFrame* aFrame,
nsStyleContext* aNewParentContext)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
nsresult result = NS_ERROR_NULL_POINTER;
if (aFrame) {
// DO NOT verify the style tree before reparenting. The frame
// tree has already been changed, so this check would just fail.
nsStyleContext* oldContext = aFrame->GetStyleContext();
if (oldContext) {
nsIPresContext *presContext = GetPresContext();
nsRefPtr<nsStyleContext> newContext;
result = NS_OK;
newContext = mStyleSet->ReParentStyleContext(presContext, oldContext,
aNewParentContext);
if (newContext) {
if (newContext != oldContext) {
PRInt32 listIndex = 0;
nsIAtom* childList = nsnull;
nsIFrame* child;
aFrame->SetStyleContext(presContext, newContext);
do {
child = aFrame->GetFirstChild(childList);
while (child) {
if (NS_FRAME_OUT_OF_FLOW != (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
// only do frames that are in flow
if (nsLayoutAtoms::placeholderFrame == child->GetType()) {
// get out of flow frame and recurse there
nsIFrame* outOfFlowFrame = ((nsPlaceholderFrame*)child)->GetOutOfFlowFrame();
NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
result = ReParentStyleContext(outOfFlowFrame, newContext);
// reparent placeholder's context under out of flow frame
nsStyleContext* outOfFlowContext = outOfFlowFrame->GetStyleContext();
ReParentStyleContext(child, outOfFlowContext);
}
else { // regular frame
result = ReParentStyleContext(child, newContext);
}
}
child = child->GetNextSibling();
}
childList = aFrame->GetAdditionalChildListName(listIndex++);
} while (childList);
// do additional contexts
PRInt32 contextIndex = -1;
while (1) {
nsStyleContext* oldExtraContext = aFrame->GetAdditionalStyleContext(++contextIndex);
if (oldExtraContext) {
nsRefPtr<nsStyleContext> newExtraContext;
newExtraContext = mStyleSet->ReParentStyleContext(presContext,
oldExtraContext,
newContext);
if (newExtraContext) {
aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
}
}
else {
result = NS_OK; // ok not to have extras (or run out)
break;
}
}
#ifdef DEBUG
VerifyStyleTree(GetPresContext(), aFrame, aNewParentContext);
#endif
}
}
}
}
return result;
}
static nsChangeHint
CaptureChange(nsStyleContext* aOldContext, nsStyleContext* aNewContext,
nsIFrame* aFrame, nsIContent* aContent,
nsStyleChangeList& aChangeList, nsChangeHint aMinChange)
{
nsChangeHint ourChange = NS_STYLE_HINT_NONE;
ourChange = aOldContext->CalcStyleDifference(aNewContext);
if (NS_UpdateHint(aMinChange, ourChange)) {
aChangeList.AppendChange(aFrame, aContent, ourChange);
}
return aMinChange;
}
void
FrameManager::ReResolveStyleContext(nsIPresContext* aPresContext,
nsIFrame* aFrame,
nsIContent* aParentContent,
nsStyleChangeList& aChangeList,
nsChangeHint aMinChange,
nsChangeHint& aResultChange)
{
// XXXldb get new context from prev-in-flow if possible, to avoid
// duplication. (Or should we just let |GetContext| handle that?)
// Getting the hint would be nice too, but that's harder.
// XXXbryner we may be able to avoid some of the refcounting goop here.
// We do need a reference to oldContext for the lifetime of this function, and it's possible
// that the frame has the last reference to it, so AddRef it here.
nsStyleContext* oldContext = aFrame->GetStyleContext();
nsStyleSet* styleSet = aPresContext->StyleSet();
if (oldContext) {
oldContext->AddRef();
nsIAtom* pseudoTag = oldContext->GetPseudoType();
nsIContent* localContent = aFrame->GetContent();
nsIContent* content = localContent ? localContent : aParentContent;
nsStyleContext* parentContext;
nsIFrame* resolvedChild = nsnull;
// Get the frame providing the parent style context. If it is a
// child, then resolve the provider first.
nsIFrame* providerFrame = nsnull;
PRBool providerIsChild = PR_FALSE;
aFrame->GetParentStyleContextFrame(aPresContext,
&providerFrame, &providerIsChild);
if (!providerIsChild) {
if (providerFrame)
parentContext = providerFrame->GetStyleContext();
else
parentContext = nsnull;
}
else {
// resolve the provider here (before aFrame below)
ReResolveStyleContext(aPresContext, providerFrame, content,
aChangeList, aMinChange, aResultChange);
// The provider's new context becomes the parent context of
// aFrame's context.
parentContext = providerFrame->GetStyleContext();
// Set |resolvedChild| so we don't bother resolving the
// provider again.
resolvedChild = providerFrame;
}
// do primary context
nsStyleContext* newContext = nsnull;
if (pseudoTag == nsCSSAnonBoxes::mozNonElement) {
NS_ASSERTION(localContent,
"non pseudo-element frame without content node");
newContext = styleSet->ResolveStyleForNonElement(parentContext).get();
}
else if (pseudoTag) {
nsIContent* pseudoContent =
aParentContent ? aParentContent : localContent;
if (pseudoTag == nsCSSPseudoElements::before ||
pseudoTag == nsCSSPseudoElements::after) {
// XXX what other pseudos do we need to treat like this?
newContext = styleSet->ProbePseudoStyleFor(pseudoContent,
pseudoTag,
parentContext).get();
if (!newContext) {
// This pseudo should no longer exist; gotta reframe
NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
aChangeList.AppendChange(aFrame, pseudoContent,
nsChangeHint_ReconstructFrame);
// We're reframing anyway; just keep the same context
newContext = oldContext;
newContext->AddRef();
}
} else {
newContext = styleSet->ResolvePseudoStyleFor(pseudoContent,
pseudoTag,
parentContext).get();
}
}
else {
NS_ASSERTION(localContent,
"non pseudo-element frame without content node");
newContext = styleSet->ResolveStyleFor(content, parentContext).get();
}
NS_ASSERTION(newContext, "failed to get new style context");
if (newContext) {
if (!parentContext) {
if (oldContext->GetRuleNode() == newContext->GetRuleNode()) {
// We're the root of the style context tree and the new style
// context returned has the same rule node. This means that
// we can use FindChildWithRules to keep a lot of the old
// style contexts around. However, we need to start from the
// same root.
newContext->Release();
newContext = oldContext;
newContext->AddRef();
}
}
if (newContext != oldContext) {
aMinChange = CaptureChange(oldContext, newContext, aFrame, content, aChangeList, aMinChange);
if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
// if frame gets regenerated, let it keep old context
aFrame->SetStyleContext(aPresContext, newContext);
}
// if old context had image and new context does not have the same image,
// stop the image load for the frame
const nsStyleBackground* oldColor = oldContext->GetStyleBackground();
const nsStyleBackground* newColor = newContext->GetStyleBackground();
PRBool equal;
if (oldColor->mBackgroundImage &&
(!newColor->mBackgroundImage ||
NS_FAILED(oldColor->mBackgroundImage->Equals(
newColor->mBackgroundImage, &equal)) ||
!equal)) {
// stop the image loading for the frame, the image has changed
aPresContext->StopImagesFor(aFrame);
}
}
oldContext->Release();
}
else {
NS_ERROR("resolve style context failed");
newContext = oldContext; // new context failed, recover... (take ref)
oldContext = nsnull;
}
// do additional contexts
PRInt32 contextIndex = -1;
while (1 == 1) {
nsStyleContext* oldExtraContext = nsnull;
oldExtraContext = aFrame->GetAdditionalStyleContext(++contextIndex);
if (oldExtraContext) {
nsStyleContext* newExtraContext = nsnull;
pseudoTag = oldExtraContext->GetPseudoType();
NS_ASSERTION(pseudoTag &&
pseudoTag != nsCSSAnonBoxes::mozNonElement,
"extra style context is not pseudo element");
newExtraContext = styleSet->ResolvePseudoStyleFor(content,
pseudoTag,
newContext).get();
if (newExtraContext) {
if (oldExtraContext != newExtraContext) {
aMinChange = CaptureChange(oldExtraContext, newExtraContext, aFrame,
content, aChangeList, aMinChange);
if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
}
}
newExtraContext->Release();
}
}
else {
break;
}
}
// now look for undisplayed child content and pseudos
if (localContent && mUndisplayedMap) {
for (UndisplayedNode* undisplayed =
mUndisplayedMap->GetFirstNode(localContent);
undisplayed; undisplayed = undisplayed->mNext) {
nsRefPtr<nsStyleContext> undisplayedContext;
pseudoTag = undisplayed->mStyle->GetPseudoType();
if (pseudoTag == nsnull) { // child content
undisplayedContext = styleSet->ResolveStyleFor(undisplayed->mContent,
newContext);
}
else if (pseudoTag == nsCSSAnonBoxes::mozNonElement) {
undisplayedContext = styleSet->ResolveStyleForNonElement(newContext);
}
else { // pseudo element
NS_NOTREACHED("no pseudo elements in undisplayed map");
NS_ASSERTION(pseudoTag, "pseudo element without tag");
undisplayedContext = styleSet->ResolvePseudoStyleFor(localContent,
pseudoTag,
newContext);
}
if (undisplayedContext) {
const nsStyleDisplay* display = undisplayedContext->GetStyleDisplay();
if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {
aChangeList.AppendChange(nsnull,
undisplayed->mContent
? NS_STATIC_CAST(nsIContent*,
undisplayed->mContent)
: localContent,
NS_STYLE_HINT_FRAMECHANGE);
// The node should be removed from the undisplayed map when
// we reframe it.
} else {
// update the undisplayed node with the new context
undisplayed->mStyle = undisplayedContext;
}
}
}
}
aResultChange = aMinChange;
if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
// Make sure not to do this for pseudo-frames -- those can't have :before
// or :after content.
if (!pseudoTag && localContent &&
localContent->IsContentOfType(nsIContent::eELEMENT)) {
// Check for a new :before pseudo and an existing :before
// frame, but only if the frame is the first-in-flow.
nsIFrame* prevInFlow = nsnull;
aFrame->GetPrevInFlow(&prevInFlow);
if (!prevInFlow) {
// Checking for a :before frame is cheaper than getting the
// :before style context.
if (!nsLayoutUtils::GetBeforeFrame(aFrame, aPresContext) &&
nsLayoutUtils::HasPseudoStyle(localContent, newContext,
nsCSSPseudoElements::before,
aPresContext)) {
// Have to create the new :before frame
NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
aChangeList.AppendChange(aFrame, content,
nsChangeHint_ReconstructFrame);
}
}
}
}
if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
// Make sure not to do this for pseudo-frames -- those can't have :before
// or :after content.
if (!pseudoTag && localContent &&
localContent->IsContentOfType(nsIContent::eELEMENT)) {
// Check for new :after content, but only if the frame is the
// first-in-flow.
nsIFrame* nextInFlow = nsnull;
aFrame->GetNextInFlow(&nextInFlow);
if (!nextInFlow) {
// Getting the :after frame is more expensive than getting the pseudo
// context, so get the pseudo context first.
if (nsLayoutUtils::HasPseudoStyle(localContent, newContext,
nsCSSPseudoElements::after,
aPresContext) &&
!nsLayoutUtils::GetAfterFrame(aFrame, aPresContext)) {
// have to create the new :after frame
NS_UpdateHint(aMinChange, nsChangeHint_ReconstructFrame);
aChangeList.AppendChange(aFrame, content,
nsChangeHint_ReconstructFrame);
}
}
}
}
if (!(aMinChange & nsChangeHint_ReconstructFrame)) {
// There is no need to waste time crawling into a frame's children on a frame change.
// The act of reconstructing frames will force new style contexts to be resolved on all
// of this frame's descendants anyway, so we want to avoid wasting time processing
// style contexts that we're just going to throw away anyway. - dwh
// now do children
PRInt32 listIndex = 0;
nsIAtom* childList = nsnull;
nsChangeHint childChange;
do {
nsIFrame* child = aFrame->GetFirstChild(childList);
while (child) {
if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
// only do frames that are in flow
if (nsLayoutAtoms::placeholderFrame == child->GetType()) { // placeholder
// get out of flow frame and recur there
nsIFrame* outOfFlowFrame =
NS_STATIC_CAST(nsPlaceholderFrame*,child)->GetOutOfFlowFrame();
NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
NS_ASSERTION(outOfFlowFrame != resolvedChild,
"out-of-flow frame not a true descendant");
// |nsFrame::GetParentStyleContextFrame| checks being out
// of flow so that this works correctly.
ReResolveStyleContext(aPresContext, outOfFlowFrame, content,
aChangeList, aMinChange, childChange);
// reresolve placeholder's context under the same parent
// as the out-of-flow frame
ReResolveStyleContext(aPresContext, child, content,
aChangeList, aMinChange, childChange);
}
else { // regular child frame
if (child != resolvedChild) {
ReResolveStyleContext(aPresContext, child, content,
aChangeList, aMinChange, childChange);
} else {
NOISY_TRACE_FRAME("child frame already resolved as descendent, skipping",aFrame);
}
}
}
child = child->GetNextSibling();
}
childList = aFrame->GetAdditionalChildListName(listIndex++);
} while (childList);
// XXX need to do overflow frames???
}
newContext->Release();
}
}
NS_IMETHODIMP
FrameManager::ComputeStyleChangeFor(nsIFrame* aFrame,
nsStyleChangeList& aChangeList,
nsChangeHint aMinChange,
nsChangeHint& aTopLevelChange)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
aTopLevelChange = aMinChange;
nsIFrame* frame = aFrame;
nsIFrame* frame2 = aFrame;
#ifdef DEBUG
{
nsIFrame* prevInFlow;
frame->GetPrevInFlow(&prevInFlow);
NS_ASSERTION(!prevInFlow, "must start with the first in flow");
}
#endif
// We want to start with this frame and walk all its next-in-flows,
// as well as all its special siblings and their next-in-flows,
// reresolving style on all the frames we encounter in this walk.
do {
// Outer loop over special siblings
do {
// Inner loop over next-in-flows of the current frame
nsChangeHint frameChange;
ReResolveStyleContext(GetPresContext(), frame, nsnull,
aChangeList, aTopLevelChange, frameChange);
NS_UpdateHint(aTopLevelChange, frameChange);
if (aTopLevelChange & nsChangeHint_ReconstructFrame) {
// If it's going to cause a framechange, then don't bother
// with the continuations or special siblings since they'll be
// clobbered by the frame reconstruct anyway.
#ifdef NS_DEBUG
nsIFrame* prevInFlow;
frame->GetPrevInFlow(&prevInFlow);
NS_ASSERTION(!prevInFlow, "continuing frame had more severe impact than first-in-flow");
#endif
return NS_OK;
}
frame->GetNextInFlow(&frame);
} while (frame);
// Might we have special siblings?
if (!(frame2->GetStateBits() & NS_FRAME_IS_SPECIAL)) {
// nothing more to do here
return NS_OK;
}
GetFrameProperty(frame2, nsLayoutAtoms::IBSplitSpecialSibling, 0, (void**)&frame2);
frame = frame2;
} while (frame2);
return NS_OK;
}
NS_IMETHODIMP
FrameManager::HasAttributeDependentStyle(nsIContent *aContent,
nsIAtom *aAttribute,
PRInt32 aModType,
nsReStyleHint *aResult)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
*aResult = mStyleSet->HasAttributeDependentStyle(GetPresContext(),
aContent, aAttribute,
aModType);
if (aAttribute == nsHTMLAtoms::style) {
// Perhaps should check that it's XUL, SVG, (or HTML) namespace, but
// it doesn't really matter. Or we could even let
// HTMLCSSStyleSheetImpl::HasAttributeDependentStyle handle it.
*aResult = nsReStyleHint(*aResult | eReStyle_Self);
}
return NS_OK;
}
// Capture state for a given frame.
// Accept a content id here, in some cases we may not have content (scroll position)
NS_IMETHODIMP
FrameManager::CaptureFrameStateFor(nsIFrame* aFrame,
nsILayoutHistoryState* aState,
nsIStatefulFrame::SpecialStateID aID)
{
NS_ENSURE_TRUE(mPresShell && aFrame && aState, NS_ERROR_FAILURE);
// Only capture state for stateful frames
nsIStatefulFrame* statefulFrame = nsnull;
aFrame->QueryInterface(NS_GET_IID(nsIStatefulFrame), (void**) &statefulFrame);
if (!statefulFrame) {
return NS_OK;
}
// Capture the state, exit early if we get null (nothing to save)
nsCOMPtr<nsIPresState> frameState;
nsresult rv = NS_OK;
rv = statefulFrame->SaveState(GetPresContext(), getter_AddRefs(frameState));
if (!frameState) {
return NS_OK;
}
// Generate the hash key to store the state under
// Exit early if we get empty key
nsCAutoString stateKey;
rv = nsContentUtils::GenerateStateKey(aFrame->GetContent(), aID, stateKey);
if(NS_FAILED(rv) || stateKey.IsEmpty()) {
return rv;
}
// Store the state
return aState->AddState(stateKey, frameState);
}
NS_IMETHODIMP
FrameManager::CaptureFrameState(nsIFrame* aFrame,
nsILayoutHistoryState* aState)
{
nsresult rv = NS_OK;
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_PRECONDITION(nsnull != aFrame && nsnull != aState, "null parameters passed in");
rv = CaptureFrameStateFor(aFrame, aState);
// Now capture state recursively for the frame hierarchy rooted at aFrame
nsIAtom* childListName = nsnull;
PRInt32 childListIndex = 0;
do {
nsIFrame* childFrame = aFrame->GetFirstChild(childListName);
while (childFrame) {
rv = CaptureFrameState(childFrame, aState);
// Get the next sibling child frame
childFrame = childFrame->GetNextSibling();
}
childListName = aFrame->GetAdditionalChildListName(childListIndex++);
} while (childListName);
return rv;
}
// Restore state for a given frame.
// Accept a content id here, in some cases we may not have content (scroll position)
NS_IMETHODIMP
FrameManager::RestoreFrameStateFor(nsIFrame* aFrame,
nsILayoutHistoryState* aState,
nsIStatefulFrame::SpecialStateID aID)
{
NS_ENSURE_TRUE(mPresShell && aFrame && aState, NS_ERROR_FAILURE);
// Only capture state for stateful frames
nsIStatefulFrame* statefulFrame = nsnull;
aFrame->QueryInterface(NS_GET_IID(nsIStatefulFrame), (void**) &statefulFrame);
if (!statefulFrame) {
return NS_OK;
}
// 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 NS_OK;
}
nsCAutoString stateKey;
nsresult rv = nsContentUtils::GenerateStateKey(content, aID, stateKey);
if (NS_FAILED(rv) || stateKey.IsEmpty()) {
return rv;
}
// Get the state from the hash
nsCOMPtr<nsIPresState> frameState;
rv = aState->GetState(stateKey, getter_AddRefs(frameState));
if (!frameState) {
return NS_OK;
}
// Restore it
rv = statefulFrame->RestoreState(GetPresContext(), frameState);
NS_ENSURE_SUCCESS(rv, rv);
// If we restore ok, remove the state from the state table
return aState->RemoveState(stateKey);
}
NS_IMETHODIMP
FrameManager::RestoreFrameState(nsIFrame* aFrame, nsILayoutHistoryState* aState)
{
nsresult rv = NS_OK;
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_PRECONDITION(nsnull != aFrame && nsnull != aState, "null parameters passed in");
rv = RestoreFrameStateFor(aFrame, aState);
// Now restore state recursively for the frame hierarchy rooted at aFrame
nsIAtom* childListName = nsnull;
PRInt32 childListIndex = 0;
do {
nsIFrame* childFrame = aFrame->GetFirstChild(childListName);
while (childFrame) {
rv = RestoreFrameState(childFrame, aState);
// Get the next sibling child frame
childFrame = childFrame->GetNextSibling();
}
childListName = aFrame->GetAdditionalChildListName(childListIndex++);
} while (childListName);
return rv;
}
//----------------------------------------------------------------------
static PLHashNumber
HashKey(void* key)
{
return NS_PTR_TO_INT32(key);
}
static PRIntn
CompareKeys(void* key1, void* key2)
{
return key1 == key2;
}
void
FrameManager::DestroyPropertyList(nsIPresContext* aPresContext)
{
if (mPropertyList) {
while (mPropertyList) {
PropertyList* tmp = mPropertyList;
mPropertyList = mPropertyList->mNext;
tmp->Destroy(aPresContext);
delete tmp;
}
}
}
FrameManager::PropertyList*
FrameManager::GetPropertyListFor(nsIAtom* aPropertyName) const
{
PropertyList* result;
for (result = mPropertyList; result; result = result->mNext) {
if (result->mName.get() == aPropertyName) {
break;
}
}
return result;
}
void
FrameManager::RemoveAllPropertiesFor(nsIPresContext* aPresContext,
nsIFrame* aFrame)
{
for (PropertyList* prop = mPropertyList; prop; prop = prop->mNext) {
prop->RemovePropertyForFrame(aPresContext, aFrame);
}
}
NS_IMETHODIMP
FrameManager::GetFrameProperty(nsIFrame* aFrame,
nsIAtom* aPropertyName,
PRUint32 aOptions,
void** aPropertyValue)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_PRECONDITION(aPropertyName && aFrame, "unexpected null param");
PropertyList* propertyList = GetPropertyListFor(aPropertyName);
if (propertyList) {
PropertyListMapEntry *entry = NS_STATIC_CAST(PropertyListMapEntry*,
PL_DHashTableOperate(&propertyList->mFrameValueMap, aFrame,
PL_DHASH_LOOKUP));
if (PL_DHASH_ENTRY_IS_BUSY(entry)) {
*aPropertyValue = entry->value;
if (aOptions & NS_IFRAME_MGR_REMOVE_PROP) {
// don't call propertyList->mDtorFunc. That's the caller's job now.
PL_DHashTableRawRemove(&propertyList->mFrameValueMap, entry);
}
return NS_OK;
}
}
*aPropertyValue = 0;
return NS_IFRAME_MGR_PROP_NOT_THERE;
}
NS_IMETHODIMP
FrameManager::SetFrameProperty(nsIFrame* aFrame,
nsIAtom* aPropertyName,
void* aPropertyValue,
NSFramePropertyDtorFunc aPropDtorFunc)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_PRECONDITION(aPropertyName && aFrame, "unexpected null param");
PropertyList* propertyList = GetPropertyListFor(aPropertyName);
if (propertyList) {
// Make sure the dtor function matches
if (aPropDtorFunc != propertyList->mDtorFunc) {
return NS_ERROR_INVALID_ARG;
}
} else {
propertyList = new PropertyList(aPropertyName, aPropDtorFunc);
if (!propertyList)
return NS_ERROR_OUT_OF_MEMORY;
if (!propertyList->mFrameValueMap.ops) {
delete propertyList;
return NS_ERROR_OUT_OF_MEMORY;
}
propertyList->mNext = mPropertyList;
mPropertyList = propertyList;
}
// The current property value (if there is one) is replaced and the current
// value is destroyed
nsresult result = NS_OK;
PropertyListMapEntry *entry = NS_STATIC_CAST(PropertyListMapEntry*,
PL_DHashTableOperate(&propertyList->mFrameValueMap, aFrame, PL_DHASH_ADD));
if (!entry)
return NS_ERROR_OUT_OF_MEMORY;
// A NULL entry->key is the sign that the entry has just been allocated
// for us. If it's non-NULL then we have an existing entry.
if (entry->key && propertyList->mDtorFunc) {
nsCOMPtr<nsIPresContext> presContext;
mPresShell->GetPresContext(getter_AddRefs(presContext));
propertyList->mDtorFunc(presContext, aFrame, aPropertyName, entry->value);
result = NS_IFRAME_MGR_PROP_OVERWRITTEN;
}
entry->key = aFrame;
entry->value = aPropertyValue;
return result;
}
NS_IMETHODIMP
FrameManager::RemoveFrameProperty(nsIFrame* aFrame,
nsIAtom* aPropertyName)
{
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
NS_PRECONDITION(aPropertyName && aFrame, "unexpected null param");
PropertyList* propertyList = GetPropertyListFor(aPropertyName);
if (propertyList) {
nsCOMPtr<nsIPresContext> presContext;
mPresShell->GetPresContext(getter_AddRefs(presContext));
if (propertyList->RemovePropertyForFrame(presContext, aFrame))
return NS_OK;
}
return NS_IFRAME_MGR_PROP_NOT_THERE;
}
//----------------------------------------------------------------------
MOZ_DECL_CTOR_COUNTER(UndisplayedMap)
UndisplayedMap::UndisplayedMap(PRUint32 aNumBuckets)
{
MOZ_COUNT_CTOR(UndisplayedMap);
mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey,
(PLHashComparator)CompareKeys,
(PLHashComparator)nsnull,
nsnull, nsnull);
mLastLookup = nsnull;
}
UndisplayedMap::~UndisplayedMap(void)
{
MOZ_COUNT_DTOR(UndisplayedMap);
Clear();
PL_HashTableDestroy(mTable);
}
PLHashEntry**
UndisplayedMap::GetEntryFor(nsIContent* aParentContent)
{
if (mLastLookup && (aParentContent == (*mLastLookup)->key)) {
return mLastLookup;
}
PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent);
PLHashEntry** entry = PL_HashTableRawLookup(mTable, hashCode, aParentContent);
if (*entry) {
mLastLookup = entry;
}
return entry;
}
UndisplayedNode*
UndisplayedMap::GetFirstNode(nsIContent* aParentContent)
{
PLHashEntry** entry = GetEntryFor(aParentContent);
if (*entry) {
return (UndisplayedNode*)((*entry)->value);
}
return nsnull;
}
nsresult
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 NS_OK;
}
node = node->mNext;
}
node->mNext = aNode;
}
else {
PLHashNumber hashCode = NS_PTR_TO_INT32(aParentContent);
PL_HashTableRawAdd(mTable, entry, hashCode, aParentContent, aNode);
mLastLookup = nsnull; // hashtable may have shifted bucket out from under us
}
return NS_OK;
}
nsresult
UndisplayedMap::AddNodeFor(nsIContent* aParentContent, nsIContent* aChild,
nsStyleContext* aStyle)
{
UndisplayedNode* node = new UndisplayedNode(aChild, aStyle);
if (! node) {
return NS_ERROR_OUT_OF_MEMORY;
}
return AppendNodeFor(node, aParentContent);
}
nsresult
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 = nsnull;
}
else {
PL_HashTableRawRemove(mTable, entry, *entry);
mLastLookup = nsnull; // 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 = nsnull;
break;
}
node = node->mNext;
}
}
}
delete aNode;
return NS_OK;
}
nsresult
UndisplayedMap::RemoveNodesFor(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");
delete node;
PL_HashTableRawRemove(mTable, entry, *entry);
mLastLookup = nsnull; // hashtable may have shifted bucket out from under us
}
return NS_OK;
}
static PRIntn PR_CALLBACK
RemoveUndisplayedEntry(PLHashEntry* he, PRIntn 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
UndisplayedMap::Clear(void)
{
mLastLookup = nsnull;
PL_HashTableEnumerateEntries(mTable, RemoveUndisplayedEntry, 0);
}
//----------------------------------------------------------------------
FrameManager::PropertyList::PropertyList(nsIAtom* aName,
NSFramePropertyDtorFunc aDtorFunc)
: mName(aName), mDtorFunc(aDtorFunc), mNext(nsnull)
{
PL_DHashTableInit(&mFrameValueMap, PL_DHashGetStubOps(), this,
sizeof(PropertyListMapEntry), 16);
}
FrameManager::PropertyList::~PropertyList()
{
PL_DHashTableFinish(&mFrameValueMap);
}
PR_STATIC_CALLBACK(PLDHashOperator)
DestroyPropertyEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
PRUint32 number, void *arg)
{
FrameManager::PropertyList *propList =
NS_STATIC_CAST(FrameManager::PropertyList*, table->data);
nsIPresContext *presContext = NS_STATIC_CAST(nsIPresContext*, arg);
PropertyListMapEntry* entry = NS_STATIC_CAST(PropertyListMapEntry*, hdr);
propList->mDtorFunc(presContext, entry->key, propList->mName, entry->value);
return PL_DHASH_NEXT;
}
void
FrameManager::PropertyList::Destroy(nsIPresContext* aPresContext)
{
// Enumerate any remaining frame/value pairs and destroy the value object
if (mDtorFunc)
PL_DHashTableEnumerate(&mFrameValueMap, DestroyPropertyEnumerator,
aPresContext);
}
PRBool
FrameManager::PropertyList::RemovePropertyForFrame(nsIPresContext* aPresContext,
nsIFrame* aFrame)
{
PropertyListMapEntry *entry = NS_STATIC_CAST(PropertyListMapEntry*,
PL_DHashTableOperate(&mFrameValueMap, aFrame, PL_DHASH_LOOKUP));
if (!PL_DHASH_ENTRY_IS_BUSY(entry))
return PR_FALSE;
if (mDtorFunc)
mDtorFunc(aPresContext, aFrame, mName, entry->value);
PL_DHashTableRawRemove(&mFrameValueMap, entry);
return PR_TRUE;
}