mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 734082 - Compute and store bounds and visual overflow bounds for both SVG leaf and container frames. r=roc.
This commit is contained in:
parent
acf3e38285
commit
f418b57d69
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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()));
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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__
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user