r=peterv, jst, ducarroz, edburns. sr=jst, shaver git-svn-id: svn://10.0.0.236/trunk@89986 18797224-902f-48f8-a5cc-f745e15eee43
544 lines
17 KiB
C++
544 lines
17 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.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 Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
|
|
#include "nsSVGContainerFrame.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsHTMLIIDs.h"
|
|
#include "nsUnitConversion.h"
|
|
#include "nsINameSpaceManager.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsSVGAtoms.h"
|
|
#include "nsIReflowCommand.h"
|
|
#include "nsIContent.h"
|
|
#include "nsSpaceManager.h"
|
|
#include "nsHTMLParts.h"
|
|
#include "nsIViewManager.h"
|
|
#include "nsIView.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsCSSRendering.h"
|
|
#include "nsIPref.h"
|
|
#include "nsIServiceManager.h"
|
|
#include "nsISVGFrame.h"
|
|
|
|
|
|
nsresult
|
|
NS_NewSVGContainerFrame ( nsIPresShell* aPresShell, nsIFrame** aNewFrame, PRBool aIsRoot)
|
|
{
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsSVGContainerFrame* it = new (aPresShell) nsSVGContainerFrame(aPresShell, aIsRoot);
|
|
if (nsnull == it)
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
|
|
} // NS_NewSVGContainerFrame
|
|
|
|
nsSVGContainerFrame::nsSVGContainerFrame(nsIPresShell* aPresShell, PRBool aIsRoot)
|
|
{
|
|
}
|
|
|
|
|
|
nsSVGContainerFrame::~nsSVGContainerFrame()
|
|
{
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::SetInitialChildList(nsIPresContext* aPresContext,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aChildList)
|
|
{
|
|
|
|
nsresult r = nsHTMLContainerFrame::SetInitialChildList(aPresContext, aListName, aChildList);
|
|
|
|
return r;
|
|
}
|
|
|
|
|
|
/**
|
|
* Initialize us.
|
|
*/
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::Init(nsIPresContext* aPresContext,
|
|
nsIContent* aContent,
|
|
nsIFrame* aParent,
|
|
nsIStyleContext* aContext,
|
|
nsIFrame* aPrevInFlow)
|
|
{
|
|
nsresult rv = nsHTMLContainerFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
|
|
|
|
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::ReflowDirtyChild(nsIPresShell* aPresShell, nsIFrame* aChild)
|
|
{
|
|
// if we are not dirty mark ourselves dirty and tell our parent we are dirty too.
|
|
if (!(mState & NS_FRAME_IS_DIRTY)) {
|
|
// Mark yourself as dirty
|
|
mState |= NS_FRAME_IS_DIRTY;
|
|
return mParent->ReflowDirtyChild(aPresShell, this);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
* Ok what we want to do here is get all the children, figure out
|
|
* their flexibility, preferred, min, max sizes and then stretch or
|
|
* shrink them to fit in the given space.
|
|
*
|
|
* So we will have 3 passes.
|
|
* 1) get our min,max,preferred size.
|
|
* 2) flow all our children to fit into the size we are given layout in
|
|
* 3) move all the children to the right locations.
|
|
*/
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::Reflow(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
if (eReflowReason_Incremental == aReflowState.reason) {
|
|
nsIFrame* targetFrame;
|
|
aReflowState.reflowCommand->GetTarget(targetFrame);
|
|
// Check to see if we are the target of the Incremental Reflow
|
|
if (targetFrame == this) {
|
|
NS_ASSERTION(0, "Incremental reflow on nsSVGContainerFrame");
|
|
} else {
|
|
nsIFrame * incrementalChild;
|
|
aReflowState.reflowCommand->GetNext(incrementalChild);
|
|
nscoord maxWidth = 0;
|
|
nscoord maxHeight = 0;
|
|
|
|
nsSize availSize(aReflowState.availableWidth, aReflowState.availableHeight);
|
|
nsHTMLReflowMetrics kidSize(&availSize);
|
|
nsHTMLReflowState kidReflowState(aPresContext, aReflowState, incrementalChild, availSize);
|
|
|
|
incrementalChild->WillReflow(aPresContext);
|
|
incrementalChild->MoveTo(aPresContext, aReflowState.mComputedBorderPadding.left, aReflowState.mComputedBorderPadding.top);
|
|
nsIView* view;
|
|
incrementalChild->GetView(aPresContext, &view);
|
|
if (view) {
|
|
//nsHTMLContainerFrame::PositionFrameView(aPresContext, child, view);
|
|
}
|
|
nsReflowStatus status;
|
|
nsresult rv = incrementalChild->Reflow(aPresContext, kidSize, kidReflowState, status);
|
|
|
|
nsRect rect;
|
|
incrementalChild->GetRect(rect);
|
|
nsCOMPtr<nsISVGFrame> svgFrame = do_QueryInterface(incrementalChild);
|
|
if (svgFrame) {
|
|
svgFrame->GetXY(&rect.x, &rect.y);
|
|
}
|
|
rect.width = kidSize.width;
|
|
rect.height = kidSize.height;
|
|
maxWidth = PR_MAX(maxWidth, rect.x+rect.width);
|
|
maxHeight = PR_MAX(maxHeight, rect.y+rect.height);
|
|
|
|
incrementalChild->SetRect(aPresContext, rect);
|
|
if (NS_FAILED(rv)) return rv;
|
|
rv = incrementalChild->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
nsIFrame * child = mFrames.FirstChild();
|
|
while (child != nsnull) {
|
|
child->GetRect(rect);
|
|
nsCOMPtr<nsISVGFrame> svgFrame = do_QueryInterface(child);
|
|
if (svgFrame) {
|
|
svgFrame->GetXY(&rect.x, &rect.y);
|
|
}
|
|
maxWidth = PR_MAX(maxWidth, rect.x+rect.width);
|
|
maxHeight = PR_MAX(maxHeight, rect.y+rect.height);
|
|
child->GetNextSibling(&child);
|
|
}
|
|
|
|
aDesiredSize.width = maxWidth;
|
|
aDesiredSize.height = maxHeight;
|
|
aDesiredSize.ascent = aDesiredSize.height;
|
|
aDesiredSize.descent = 0;
|
|
}
|
|
} else {
|
|
|
|
nscoord maxWidth = 0;
|
|
nscoord maxHeight = 0;
|
|
|
|
nsIFrame * child = mFrames.FirstChild();
|
|
|
|
while (child != nsnull) {
|
|
|
|
nsSize availSize(aReflowState.availableWidth, aReflowState.availableHeight);
|
|
nsHTMLReflowMetrics kidSize(&availSize);
|
|
nsHTMLReflowState kidReflowState(aPresContext, aReflowState, child, availSize);
|
|
|
|
child->WillReflow(aPresContext);
|
|
child->MoveTo(aPresContext, aReflowState.mComputedBorderPadding.left, aReflowState.mComputedBorderPadding.top);
|
|
nsIView* view;
|
|
child->GetView(aPresContext, &view);
|
|
if (view) {
|
|
//nsHTMLContainerFrame::PositionFrameView(aPresContext, child, view);
|
|
}
|
|
nsReflowStatus status;
|
|
nsresult rv = child->Reflow(aPresContext, kidSize, kidReflowState, status);
|
|
|
|
nsRect rect;
|
|
child->GetRect(rect);
|
|
nsCOMPtr<nsISVGFrame> svgFrame = do_QueryInterface(child);
|
|
if (svgFrame) {
|
|
svgFrame->GetXY(&rect.x, &rect.y);
|
|
}
|
|
rect.width = kidSize.width;
|
|
rect.height = kidSize.height;
|
|
maxWidth = PR_MAX(maxWidth, rect.x+rect.width);
|
|
maxHeight = PR_MAX(maxHeight, rect.y+rect.height);
|
|
|
|
child->SetRect(aPresContext, rect);
|
|
if (NS_FAILED(rv)) return rv;
|
|
rv = child->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
|
|
if (NS_FAILED(rv)) return rv;
|
|
child->GetNextSibling(&child);
|
|
}
|
|
|
|
|
|
aDesiredSize.width = maxWidth;
|
|
aDesiredSize.height = maxHeight;
|
|
aDesiredSize.ascent = aDesiredSize.height;
|
|
aDesiredSize.descent = 0;
|
|
}
|
|
|
|
if (nsnull != aDesiredSize.maxElementSize) {
|
|
aDesiredSize.maxElementSize->width = aDesiredSize.width;
|
|
aDesiredSize.maxElementSize->height = aDesiredSize.height;
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::DidReflow(nsIPresContext* aPresContext,
|
|
nsDidReflowStatus aStatus)
|
|
{
|
|
nsresult rv = nsHTMLContainerFrame::DidReflow(aPresContext, aStatus);
|
|
NS_ASSERTION(rv == NS_OK,"DidReflow failed");
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
// Marks the frame as dirty and generates an incremental reflow
|
|
// command targeted at this frame
|
|
nsresult
|
|
nsSVGContainerFrame::GenerateDirtyReflowCommand(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell)
|
|
{
|
|
if (mState & NS_FRAME_IS_DIRTY)
|
|
return NS_OK;
|
|
|
|
// ask out parent to dirty things.
|
|
mState |= NS_FRAME_IS_DIRTY;
|
|
return mParent->ReflowDirtyChild(&aPresShell, this);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::RemoveFrame(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aOldFrame)
|
|
{
|
|
|
|
// remove the child frame
|
|
mFrames.DestroyFrame(aPresContext, aOldFrame);
|
|
|
|
// mark us dirty and generate a reflow command
|
|
return GenerateDirtyReflowCommand(aPresContext, aPresShell);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::Destroy(nsIPresContext* aPresContext)
|
|
{
|
|
|
|
return nsHTMLContainerFrame::Destroy(aPresContext);
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::InsertFrames(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aPrevFrame,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
// insert the frames in out regular frame list
|
|
mFrames.InsertFrames(this, aPrevFrame, aFrameList);
|
|
|
|
// mark us dirty and generate a reflow command
|
|
return GenerateDirtyReflowCommand(aPresContext, aPresShell);
|
|
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::AppendFrames(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
// append in regular frames
|
|
mFrames.AppendFrames(this, aFrameList);
|
|
|
|
// mark us dirty and generate a reflow command
|
|
return GenerateDirtyReflowCommand(aPresContext, aPresShell);
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::AttributeChanged(nsIPresContext* aPresContext,
|
|
nsIContent* aChild,
|
|
PRInt32 aNameSpaceID,
|
|
nsIAtom* aAttribute,
|
|
PRInt32 aHint)
|
|
{
|
|
nsresult rv = nsHTMLContainerFrame::AttributeChanged(aPresContext, aChild,
|
|
aNameSpaceID, aAttribute, aHint);
|
|
#if 0
|
|
if (aAttribute == nsHTMLAtoms::width ||
|
|
aAttribute == nsHTMLAtoms::height ||
|
|
aAttribute == nsHTMLAtoms::align ||
|
|
aAttribute == nsHTMLAtoms::valign ||
|
|
aAttribute == nsXULAtoms::flex ||
|
|
aAttribute == nsXULAtoms::orient ||
|
|
aAttribute == nsXULAtoms::autostretch) {
|
|
|
|
if (aAttribute == nsXULAtoms::orient || aAttribute == nsXULAtoms::debug || aAttribute == nsHTMLAtoms::align || aAttribute == nsHTMLAtoms::valign) {
|
|
mInner->mValign = nsSVGContainerFrame::vAlign_Top;
|
|
mInner->mHalign = nsSVGContainerFrame::hAlign_Left;
|
|
|
|
GetInitialVAlignment(mInner->mValign);
|
|
GetInitialHAlignment(mInner->mHalign);
|
|
|
|
PRBool orient = mState & NS_STATE_IS_HORIZONTAL;
|
|
GetInitialOrientation(orient);
|
|
if (orient)
|
|
mState |= NS_STATE_IS_HORIZONTAL;
|
|
else
|
|
mState &= ~NS_STATE_IS_HORIZONTAL;
|
|
|
|
PRBool debug = mState & NS_STATE_SET_TO_DEBUG;
|
|
PRBool debugSet = mInner->GetInitialDebug(debug);
|
|
if (debugSet) {
|
|
mState |= NS_STATE_DEBUG_WAS_SET;
|
|
if (debug)
|
|
mState |= NS_STATE_SET_TO_DEBUG;
|
|
else
|
|
mState &= ~NS_STATE_SET_TO_DEBUG;
|
|
} else {
|
|
mState &= ~NS_STATE_DEBUG_WAS_SET;
|
|
}
|
|
|
|
|
|
PRBool autostretch = mState & NS_STATE_AUTO_STRETCH;
|
|
GetInitialAutoStretch(autostretch);
|
|
if (autostretch)
|
|
mState |= NS_STATE_AUTO_STRETCH;
|
|
else
|
|
mState &= ~NS_STATE_AUTO_STRETCH;
|
|
}
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
aPresContext->GetShell(getter_AddRefs(shell));
|
|
GenerateDirtyReflowCommand(aPresContext, *shell);
|
|
}
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::Paint ( nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
const nsStyleDisplay* disp = (const nsStyleDisplay*)
|
|
mStyleContext->GetStyleData(eStyleStruct_Display);
|
|
|
|
// if we aren't visible then we are done.
|
|
if (!disp->IsVisibleOrCollapsed())
|
|
return NS_OK;
|
|
//printf("nsSVGContainerFrame::Paint Start\n");
|
|
// if we are visible then tell our superclass to paint
|
|
nsresult r = nsHTMLContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect,
|
|
aWhichLayer);
|
|
//printf("nsSVGContainerFrame::Paint End\n");
|
|
|
|
return r;
|
|
}
|
|
|
|
// Paint one child frame
|
|
void
|
|
nsSVGContainerFrame::PaintChild(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsIFrame* aFrame,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
const nsStyleDisplay* disp;
|
|
aFrame->GetStyleData(eStyleStruct_Display, ((const nsStyleStruct *&)disp));
|
|
|
|
// if collapsed don't paint the child.
|
|
if (disp->mVisible == NS_STYLE_VISIBILITY_COLLAPSE)
|
|
return;
|
|
|
|
nsHTMLContainerFrame::PaintChild(aPresContext, aRenderingContext, aDirtyRect, aFrame, aWhichLayer);
|
|
}
|
|
|
|
void
|
|
nsSVGContainerFrame::PaintChildren(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
const nsStyleDisplay* disp = (const nsStyleDisplay*)
|
|
mStyleContext->GetStyleData(eStyleStruct_Display);
|
|
|
|
// Child elements have the opportunity to override the visibility property
|
|
// of their parent and display even if the parent is hidden
|
|
PRBool clipState;
|
|
|
|
nsRect r(0,0,mRect.width, mRect.height);
|
|
PRBool hasClipped = PR_FALSE;
|
|
|
|
// If overflow is hidden then set the clip rect so that children
|
|
// don't leak out of us
|
|
if (NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
|
|
//nsMargin dm(0,0,0,0);
|
|
//mInner->GetDebugInset(dm);
|
|
nsMargin im(0,0,0,0);
|
|
GetInset(im);
|
|
nsMargin border(0,0,0,0);
|
|
const nsStyleBorderPadding* borderpadding = (const nsStyleBorderPadding*)
|
|
mStyleContext->GetStyleData(eStyleStruct_BorderPaddingShortcut);
|
|
borderpadding->GetBorderPadding(border);
|
|
r.Deflate(im);
|
|
//r.Deflate(dm);
|
|
r.Deflate(border);
|
|
}
|
|
|
|
nsIFrame* kid = mFrames.FirstChild();
|
|
while (nsnull != kid) {
|
|
if (!hasClipped && NS_STYLE_OVERFLOW_HIDDEN == disp->mOverflow) {
|
|
// if we haven't already clipped and we should
|
|
// check to see if the child is in out bounds. If not then
|
|
// we begin clipping.
|
|
nsRect cr(0,0,0,0);
|
|
kid->GetRect(cr);
|
|
|
|
// if our rect does not contain the childs then begin clipping
|
|
if (!r.Contains(cr)) {
|
|
aRenderingContext.PushState();
|
|
aRenderingContext.SetClipRect(r,
|
|
nsClipCombine_kIntersect, clipState);
|
|
hasClipped = PR_TRUE;
|
|
}
|
|
}
|
|
|
|
PaintChild(aPresContext, aRenderingContext, aDirtyRect, kid, aWhichLayer);
|
|
kid->GetNextSibling(&kid);
|
|
}
|
|
|
|
if (hasClipped) {
|
|
aRenderingContext.PopState(clipState);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsSVGContainerFrame::AddRef(void)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP_(nsrefcnt)
|
|
nsSVGContainerFrame::Release(void)
|
|
{
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::GetFrameName(nsString& aResult) const
|
|
{
|
|
aResult = NS_ConvertASCIItoUCS2("nsSVGContainerFrame");
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::GetFrameForPoint(nsIPresContext* aPresContext,
|
|
const nsPoint& aPoint,
|
|
nsFramePaintLayer aWhichLayer,
|
|
nsIFrame** aFrame)
|
|
{
|
|
// this should act like a block, so we need to override
|
|
return GetFrameForPointUsing(aPresContext, aPoint, nsnull, aWhichLayer, (aWhichLayer == NS_FRAME_PAINT_LAYER_BACKGROUND), aFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
nsSVGContainerFrame::GetCursor(nsIPresContext* aPresContext,
|
|
nsPoint& aPoint,
|
|
PRInt32& aCursor)
|
|
{
|
|
|
|
return nsHTMLContainerFrame::GetCursor(aPresContext, aPoint, aCursor);
|
|
}
|
|
|
|
void
|
|
nsSVGContainerFrame::GetInset(nsMargin& margin)
|
|
{
|
|
margin.top = 0;
|
|
margin.left = 0;
|
|
margin.right = 0;
|
|
margin.bottom = 0;
|
|
}
|
|
|