mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
5496fd604a
Clean up the FrameManager a bit, mainly by moving the destruction of the frame tree into the |Destroy| method rather than doing it in the destructor. This would make it safer to convert nsIFrameManager to IDL and use the frame manager from JS. It also reduces the risk of leaks caused by having something owned by a frame own the frame manager. Breaking cycles in a destructor can be risky because the objects entrained by the cycle could include the object whose destructor would break the cycle. b=65800 r=waterson@netscape.com sr=attinasi@netscape.com
2694 lines
90 KiB
C++
2694 lines
90 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.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 "nsIFrameManager.h"
|
|
#include "nsIFrame.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsIStyleSet.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsStyleChangeList.h"
|
|
#include "nsIEventQueueService.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "prthread.h"
|
|
#include "plhash.h"
|
|
#include "nsDST.h"
|
|
#include "nsPlaceholderFrame.h"
|
|
#include "nsLayoutAtoms.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 "nsIXMLContent.h"
|
|
#include "nsIDocument.h"
|
|
#include "nsIBindingManager.h"
|
|
#include "nsIScrollableFrame.h"
|
|
|
|
#define NEW_CONTEXT_PARENTAGE_INVARIANT
|
|
|
|
#ifdef NEW_CONTEXT_PARENTAGE_INVARIANT
|
|
|
|
#ifdef DEBUG
|
|
#undef NOISY_DEBUG
|
|
#else
|
|
#undef NOISY_DEBUG
|
|
#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
|
|
|
|
#endif // NEW_CONTEXT_PARENTAGE_INVARIANT
|
|
|
|
// Class IID's
|
|
static NS_DEFINE_IID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
|
|
|
|
// IID's
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// 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;
|
|
};
|
|
|
|
MOZ_DECL_CTOR_COUNTER(UndisplayedNode)
|
|
|
|
class UndisplayedNode {
|
|
public:
|
|
UndisplayedNode(nsIContent* aContent, nsIStyleContext* aStyle)
|
|
{
|
|
MOZ_COUNT_CTOR(UndisplayedNode);
|
|
mContent = aContent;
|
|
mStyle = aStyle;
|
|
NS_ADDREF(mStyle);
|
|
mNext = nsnull;
|
|
}
|
|
UndisplayedNode(nsIStyleContext* aPseudoStyle)
|
|
{
|
|
MOZ_COUNT_CTOR(UndisplayedNode);
|
|
mContent = nsnull;
|
|
mStyle = aPseudoStyle;
|
|
NS_ADDREF(mStyle);
|
|
mNext = nsnull;
|
|
}
|
|
~UndisplayedNode(void)
|
|
{
|
|
MOZ_COUNT_DTOR(UndisplayedNode);
|
|
NS_RELEASE(mStyle);
|
|
if (mNext) {
|
|
delete mNext;
|
|
}
|
|
}
|
|
|
|
nsIContent* mContent;
|
|
nsIStyleContext* mStyle;
|
|
UndisplayedNode* mNext;
|
|
};
|
|
|
|
class UndisplayedMap {
|
|
public:
|
|
UndisplayedMap(PRUint32 aNumBuckets = 16);
|
|
~UndisplayedMap(void);
|
|
|
|
UndisplayedNode* GetFirstNode(nsIContent* aParentContent);
|
|
|
|
nsresult AddNodeFor(nsIContent* aParentContent, nsIContent* aChild, nsIStyleContext* aStyle);
|
|
nsresult AddNodeFor(nsIContent* aParentContent, nsIStyleContext* aPseudoStyle);
|
|
|
|
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);
|
|
|
|
nsIFrame* mFrame; // the frame that can't be rendered
|
|
CantRenderReplacedElementEvent* mNext; // next event in the list
|
|
};
|
|
|
|
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, nsIStyleSet* 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(nsIPresContext* aPresContext, 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 SetPlaceholderFrameFor(nsIFrame* aFrame,
|
|
nsIFrame* aPlaceholderFrame);
|
|
NS_IMETHOD ClearPlaceholderFrameMap();
|
|
|
|
// Undisplayed content functions
|
|
NS_IMETHOD SetUndisplayedContent(nsIContent* aContent, nsIStyleContext* aStyleContext);
|
|
NS_IMETHOD SetUndisplayedPseudoIn(nsIStyleContext* aPseudoContext,
|
|
nsIContent* aParentContent);
|
|
NS_IMETHOD ClearUndisplayedContentIn(nsIContent* aContent, nsIContent* aParentContent);
|
|
NS_IMETHOD ClearAllUndisplayedContentIn(nsIContent* aParentContent);
|
|
NS_IMETHOD ClearUndisplayedContentMap();
|
|
|
|
// 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);
|
|
|
|
NS_IMETHOD CantRenderReplacedElement(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame);
|
|
|
|
NS_IMETHOD NotifyDestroyingFrame(nsIFrame* aFrame);
|
|
|
|
NS_IMETHOD ReParentStyleContext(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
nsIStyleContext* aNewParentContext);
|
|
NS_IMETHOD ComputeStyleChangeFor(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
PRInt32 aAttrNameSpaceID,
|
|
nsIAtom* aAttribute,
|
|
nsStyleChangeList& aChangeList,
|
|
PRInt32 aMinChange,
|
|
PRInt32& aTopLevelChange);
|
|
NS_IMETHOD AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent,
|
|
PRBool &aAffects);
|
|
|
|
// Capture state from the entire frame heirarchy and store in aState
|
|
NS_IMETHOD CaptureFrameState(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
nsILayoutHistoryState* aState);
|
|
NS_IMETHOD RestoreFrameState(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
nsILayoutHistoryState* aState);
|
|
// Add/restore state for one frame (special, global type, like scroll position)
|
|
NS_IMETHOD CaptureFrameStateFor(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
nsILayoutHistoryState* aState,
|
|
nsIStatefulFrame::SpecialStateID aID = nsIStatefulFrame::eNoID);
|
|
NS_IMETHOD RestoreFrameStateFor(nsIPresContext* aPresContext,
|
|
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,
|
|
NSFMPropertyDtorFunc aPropDtorFunc);
|
|
NS_IMETHOD RemoveFrameProperty(nsIFrame* aFrame,
|
|
nsIAtom* aPropertyName);
|
|
|
|
NS_IMETHOD GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aParent, nsIFrame* aChild, nsIFrame** aResult);
|
|
|
|
#ifdef NS_DEBUG
|
|
NS_IMETHOD DebugVerifyStyleTree(nsIPresContext* aPresContext, nsIFrame* aFrame);
|
|
#endif
|
|
|
|
private:
|
|
struct PropertyList {
|
|
nsCOMPtr<nsIAtom> mName; // property name
|
|
nsDST* mFrameValueMap; // map of frame/value pairs
|
|
NSFMPropertyDtorFunc mDtorFunc; // property specific value dtor function
|
|
PropertyList* mNext;
|
|
|
|
PropertyList(nsIAtom* aName,
|
|
NSFMPropertyDtorFunc aDtorFunc,
|
|
nsDST::NodeArena* aDSTNodeArena);
|
|
~PropertyList();
|
|
|
|
// Removes the property associated with the given frame, and destroys
|
|
// the property value
|
|
PRBool RemovePropertyForFrame(nsIPresContext* aPresContext, nsIFrame* aFrame);
|
|
|
|
// Remove and destroy all remaining properties
|
|
void RemoveAllProperties(nsIPresContext* aPresContext);
|
|
};
|
|
|
|
nsIPresShell* mPresShell; // weak link, because the pres shell owns us
|
|
nsIStyleSet* mStyleSet; // weak link. pres shell holds a reference
|
|
nsIFrame* mRootFrame;
|
|
nsDST::NodeArena* mDSTNodeArena; // weak link. DST owns
|
|
nsDST* mPrimaryFrameMap;
|
|
FrameHashTable* mPlaceholderMap;
|
|
UndisplayedMap* mUndisplayedMap;
|
|
CantRenderReplacedElementEvent* mPostedEvents;
|
|
PropertyList* mPropertyList;
|
|
|
|
void ReResolveStyleContext(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
nsIStyleContext* aParentContext,
|
|
nsIContent* aParentContent,
|
|
PRInt32 aAttrNameSpaceID,
|
|
nsIAtom* aAttribute,
|
|
nsStyleChangeList& aChangeList,
|
|
PRInt32 aMinChange,
|
|
PRInt32& 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);
|
|
};
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
NS_LAYOUT 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()
|
|
{
|
|
NS_INIT_REFCNT();
|
|
}
|
|
|
|
FrameManager::~FrameManager()
|
|
{
|
|
NS_ASSERTION(!mPresShell, "FrameManager::Destroy never called");
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS1(FrameManager, nsIFrameManager)
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::Init(nsIPresShell* aPresShell,
|
|
nsIStyleSet* aStyleSet)
|
|
{
|
|
NS_ASSERTION(aPresShell, "null aPresShell");
|
|
NS_ASSERTION(aStyleSet, "null aStyleSet");
|
|
|
|
mPresShell = aPresShell;
|
|
mStyleSet = aStyleSet;
|
|
|
|
// Allocate the node arena that's shared by all the DST objects
|
|
mDSTNodeArena = nsDST::NewMemoryArena();
|
|
if (!mDSTNodeArena) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
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
|
|
if (mRootFrame) {
|
|
mRootFrame->Destroy(presContext);
|
|
mRootFrame = nsnull;
|
|
}
|
|
|
|
delete mPrimaryFrameMap;
|
|
delete mPlaceholderMap;
|
|
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(nsIPresContext* aPresContext, nsIFrame** aCanvasFrame) const
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
NS_PRECONDITION(aCanvasFrame, "aCanvasFrame argument cannot be null");
|
|
NS_PRECONDITION(aPresContext, "aPresContext 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) {
|
|
nsCOMPtr<nsIAtom> frameType;
|
|
siblingFrame->GetFrameType(getter_AddRefs(frameType));
|
|
if (frameType.get() == nsLayoutAtoms::canvasFrame) {
|
|
// this is it: set the out-arg and stop looking
|
|
*aCanvasFrame = siblingFrame;
|
|
break;
|
|
} else {
|
|
siblingFrame->GetNextSibling(&siblingFrame);
|
|
}
|
|
}
|
|
// move on to the child's child
|
|
childFrame->FirstChild(aPresContext, nsnull, &childFrame);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Primary frame functions
|
|
NS_IMETHODIMP
|
|
FrameManager::GetPrimaryFrameFor(nsIContent* aContent, nsIFrame** aResult)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
NS_ENSURE_ARG_POINTER(aContent);
|
|
if (!aContent || !aResult) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
*aResult = nsnull; // initialize out param
|
|
|
|
nsresult rv;
|
|
if (mPrimaryFrameMap) {
|
|
mPrimaryFrameMap->Search(aContent, 0, (void**)aResult);
|
|
if (!*aResult) {
|
|
// 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<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));
|
|
NS_ASSERTION(styleSet, "bad style set");
|
|
mPresShell->GetPresContext(getter_AddRefs(presContext));
|
|
NS_ASSERTION(presContext, "bad presContext");
|
|
if (!styleSet || !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;
|
|
nsCOMPtr<nsIContent> prevSibling, parent;
|
|
rv = aContent->GetParent(*getter_AddRefs(parent));
|
|
if (NS_SUCCEEDED(rv) && parent)
|
|
{
|
|
PRInt32 index;
|
|
rv = parent->IndexOf(aContent, index);
|
|
if (NS_SUCCEEDED(rv) && index>0) // no use looking if it's the first child
|
|
{
|
|
rv = parent->ChildAt(index-1, *getter_AddRefs(prevSibling));
|
|
if (NS_SUCCEEDED(rv) && prevSibling)
|
|
{
|
|
mPrimaryFrameMap->Search(prevSibling.get(), 0, (void**)&hint.mPrimaryFrameForPrevSibling);
|
|
}
|
|
}
|
|
}
|
|
|
|
// walk the frame tree to find the frame that maps aContent.
|
|
// Use the hint if we have it.
|
|
styleSet->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) {
|
|
mPrimaryFrameMap->Remove(aContent);
|
|
}
|
|
} else {
|
|
// Create a new DST if necessary
|
|
if (!mPrimaryFrameMap) {
|
|
mPrimaryFrameMap = new nsDST(mDSTNodeArena);
|
|
if (!mPrimaryFrameMap) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
// Add a mapping to the hash table
|
|
mPrimaryFrameMap->Insert(aContent, (void*)aPrimaryFrame, nsnull);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::ClearPrimaryFrameMap()
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
if (mPrimaryFrameMap) {
|
|
mPrimaryFrameMap->Clear();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
// Placeholder frame functions
|
|
NS_IMETHODIMP
|
|
FrameManager::GetPlaceholderFrameFor(nsIFrame* aFrame,
|
|
nsIFrame** aResult) const
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
NS_ENSURE_ARG_POINTER(aFrame);
|
|
if (!aResult || !aFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
if (mPlaceholderMap) {
|
|
*aResult = (nsIFrame*)mPlaceholderMap->Get(aFrame);
|
|
} else {
|
|
*aResult = nsnull;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::SetPlaceholderFrameFor(nsIFrame* aFrame,
|
|
nsIFrame* aPlaceholderFrame)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
NS_ENSURE_ARG_POINTER(aFrame);
|
|
#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()
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
if (mPlaceholderMap) {
|
|
mPlaceholderMap->Clear();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::SetUndisplayedContent(nsIContent* aContent,
|
|
nsIStyleContext* aStyleContext)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
if (! mUndisplayedMap) {
|
|
mUndisplayedMap = new UndisplayedMap;
|
|
}
|
|
if (mUndisplayedMap) {
|
|
nsresult result = NS_OK;
|
|
nsIContent* parent = nsnull;
|
|
aContent->GetParent(parent);
|
|
if (parent) {
|
|
result = mUndisplayedMap->AddNodeFor(parent, aContent, aStyleContext);
|
|
NS_RELEASE(parent);
|
|
}
|
|
return result;
|
|
}
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::SetUndisplayedPseudoIn(nsIStyleContext* aPseudoContext,
|
|
nsIContent* aParentContent)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
if (! mUndisplayedMap) {
|
|
mUndisplayedMap = new UndisplayedMap;
|
|
}
|
|
if (mUndisplayedMap) {
|
|
return mUndisplayedMap->AddNodeFor(aParentContent, aPseudoContext);
|
|
}
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::ClearUndisplayedContentIn(nsIContent* aContent, nsIContent* aParentContent)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
if (mUndisplayedMap) {
|
|
UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent);
|
|
while (node) {
|
|
if (node->mContent == aContent) {
|
|
return mUndisplayedMap->RemoveNodeFor(aParentContent, node);
|
|
}
|
|
node = node->mNext;
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::ClearAllUndisplayedContentIn(nsIContent* aParentContent)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
if (mUndisplayedMap) {
|
|
return mUndisplayedMap->RemoveNodesFor(aParentContent);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::ClearUndisplayedContentMap()
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
if (mUndisplayedMap) {
|
|
mUndisplayedMap->Clear();
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::AppendFrames(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIFrame* aParentFrame,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
nsIFrame* insertionPoint = nsnull;
|
|
GetInsertionPoint(&aPresShell, aParentFrame, aFrameList, &insertionPoint);
|
|
if (insertionPoint) {
|
|
// First append the frames.
|
|
nsresult rv = insertionPoint->AppendFrames(aPresContext, aPresShell, aListName, aFrameList);
|
|
|
|
// Now reparent the style contexts to keep the frames in sync.
|
|
nsIFrame* walkit = aFrameList;
|
|
nsCOMPtr<nsIStyleContext> styleContext;
|
|
insertionPoint->GetStyleContext(getter_AddRefs(styleContext));
|
|
while (walkit) {
|
|
walkit->SetParent(insertionPoint);
|
|
aPresContext->ReParentStyleContext(walkit, styleContext);
|
|
walkit->GetNextSibling(&walkit);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
return aParentFrame->AppendFrames(aPresContext, aPresShell, aListName,
|
|
aFrameList);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::InsertFrames(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIFrame* aParentFrame,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aPrevFrame,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
nsIFrame* insertionPoint = nsnull;
|
|
GetInsertionPoint(&aPresShell, aParentFrame, aFrameList, &insertionPoint);
|
|
if (insertionPoint) {
|
|
// First insert the frames.
|
|
nsresult rv = insertionPoint->InsertFrames(aPresContext, aPresShell, aListName, aPrevFrame, aFrameList);
|
|
|
|
// Now reparent the style contexts to keep the frames in sync.
|
|
nsIFrame* walkit = aFrameList;
|
|
nsCOMPtr<nsIStyleContext> styleContext;
|
|
insertionPoint->GetStyleContext(getter_AddRefs(styleContext));
|
|
while (walkit) {
|
|
walkit->SetParent(insertionPoint);
|
|
aPresContext->ReParentStyleContext(walkit, styleContext);
|
|
walkit->GetNextSibling(&walkit);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
return aParentFrame->InsertFrames(aPresContext, aPresShell, aListName,
|
|
aPrevFrame, aFrameList);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::RemoveFrame(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIFrame* aParentFrame,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aOldFrame)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
nsIFrame* insertionPoint = nsnull;
|
|
GetInsertionPoint(&aPresShell, aParentFrame, aOldFrame, &insertionPoint);
|
|
if (insertionPoint)
|
|
return insertionPoint->RemoveFrame(aPresContext, aPresShell, aListName, aOldFrame);
|
|
|
|
return aParentFrame->RemoveFrame(aPresContext, aPresShell, aListName,
|
|
aOldFrame);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::ReplaceFrame(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIFrame* aParentFrame,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aOldFrame,
|
|
nsIFrame* aNewFrame)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
return aParentFrame->ReplaceFrame(aPresContext, aPresShell, 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);
|
|
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) {
|
|
// Removes the event and destroys it
|
|
PL_DequeueEvent(tmp, plqueue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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->mStyleSet->CantRenderReplacedElement(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)
|
|
{
|
|
PL_InitEvent(this, aFrameManager,
|
|
(PLHandleEventProc)&FrameManager::HandlePLEvent,
|
|
(PLDestroyEventProc)&FrameManager::DestroyPLEvent);
|
|
mFrame = aFrame;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::CantRenderReplacedElement(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
#ifdef NOISY_EVENTS
|
|
printf("%p FrameManager::CantRenderReplacedElement called\n", this);
|
|
#endif
|
|
nsIEventQueueService* eventService;
|
|
nsresult rv;
|
|
|
|
// We need to notify the style stystem, but post the notification so it
|
|
// doesn't happen now
|
|
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) {
|
|
#ifdef NS_DEBUG
|
|
// Verify that there isn't already a posted event associated with
|
|
// this frame
|
|
NS_ASSERTION(!*FindPostedEventFor(aFrame), "frame already has posted event");
|
|
#endif
|
|
CantRenderReplacedElementEvent* ev;
|
|
|
|
// Create a new event
|
|
ev = new CantRenderReplacedElementEvent(this, aFrame);
|
|
|
|
// Add the event to our linked list of posted events
|
|
ev->mNext = mPostedEvents;
|
|
mPostedEvents = ev;
|
|
|
|
// Post the event
|
|
eventQueue->PostEvent(ev);
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
#ifdef NS_DEBUG
|
|
static void
|
|
DumpContext(nsIFrame* aFrame, nsIStyleContext* 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(name, stdout);
|
|
}
|
|
fprintf(stdout, " (%p)", NS_STATIC_CAST(void*, aFrame));
|
|
}
|
|
if (aContext) {
|
|
fprintf(stdout, " style: %p ", NS_STATIC_CAST(void*, aContext));
|
|
|
|
nsIAtom* pseudoTag;
|
|
aContext->GetPseudoType(pseudoTag);
|
|
if (pseudoTag) {
|
|
nsAutoString buffer;
|
|
pseudoTag->ToString(buffer);
|
|
fputs(buffer, stdout);
|
|
fputs(" ", stdout);
|
|
NS_RELEASE(pseudoTag);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef NEW_CONTEXT_PARENTAGE_INVARIANT
|
|
static void
|
|
VerifyContextParent(nsIPresContext* aPresContext, nsIFrame* aFrame,
|
|
nsIStyleContext* aContext, nsIStyleContext* aParentContext)
|
|
{
|
|
nsIAtom* frameType;
|
|
|
|
// get the contexts not provided
|
|
if (!aContext) {
|
|
aFrame->GetStyleContext(&aContext);
|
|
} else {
|
|
// addref here so we can release at end
|
|
NS_ADDREF(aContext);
|
|
}
|
|
|
|
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
|
|
aFrame->GetFrameType(&frameType);
|
|
if (nsLayoutAtoms::placeholderFrame == frameType) { // placeholder
|
|
// get out of flow frame's context as the parent context
|
|
nsIFrame* outOfFlowFrame = ((nsPlaceholderFrame*)aFrame)->GetOutOfFlowFrame();
|
|
NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
|
|
outOfFlowFrame->GetStyleContext(&aParentContext);
|
|
} else {
|
|
// get the parent context from the frame (indirectly)
|
|
nsIFrame* providerFrame = nsnull;
|
|
nsContextProviderRelationship relationship;
|
|
aFrame->GetParentStyleContextProvider(aPresContext,&providerFrame,relationship);
|
|
if (providerFrame) {
|
|
providerFrame->GetStyleContext(&aParentContext);
|
|
} else {
|
|
// no parent context provider: it is OK, some frames' contexts do not have parents
|
|
}
|
|
}
|
|
NS_IF_RELEASE(frameType);
|
|
} else {
|
|
// addref the parent context so we can release it at end
|
|
NS_ADDREF(aParentContext);
|
|
}
|
|
|
|
NS_ASSERTION(aContext, "Failure to get required contexts");
|
|
nsIStyleContext* 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);
|
|
fputs("\n", stdout);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (actualParentContext) {
|
|
DumpContext(aFrame, aContext);
|
|
fputs("Has parent context: ", stdout);
|
|
DumpContext(nsnull, actualParentContext);
|
|
fputs("Should be null\n\n", stdout);
|
|
}
|
|
}
|
|
|
|
NS_IF_RELEASE(actualParentContext);
|
|
NS_IF_RELEASE(aParentContext);
|
|
NS_IF_RELEASE(aContext);
|
|
}
|
|
|
|
static void
|
|
VerifyStyleTree(nsIPresContext* aPresContext, nsIFrame* aFrame, nsIStyleContext* aParentContext)
|
|
{
|
|
nsIStyleContext* context;
|
|
aFrame->GetStyleContext(&context);
|
|
|
|
VerifyContextParent(aPresContext, aFrame, context, aParentContext);
|
|
|
|
PRInt32 listIndex = 0;
|
|
nsIAtom* childList = nsnull;
|
|
nsIFrame* child;
|
|
nsIAtom* frameType;
|
|
|
|
do {
|
|
child = nsnull;
|
|
nsresult result = aFrame->FirstChild(aPresContext, childList, &child);
|
|
while ((NS_SUCCEEDED(result)) && child) {
|
|
nsFrameState state;
|
|
child->GetFrameState(&state);
|
|
if (NS_FRAME_OUT_OF_FLOW != (state & NS_FRAME_OUT_OF_FLOW)) {
|
|
// only do frames that are in flow
|
|
child->GetFrameType(&frameType);
|
|
if (nsLayoutAtoms::placeholderFrame == frameType) {
|
|
// 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");
|
|
nsIStyleContext* outOfFlowContext;
|
|
|
|
// recurse to out of flow frame, letting the parent context get resolved
|
|
VerifyStyleTree(aPresContext, outOfFlowFrame, nsnull);
|
|
|
|
// verify placeholder using the out of flow's context as parent context
|
|
outOfFlowFrame->GetStyleContext(&outOfFlowContext);
|
|
VerifyContextParent(aPresContext,child, nsnull, outOfFlowContext);
|
|
NS_IF_RELEASE(outOfFlowContext);
|
|
}
|
|
else { // regular frame
|
|
VerifyStyleTree(aPresContext, child, nsnull);
|
|
}
|
|
NS_IF_RELEASE(frameType);
|
|
}
|
|
child->GetNextSibling(&child);
|
|
}
|
|
|
|
NS_IF_RELEASE(childList);
|
|
aFrame->GetAdditionalChildListName(listIndex++, &childList);
|
|
} while (childList);
|
|
|
|
// do additional contexts
|
|
PRInt32 contextIndex = -1;
|
|
while (1 == 1) {
|
|
nsIStyleContext* extraContext = nsnull;
|
|
aFrame->GetAdditionalStyleContext(++contextIndex, &extraContext);
|
|
if (extraContext) {
|
|
VerifyContextParent(aPresContext, aFrame, extraContext, context);
|
|
NS_RELEASE(extraContext);
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
NS_RELEASE(context);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::DebugVerifyStyleTree(nsIPresContext* aPresContext, nsIFrame* aFrame)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
if (aFrame) {
|
|
nsIStyleContext* context;
|
|
aFrame->GetStyleContext(&context);
|
|
nsIStyleContext* parentContext = context->GetParent();
|
|
VerifyStyleTree(aPresContext, aFrame, parentContext);
|
|
NS_IF_RELEASE(parentContext);
|
|
NS_RELEASE(context);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
#else // NEW_CONTEXT_PARENTAGE_INVARIANT
|
|
|
|
static void
|
|
VerifyContextParent(nsIFrame* aFrame, nsIStyleContext* aContext, nsIStyleContext* aParentContext)
|
|
{
|
|
nsIStyleContext* 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);
|
|
fputs("\n", stdout);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (actualParentContext) {
|
|
DumpContext(aFrame, aContext);
|
|
fputs("Has parent context: ", stdout);
|
|
DumpContext(nsnull, actualParentContext);
|
|
fputs("Should be null\n\n", stdout);
|
|
}
|
|
}
|
|
NS_IF_RELEASE(actualParentContext);
|
|
}
|
|
|
|
static void
|
|
VerifyContextParent(nsIFrame* aFrame, nsIStyleContext* aParentContext)
|
|
{
|
|
nsIStyleContext* context;
|
|
aFrame->GetStyleContext(&context);
|
|
VerifyContextParent(aFrame, context, aParentContext);
|
|
NS_RELEASE(context);
|
|
}
|
|
|
|
static void
|
|
VerifyStyleTree(nsIPresContext* aPresContext, nsIFrame* aFrame, nsIStyleContext* aParentContext)
|
|
{
|
|
nsIStyleContext* context;
|
|
aFrame->GetStyleContext(&context);
|
|
|
|
VerifyContextParent(aFrame, aParentContext);
|
|
|
|
PRInt32 listIndex = 0;
|
|
nsIAtom* childList = nsnull;
|
|
nsIFrame* child;
|
|
nsIAtom* frameType;
|
|
|
|
do {
|
|
child = nsnull;
|
|
nsresult result = aFrame->FirstChild(aPresContext, childList, &child);
|
|
while ((NS_SUCCEEDED(result)) && child) {
|
|
nsFrameState state;
|
|
child->GetFrameState(&state);
|
|
if (NS_FRAME_OUT_OF_FLOW != (state & NS_FRAME_OUT_OF_FLOW)) {
|
|
// only do frames that are in flow
|
|
child->GetFrameType(&frameType);
|
|
if (nsLayoutAtoms::placeholderFrame == frameType) { // placeholder
|
|
// get out of flow frame and recurse there
|
|
nsIFrame* outOfFlowFrame = ((nsPlaceholderFrame*)child)->GetOutOfFlowFrame();
|
|
NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
|
|
|
|
nsIStyleContext* outOfFlowContext;
|
|
outOfFlowFrame->GetStyleContext(&outOfFlowContext);
|
|
VerifyContextParent(child, outOfFlowContext);
|
|
NS_RELEASE(outOfFlowContext);
|
|
|
|
VerifyStyleTree(aPresContext, outOfFlowFrame, context);
|
|
}
|
|
else { // regular frame
|
|
VerifyStyleTree(aPresContext, child, context);
|
|
}
|
|
NS_IF_RELEASE(frameType);
|
|
}
|
|
|
|
child->GetNextSibling(&child);
|
|
}
|
|
|
|
NS_IF_RELEASE(childList);
|
|
aFrame->GetAdditionalChildListName(listIndex++, &childList);
|
|
} while (childList);
|
|
|
|
// do additional contexts
|
|
PRInt32 contextIndex = -1;
|
|
while (1 == 1) {
|
|
nsIStyleContext* extraContext = nsnull;
|
|
aFrame->GetAdditionalStyleContext(++contextIndex, &extraContext);
|
|
if (extraContext) {
|
|
VerifyContextParent(aFrame, extraContext, context);
|
|
NS_RELEASE(extraContext);
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
NS_RELEASE(context);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::DebugVerifyStyleTree(nsIPresContext* aPresContext, nsIFrame* aFrame)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
if (aFrame) {
|
|
nsIStyleContext* context;
|
|
aFrame->GetStyleContext(&context);
|
|
nsIStyleContext* parentContext = context->GetParent();
|
|
VerifyStyleTree(aPresContext, aFrame, parentContext);
|
|
NS_IF_RELEASE(parentContext);
|
|
NS_RELEASE(context);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
#endif //NEW_CONTEXT_PARENTAGE_INVARIANT
|
|
#endif // DEBUG
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::ReParentStyleContext(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
nsIStyleContext* aNewParentContext)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
nsresult result = NS_ERROR_NULL_POINTER;
|
|
if (aFrame) {
|
|
#ifdef NS_DEBUG
|
|
DebugVerifyStyleTree(aPresContext, aFrame);
|
|
#endif
|
|
|
|
nsIStyleContext* oldContext = nsnull;
|
|
aFrame->GetStyleContext(&oldContext);
|
|
|
|
if (oldContext) {
|
|
nsIStyleContext* newContext = nsnull;
|
|
result = mStyleSet->ReParentStyleContext(aPresContext,
|
|
oldContext, aNewParentContext,
|
|
&newContext);
|
|
if (NS_SUCCEEDED(result) && newContext) {
|
|
if (newContext != oldContext) {
|
|
PRInt32 listIndex = 0;
|
|
nsIAtom* childList = nsnull;
|
|
nsIFrame* child;
|
|
nsIAtom* frameType;
|
|
|
|
do {
|
|
child = nsnull;
|
|
result = aFrame->FirstChild(aPresContext, childList, &child);
|
|
while ((NS_SUCCEEDED(result)) && child) {
|
|
nsFrameState state;
|
|
child->GetFrameState(&state);
|
|
if (NS_FRAME_OUT_OF_FLOW != (state & NS_FRAME_OUT_OF_FLOW)) {
|
|
// only do frames that are in flow
|
|
child->GetFrameType(&frameType);
|
|
if (nsLayoutAtoms::placeholderFrame == frameType) { // placeholder
|
|
// get out of flow frame and recurse there
|
|
nsIFrame* outOfFlowFrame = ((nsPlaceholderFrame*)child)->GetOutOfFlowFrame();
|
|
NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
|
|
|
|
result = ReParentStyleContext(aPresContext, outOfFlowFrame, newContext);
|
|
|
|
// reparent placeholder's context under out of flow frame
|
|
nsIStyleContext* outOfFlowContext;
|
|
outOfFlowFrame->GetStyleContext(&outOfFlowContext);
|
|
ReParentStyleContext(aPresContext, child, outOfFlowContext);
|
|
NS_RELEASE(outOfFlowContext);
|
|
}
|
|
else { // regular frame
|
|
result = ReParentStyleContext(aPresContext, child, newContext);
|
|
}
|
|
NS_IF_RELEASE(frameType);
|
|
}
|
|
|
|
child->GetNextSibling(&child);
|
|
}
|
|
|
|
NS_IF_RELEASE(childList);
|
|
aFrame->GetAdditionalChildListName(listIndex++, &childList);
|
|
} while (childList);
|
|
|
|
aFrame->SetStyleContext(aPresContext, newContext);
|
|
|
|
// do additional contexts
|
|
PRInt32 contextIndex = -1;
|
|
while (1 == 1) {
|
|
nsIStyleContext* oldExtraContext = nsnull;
|
|
result = aFrame->GetAdditionalStyleContext(++contextIndex, &oldExtraContext);
|
|
if (NS_SUCCEEDED(result)) {
|
|
if (oldExtraContext) {
|
|
nsIStyleContext* newExtraContext = nsnull;
|
|
result = mStyleSet->ReParentStyleContext(aPresContext,
|
|
oldExtraContext, newContext,
|
|
&newExtraContext);
|
|
if (NS_SUCCEEDED(result) && newExtraContext) {
|
|
aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
|
|
NS_RELEASE(newExtraContext);
|
|
}
|
|
NS_RELEASE(oldExtraContext);
|
|
}
|
|
}
|
|
else {
|
|
result = NS_OK; // ok not to have extras (or run out)
|
|
break;
|
|
}
|
|
}
|
|
#ifdef NS_DEBUG
|
|
VerifyStyleTree(aPresContext, aFrame, aNewParentContext);
|
|
#endif
|
|
}
|
|
NS_RELEASE(newContext);
|
|
}
|
|
NS_RELEASE(oldContext);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static PRBool
|
|
HasAttributeContent(nsIStyleContext* aStyleContext,
|
|
PRInt32 aNameSpaceID,
|
|
nsIAtom* aAttribute)
|
|
{
|
|
PRBool result = PR_FALSE;
|
|
if (aStyleContext) {
|
|
const nsStyleContent* content = (const nsStyleContent*)aStyleContext->GetStyleData(eStyleStruct_Content);
|
|
PRUint32 count = content->ContentCount();
|
|
while ((0 < count) && (! result)) {
|
|
nsStyleContentType contentType;
|
|
nsAutoString contentString;
|
|
content->GetContentAt(--count, contentType, contentString);
|
|
if (eStyleContentType_Attr == contentType) {
|
|
nsIAtom* attrName = nsnull;
|
|
PRInt32 attrNameSpace = kNameSpaceID_None;
|
|
PRInt32 barIndex = contentString.FindChar('|'); // CSS namespace delimiter
|
|
if (-1 != barIndex) {
|
|
nsAutoString nameSpaceVal;
|
|
contentString.Left(nameSpaceVal, barIndex);
|
|
PRInt32 error;
|
|
attrNameSpace = nameSpaceVal.ToInteger(&error, 10);
|
|
contentString.Cut(0, barIndex + 1);
|
|
if (contentString.Length()) {
|
|
attrName = NS_NewAtom(contentString);
|
|
}
|
|
}
|
|
else {
|
|
attrName = NS_NewAtom(contentString);
|
|
}
|
|
if ((attrName == aAttribute) &&
|
|
((attrNameSpace == aNameSpaceID) ||
|
|
(attrNameSpace == kNameSpaceID_Unknown))) {
|
|
result = PR_TRUE;
|
|
}
|
|
NS_IF_RELEASE(attrName);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
static PRInt32
|
|
CaptureChange(nsIStyleContext* aOldContext, nsIStyleContext* aNewContext,
|
|
nsIFrame* aFrame, nsIContent* aContent,
|
|
nsStyleChangeList& aChangeList, PRInt32 aMinChange)
|
|
{
|
|
PRInt32 ourChange = NS_STYLE_HINT_NONE;
|
|
aNewContext->CalcStyleDifference(aOldContext, ourChange);
|
|
if (aMinChange < ourChange) {
|
|
aChangeList.AppendChange(aFrame, aContent, ourChange);
|
|
aMinChange = ourChange;
|
|
}
|
|
return aMinChange;
|
|
}
|
|
|
|
#ifdef NEW_CONTEXT_PARENTAGE_INVARIANT
|
|
|
|
void
|
|
FrameManager::ReResolveStyleContext(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
nsIStyleContext* aParentContext,
|
|
nsIContent* aParentContent,
|
|
PRInt32 aAttrNameSpaceID,
|
|
nsIAtom* aAttribute,
|
|
nsStyleChangeList& aChangeList,
|
|
PRInt32 aMinChange,
|
|
PRInt32& aResultChange)
|
|
{
|
|
nsIFrame *resolvedDescendant = nsnull;
|
|
|
|
nsIStyleContext* oldContext = nsnull;
|
|
nsresult result = aFrame->GetStyleContext(&oldContext);
|
|
if (NS_SUCCEEDED(result) && oldContext) {
|
|
nsIAtom* pseudoTag = nsnull;
|
|
oldContext->GetPseudoType(pseudoTag);
|
|
nsIContent* localContent = nsnull;
|
|
nsIContent* content = nsnull;
|
|
result = aFrame->GetContent(&localContent);
|
|
if (NS_SUCCEEDED(result) && localContent) {
|
|
content = localContent;
|
|
}
|
|
else {
|
|
content = aParentContent;
|
|
}
|
|
if (aParentContent && aAttribute) { // attribute came from parent, we don't care about it here when recursing
|
|
nsFrameState frameState;
|
|
aFrame->GetFrameState(&frameState);
|
|
if (0 == (frameState & NS_FRAME_GENERATED_CONTENT)) { // keep it for generated content
|
|
aAttribute = nsnull;
|
|
}
|
|
}
|
|
|
|
if (aParentContext == nsnull) {
|
|
NOISY_TRACE_FRAME("null parent context in ReResolveStyle: getting provider frame for",aFrame);
|
|
// get the parent context from the frame (indirectly)
|
|
nsIFrame* providerFrame = nsnull;
|
|
nsContextProviderRelationship relationship;
|
|
aFrame->GetParentStyleContextProvider(aPresContext,&providerFrame,relationship);
|
|
if (providerFrame) {
|
|
NOISY_TRACE("provider frame retrieved in ReResolveStyle: ");
|
|
|
|
// see if we need to recurse and resolve the provider frame first
|
|
if (relationship != eContextProvider_Ancestor) {
|
|
NOISY_TRACE("non-ancestor provider, recursing.\n");
|
|
// provider is not an ancestor, so assume we have to reresolve it first
|
|
ReResolveStyleContext(aPresContext, providerFrame, nsnull, content,
|
|
aAttrNameSpaceID, aAttribute,
|
|
aChangeList, aMinChange, aResultChange);
|
|
resolvedDescendant = providerFrame;
|
|
NOISY_TRACE("returned from recursion, descendant parent context provider resolved.\n");
|
|
} else {
|
|
NOISY_TRACE("ancestor provider, assuming already resolved.\n");
|
|
}
|
|
providerFrame->GetStyleContext(&aParentContext);
|
|
}
|
|
} else {
|
|
// addref the parent passed in so we can release it at the end
|
|
NS_ADDREF(aParentContext);
|
|
NOISY_TRACE_FRAME("non-null parent context provided: using it and assuming already resolved",aFrame);
|
|
}
|
|
NS_ASSERTION(aParentContext, "Failed to resolve parent context");
|
|
|
|
// do primary context
|
|
nsIStyleContext* newContext = nsnull;
|
|
if (pseudoTag) {
|
|
nsIContent* pseudoContent = (aParentContent ? aParentContent : localContent);
|
|
aPresContext->ResolvePseudoStyleContextFor(pseudoContent, pseudoTag, aParentContext, PR_FALSE,
|
|
&newContext);
|
|
NS_RELEASE(pseudoTag);
|
|
}
|
|
else {
|
|
NS_ASSERTION(localContent, "non pseudo-element frame without content node");
|
|
aPresContext->ResolveStyleContextFor(content, aParentContext, PR_TRUE, &newContext);
|
|
}
|
|
NS_ASSERTION(newContext, "failed to get new style context");
|
|
if (newContext) {
|
|
if (newContext != oldContext) {
|
|
aMinChange = CaptureChange(oldContext, newContext, aFrame, content, aChangeList, aMinChange);
|
|
if (aMinChange < NS_STYLE_HINT_FRAMECHANGE) { // 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
|
|
nsStyleColor oldColor;
|
|
nsStyleColor newColor;
|
|
oldContext->GetStyle(eStyleStruct_Color, (nsStyleColor &)oldColor);
|
|
newContext->GetStyle(eStyleStruct_Color, (nsStyleColor &)newColor);
|
|
if(oldColor.mBackgroundImage.Length() > 0 &&
|
|
oldColor.mBackgroundImage != newColor.mBackgroundImage ){
|
|
// stop the image loading for the frame, the image has changed
|
|
aPresContext->StopAllLoadImagesFor(aFrame, aFrame);
|
|
}
|
|
}
|
|
else {
|
|
oldContext->RemapStyle(aPresContext, PR_FALSE);
|
|
if (aAttribute && (aMinChange < NS_STYLE_HINT_REFLOW) &&
|
|
HasAttributeContent(oldContext, aAttrNameSpaceID, aAttribute)) {
|
|
aChangeList.AppendChange(aFrame, content, NS_STYLE_HINT_REFLOW);
|
|
}
|
|
}
|
|
NS_RELEASE(oldContext);
|
|
}
|
|
else {
|
|
NS_ERROR("resolve style context failed");
|
|
newContext = oldContext; // new context failed, recover... (take ref)
|
|
}
|
|
|
|
// do additional contexts
|
|
PRInt32 contextIndex = -1;
|
|
while (1 == 1) {
|
|
nsIStyleContext* oldExtraContext = nsnull;
|
|
result = aFrame->GetAdditionalStyleContext(++contextIndex, &oldExtraContext);
|
|
if (NS_SUCCEEDED(result)) {
|
|
if (oldExtraContext) {
|
|
nsIStyleContext* newExtraContext = nsnull;
|
|
oldExtraContext->GetPseudoType(pseudoTag);
|
|
NS_ASSERTION(pseudoTag, "extra style context is not pseudo element");
|
|
result = aPresContext->ResolvePseudoStyleContextFor(content, pseudoTag, newContext,
|
|
PR_FALSE, &newExtraContext);
|
|
NS_RELEASE(pseudoTag);
|
|
if (NS_SUCCEEDED(result) && newExtraContext) {
|
|
if (oldExtraContext != newExtraContext) {
|
|
aMinChange = CaptureChange(oldExtraContext, newExtraContext, aFrame,
|
|
content, aChangeList, aMinChange);
|
|
if (aMinChange < NS_STYLE_HINT_FRAMECHANGE) {
|
|
aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
|
|
}
|
|
}
|
|
else {
|
|
oldExtraContext->RemapStyle(aPresContext, PR_FALSE);
|
|
if (aAttribute && (aMinChange < NS_STYLE_HINT_REFLOW) &&
|
|
HasAttributeContent(oldContext, aAttrNameSpaceID, aAttribute)) {
|
|
aChangeList.AppendChange(aFrame, content, NS_STYLE_HINT_REFLOW);
|
|
}
|
|
}
|
|
NS_RELEASE(newExtraContext);
|
|
}
|
|
NS_RELEASE(oldExtraContext);
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// now look for undisplayed child content and pseudos
|
|
if (localContent && mUndisplayedMap) {
|
|
UndisplayedNode* undisplayed = mUndisplayedMap->GetFirstNode(localContent);
|
|
while (undisplayed) {
|
|
nsIStyleContext* undisplayedContext = nsnull;
|
|
undisplayed->mStyle->GetPseudoType(pseudoTag);
|
|
if (undisplayed->mContent && pseudoTag == nsnull) { // child content
|
|
aPresContext->ResolveStyleContextFor(undisplayed->mContent, newContext,
|
|
PR_TRUE, &undisplayedContext);
|
|
}
|
|
else { // pseudo element
|
|
NS_ASSERTION(pseudoTag, "pseudo element without tag");
|
|
aPresContext->ResolvePseudoStyleContextFor(localContent, pseudoTag, newContext, PR_FALSE,
|
|
&undisplayedContext);
|
|
}
|
|
NS_IF_RELEASE(pseudoTag);
|
|
if (undisplayedContext) {
|
|
if (undisplayedContext == undisplayed->mStyle) {
|
|
undisplayedContext->RemapStyle(aPresContext);
|
|
}
|
|
const nsStyleDisplay* display =
|
|
(const nsStyleDisplay*)undisplayedContext->GetStyleData(eStyleStruct_Display);
|
|
if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {
|
|
aChangeList.AppendChange(nsnull, ((undisplayed->mContent) ? undisplayed->mContent : localContent),
|
|
NS_STYLE_HINT_FRAMECHANGE);
|
|
}
|
|
NS_RELEASE(undisplayedContext);
|
|
}
|
|
undisplayed = undisplayed->mNext;
|
|
}
|
|
}
|
|
|
|
aResultChange = aMinChange;
|
|
|
|
// now do children
|
|
PRInt32 listIndex = 0;
|
|
nsIAtom* childList = nsnull;
|
|
PRInt32 childChange;
|
|
nsIFrame* child;
|
|
|
|
do {
|
|
child = nsnull;
|
|
result = aFrame->FirstChild(aPresContext, childList, &child);
|
|
while ((NS_SUCCEEDED(result)) && (child)) {
|
|
nsFrameState state;
|
|
child->GetFrameState(&state);
|
|
if (NS_FRAME_OUT_OF_FLOW != (state & NS_FRAME_OUT_OF_FLOW)) {
|
|
// only do frames that are in flow
|
|
nsCOMPtr<nsIAtom> frameType;
|
|
child->GetFrameType(getter_AddRefs(frameType));
|
|
if (nsLayoutAtoms::placeholderFrame == frameType.get()) { // placeholder
|
|
// get out of flow frame and recurse there
|
|
nsIFrame* outOfFlowFrame = ((nsPlaceholderFrame*)child)->GetOutOfFlowFrame();
|
|
NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
|
|
|
|
if (outOfFlowFrame != resolvedDescendant) {
|
|
ReResolveStyleContext(aPresContext, outOfFlowFrame, nsnull, content,
|
|
aAttrNameSpaceID, aAttribute,
|
|
aChangeList, aMinChange, childChange);
|
|
} else {
|
|
NOISY_TRACE("out of flow frame already resolved as descendent\n");
|
|
}
|
|
|
|
// reresolve placeholder's context under out of flow frame
|
|
nsIStyleContext* outOfFlowContext;
|
|
outOfFlowFrame->GetStyleContext(&outOfFlowContext);
|
|
ReResolveStyleContext(aPresContext, child, outOfFlowContext, content,
|
|
kNameSpaceID_Unknown, nsnull,
|
|
aChangeList, aMinChange, childChange);
|
|
NS_RELEASE(outOfFlowContext);
|
|
}
|
|
else { // regular child frame
|
|
if (child != resolvedDescendant) {
|
|
ReResolveStyleContext(aPresContext, child, nsnull, content,
|
|
aAttrNameSpaceID, aAttribute,
|
|
aChangeList, aMinChange, childChange);
|
|
} else {
|
|
NOISY_TRACE_FRAME("child frame already resolved as descendent, skipping",aFrame);
|
|
}
|
|
}
|
|
}
|
|
child->GetNextSibling(&child);
|
|
}
|
|
|
|
NS_IF_RELEASE(childList);
|
|
aFrame->GetAdditionalChildListName(listIndex++, &childList);
|
|
} while (childList);
|
|
// XXX need to do overflow frames???
|
|
|
|
NS_RELEASE(newContext);
|
|
NS_IF_RELEASE(localContent);
|
|
NS_IF_RELEASE(aParentContext);
|
|
}
|
|
}
|
|
|
|
#else // NEW_CONTEXT_PARENTAGE_INVARIANT
|
|
|
|
void
|
|
FrameManager::ReResolveStyleContext(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
nsIStyleContext* aParentContext,
|
|
nsIContent* aParentContent,
|
|
PRInt32 aAttrNameSpaceID,
|
|
nsIAtom* aAttribute,
|
|
nsStyleChangeList& aChangeList,
|
|
PRInt32 aMinChange,
|
|
PRInt32& aResultChange)
|
|
{
|
|
nsIStyleContext* oldContext = nsnull;
|
|
nsresult result = aFrame->GetStyleContext(&oldContext);
|
|
if (NS_SUCCEEDED(result) && oldContext) {
|
|
nsIAtom* pseudoTag = nsnull;
|
|
oldContext->GetPseudoType(pseudoTag);
|
|
nsIContent* localContent = nsnull;
|
|
nsIContent* content = nsnull;
|
|
result = aFrame->GetContent(&localContent);
|
|
if (NS_SUCCEEDED(result) && localContent) {
|
|
content = localContent;
|
|
}
|
|
else {
|
|
content = aParentContent;
|
|
}
|
|
if (aParentContent && aAttribute) { // attribute came from parent, we don't care about it here when recursing
|
|
nsFrameState frameState;
|
|
aFrame->GetFrameState(&frameState);
|
|
if (0 == (frameState & NS_FRAME_GENERATED_CONTENT)) { // keep it for generated content
|
|
aAttribute = nsnull;
|
|
}
|
|
}
|
|
|
|
// do primary context
|
|
nsIStyleContext* newContext = nsnull;
|
|
if (pseudoTag) {
|
|
aPresContext->ResolvePseudoStyleContextFor(aParentContent, pseudoTag, aParentContext, PR_FALSE,
|
|
&newContext);
|
|
NS_RELEASE(pseudoTag);
|
|
}
|
|
else {
|
|
NS_ASSERTION(localContent, "non pseudo-element frame without content node");
|
|
aPresContext->ResolveStyleContextFor(content, aParentContext, PR_TRUE, &newContext);
|
|
}
|
|
NS_ASSERTION(newContext, "failed to get new style context");
|
|
if (newContext) {
|
|
if (newContext != oldContext) {
|
|
aMinChange = CaptureChange(oldContext, newContext, aFrame, content, aChangeList, aMinChange);
|
|
if (aMinChange < NS_STYLE_HINT_FRAMECHANGE) { // 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
|
|
nsStyleColor oldColor;
|
|
nsStyleColor newColor;
|
|
oldContext->GetStyle(eStyleStruct_Color, (nsStyleColor &)oldColor);
|
|
newContext->GetStyle(eStyleStruct_Color, (nsStyleColor &)newColor);
|
|
if(oldColor.mBackgroundImage.Length() > 0 &&
|
|
oldColor.mBackgroundImage != newColor.mBackgroundImage ){
|
|
// stop the image loading for the frame, the image has changed
|
|
aPresContext->StopAllLoadImagesFor(aFrame, aFrame);
|
|
}
|
|
}
|
|
else {
|
|
oldContext->RemapStyle(aPresContext, PR_FALSE);
|
|
if (aAttribute && (aMinChange < NS_STYLE_HINT_REFLOW) &&
|
|
HasAttributeContent(oldContext, aAttrNameSpaceID, aAttribute)) {
|
|
aChangeList.AppendChange(aFrame, content, NS_STYLE_HINT_REFLOW);
|
|
}
|
|
}
|
|
NS_RELEASE(oldContext);
|
|
}
|
|
else {
|
|
NS_ERROR("resolve style context failed");
|
|
newContext = oldContext; // new context failed, recover... (take ref)
|
|
}
|
|
|
|
// do additional contexts
|
|
PRInt32 contextIndex = -1;
|
|
while (1 == 1) {
|
|
nsIStyleContext* oldExtraContext = nsnull;
|
|
result = aFrame->GetAdditionalStyleContext(++contextIndex, &oldExtraContext);
|
|
if (NS_SUCCEEDED(result)) {
|
|
if (oldExtraContext) {
|
|
nsIStyleContext* newExtraContext = nsnull;
|
|
oldExtraContext->GetPseudoType(pseudoTag);
|
|
NS_ASSERTION(pseudoTag, "extra style context is not pseudo element");
|
|
result = aPresContext->ResolvePseudoStyleContextFor(content, pseudoTag, newContext,
|
|
PR_FALSE, &newExtraContext);
|
|
NS_RELEASE(pseudoTag);
|
|
if (NS_SUCCEEDED(result) && newExtraContext) {
|
|
if (oldExtraContext != newExtraContext) {
|
|
aMinChange = CaptureChange(oldExtraContext, newExtraContext, aFrame,
|
|
content, aChangeList, aMinChange);
|
|
if (aMinChange < NS_STYLE_HINT_FRAMECHANGE) {
|
|
aFrame->SetAdditionalStyleContext(contextIndex, newExtraContext);
|
|
}
|
|
}
|
|
else {
|
|
oldExtraContext->RemapStyle(aPresContext, PR_FALSE);
|
|
if (aAttribute && (aMinChange < NS_STYLE_HINT_REFLOW) &&
|
|
HasAttributeContent(oldContext, aAttrNameSpaceID, aAttribute)) {
|
|
aChangeList.AppendChange(aFrame, content, NS_STYLE_HINT_REFLOW);
|
|
}
|
|
}
|
|
NS_RELEASE(newExtraContext);
|
|
}
|
|
NS_RELEASE(oldExtraContext);
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// now look for undisplayed child content and pseudos
|
|
if (localContent && mUndisplayedMap) {
|
|
UndisplayedNode* undisplayed = mUndisplayedMap->GetFirstNode(localContent);
|
|
while (undisplayed) {
|
|
nsIStyleContext* undisplayedContext = nsnull;
|
|
undisplayed->mStyle->GetPseudoType(pseudoTag);
|
|
if (undisplayed->mContent && pseudoTag == nsnull) { // child content
|
|
aPresContext->ResolveStyleContextFor(undisplayed->mContent, newContext,
|
|
PR_TRUE, &undisplayedContext);
|
|
}
|
|
else { // pseudo element
|
|
NS_ASSERTION(pseudoTag, "pseudo element without tag");
|
|
aPresContext->ResolvePseudoStyleContextFor(localContent, pseudoTag, newContext, PR_FALSE,
|
|
&undisplayedContext);
|
|
}
|
|
NS_IF_RELEASE(pseudoTag);
|
|
if (undisplayedContext) {
|
|
if (undisplayedContext == undisplayed->mStyle) {
|
|
undisplayedContext->RemapStyle(aPresContext);
|
|
}
|
|
const nsStyleDisplay* display =
|
|
(const nsStyleDisplay*)undisplayedContext->GetStyleData(eStyleStruct_Display);
|
|
if (display->mDisplay != NS_STYLE_DISPLAY_NONE) {
|
|
aChangeList.AppendChange(nsnull, ((undisplayed->mContent) ? undisplayed->mContent : localContent),
|
|
NS_STYLE_HINT_FRAMECHANGE);
|
|
}
|
|
NS_RELEASE(undisplayedContext);
|
|
}
|
|
undisplayed = undisplayed->mNext;
|
|
}
|
|
}
|
|
|
|
aResultChange = aMinChange;
|
|
|
|
// now do children
|
|
PRInt32 listIndex = 0;
|
|
nsIAtom* childList = nsnull;
|
|
PRInt32 childChange;
|
|
nsIFrame* child;
|
|
|
|
do {
|
|
child = nsnull;
|
|
result = aFrame->FirstChild(aPresContext, childList, &child);
|
|
while ((NS_SUCCEEDED(result)) && (child)) {
|
|
nsFrameState state;
|
|
child->GetFrameState(&state);
|
|
if (NS_FRAME_OUT_OF_FLOW != (state & NS_FRAME_OUT_OF_FLOW)) {
|
|
// only do frames that are in flow
|
|
nsCOMPtr<nsIAtom> frameType;
|
|
child->GetFrameType(getter_AddRefs(frameType));
|
|
if (nsLayoutAtoms::placeholderFrame == frameType.get()) { // placeholder
|
|
// get out of flow frame and recurse there
|
|
nsIFrame* outOfFlowFrame = ((nsPlaceholderFrame*)child)->GetOutOfFlowFrame();
|
|
NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
|
|
|
|
ReResolveStyleContext(aPresContext, outOfFlowFrame, newContext, content,
|
|
aAttrNameSpaceID, aAttribute,
|
|
aChangeList, aMinChange, childChange);
|
|
|
|
// reresolve placeholder's context under out of flow frame
|
|
nsIStyleContext* outOfFlowContext;
|
|
outOfFlowFrame->GetStyleContext(&outOfFlowContext);
|
|
ReResolveStyleContext(aPresContext, child, outOfFlowContext, content,
|
|
kNameSpaceID_Unknown, nsnull,
|
|
aChangeList, aMinChange, childChange);
|
|
NS_RELEASE(outOfFlowContext);
|
|
}
|
|
else { // regular child frame
|
|
ReResolveStyleContext(aPresContext, child, newContext, content,
|
|
aAttrNameSpaceID, aAttribute,
|
|
aChangeList, aMinChange, childChange);
|
|
}
|
|
}
|
|
child->GetNextSibling(&child);
|
|
}
|
|
|
|
NS_IF_RELEASE(childList);
|
|
aFrame->GetAdditionalChildListName(listIndex++, &childList);
|
|
} while (childList);
|
|
// XXX need to do overflow frames???
|
|
|
|
NS_RELEASE(newContext);
|
|
NS_IF_RELEASE(localContent);
|
|
}
|
|
}
|
|
|
|
#endif // NEW_CONTEXT_PARENTAGE_INVARIANT
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::ComputeStyleChangeFor(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
PRInt32 aAttrNameSpaceID,
|
|
nsIAtom* aAttribute,
|
|
nsStyleChangeList& aChangeList,
|
|
PRInt32 aMinChange,
|
|
PRInt32& aTopLevelChange)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
aTopLevelChange = NS_STYLE_HINT_NONE;
|
|
nsIFrame* frame = aFrame;
|
|
|
|
do {
|
|
nsIStyleContext* styleContext = nsnull;
|
|
frame->GetStyleContext(&styleContext);
|
|
nsIStyleContext* parentContext = styleContext->GetParent();
|
|
PRInt32 frameChange;
|
|
ReResolveStyleContext(aPresContext, frame, parentContext, nsnull,
|
|
aAttrNameSpaceID, aAttribute,
|
|
aChangeList, aMinChange, frameChange);
|
|
#ifdef NS_DEBUG
|
|
VerifyStyleTree(aPresContext, frame, parentContext);
|
|
#endif
|
|
NS_IF_RELEASE(parentContext);
|
|
NS_RELEASE(styleContext);
|
|
if (aTopLevelChange < frameChange) {
|
|
aTopLevelChange = frameChange;
|
|
}
|
|
|
|
frame->GetNextInFlow(&frame);
|
|
} while (frame);
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::AttributeAffectsStyle(nsIAtom *aAttribute, nsIContent *aContent,
|
|
PRBool &aAffects)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
|
|
nsCOMPtr<nsIXMLContent> xml(do_QueryInterface(aContent));
|
|
if (xml) {
|
|
rv = mStyleSet->AttributeAffectsStyle(aAttribute, aContent, aAffects);
|
|
} else {
|
|
// not an XML element, so assume it is an HTML element and further assume that
|
|
// any attribute may affect style
|
|
// NOTE: we could list all of the presentation-hint attributes and check them,
|
|
// but there are a lot of them and this is safer.
|
|
aAffects = PR_TRUE;
|
|
rv = NS_OK;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
// 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(nsIPresContext* aPresContext, nsIFrame* aFrame, nsILayoutHistoryState* aState, nsIStatefulFrame::SpecialStateID aID)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
NS_PRECONDITION(nsnull != aFrame && nsnull != aState, "null parameters passed in");
|
|
|
|
// See if the frame is stateful.
|
|
// Frames are not ref-counted so no addref/release is required on statefulFrame.
|
|
nsIStatefulFrame* statefulFrame = nsnull;
|
|
aFrame->QueryInterface(NS_GET_IID(nsIStatefulFrame), (void**) &statefulFrame);
|
|
if (nsnull != statefulFrame) {
|
|
|
|
// If not given one, get the content ID
|
|
PRUint32 ID = aID;
|
|
if (nsIStatefulFrame::eNoID == ID) {
|
|
nsCOMPtr<nsIContent> content;
|
|
rv = aFrame->GetContent(getter_AddRefs(content));
|
|
if (NS_SUCCEEDED(rv) && content) {
|
|
rv = content->GetContentID(&ID);
|
|
}
|
|
}
|
|
if (NS_SUCCEEDED(rv) && ID) { // Must have ID (don't do anonymous content)
|
|
|
|
// Get the state type
|
|
nsIStatefulFrame::StateType type = nsIStatefulFrame::eNoType;
|
|
rv = statefulFrame->GetStateType(aPresContext, &type);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
// Get the state
|
|
nsCOMPtr<nsIPresState> frameState;
|
|
rv = statefulFrame->SaveState(aPresContext, getter_AddRefs(frameState));
|
|
if (NS_SUCCEEDED(rv) && frameState) {
|
|
|
|
// add an association between (ID, type) and (state) to the
|
|
// history state storage object, aState.
|
|
rv = aState->AddState(ID, frameState, type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::CaptureFrameState(nsIPresContext* aPresContext, 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(aPresContext, aFrame, aState);
|
|
|
|
// Now capture state recursively for the frame hierarchy rooted at aFrame
|
|
nsIAtom* childListName = nsnull;
|
|
PRInt32 childListIndex = 0;
|
|
do {
|
|
nsIFrame* childFrame;
|
|
aFrame->FirstChild(aPresContext, childListName, &childFrame);
|
|
while (childFrame) {
|
|
rv = CaptureFrameState(aPresContext, childFrame, aState);
|
|
// Get the next sibling child frame
|
|
childFrame->GetNextSibling(&childFrame);
|
|
}
|
|
NS_IF_RELEASE(childListName);
|
|
aFrame->GetAdditionalChildListName(childListIndex++, &childListName);
|
|
} 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(nsIPresContext* aPresContext, nsIFrame* aFrame, nsILayoutHistoryState* aState, nsIStatefulFrame::SpecialStateID aID)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
NS_PRECONDITION(nsnull != aFrame && nsnull != aState, "null parameters passed in");
|
|
|
|
// See if the frame is stateful.
|
|
// Frames are not ref-counted so no addref/release is required on statefulFrame.
|
|
nsIStatefulFrame* statefulFrame = nsnull;
|
|
aFrame->QueryInterface(NS_GET_IID(nsIStatefulFrame), (void**) &statefulFrame);
|
|
if (nsnull != statefulFrame) {
|
|
// If not given one, get the content ID
|
|
PRUint32 ID = aID;
|
|
if (nsIStatefulFrame::eNoID == ID) {
|
|
nsCOMPtr<nsIContent> content;
|
|
rv = aFrame->GetContent(getter_AddRefs(content));
|
|
if (NS_SUCCEEDED(rv) && content) {
|
|
rv = content->GetContentID(&ID);
|
|
}
|
|
}
|
|
if (NS_SUCCEEDED(rv) && ID) { // Must have ID (don't do anonymous content)
|
|
|
|
nsIStatefulFrame::StateType type = nsIStatefulFrame::eNoType;
|
|
rv = statefulFrame->GetStateType(aPresContext, &type);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
nsCOMPtr<nsIPresState> frameState;
|
|
rv = aState->GetState(ID, getter_AddRefs(frameState), type);
|
|
if (NS_SUCCEEDED(rv) && frameState) {
|
|
|
|
// First restore the state.
|
|
rv = statefulFrame->RestoreState(aPresContext, frameState);
|
|
|
|
// Now remove the state from the state table.
|
|
aState->RemoveState(ID, type);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::RestoreFrameState(nsIPresContext* aPresContext, 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(aPresContext, aFrame, aState);
|
|
|
|
// Now restore state recursively for the frame hierarchy rooted at aFrame
|
|
nsIAtom* childListName = nsnull;
|
|
PRInt32 childListIndex = 0;
|
|
do {
|
|
nsIFrame* childFrame;
|
|
aFrame->FirstChild(aPresContext, childListName, &childFrame);
|
|
while (childFrame) {
|
|
rv = RestoreFrameState(aPresContext, childFrame, aState);
|
|
// Get the next sibling child frame
|
|
childFrame->GetNextSibling(&childFrame);
|
|
}
|
|
NS_IF_RELEASE(childListName);
|
|
aFrame->GetAdditionalChildListName(childListIndex++, &childListName);
|
|
} while (childListName);
|
|
|
|
return rv;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
static PLHashNumber
|
|
HashKey(void* key)
|
|
{
|
|
return (PLHashNumber)key;
|
|
}
|
|
|
|
static PRIntn
|
|
CompareKeys(void* key1, void* key2)
|
|
{
|
|
return key1 == key2;
|
|
}
|
|
|
|
MOZ_DECL_CTOR_COUNTER(FrameHashTable)
|
|
|
|
FrameHashTable::FrameHashTable(PRUint32 aNumBuckets)
|
|
{
|
|
MOZ_COUNT_CTOR(FrameHashTable);
|
|
mTable = PL_NewHashTable(aNumBuckets, (PLHashFunction)HashKey,
|
|
(PLHashComparator)CompareKeys,
|
|
(PLHashComparator)nsnull,
|
|
nsnull, nsnull);
|
|
}
|
|
|
|
FrameHashTable::~FrameHashTable()
|
|
{
|
|
MOZ_COUNT_DTOR(FrameHashTable);
|
|
PL_HashTableDestroy(mTable);
|
|
}
|
|
|
|
void*
|
|
FrameHashTable::Get(void* aKey)
|
|
{
|
|
PRInt32 hashCode = (PRInt32) aKey;
|
|
PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey);
|
|
PLHashEntry* he = *hep;
|
|
if (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 (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 (he) {
|
|
oldValue = he->value;
|
|
PL_HashTableRawRemove(mTable, hep, he);
|
|
}
|
|
return oldValue;
|
|
}
|
|
|
|
static PRIntn PR_CALLBACK
|
|
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 PR_CALLBACK
|
|
EnumEntries(PLHashEntry* he, PRIntn i, void* arg)
|
|
{
|
|
// Continue enumerating
|
|
return HT_ENUMERATE_NEXT;
|
|
}
|
|
|
|
void
|
|
FrameHashTable::Dump(FILE* fp)
|
|
{
|
|
PL_HashTableDump(mTable, EnumEntries, fp);
|
|
}
|
|
#endif
|
|
|
|
void
|
|
FrameManager::DestroyPropertyList(nsIPresContext* aPresContext)
|
|
{
|
|
if (mPropertyList) {
|
|
while (mPropertyList) {
|
|
PropertyList* tmp = mPropertyList;
|
|
|
|
mPropertyList = mPropertyList->mNext;
|
|
tmp->RemoveAllProperties(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_ENSURE_ARG_POINTER(aPropertyName);
|
|
PropertyList* propertyList = GetPropertyListFor(aPropertyName);
|
|
nsresult result;
|
|
|
|
if (propertyList) {
|
|
result = propertyList->mFrameValueMap->Search(aFrame, aOptions, aPropertyValue);
|
|
|
|
} else {
|
|
*aPropertyValue = 0;
|
|
result = NS_IFRAME_MGR_PROP_NOT_THERE;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::SetFrameProperty(nsIFrame* aFrame,
|
|
nsIAtom* aPropertyName,
|
|
void* aPropertyValue,
|
|
NSFMPropertyDtorFunc aPropDtorFunc)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
NS_ENSURE_ARG_POINTER(aPropertyName);
|
|
PropertyList* propertyList = GetPropertyListFor(aPropertyName);
|
|
nsresult result = NS_OK;
|
|
|
|
if (propertyList) {
|
|
// Make sure the dtor function matches
|
|
if (aPropDtorFunc != propertyList->mDtorFunc) {
|
|
return NS_ERROR_INVALID_ARG;
|
|
}
|
|
|
|
} else {
|
|
propertyList = new PropertyList(aPropertyName, aPropDtorFunc, mDSTNodeArena);
|
|
if (!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
|
|
void* oldValue;
|
|
result = propertyList->mFrameValueMap->Insert(aFrame, aPropertyValue, &oldValue);
|
|
if (NS_DST_VALUE_OVERWRITTEN == result) {
|
|
if (propertyList->mDtorFunc) {
|
|
nsCOMPtr<nsIPresContext> presContext;
|
|
mPresShell->GetPresContext(getter_AddRefs(presContext));
|
|
|
|
propertyList->mDtorFunc(presContext, aFrame, aPropertyName, oldValue);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::RemoveFrameProperty(nsIFrame* aFrame,
|
|
nsIAtom* aPropertyName)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
NS_ENSURE_ARG_POINTER(aPropertyName);
|
|
PropertyList* propertyList = GetPropertyListFor(aPropertyName);
|
|
nsresult result = NS_IFRAME_MGR_PROP_NOT_THERE;
|
|
|
|
if (propertyList) {
|
|
nsCOMPtr<nsIPresContext> presContext;
|
|
mPresShell->GetPresContext(getter_AddRefs(presContext));
|
|
|
|
if (propertyList->RemovePropertyForFrame(presContext, aFrame)) {
|
|
result = NS_OK;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
FrameManager::GetInsertionPoint(nsIPresShell* aShell, nsIFrame* aParent, nsIFrame* aChild, nsIFrame** aResult)
|
|
{
|
|
NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_AVAILABLE);
|
|
*aResult = nsnull;
|
|
|
|
nsCOMPtr<nsIContent> content;
|
|
aParent->GetContent(getter_AddRefs(content));
|
|
if (!content)
|
|
return NS_OK;
|
|
nsCOMPtr<nsIDocument> document;
|
|
content->GetDocument(*getter_AddRefs(document));
|
|
if (!document)
|
|
return NS_OK;
|
|
|
|
nsCOMPtr<nsIBindingManager> bindingManager;
|
|
document->GetBindingManager(getter_AddRefs(bindingManager));
|
|
if (!bindingManager)
|
|
return NS_OK;
|
|
|
|
nsCOMPtr<nsIContent> insertionElement;
|
|
nsIFrame* frame = nsnull;
|
|
if (aChild) {
|
|
nsCOMPtr<nsIContent> currContent;
|
|
aChild->GetContent(getter_AddRefs(currContent));
|
|
|
|
// Check to see if the content is anonymous.
|
|
nsCOMPtr<nsIContent> bindingParent;
|
|
currContent->GetBindingParent(getter_AddRefs(bindingParent));
|
|
if (bindingParent == content)
|
|
return NS_OK; // It is anonymous. Don't use the insertion point, since that's only
|
|
// for the explicit kids.
|
|
|
|
PRUint32 index;
|
|
bindingManager->GetInsertionPoint(content, currContent, getter_AddRefs(insertionElement), &index);
|
|
if (insertionElement) {
|
|
aShell->GetPrimaryFrameFor(insertionElement, &frame);
|
|
if (frame) {
|
|
nsCOMPtr<nsIScrollableFrame> scroll(do_QueryInterface(frame));
|
|
if (scroll)
|
|
scroll->GetScrolledFrame(nsnull, frame);
|
|
if (frame != aParent) {
|
|
nsIFrame* nestedPoint = nsnull;
|
|
GetInsertionPoint(aShell, frame, aChild, &nestedPoint);
|
|
*aResult = nestedPoint ? nestedPoint : frame;
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
}
|
|
else {
|
|
PRBool dummy;
|
|
PRUint32 index;
|
|
bindingManager->GetSingleInsertionPoint(content, getter_AddRefs(insertionElement), &index, &dummy);
|
|
if (insertionElement) {
|
|
aShell->GetPrimaryFrameFor(insertionElement, &frame);
|
|
if (frame) {
|
|
nsCOMPtr<nsIScrollableFrame> scroll(do_QueryInterface(frame));
|
|
if (scroll)
|
|
scroll->GetScrolledFrame(nsnull, frame);
|
|
if (frame != aParent) {
|
|
nsIFrame* nestedPoint = nsnull;
|
|
GetInsertionPoint(aShell, frame, aChild, &nestedPoint);
|
|
*aResult = nestedPoint ? nestedPoint : frame;
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
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 = (PLHashNumber)(void*)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) {
|
|
NS_ASSERTION((node->mContent != aNode->mContent) ||
|
|
((node->mContent == nsnull) &&
|
|
(node->mStyle != aNode->mStyle)), "node in map twice");
|
|
node = node->mNext;
|
|
}
|
|
node->mNext = aNode;
|
|
}
|
|
else {
|
|
PLHashNumber hashCode = (PLHashNumber)(void*)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,
|
|
nsIStyleContext* aStyle)
|
|
{
|
|
UndisplayedNode* node = new UndisplayedNode(aChild, aStyle);
|
|
if (! node) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
return AppendNodeFor(node, aParentContent);
|
|
}
|
|
|
|
nsresult
|
|
UndisplayedMap::AddNodeFor(nsIContent* aParentContent, nsIStyleContext* aPseudoStyle)
|
|
{
|
|
UndisplayedNode* node = new UndisplayedNode(aPseudoStyle);
|
|
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);
|
|
delete node;
|
|
if (entry == mLastLookup) {
|
|
mLastLookup = nsnull;
|
|
}
|
|
PL_HashTableRawRemove(mTable, entry, *entry);
|
|
}
|
|
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,
|
|
NSFMPropertyDtorFunc aDtorFunc,
|
|
nsDST::NodeArena* aDSTNodeArena)
|
|
: mName(aName), mFrameValueMap(new nsDST(aDSTNodeArena)),
|
|
mDtorFunc(aDtorFunc), mNext(nsnull)
|
|
{
|
|
}
|
|
|
|
FrameManager::PropertyList::~PropertyList()
|
|
{
|
|
delete mFrameValueMap;
|
|
}
|
|
|
|
class DestroyPropertyValuesFunctor : public nsDSTNodeFunctor {
|
|
public:
|
|
DestroyPropertyValuesFunctor(nsIPresContext* aPresContext,
|
|
nsIAtom* aPropertyName,
|
|
NSFMPropertyDtorFunc aDtorFunc)
|
|
: mPresContext(aPresContext), mPropertyName(aPropertyName), mDtorFunc(aDtorFunc) {}
|
|
|
|
virtual void operator () (void *aKey, void *aValue) {
|
|
mDtorFunc(mPresContext, (nsIFrame*)aKey, mPropertyName, aValue);
|
|
}
|
|
|
|
nsIPresContext* mPresContext;
|
|
nsIAtom* mPropertyName;
|
|
NSFMPropertyDtorFunc mDtorFunc;
|
|
};
|
|
|
|
void
|
|
FrameManager::PropertyList::RemoveAllProperties(nsIPresContext* aPresContext)
|
|
{
|
|
if (mDtorFunc) {
|
|
DestroyPropertyValuesFunctor functor(aPresContext, mName, mDtorFunc);
|
|
|
|
// Enumerate any remaining frame/value pairs and destroy the value object
|
|
mFrameValueMap->Enumerate(functor);
|
|
}
|
|
}
|
|
|
|
PRBool
|
|
FrameManager::PropertyList::RemovePropertyForFrame(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame)
|
|
{
|
|
void* value;
|
|
nsresult result;
|
|
|
|
// If the property exists, then we need to run the dtor function so
|
|
// do a search with the option to remove the key/value pair
|
|
result = mFrameValueMap->Search(aFrame, NS_DST_REMOVE_KEY_VALUE, &value);
|
|
|
|
if (NS_OK == result) {
|
|
// The property was set
|
|
if (mDtorFunc) {
|
|
// Destroy the property value
|
|
mDtorFunc(aPresContext, aFrame, mName, value);
|
|
}
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
return PR_FALSE;
|
|
}
|
|
|