Mozilla/mozilla/layout/xul/base/src/nsBoxLayoutState.cpp
cathleen%netscape.com dd8ebc5bd1 eliminate nsCRT::zero() to use memset to zero instead, bug 118135, r=dp sr=waterson
git-svn-id: svn://10.0.0.236/trunk@113183 18797224-902f-48f8-a5cc-f745e15eee43
2002-01-30 02:55:57 +00:00

472 lines
14 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* 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 Communicator client code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
//
// Eric Vaughan
// Netscape Communications
//
// See documentation in associated header file
//
#include "nsBoxLayoutState.h"
#include "nsHTMLReflowCommand.h"
#include "nsBoxFrame.h"
#include "nsIStyleContext.h"
#include "nsHTMLAtoms.h"
#include "nsXULAtoms.h"
#include "nsIContent.h"
#include "nsINameSpaceManager.h"
#include "nsIPresContext.h"
nsBoxLayoutState::nsBoxLayoutState(nsIPresContext* aPresContext):mPresContext(aPresContext),
mReflowState(nsnull),
mType(Dirty),
mMaxElementSize(nsnull),
mOverFlowSize(0,0),
mIncludeOverFlow(PR_TRUE),
mLayoutFlags(0),
mDisablePainting(PR_FALSE)
{
}
nsBoxLayoutState::nsBoxLayoutState(const nsBoxLayoutState& aState)
{
mPresContext = aState.mPresContext;
mType = aState.mType;
mReflowState = aState.mReflowState;
mMaxElementSize = aState.mMaxElementSize;
mOverFlowSize = aState.mOverFlowSize;
mLayoutFlags = aState.mLayoutFlags;
mDisablePainting = aState.mDisablePainting;
}
nsBoxLayoutState::nsBoxLayoutState(nsIPresShell* aShell):mReflowState(nsnull),
mType(Dirty),
mMaxElementSize(nsnull),
mOverFlowSize(0,0),
mIncludeOverFlow(PR_TRUE),
mLayoutFlags(0),
mDisablePainting(PR_FALSE)
{
aShell->GetPresContext(getter_AddRefs(mPresContext));
}
nsBoxLayoutState::nsBoxLayoutState(nsIPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsHTMLReflowMetrics& aDesiredSize):mPresContext(aPresContext),
mReflowState(&aReflowState),
mType(Dirty),
mOverFlowSize(0,0),
mIncludeOverFlow(PR_TRUE),
mLayoutFlags(0),
mDisablePainting(PR_FALSE)
{
mMaxElementSize = aDesiredSize.maxElementSize;
}
void
nsBoxLayoutState::GetMaxElementSize(nsSize** aMaxElementSize)
{
*aMaxElementSize = nsnull;
if (mReflowState)
*aMaxElementSize = mMaxElementSize;
}
void
nsBoxLayoutState::GetOverFlowSize(nsSize& aSize)
{
aSize = mOverFlowSize;
}
void
nsBoxLayoutState::SetOverFlowSize(const nsSize& aSize)
{
mOverFlowSize = aSize;
}
void
nsBoxLayoutState::GetIncludeOverFlow(PRBool& aOverflow)
{
aOverflow = mIncludeOverFlow;
}
void
nsBoxLayoutState::SetLayoutFlags(const PRUint32& aFlags)
{
mLayoutFlags = aFlags;
}
void
nsBoxLayoutState::GetLayoutFlags(PRUint32& aFlags)
{
aFlags = mLayoutFlags;
}
void
nsBoxLayoutState::SetIncludeOverFlow(const PRBool& aOverflow)
{
mIncludeOverFlow = aOverflow;
}
void
nsBoxLayoutState::HandleReflow(nsIBox* aRootBox)
{
switch(mReflowState->reason)
{
case eReflowReason_Incremental:
{
// ok if the target was not a box. Then unwind it down
UnWind(mReflowState->reflowCommand, aRootBox);
mType = Dirty;
break;
}
case eReflowReason_Dirty:
mType = Dirty;
break;
// if the a resize reflow then it doesn't need to be reflowed. Only if the size is different
// from the new size would we actually do a reflow
case eReflowReason_Resize:
mType = Resize;
break;
// if its an initial reflow we must place the child.
// otherwise we might think it was already placed when it wasn't
case eReflowReason_Initial:
mType = Initial;
break;
case eReflowReason_StyleChange:
// printf("STYLE CHANGE REFLOW. Blowing away all box caches!!\n");
aRootBox->MarkChildrenStyleChange();
// fall through to dirty
default:
mType = Dirty;
}
}
void
nsBoxLayoutState::UnWind(nsHTMLReflowCommand* aCommand, nsIBox* aBox)
{
// if incremental unwindow the chain
nsIFrame* incrementalChild = nsnull;
nsIFrame* target = nsnull;
aCommand->GetTarget(target);
nsReflowType type;
mReflowState->reflowCommand->GetType(type);
while(1)
{
aCommand->GetNext(incrementalChild, PR_FALSE);
if (incrementalChild == nsnull)
return;
// get the box for the given incrementalChild. If adaptor is true then
// it is some wrapped HTML frame.
PRBool isAdaptor = PR_FALSE;
nsIBox* ibox = GetBoxForFrame(incrementalChild, isAdaptor);
if (ibox) {
nsFrameState state;
incrementalChild->GetFrameState(&state);
state &= ~NS_FRAME_HAS_DIRTY_CHILDREN;
incrementalChild->SetFrameState(state);
// ok we got a box is it the target?
if (incrementalChild == target) {
nsFrameState boxState;
nsIFrame* frame;
aBox->GetFrame(&frame);
frame->GetFrameState(&boxState);
boxState |= NS_FRAME_HAS_DIRTY_CHILDREN;
frame->SetFrameState(boxState);
// the target is a box?
// mark it dirty generating a new reflow command targeted
// at us and coelesce out this one.
if (type == eReflowType_StyleChanged) {
ibox->MarkStyleChange(*this);
// could be a visiblity change. Like collapse so we need to dirty
// parent so it gets redrawn. But be carefull we
// don't want to just mark dirty that would notify the
// box and it would notify its layout manager. This would
// be really bad for grid because it would blow away
// all is cached infomation for is colums and rows. Because the
// our parent is most likely a rows or columns and it will think
// its child is getting bigger or something.
nsIBox* parent;
ibox->GetParentBox(&parent);
if (parent) {
nsFrameState parentState;
nsIFrame* parentFrame;
parent->GetFrame(&parentFrame);
parentFrame->GetFrameState(&parentState);
parentState |= NS_FRAME_IS_DIRTY;
parentFrame->SetFrameState(parentState);
}
} else {
ibox->MarkDirty(*this);
}
return;
}
// was the child html?
if (isAdaptor) {
// the target is deep inside html we will have to honor this one.
// mark us as dirty so we don't post
// a dirty reflow
state = 0;
nsIFrame* frame;
aBox->GetFrame(&frame);
frame->GetFrameState(&state);
state |= NS_FRAME_HAS_DIRTY_CHILDREN;
frame->SetFrameState(state);
incrementalChild->GetFrameState(&state);
state &= ~NS_FRAME_IS_DIRTY;
incrementalChild->SetFrameState(state);
// mark the adaptor dirty
ibox->MarkDirty(*this);
// we are done and we did not coelesce
return;
}
} else {
NS_ERROR("This should not happen! We should always get a box");
break;
}
aCommand->GetNext(incrementalChild);
}
}
/*
void
nsBoxLayoutState::UnWind(nsHTMLReflowCommand* aCommand, nsIBox* aBox)
{
nsFrameState state;
nsIFrame* frame;
aBox->GetFrame(&frame);
frame->GetFrameState(&state);
state |= NS_FRAME_HAS_DIRTY_CHILDREN;
frame->SetFrameState(state);
// if incremental unwindow the chain
nsIFrame* incrementalChild = nsnull;
while(1)
{
aCommand->GetNext(incrementalChild, PR_FALSE);
if (incrementalChild == nsnull)
break;
nsIFrame* target = nsnull;
aCommand->GetTarget(target);
PRBool isAdaptor = PR_FALSE;
nsIBox* ibox = GetBoxForFrame(incrementalChild, isAdaptor);
if (ibox) {
if (incrementalChild == target || isAdaptor) {
ibox->MarkDirty(*this);
if (isAdaptor)
break;
} else
ibox->MarkDirtyChildren(*this);
} else {
break;
}
aCommand->GetNext(incrementalChild);
}
}
*/
nsIBox*
nsBoxLayoutState::GetTargetBox(nsHTMLReflowCommand* mCommand, PRBool& aIsAdaptor)
{
nsIFrame* target = nsnull;
mReflowState->reflowCommand->GetTarget(target);
return GetBoxForFrame(target, aIsAdaptor);
}
nsIBox*
nsBoxLayoutState::GetBoxForFrame(nsIFrame* aFrame, PRBool& aIsAdaptor)
{
if (aFrame == nsnull)
return nsnull;
nsIBox* ibox = nsnull;
if (NS_FAILED(aFrame->QueryInterface(NS_GET_IID(nsIBox), (void**)&ibox))) {
aIsAdaptor = PR_TRUE;
// if we hit a non box. Find the box in out last container
// and clear its cache.
nsIFrame* parent = nsnull;
aFrame->GetParent(&parent);
nsIBox* parentBox = nsnull;
if (NS_FAILED(parent->QueryInterface(NS_GET_IID(nsIBox), (void**)&parentBox)))
return nsnull;
if (parentBox) {
nsIBox* start = nsnull;
parentBox->GetChildBox(&start);
while (start) {
nsIFrame* frame = nsnull;
start->GetFrame(&frame);
if (frame == aFrame) {
ibox = start;
break;
}
start->GetNextBox(&start);
}
}
}
return ibox;
}
/*
void
nsBoxLayoutState::DirtyAllChildren(nsBoxLayoutState& aState, nsIBox* aBox)
{
aBox->MarkDirty(aState);
nsIBox* first = nsnull;
aBox->GetChildBox(&first);
if (first)
aBox->MarkDirtyChildren(aState);
while(first)
{
DirtyAllChildren(aState, first);
first->GetNextBox(&first);
}
}
*/
void*
nsBoxLayoutState::Allocate(size_t sz, nsIPresShell* aPresShell)
{
// Check the recycle list first.
void* result = nsnull;
aPresShell->AllocateFrame(sz, &result);
if (result) {
memset(result, 0, sz);
}
return result;
}
// Overridden to prevent the global delete from being called, since the memory
// came out of an nsIArena instead of the global delete operator's heap.
void
nsBoxLayoutState::Free(void* aPtr, size_t sz)
{
// Don't let the memory be freed, since it will be recycled
// instead. Don't call the global operator delete.
// Stash the size of the object in the first four bytes of the
// freed up memory. The Destroy method can then use this information
// to recycle the object.
size_t* szPtr = (size_t*)aPtr;
*szPtr = sz;
}
void
nsBoxLayoutState::RecycleFreedMemory(nsIPresShell* aShell, void* aMem)
{
size_t* sz = (size_t*)aMem;
aShell->FreeFrame(*sz, aMem);
}
nsresult
nsBoxLayoutState::GetPresShell(nsIPresShell** aShell)
{
if (mPresContext)
return mPresContext->GetShell(aShell);
else {
*aShell = nsnull;
return NS_OK;
}
}
nsresult
nsBoxLayoutState::PushStackMemory()
{
nsCOMPtr<nsIPresShell> shell;
mPresContext->GetShell(getter_AddRefs(shell));
return shell->PushStackMemory();
}
nsresult
nsBoxLayoutState::PopStackMemory()
{
nsCOMPtr<nsIPresShell> shell;
mPresContext->GetShell(getter_AddRefs(shell));
return shell->PopStackMemory();
}
nsresult
nsBoxLayoutState::AllocateStackMemory(size_t aSize, void** aResult)
{
nsCOMPtr<nsIPresShell> shell;
mPresContext->GetShell(getter_AddRefs(shell));
return shell->AllocateStackMemory(aSize, aResult);
}