Mozilla/mozilla/layout/xul/base/src/nsBoxLayoutState.cpp

297 lines
11 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla 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/MPL/
*
* 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 of 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 MPL, 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 MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
//
// Eric Vaughan
// Netscape Communications
//
// See documentation in associated header file
//
#include "nsBoxLayoutState.h"
#include "nsReflowPath.h"
#include "nsBoxFrame.h"
#include "nsHTMLAtoms.h"
#include "nsXULAtoms.h"
#include "nsIContent.h"
#include "nsPresContext.h"
nsBoxLayoutState::nsBoxLayoutState(nsPresContext* aPresContext):mPresContext(aPresContext),
mReflowState(nsnull),
mType(Dirty),
mMaxElementWidth(nsnull),
mScrolledBlockSizeConstraint(-1,-1),
mLayoutFlags(0),
mPaintingDisabled(PR_FALSE)
{
NS_ASSERTION(mPresContext, "PresContext must be non-null");
}
nsBoxLayoutState::nsBoxLayoutState(const nsBoxLayoutState& aState)
{
mPresContext = aState.mPresContext;
mType = aState.mType;
mReflowState = aState.mReflowState;
mMaxElementWidth = aState.mMaxElementWidth;
mScrolledBlockSizeConstraint = aState.mScrolledBlockSizeConstraint;
mLayoutFlags = aState.mLayoutFlags;
mPaintingDisabled = aState.mPaintingDisabled;
NS_ASSERTION(mPresContext, "PresContext must be non-null");
}
nsBoxLayoutState::nsBoxLayoutState(nsIPresShell* aShell):mReflowState(nsnull),
mType(Dirty),
mMaxElementWidth(nsnull),
mScrolledBlockSizeConstraint(-1,-1),
mLayoutFlags(0),
mPaintingDisabled(PR_FALSE)
{
mPresContext = aShell->GetPresContext();
NS_ASSERTION(mPresContext, "PresContext must be non-null");
}
nsBoxLayoutState::nsBoxLayoutState(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsHTMLReflowMetrics& aDesiredSize):mPresContext(aPresContext),
mReflowState(&aReflowState),
mType(Dirty),
mMaxElementWidth(nsnull),
mScrolledBlockSizeConstraint(-1,-1),
mLayoutFlags(0),
mPaintingDisabled(PR_FALSE)
{
if (aDesiredSize.mComputeMEW)
mMaxElementWidth = &aDesiredSize.mMaxElementWidth;
NS_ASSERTION(mPresContext, "PresContext must be non-null");
}
void
nsBoxLayoutState::HandleReflow(nsIBox* aRootBox)
{
switch(mReflowState->reason)
{
case eReflowReason_Incremental:
{
// XXXwaterson according to evaughan's prior comment here,
// the target ought not to be a box frame. This makes some
// of the logic in `Unwind' confusing.
Unwind(mReflowState->path, 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(nsReflowPath* aReflowPath, nsIBox* aRootBox)
{
// If incremental, unwind the reflow path, updating dirty bits
// appropriately. We recursively descend through the reflow tree,
// clearing the NS_FRAME_HAS_DIRTY_CHILDREN bit on each frame until
// we reach a target frame. When we reach a target frame, we set the
// NS_FRAME_HAS_DIRTY_CHILDREN bit on the root box's frame, and then
// call the target box's MarkDirty method. This will reset the
// NS_FRAME_HAS_DIRTY_CHILDREN bit on each box on the path back to
// root, as well as initialize each box correctly for a dirty
// layout.
nsReflowPath::iterator iter = aReflowPath->FirstChild();
nsReflowPath::iterator end = aReflowPath->EndChildren();
for ( ; iter != end; ++iter) {
// Get the box for the given frame.
PRBool isAdaptor = PR_FALSE;
nsIBox* ibox = GetBoxForFrame(*iter, isAdaptor);
if (! ibox) {
NS_ERROR("This should not happen! We should always get a box");
continue;
}
// Clear the dirty-children bit. This will be re-set by MarkDirty
// once we reach a target.
(*iter)->RemoveStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
if (isAdaptor) {
// It's nested HTML. Mark the root box's frame with
// NS_FRAME_HAS_DIRTY_CHILDREN so MarkDirty won't walk off the
// top of the box hierarchy and schedule another reflow command.
aRootBox->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
// Clear the frame's dirty bit so that MarkDirty doesn't
// optimize the layout away.
(*iter)->RemoveStateBits(NS_FRAME_IS_DIRTY);
// Mark the adaptor dirty.
ibox->MarkDirty(*this);
// We are done and we did not coelesce.
continue;
}
// Is the box frame the target?
// XXXwaterson according to the evaughan's previous comments in
// HandleReflow, it ought to never be. Yet here we are.
nsHTMLReflowCommand *command = iter.get()->mReflowCommand;
if (command) {
// Mark the root box's frame with NS_FRAME_HAS_DIRTY_CHILDREN so
// that MarkDirty won't walk off the top of the box hierarchy
// and schedule another reflow command.
aRootBox->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
// The target is a box. Mark it dirty, generating a new reflow
// command targeted at us and coelesce out this one.
nsReflowType type;
command->GetType(type);
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) {
parent->AddStateBits(NS_FRAME_IS_DIRTY);
}
}
else
ibox->MarkDirty(*this);
}
// Recursively unwind the reflow path.
Unwind(iter.get(), aRootBox);
}
}
nsIBox*
nsBoxLayoutState::GetBoxForFrame(nsIFrame* aFrame, PRBool& aIsAdaptor)
{
if (aFrame && !aFrame->IsBoxFrame())
aIsAdaptor = PR_TRUE;
return aFrame;
}
/*
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 = aPresShell->AllocateFrame(sz);
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);
}