gecko/layout/svg/base/src/nsSVGForeignObjectFrame.cpp

722 lines
22 KiB
C++
Raw Normal View History

/* -*- 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 the Mozilla SVG project.
*
* The Initial Developer of the Original Code is
* Crocodile Clips Ltd..
* Portions created by the Initial Developer are Copyright (C) 2001
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
*
* 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 ***** */
#include "nsSVGForeignObjectFrame.h"
#include "nsIDOMSVGForeignObjectElem.h"
#include "nsIDOMSVGMatrix.h"
#include "nsIDOMSVGSVGElement.h"
#include "nsSVGOuterSVGFrame.h"
#include "nsRegion.h"
#include "nsGkAtoms.h"
#include "nsLayoutUtils.h"
#include "nsSVGUtils.h"
#include "nsIURI.h"
#include "nsSVGRect.h"
#include "nsSVGMatrix.h"
#include "nsINameSpaceManager.h"
#include "nsSVGForeignObjectElement.h"
#include "nsSVGContainerFrame.h"
#include "gfxContext.h"
#include "gfxMatrix.h"
//----------------------------------------------------------------------
// Implementation
nsIFrame*
NS_NewSVGForeignObjectFrame(nsIPresShell *aPresShell,
nsStyleContext *aContext)
{
return new (aPresShell) nsSVGForeignObjectFrame(aContext);
}
nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* aContext)
: nsSVGForeignObjectFrameBase(aContext),
mInReflow(PR_FALSE)
{
AddStateBits(NS_FRAME_REFLOW_ROOT |
NS_FRAME_MAY_BE_TRANSFORMED_OR_HAVE_RENDERING_OBSERVERS);
}
//----------------------------------------------------------------------
// nsIFrame methods
NS_QUERYFRAME_HEAD(nsSVGForeignObjectFrame)
NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
NS_QUERYFRAME_TAIL_INHERITING(nsSVGForeignObjectFrameBase)
NS_IMETHODIMP
nsSVGForeignObjectFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
nsIFrame* aPrevInFlow)
{
#ifdef DEBUG
nsCOMPtr<nsIDOMSVGForeignObjectElement> foreignObject = do_QueryInterface(aContent);
NS_ASSERTION(foreignObject, "Content is not an SVG foreignObject!");
#endif
nsresult rv = nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow);
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM |
(aParent->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD));
if (NS_SUCCEEDED(rv)) {
nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this);
}
return rv;
}
void nsSVGForeignObjectFrame::Destroy()
{
nsSVGUtils::GetOuterSVGFrame(this)->UnregisterForeignObject(this);
nsSVGForeignObjectFrameBase::Destroy();
}
nsIAtom *
nsSVGForeignObjectFrame::GetType() const
{
return nsGkAtoms::svgForeignObjectFrame;
}
NS_IMETHODIMP
nsSVGForeignObjectFrame::AttributeChanged(PRInt32 aNameSpaceID,
nsIAtom *aAttribute,
PRInt32 aModType)
{
if (aNameSpaceID == kNameSpaceID_None) {
if (aAttribute == nsGkAtoms::width ||
aAttribute == nsGkAtoms::height) {
UpdateGraphic(); // update mRect before requesting reflow
// XXXjwatt: why mark intrinsic widths dirty? can't we just use eResize?
RequestReflow(nsIPresShell::eStyleChange);
} else if (aAttribute == nsGkAtoms::x ||
aAttribute == nsGkAtoms::y) {
UpdateGraphic();
} else if (aAttribute == nsGkAtoms::transform) {
// make sure our cached transform matrix gets (lazily) updated
mCanvasTM = nsnull;
UpdateGraphic();
}
}
return NS_OK;
}
NS_IMETHODIMP
nsSVGForeignObjectFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
// InitialUpdate and AttributeChanged make sure mRect is up to date before
// we're called (UpdateCoveredRegion sets mRect).
NS_ASSERTION(!aReflowState.parentReflowState,
"should only get reflow from being reflow root");
NS_ASSERTION(aReflowState.ComputedWidth() == GetSize().width &&
aReflowState.ComputedHeight() == GetSize().height,
"reflow roots should be reflown at existing size and "
"svg.css should ensure we have no padding/border/margin");
DoReflow();
// XXX why don't we convert from CSS pixels to app units? How does this work?
aDesiredSize.width = aReflowState.ComputedWidth();
aDesiredSize.height = aReflowState.ComputedHeight();
aDesiredSize.mOverflowArea =
nsRect(nsPoint(0, 0), nsSize(aDesiredSize.width, aDesiredSize.height));
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
void
nsSVGForeignObjectFrame::InvalidateInternal(const nsRect& aDamageRect,
nscoord aX, nscoord aY,
nsIFrame* aForChild,
PRUint32 aFlags)
{
if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
return;
nsRegion* region = (aFlags & INVALIDATE_CROSS_DOC)
? &mCrossDocDirtyRegion : &mSameDocDirtyRegion;
region->Or(*region, aDamageRect + nsPoint(aX, aY));
FlushDirtyRegion();
}
//----------------------------------------------------------------------
// nsISVGChildFrame methods
/**
* Gets the rectangular region in app units (rounded out device pixels)
* that encloses the rectangle after it has been transformed by aMatrix.
* Useful in UpdateCoveredRegion/FlushDirtyRegion.
*/
static nsRect
GetTransformedRegion(float aX, float aY, float aWidth, float aHeight,
nsIDOMSVGMatrix* aMatrix, nsPresContext *aPresContext)
{
float x[4], y[4];
x[0] = aX;
y[0] = aY;
x[1] = aX + aWidth;
y[1] = aY;
x[2] = aX + aWidth;
y[2] = aY + aHeight;
x[3] = aX;
y[3] = aY + aHeight;
for (int i = 0; i < 4; i++) {
nsSVGUtils::TransformPoint(aMatrix, &x[i], &y[i]);
}
float xmin, xmax, ymin, ymax;
xmin = xmax = x[0];
ymin = ymax = y[0];
for (int i = 1; i < 4; i++) {
if (x[i] < xmin)
xmin = x[i];
else if (x[i] > xmax)
xmax = x[i];
if (y[i] < ymin)
ymin = y[i];
else if (y[i] > ymax)
ymax = y[i];
}
return nsSVGUtils::ToAppPixelRect(aPresContext, xmin, ymin, xmax, ymax);
}
NS_IMETHODIMP
nsSVGForeignObjectFrame::PaintSVG(nsSVGRenderState *aContext,
const nsIntRect *aDirtyRect)
{
if (IsDisabled())
return NS_OK;
nsIFrame* kid = GetFirstChild(nsnull);
if (!kid)
return NS_OK;
// GetCanvasTM includes a device pixel to CSS pixel scaling. Since non-SVG
// content takes care of this itself, we need to remove this pre-scaling from
// the matrix returned by GetTMIncludingOffset before painting our children.
float cssPxPerDevPx =
PresContext()->AppUnitsToFloatCSSPixels(
PresContext()->AppUnitsPerDevPixel());
nsCOMPtr<nsIDOMSVGMatrix> cssPxToDevPxMatrix;
NS_NewSVGMatrix(getter_AddRefs(cssPxToDevPxMatrix),
cssPxPerDevPx, 0.0f,
0.0f, cssPxPerDevPx);
nsCOMPtr<nsIDOMSVGMatrix> localTM = GetTMIncludingOffset();
// POST-multiply px conversion!
nsCOMPtr<nsIDOMSVGMatrix> tm;
localTM->Multiply(cssPxToDevPxMatrix, getter_AddRefs(tm));
gfxMatrix matrix = nsSVGUtils::ConvertSVGMatrixToThebes(tm);
nsIRenderingContext *ctx = aContext->GetRenderingContext(this);
if (!ctx || matrix.IsSingular()) {
NS_WARNING("Can't render foreignObject element!");
return NS_ERROR_FAILURE;
}
/* Check if we need to draw anything. */
if (aDirtyRect) {
gfxRect extent = matrix.TransformBounds(
gfxRect(kid->GetRect().x, kid->GetRect().y,
kid->GetRect().width, kid->GetRect().height));
extent.RoundOut();
nsIntRect rect;
if (NS_SUCCEEDED(nsSVGUtils::GfxRectToIntRect(extent, &rect)) &&
!aDirtyRect->Intersects(rect))
return NS_OK;
}
gfxContext *gfx = aContext->GetGfxContext();
gfx->Save();
if (GetStyleDisplay()->IsScrollableOverflow()) {
float x, y, width, height;
static_cast<nsSVGElement*>(mContent)->
GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
// tm already includes the x,y offset
nsSVGUtils::SetClipRect(gfx, localTM, 0.0f, 0.0f, width, height);
}
gfx->Multiply(matrix);
nsresult rv = nsLayoutUtils::PaintFrame(ctx, kid, nsRegion(kid->GetRect()),
NS_RGBA(0,0,0,0));
gfx->Restore();
return rv;
}
nsresult
nsSVGForeignObjectFrame::TransformPointFromOuterPx(const nsPoint &aIn,
nsPoint* aOut)
{
if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMSVGMatrix> tm = GetTMIncludingOffset();
nsCOMPtr<nsIDOMSVGMatrix> inverse;
nsresult rv = tm->Inverse(getter_AddRefs(inverse));
if (NS_FAILED(rv))
return rv;
float x = PresContext()->AppUnitsToDevPixels(aIn.x);
float y = PresContext()->AppUnitsToDevPixels(aIn.y);
nsSVGUtils::TransformPoint(inverse, &x, &y);
*aOut = nsPoint(PresContext()->DevPixelsToAppUnits(NSToIntRound(x)),
PresContext()->DevPixelsToAppUnits(NSToIntRound(y)));
return NS_OK;
}
gfxMatrix
nsSVGForeignObjectFrame::GetTransformMatrix(nsIFrame **aOutAncestor)
{
NS_PRECONDITION(aOutAncestor, "We need an ancestor to write to!");
/* Set the ancestor to be the outer frame. */
*aOutAncestor = nsSVGUtils::GetOuterSVGFrame(this);
NS_ASSERTION(*aOutAncestor, "How did we end up without an outer frame?");
/* Return the matrix back to the root, factoring in the x and y offsets. */
nsCOMPtr<nsIDOMSVGMatrix> matrix = GetTMIncludingOffset();
return nsSVGUtils::ConvertSVGMatrixToThebes(matrix);
}
NS_IMETHODIMP_(nsIFrame*)
nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint)
{
if (IsDisabled())
return NS_OK;
nsIFrame* kid = GetFirstChild(nsnull);
if (!kid) {
return nsnull;
}
nsPoint pt;
if (NS_FAILED(TransformPointFromOuterPx(aPoint, &pt)))
return nsnull;
return nsLayoutUtils::GetFrameForPoint(kid, pt);
}
nsPoint
nsSVGForeignObjectFrame::TransformPointFromOuter(nsPoint aPt)
{
nsPoint pt(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
TransformPointFromOuterPx(aPt, &pt);
return pt;
}
NS_IMETHODIMP_(nsRect)
nsSVGForeignObjectFrame::GetCoveredRegion()
{
return mRect;
}
NS_IMETHODIMP
nsSVGForeignObjectFrame::UpdateCoveredRegion()
{
if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMSVGMatrix> ctm = GetCanvasTM();
if (!ctm)
return NS_ERROR_FAILURE;
float x, y, w, h;
static_cast<nsSVGForeignObjectElement*>(mContent)->
GetAnimatedLengthValues(&x, &y, &w, &h, nsnull);
// If mRect's width or height are negative, reflow blows up! We must clamp!
if (w < 0.0f) w = 0.0f;
if (h < 0.0f) h = 0.0f;
// XXXjwatt: _this_ is where we should reflow _if_ mRect.width has changed!
// we should not unconditionally reflow in AttributeChanged
mRect = GetTransformedRegion(x, y, w, h, ctm, PresContext());
return NS_OK;
}
NS_IMETHODIMP
nsSVGForeignObjectFrame::InitialUpdate()
{
NS_ASSERTION(GetStateBits() & NS_FRAME_FIRST_REFLOW,
"Yikes! We've been called already! Hopefully we weren't called "
"before our nsSVGOuterSVGFrame's initial Reflow()!!!");
UpdateCoveredRegion();
DoReflow();
NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW),
"We don't actually participate in reflow");
// Do unset the various reflow bits, though.
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
NS_FRAME_HAS_DIRTY_CHILDREN);
return NS_OK;
}
void
nsSVGForeignObjectFrame::NotifySVGChanged(PRUint32 aFlags)
{
PRBool reflow = PR_FALSE;
if (aFlags & TRANSFORM_CHANGED) {
// In an ideal world we would reflow when our CTM changes. This is because
// glyph metrics do not necessarily scale uniformly with change in scale
// and, as a result, CTM changes may require text to break at different
// points. The problem would be how to keep performance acceptable when
// e.g. the transform of an ancestor is animated.
// We also seem to get some sort of infinite loop post bug 421584 if we
// reflow.
mCanvasTM = nsnull;
if (!(aFlags & SUPPRESS_INVALIDATION)) {
UpdateGraphic();
}
} else if (aFlags & COORD_CONTEXT_CHANGED) {
// Our coordinate context's width/height has changed. If we have a
// percentage width/height our dimensions will change so we must reflow.
nsSVGForeignObjectElement *fO =
static_cast<nsSVGForeignObjectElement*>(mContent);
if (fO->mLengthAttributes[nsSVGForeignObjectElement::WIDTH].IsPercentage() ||
fO->mLengthAttributes[nsSVGForeignObjectElement::HEIGHT].IsPercentage()) {
reflow = PR_TRUE;
}
}
if (reflow) {
// If we're called while the PresShell is handling reflow events then we
// must have been called as a result of the NotifyViewportChange() call in
// our nsSVGOuterSVGFrame's Reflow() method. We must not call RequestReflow
// at this point (i.e. during reflow) because it could confuse the
// PresShell and prevent it from reflowing us properly in future. Besides
// that, nsSVGOuterSVGFrame::DidReflow will take care of reflowing us
// synchronously, so there's no need.
PRBool reflowing;
PresContext()->PresShell()->IsReflowLocked(&reflowing);
if (!reflowing) {
UpdateGraphic(); // update mRect before requesting reflow
RequestReflow(nsIPresShell::eResize);
}
}
}
NS_IMETHODIMP
nsSVGForeignObjectFrame::NotifyRedrawSuspended()
{
return NS_OK;
}
NS_IMETHODIMP
nsSVGForeignObjectFrame::NotifyRedrawUnsuspended()
{
if (!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
if (GetStateBits() & NS_STATE_SVG_DIRTY) {
UpdateGraphic();
} else {
FlushDirtyRegion();
}
}
return NS_OK;
}
NS_IMETHODIMP
nsSVGForeignObjectFrame::SetMatrixPropagation(PRBool aPropagate)
{
if (aPropagate) {
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
} else {
RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
}
return NS_OK;
}
PRBool
nsSVGForeignObjectFrame::GetMatrixPropagation()
{
return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
}
NS_IMETHODIMP
nsSVGForeignObjectFrame::GetBBox(nsIDOMSVGRect **_retval)
{
*_retval = nsnull;
if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMSVGMatrix> ctm = GetCanvasTM();
if (!ctm)
return NS_ERROR_FAILURE;
float x, y, w, h;
static_cast<nsSVGForeignObjectElement*>(mContent)->
GetAnimatedLengthValues(&x, &y, &w, &h, nsnull);
if (w < 0.0f) w = 0.0f;
if (h < 0.0f) h = 0.0f;
gfxRect bounds =
nsSVGUtils::ConvertSVGMatrixToThebes(ctm).TransformBounds(gfxRect(x, y, w, h));
return NS_NewSVGRect(_retval, bounds);
}
//----------------------------------------------------------------------
already_AddRefed<nsIDOMSVGMatrix>
nsSVGForeignObjectFrame::GetTMIncludingOffset()
{
nsCOMPtr<nsIDOMSVGMatrix> ctm = GetCanvasTM();
if (!ctm)
return nsnull;
nsSVGForeignObjectElement *fO =
static_cast<nsSVGForeignObjectElement*>(mContent);
float x, y;
fO->GetAnimatedLengthValues(&x, &y, nsnull);
nsIDOMSVGMatrix* matrix;
ctm->Translate(x, y, &matrix);
return matrix;
}
already_AddRefed<nsIDOMSVGMatrix>
nsSVGForeignObjectFrame::GetCanvasTM()
{
if (!GetMatrixPropagation()) {
nsIDOMSVGMatrix *retval;
NS_NewSVGMatrix(&retval);
return retval;
}
if (!mCanvasTM) {
// get our parent's tm and append local transforms (if any):
NS_ASSERTION(mParent, "null parent");
nsSVGContainerFrame *containerFrame = static_cast<nsSVGContainerFrame*>
(mParent);
nsCOMPtr<nsIDOMSVGMatrix> parentTM = containerFrame->GetCanvasTM();
NS_ASSERTION(parentTM, "null TM");
// got the parent tm, now check for local tm:
nsSVGGraphicElement *element =
static_cast<nsSVGGraphicElement*>(mContent);
nsCOMPtr<nsIDOMSVGMatrix> localTM = element->GetLocalTransformMatrix();
if (localTM)
parentTM->Multiply(localTM, getter_AddRefs(mCanvasTM));
else
mCanvasTM = parentTM;
}
nsIDOMSVGMatrix* retval = mCanvasTM.get();
NS_IF_ADDREF(retval);
return retval;
}
//----------------------------------------------------------------------
// Implementation helpers
void nsSVGForeignObjectFrame::RequestReflow(nsIPresShell::IntrinsicDirty aType)
{
if (GetStateBits() & NS_FRAME_FIRST_REFLOW)
// If we haven't had an InitialUpdate yet, nothing to do.
return;
nsIFrame* kid = GetFirstChild(nsnull);
if (!kid)
return;
PresContext()->PresShell()->FrameNeedsReflow(kid, aType, NS_FRAME_IS_DIRTY);
}
void nsSVGForeignObjectFrame::UpdateGraphic()
{
nsSVGUtils::UpdateGraphic(this);
// Clear any layout dirty region since we invalidated our whole area.
mSameDocDirtyRegion.SetEmpty();
mCrossDocDirtyRegion.SetEmpty();
}
void
nsSVGForeignObjectFrame::MaybeReflowFromOuterSVGFrame()
{
// If we're already scheduled to reflow (i.e. our kid is dirty) we don't
// want to reflow now or else our presShell will do extra work trying to
// reflow us a second time. (It will also complain if it finds that a reflow
// root scheduled for reflow isn't dirty).
nsIFrame* kid = GetFirstChild(nsnull);
if (kid->GetStateBits() & NS_FRAME_IS_DIRTY) {
return;
}
kid->AddStateBits(NS_FRAME_IS_DIRTY); // we must be fully marked dirty
if (kid->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN) {
return;
}
DoReflow();
}
void
nsSVGForeignObjectFrame::DoReflow()
{
#ifdef DEBUG
printf("**nsSVGForeignObjectFrame::DoReflow()\n");
#endif
NS_ASSERTION(!(nsSVGUtils::GetOuterSVGFrame(this)->
GetStateBits() & NS_FRAME_FIRST_REFLOW),
"Calling InitialUpdate too early - must not call DoReflow!!!");
if (IsDisabled())
return;
if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)
return;
nsPresContext *presContext = PresContext();
nsIFrame* kid = GetFirstChild(nsnull);
if (!kid)
return;
// initiate a synchronous reflow here and now:
nsSize availableSpace(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
nsCOMPtr<nsIRenderingContext> renderingContext;
nsIPresShell* presShell = presContext->PresShell();
NS_ASSERTION(presShell, "null presShell");
presShell->CreateRenderingContext(this,getter_AddRefs(renderingContext));
if (!renderingContext)
return;
nsSVGForeignObjectElement *fO = static_cast<nsSVGForeignObjectElement*>
(mContent);
float width =
fO->mLengthAttributes[nsSVGForeignObjectElement::WIDTH].GetAnimValue(fO);
float height =
fO->mLengthAttributes[nsSVGForeignObjectElement::HEIGHT].GetAnimValue(fO);
nsSize size(nsPresContext::CSSPixelsToAppUnits(width),
nsPresContext::CSSPixelsToAppUnits(height));
mInReflow = PR_TRUE;
nsHTMLReflowState reflowState(presContext, kid,
renderingContext,
nsSize(size.width, NS_UNCONSTRAINEDSIZE));
nsHTMLReflowMetrics desiredSize;
nsReflowStatus status;
// We don't use size.height above because that tells the child to do
// page/column breaking at that height.
NS_ASSERTION(reflowState.mComputedBorderPadding == nsMargin(0, 0, 0, 0) &&
reflowState.mComputedMargin == nsMargin(0, 0, 0, 0),
"style system should ensure that :-moz-svg-foreign content "
"does not get styled");
NS_ASSERTION(reflowState.ComputedWidth() == size.width,
"reflow state made child wrong size");
reflowState.SetComputedHeight(size.height);
ReflowChild(kid, presContext, desiredSize, reflowState, 0, 0,
NS_FRAME_NO_MOVE_FRAME, status);
NS_ASSERTION(size.width == desiredSize.width &&
size.height == desiredSize.height, "unexpected size");
FinishReflowChild(kid, presContext, &reflowState, desiredSize, 0, 0,
NS_FRAME_NO_MOVE_FRAME);
mInReflow = PR_FALSE;
FlushDirtyRegion();
}
void
nsSVGForeignObjectFrame::InvalidateDirtyRect(nsSVGOuterSVGFrame* aOuter,
const nsRect& aRect, PRUint32 aFlags)
{
if (aRect.IsEmpty())
return;
nsPresContext* presContext = PresContext();
nsCOMPtr<nsIDOMSVGMatrix> tm = GetTMIncludingOffset();
nsIntRect r = nsRect::ToOutsidePixels(aRect, presContext->AppUnitsPerDevPixel());
float x = r.x, y = r.y, w = r.width, h = r.height;
nsRect rect = GetTransformedRegion(x, y, w, h, tm, presContext);
// XXX invalidate the entire covered region
// See bug 418063
rect.UnionRect(rect, mRect);
rect = nsSVGUtils::FindFilterInvalidation(this, rect);
aOuter->InvalidateWithFlags(rect, aFlags);
}
void
nsSVGForeignObjectFrame::FlushDirtyRegion()
{
if ((mSameDocDirtyRegion.IsEmpty() && mCrossDocDirtyRegion.IsEmpty()) ||
mInReflow)
return;
nsSVGOuterSVGFrame *outerSVGFrame = nsSVGUtils::GetOuterSVGFrame(this);
if (!outerSVGFrame) {
NS_ERROR("null outerSVGFrame");
return;
}
if (outerSVGFrame->IsRedrawSuspended())
return;
InvalidateDirtyRect(outerSVGFrame, mSameDocDirtyRegion.GetBounds(), 0);
InvalidateDirtyRect(outerSVGFrame, mCrossDocDirtyRegion.GetBounds(), INVALIDATE_CROSS_DOC);
mSameDocDirtyRegion.SetEmpty();
mCrossDocDirtyRegion.SetEmpty();
}