Bug 366697 - getCTM() shouldn't return the same matrix as .getScreenCTM() for nested SVG elements. r=longsonr

This commit is contained in:
Ryo Onodera 2009-06-26 21:13:07 +01:00
parent 123b9246d1
commit 95bc019eac
13 changed files with 331 additions and 240 deletions

View File

@ -932,7 +932,8 @@ nsSVGElement::GetOwnerSVGElement(nsIDOMSVGSVGElement * *aOwnerSVGElement)
NS_IMETHODIMP
nsSVGElement::GetViewportElement(nsIDOMSVGElement * *aViewportElement)
{
return nsSVGUtils::GetNearestViewportElement(this, aViewportElement);
nsSVGUtils::GetNearestViewportElement(this, aViewportElement);
return NS_OK; // we can't throw exceptions from this API.
}
//----------------------------------------------------------------------

View File

@ -38,6 +38,7 @@
#include "nsCOMPtr.h"
#include "nsSVGForeignObjectElement.h"
#include "nsSVGMatrix.h"
nsSVGElement::LengthInfo nsSVGForeignObjectElement::sLengthInfo[4] =
{
@ -116,6 +117,25 @@ nsSVGForeignObjectElement::PrependLocalTransformTo(const gfxMatrix &aMatrix)
return gfxMatrix().Translate(gfxPoint(x, y)) * matrix;
}
nsresult
nsSVGForeignObjectElement::AppendTransform(nsIDOMSVGMatrix *aCTM,
nsIDOMSVGMatrix **_retval)
{
nsresult rv;
// foreignObject is one of establishing-viewport elements.
// so we are translated by foreignObject's x and y attribs.
float x, y;
GetAnimatedLengthValues(&x, &y, nsnull);
nsCOMPtr<nsIDOMSVGMatrix> translate;
rv = NS_NewSVGMatrix(getter_AddRefs(translate), 1, 0, 0, 1, x, y);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDOMSVGMatrix> tmp;
rv = aCTM->Multiply(translate, getter_AddRefs(tmp));
if (NS_FAILED(rv)) return rv;
return nsSVGGraphicElement::AppendTransform(tmp, _retval);
}
//----------------------------------------------------------------------
// nsIContent methods

View File

@ -74,6 +74,9 @@ public:
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
// public helpers
nsresult AppendTransform(nsIDOMSVGMatrix *aCTM,
nsIDOMSVGMatrix **_retval);
protected:
virtual LengthAttributesInfo GetLengthInfo();

View File

@ -75,7 +75,8 @@ nsSVGGraphicElement::nsSVGGraphicElement(nsINodeInfo *aNodeInfo)
/* readonly attribute nsIDOMSVGElement nearestViewportElement; */
NS_IMETHODIMP nsSVGGraphicElement::GetNearestViewportElement(nsIDOMSVGElement * *aNearestViewportElement)
{
return nsSVGUtils::GetNearestViewportElement(this, aNearestViewportElement);
nsSVGUtils::GetNearestViewportElement(this, aNearestViewportElement);
return NS_OK; // we can't throw exceptions from this API.
}
/* readonly attribute nsIDOMSVGElement farthestViewportElement; */
@ -103,8 +104,8 @@ NS_IMETHODIMP nsSVGGraphicElement::GetBBox(nsIDOMSVGRect **_retval)
/* Helper for GetCTM and GetScreenCTM */
nsresult
nsSVGGraphicElement::AppendLocalTransform(nsIDOMSVGMatrix *aCTM,
nsIDOMSVGMatrix **_retval)
nsSVGGraphicElement::AppendTransform(nsIDOMSVGMatrix *aCTM,
nsIDOMSVGMatrix **_retval)
{
if (!mTransforms) {
*_retval = aCTM;
@ -127,61 +128,15 @@ nsSVGGraphicElement::AppendLocalTransform(nsIDOMSVGMatrix *aCTM,
}
/* nsIDOMSVGMatrix getCTM (); */
NS_IMETHODIMP nsSVGGraphicElement::GetCTM(nsIDOMSVGMatrix **_retval)
NS_IMETHODIMP nsSVGGraphicElement::GetCTM(nsIDOMSVGMatrix * *aCTM)
{
nsresult rv;
*_retval = nsnull;
nsIDocument* currentDoc = GetCurrentDoc();
if (currentDoc) {
// Flush all pending notifications so that our frames are uptodate
currentDoc->FlushPendingNotifications(Flush_Layout);
}
nsIContent* parent = nsSVGUtils::GetParentElement(this);
nsCOMPtr<nsIDOMSVGLocatable> locatableElement = do_QueryInterface(parent);
if (!locatableElement) {
// we don't have an SVGLocatable parent so we aren't even rendered
NS_WARNING("SVGGraphicElement without an SVGLocatable parent");
return NS_ERROR_FAILURE;
}
// get our parent's CTM
nsCOMPtr<nsIDOMSVGMatrix> parentCTM;
rv = locatableElement->GetCTM(getter_AddRefs(parentCTM));
if (NS_FAILED(rv)) return rv;
return AppendLocalTransform(parentCTM, _retval);
return nsSVGUtils::GetCTM(this, aCTM);
}
/* nsIDOMSVGMatrix getScreenCTM (); */
NS_IMETHODIMP nsSVGGraphicElement::GetScreenCTM(nsIDOMSVGMatrix **_retval)
NS_IMETHODIMP nsSVGGraphicElement::GetScreenCTM(nsIDOMSVGMatrix * *aCTM)
{
nsresult rv;
*_retval = nsnull;
nsIDocument* currentDoc = GetCurrentDoc();
if (currentDoc) {
// Flush all pending notifications so that our frames are uptodate
currentDoc->FlushPendingNotifications(Flush_Layout);
}
nsIContent* parent = nsSVGUtils::GetParentElement(this);
nsCOMPtr<nsIDOMSVGLocatable> locatableElement = do_QueryInterface(parent);
if (!locatableElement) {
// we don't have an SVGLocatable parent so we aren't even rendered
NS_WARNING("SVGGraphicElement without an SVGLocatable parent");
return NS_ERROR_FAILURE;
}
// get our parent's "screen" CTM
nsCOMPtr<nsIDOMSVGMatrix> parentScreenCTM;
rv = locatableElement->GetScreenCTM(getter_AddRefs(parentScreenCTM));
if (NS_FAILED(rv)) return rv;
return AppendLocalTransform(parentScreenCTM, _retval);
return nsSVGUtils::GetScreenCTM(this, aCTM);
}
/* nsIDOMSVGMatrix getTransformToElement (in nsIDOMSVGElement element); */

View File

@ -64,6 +64,9 @@ public:
virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix);
// public helpers
virtual nsresult AppendTransform(nsIDOMSVGMatrix *aCTM,
nsIDOMSVGMatrix **_retval);
protected:
// nsSVGElement overrides
virtual PRBool IsEventName(nsIAtom* aName);
@ -75,8 +78,6 @@ protected:
// helper
nsresult CreateTransformList();
nsresult AppendLocalTransform(nsIDOMSVGMatrix *aCTM,
nsIDOMSVGMatrix **_retval);
};
#endif // __NS_SVGGRAPHICELEMENT_H__

View File

@ -746,7 +746,8 @@ nsSVGSVGElement::GetPreserveAspectRatio(nsIDOMSVGAnimatedPreserveAspectRatio
NS_IMETHODIMP
nsSVGSVGElement::GetNearestViewportElement(nsIDOMSVGElement * *aNearestViewportElement)
{
return nsSVGUtils::GetNearestViewportElement(this, aNearestViewportElement);
nsSVGUtils::GetNearestViewportElement(this, aNearestViewportElement);
return NS_OK; // we can't throw exceptions from this API.
}
/* readonly attribute nsIDOMSVGElement farthestViewportElement; */
@ -774,205 +775,68 @@ nsSVGSVGElement::GetBBox(nsIDOMSVGRect **_retval)
return NS_ERROR_NOT_IMPLEMENTED; // XXX: outer svg
}
/* nsIDOMSVGMatrix getCTM (); */
NS_IMETHODIMP
nsSVGSVGElement::GetCTM(nsIDOMSVGMatrix **_retval)
nsresult
nsSVGSVGElement::AppendTransform(nsIDOMSVGMatrix *aCTM,
nsIDOMSVGMatrix **_retval)
{
nsresult rv;
*_retval = nsnull;
nsIDocument* currentDoc = GetCurrentDoc();
if (currentDoc) {
// Flush all pending notifications so that our frames are uptodate
currentDoc->FlushPendingNotifications(Flush_Layout);
}
// first check what are our parents and calculate offsets accordingly.
// first try to get the "screen" CTM of our nearest SVG ancestor
nsCOMPtr<nsIContent> element = this;
nsCOMPtr<nsIContent> ancestor;
unsigned short ancestorCount = 0;
nsCOMPtr<nsIDOMSVGMatrix> ancestorCTM;
while (1) {
ancestor = nsSVGUtils::GetParentElement(element);
if (!ancestor) {
// reached the top of our parent chain without finding an SVG ancestor
break;
}
nsSVGSVGElement *viewportElement = QI_AND_CAST_TO_NSSVGSVGELEMENT(ancestor);
if (viewportElement) {
rv = viewportElement->GetViewboxToViewportTransform(getter_AddRefs(ancestorCTM));
if (NS_FAILED(rv)) return rv;
break;
}
nsCOMPtr<nsIDOMSVGLocatable> locatableElement = do_QueryInterface(ancestor);
if (locatableElement) {
rv = locatableElement->GetCTM(getter_AddRefs(ancestorCTM));
if (NS_FAILED(rv)) return rv;
break;
}
// ancestor was not SVG content. loop until we find an SVG ancestor
element = ancestor;
ancestorCount++;
}
// now account for our offset
if (!ancestorCTM) {
// we didn't find an SVG ancestor
float s=1, x=0, y=0;
if (IsRoot()) {
// we're the root element. get our currentScale and currentTranslate vals
s = mCurrentScale;
x = mCurrentTranslate.GetX();
y = mCurrentTranslate.GetY();
}
else {
// we're inline in some non-SVG content. get our offset from the root
GetOffsetToAncestor(nsnull, x, y);
}
rv = NS_NewSVGMatrix(getter_AddRefs(ancestorCTM), s, 0, 0, s, x, y);
float s=1, x=0, y=0;
nsIContent *ancestor = nsSVGUtils::GetParentElement(this);
if (ancestor && ancestor->GetNameSpaceID() == kNameSpaceID_SVG &&
ancestor->Tag() == nsGkAtoms::foreignObject) {
// this is a nested <svg> element. immediate parent is an <foreignObject> element.
// we ignore this <svg> element's x and y attribs in layout so do the same.
} else {
nsCOMPtr<nsIDOMSVGElement> nearestViewportElement;
rv = nsSVGUtils::GetNearestViewportElement(this, getter_AddRefs(nearestViewportElement));
if (NS_FAILED(rv)) return rv;
}
else {
// we found an SVG ancestor
float x=0, y=0;
nsCOMPtr<nsIDOMSVGMatrix> tmp;
if (ancestorCount == 0) {
// our immediate parent is an SVG element. get our 'x' and 'y' attribs.
// cast to nsSVGElement so we get our ancestor coord context.
x = mLengthAttributes[X].GetAnimValue(static_cast<nsSVGElement*>
(this));
y = mLengthAttributes[Y].GetAnimValue(static_cast<nsSVGElement*>
(this));
}
else {
// We have an SVG ancestor, but with non-SVG content between us
#if 0
nsCOMPtr<nsIDOMSVGForeignObjectElement> foreignObject
= do_QueryInterface(ancestor);
if (!foreignObject) {
NS_ERROR("the none-SVG content in the parent chain between us and our "
"SVG ancestor isn't rooted in a foreignObject element");
return NS_ERROR_FAILURE;
if (!nearestViewportElement) {
if (IsRoot()) {
// we're the root element. get our currentScale and currentTranslate vals
s = mCurrentScale;
x = mCurrentTranslate.GetX();
y = mCurrentTranslate.GetY();
} else {
// we're inline in some non-SVG content. get our offset from the root
GetOffsetToAncestor(nsnull, x, y);
}
#endif
// XXXjwatt: this isn't quite right since foreignObject can transform its
// content, but it's close enough until we turn foreignObject back on
GetOffsetToAncestor(ancestor, x, y);
} else {
// this is a nested <svg> element.
GetAnimatedLengthValues(&x, &y, nsnull);
}
rv = ancestorCTM->Translate(x, y, getter_AddRefs(tmp));
if (NS_FAILED(rv)) return rv;
ancestorCTM.swap(tmp);
}
nsCOMPtr<nsIDOMSVGMatrix> local;
rv = NS_NewSVGMatrix(getter_AddRefs(local), s, 0, 0, s, x, y);
if (NS_FAILED(rv)) return rv;
// finally append our viewbox transform
nsCOMPtr<nsIDOMSVGMatrix> tmp;
rv = GetViewboxToViewportTransform(getter_AddRefs(tmp));
nsCOMPtr<nsIDOMSVGMatrix> viewbox;
rv = GetViewboxToViewportTransform(getter_AddRefs(viewbox));
if (NS_FAILED(rv)) return rv;
return ancestorCTM->Multiply(tmp, _retval); // addrefs, so we don't
nsCOMPtr<nsIDOMSVGMatrix> tmp;
rv = local->Multiply(viewbox, getter_AddRefs(tmp)); // addrefs, so we don't
if (NS_FAILED(rv)) return rv;
return aCTM->Multiply(tmp, _retval); // addrefs, so we don't
}
/* nsIDOMSVGMatrix getCTM (); */
NS_IMETHODIMP
nsSVGSVGElement::GetCTM(nsIDOMSVGMatrix * *aCTM)
{
return nsSVGUtils::GetCTM(this, aCTM);
}
/* nsIDOMSVGMatrix getScreenCTM (); */
NS_IMETHODIMP
nsSVGSVGElement::GetScreenCTM(nsIDOMSVGMatrix **_retval)
nsSVGSVGElement::GetScreenCTM(nsIDOMSVGMatrix **aCTM)
{
nsresult rv;
*_retval = nsnull;
nsIDocument* currentDoc = GetCurrentDoc();
if (currentDoc) {
// Flush all pending notifications so that our frames are uptodate
currentDoc->FlushPendingNotifications(Flush_Layout);
}
// first try to get the "screen" CTM of our nearest SVG ancestor
nsCOMPtr<nsIContent> element = this;
nsCOMPtr<nsIContent> ancestor;
unsigned short ancestorCount = 0;
nsCOMPtr<nsIDOMSVGMatrix> ancestorScreenCTM;
while (1) {
ancestor = nsSVGUtils::GetParentElement(element);
if (!ancestor) {
// reached the top of our parent chain without finding an SVG ancestor
break;
}
nsCOMPtr<nsIDOMSVGLocatable> locatableElement = do_QueryInterface(ancestor);
if (locatableElement) {
rv = locatableElement->GetScreenCTM(getter_AddRefs(ancestorScreenCTM));
if (NS_FAILED(rv)) return rv;
break;
}
// ancestor was not SVG content. loop until we find an SVG ancestor
element = ancestor;
ancestorCount++;
}
// now account for our offset
if (!ancestorScreenCTM) {
// we didn't find an SVG ancestor
float s=1, x=0, y=0;
if (IsRoot()) {
// we're the root element. get our currentScale and currentTranslate vals
s = mCurrentScale;
x = mCurrentTranslate.GetX();
y = mCurrentTranslate.GetY();
}
else {
// we're inline in some non-SVG content. get our offset from the root
GetOffsetToAncestor(nsnull, x, y);
}
rv = NS_NewSVGMatrix(getter_AddRefs(ancestorScreenCTM), s, 0, 0, s, x, y);
if (NS_FAILED(rv)) return rv;
}
else {
// we found an SVG ancestor
float x=0, y=0;
nsCOMPtr<nsIDOMSVGMatrix> tmp;
if (ancestorCount == 0) {
// our immediate parent is an SVG element. get our 'x' and 'y' attribs
// cast to nsSVGElement so we get our ancestor coord context.
x = mLengthAttributes[X].GetAnimValue(static_cast<nsSVGElement*>
(this));
y = mLengthAttributes[Y].GetAnimValue(static_cast<nsSVGElement*>
(this));
}
else {
// We have an SVG ancestor, but with non-SVG content between us
#if 0
nsCOMPtr<nsIDOMSVGForeignObjectElement> foreignObject
= do_QueryInterface(ancestor);
if (!foreignObject) {
NS_ERROR("the none-SVG content in the parent chain between us and our "
"SVG ancestor isn't rooted in a foreignObject element");
return NS_ERROR_FAILURE;
}
#endif
// XXXjwatt: this isn't quite right since foreignObject can transform its
// content, but it's close enough until we turn foreignObject back on
GetOffsetToAncestor(ancestor, x, y);
}
rv = ancestorScreenCTM->Translate(x, y, getter_AddRefs(tmp));
if (NS_FAILED(rv)) return rv;
ancestorScreenCTM.swap(tmp);
}
// finally append our viewbox transform
nsCOMPtr<nsIDOMSVGMatrix> tmp;
rv = GetViewboxToViewportTransform(getter_AddRefs(tmp));
if (NS_FAILED(rv)) return rv;
return ancestorScreenCTM->Multiply(tmp, _retval); // addrefs, so we don't
return nsSVGUtils::GetScreenCTM(this, aCTM);
}
/* nsIDOMSVGMatrix getTransformToElement (in nsIDOMSVGElement element); */

View File

@ -200,6 +200,8 @@ public:
// public helpers:
nsresult GetViewboxToViewportTransform(nsIDOMSVGMatrix **_retval);
nsresult AppendTransform(nsIDOMSVGMatrix *aCTM,
nsIDOMSVGMatrix **_retval);
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;

View File

@ -50,6 +50,8 @@ _TEST_FILES = \
bounds-helper.svg \
test_dataTypes.html \
dataTypes-helper.svg \
getCTM-helper.svg \
test_getCTM.html \
test_getSubStringLength.xhtml \
getSubStringLength-helper.svg \
test_pathSeg.xhtml \

View File

@ -0,0 +1,35 @@
<?xml version="1.0"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100" height="100" viewBox="-11 -22 100 100">
<g transform="translate(3, 4)">
<svg x="1" y="2" width="888" height="999">
<g>
<svg x="30" y="40" width="100" height="100">
<g id="buggy"/>
</svg>
<defs>
<symbol id="sym" width="100" height="100">
<rect id="symbolRect" width="0" height="0"
transform="translate(70, 80)"/>
</symbol>
</defs>
<svg id="inner" x="30" y="40" width="100" height="100">
<g id="g1"/>
</svg>
<foreignObject x="30" y="40" width="100" height="100" transform="translate(1, 1)">
<!-- current layout implementation ignores x="50" and y="60".
thus, I made getCTM and getScreenCTM do the same. -->
<svg id="outer" x="50" y="60" width="100" height="100">
<g id="g2" transform="translate(600, 700)"/>
</svg>
</foreignObject>
<!-- something invalid -->
<foreignObject>
<g id="g3"/>
</foreignObject>
<use xlink:href="#sym" id="use"/>
</g>
</svg>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,91 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=366697
-->
<head>
<title>Test for Bug 366697</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=366697">Mozilla Bug 366697</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<iframe id="svg" src="getCTM-helper.svg"></iframe>
<pre id="test">
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
function runTest()
{
var doc = $("svg").contentWindow.document;
/* Minimal */
var buggy = doc.getElementById("buggy");
is(buggy.getCTM().e, 30, "buggy.getCTM().e");
is(buggy.getCTM().f, 40, "buggy.getCTM().f");
var root = doc.documentElement;
var inner = doc.getElementById("inner");
var g1 = doc.getElementById("g1");
var outer = doc.getElementById("outer");
var g2 = doc.getElementById("g2");
var g3 = doc.getElementById("g3");
var sym = doc.getElementById("sym");
var symbolRect = doc.getElementById("symbolRect");
/* Tests the consistency with nearestViewportElement
(code is from test_viewport.html) */
// root.nearestViewportElement == null
is((function(){try{return root.getCTM()}catch(e){return e}})(), null, "root.getCTM()");
// inner.nearestViewportElement == root
is((function(){try{return inner.getCTM().e}catch(e){return e}})(), 31, "inner.getCTM().e");
is((function(){try{return inner.getCTM().f}catch(e){return e}})(), 42, "inner.getCTM().f");
// g1.nearestViewportElement == inner
is((function(){try{return g1.getCTM().e}catch(e){return e}})(), 30, "g1.getCTM().e");
is((function(){try{return g1.getCTM().f}catch(e){return e}})(), 40, "g1.getCTM().f");
// outer.nearestViewportElement == null
is((function(){try{return outer.getCTM()}catch(e){return e}})(), null, "outer.getCTM()");
// g2.nearestViewportElement == outer
is((function(){try{return g2.getCTM().e}catch(e){return e}})(), 600, "g2.getCTM().e");
is((function(){try{return g2.getCTM().f}catch(e){return e}})(), 700, "g2.getCTM().f");
// g3.nearestViewportElement == null
is((function(){try{return g3.getCTM()}catch(e){return e}})(), null, "g3.getCTM()");
// symbolRect.nearestViewportElement == sym
is((function(){try{return symbolRect.getCTM().e}catch(e){return e}})(), 70, "symbolRect.getCTM().e");
is((function(){try{return symbolRect.getCTM().f}catch(e){return e}})(), 80, "symbolRect.getCTM().f");
/* Tests the consistency with farthestViewportElement
(code is from test_viewport.html) */
// root.farthestViewportElement == null (but actually == root)
is((function(){try{return root.getScreenCTM().e}catch(e){return e}})(), 11, "root.getScreenCTM().e");
is((function(){try{return root.getScreenCTM().f}catch(e){return e}})(), 22, "root.getScreenCTM().f");
// inner.farthestViewportElement == root
is((function(){try{return inner.getScreenCTM().e}catch(e){return e}})(), 45, "inner.getScreenCTM().e");
is((function(){try{return inner.getScreenCTM().f}catch(e){return e}})(), 68, "inner.getScreenCTM().f");
// g1.farthestViewportElement == root
is((function(){try{return g1.getScreenCTM().e}catch(e){return e}})(), 45, "g1.getScreenCTM().e");
is((function(){try{return g1.getScreenCTM().f}catch(e){return e}})(), 68, "g1.getScreenCTM().f");
// outer.farthestViewportElement == null (but actually == root)
is((function(){try{return outer.getScreenCTM().e}catch(e){return e}})(), 46, "outer.getScreenCTM().e");
is((function(){try{return outer.getScreenCTM().f}catch(e){return e}})(), 69, "outer.getScreenCTM().f");
// g2.farthestViewportElement == outer (but actually == root)
is((function(){try{return g2.getScreenCTM().e}catch(e){return e}})(), 646, "g2.getScreenCTM().e");
is((function(){try{return g2.getScreenCTM().f}catch(e){return e}})(), 769, "g2.getScreenCTM().f");
// g3.farthestViewportElement == null (but actually == null)
is((function(){try{return g3.getScreenCTM()}catch(e){return e}})(), null, "g3.getScreenCTM()");
// symbolRect.farthestViewportElement == root
is((function(){try{return symbolRect.getScreenCTM().e}catch(e){return e}})(), 85, "symbolRect.getScreenCTM().e");
is((function(){try{return symbolRect.getScreenCTM().f}catch(e){return e}})(), 108, "symbolRect.getScreenCTM().f");
SimpleTest.finish();
}
window.addEventListener("load", runTest, false);
</script>
</pre>
</body>
</html>

View File

@ -39,7 +39,7 @@ function runTest()
is(g1.viewportElement, inner, "g1.viewportElement");
is(outer.viewportElement, null, "outer.viewportElement");
is(g2.viewportElement, outer, "g2.viewportElement");
is(g3.viewportElement, null, "g2.viewportElement");
is(g3.viewportElement, null, "g3.viewportElement");
is(symbolRect.viewportElement, sym, "symbolRect.viewportElement");
<!-- nearestViewportElement -->

View File

@ -69,6 +69,7 @@
#include "nsSVGContainerFrame.h"
#include "nsSVGLength2.h"
#include "nsGenericElement.h"
#include "nsSVGGraphicElement.h"
#include "nsAttrValue.h"
#include "nsSVGGeometryFrame.h"
#include "nsIScriptError.h"
@ -457,10 +458,104 @@ nsSVGUtils::GetNearestViewportElement(nsIContent *aContent,
ancestor = GetParentElement(ancestor);
}
if (ancestor && ancestor->GetNameSpaceID() == kNameSpaceID_SVG &&
ancestor->Tag() == nsGkAtoms::foreignObject )
return NS_ERROR_FAILURE;
return NS_OK;
}
nsresult
nsSVGUtils::AppendTransformUptoElement(nsIContent *aContent, nsIDOMSVGElement *aElement, nsIDOMSVGMatrix * *aCTM){
nsresult rv;
nsCOMPtr<nsIDOMSVGElement> element = do_QueryInterface(aContent);
nsIContent *ancestor = GetParentElement(aContent);
if (!aElement) {
// calculating GetScreenCTM(): traverse upto the root or non-SVG content.
if (ancestor && ancestor->GetNameSpaceID() == kNameSpaceID_SVG) {
if (ancestor->Tag() == nsGkAtoms::foreignObject && aContent->Tag() != nsGkAtoms::svg)
return NS_ERROR_FAILURE;
rv = AppendTransformUptoElement(ancestor, aElement, aCTM);
if (NS_FAILED(rv)) return rv;
}
} else if (element != aElement) { // calculating GetCTM(): stop at aElement.
NS_ASSERTION(ancestor != nsnull, "ancestor shouldn't be null.");
if (!ancestor)
return NS_ERROR_FAILURE;
rv = AppendTransformUptoElement(ancestor, aElement, aCTM);
if (NS_FAILED(rv)) return rv;
}
nsCOMPtr<nsIDOMSVGMatrix> tmp;
if (nsCOMPtr<nsIDOMSVGSVGElement>(do_QueryInterface(aContent))) {
nsSVGSVGElement *svgElement = static_cast<nsSVGSVGElement*>(aContent);
rv = svgElement->AppendTransform(*aCTM, getter_AddRefs(tmp));
if (NS_FAILED(rv)) return rv;
} else if (nsCOMPtr<nsIDOMSVGTransformable>(do_QueryInterface(aContent))) {
nsSVGGraphicElement *graphicElement = static_cast<nsSVGGraphicElement*>(aContent);
rv = graphicElement->AppendTransform(*aCTM, getter_AddRefs(tmp));
if (NS_FAILED(rv)) return rv;
} else {
//XXX aContent may be other type of viewport-establising elements
// (e.g. <use> and <symbol>) and handle them?
}
if (tmp)
tmp.swap(*aCTM);
return NS_OK;
}
nsresult
nsSVGUtils::GetCTM(nsIContent *aContent, nsIDOMSVGMatrix * *aCTM)
{
nsresult rv;
nsIDocument* currentDoc = aContent->GetCurrentDoc();
if (currentDoc) {
// Flush all pending notifications so that our frames are uptodate
currentDoc->FlushPendingNotifications(Flush_Layout);
}
*aCTM = nsnull;
nsCOMPtr<nsIDOMSVGElement> nearestViewportElement;
rv = GetNearestViewportElement(aContent, getter_AddRefs(nearestViewportElement));
// According to the spec(http://www.w3.org/TR/SVG11/types.html#InterfaceSVGLocatable),
// GetCTM is strictly defined to be the CTM for nearestViewportElement,
// Thus, if it is null, this is null, too.
if (NS_FAILED(rv) || !nearestViewportElement)
return NS_OK; // we can't throw exceptions from this API.
nsCOMPtr<nsIDOMSVGMatrix> tmp;
rv = NS_NewSVGMatrix(getter_AddRefs(tmp), 1, 0, 0, 1, 0, 0);
if (NS_FAILED(rv)) return NS_OK; // we can't throw exceptions from this API.
tmp.swap(*aCTM);
rv = AppendTransformUptoElement(aContent, nearestViewportElement, aCTM);
if (NS_FAILED(rv))
tmp.swap(*aCTM);
return NS_OK; // we can't throw exceptions from this API.
}
nsresult
nsSVGUtils::GetScreenCTM(nsIContent *aContent, nsIDOMSVGMatrix * *aCTM)
{
nsresult rv;
nsIDocument* currentDoc = aContent->GetCurrentDoc();
if (currentDoc) {
// Flush all pending notifications so that our frames are uptodate
currentDoc->FlushPendingNotifications(Flush_Layout);
}
*aCTM = nsnull;
nsCOMPtr<nsIDOMSVGMatrix> tmp;
rv = NS_NewSVGMatrix(getter_AddRefs(tmp), 1, 0, 0, 1, 0, 0);
if (NS_FAILED(rv)) return NS_OK; // we can't throw exceptions from this API.
tmp.swap(*aCTM);
rv = AppendTransformUptoElement(aContent, nsnull, aCTM);
if (NS_FAILED(rv))
tmp.swap(*aCTM);
return NS_OK; // we can't throw exceptions from this API.
}
nsSVGDisplayContainerFrame*
nsSVGUtils::GetNearestSVGViewport(nsIFrame *aFrame)
{

View File

@ -250,6 +250,28 @@ public:
static float CoordToFloat(nsPresContext *aPresContext,
nsSVGElement *aContent,
const nsStyleCoord &aCoord);
/*
* This does the actual job for GetCTM and GetScreenCTM. When called,
* this goes up the tree starting from aContent, until reaching to aElement.
* When aElement is null, this goes up to the outermost SVG parent. Then,
* this post-multiplies aCTM by each parent node's transformation matrix,
* going down the tree from aElement to aContent.
* This doesn't initialize aCTM. So callers usually should pass
* the identity matrix by aCTM.
*/
static nsresult AppendTransformUptoElement(nsIContent *aContent,
nsIDOMSVGElement *aElement,
nsIDOMSVGMatrix * *aCTM);
/*
* Return the CTM
*/
static nsresult GetCTM(nsIContent *aContent, nsIDOMSVGMatrix * *aCTM);
/*
* Return the screen CTM
*/
static nsresult GetScreenCTM(nsIContent *aContent, nsIDOMSVGMatrix * *aCTM);
/*
* Return the nearest viewport element
*/