gecko-dev/layout/base/src/nsPresShell.cpp

1036 lines
27 KiB
C++
Raw Normal View History

1998-04-13 20:24:54 +00:00
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsIPresShell.h"
#include "nsIPresContext.h"
#include "nsIContent.h"
#include "nsIContentDelegate.h"
#include "nsIDocument.h"
#include "nsIDocumentObserver.h"
1998-04-13 20:24:54 +00:00
#include "nsIStyleSet.h"
#include "nsIStyleContext.h"
1998-05-20 16:24:54 +00:00
#include "nsFrame.h"
1998-06-09 04:51:44 +00:00
#include "nsIReflowCommand.h"
1998-04-13 20:24:54 +00:00
#include "nsIViewManager.h"
#include "nsCRT.h"
#include "plhash.h"
#include "prlog.h"
1998-06-09 04:51:44 +00:00
#include "nsVoidArray.h"
1998-04-13 20:24:54 +00:00
#undef NOISY
#if 0
static PLHashNumber
HashKey(nsIFrame* key)
1998-04-13 20:24:54 +00:00
{
return (PLHashNumber) key;
}
static PRIntn
CompareKeys(nsIFrame* key1, nsIFrame* key2)
1998-04-13 20:24:54 +00:00
{
return key1 == key2;
}
class FrameHashTable {
public:
FrameHashTable();
~FrameHashTable();
void* Get(nsIFrame* aKey);
void* Put(nsIFrame* aKey, void* aValue);
void* Remove(nsIFrame* aKey);
protected:
PLHashTable* mTable;
};
FrameHashTable::FrameHashTable()
{
mTable = PL_NewHashTable(8, (PLHashFunction) HashKey,
(PLHashComparator) CompareKeys,
(PLHashComparator) nsnull,
nsnull, nsnull);
}
FrameHashTable::~FrameHashTable()
{
// XXX if debugging then we should assert that the table is empty
PL_HashTableDestroy(mTable);
}
/**
* Get the data associated with a frame.
*/
void*
FrameHashTable::Get(nsIFrame* aKey)
1998-04-13 20:24:54 +00:00
{
PRInt32 hashCode = (PRInt32) aKey;
PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey);
PLHashEntry* he = *hep;
if (nsnull != he) {
return he->value;
}
return nsnull;
}
/**
* Create an association between a frame and some data. This call
* returns an old association if there was one (or nsnull if there
* wasn't).
*/
void*
FrameHashTable::Put(nsIFrame* aKey, void* aData)
1998-04-13 20:24:54 +00:00
{
PRInt32 hashCode = (PRInt32) aKey;
PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey);
PLHashEntry* he = *hep;
if (nsnull != he) {
void* oldValue = he->value;
he->value = aData;
return oldValue;
}
PL_HashTableRawAdd(mTable, hep, hashCode, aKey, aData);
return nsnull;
}
/**
* Remove an association between a frame and it's data. This returns
* the old associated data.
*/
void*
FrameHashTable::Remove(nsIFrame* aKey)
1998-04-13 20:24:54 +00:00
{
PRInt32 hashCode = (PRInt32) aKey;
PLHashEntry** hep = PL_HashTableRawLookup(mTable, hashCode, aKey);
PLHashEntry* he = *hep;
void* oldValue = nsnull;
if (nsnull != he) {
oldValue = he->value;
PL_HashTableRawRemove(mTable, hep, he);
}
return oldValue;
}
#endif
1998-04-13 20:24:54 +00:00
//----------------------------------------------------------------------
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kIPresShellIID, NS_IPRESSHELL_IID);
static NS_DEFINE_IID(kIDocumentObserverIID, NS_IDOCUMENT_OBSERVER_IID);
1998-04-13 20:24:54 +00:00
class PresShell : public nsIPresShell, private nsIDocumentObserver {
1998-04-13 20:24:54 +00:00
public:
PresShell();
void* operator new(size_t sz) {
void* rv = new char[sz];
nsCRT::zero(rv, sz);
return rv;
}
// nsISupports
NS_DECL_ISUPPORTS
// nsIDocumentObserver
NS_IMETHOD BeginUpdate();
NS_IMETHOD EndUpdate();
NS_IMETHOD BeginLoad();
NS_IMETHOD EndLoad();
NS_IMETHOD BeginReflow(nsIPresShell* aShell);
NS_IMETHOD EndReflow(nsIPresShell* aShell);
NS_IMETHOD ContentChanged(nsIContent* aContent,
nsISupports* aSubContent);
NS_IMETHOD ContentAppended(nsIContent* aContainer);
NS_IMETHOD ContentInserted(nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer);
NS_IMETHOD ContentReplaced(nsIContent* aContainer,
nsIContent* aOldChild,
nsIContent* aNewChild,
PRInt32 aIndexInContainer);
NS_IMETHOD ContentWillBeRemoved(nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer);
NS_IMETHOD ContentHasBeenRemoved(nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer);
NS_IMETHOD StyleSheetAdded(nsIStyleSheet* aStyleSheet);
1998-04-13 20:24:54 +00:00
// nsIPresShell
NS_IMETHOD Init(nsIDocument* aDocument,
nsIPresContext* aPresContext,
nsIViewManager* aViewManager,
nsIStyleSet* aStyleSet);
1998-04-13 20:24:54 +00:00
virtual nsIDocument* GetDocument();
virtual nsIPresContext* GetPresContext();
virtual nsIViewManager* GetViewManager();
virtual nsIStyleSet* GetStyleSet();
NS_IMETHOD EnterReflowLock();
NS_IMETHOD ExitReflowLock();
1998-04-13 20:24:54 +00:00
virtual void BeginObservingDocument();
virtual void EndObservingDocument();
virtual void ResizeReflow(nscoord aWidth, nscoord aHeight);
virtual nsIFrame* GetRootFrame();
virtual nsIFrame* FindFrameWithContent(nsIContent* aContent);
1998-06-09 04:51:44 +00:00
virtual void AppendReflowCommand(nsIReflowCommand* aReflowCommand);
1998-04-13 20:24:54 +00:00
virtual void ProcessReflowCommands();
protected:
~PresShell();
1998-07-13 19:49:42 +00:00
#ifdef NS_DEBUG
void VerifyIncrementalReflow();
#endif
1998-05-20 16:24:54 +00:00
1998-04-13 20:24:54 +00:00
nsIDocument* mDocument;
nsIPresContext* mPresContext;
nsIStyleSet* mStyleSet;
nsIFrame* mRootFrame;
nsIViewManager* mViewManager;
PRUint32 mUpdateCount;
nsVoidArray mReflowCommands;
PRUint32 mReflowLockCount;
1998-04-13 20:24:54 +00:00
};
1998-07-13 19:49:42 +00:00
#ifdef NS_DEBUG
/**
* Note: the log module is created during library initialization which
* means that you cannot perform logging before then.
*/
static PRLogModuleInfo* gLogModule = PR_NewLogModule("verifyreflow");
#endif
static PRBool gVerifyReflow = PRBool(0x55);
NS_LAYOUT PRBool
nsIPresShell::GetVerifyReflowEnable()
{
#ifdef NS_DEBUG
if (gVerifyReflow == PRBool(0x55)) {
gVerifyReflow = 0 != gLogModule->level;
printf("Note: verifyreflow is %sabled\n",
gVerifyReflow ? "en" : "dis");
}
#endif
return gVerifyReflow;
}
NS_LAYOUT void
nsIPresShell::SetVerifyReflowEnable(PRBool aEnabled)
{
gVerifyReflow = aEnabled;
}
1998-04-13 20:24:54 +00:00
//----------------------------------------------------------------------
1998-07-13 19:49:42 +00:00
NS_LAYOUT nsresult
NS_NewPresShell(nsIPresShell** aInstancePtrResult)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
if (nsnull == aInstancePtrResult) {
return NS_ERROR_NULL_POINTER;
}
PresShell* it = new PresShell();
if (nsnull == it) {
return NS_ERROR_OUT_OF_MEMORY;
}
return it->QueryInterface(kIPresShellIID, (void **) aInstancePtrResult);
}
1998-04-13 20:24:54 +00:00
PresShell::PresShell()
{
}
nsrefcnt
PresShell::AddRef(void)
1998-04-13 20:24:54 +00:00
{
return ++mRefCnt;
}
nsrefcnt
PresShell::Release(void)
1998-04-13 20:24:54 +00:00
{
NS_PRECONDITION(0 != mRefCnt, "bad refcnt");
if (--mRefCnt == 0) {
delete this;
return 0;
}
return mRefCnt;
}
nsresult
PresShell::QueryInterface(const nsIID& aIID, void** aInstancePtr)
1998-04-13 20:24:54 +00:00
{
if (aIID.Equals(kIPresShellIID)) {
*aInstancePtr = (void*) ((nsIPresShell*) this);
AddRef();
return NS_OK;
}
if (aIID.Equals(kIDocumentObserverIID)) {
*aInstancePtr = (void*) ((nsIDocumentObserver*) this);
AddRef();
return NS_OK;
}
if (aIID.Equals(kISupportsIID)) {
*aInstancePtr = (void*) ((nsISupports*) ((nsIPresShell*)this));
AddRef();
return NS_OK;
}
return NS_NOINTERFACE;
}
PresShell::~PresShell()
{
if (nsnull != mDocument) {
mDocument->DeleteShell(this);
NS_RELEASE(mDocument);
}
if (nsnull != mRootFrame) {
mRootFrame->DeleteFrame();
}
NS_IF_RELEASE(mPresContext);
NS_IF_RELEASE(mViewManager);
NS_IF_RELEASE(mStyleSet);
}
/**
* Initialize the presentation shell. Create view manager and style
* manager.
*/
nsresult
PresShell::Init(nsIDocument* aDocument,
nsIPresContext* aPresContext,
nsIViewManager* aViewManager,
nsIStyleSet* aStyleSet)
1998-04-13 20:24:54 +00:00
{
NS_PRECONDITION(nsnull != aDocument, "null ptr");
NS_PRECONDITION(nsnull != aPresContext, "null ptr");
NS_PRECONDITION(nsnull != aViewManager, "null ptr");
if ((nsnull == aDocument) || (nsnull == aPresContext) ||
(nsnull == aViewManager)) {
return NS_ERROR_NULL_POINTER;
}
if (nsnull != mDocument) {
return NS_ERROR_ALREADY_INITIALIZED;
}
mDocument = aDocument;
NS_ADDREF(aDocument);
mViewManager = aViewManager;
NS_ADDREF(mViewManager);
// Bind the context to the presentation shell.
mPresContext = aPresContext;
NS_ADDREF(aPresContext);
aPresContext->SetShell(this);
mStyleSet = aStyleSet;
NS_ADDREF(aStyleSet);
return NS_OK;
}
NS_METHOD
PresShell::EnterReflowLock()
{
++mReflowLockCount;
return NS_OK;
}
NS_METHOD
PresShell::ExitReflowLock()
{
PRUint32 newReflowLockCount = mReflowLockCount - 1;
if (newReflowLockCount == 0) {
ProcessReflowCommands();
}
mReflowLockCount = newReflowLockCount;
return NS_OK;
}
nsIDocument*
PresShell::GetDocument()
1998-04-13 20:24:54 +00:00
{
NS_IF_ADDREF(mDocument);
return mDocument;
}
nsIPresContext*
PresShell::GetPresContext()
1998-04-13 20:24:54 +00:00
{
NS_IF_ADDREF(mPresContext);
return mPresContext;
}
nsIViewManager*
PresShell::GetViewManager()
1998-04-13 20:24:54 +00:00
{
NS_IF_ADDREF(mViewManager);
return mViewManager;
}
nsIStyleSet*
PresShell::GetStyleSet()
1998-04-13 20:24:54 +00:00
{
NS_IF_ADDREF(mStyleSet);
return mStyleSet;
}
// Make shell be a document observer
void
PresShell::BeginObservingDocument()
1998-04-13 20:24:54 +00:00
{
if (nsnull != mDocument) {
mDocument->AddObserver(this);
}
}
// Make shell stop being a document observer
void
PresShell::EndObservingDocument()
1998-04-13 20:24:54 +00:00
{
if (nsnull != mDocument) {
mDocument->RemoveObserver(this);
}
}
void
PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
1998-04-13 20:24:54 +00:00
{
EnterReflowLock();
1998-04-13 20:24:54 +00:00
if (nsnull != mPresContext) {
nsRect r(0, 0, aWidth, aHeight);
mPresContext->SetVisibleArea(r);
}
nsReflowReason reflowReason = eReflowReason_Resize;
1998-04-13 20:24:54 +00:00
if (nsnull == mRootFrame) {
if (nsnull != mDocument) {
nsIContent* root = mDocument->GetRootContent();
if (nsnull != root) {
nsIContentDelegate* cd = root->GetDelegate(mPresContext);
if (nsnull != cd) {
nsIStyleContext* rootSC =
mPresContext->ResolveStyleContextFor(root, nsnull);
nsresult rv = cd->CreateFrame(mPresContext, root, nsnull,
rootSC, mRootFrame);
NS_RELEASE(rootSC);
1998-04-13 20:24:54 +00:00
NS_RELEASE(cd);
reflowReason = eReflowReason_Initial;
1998-04-13 20:24:54 +00:00
// Bind root frame to root view (and root window)
nsIView* rootView = mViewManager->GetRootView();
mRootFrame->SetView(rootView);
NS_RELEASE(rootView);
}
NS_RELEASE(root);
}
}
}
if (nsnull != mRootFrame) {
// Kick off a top-down reflow
1998-05-20 16:24:54 +00:00
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
("enter nsPresShell::ResizeReflow: %d,%d", aWidth, aHeight));
1998-04-13 20:24:54 +00:00
#ifdef NS_DEBUG
1998-05-20 16:24:54 +00:00
if (nsIFrame::GetVerifyTreeEnable()) {
mRootFrame->VerifyTree();
}
1998-04-13 20:24:54 +00:00
#endif
nsRect bounds;
mPresContext->GetVisibleArea(bounds);
1998-05-20 16:24:54 +00:00
nsSize maxSize(bounds.width, bounds.height);
nsReflowMetrics desiredSize(nsnull);
1998-05-12 04:17:56 +00:00
nsReflowStatus status;
nsReflowState reflowState(mRootFrame, reflowReason, maxSize);
mRootFrame->Reflow(*mPresContext, desiredSize, reflowState, status);
1998-04-13 20:24:54 +00:00
mRootFrame->SizeTo(desiredSize.width, desiredSize.height);
#ifdef NS_DEBUG
1998-05-20 16:24:54 +00:00
if (nsIFrame::GetVerifyTreeEnable()) {
mRootFrame->VerifyTree();
1998-04-13 20:24:54 +00:00
}
#endif
1998-05-20 16:24:54 +00:00
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS, ("exit nsPresShell::ResizeReflow"));
1998-04-13 20:24:54 +00:00
// XXX if debugging then we should assert that the cache is empty
} else {
#ifdef NOISY
printf("PresShell::ResizeReflow: null root frame\n");
#endif
}
ExitReflowLock();
1998-04-13 20:24:54 +00:00
}
nsIFrame*
PresShell::GetRootFrame()
1998-04-13 20:24:54 +00:00
{
return mRootFrame;
}
NS_IMETHODIMP
PresShell::BeginUpdate()
1998-04-13 20:24:54 +00:00
{
mUpdateCount++;
return NS_OK;
1998-04-13 20:24:54 +00:00
}
NS_IMETHODIMP
PresShell::EndUpdate()
1998-04-13 20:24:54 +00:00
{
NS_PRECONDITION(0 != mUpdateCount, "too many EndUpdate's");
if (--mUpdateCount == 0) {
// XXX do something here
}
return NS_OK;
}
NS_IMETHODIMP
PresShell::BeginLoad()
{
return NS_OK;
}
NS_IMETHODIMP
PresShell::EndLoad()
{
return NS_OK;
}
NS_IMETHODIMP
PresShell::BeginReflow(nsIPresShell* aShell)
{
return NS_OK;
}
NS_IMETHODIMP
PresShell::EndReflow(nsIPresShell* aShell)
{
return NS_OK;
1998-04-13 20:24:54 +00:00
}
void
1998-06-09 04:51:44 +00:00
PresShell::AppendReflowCommand(nsIReflowCommand* aReflowCommand)
1998-04-13 20:24:54 +00:00
{
mReflowCommands.AppendElement(aReflowCommand);
1998-06-09 04:51:44 +00:00
NS_ADDREF(aReflowCommand);
1998-04-13 20:24:54 +00:00
}
void
PresShell::ProcessReflowCommands()
1998-04-13 20:24:54 +00:00
{
if (0 != mReflowCommands.Count()) {
nsReflowMetrics desiredSize(nsnull);
1998-04-13 20:24:54 +00:00
while (0 != mReflowCommands.Count()) {
1998-06-09 04:51:44 +00:00
nsIReflowCommand* rc = (nsIReflowCommand*) mReflowCommands.ElementAt(0);
1998-04-13 20:24:54 +00:00
mReflowCommands.RemoveElementAt(0);
// Dispatch the reflow command
nsSize maxSize;
mRootFrame->GetSize(maxSize);
1998-06-09 04:51:44 +00:00
#ifdef NS_DEBUG
nsIReflowCommand::ReflowType type;
1998-06-09 04:51:44 +00:00
rc->GetType(type);
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
("PresShell::ProcessReflowCommands: begin reflow command type=%d",
type));
1998-06-09 04:51:44 +00:00
#endif
rc->Dispatch(*mPresContext, desiredSize, maxSize);
NS_RELEASE(rc);
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
("PresShell::ProcessReflowCommands: end reflow command"));
1998-04-13 20:24:54 +00:00
}
// Place and size the root frame
mRootFrame->SizeTo(desiredSize.width, desiredSize.height);
#ifdef NS_DEBUG
1998-05-20 16:24:54 +00:00
if (nsIFrame::GetVerifyTreeEnable()) {
mRootFrame->VerifyTree();
}
1998-07-13 19:49:42 +00:00
if (GetVerifyReflowEnable()) {
VerifyIncrementalReflow();
}
1998-04-13 20:24:54 +00:00
#endif
}
}
#ifdef NS_DEBUG
static char*
ContentTag(nsIContent* aContent, PRIntn aSlot)
{
static char buf0[100], buf1[100], buf2[100];
static char* bufs[] = { buf0, buf1, buf2 };
char* buf = bufs[aSlot];
nsIAtom* atom = aContent->GetTag();
if (nsnull != atom) {
nsAutoString tmp;
atom->ToString(tmp);
tmp.ToCString(buf, 100);
}
else {
buf[0] = 0;
}
return buf;
}
#endif
NS_IMETHODIMP
PresShell::ContentChanged(nsIContent* aContent,
nsISupports* aSubContent)
1998-04-13 20:24:54 +00:00
{
NS_PRECONDITION(nsnull != mRootFrame, "null root frame");
EnterReflowLock();
// Notify the first frame that maps the content. It will generate a reflow
// command
nsIFrame* frame = FindFrameWithContent(aContent);
// It's possible the frame whose content changed isn't inserted into the
// frame hierarchy yet. This sometimes happens with images inside tables
if (nsnull != frame) {
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
("PresShell::ContentChanged: content=%p[%s] subcontent=%p frame=%p",
aContent, ContentTag(aContent, 0),
aSubContent, frame));
frame->ContentChanged(this, mPresContext, aContent, aSubContent);
}
ExitReflowLock();
return NS_OK;
1998-04-13 20:24:54 +00:00
}
NS_IMETHODIMP
PresShell::ContentAppended(nsIContent* aContainer)
1998-04-13 20:24:54 +00:00
{
NS_PRECONDITION(nsnull != mRootFrame, "null root frame");
EnterReflowLock();
nsIContent* parentContainer = aContainer;
while (nsnull != parentContainer) {
nsIFrame* frame = FindFrameWithContent(parentContainer);
if (nsnull != frame) {
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
("PresShell::ContentAppended: container=%p[%s] frame=%p",
aContainer, ContentTag(aContainer, 0), frame));
frame->ContentAppended(this, mPresContext, aContainer);
break;
}
parentContainer = parentContainer->GetParent();
}
ExitReflowLock();
return NS_OK;
1998-04-13 20:24:54 +00:00
}
NS_IMETHODIMP
PresShell::ContentInserted(nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer)
1998-04-13 20:24:54 +00:00
{
NS_PRECONDITION(nsnull != mRootFrame, "null root frame");
EnterReflowLock();
nsIFrame* frame = FindFrameWithContent(aContainer);
NS_PRECONDITION(nsnull != frame, "null frame");
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
("PresShell::ContentInserted: container=%p[%s] child=%p[%s][%d] frame=%p",
aContainer, ContentTag(aContainer, 0),
aChild, ContentTag(aChild, 1), aIndexInContainer,
frame));
frame->ContentInserted(this, mPresContext, aContainer, aChild,
aIndexInContainer);
ExitReflowLock();
return NS_OK;
1998-04-13 20:24:54 +00:00
}
NS_IMETHODIMP
PresShell::ContentReplaced(nsIContent* aContainer,
nsIContent* aOldChild,
nsIContent* aNewChild,
PRInt32 aIndexInContainer)
1998-04-13 20:24:54 +00:00
{
NS_PRECONDITION(nsnull != mRootFrame, "null root frame");
EnterReflowLock();
nsIFrame* frame = FindFrameWithContent(aContainer);
NS_PRECONDITION(nsnull != frame, "null frame");
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
("PresShell::ContentReplaced: container=%p[%s] oldChild=%p[%s][%d] newChild=%p[%s] frame=%p",
aContainer, ContentTag(aContainer, 0),
aOldChild, ContentTag(aOldChild, 1), aIndexInContainer,
aNewChild, ContentTag(aNewChild, 2), frame));
frame->ContentReplaced(this, mPresContext, aContainer, aOldChild,
aNewChild, aIndexInContainer);
ExitReflowLock();
return NS_OK;
1998-04-13 20:24:54 +00:00
}
// XXX keep this?
NS_IMETHODIMP
PresShell::ContentWillBeRemoved(nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer)
1998-04-13 20:24:54 +00:00
{
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
("PresShell::ContentWillBeRemoved: container=%p[%s] child=%p[%s][%d]",
aContainer, ContentTag(aContainer, 0),
aChild, ContentTag(aChild, 1), aIndexInContainer));
return NS_OK;
1998-04-13 20:24:54 +00:00
}
NS_IMETHODIMP
PresShell::ContentHasBeenRemoved(nsIContent* aContainer,
nsIContent* aChild,
PRInt32 aIndexInContainer)
1998-04-13 20:24:54 +00:00
{
NS_PRECONDITION(nsnull != mRootFrame, "null root frame");
nsIFrame* frame = FindFrameWithContent(aContainer);
NS_PRECONDITION(nsnull != frame, "null frame");
NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
("PresShell::ContentHasBeenRemoved: container=%p child=%p[%d] frame=%p",
aContainer, aChild, aIndexInContainer, frame));
frame->ContentDeleted(this, mPresContext, aContainer, aChild,
aIndexInContainer);
1998-04-13 20:24:54 +00:00
ProcessReflowCommands();
return NS_OK;
1998-04-13 20:24:54 +00:00
}
NS_IMETHODIMP
PresShell::StyleSheetAdded(nsIStyleSheet* aStyleSheet)
1998-04-13 20:24:54 +00:00
{
return NS_OK;
1998-04-13 20:24:54 +00:00
}
static nsIFrame*
FindFrameWithContent(nsIFrame* aFrame, nsIContent* aContent)
1998-04-13 20:24:54 +00:00
{
nsIContent* frameContent;
aFrame->GetContent(frameContent);
1998-04-13 20:24:54 +00:00
if (frameContent == aContent) {
NS_RELEASE(frameContent);
return aFrame;
}
NS_RELEASE(frameContent);
aFrame->FirstChild(aFrame);
1998-04-13 20:24:54 +00:00
while (aFrame) {
nsIFrame* result = FindFrameWithContent(aFrame, aContent);
if (result) {
return result;
}
aFrame->GetNextSibling(aFrame);
1998-04-13 20:24:54 +00:00
}
return nsnull;
}
nsIFrame*
PresShell::FindFrameWithContent(nsIContent* aContent)
1998-04-13 20:24:54 +00:00
{
// For the time being do a brute force depth-first search of
// the frame tree
return ::FindFrameWithContent(mRootFrame, aContent);
}
1998-07-13 19:49:42 +00:00
#ifdef NS_DEBUG
#include "nsViewsCID.h"
#include "nsWidgetsCID.h"
#include "nsIScrollableView.h"
#include "nsIDeviceContext.h"
#include "nsIURL.h"
#include "nsICSSParser.h"
#include "nsIStyleSheet.h"
static NS_DEFINE_IID(kViewManagerCID, NS_VIEW_MANAGER_CID);
static NS_DEFINE_IID(kIViewManagerIID, NS_IVIEWMANAGER_IID);
static NS_DEFINE_IID(kScrollingViewCID, NS_SCROLLING_VIEW_CID);
static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID);
static NS_DEFINE_IID(kScrollViewIID, NS_ISCROLLABLEVIEW_IID);
static NS_DEFINE_IID(kWidgetCID, NS_CHILD_CID);
static void
ShowDiffs(nsIFrame* k1, nsIFrame* k2, const nsRect& r1, const nsRect& r2)
{
printf("verifyreflow: ");
k1->ListTag(stdout);
printf(" ");
stdout << r1;
printf(" != ");
k2->ListTag(stdout);
printf(" ");
stdout << r2;
printf("\n");
}
1998-04-13 20:24:54 +00:00
1998-07-13 19:49:42 +00:00
static void
CompareTrees(nsIFrame* aA, nsIFrame* aB)
1998-04-13 20:24:54 +00:00
{
1998-07-13 19:49:42 +00:00
PRInt32 n1, n2;
aA->ChildCount(n1);
aB->ChildCount(n2);
NS_ASSERTION(n1 == n2, "child counts don't match");
nsIFrame* k1, *k2;
aA->FirstChild(k1);
aB->FirstChild(k2);
nsRect r1, r2;
nsIView* v1, *v2;
nsIWidget* w1, *w2;
for (;;) {
if (nsnull == k1) {
NS_ASSERTION(nsnull == k2, "child lists are different");
break;
}
NS_ASSERTION(nsnull != k2, "child lists are different");
// Verify that the frames are the same size
k1->GetRect(r1);
k2->GetRect(r2);
if (r1 != r2) {
ShowDiffs(k1, k2, r1, r2);
}
else {
// Make sure either both have boths or neither have views; if they
// do have views, make sure the views are the same size. If the
// views have widgets, make sure they both do or neither does. If
// they do, make sure the widgets are the same size.
k1->GetView(v1);
k2->GetView(v2);
if (nsnull != v1) {
NS_ASSERTION(nsnull != v2, "child views are not matched");
v1->GetBounds(r1);
v2->GetBounds(r2);
NS_ASSERTION(r1 == r2, "child views are different sizes");
w1 = v1->GetWidget();
w2 = v2->GetWidget();
if (nsnull != w1) {
NS_ASSERTION(nsnull != w2, "child widgets are not matched");
w1->GetBounds(r1);
w2->GetBounds(r2);
NS_ASSERTION(r1 == r2, "child widgets are different sizes");
}
else {
NS_ASSERTION(nsnull == w2, "child widgets are not matched");
}
NS_RELEASE(v1);
NS_RELEASE(v2);
}
else {
NS_ASSERTION(nsnull == v2, "child views are not matched");
}
// Compare the sub-trees too
CompareTrees(k1, k2);
}
// Advance to next sibling
k1->GetNextSibling(k1);
k2->GetNextSibling(k2);
1998-04-13 20:24:54 +00:00
}
1998-07-13 19:49:42 +00:00
}
// XXX: copy of nsWebWidget's ua.css loading code!!!
#define UA_CSS_URL "resource:/res/ua.css"
static nsIStyleSheet* gUAStyleSheet;
static nsresult
InitUAStyleSheet()
{
nsresult rv = NS_OK;
if (nsnull == gUAStyleSheet) { // snarf one
nsIURL* uaURL;
rv = NS_NewURL(&uaURL, nsnull, UA_CSS_URL); // XXX this bites, fix it
if (NS_OK == rv) {
// Get an input stream from the url
PRInt32 ec;
nsIInputStream* in = uaURL->Open(&ec);
if (nsnull != in) {
// Translate the input using the argument character set id into unicode
nsIUnicharInputStream* uin;
rv = NS_NewConverterStream(&uin, nsnull, in);
if (NS_OK == rv) {
// Create parser and set it up to process the input file
nsICSSParser* css;
rv = NS_NewCSSParser(&css);
if (NS_OK == rv) {
// Parse the input and produce a style set
// XXX note: we are ignoring rv until the error code stuff in the
// input routines is converted to use nsresult's
css->Parse(uin, uaURL, gUAStyleSheet);
NS_RELEASE(css);
}
NS_RELEASE(uin);
}
NS_RELEASE(in);
}
else {
// printf("open of %s failed: error=%x\n", UA_CSS_URL, ec);
rv = NS_ERROR_ILLEGAL_VALUE; // XXX need a better error code here
}
NS_RELEASE(uaURL);
}
1998-04-13 20:24:54 +00:00
}
1998-07-13 19:49:42 +00:00
return rv;
1998-04-13 20:24:54 +00:00
}
1998-07-13 19:49:42 +00:00
static nsresult
CreateStyleSet(nsIDocument* aDocument, nsIStyleSet** aStyleSet)
{
nsresult rv = InitUAStyleSheet();
if (NS_OK != rv) {
NS_WARNING("unable to load UA style sheet");
}
rv = NS_NewStyleSet(aStyleSet);
if (NS_OK == rv) {
PRInt32 count = aDocument->GetNumberOfStyleSheets();
for (PRInt32 index = 0; index < count; index++) {
nsIStyleSheet* sheet = aDocument->GetStyleSheetAt(index);
(*aStyleSet)->AppendDocStyleSheet(sheet);
NS_RELEASE(sheet);
}
if (nsnull != gUAStyleSheet) {
(*aStyleSet)->AppendBackstopStyleSheet(gUAStyleSheet);
}
}
return rv;
}
// After an incremental reflow, we verify the correctness by doing a
// full reflow into a fresh frame tree.
void
PresShell::VerifyIncrementalReflow()
{
// All the stuff we are creating that needs releasing
nsIPresContext* cx;
nsIViewManager* vm;
nsIView* view;
nsIPresShell* sh;
nsIStyleSet* ss;
// Create a presentation context to view the new frame tree
nsresult rv;
if (mPresContext->IsPaginated()) {
rv = NS_NewPrintPreviewContext(&cx);
}
else {
rv = NS_NewGalleyContext(&cx);
}
NS_ASSERTION(NS_OK == rv, "failed to create presentation context");
nsIDeviceContext* dc = mPresContext->GetDeviceContext();
cx->Init(dc);
NS_RELEASE(dc);
rv = CreateStyleSet(mDocument, &ss);
NS_ASSERTION(NS_OK == rv, "failed to create style set");
// Get our scrolling preference
nsScrollPreference scrolling;
nsIView* rootView = mViewManager->GetRootView();
nsIScrollableView* scrollView;
rv = rootView->QueryInterface(kScrollViewIID, (void**)&scrollView);
if (NS_OK == rv) {
scrolling = scrollView->GetScrollPreference();
NS_RELEASE(scrollView);
}
nsIWidget* rootWidget = rootView->GetWidget();
void* nativeParentWidget = rootWidget->GetNativeData(NS_NATIVE_WIDGET);
NS_RELEASE(rootView);
// Create a new view manager.
rv = NSRepository::CreateInstance(kViewManagerCID, nsnull, kIViewManagerIID,
(void**) &vm);
if ((NS_OK != rv) || (NS_OK != vm->Init(cx))) {
NS_ASSERTION(NS_OK == rv, "failed to create view manager");
}
// Create a child window of the parent that is our "root view/window"
// Create a view
nsRect tbounds;
mPresContext->GetVisibleArea(tbounds);
// tbounds *= mPresContext->GetPixelsToTwips();
rv = NSRepository::CreateInstance(kScrollingViewCID, nsnull, kIViewIID,
(void **) &view);
if ((NS_OK != rv) || (NS_OK != view->Init(vm, tbounds, nsnull, &kWidgetCID,
nsnull, nativeParentWidget))) {
NS_ASSERTION(NS_OK == rv, "failed to create scroll view");
}
rv = view->QueryInterface(kScrollViewIID, (void**)&scrollView);
if (NS_OK == rv) {
scrollView->SetScrollPreference(scrolling);
NS_RELEASE(scrollView);
}
else {
NS_ASSERTION(0, "invalid scrolling view");
}
// Setup hierarchical relationship in view manager
vm->SetRootView(view);
nsIWidget* window = view->GetWidget();
if (window) {
vm->SetRootWindow(window);
NS_RELEASE(window);
}
NS_RELEASE(view);
// Make the new presentation context the same size as our
// presentation context.
nsRect r;
mPresContext->GetVisibleArea(r);
cx->SetVisibleArea(r);
// Create a new presentation shell to view the document
rv = mDocument->CreateShell(cx, vm, ss, &sh);
NS_ASSERTION(NS_OK == rv, "failed to create presentation shell");
float p2t = cx->GetPixelsToTwips();
sh->ResizeReflow(r.width, r.height);
// Now that the document has been reflowed, use its frame tree to
// compare against our frame tree.
CompareTrees(GetRootFrame(), sh->GetRootFrame());
vm->SetRootView(nsnull);
vm->SetRootWindow(nsnull);
NS_RELEASE(vm);
NS_RELEASE(cx);
NS_RELEASE(sh);
NS_RELEASE(ss);
}
#endif