diff --git a/layout/html/base/src/nsAbsolutelyPositionedContainer.cpp b/layout/html/base/src/nsAbsolutelyPositionedContainer.cpp new file mode 100644 index 000000000000..703a96ffebcd --- /dev/null +++ b/layout/html/base/src/nsAbsolutelyPositionedContainer.cpp @@ -0,0 +1,275 @@ +/* -*- 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 "nsAbsolutelyPositionedContainer.h" +#include "nsContainerFrame.h" +#include "nsHTMLIIDs.h" +#include "nsIAreaFrame.h" +#include "nsIReflowCommand.h" +#include "nsIStyleContext.h" +#include "nsIViewManager.h" +#include "nsLayoutAtoms.h" + +static NS_DEFINE_IID(kAreaFrameIID, NS_IAREAFRAME_IID); + +void +nsAbsolutelyPositionedContainer::DeleteFrames(nsIPresContext& aPresContext) +{ + mAbsoluteFrames.DeleteFrames(aPresContext); +} + +nsresult +nsAbsolutelyPositionedContainer::FirstChild(nsIAtom* aListName, + nsIFrame** aFirstChild) const +{ + NS_PRECONDITION(nsLayoutAtoms::absoluteList == aListName, "unexpected child list name"); + *aFirstChild = mAbsoluteFrames.FirstChild(); + return NS_OK; +} + +nsresult +nsAbsolutelyPositionedContainer::SetInitialChildList(nsIPresContext& aPresContext, + nsIAtom* aListName, + nsIFrame* aChildList) +{ + NS_PRECONDITION(nsLayoutAtoms::absoluteList == aListName, "unexpected child list name"); + mAbsoluteFrames.SetFrames(aChildList); + return NS_OK; +} + +nsresult +nsAbsolutelyPositionedContainer::Reflow(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState) +{ + // Make a copy of the reflow state. If the reason is eReflowReason_Incremental, + // then change it to eReflowReason_Resize + nsHTMLReflowState reflowState(aReflowState); + if (eReflowReason_Incremental == reflowState.reason) { + reflowState.reason = eReflowReason_Resize; + } + + nsIFrame* kidFrame; + for (kidFrame = mAbsoluteFrames.FirstChild(); nsnull != kidFrame; kidFrame->GetNextSibling(&kidFrame)) { + // Reflow the frame + nsReflowStatus kidStatus; + ReflowAbsoluteFrame(aPresContext, reflowState, kidFrame, PR_FALSE, + kidStatus); + } + return NS_OK; +} + +nsresult +nsAbsolutelyPositionedContainer::IncrementalReflow(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, + PRBool& aWasHandled) +{ + // Initialize the OUT paremeter + aWasHandled = PR_FALSE; + + // See if the reflow command is targeted at us + nsIFrame* targetFrame; + aReflowState.reflowCommand->GetTarget(targetFrame); + + if (aReflowState.frame == targetFrame) { + nsIAtom* listName; + PRBool isAbsoluteChild; + + // It's targeted at us. See if the child frame is absolutely positioned + aReflowState.reflowCommand->GetChildListName(listName); + isAbsoluteChild = nsLayoutAtoms::absoluteList == listName; + NS_IF_RELEASE(listName); + + if (isAbsoluteChild) { + nsIReflowCommand::ReflowType type; + nsIFrame* newFrames; + PRInt32 numFrames = 0; + + // Get the type of reflow command + aReflowState.reflowCommand->GetType(type); + + // Handle each specific type + if (nsIReflowCommand::FrameAppended == type) { + // Add the frames to our list of absolutely position frames + aReflowState.reflowCommand->GetChildFrame(newFrames); + NS_ASSERTION(nsnull != newFrames, "null child list"); + numFrames = nsContainerFrame::LengthOf(newFrames); + mAbsoluteFrames.AppendFrames(nsnull, newFrames); + + } else if (nsIReflowCommand::FrameRemoved == type) { + // Get the new frame + nsIFrame* childFrame; + aReflowState.reflowCommand->GetChildFrame(childFrame); + + PRBool result = mAbsoluteFrames.DeleteFrame(aPresContext, childFrame); + NS_ASSERTION(result, "didn't find frame to delete"); + + } else if (nsIReflowCommand::FrameInserted == type) { + // Get the previous sibling + nsIFrame* prevSibling; + aReflowState.reflowCommand->GetPrevSiblingFrame(prevSibling); + + // Insert the new frames + aReflowState.reflowCommand->GetChildFrame(newFrames); + NS_ASSERTION(nsnull != newFrames, "null child list"); + numFrames = nsContainerFrame::LengthOf(newFrames); + mAbsoluteFrames.InsertFrames(nsnull, prevSibling, newFrames); + + } else { + NS_ASSERTION(PR_FALSE, "unexpected reflow type"); + } + + // For inserted and appended reflow commands we need to reflow the + // newly added frames + if ((nsIReflowCommand::FrameAppended == type) || + (nsIReflowCommand::FrameInserted == type)) { + + while (numFrames-- > 0) { + nsReflowStatus status; + + ReflowAbsoluteFrame(aPresContext, aReflowState, newFrames, PR_TRUE, status); + newFrames->GetNextSibling(&newFrames); + } + } + + // Indicate we handled the reflow command + aWasHandled = PR_TRUE; + } + + } else { + // Peek at the next frame in the reflow path + nsIFrame* nextFrame; + aReflowState.reflowCommand->GetNext(nextFrame, PR_FALSE); + + // See if it's one of our absolutely positioned child frames + NS_ASSERTION(nsnull != nextFrame, "next frame in reflow command is null"); + if (mAbsoluteFrames.ContainsFrame(nextFrame)) { + // Remove the next frame from the reflow path + aReflowState.reflowCommand->GetNext(nextFrame, PR_TRUE); + + nsReflowStatus kidStatus; + ReflowAbsoluteFrame(aPresContext, aReflowState, nextFrame, PR_FALSE, kidStatus); + // XXX Make sure the frame is repainted. For the time being, since we + // have no idea what actually changed repaint it all... + nsIView* view; + nextFrame->GetView(&view); + if (nsnull != view) { + nsIViewManager* viewMgr; + view->GetViewManager(viewMgr); + if (nsnull != viewMgr) { + viewMgr->UpdateView(view, (nsIRegion*)nsnull, NS_VMREFRESH_NO_SYNC); + NS_RELEASE(viewMgr); + } + } + aWasHandled = PR_TRUE; + } + } + + return NS_OK; +} + +// XXX Optimize the case where it's a resize reflow and the absolutely +// positioned child has the exact same size and position and skip the +// reflow... +nsresult +nsAbsolutelyPositionedContainer::ReflowAbsoluteFrame(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, + nsIFrame* aKidFrame, + PRBool aInitialReflow, + nsReflowStatus& aStatus) +{ + nsresult rv; + nsMargin border; + + // Get the border values + aReflowState.mStyleSpacing->GetBorder(border); + + nsIHTMLReflow* htmlReflow; + rv = aKidFrame->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); + if (NS_SUCCEEDED(rv)) { + htmlReflow->WillReflow(aPresContext); + + nsSize availSize(aReflowState.computedWidth, NS_UNCONSTRAINEDSIZE); + nsHTMLReflowMetrics kidDesiredSize(nsnull); + nsHTMLReflowState kidReflowState(aPresContext, aReflowState, aKidFrame, + availSize); + + // If it's the initial reflow, then override the reflow reason. This is + // used when frames are inserted incrementally + if (aInitialReflow) { + kidReflowState.reason = eReflowReason_Initial; + } + + rv = htmlReflow->Reflow(aPresContext, kidDesiredSize, kidReflowState, aStatus); + + // XXX If the child had a fixed height, then make sure it respected it... + if (NS_AUTOHEIGHT != kidReflowState.computedHeight) { + if (kidDesiredSize.height < kidReflowState.computedHeight) { + kidDesiredSize.height = kidReflowState.computedHeight; + kidDesiredSize.height += kidReflowState.mComputedBorderPadding.top + + kidReflowState.mComputedBorderPadding.bottom; + } + } + + // Position the child relative to our padding edge + nsRect rect(border.left + kidReflowState.computedOffsets.left + kidReflowState.computedMargin.left, + border.top + kidReflowState.computedOffsets.top + kidReflowState.computedMargin.top, + kidDesiredSize.width, kidDesiredSize.height); + aKidFrame->SetRect(rect); + } + + return rv; +} + +nsresult +nsAbsolutelyPositionedContainer::GetPositionedInfo(nscoord& aXMost, nscoord& aYMost) const +{ + aXMost = aYMost = 0; + for (nsIFrame* f = mAbsoluteFrames.FirstChild(); nsnull != f; f->GetNextSibling(&f)) { + // Get the frame's x-most and y-most. This is for its flowed content only + nsRect rect; + f->GetRect(rect); + + if (rect.XMost() > aXMost) { + aXMost = rect.XMost(); + } + if (rect.YMost() > aYMost) { + aYMost = rect.YMost(); + } + + // If the child frame is also an area frame, then take into account its child + // absolutely positioned elements + nsIAreaFrame* areaFrame; + if (NS_SUCCEEDED(f->QueryInterface(kAreaFrameIID, (void**)&areaFrame))) { + nscoord xMost, yMost; + + areaFrame->GetPositionedInfo(xMost, yMost); + // Convert to our coordinate space + xMost += rect.x; + yMost += rect.y; + + if (xMost > aXMost) { + aXMost = xMost; + } + if (yMost > aYMost) { + aYMost = yMost; + } + } + } + + return NS_OK; +} + diff --git a/layout/html/base/src/nsAbsolutelyPositionedContainer.h b/layout/html/base/src/nsAbsolutelyPositionedContainer.h new file mode 100644 index 000000000000..6144dbb406b9 --- /dev/null +++ b/layout/html/base/src/nsAbsolutelyPositionedContainer.h @@ -0,0 +1,76 @@ +/* -*- 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. + */ +#ifndef nsAbsolutelyPositionedContainer_h___ +#define nsAbsolutelyPositionedContainer_h___ + +#include "nslayout.h" +#include "nsIHTMLReflow.h" +#include "nsFrameList.h" + +class nsIAtom; +class nsIFrame; +class nsIPresContext; + +/** + * This class contains the logic for being an absolutely containing block. + * + * There is no principal child list, just a named child list which contains + * the absolutely positioned frames + * + * @see nsLayoutAtoms::absoluteList + */ +class nsAbsolutelyPositionedContainer +{ +public: + nsresult FirstChild(nsIAtom* aListName, nsIFrame** aFirstChild) const; + + nsresult SetInitialChildList(nsIPresContext& aPresContext, + nsIAtom* aListName, + nsIFrame* aChildList); + + // Called by the delegating frame after it has done its reflow first. This + // function will reflow any absolutely positioned child frames that need to + // be reflowed, e.g., because the absolutely positioned child frame has + // 'auto' for an offset, or a percentage based width or height + nsresult Reflow(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState); + + // Called only for a reflow reason of eReflowReason_Incremental. The + // aWasHandled return value indicates whether the reflow command was + // handled (i.e., the reflow command involved an absolutely positioned + // child element), or whether the caller should handle it + nsresult IncrementalReflow(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, + PRBool& aWasHandled); + + void DeleteFrames(nsIPresContext& aPresContext); + + nsresult GetPositionedInfo(nscoord& aXMost, nscoord& aYMost) const; + +protected: + nsresult ReflowAbsoluteFrame(nsIPresContext& aPresContext, + const nsHTMLReflowState& aReflowState, + nsIFrame* aKidFrame, + PRBool aInitialReflow, + nsReflowStatus& aStatus); + +protected: + nsFrameList mAbsoluteFrames; // additional named child list +}; + +#endif /* nsAbsolutelyPositionedContainer_h___ */