Bug 734082 - Compute and store bounds and visual overflow bounds for both SVG leaf and container frames. r=roc.

This commit is contained in:
Jonathan Watt 2012-05-17 05:05:09 +01:00
parent acf3e38285
commit f418b57d69
35 changed files with 768 additions and 124 deletions

View File

@ -45,6 +45,7 @@
#include "DOMSVGMatrix.h"
#include "nsGkAtoms.h"
#include "nsIDOMEventTarget.h"
#include "nsIDOMMutationEvent.h"
#include "nsIFrame.h"
#include "nsISVGChildFrame.h"
#include "nsIDOMSVGPoint.h"
@ -179,6 +180,43 @@ nsSVGGraphicElement::IsAttributeMapped(const nsIAtom* name) const
nsSVGGraphicElementBase::IsAttributeMapped(name);
}
nsChangeHint
nsSVGGraphicElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
PRInt32 aModType) const
{
nsChangeHint retval =
nsSVGGraphicElementBase::GetAttributeChangeHint(aAttribute, aModType);
if (aAttribute == nsGkAtoms::transform) {
// We add nsChangeHint_UpdateOverflow so that nsFrame::UpdateOverflow()
// will be called on us and our ancestors.
nsIFrame* frame =
const_cast<nsSVGGraphicElement*>(this)->GetPrimaryFrame();
if (frame && frame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) {
// No need to do anything.
} else if (aModType == nsIDOMMutationEvent::ADDITION ||
aModType == nsIDOMMutationEvent::REMOVAL) {
// In order to handle view creation/destruction and stacking context
// changes, the code in nsStyleDisplay::CalcDifference uses
// nsChangeHint_ReconstructFrame if the transform was added/removed.
// XXXSDL Currently we don't need to reconstruct SVG frames when their
// transform is set/unset since we don't currently create GFX layers for
// SVG transforms, but we will after bug 614732 is fixed. Also change the
// assertion in ApplyRenderingChangeToTree when we do that.
NS_UpdateHint(retval, nsChangeHint_UpdateOverflow);
} else {
NS_ABORT_IF_FALSE(aModType == nsIDOMMutationEvent::MODIFICATION,
"Unknown modification type.");
// We just assume the old and new transforms are different.
// XXXSDL Once we use GFX layers for SVG transforms, we will need to pass
// the nsChangeHint_UpdateTransformLayer hint too. Note that the
// assertion in ApplyRenderingChangeToTree will fail if that hint is
// passed on nsIDOMMutationEvent::REMOVAL though.
NS_UpdateHint(retval, nsChangeHint_UpdateOverflow);
}
}
return retval;
}
//----------------------------------------------------------------------
// nsSVGElement overrides

View File

@ -62,6 +62,9 @@ public:
// nsIContent interface
NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute,
PRInt32 aModType) const;
virtual gfxMatrix PrependLocalTransformsTo(const gfxMatrix &aMatrix,
TransformTypes aWhich = eAllTransforms) const;
virtual void SetAnimateMotionTransform(const gfxMatrix* aMatrix);

View File

@ -41,6 +41,7 @@
#include "mozilla/Util.h"
#include "nsGkAtoms.h"
#include "nsLayoutUtils.h"
#include "DOMSVGNumber.h"
#include "DOMSVGLength.h"
#include "nsSVGAngle.h"
@ -204,7 +205,8 @@ nsSVGSVGElement::nsSVGSVGElement(already_AddRefed<nsINodeInfo> aNodeInfo,
mPreviousScale(1.0f),
mStartAnimationOnBindToTree(!aFromParser),
mImageNeedsTransformInvalidation(false),
mIsPaintingSVGImageElement(false)
mIsPaintingSVGImageElement(false),
mHasChildrenOnlyTransform(false)
{
}
@ -1021,6 +1023,33 @@ nsSVGSVGElement::GetViewBoxTransform() const
mPreserveAspectRatio.GetAnimValue());
}
void
nsSVGSVGElement::ChildrenOnlyTransformChanged()
{
// Avoid wasteful calls:
NS_ABORT_IF_FALSE(!(GetPrimaryFrame()->GetStateBits() &
NS_STATE_SVG_NONDISPLAY_CHILD),
"Non-display SVG frames don't maintain overflow rects");
bool hasChildrenOnlyTransform = HasViewBoxOrSyntheticViewBox() ||
(IsRoot() && (mCurrentTranslate != nsSVGTranslatePoint(0.0f, 0.0f) ||
mCurrentScale != 1.0f));
// XXXSDL Currently we don't destroy frames if
// hasChildrenOnlyTransform != mHasChildrenOnlyTransform
// but we should once we start using GFX layers for SVG transforms
// (see the comment in nsSVGGraphicElement::GetAttributeChangeHint).
nsChangeHint changeHint =
nsChangeHint(nsChangeHint_RepaintFrame |
nsChangeHint_UpdateOverflow |
nsChangeHint_ChildrenOnlyTransform);
nsLayoutUtils::PostRestyleEvent(this, nsRestyleHint(0), changeHint);
mHasChildrenOnlyTransform = hasChildrenOnlyTransform;
}
nsresult
nsSVGSVGElement::BindToTree(nsIDocument* aDocument,
nsIContent* aParent,

View File

@ -62,8 +62,15 @@ class nsSVGSVGElement;
class nsSVGTranslatePoint {
public:
nsSVGTranslatePoint(float aX, float aY) :
mX(aX), mY(aY) {}
nsSVGTranslatePoint()
: mX(0.0f)
, mY(0.0f)
{}
nsSVGTranslatePoint(float aX, float aY)
: mX(aX)
, mY(aY)
{}
void SetX(float aX)
{ mX = aX; }
@ -76,6 +83,10 @@ public:
nsresult ToDOMVal(nsSVGSVGElement *aElement, nsIDOMSVGPoint **aResult);
bool operator!=(const nsSVGTranslatePoint &rhs) const {
return mX != rhs.mX || mY != rhs.mY;
}
private:
struct DOMVal : public nsIDOMSVGPoint {
@ -128,7 +139,6 @@ class nsSVGSVGElement : public nsSVGSVGElementBase,
friend class nsSVGInnerSVGFrame;
friend class nsSVGImageFrame;
protected:
friend nsresult NS_NewSVGSVGElement(nsIContent **aResult,
already_AddRefed<nsINodeInfo> aNodeInfo,
mozilla::dom::FromParser aFromParser);
@ -220,8 +230,28 @@ public:
*/
bool ShouldSynthesizeViewBox() const;
bool HasViewBoxOrSyntheticViewBox() const {
return HasViewBox() || ShouldSynthesizeViewBox();
}
gfxMatrix GetViewBoxTransform() const;
bool HasChildrenOnlyTransform() const {
return mHasChildrenOnlyTransform;
}
/**
* This method notifies the style system that the overflow rects of our
* immediate childrens' frames need to be updated. It is called by our own
* frame when changes (e.g. to currentScale) cause our children-only
* transform to change.
*
* The reason we have this method instead of overriding
* GetAttributeChangeHint is because we need to act on non-attribute (e.g.
* currentScale) changes in addition to attribute (e.g. viewBox) changes.
*/
void ChildrenOnlyTransformChanged();
// This services any pending notifications for the transform on on this root
// <svg> node needing to be recalculated. (Only applicable in
// SVG-as-an-image documents.)
@ -250,7 +280,6 @@ private:
void ClearImageOverridePreserveAspectRatio();
const SVGPreserveAspectRatio* GetImageOverridePreserveAspectRatio() const;
protected:
// nsSVGElement overrides
bool IsEventName(nsIAtom* aName);
@ -349,6 +378,7 @@ protected:
bool mStartAnimationOnBindToTree;
bool mImageNeedsTransformInvalidation;
bool mIsPaintingSVGImageElement;
bool mHasChildrenOnlyTransform;
};
#endif

View File

@ -7673,9 +7673,14 @@ ApplyRenderingChangeToTree(nsPresContext* aPresContext,
nsIFrame* aFrame,
nsChangeHint aChange)
{
// We check GetStyleDisplay()->HasTransform() in addition to checking
// IsTransformed() since we can get here for some frames that don't have the
// NS_FRAME_MAY_BE_TRANSFORMED bit set (e.g. nsTableFrame; for a transformed
// table that bit is only set on the nsTableOuterFrame).
NS_ASSERTION(!(aChange & nsChangeHint_UpdateTransformLayer) ||
aFrame->IsTransformed() ||
aFrame->GetStyleDisplay()->HasTransform(),
"Only transform style should give a UpdateTransformLayer hint");
"Unexpected UpdateTransformLayer hint");
nsIPresShell *shell = aPresContext->PresShell();
if (shell->IsPaintingSuppressed()) {
@ -7948,7 +7953,40 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList)
ApplyRenderingChangeToTree(presContext, frame, hint);
didInvalidate = true;
}
NS_ASSERTION(!(hint & nsChangeHint_ChildrenOnlyTransform) ||
(hint & nsChangeHint_UpdateOverflow),
"nsChangeHint_UpdateOverflow should be passed too");
if ((hint & nsChangeHint_UpdateOverflow) && !didReflow) {
if (hint & nsChangeHint_ChildrenOnlyTransform) {
// When we process restyle events starting from the root of the frame
// tree, we start at a ViewportFrame and traverse down the tree from
// there. When we reach its nsHTMLScrollFrame child, that frame's
// GetContent() returns the root element of the document, even though
// that frame is not the root element's primary frame. The result of
// this quirk is that we remove any pending change hints for the
// root element and process them for the nsHTMLScrollFrame instead of
// the root element's primary frame. For most change hints this is
// not a problem, but for nsChangeHint_ChildrenOnlyTransform it is,
// since the children that we want to call UpdateOverflow on are the
// frames for the children of the root element, not the nsCanvasFrame
// child of the nsHTMLScrollFrame. As a result we need to ignore
// |frame| here and use frame->GetContent()->GetPrimaryFrame().
nsIFrame *f = frame->GetContent()->GetPrimaryFrame();
NS_ABORT_IF_FALSE(f->IsFrameOfType(nsIFrame::eSVG |
nsIFrame::eSVGContainer),
"Children-only transforms only expected on SVG frames");
// Update overflow areas of children first:
nsIFrame* childFrame = f->GetFirstPrincipalChild();
for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
NS_ABORT_IF_FALSE(childFrame->IsFrameOfType(nsIFrame::eSVG),
"Not expecting non-SVG children");
childFrame->UpdateOverflow();
NS_ASSERTION(!nsLayoutUtils::GetNextContinuationOrSpecialSibling(childFrame),
"SVG frames should not have continuations or special siblings");
NS_ASSERTION(childFrame->GetParent() == frame,
"SVG child frame not expected to have different parent");
}
}
while (frame) {
nsOverflowAreas* pre = static_cast<nsOverflowAreas*>
(frame->Properties().Get(frame->PreTransformOverflowAreasProperty()));

View File

@ -113,7 +113,13 @@ enum nsChangeHint {
* either through a change in its transform or a change in its position.
* Does not update any descendant frames.
*/
nsChangeHint_UpdateOverflow = 0x800
nsChangeHint_UpdateOverflow = 0x800,
/**
* The children-only transform of an SVG frame changed, requiring the
* overflow rects of the frame's immediate children to be updated.
*/
nsChangeHint_ChildrenOnlyTransform = 0x1000
};
// Redefine these operators to return nothing. This will catch any use

View File

@ -2522,6 +2522,12 @@ nsRect
nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
{
NS_PRECONDITION(aFrame, "Can't get the bounds of a nonexistent frame!");
if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
// TODO: SVG needs to define what percentage translations resolve against.
return nsRect();
}
return nsRect(nsPoint(0, 0), aFrame->GetSize());
}
@ -2534,6 +2540,11 @@ nsDisplayTransform::GetFrameBoundsForTransform(const nsIFrame* aFrame)
nsRect result;
if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
// TODO: SVG needs to define what percentage translations resolve against.
return result;
}
/* Iterate through the continuation list, unioning together all the
* bounding rects.
*/
@ -2563,8 +2574,8 @@ gfxPoint3D GetDeltaToMozTransformOrigin(const nsIFrame* aFrame,
const nsRect* aBoundsOverride)
{
NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(),
"Can't get a delta for an untransformed frame!");
NS_PRECONDITION(aFrame->IsTransformed(),
"Shouldn't get a delta for an untransformed frame!");
/* For both of the coordinates, if the value of -moz-transform is a
* percentage, it's relative to the size of the frame. Otherwise, if it's
@ -2600,6 +2611,14 @@ gfxPoint3D GetDeltaToMozTransformOrigin(const nsIFrame* aFrame,
*coords[index] =
NSAppUnitsToFloatPixels(coord.GetCoordValue(), aAppUnitsPerPixel);
}
if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
coord.GetUnit() != eStyleUnit_Percent) {
// <length> values represent offsets from the origin of the SVG element's
// user space, not the top left of its bounds, so we must adjust for that:
nscoord offset =
(index == 0) ? aFrame->GetPosition().x : aFrame->GetPosition().y;
*coords[index] -= NSAppUnitsToFloatPixels(offset, aAppUnitsPerPixel);
}
}
*coords[2] = NSAppUnitsToFloatPixels(display->mTransformOrigin[2].GetCoordValue(),
@ -2620,8 +2639,8 @@ gfxPoint3D GetDeltaToMozPerspectiveOrigin(const nsIFrame* aFrame,
float aAppUnitsPerPixel)
{
NS_PRECONDITION(aFrame, "Can't get delta for a null frame!");
NS_PRECONDITION(aFrame->GetStyleDisplay()->HasTransform(),
"Can't get a delta for an untransformed frame!");
NS_PRECONDITION(aFrame->IsTransformed(),
"Shouldn't get a delta for an untransformed frame!");
NS_PRECONDITION(aFrame->GetParentStyleContextFrame(),
"Can't get delta without a style parent!");
@ -2711,18 +2730,29 @@ nsDisplayTransform::GetResultingTransformMatrix(const nsIFrame* aFrame,
/* Get the matrix, then change its basis to factor in the origin. */
bool dummy;
gfx3DMatrix result;
// Call IsSVGTransformed() regardless of the value of
// disp->mSpecifiedTransform, since we still need any transformFromSVGParent.
gfxMatrix svgTransform, transformFromSVGParent;
bool hasSVGTransforms =
aFrame->IsSVGTransformed(&svgTransform, &transformFromSVGParent);
/* Transformed frames always have a transform, or are preserving 3d (and might still have perspective!) */
if (disp->mSpecifiedTransform) {
result = nsStyleTransformMatrix::ReadTransforms(disp->mSpecifiedTransform,
aFrame->GetStyleContext(),
aFrame->PresContext(),
dummy, bounds, aAppUnitsPerPixel);
} else if (hasSVGTransforms) {
result = gfx3DMatrix::From2D(svgTransform);
} else {
NS_ASSERTION(aFrame->GetStyleDisplay()->mTransformStyle == NS_STYLE_TRANSFORM_STYLE_PRESERVE_3D ||
aFrame->GetStyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN,
"If we don't have a transform, then we must have another reason to have an nsDisplayTransform created");
}
if (hasSVGTransforms && !transformFromSVGParent.IsIdentity()) {
result = result * gfx3DMatrix::From2D(transformFromSVGParent);
}
const nsStyleDisplay* parentDisp = nsnull;
nsStyleContext* parentStyleContext = aFrame->GetStyleContext()->GetParent();
if (parentStyleContext) {

View File

@ -2340,8 +2340,8 @@ public:
static nsRect GetFrameBoundsForTransform(const nsIFrame* aFrame);
/**
* Given a frame with the -moz-transform property, returns the
* transformation matrix for that frame.
* Given a frame with the -moz-transform property or an SVG transform,
* returns the transformation matrix for that frame.
*
* @param aFrame The frame to get the matrix from.
* @param aOrigin Relative to which point this transform should be applied.

View File

@ -4606,6 +4606,22 @@ nsLayoutUtils::DeregisterImageRequest(nsPresContext* aPresContext,
}
}
/* static */
void
nsLayoutUtils::PostRestyleEvent(Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint)
{
nsIDocument* doc = aElement->GetCurrentDoc();
if (doc) {
nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
if (presShell) {
presShell->FrameConstructor()->PostRestyleEvent(
aElement, aRestyleHint, aMinChangeHint);
}
}
}
nsSetAttrRunnable::nsSetAttrRunnable(nsIContent* aContent, nsIAtom* aAttrName,
const nsAString& aValue)
: mContent(aContent),

View File

@ -55,6 +55,7 @@ class nsClientRectList;
class nsFontFaceList;
#include "prtypes.h"
#include "nsChangeHint.h"
#include "nsStyleContext.h"
#include "nsAutoPtr.h"
#include "nsStyleSet.h"
@ -1652,6 +1653,15 @@ public:
imgIRequest* aRequest,
bool* aRequestRegistered);
/**
* Shim to nsCSSFrameConstructor::PostRestyleEvent. Exists so that we
* can avoid including nsCSSFrameConstructor.h and all its dependencies
* in content files.
*/
static void PostRestyleEvent(mozilla::dom::Element* aElement,
nsRestyleHint aRestyleHint,
nsChangeHint aMinChangeHint);
#ifdef DEBUG
/**
* Assert that there are no duplicate continuations of the same frame

View File

@ -961,7 +961,15 @@ bool
nsIFrame::IsTransformed() const
{
return (mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
GetStyleDisplay()->HasTransform();
(GetStyleDisplay()->HasTransform() ||
IsSVGTransformed());
}
bool
nsIFrame::IsSVGTransformed(gfxMatrix *aOwnTransforms,
gfxMatrix *aFromParentTransforms) const
{
return false;
}
bool
@ -4643,9 +4651,11 @@ nsIFrame::InvalidateInternalAfterResize(const nsRect& aDamageRect, nscoord aX,
}
}
if (IsTransformed() && !rectIsTransformed) {
nsRect newDamageRect;
newDamageRect.UnionRect(nsDisplayTransform::TransformRectOut
(aDamageRect, this, nsPoint(-aX, -aY)), aDamageRect);
nsRect newDamageRect = nsDisplayTransform::TransformRectOut
(aDamageRect, this, nsPoint(-aX, -aY));
if (!(GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
newDamageRect.UnionRect(newDamageRect, aDamageRect);
}
// If we are preserving 3d, then our computed transform includes that of any
// ancestor frames that also preserve 3d. Mark the rectangle as already being
@ -4703,6 +4713,7 @@ nsIFrame::GetTransformMatrix(nsIFrame* aStopAtAncestor,
gfx3DMatrix result =
nsDisplayTransform::GetResultingTransformMatrix(this, nsPoint(0, 0),
scaleFactor, nsnull, aOutAncestor);
// XXXjwatt: seems like this will double count offsets in the face of preserve-3d:
nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
/* Combine the raw transform with a translation to our parent. */
result *= gfx3DMatrix::Translation
@ -4835,6 +4846,21 @@ ComputeOutlineAndEffectsRect(nsIFrame* aFrame, bool* aAnyOutlineOrEffects,
nsRect r = aOverflowRect;
*aAnyOutlineOrEffects = false;
if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
// For SVG frames, we only need to account for filters.
// TODO: We could also take account of clipPath and mask to reduce the
// visual overflow, but that's not essential.
if (aFrame->GetStyleSVGReset()->mFilter) {
*aAnyOutlineOrEffects = true;
if (aStoreRectProperties) {
aFrame->Properties().
Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r));
}
r = nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame, aOverflowRect);
}
return r;
}
// box-shadow
nsCSSShadowArray* boxShadows = aFrame->GetStyleBorder()->mBoxShadow;
if (boxShadows) {
@ -6741,10 +6767,13 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
// If the overflow area width or height is nscoord_MAX, then a
// saturating union may have encounted an overflow, so the overflow may not
// contain the frame border-box. Don't warn in that case.
// Don't warn for SVG either, since SVG doesn't need the overflow area
// to contain the frame bounds.
NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
DebugOnly<nsRect*> r = &aOverflowAreas.Overflow(otype);
NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 ||
r->width == nscoord_MAX || r->height == nscoord_MAX ||
(mState & NS_FRAME_SVG_LAYOUT) ||
r->Contains(nsRect(nsPoint(0,0), aNewSize)),
"Computed overflow area must contain frame bounds");
}
@ -6763,10 +6792,13 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
}
// Overflow area must always include the frame's top-left and bottom-right,
// even if the frame rect is empty.
// even if the frame rect is empty (so we can scroll to those positions).
// Pending a real fix for bug 426879, don't do this for inline frames
// with zero width.
if (aNewSize.width != 0 || !IsInlineFrame(this)) {
// Do not do this for SVG either, since it will usually massively increase
// the area unnecessarily.
if ((aNewSize.width != 0 || !IsInlineFrame(this)) &&
!(GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
nsRect& o = aOverflowAreas.Overflow(otype);
o.UnionRectEdges(o, bounds);
@ -6870,7 +6902,10 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
Invalidate(aOverflowAreas.VisualOverflow());
}
}
if (anyOverflowChanged && hasTransform) {
// XXXSDL For SVG the invalidation happens in UpdateBounds for now, so we
// don't currently invalidate SVG here:
if (anyOverflowChanged && hasTransform &&
!(GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
// When there's a transform, changes to that style might require
// repainting of the old and new overflow areas in the widget.
// Repainting of the frame itself will not be required if there's

View File

@ -242,8 +242,9 @@ typedef PRUint64 nsFrameState;
// more details.
#define NS_FRAME_IS_SPECIAL NS_FRAME_STATE_BIT(15)
// If this bit is set, the frame may have a transform that it applies
// to its coordinate system (e.g. CSS transform, SVG foreignObject).
// If this bit is set, then transforms (e.g. CSS or SVG transforms) are allowed
// to affect the frame, and a transform may currently be in affect. If this bit
// is not set, then any transforms on the frame will be ignored.
// This is used primarily in GetTransformMatrix to optimize for the
// common case.
#define NS_FRAME_MAY_BE_TRANSFORMED NS_FRAME_STATE_BIT(16)
@ -311,6 +312,11 @@ typedef PRUint64 nsFrameState;
// context?
#define NS_FRAME_FONT_INFLATION_FLOW_ROOT NS_FRAME_STATE_BIT(42)
// This bit is set on SVG frames that are laid out using SVG's coordinate
// system based layout (as opposed to any of the CSS layout models). Note that
// this does not include nsSVGOuterSVGFrame since it takes part is CSS layout.
#define NS_FRAME_SVG_LAYOUT NS_FRAME_STATE_BIT(43)
// Box layout bits
#define NS_STATE_IS_HORIZONTAL NS_FRAME_STATE_BIT(22)
#define NS_STATE_IS_DIRECTION_NORMAL NS_FRAME_STATE_BIT(31)
@ -1229,10 +1235,23 @@ public:
virtual bool NeedsView() { return false; }
/**
* Returns whether this frame has a transform matrix applied to it. This is true
* if we have the -moz-transform property or if we're an SVGForeignObjectFrame.
* Returns true if this frame is transformed (e.g. has CSS or SVG transforms)
* or if its parent is an SVG frame that has children-only transforms (e.g.
* an SVG viewBox attribute).
*/
virtual bool IsTransformed() const;
bool IsTransformed() const;
/**
* Returns true if this frame is an SVG frame that has SVG transforms applied
* to it, or if its parent frame is an SVG frame that has children-only
* transforms (e.g. an SVG viewBox attribute).
* If aOwnTransforms is non-null and the frame has its own SVG transforms,
* aOwnTransforms will be set to these transforms. If aFromParentTransforms
* is non-null and the frame has an SVG parent with children-only transforms,
* then aFromParentTransforms will be set to these transforms.
*/
virtual bool IsSVGTransformed(gfxMatrix *aOwnTransforms = nsnull,
gfxMatrix *aFromParentTransforms = nsnull) const;
/**
* Returns whether this frame will attempt to preserve the 3d transforms of its
@ -1969,8 +1988,8 @@ public:
* @return A gfxMatrix that converts points in this frame's coordinate space
* into points in aOutAncestor's coordinate space.
*/
virtual gfx3DMatrix GetTransformMatrix(nsIFrame* aStopAtAncestor,
nsIFrame **aOutAncestor);
gfx3DMatrix GetTransformMatrix(nsIFrame* aStopAtAncestor,
nsIFrame **aOutAncestor);
/**
* Bit-flags to pass to IsFrameOfType()

View File

@ -110,7 +110,8 @@ public:
enum SVGChangedFlags {
DO_NOT_NOTIFY_RENDERING_OBSERVERS = 0x01,
TRANSFORM_CHANGED = 0x02,
COORD_CONTEXT_CHANGED = 0x04
COORD_CONTEXT_CHANGED = 0x04,
FULL_ZOOM_CHANGED = 0x08
};
virtual void NotifySVGChanged(PRUint32 aFlags)=0;
@ -140,9 +141,6 @@ public:
// Are we a container frame?
NS_IMETHOD_(bool) IsDisplayContainer()=0;
// Does this frame have an current covered region in mRect (aka GetRect())?
NS_IMETHOD_(bool) HasValidCoveredRect()=0;
};
#endif // __NS_ISVGCHILDFRAME_H__

View File

@ -38,8 +38,12 @@
#include "nsSVGContainerFrame.h"
// Keep others in (case-insensitive) order:
#include "nsSVGEffects.h"
#include "nsSVGElement.h"
#include "nsSVGUtils.h"
#include "SVGAnimatedTransformList.h"
using namespace mozilla;
NS_QUERYFRAME_HEAD(nsSVGContainerFrame)
NS_QUERYFRAME_ENTRY(nsSVGContainerFrame)
@ -96,6 +100,17 @@ nsSVGContainerFrame::RemoveFrame(ChildListID aListID,
return NS_OK;
}
bool
nsSVGContainerFrame::UpdateOverflow()
{
if (mState & NS_STATE_SVG_NONDISPLAY_CHILD) {
// We don't maintain overflow rects.
// XXX It would have be better if the restyle request hadn't even happened.
return false;
}
return nsSVGContainerFrameBase::UpdateOverflow();
}
NS_IMETHODIMP
nsSVGDisplayContainerFrame::Init(nsIContent* aContent,
nsIFrame* aParent,
@ -163,6 +178,32 @@ nsSVGDisplayContainerFrame::RemoveFrame(ChildListID aListID,
return rv;
}
bool
nsSVGDisplayContainerFrame::IsSVGTransformed(gfxMatrix *aOwnTransform,
gfxMatrix *aFromParentTransform) const
{
bool foundTransform = false;
// Check if our parent has children-only transforms:
nsIFrame *parent = GetParent();
if (parent &&
parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
HasChildrenOnlyTransform(aFromParentTransform);
}
nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
const SVGAnimatedTransformList *list = content->GetAnimatedTransformList();
if (list && !list->GetAnimValue().IsEmpty()) {
if (aOwnTransform) {
*aOwnTransform = content->PrependLocalTransformsTo(gfxMatrix(),
nsSVGElement::eUserSpaceToParent);
}
foundTransform = true;
}
return foundTransform;
}
//----------------------------------------------------------------------
// nsISVGChildFrame methods
@ -203,6 +244,9 @@ nsSVGDisplayContainerFrame::UpdateBounds()
NS_ABORT_IF_FALSE(!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
"UpdateBounds mechanism not designed for this");
NS_ABORT_IF_FALSE(GetType() != nsGkAtoms::svgOuterSVGFrame,
"Do not call on outer-<svg>");
if (!nsSVGUtils::NeedsUpdatedBounds(this)) {
return;
}
@ -221,6 +265,8 @@ nsSVGDisplayContainerFrame::UpdateBounds()
mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children
}
nsOverflowAreas overflowRects;
for (nsIFrame* kid = mFrames.FirstChild(); kid;
kid = kid->GetNextSibling()) {
nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
@ -228,14 +274,43 @@ nsSVGDisplayContainerFrame::UpdateBounds()
NS_ABORT_IF_FALSE(!(kid->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
"Check for this explicitly in the |if|, then");
SVGFrame->UpdateBounds();
// We build up our child frame overflows here instead of using
// nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same
// frame list, and we're iterating over that list now anyway.
ConsiderChildOverflow(overflowRects, kid);
}
}
// <svg> can create an SVG viewport with an offset due to its
// x/y/width/height attributes, and <use> can introduce an offset with an
// empty mRect (any width/height is copied to an anonymous <svg> child).
// Other than that containers should not set mRect since all other offsets
// come from transforms, which are accounted for by nsDisplayTransform.
// Note that we rely on |overflow:visible| to allow display list items to be
// created for our children.
NS_ABORT_IF_FALSE(mContent->Tag() == nsGkAtoms::svg ||
(mContent->Tag() == nsGkAtoms::use &&
mRect.Size() == nsSize(0,0)) ||
mRect.IsEqualEdges(nsRect()),
"Only inner-<svg>/<use> is expected to have mRect set");
if (mState & NS_FRAME_FIRST_REFLOW) {
// Make sure we have our filter property (if any) before calling
// FinishAndStoreOverflow (subsequent filter changes are handled off
// nsChangeHint_UpdateEffects):
nsSVGEffects::UpdateEffects(this);
}
FinishAndStoreOverflow(overflowRects, mRect.Size());
// Remove state bits after FinishAndStoreOverflow so that it doesn't
// invalidate on first reflow:
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
NS_FRAME_HAS_DIRTY_CHILDREN);
// XXXsvgreflow once we store bounds on containers, do...
// nsSVGUtils::InvalidateBounds(this);
// XXXSDL Make Invalidate() call nsSVGUtils::InvalidateBounds(this)
// so that we invalidate under FinishAndStoreOverflow().
}
void

View File

@ -68,8 +68,11 @@ class nsSVGContainerFrame : public nsSVGContainerFrameBase
friend nsIFrame* NS_NewSVGContainerFrame(nsIPresShell* aPresShell,
nsStyleContext* aContext);
protected:
nsSVGContainerFrame(nsStyleContext* aContext) :
nsSVGContainerFrameBase(aContext) {}
nsSVGContainerFrame(nsStyleContext* aContext)
: nsSVGContainerFrameBase(aContext)
{
AddStateBits(NS_FRAME_SVG_LAYOUT);
}
public:
NS_DECL_QUERYFRAME_TARGET(nsSVGContainerFrame)
@ -79,6 +82,17 @@ public:
// Returns the transform to our gfxContext (to device pixels, not CSS px)
virtual gfxMatrix GetCanvasTM() { return gfxMatrix(); }
/**
* Returns true if the frame's content has a transform that applies only to
* its children, and not to the frame itself. For example, an implicit
* transform introduced by a 'viewBox' attribute, or an explicit transform
* due to a root-<svg> having its currentScale/currentTransform properties
* set. If aTransform is non-null, then it will be set to the transform.
*/
virtual bool HasChildrenOnlyTransform(gfxMatrix *aTransform) const {
return false;
}
// nsIFrame:
NS_IMETHOD AppendFrames(ChildListID aListID,
nsFrameList& aFrameList);
@ -93,6 +107,8 @@ public:
return nsSVGContainerFrameBase::IsFrameOfType(
aFlags & ~(nsIFrame::eSVG | nsIFrame::eSVGContainer));
}
virtual bool UpdateOverflow();
};
/**
@ -103,8 +119,11 @@ class nsSVGDisplayContainerFrame : public nsSVGContainerFrame,
public nsISVGChildFrame
{
protected:
nsSVGDisplayContainerFrame(nsStyleContext* aContext) :
nsSVGContainerFrame(aContext) {}
nsSVGDisplayContainerFrame(nsStyleContext* aContext)
: nsSVGContainerFrame(aContext)
{
AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
}
public:
NS_DECL_QUERYFRAME_TARGET(nsSVGDisplayContainerFrame)
@ -121,6 +140,9 @@ public:
nsIFrame* aParent,
nsIFrame* aPrevInFlow);
virtual bool IsSVGTransformed(gfxMatrix *aOwnTransform = nsnull,
gfxMatrix *aFromParentTransform = nsnull) const;
// nsISVGChildFrame interface:
NS_IMETHOD PaintSVG(nsRenderingContext* aContext,
const nsIntRect *aDirtyRect);
@ -131,7 +153,6 @@ public:
virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
PRUint32 aFlags);
NS_IMETHOD_(bool) IsDisplayContainer() { return true; }
NS_IMETHOD_(bool) HasValidCoveredRect() { return false; }
};
#endif

View File

@ -99,7 +99,8 @@ public:
nsSVGFilterPaintCallback *aPaint,
const nsIntRect *aDirtyOutputRect,
const nsIntRect *aDirtyInputRect,
const nsIntRect *aOverrideSourceBBox);
const nsIntRect *aOverrideSourceBBox,
const gfxMatrix *aOverrideUserToDeviceSpace = nsnull);
~nsAutoFilterInstance() {}
// If this returns null, then draw nothing. Either the filter draws
@ -115,7 +116,8 @@ nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
nsSVGFilterPaintCallback *aPaint,
const nsIntRect *aDirtyOutputRect,
const nsIntRect *aDirtyInputRect,
const nsIntRect *aOverrideSourceBBox)
const nsIntRect *aOverrideSourceBBox,
const gfxMatrix *aOverrideUserToDeviceSpace)
{
const nsSVGFilterElement *filter = aFilterFrame->GetFilterContent();
@ -161,10 +163,11 @@ nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
return;
}
gfxMatrix userToDeviceSpace = nsSVGUtils::GetCanvasTM(aTarget);
if (userToDeviceSpace.IsSingular()) {
// nothing to draw
return;
gfxMatrix userToDeviceSpace;
if (aOverrideUserToDeviceSpace) {
userToDeviceSpace = *aOverrideUserToDeviceSpace;
} else {
userToDeviceSpace = nsSVGUtils::GetCanvasTM(aTarget);
}
// Calculate filterRes (the width and height of the pixel buffer of the
@ -195,7 +198,12 @@ nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
} else {
// Match filterRes as closely as possible to the pixel density of the nearest
// outer 'svg' device space:
float scale = nsSVGUtils::MaxExpansion(userToDeviceSpace);
gfxMatrix canvasTM = nsSVGUtils::GetCanvasTM(aTarget);
if (canvasTM.IsSingular()) {
// nothing to draw
return;
}
float scale = nsSVGUtils::MaxExpansion(canvasTM);
filterRegion.Scale(scale);
filterRegion.RoundOut();
@ -228,8 +236,20 @@ nsAutoFilterInstance::nsAutoFilterInstance(nsIFrame *aTarget,
nsIntRect targetBoundsDeviceSpace;
nsISVGChildFrame* svgTarget = do_QueryFrame(aTarget);
if (svgTarget) {
targetBoundsDeviceSpace.UnionRect(targetBoundsDeviceSpace,
svgTarget->GetCoveredRegion().ToOutsidePixels(aTarget->PresContext()->AppUnitsPerDevPixel()));
if (aOverrideUserToDeviceSpace) {
// If aOverrideUserToDeviceSpace is specified, it is a simple
// CSS-px-to-dev-px transform passed by nsSVGFilterFrame::GetFilterBBox()
// when requesting the filter expansion of the overflow rects in frame
// space. In this case GetCoveredRegion() is not what we want since it is
// in outer-<svg> space, GetFilterBBox passes in the pre-filter bounds of
// the frame in frame space for us to use instead.
NS_ASSERTION(aDirtyInputRect, "Who passed aOverrideUserToDeviceSpace?");
targetBoundsDeviceSpace = *aDirtyInputRect;
} else {
targetBoundsDeviceSpace =
svgTarget->GetCoveredRegion().ToOutsidePixels(aTarget->
PresContext()->AppUnitsPerDevPixel());
}
}
nsIntRect targetBoundsFilterSpace =
MapDeviceRectToFilterSpace(deviceToFilterSpace, filterRes, &targetBoundsDeviceSpace);
@ -471,9 +491,27 @@ nsSVGFilterFrame::GetSourceForInvalidArea(nsIFrame *aTarget, const nsIntRect& aR
}
nsIntRect
nsSVGFilterFrame::GetFilterBBox(nsIFrame *aTarget, const nsIntRect *aSourceBBox)
nsSVGFilterFrame::GetFilterBBox(nsIFrame *aTarget,
const nsIntRect *aOverrideBBox,
const nsIntRect *aPreFilterBounds)
{
nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, nsnull, aSourceBBox);
bool overrideCTM = false;
gfxMatrix ctm;
if (aTarget->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
// For most filter operations on SVG frames we want information in
// outer-<svg> device space, but in this case we want the visual overflow
// rect relative to aTarget itself. For that we need to prevent the filter
// code using GetCanvasTM().
overrideCTM = true;
PRInt32 appUnitsPerDevPixel = aTarget->PresContext()->AppUnitsPerDevPixel();
float devPxPerCSSPx =
1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
ctm.Scale(devPxPerCSSPx, devPxPerCSSPx);
}
nsAutoFilterInstance instance(aTarget, this, nsnull, nsnull, aPreFilterBounds,
aOverrideBBox, overrideCTM ? &ctm : nsnull);
if (!instance.get())
return nsIntRect();

View File

@ -99,9 +99,11 @@ public:
* Returns the bounding box of the post-filter area of aTarget.
* The rectangles are relative to the origin of the outer svg, if aTarget is SVG,
* relative to aTarget itself otherwise, in device pixels.
* @param aSourceBBox overrides the normal bbox for the source, if non-null
* @param aOverrideBBox overrides the normal bbox for the source, if non-null
*/
nsIntRect GetFilterBBox(nsIFrame *aTarget, const nsIntRect *aSourceBBox);
nsIntRect GetFilterBBox(nsIFrame *aTarget,
const nsIntRect *aOverrideBBox = nsnull,
const nsIntRect *aPreFilterBounds = nsnull);
#ifdef DEBUG
NS_IMETHOD Init(nsIContent* aContent,

View File

@ -73,7 +73,8 @@ nsSVGForeignObjectFrame::nsSVGForeignObjectFrame(nsStyleContext* aContext)
: nsSVGForeignObjectFrameBase(aContext),
mInReflow(false)
{
AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED);
AddStateBits(NS_FRAME_REFLOW_ROOT | NS_FRAME_MAY_BE_TRANSFORMED |
NS_FRAME_SVG_LAYOUT);
}
//----------------------------------------------------------------------
@ -305,24 +306,6 @@ nsSVGForeignObjectFrame::PaintSVG(nsRenderingContext *aContext,
return rv;
}
gfx3DMatrix
nsSVGForeignObjectFrame::GetTransformMatrix(nsIFrame* aAncestor,
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?");
if (GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD) {
return gfx3DMatrix::From2D(gfxMatrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
}
/* Return the matrix back to the root, factoring in the x and y offsets. */
return gfx3DMatrix::From2D(GetCanvasTMForChildren());
}
NS_IMETHODIMP_(nsIFrame*)
nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint)
{
@ -415,6 +398,19 @@ nsSVGForeignObjectFrame::UpdateBounds()
DoReflow();
if (mState & NS_FRAME_FIRST_REFLOW) {
// Make sure we have our filter property (if any) before calling
// FinishAndStoreOverflow (subsequent filter changes are handled off
// nsChangeHint_UpdateEffects):
nsSVGEffects::UpdateEffects(this);
}
// TODO: once we support |overflow:visible| on foreignObject, then we will
// need to take account of our descendants here.
nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
nsOverflowAreas overflowAreas(overflow, overflow);
FinishAndStoreOverflow(overflowAreas, mRect.Size());
// Now unset the various reflow bits:
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
NS_FRAME_HAS_DIRTY_CHILDREN);
@ -423,6 +419,7 @@ nsSVGForeignObjectFrame::UpdateBounds()
// We only invalidate if our outer-<svg> has already had its
// initial reflow (since if it hasn't, its entire area will be
// invalidated when it gets that initial reflow):
// XXXSDL Let FinishAndStoreOverflow do this.
nsSVGUtils::InvalidateBounds(this, true);
}
}

View File

@ -81,20 +81,6 @@ public:
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus);
/**
* Foreign objects are always transformed.
*/
virtual bool IsTransformed() const
{
return true;
}
/**
* Foreign objects can return a transform matrix.
*/
virtual gfx3DMatrix GetTransformMatrix(nsIFrame* aAncestor,
nsIFrame **aOutAncestor);
/**
* Get the "type" of the frame
*
@ -129,9 +115,6 @@ public:
virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
PRUint32 aFlags);
NS_IMETHOD_(bool) IsDisplayContainer() { return true; }
NS_IMETHOD_(bool) HasValidCoveredRect() {
return !(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD);
}
gfxMatrix GetCanvasTM();

View File

@ -69,7 +69,11 @@ class nsSVGGeometryFrame : public nsSVGGeometryFrameBase
protected:
NS_DECL_FRAMEARENA_HELPERS
nsSVGGeometryFrame(nsStyleContext *aContext) : nsSVGGeometryFrameBase(aContext) {}
nsSVGGeometryFrame(nsStyleContext *aContext)
: nsSVGGeometryFrameBase(aContext)
{
AddStateBits(NS_FRAME_SVG_LAYOUT);
}
public:
// nsIFrame interface:

View File

@ -512,6 +512,10 @@ nsSVGGlyphFrame::UpdateBounds()
mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG(
mRect, GetCanvasTM(), PresContext());
nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
nsOverflowAreas overflowAreas(overflow, overflow);
FinishAndStoreOverflow(overflowAreas, mRect.Size());
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
NS_FRAME_HAS_DIRTY_CHILDREN);
@ -519,6 +523,7 @@ nsSVGGlyphFrame::UpdateBounds()
// We only invalidate if our outer-<svg> has already had its
// initial reflow (since if it hasn't, its entire area will be
// invalidated when it gets that initial reflow):
// XXXSDL Let FinishAndStoreOverflow do this.
nsSVGUtils::InvalidateBounds(this, true);
}
}

View File

@ -185,9 +185,6 @@ public:
virtual void UpdateBounds();
virtual void NotifySVGChanged(PRUint32 aFlags);
NS_IMETHOD_(bool) IsDisplayContainer() { return false; }
NS_IMETHOD_(bool) HasValidCoveredRect() {
return !(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD);
}
// nsSVGGeometryFrame methods
gfxMatrix GetCanvasTM();

View File

@ -514,6 +514,17 @@ nsSVGImageFrame::UpdateBounds()
mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG(
mRect, GetCanvasTM(), PresContext());
if (mState & NS_FRAME_FIRST_REFLOW) {
// Make sure we have our filter property (if any) before calling
// FinishAndStoreOverflow (subsequent filter changes are handled off
// nsChangeHint_UpdateEffects):
nsSVGEffects::UpdateEffects(this);
}
nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
nsOverflowAreas overflowAreas(overflow, overflow);
FinishAndStoreOverflow(overflowAreas, mRect.Size());
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
NS_FRAME_HAS_DIRTY_CHILDREN);
@ -521,6 +532,7 @@ nsSVGImageFrame::UpdateBounds()
// We only invalidate if our outer-<svg> has already had its
// initial reflow (since if it hasn't, its entire area will be
// invalidated when it gets that initial reflow):
// XXXSDL Let FinishAndStoreOverflow do this.
nsSVGUtils::InvalidateBounds(this, true);
}
}

View File

@ -113,6 +113,20 @@ nsSVGInnerSVGFrame::PaintSVG(nsRenderingContext *aContext,
return nsSVGInnerSVGFrameBase::PaintSVG(aContext, aDirtyRect);
}
void
nsSVGInnerSVGFrame::UpdateBounds()
{
// mRect must be set before FinishAndStoreOverflow is called in order
// for our overflow areas to be clipped correctly.
float x, y, width, height;
static_cast<nsSVGSVGElement*>(mContent)->
GetAnimatedLengthValues(&x, &y, &width, &height, nsnull);
mRect = nsLayoutUtils::RoundGfxRectToAppRect(
gfxRect(x, y, width, height),
PresContext()->AppUnitsPerCSSPixel());
nsSVGInnerSVGFrameBase::UpdateBounds();
}
void
nsSVGInnerSVGFrame::NotifySVGChanged(PRUint32 aFlags)
{
@ -171,24 +185,21 @@ nsSVGInnerSVGFrame::AttributeChanged(PRInt32 aNameSpaceID,
PRInt32 aModType)
{
if (aNameSpaceID == kNameSpaceID_None) {
nsSVGSVGElement* content = static_cast<nsSVGSVGElement*>(mContent);
if (aAttribute == nsGkAtoms::width ||
aAttribute == nsGkAtoms::height) {
nsSVGSVGElement* svg = static_cast<nsSVGSVGElement*>(mContent);
if (svg->HasViewBox()) {
if (content->HasViewBoxOrSyntheticViewBox()) {
// make sure our cached transform matrix gets (lazily) updated
mCanvasTM = nsnull;
content->ChildrenOnlyTransformChanged();
nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
} else {
PRUint32 flags = COORD_CONTEXT_CHANGED;
if (mCanvasTM && mCanvasTM->IsSingular()) {
mCanvasTM = nsnull;
flags |= TRANSFORM_CHANGED;
}
nsSVGUtils::NotifyChildrenOfSVGChange(this, flags);
@ -205,6 +216,12 @@ nsSVGInnerSVGFrame::AttributeChanged(PRInt32 aNameSpaceID,
nsSVGUtils::NotifyChildrenOfSVGChange(
this, aAttribute == nsGkAtoms::viewBox ?
TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED);
if (aAttribute == nsGkAtoms::viewBox ||
(aAttribute == nsGkAtoms::preserveAspectRatio &&
content->HasViewBoxOrSyntheticViewBox())) {
content->ChildrenOnlyTransformChanged();
}
}
}
@ -265,3 +282,17 @@ nsSVGInnerSVGFrame::GetCanvasTM()
return *mCanvasTM;
}
bool
nsSVGInnerSVGFrame::HasChildrenOnlyTransform(gfxMatrix *aTransform) const
{
nsSVGSVGElement *content = static_cast<nsSVGSVGElement*>(mContent);
if (content->HasViewBoxOrSyntheticViewBox()) {
// XXX Maybe return false if the transform is the identity transform?
if (aTransform) {
*aTransform = content->GetViewBoxTransform();
}
return true;
}
return false;
}

View File

@ -84,12 +84,15 @@ public:
// nsISVGChildFrame interface:
NS_IMETHOD PaintSVG(nsRenderingContext *aContext, const nsIntRect *aDirtyRect);
virtual void UpdateBounds();
virtual void NotifySVGChanged(PRUint32 aFlags);
NS_IMETHOD_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
// nsSVGContainerFrame methods:
virtual gfxMatrix GetCanvasTM();
virtual bool HasChildrenOnlyTransform(gfxMatrix *aTransform) const;
// nsISVGSVGFrame interface:
virtual void NotifyViewportOrTransformChanged(PRUint32 aFlags);

View File

@ -146,6 +146,8 @@ nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(nsStyleContext* aContext)
#endif
, mIsRootContent(false)
{
// Outer-<svg> has CSS layout, so remove this bit:
RemoveStateBits(NS_FRAME_SVG_LAYOUT);
}
NS_IMETHODIMP
@ -416,7 +418,7 @@ nsSVGOuterSVGFrame::Reflow(nsPresContext* aPresContext,
svgElem->SetViewportSize(newViewportSize);
}
if (mFullZoom != PresContext()->GetFullZoom()) {
changeBits |= TRANSFORM_CHANGED;
changeBits |= FULL_ZOOM_CHANGED;
mFullZoom = PresContext()->GetFullZoom();
}
mViewportInitialized = true;
@ -617,9 +619,14 @@ nsSVGOuterSVGFrame::AttributeChanged(PRInt32 aNameSpaceID,
this, aAttribute == nsGkAtoms::viewBox ?
TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED);
static_cast<nsSVGSVGElement*>(mContent)->ChildrenOnlyTransformChanged();
} else if (aAttribute == nsGkAtoms::width ||
aAttribute == nsGkAtoms::height) {
// Don't call ChildrenOnlyTransformChanged() here, since we call it
// under Reflow if the width/height actually changed.
nsIFrame* embeddingFrame;
if (IsRootOfReplacedElementSubDoc(&embeddingFrame) && embeddingFrame) {
if (DependsOnIntrinsicSize(embeddingFrame)) {
@ -703,7 +710,8 @@ void
nsSVGOuterSVGFrame::NotifyViewportOrTransformChanged(PRUint32 aFlags)
{
NS_ABORT_IF_FALSE(aFlags &&
!(aFlags & ~(COORD_CONTEXT_CHANGED | TRANSFORM_CHANGED)),
!(aFlags & ~(COORD_CONTEXT_CHANGED | TRANSFORM_CHANGED |
FULL_ZOOM_CHANGED)),
"Unexpected aFlags value");
// No point in doing anything when were not init'ed yet:
@ -729,9 +737,21 @@ nsSVGOuterSVGFrame::NotifyViewportOrTransformChanged(PRUint32 aFlags)
}
}
bool haveNonFulLZoomTransformChange = (aFlags & TRANSFORM_CHANGED);
if (aFlags & FULL_ZOOM_CHANGED) {
// Convert FULL_ZOOM_CHANGED to TRANSFORM_CHANGED:
aFlags = (aFlags & ~FULL_ZOOM_CHANGED) | TRANSFORM_CHANGED;
}
if (aFlags & TRANSFORM_CHANGED) {
// Make sure our canvas transform matrix gets (lazily) recalculated:
mCanvasTM = nsnull;
if (haveNonFulLZoomTransformChange &&
!(mState & NS_STATE_SVG_NONDISPLAY_CHILD)) {
content->ChildrenOnlyTransformChanged();
}
}
nsSVGUtils::NotifyChildrenOfSVGChange(this, aFlags);
@ -765,6 +785,24 @@ nsSVGOuterSVGFrame::GetCanvasTM()
return *mCanvasTM;
}
bool
nsSVGOuterSVGFrame::HasChildrenOnlyTransform(gfxMatrix *aTransform) const
{
nsSVGSVGElement *content = static_cast<nsSVGSVGElement*>(mContent);
bool hasTransform = content->HasChildrenOnlyTransform();
if (hasTransform && aTransform) {
// Outer-<svg> doesn't use x/y, so we can pass eChildToUserSpace here.
gfxMatrix identity;
*aTransform =
content->PrependLocalTransformsTo(identity,
nsSVGElement::eChildToUserSpace);
}
return hasTransform;
}
//----------------------------------------------------------------------
// Implementation helpers

View File

@ -113,12 +113,21 @@ public:
nsIAtom* aAttribute,
PRInt32 aModType);
virtual bool IsSVGTransformed(gfxMatrix *aOwnTransform,
gfxMatrix *aFromParentTransform) const {
// Outer-<svg> can transform its children with viewBox, currentScale and
// currentTranslate, but it itself is not transformed by SVG transforms.
return false;
}
// nsISVGSVGFrame interface:
virtual void NotifyViewportOrTransformChanged(PRUint32 aFlags);
// nsSVGContainerFrame methods:
virtual gfxMatrix GetCanvasTM();
virtual bool HasChildrenOnlyTransform(gfxMatrix *aTransform) const;
#ifdef XP_MACOSX
bool BitmapFallbackEnabled() const {
return mEnableBitmapFallback;

View File

@ -49,6 +49,9 @@
#include "nsSVGMarkerFrame.h"
#include "nsSVGPathGeometryElement.h"
#include "nsSVGUtils.h"
#include "SVGAnimatedTransformList.h"
using namespace mozilla;
//----------------------------------------------------------------------
// Implementation
@ -106,6 +109,32 @@ nsSVGPathGeometryFrame::GetType() const
return nsGkAtoms::svgPathGeometryFrame;
}
bool
nsSVGPathGeometryFrame::IsSVGTransformed(gfxMatrix *aOwnTransform,
gfxMatrix *aFromParentTransform) const
{
bool foundTransform = false;
// Check if our parent has children-only transforms:
nsIFrame *parent = GetParent();
if (parent &&
parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
HasChildrenOnlyTransform(aFromParentTransform);
}
nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
const SVGAnimatedTransformList *list = content->GetAnimatedTransformList();
if (list && !list->GetAnimValue().IsEmpty()) {
if (aOwnTransform) {
*aOwnTransform = content->PrependLocalTransformsTo(gfxMatrix(),
nsSVGElement::eUserSpaceToParent);
}
foundTransform = true;
}
return foundTransform;
}
//----------------------------------------------------------------------
// nsISVGChildFrame methods
@ -235,9 +264,22 @@ nsSVGPathGeometryFrame::UpdateBounds()
mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG(
mRect, GetCanvasTM(), PresContext());
if (mState & NS_FRAME_FIRST_REFLOW) {
// Make sure we have our filter property (if any) before calling
// FinishAndStoreOverflow (subsequent filter changes are handled off
// nsChangeHint_UpdateEffects):
nsSVGEffects::UpdateEffects(this);
}
nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
nsOverflowAreas overflowAreas(overflow, overflow);
FinishAndStoreOverflow(overflowAreas, mRect.Size());
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
NS_FRAME_HAS_DIRTY_CHILDREN);
// XXXSDL get rid of this in favor of the invalidate call in
// FinishAndStoreOverflow?
if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
// We only invalidate if our outer-<svg> has already had its
// initial reflow (since if it hasn't, its entire area will be

View File

@ -68,8 +68,11 @@ class nsSVGPathGeometryFrame : public nsSVGPathGeometryFrameBase,
friend nsIFrame*
NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
protected:
nsSVGPathGeometryFrame(nsStyleContext* aContext) :
nsSVGPathGeometryFrameBase(aContext) {}
nsSVGPathGeometryFrame(nsStyleContext* aContext)
: nsSVGPathGeometryFrameBase(aContext)
{
AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
}
public:
NS_DECL_QUERYFRAME
@ -89,6 +92,9 @@ public:
*/
virtual nsIAtom* GetType() const;
virtual bool IsSVGTransformed(gfxMatrix *aOwnTransforms = nsnull,
gfxMatrix *aFromParentTransforms = nsnull) const;
#ifdef DEBUG
NS_IMETHOD GetFrameName(nsAString& aResult) const
{
@ -110,9 +116,6 @@ protected:
virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
PRUint32 aFlags);
NS_IMETHOD_(bool) IsDisplayContainer() { return false; }
NS_IMETHOD_(bool) HasValidCoveredRect() {
return !(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD);
}
protected:
void GeneratePath(gfxContext *aContext,

View File

@ -39,6 +39,7 @@
// Keep in (case-insensitive) order:
#include "gfxMatrix.h"
#include "gfxRect.h"
#include "nsSVGEffects.h"
#include "nsSVGGFrame.h"
#include "nsSVGSwitchElement.h"
#include "nsSVGUtils.h"
@ -82,6 +83,7 @@ public:
NS_IMETHOD PaintSVG(nsRenderingContext* aContext, const nsIntRect *aDirtyRect);
NS_IMETHODIMP_(nsIFrame*) GetFrameForPoint(const nsPoint &aPoint);
NS_IMETHODIMP_(nsRect) GetCoveredRegion();
virtual void UpdateBounds();
virtual SVGBBox GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
PRUint32 aFlags);
@ -164,6 +166,68 @@ nsSVGSwitchFrame::GetCoveredRegion()
return rect;
}
void
nsSVGSwitchFrame::UpdateBounds()
{
NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingUpdateBounds(this),
"This call is probaby a wasteful mistake");
NS_ABORT_IF_FALSE(!(GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
"UpdateBounds mechanism not designed for this");
if (!nsSVGUtils::NeedsUpdatedBounds(this)) {
return;
}
// If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame,
// then our outer-<svg> has previously had its initial reflow. In that case
// we need to make sure that that bit has been removed from ourself _before_
// recursing over our children to ensure that they know too. Otherwise, we
// need to remove it _after_ recursing over our children so that they know
// the initial reflow is currently underway.
bool outerSVGHasHadFirstReflow =
(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0;
if (outerSVGHasHadFirstReflow) {
mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children
}
nsOverflowAreas overflowRects;
nsIFrame *child = GetActiveChildFrame();
if (child) {
nsISVGChildFrame* svgChild = do_QueryFrame(child);
if (svgChild) {
NS_ABORT_IF_FALSE(!(child->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD),
"Check for this explicitly in the |if|, then");
svgChild->UpdateBounds();
// We build up our child frame overflows here instead of using
// nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same
// frame list, and we're iterating over that list now anyway.
ConsiderChildOverflow(overflowRects, child);
}
}
if (mState & NS_FRAME_FIRST_REFLOW) {
// Make sure we have our filter property (if any) before calling
// FinishAndStoreOverflow (subsequent filter changes are handled off
// nsChangeHint_UpdateEffects):
nsSVGEffects::UpdateEffects(this);
}
FinishAndStoreOverflow(overflowRects, mRect.Size());
// Remove state bits after FinishAndStoreOverflow so that it doesn't
// invalidate on first reflow:
mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
NS_FRAME_HAS_DIRTY_CHILDREN);
// XXXSDL Make Invalidate() call nsSVGUtils::InvalidateBounds(this)
// so that we invalidate under FinishAndStoreOverflow().
}
SVGBBox
nsSVGSwitchFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace,
PRUint32 aFlags)

View File

@ -257,7 +257,7 @@ nsSVGTextFrame::UpdateBounds()
// areas correctly:
nsSVGTextFrameBase::UpdateBounds();
// XXXsvgreflow once we store bounds on containers, call
// XXXSDL once we store bounds on containers, call
// nsSVGUtils::InvalidateBounds(this) if not first reflow.
}

View File

@ -87,6 +87,7 @@ public:
#endif
// nsISVGChildFrame interface:
virtual void UpdateBounds();
virtual void NotifySVGChanged(PRUint32 aFlags);
// nsIAnonymousContentCreator
@ -195,6 +196,21 @@ nsSVGUseFrame::IsLeaf() const
//----------------------------------------------------------------------
// nsISVGChildFrame methods
void
nsSVGUseFrame::UpdateBounds()
{
// We only handle x/y offset here, since any width/height that is in force is
// handled by the nsSVGOuterSVGFrame for the anonymous <svg> that will be
// created for that purpose.
float x, y;
static_cast<nsSVGUseElement*>(mContent)->
GetAnimatedLengthValues(&x, &y, nsnull);
mRect.MoveTo(nsLayoutUtils::RoundGfxRectToAppRect(
gfxRect(x, y, 0.0, 0.0),
PresContext()->AppUnitsPerCSSPixel()).TopLeft());
nsSVGUseFrameBase::UpdateBounds();
}
void
nsSVGUseFrame::NotifySVGChanged(PRUint32 aFlags)
{

View File

@ -47,6 +47,7 @@
#include "gfxUtils.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/Preferences.h"
#include "nsCSSFrameConstructor.h"
#include "nsComputedDOMStyle.h"
#include "nsContentUtils.h"
#include "nsFrameList.h"
@ -587,6 +588,26 @@ nsSVGUtils::GetNearestSVGViewport(nsIFrame *aFrame)
return nsnull;
}
nsRect
nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
const nsRect &aUnfilteredRect)
{
NS_ABORT_IF_FALSE(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
"Called on invalid frame type");
nsSVGFilterFrame *filter = nsSVGEffects::GetFilterFrame(aFrame);
if (!filter) {
return aUnfilteredRect;
}
PRInt32 appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
nsIntRect unfilteredRect =
aUnfilteredRect.ToOutsidePixels(appUnitsPerDevPixel);
nsIntRect rect = filter->GetFilterBBox(aFrame, nsnull, &unfilteredRect);
nsRect r = rect.ToAppUnits(appUnitsPerDevPixel) - aFrame->GetPosition();
return r;
}
nsRect
nsSVGUtils::FindFilterInvalidation(nsIFrame *aFrame, const nsRect& aRect)
{
@ -696,7 +717,7 @@ nsSVGUtils::InvalidateBounds(nsIFrame *aFrame, bool aDuringUpdate)
return;
}
// XXXsvgreflow we want to reduce the bounds when passing through inner-<svg>
// XXXSDL we want to reduce the bounds when passing through inner-<svg>
// and <use>, etc.
nsSVGOuterSVGFrame* outerSVGFrame = GetOuterSVGFrame(aFrame);
@ -762,7 +783,7 @@ nsSVGUtils::ScheduleBoundsUpdate(nsIFrame *aFrame)
return;
}
// XXXsvgreflow once we store bounds on containers, we will not need to
// XXXSDL once we store bounds on containers, we will not need to
// mark our descendants dirty.
MarkDirtyBitsOnDescendants(aFrame);
@ -1144,20 +1165,33 @@ nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
bool isOK = true;
nsSVGFilterFrame *filterFrame = effectProperties.GetFilterFrame(&isOK);
/* Check if we need to draw anything. HasValidCoveredRect only returns
* true for path geometry and glyphs, so basically we're traversing
* all containers and we can only skip leaves here.
*/
if (aDirtyRect && svgChildFrame->HasValidCoveredRect()) {
if (filterFrame) {
if (!aDirtyRect->Intersects(filterFrame->GetFilterBBox(aFrame, nsnull)))
return;
} else {
nsRect leafBounds = nsSVGUtils::TransformFrameRectToOuterSVG(
aFrame->GetRect(), GetCanvasTM(aFrame), aFrame->PresContext());
nsRect rect = aDirtyRect->ToAppUnits(aFrame->PresContext()->AppUnitsPerDevPixel());
if (!rect.Intersects(leafBounds))
return;
if (aDirtyRect &&
!(aFrame->GetStateBits() & NS_STATE_SVG_NONDISPLAY_CHILD)) {
// Here we convert aFrame's paint bounds to outer-<svg> device space,
// compare it to aDirtyRect, and return early if they don't intersect.
// We don't do this optimization for nondisplay SVG since nondisplay
// SVG doesn't maintain bounds/overflow rects.
nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry)) {
// Unlike containers, leaf frames do not include GetPosition() in
// GetCanvasTM().
overflowRect = overflowRect + aFrame->GetPosition();
}
PRUint32 appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel();
gfxMatrix tm = GetCanvasTM(aFrame);
if (aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
gfxMatrix childrenOnlyTM;
if (static_cast<nsSVGContainerFrame*>(aFrame)->
HasChildrenOnlyTransform(&childrenOnlyTM)) {
// Undo the children-only transform:
tm = childrenOnlyTM.Invert() * tm;
}
}
nsIntRect bounds = nsSVGUtils::TransformFrameRectToOuterSVG(overflowRect,
tm, aFrame->PresContext()).
ToOutsidePixels(appUnitsPerDevPx);
if (!aDirtyRect->Intersects(bounds)) {
return;
}
}

View File

@ -45,6 +45,7 @@
#include "gfxPoint.h"
#include "gfxRect.h"
#include "nsAlgorithm.h"
#include "nsChangeHint.h"
#include "nsColor.h"
#include "nsCOMPtr.h"
#include "nsID.h"
@ -320,7 +321,15 @@ public:
* returns nsnull.
*/
static nsSVGDisplayContainerFrame* GetNearestSVGViewport(nsIFrame *aFrame);
/**
* Returns the frame's post-filter visual overflow rect when passed the
* frame's pre-filter visual overflow rect. If the frame is not currently
* being filtered, this function simply returns aUnfilteredRect.
*/
static nsRect GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
const nsRect &aUnfilteredRect);
/**
* Figures out the worst case invalidation area for a frame, taking
* filters into account.

View File

@ -68,3 +68,12 @@ foreignObject {
-moz-transform: translate(0) !important;
text-indent: 0;
}
/* Set |transform-origin:0% 0%;| for all SVG elements except outer-<svg>,
noting that 'svg' as a child of 'foreignObject' counts as outer-<svg>.
*/
*:not(svg),
*:not(foreignObject) > svg {
-moz-transform-origin:0 0;
}