Bug 474230. Cleanup scale and translate in nsSVGElement similar to the new style SVG classes. r=jwatt

This commit is contained in:
Craig Topper 2009-04-28 15:25:03 +02:00
parent 59514b8d37
commit 381741da91
5 changed files with 186 additions and 229 deletions

View File

@ -80,23 +80,21 @@ nsDOMSVGZoomEvent::nsDOMSVGZoomEvent(nsPresContext* aPresContext,
// properties will be left null which is probably what we want.
nsCOMPtr<nsIDOMSVGSVGElement> svgElement = do_QueryInterface(rootContent);
if (svgElement) {
svgElement->GetCurrentScale(&mNewScale);
float x, y;
nsCOMPtr<nsIDOMSVGPoint> currentTranslate;
svgElement->GetCurrentTranslate(getter_AddRefs(currentTranslate));
currentTranslate->GetX(&x);
currentTranslate->GetY(&y);
NS_NewSVGReadonlyPoint(getter_AddRefs(mNewTranslate), x, y);
nsSVGSVGElement *SVGSVGElement =
static_cast<nsSVGSVGElement*>(rootContent);
mNewScale = SVGSVGElement->GetCurrentScale();
mPreviousScale = SVGSVGElement->GetPreviousScale();
const nsSVGTranslatePoint& translate =
SVGSVGElement->GetCurrentTranslate();
NS_NewSVGReadonlyPoint(getter_AddRefs(mNewTranslate),
translate.GetX(), translate.GetY());
const nsSVGTranslatePoint& prevTranslate =
SVGSVGElement->GetPreviousTranslate();
NS_NewSVGReadonlyPoint(getter_AddRefs(mPreviousTranslate),
SVGSVGElement->GetPreviousTranslate_x(),
SVGSVGElement->GetPreviousTranslate_y());
// Important: we call RecordCurrentST() here to make sure that
// scripts that create an SVGZoomEvent won't get our "Previous" data
SVGSVGElement->RecordCurrentScaleTranslate();
prevTranslate.GetX(), prevTranslate.GetY());
}
}
}

View File

@ -71,6 +71,64 @@
nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
#endif // MOZ_SMIL
NS_SVG_VAL_IMPL_CYCLE_COLLECTION(nsSVGTranslatePoint::DOMVal, mElement)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGTranslatePoint::DOMVal)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGTranslatePoint::DOMVal)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGTranslatePoint::DOMVal)
NS_INTERFACE_MAP_ENTRY(nsIDOMSVGPoint)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(SVGPoint)
NS_INTERFACE_MAP_END
nsresult
nsSVGTranslatePoint::ToDOMVal(nsSVGSVGElement *aElement,
nsIDOMSVGPoint **aResult)
{
*aResult = new DOMVal(this, aElement);
if (!*aResult)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult);
return NS_OK;
}
NS_IMETHODIMP
nsSVGTranslatePoint::DOMVal::SetX(float aValue)
{
NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
return mElement->SetCurrentTranslate(aValue, mVal->GetY());
}
NS_IMETHODIMP
nsSVGTranslatePoint::DOMVal::SetY(float aValue)
{
NS_ENSURE_FINITE(aValue, NS_ERROR_ILLEGAL_VALUE);
return mElement->SetCurrentTranslate(mVal->GetX(), aValue);
}
/* nsIDOMSVGPoint matrixTransform (in nsIDOMSVGMatrix matrix); */
NS_IMETHODIMP
nsSVGTranslatePoint::DOMVal::MatrixTransform(nsIDOMSVGMatrix *matrix,
nsIDOMSVGPoint **_retval)
{
if (!matrix)
return NS_ERROR_DOM_SVG_WRONG_TYPE_ERR;
float a, b, c, d, e, f;
matrix->GetA(&a);
matrix->GetB(&b);
matrix->GetC(&c);
matrix->GetD(&d);
matrix->GetE(&e);
matrix->GetF(&f);
float x = mVal->GetX();
float y = mVal->GetY();
return NS_NewSVGPoint(_retval, a*x + c*y + e, b*x + d*y + f);
}
nsSVGElement::LengthInfo nsSVGSVGElement::sLengthInfo[4] =
{
@ -140,44 +198,17 @@ nsSVGSVGElement::nsSVGSVGElement(nsINodeInfo* aNodeInfo, PRBool aFromParser)
mViewportWidth(0),
mViewportHeight(0),
mCoordCtxMmPerPx(0),
mPreviousTranslate_x(0),
mPreviousTranslate_y(0),
mPreviousScale(0),
mRedrawSuspendCount(0),
mDispatchEvent(PR_FALSE)
mCurrentTranslate(0.0f, 0.0f),
mCurrentScale(1.0f),
mPreviousTranslate(0.0f, 0.0f),
mPreviousScale(1.0f),
mRedrawSuspendCount(0)
#ifdef MOZ_SMIL
,mStartAnimationOnBindToTree(!aFromParser)
#endif // MOZ_SMIL
{
}
nsresult
nsSVGSVGElement::Init()
{
nsresult rv = nsSVGSVGElementBase::Init();
NS_ENSURE_SUCCESS(rv,rv);
// DOM property: currentScale
{
rv = NS_NewSVGNumber(getter_AddRefs(mCurrentScale), 1.0f);
NS_ENSURE_SUCCESS(rv,rv);
NS_ADD_SVGVALUE_OBSERVER(mCurrentScale);
}
// DOM property: currentTranslate
{
rv = NS_NewSVGPoint(getter_AddRefs(mCurrentTranslate));
NS_ENSURE_SUCCESS(rv,rv);
NS_ADD_SVGVALUE_OBSERVER(mCurrentTranslate);
}
// initialise "Previous" values
RecordCurrentScaleTranslate();
mDispatchEvent = PR_TRUE;
return rv;
}
//----------------------------------------------------------------------
// nsIDOMNode methods
@ -343,7 +374,8 @@ nsSVGSVGElement::GetCurrentView(nsIDOMSVGViewSpec * *aCurrentView)
NS_IMETHODIMP
nsSVGSVGElement::GetCurrentScale(float *aCurrentScale)
{
return mCurrentScale->GetValue(aCurrentScale);
*aCurrentScale = mCurrentScale;
return NS_OK;
}
#define CURRENT_SCALE_MAX 16.0f
@ -352,27 +384,15 @@ nsSVGSVGElement::GetCurrentScale(float *aCurrentScale)
NS_IMETHODIMP
nsSVGSVGElement::SetCurrentScale(float aCurrentScale)
{
NS_ENSURE_FINITE(aCurrentScale, NS_ERROR_ILLEGAL_VALUE);
// Prevent bizarre behaviour and maxing out of CPU and memory by clamping
if (aCurrentScale < CURRENT_SCALE_MIN)
aCurrentScale = CURRENT_SCALE_MIN;
else if (aCurrentScale > CURRENT_SCALE_MAX)
aCurrentScale = CURRENT_SCALE_MAX;
return mCurrentScale->SetValue(aCurrentScale);
// We have to dispatch the required SVGZoom event from DidModifySVGObservable
// since dispatching it here is too late (i.e. after repaint)
return SetCurrentScaleTranslate(aCurrentScale,
mCurrentTranslate.GetX(), mCurrentTranslate.GetY());
}
/* readonly attribute nsIDOMSVGPoint currentTranslate; */
NS_IMETHODIMP
nsSVGSVGElement::GetCurrentTranslate(nsIDOMSVGPoint * *aCurrentTranslate)
{
*aCurrentTranslate = mCurrentTranslate;
NS_ADDREF(*aCurrentTranslate);
return NS_OK;
return mCurrentTranslate.ToDOMVal(this, aCurrentTranslate);
}
/* unsigned long suspendRedraw (in unsigned long max_wait_milliseconds); */
@ -811,9 +831,9 @@ nsSVGSVGElement::GetCTM(nsIDOMSVGMatrix **_retval)
float s=1, x=0, y=0;
if (IsRoot()) {
// we're the root element. get our currentScale and currentTranslate vals
mCurrentScale->GetValue(&s);
mCurrentTranslate->GetX(&x);
mCurrentTranslate->GetY(&y);
s = mCurrentScale;
x = mCurrentTranslate.GetX();
y = mCurrentTranslate.GetY();
}
else {
// we're inline in some non-SVG content. get our offset from the root
@ -908,9 +928,9 @@ nsSVGSVGElement::GetScreenCTM(nsIDOMSVGMatrix **_retval)
float s=1, x=0, y=0;
if (IsRoot()) {
// we're the root element. get our currentScale and currentTranslate vals
mCurrentScale->GetValue(&s);
mCurrentTranslate->GetX(&x);
mCurrentTranslate->GetY(&y);
s = mCurrentScale;
x = mCurrentTranslate.GetX();
y = mCurrentTranslate.GetY();
}
else {
// we're inline in some non-SVG content. get our offset from the root
@ -1011,66 +1031,22 @@ nsSVGSVGElement::SetZoomAndPan(PRUint16 aZoomAndPan)
//----------------------------------------------------------------------
// helper methods for implementing SVGZoomEvent:
nsresult
nsSVGSVGElement::GetCurrentScaleNumber(nsIDOMSVGNumber **aResult)
{
*aResult = mCurrentScale;
NS_ADDREF(*aResult);
return NS_OK;
}
NS_IMETHODIMP
nsSVGSVGElement::SetCurrentScaleTranslate(float s, float x, float y)
{
RecordCurrentScaleTranslate();
mDispatchEvent = PR_FALSE;
SetCurrentScale(s); // clamps! don't call mCurrentScale->SetValue() directly
mCurrentTranslate->SetX(x);
mCurrentTranslate->SetY(y);
mDispatchEvent = PR_TRUE;
NS_ENSURE_FINITE3(s, x, y, NS_ERROR_ILLEGAL_VALUE);
// now dispatch an SVGZoom event if we are the root element
nsIDocument* doc = GetCurrentDoc();
if (doc) {
nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
NS_ASSERTION(presShell, "no presShell");
if (presShell && IsRoot()) {
nsEventStatus status = nsEventStatus_eIgnore;
nsGUIEvent event(PR_TRUE, NS_SVG_ZOOM, 0);
event.eventStructType = NS_SVGZOOM_EVENT;
presShell->HandleDOMEventWithTarget(this, &event, &status);
}
if (s == mCurrentScale &&
x == mCurrentTranslate.GetX() && y == mCurrentTranslate.GetY()) {
return NS_OK;
}
return NS_OK;
}
NS_IMETHODIMP
nsSVGSVGElement::SetCurrentTranslate(float x, float y)
{
RecordCurrentScaleTranslate();
mDispatchEvent = PR_FALSE;
mCurrentTranslate->SetX(x);
mCurrentTranslate->SetY(y);
mDispatchEvent = PR_TRUE;
// now dispatch an SVGScroll event if we are the root element
nsIDocument* doc = GetCurrentDoc();
if (doc) {
nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
NS_ASSERTION(presShell, "no presShell");
if (presShell && IsRoot()) {
nsEventStatus status = nsEventStatus_eIgnore;
nsEvent event(PR_TRUE, NS_SVG_SCROLL);
event.eventStructType = NS_SVG_EVENT;
presShell->HandleDOMEventWithTarget(this, &event, &status);
}
}
return NS_OK;
}
void
nsSVGSVGElement::RecordCurrentScaleTranslate()
{
// Prevent bizarre behaviour and maxing out of CPU and memory by clamping
if (s < CURRENT_SCALE_MIN)
s = CURRENT_SCALE_MIN;
else if (s > CURRENT_SCALE_MAX)
s = CURRENT_SCALE_MAX;
// IMPORTANT: If either mCurrentTranslate *or* mCurrentScale is changed then
// mPreviousTranslate_x, mPreviousTranslate_y *and* mPreviousScale must all
// be updated otherwise SVGZoomEvents will end up with invalid data. I.e. an
@ -1079,9 +1055,33 @@ nsSVGSVGElement::RecordCurrentScaleTranslate()
// change that caused the event's dispatch, which is *not* necessarily the
// same thing as the values of currentScale and currentTranslate prior to
// their own last change.
mCurrentScale->GetValue(&mPreviousScale);
mCurrentTranslate->GetX(&mPreviousTranslate_x);
mCurrentTranslate->GetY(&mPreviousTranslate_y);
mPreviousScale = mCurrentScale;
mPreviousTranslate = mCurrentTranslate;
mCurrentScale = s;
mCurrentTranslate = nsSVGTranslatePoint(x, y);
// now dispatch the appropriate event if we are the root element
nsIDocument* doc = GetCurrentDoc();
if (doc) {
nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
NS_ASSERTION(presShell, "no presShell");
if (presShell && IsRoot()) {
PRBool scaling = (s != mCurrentScale);
nsEventStatus status = nsEventStatus_eIgnore;
nsGUIEvent event(PR_TRUE, scaling ? NS_SVG_ZOOM : NS_SVG_SCROLL, 0);
event.eventStructType = scaling ? NS_SVGZOOM_EVENT : NS_SVG_EVENT;
presShell->HandleDOMEventWithTarget(this, &event, &status);
InvalidateTransformNotifyFrame();
}
}
return NS_OK;
}
NS_IMETHODIMP
nsSVGSVGElement::SetCurrentTranslate(float x, float y)
{
return SetCurrentScaleTranslate(mCurrentScale, x, y);
}
#ifdef MOZ_SMIL
@ -1149,75 +1149,6 @@ nsSVGSVGElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor)
}
#endif // MOZ_SMIL
//----------------------------------------------------------------------
// nsISVGValueObserver methods:
NS_IMETHODIMP
nsSVGSVGElement::WillModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType)
{
if (mDispatchEvent) {
// Modification isn't due to calling SetCurrent[Scale]Translate, so if
// currentScale or currentTranslate is about to change we must record their
// current values.
nsCOMPtr<nsIDOMSVGNumber> n = do_QueryInterface(observable);
if (n && n==mCurrentScale) {
RecordCurrentScaleTranslate();
}
else {
nsCOMPtr<nsIDOMSVGPoint> p = do_QueryInterface(observable);
if (p && p==mCurrentTranslate) {
RecordCurrentScaleTranslate();
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsSVGSVGElement::DidModifySVGObservable (nsISVGValue* observable,
nsISVGValue::modificationType aModType)
{
nsIDocument* doc = GetCurrentDoc();
if (!doc) return NS_ERROR_FAILURE;
nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
NS_ASSERTION(presShell, "no presShell");
if (!presShell) return NS_ERROR_FAILURE;
// If currentScale or currentTranslate has changed, we are the root element,
// and the changes wasn't caused by SetCurrent[Scale]Translate then we must
// dispatch an SVGZoom or SVGScroll DOM event before repainting
nsCOMPtr<nsIDOMSVGNumber> n = do_QueryInterface(observable);
if (n && n==mCurrentScale) {
if (mDispatchEvent && IsRoot()) {
nsEventStatus status = nsEventStatus_eIgnore;
nsGUIEvent event(PR_TRUE, NS_SVG_ZOOM, 0);
event.eventStructType = NS_SVGZOOM_EVENT;
presShell->HandleDOMEventWithTarget(this, &event, &status);
}
else {
return NS_OK; // we don't care about currentScale changes on non-root
}
} else {
nsCOMPtr<nsIDOMSVGPoint> p = do_QueryInterface(observable);
if (p && p==mCurrentTranslate) {
if (mDispatchEvent && IsRoot()) {
nsEventStatus status = nsEventStatus_eIgnore;
nsEvent event(PR_TRUE, NS_SVG_SCROLL);
event.eventStructType = NS_SVG_EVENT;
presShell->HandleDOMEventWithTarget(this, &event, &status);
}
else {
return NS_OK; // we don't care about currentScale changes on non-root
}
}
}
InvalidateTransformNotifyFrame();
return NS_OK;
}
//----------------------------------------------------------------------
// nsSVGElement overrides

View File

@ -46,6 +46,7 @@
#include "nsIDOMSVGLocatable.h"
#include "nsIDOMSVGZoomAndPan.h"
#include "nsIDOMSVGMatrix.h"
#include "nsIDOMSVGPoint.h"
#include "nsSVGLength2.h"
#include "nsSVGEnum.h"
#include "nsSVGViewBox.h"
@ -61,6 +62,52 @@ class nsSMILTimeContainer;
typedef nsSVGStylableElement nsSVGSVGElementBase;
class nsSVGSVGElement;
class nsSVGTranslatePoint {
public:
nsSVGTranslatePoint(float aX, float aY) :
mX(aX), mY(aY) {}
void SetX(float aX)
{ mX = aX; }
void SetY(float aY)
{ mY = aY; }
float GetX() const
{ return mX; }
float GetY() const
{ return mY; }
nsresult ToDOMVal(nsSVGSVGElement *aElement, nsIDOMSVGPoint **aResult);
private:
struct DOMVal : public nsIDOMSVGPoint {
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(DOMVal)
DOMVal(nsSVGTranslatePoint* aVal, nsSVGSVGElement *aElement)
: mVal(aVal), mElement(aElement) {}
NS_IMETHOD GetX(float *aValue)
{ *aValue = mVal->GetX(); return NS_OK; }
NS_IMETHOD GetY(float *aValue)
{ *aValue = mVal->GetY(); return NS_OK; }
NS_IMETHOD SetX(float aValue);
NS_IMETHOD SetY(float aValue);
NS_IMETHOD MatrixTransform(nsIDOMSVGMatrix *matrix,
nsIDOMSVGPoint **_retval);
nsSVGTranslatePoint *mVal; // kept alive because it belongs to mElement
nsRefPtr<nsSVGSVGElement> mElement;
};
float mX;
float mY;
};
class svgFloatSize {
public:
svgFloatSize(float aWidth, float aHeight)
@ -88,7 +135,6 @@ protected:
nsINodeInfo *aNodeInfo,
PRBool aFromParser);
nsSVGSVGElement(nsINodeInfo* aNodeInfo, PRBool aFromParser);
nsresult Init();
public:
@ -104,9 +150,6 @@ public:
NS_FORWARD_NSIDOMELEMENT(nsSVGSVGElementBase::)
NS_FORWARD_NSIDOMSVGELEMENT(nsSVGSVGElementBase::)
// helper methods for implementing SVGZoomEvent:
nsresult GetCurrentScaleNumber(nsIDOMSVGNumber **aResult);
/**
* For use by zoom controls to allow currentScale, currentTranslate.x and
* currentTranslate.y to be set by a single operation that dispatches a
@ -122,17 +165,16 @@ public:
NS_IMETHOD SetCurrentTranslate(float x, float y);
/**
* Record the current values of currentScale, currentTranslate.x and
* currentTranslate.y prior to changing the value of one of them.
* Retrieve the value of currentScale and currentTranslate.
*/
void RecordCurrentScaleTranslate();
const nsSVGTranslatePoint& GetCurrentTranslate() { return mCurrentTranslate; }
float GetCurrentScale() { return mCurrentScale; }
/**
* Retrieve the value of currentScale, currentTranslate.x or
* currentTranslate.y prior to the last change made to any one of them.
*/
float GetPreviousTranslate_x() { return mPreviousTranslate_x; }
float GetPreviousTranslate_y() { return mPreviousTranslate_y; }
const nsSVGTranslatePoint& GetPreviousTranslate() { return mPreviousTranslate; }
float GetPreviousScale() { return mPreviousScale; }
#ifdef MOZ_SMIL
@ -145,12 +187,6 @@ public:
virtual nsresult PreHandleEvent(nsEventChainPreVisitor& aVisitor);
#endif // MOZ_SMIL
// nsISVGValueObserver
NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
nsISVGValue::modificationType aModType);
NS_IMETHOD DidModifySVGObservable (nsISVGValue* observable,
nsISVGValue::modificationType aModType);
// nsSVGElement specializations:
virtual void DidChangeLength(PRUint8 aAttrEnum, PRBool aDoSetAttr);
virtual void DidChangeEnum(PRUint8 aAttrEnum, PRBool aDoSetAttr);
@ -253,15 +289,13 @@ protected:
#endif // MOZ_SMIL
// zoom and pan
// IMPORTANT: only RecordCurrentScaleTranslate should change the "mPreviousX"
// members below - see the comment in RecordCurrentScaleTranslate
nsCOMPtr<nsIDOMSVGPoint> mCurrentTranslate;
nsCOMPtr<nsIDOMSVGNumber> mCurrentScale;
float mPreviousTranslate_x;
float mPreviousTranslate_y;
// IMPORTANT: see the comment in RecordCurrentScaleTranslate before writing
// code to change any of these!
nsSVGTranslatePoint mCurrentTranslate;
float mCurrentScale;
nsSVGTranslatePoint mPreviousTranslate;
float mPreviousScale;
PRInt32 mRedrawSuspendCount;
PRPackedBool mDispatchEvent;
#ifdef MOZ_SMIL
// For outermost <svg> elements created from parsing, animation is started by

View File

@ -149,6 +149,7 @@ nsSVGOuterSVGFrame::nsSVGOuterSVGFrame(nsStyleContext* aContext)
#ifdef XP_MACOSX
, mEnableBitmapFallback(PR_FALSE)
#endif
, mIsRootContent(PR_FALSE)
{
}
@ -170,9 +171,7 @@ nsSVGOuterSVGFrame::Init(nsIContent* aContent,
if (doc) {
// we only care about our content's zoom and pan values if it's the root element
if (doc->GetRootContent() == mContent) {
nsSVGSVGElement *SVGElement = static_cast<nsSVGSVGElement*>(mContent);
SVGElement->GetCurrentTranslate(getter_AddRefs(mCurrentTranslate));
SVGElement->GetCurrentScaleNumber(getter_AddRefs(mCurrentScale));
mIsRootContent = PR_TRUE;
}
// AddMutationObserver checks that the observer is not already added.
// sSVGMutationObserver has the same lifetime as the document so does
@ -774,16 +773,14 @@ nsSVGOuterSVGFrame::GetCanvasTM()
// our content is the document element so we must premultiply the values
// of its currentScale and currentTranslate properties
if (mCurrentScale &&
mCurrentTranslate) {
if (mIsRootContent) {
nsCOMPtr<nsIDOMSVGMatrix> zoomPanMatrix;
nsCOMPtr<nsIDOMSVGMatrix> temp;
float scale, x, y;
mCurrentScale->GetValue(&scale);
mCurrentTranslate->GetX(&x);
mCurrentTranslate->GetY(&y);
float scale = svgElement->GetCurrentScale();
const nsSVGTranslatePoint& translate = svgElement->GetCurrentTranslate();
svgElement->CreateSVGMatrix(getter_AddRefs(zoomPanMatrix));
zoomPanMatrix->Translate(x, y, getter_AddRefs(temp));
zoomPanMatrix->Translate(translate.GetX(), translate.GetY(), getter_AddRefs(temp));
temp->Scale(scale, getter_AddRefs(zoomPanMatrix));
zoomPanMatrix->Multiply(mCanvasTM, getter_AddRefs(temp));
temp.swap(mCanvasTM);

View File

@ -166,16 +166,13 @@ protected:
PRUint32 mRedrawSuspendCount;
nsCOMPtr<nsIDOMSVGMatrix> mCanvasTM;
// zoom and pan
nsCOMPtr<nsIDOMSVGPoint> mCurrentTranslate;
nsCOMPtr<nsIDOMSVGNumber> mCurrentScale;
float mFullZoom;
PRPackedBool mViewportInitialized;
#ifdef XP_MACOSX
PRPackedBool mEnableBitmapFallback;
#endif
PRPackedBool mIsRootContent;
};
#endif