425 lines
14 KiB
C++
425 lines
14 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.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 "nsAbsoluteFrame.h"
|
|
#include "nsIContentDelegate.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsIPtr.h"
|
|
#include "nsIScrollableView.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsIView.h"
|
|
#include "nsIViewManager.h"
|
|
#include "nsBodyFrame.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsViewsCID.h"
|
|
|
|
static NS_DEFINE_IID(kIScrollableViewIID, NS_ISCROLLABLEVIEW_IID);
|
|
|
|
NS_DEF_PTR(nsIStyleContext);
|
|
|
|
nsresult
|
|
nsAbsoluteFrame::NewFrame(nsIFrame** aInstancePtrResult,
|
|
nsIContent* aContent,
|
|
nsIFrame* aParent)
|
|
{
|
|
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
|
|
if (nsnull == aInstancePtrResult) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsIFrame* it = new nsAbsoluteFrame(aContent, aParent);
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
*aInstancePtrResult = it;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsAbsoluteFrame::nsAbsoluteFrame(nsIContent* aContent, nsIFrame* aParent)
|
|
: nsFrame(aContent, aParent)
|
|
{
|
|
mFrame = nsnull;
|
|
}
|
|
|
|
nsAbsoluteFrame::~nsAbsoluteFrame()
|
|
{
|
|
}
|
|
|
|
nsIView* nsAbsoluteFrame::CreateView(nsIView* aContainingView,
|
|
const nsRect& aRect,
|
|
nsStylePosition* aPosition,
|
|
nsStyleDisplay* aDisplay) const
|
|
{
|
|
nsIView* view;
|
|
|
|
static NS_DEFINE_IID(kViewCID, NS_VIEW_CID);
|
|
static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID);
|
|
|
|
nsresult result = NSRepository::CreateInstance(kViewCID,
|
|
nsnull,
|
|
kIViewIID,
|
|
(void **)&view);
|
|
if (NS_OK == result) {
|
|
nsIViewManager* viewManager = aContainingView->GetViewManager();
|
|
|
|
// Initialize the view
|
|
NS_ASSERTION(nsnull != viewManager, "null view manager");
|
|
|
|
// See if the containing view is a scroll view
|
|
nsIScrollableView* scrollView = nsnull;
|
|
nsresult result;
|
|
nsViewClip clip = {0, 0, 0, 0};
|
|
PRUint8 clipType = (aDisplay->mClipFlags & NS_STYLE_CLIP_TYPE_MASK);
|
|
nsViewClip* pClip = nsnull;
|
|
|
|
// Is there a clip rect specified?
|
|
if (NS_STYLE_CLIP_RECT == clipType) {
|
|
if ((NS_STYLE_CLIP_LEFT_AUTO & aDisplay->mClipFlags) == 0) {
|
|
clip.mLeft = aDisplay->mClip.left;
|
|
}
|
|
if ((NS_STYLE_CLIP_RIGHT_AUTO & aDisplay->mClipFlags) == 0) {
|
|
clip.mRight = aDisplay->mClip.right;
|
|
}
|
|
if ((NS_STYLE_CLIP_TOP_AUTO & aDisplay->mClipFlags) == 0) {
|
|
clip.mTop = aDisplay->mClip.top;
|
|
}
|
|
if ((NS_STYLE_CLIP_BOTTOM_AUTO & aDisplay->mClipFlags) == 0) {
|
|
clip.mBottom = aDisplay->mClip.bottom;
|
|
}
|
|
pClip = &clip;
|
|
}
|
|
else if (NS_STYLE_CLIP_INHERIT == clipType) {
|
|
// XXX need to handle clip inherit (get from parent style context)
|
|
NS_NOTYETIMPLEMENTED("clip inherit");
|
|
}
|
|
|
|
PRInt32 zIndex = 0;
|
|
if (aPosition->mZIndex.GetUnit() == eStyleUnit_Integer) {
|
|
zIndex = aPosition->mZIndex.GetIntValue();
|
|
} else if (aPosition->mZIndex.GetUnit() == eStyleUnit_Auto) {
|
|
zIndex = 0;
|
|
} else if (aPosition->mZIndex.GetUnit() == eStyleUnit_Inherit) {
|
|
// XXX need to handle z-index "inherit"
|
|
NS_NOTYETIMPLEMENTED("zIndex: inherit");
|
|
}
|
|
|
|
result = aContainingView->QueryInterface(kIScrollableViewIID, (void**)&scrollView);
|
|
if (NS_OK == result) {
|
|
nsIView* scrolledView = scrollView->GetScrolledView();
|
|
|
|
view->Init(viewManager, aRect, scrolledView, nsnull, nsnull, nsnull,
|
|
zIndex, pClip);
|
|
viewManager->InsertChild(scrolledView, view, 0);
|
|
NS_RELEASE(scrolledView);
|
|
NS_RELEASE(scrollView);
|
|
} else {
|
|
view->Init(viewManager, aRect, aContainingView, nsnull, nsnull, nsnull,
|
|
zIndex, pClip);
|
|
viewManager->InsertChild(aContainingView, view, 0);
|
|
}
|
|
|
|
NS_RELEASE(viewManager);
|
|
}
|
|
|
|
return view;
|
|
}
|
|
|
|
// Returns the offset from this frame to aFrameTo
|
|
void nsAbsoluteFrame::GetOffsetFromFrame(nsIFrame* aFrameTo, nsPoint& aOffset) const
|
|
{
|
|
nsIFrame* frame = (nsIFrame*)this;
|
|
|
|
aOffset.MoveTo(0, 0);
|
|
do {
|
|
nsPoint origin;
|
|
|
|
frame->GetOrigin(origin);
|
|
aOffset += origin;
|
|
frame->GetGeometricParent(frame);
|
|
} while ((nsnull != frame) && (frame != aFrameTo));
|
|
}
|
|
|
|
void nsAbsoluteFrame::ComputeViewBounds(nsIFrame* aContainingBlock,
|
|
nsStylePosition* aPosition,
|
|
nsRect& aRect) const
|
|
{
|
|
nsRect containingRect;
|
|
|
|
// XXX We should be using the inner rect, and not just the bounding rect.
|
|
// Because of the way the frame sizing protocol works (it's top-down, and
|
|
// the size of a container is set after reflowing its children), get the
|
|
// rect from the containing block's view
|
|
nsIView* containingView;
|
|
aContainingBlock->GetView(containingView);
|
|
containingView->GetBounds(containingRect);
|
|
NS_RELEASE(containingView);
|
|
containingRect.x = containingRect.y = 0;
|
|
|
|
// Compute the offset and size of the view based on the position properties
|
|
// and the inner rect of the containing block
|
|
nsStylePosition* position = (nsStylePosition*)mStyleContext->GetData(eStyleStruct_Position);
|
|
|
|
// If either the left or top are 'auto' then get the offset of our frame from
|
|
// the containing block
|
|
nsPoint offset; // offset from containing block
|
|
if ((eStyleUnit_Auto == position->mLeftOffset.GetUnit()) ||
|
|
(eStyleUnit_Auto == position->mTopOffset.GetUnit())) {
|
|
GetOffsetFromFrame(aContainingBlock, offset);
|
|
}
|
|
|
|
// x-offset
|
|
if (eStyleUnit_Auto == position->mLeftOffset.GetUnit()) {
|
|
// Use the current x-offset of our frame translated into the coordinate space
|
|
// of the containing block
|
|
aRect.x = offset.x;
|
|
} else if (eStyleUnit_Coord == position->mLeftOffset.GetUnit()) {
|
|
aRect.x = position->mLeftOffset.GetCoordValue();
|
|
} else {
|
|
NS_ASSERTION(eStyleUnit_Percent == position->mLeftOffset.GetUnit(),
|
|
"unexpected offset type");
|
|
// Percentage values refer to the width of the containing block
|
|
aRect.x = containingRect.x +
|
|
(nscoord)((float)containingRect.width *
|
|
position->mLeftOffset.GetPercentValue());
|
|
}
|
|
|
|
// y-offset
|
|
if (eStyleUnit_Auto == position->mTopOffset.GetUnit()) {
|
|
// Use the current y-offset of our frame translated into the coordinate space
|
|
// of the containing block
|
|
aRect.y = offset.y;
|
|
} else if (eStyleUnit_Coord == position->mTopOffset.GetUnit()) {
|
|
aRect.y = position->mTopOffset.GetCoordValue();
|
|
} else {
|
|
NS_ASSERTION(eStyleUnit_Percent == position->mTopOffset.GetUnit(),
|
|
"unexpected offset type");
|
|
// Percentage values refer to the height of the containing block
|
|
aRect.y = containingRect.y +
|
|
(nscoord)((float)containingRect.height *
|
|
position->mTopOffset.GetPercentValue());
|
|
}
|
|
|
|
// width
|
|
if (eStyleUnit_Auto == position->mWidth.GetUnit()) {
|
|
// Use the right-edge of the containing block
|
|
aRect.width = containingRect.width - aRect.x;
|
|
} else if (eStyleUnit_Coord == position->mWidth.GetUnit()) {
|
|
aRect.width = position->mWidth.GetCoordValue();
|
|
} else {
|
|
NS_ASSERTION(eStyleUnit_Percent == position->mWidth.GetUnit(),
|
|
"unexpected width type");
|
|
aRect.width = (nscoord)((float)containingRect.width *
|
|
position->mWidth.GetPercentValue());
|
|
}
|
|
|
|
// height
|
|
if (eStyleUnit_Auto == position->mHeight.GetUnit()) {
|
|
// Allow it to be as high as it wants
|
|
aRect.height = NS_UNCONSTRAINEDSIZE;
|
|
} else if (eStyleUnit_Coord == position->mHeight.GetUnit()) {
|
|
aRect.height = position->mHeight.GetCoordValue();
|
|
} else {
|
|
NS_ASSERTION(eStyleUnit_Percent == position->mHeight.GetUnit(),
|
|
"unexpected height type");
|
|
aRect.height = (nscoord)((float)containingRect.height *
|
|
position->mHeight.GetPercentValue());
|
|
}
|
|
}
|
|
|
|
nsIFrame* nsAbsoluteFrame::GetContainingBlock() const
|
|
{
|
|
// Look for a containing frame that is absolutely positioned. If we don't
|
|
// find one then use the initial containg block which is the root frame
|
|
nsIFrame* lastFrame = (nsIFrame*)this;
|
|
nsIFrame* result;
|
|
|
|
GetContentParent(result);
|
|
while (nsnull != result) {
|
|
nsStylePosition* position;
|
|
|
|
// Get the style data
|
|
result->GetStyleData(eStyleStruct_Position, (nsStyleStruct*&)position);
|
|
|
|
if (position->mPosition == NS_STYLE_POSITION_ABSOLUTE) {
|
|
break;
|
|
}
|
|
|
|
// Get the next contentual parent
|
|
lastFrame = result;
|
|
result->GetContentParent(result);
|
|
}
|
|
|
|
if (nsnull == result) {
|
|
result = lastFrame;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
NS_METHOD nsAbsoluteFrame::Reflow(nsIPresContext* aPresContext,
|
|
nsReflowMetrics& aDesiredSize,
|
|
const nsReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
// Have we created the absolutely positioned item yet?
|
|
if (nsnull == mFrame) {
|
|
// If the content object is a container then wrap it in a body pseudo-frame
|
|
if (mContent->CanContainChildren()) {
|
|
nsBodyFrame::NewFrame(&mFrame, mContent, this);
|
|
|
|
// Use our style context for the pseudo-frame
|
|
mFrame->SetStyleContext(aPresContext, mStyleContext);
|
|
|
|
} else {
|
|
// Create the absolutely positioned item as a pseudo-frame child. We'll
|
|
// also create a view
|
|
nsIContentDelegate* delegate = mContent->GetDelegate(aPresContext);
|
|
|
|
nsresult rv = delegate->CreateFrame(aPresContext, mContent, this,
|
|
mStyleContext, mFrame);
|
|
NS_RELEASE(delegate);
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
}
|
|
|
|
// Get the containing block, and its associated view
|
|
nsIFrame* containingBlock = GetContainingBlock();
|
|
|
|
// Use the position properties to determine the offset and size
|
|
nsStylePosition* position = (nsStylePosition*)mStyleContext->GetData(eStyleStruct_Position);
|
|
nsRect rect;
|
|
|
|
ComputeViewBounds(containingBlock, position, rect);
|
|
|
|
// Create a view for the frame
|
|
nsStyleDisplay* display = (nsStyleDisplay*)mStyleContext->GetData(eStyleStruct_Display);
|
|
nsIView* containingView;
|
|
containingBlock->GetView(containingView);
|
|
nsIView* view = CreateView(containingView, rect, position, display);
|
|
NS_RELEASE(containingView);
|
|
|
|
mFrame->SetView(view);
|
|
NS_RELEASE(view);
|
|
|
|
// Resize reflow the absolutely positioned element
|
|
nsSize availSize(rect.width, rect.height);
|
|
|
|
if (NS_STYLE_OVERFLOW_VISIBLE == display->mOverflow) {
|
|
// Don't constrain the height since the container should be enlarged to
|
|
// contain overflowing frames
|
|
availSize.height = NS_UNCONSTRAINEDSIZE;
|
|
}
|
|
|
|
nsReflowMetrics desiredSize(nsnull);
|
|
nsReflowState reflowState(mFrame, aReflowState, availSize, eReflowReason_Initial);
|
|
mFrame->WillReflow(*aPresContext);
|
|
mFrame->Reflow(aPresContext, desiredSize, reflowState, aStatus);
|
|
|
|
// Figure out what size to actually use. If the position style is 'auto' or
|
|
// the container should be enlarged to contain overflowing frames then use
|
|
// the desired size
|
|
if ((eStyleUnit_Auto == position->mWidth.GetUnit()) ||
|
|
((desiredSize.width > availSize.width) &&
|
|
(NS_STYLE_OVERFLOW_VISIBLE == display->mOverflow))) {
|
|
rect.width = desiredSize.width;
|
|
}
|
|
if (eStyleUnit_Auto == position->mHeight.GetUnit()) {
|
|
rect.height = desiredSize.height;
|
|
}
|
|
mFrame->SizeTo(rect.width, rect.height);
|
|
mFrame->DidReflow(*aPresContext, NS_FRAME_REFLOW_FINISHED);
|
|
}
|
|
|
|
// Return our desired size as (0, 0)
|
|
return nsFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
|
|
}
|
|
|
|
NS_METHOD
|
|
nsAbsoluteFrame::ChildCount(PRInt32& aChildCount) const
|
|
{
|
|
aChildCount = 1;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_METHOD
|
|
nsAbsoluteFrame::ChildAt(PRInt32 aIndex, nsIFrame*& aFrame) const
|
|
{
|
|
aFrame = (0 == aIndex) ? mFrame : nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_METHOD
|
|
nsAbsoluteFrame::IndexOf(const nsIFrame* aChild, PRInt32& aIndex) const
|
|
{
|
|
aIndex = (aChild == mFrame) ? 0 : -1;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_METHOD
|
|
nsAbsoluteFrame::FirstChild(nsIFrame*& aFirstChild) const
|
|
{
|
|
aFirstChild = mFrame;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_METHOD
|
|
nsAbsoluteFrame::NextChild(const nsIFrame* aChild, nsIFrame*& aNextChild) const
|
|
{
|
|
aNextChild = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_METHOD
|
|
nsAbsoluteFrame::PrevChild(const nsIFrame* aChild, nsIFrame*& aPrevChild) const
|
|
{
|
|
aPrevChild = nsnull;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_METHOD
|
|
nsAbsoluteFrame::LastChild(nsIFrame*& aLastChild) const
|
|
{
|
|
aLastChild = mFrame;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_METHOD nsAbsoluteFrame::List(FILE* out, PRInt32 aIndent) const
|
|
{
|
|
// Indent
|
|
for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
|
|
// Output the tag
|
|
fputs("*absolute", out);
|
|
|
|
PRInt32 contentIndex;
|
|
GetContentIndex(contentIndex);
|
|
fprintf(out, "(%d)@%p ", contentIndex, this);
|
|
|
|
// Output the rect
|
|
out << mRect;
|
|
|
|
// List the absolutely positioned frame
|
|
if (0 != mState) {
|
|
fprintf(out, " [state=%08x]", mState);
|
|
}
|
|
fputs("<\n", out);
|
|
mFrame->List(out, aIndent + 1);
|
|
fputs(">\n", out);
|
|
|
|
return NS_OK;
|
|
}
|