Bug 783915 - Make SVG Fragment identifier processing more robust. r=dholbert

This commit is contained in:
Robert Longson 2012-08-25 10:02:34 -07:00
parent f02731254d
commit 292fc70a0c
7 changed files with 125 additions and 72 deletions

View File

@ -201,7 +201,7 @@ ToPreserveAspectRatio(const nsAString &aString,
nsresult
SVGAnimatedPreserveAspectRatio::SetBaseValueString(
const nsAString &aValueAsString, nsSVGElement *aSVGElement)
const nsAString &aValueAsString, nsSVGElement *aSVGElement, bool aDoSetAttr)
{
SVGPreserveAspectRatio val;
nsresult res = ToPreserveAspectRatio(aValueAsString, &val);
@ -209,18 +209,23 @@ SVGAnimatedPreserveAspectRatio::SetBaseValueString(
return res;
}
nsAttrValue emptyOrOldValue;
if (aDoSetAttr) {
emptyOrOldValue = aSVGElement->WillChangePreserveAspectRatio();
}
mBaseVal = val;
mIsBaseSet = true;
if (!mIsAnimated) {
mAnimVal = mBaseVal;
}
else {
if (aDoSetAttr) {
aSVGElement->DidChangePreserveAspectRatio(emptyOrOldValue);
}
if (mIsAnimated) {
aSVGElement->AnimationNeedsResample();
}
// We don't need to call DidChange* here - we're only called by
// nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
// which takes care of notifying.
return NS_OK;
}

View File

@ -92,7 +92,8 @@ public:
}
nsresult SetBaseValueString(const nsAString& aValue,
nsSVGElement *aSVGElement);
nsSVGElement *aSVGElement,
bool aDoSetAttr);
void GetBaseValueString(nsAString& aValue) const;
void SetBaseValue(const SVGPreserveAspectRatio &aValue,

View File

@ -4,7 +4,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "SVGFragmentIdentifier.h"
#include "mozilla/CharTokenizer.h"
#include "nsIDOMSVGDocument.h"
#include "nsSVGSVGElement.h"
#include "nsSVGViewElement.h"
@ -21,6 +20,12 @@ IsMatchingParameter(const nsAString &aString, const nsAString &aParameterName)
aString.CharAt(aParameterName.Length()) == '(';
}
inline bool
IgnoreWhitespace(PRUnichar aChar)
{
return false;
}
static nsSVGViewElement*
GetViewElement(nsIDocument *aDocument, const nsAString &aId)
{
@ -94,19 +99,20 @@ SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString &aViewSpec,
return false;
}
// SVGViewAttribute may occur in any order, but each type may only occur at most one time
// in a correctly formed SVGViewSpec.
// If we encounter any element more than once or get any syntax errors we're going to
// return without updating the root element
const nsAString *viewBoxParams = nullptr;
const nsAString *preserveAspectRatioParams = nullptr;
const nsAString *zoomAndPanParams = nullptr;
// SVGViewAttributes may occur in any order, but each type may only occur
// at most one time in a correctly formed SVGViewSpec.
// If we encounter any attribute more than once or get any syntax errors
// we're going to return false and cancel any changes.
bool viewBoxFound = false;
bool preserveAspectRatioFound = false;
bool zoomAndPanFound = false;
// Each token is a SVGViewAttribute
int32_t bracketPos = aViewSpec.FindChar('(');
CharTokenizer<';'>tokenizer(
Substring(aViewSpec, bracketPos + 1, aViewSpec.Length() - bracketPos - 2));
uint32_t lengthOfViewSpec = aViewSpec.Length() - bracketPos - 2;
nsCharSeparatedTokenizerTemplate<IgnoreWhitespace> tokenizer(
Substring(aViewSpec, bracketPos + 1, lengthOfViewSpec), ';');
if (!tokenizer.hasMoreTokens()) {
return false;
@ -125,50 +131,62 @@ SVGFragmentIdentifier::ProcessSVGViewSpec(const nsAString &aViewSpec,
Substring(token, bracketPos + 1, token.Length() - bracketPos - 2);
if (IsMatchingParameter(token, NS_LITERAL_STRING("viewBox"))) {
if (viewBoxParams) {
if (viewBoxFound ||
NS_FAILED(root->mViewBox.SetBaseValueString(
params, root, true))) {
return false;
}
viewBoxParams = &params;
viewBoxFound = true;
} else if (IsMatchingParameter(token, NS_LITERAL_STRING("preserveAspectRatio"))) {
if (preserveAspectRatioParams) {
if (preserveAspectRatioFound ||
NS_FAILED(root->mPreserveAspectRatio.SetBaseValueString(
params, root, true))) {
return false;
}
preserveAspectRatioParams = &params;
preserveAspectRatioFound = true;
} else if (IsMatchingParameter(token, NS_LITERAL_STRING("zoomAndPan"))) {
if (zoomAndPanParams) {
if (zoomAndPanFound) {
return false;
}
zoomAndPanParams = &params;
nsIAtom *valAtom = NS_GetStaticAtom(params);
if (!valAtom) {
return false;
}
const nsSVGEnumMapping *mapping = nsSVGSVGElement::sZoomAndPanMap;
while (mapping->mKey) {
if (valAtom == *(mapping->mKey)) {
// If we've got a valid zoomAndPan value, then set it on our root element.
if (NS_FAILED(root->mEnumAttributes[nsSVGSVGElement::ZOOMANDPAN].SetBaseValue(
mapping->mVal, root))) {
return false;
}
break;
}
mapping++;
}
if (!mapping->mKey) {
// Unrecognised zoomAndPan value
return false;
}
zoomAndPanFound = true;
} else {
// We don't support transform or viewTarget currently
return false;
}
} while (tokenizer.hasMoreTokens());
if (viewBoxParams) {
root->mViewBox.SetBaseValueString(*viewBoxParams, root);
} else {
RestoreOldViewBox(root);
}
if (preserveAspectRatioParams) {
root->mPreserveAspectRatio.SetBaseValueString(*preserveAspectRatioParams, root);
} else {
RestoreOldPreserveAspectRatio(root);
}
if (zoomAndPanParams) {
nsCOMPtr<nsIAtom> valAtom = do_GetAtom(*zoomAndPanParams);
const nsSVGEnumMapping *mapping = root->sZoomAndPanMap;
while (mapping->mKey) {
if (valAtom == *(mapping->mKey)) {
root->mEnumAttributes[nsSVGSVGElement::ZOOMANDPAN].SetBaseValue(mapping->mVal, root);
break;
}
mapping++;
if (root->mUseCurrentView) {
// A previous SVGViewSpec may have overridden some attributes.
// If they are no longer overridden we need to restore the old values.
if (!viewBoxFound) {
RestoreOldViewBox(root);
}
if (!preserveAspectRatioFound) {
RestoreOldPreserveAspectRatio(root);
}
if (!zoomAndPanFound) {
RestoreOldZoomAndPan(root);
}
} else {
RestoreOldZoomAndPan(root);
}
return true;

View File

@ -521,7 +521,7 @@ nsSVGElement::ParseAttribute(int32_t aNamespaceID,
if (aAttribute == nsGkAtoms::viewBox) {
nsSVGViewBox* viewBox = GetViewBox();
if (viewBox) {
rv = viewBox->SetBaseValueString(aValue, this);
rv = viewBox->SetBaseValueString(aValue, this, false);
if (NS_FAILED(rv)) {
viewBox->Init();
} else {
@ -535,7 +535,7 @@ nsSVGElement::ParseAttribute(int32_t aNamespaceID,
SVGAnimatedPreserveAspectRatio *preserveAspectRatio =
GetPreserveAspectRatio();
if (preserveAspectRatio) {
rv = preserveAspectRatio->SetBaseValueString(aValue, this);
rv = preserveAspectRatio->SetBaseValueString(aValue, this, false);
if (NS_FAILED(rv)) {
preserveAspectRatio->Init();
} else {

View File

@ -148,20 +148,25 @@ ToSVGViewBoxRect(const nsAString& aStr, nsSVGViewBoxRect *aViewBox)
nsresult
nsSVGViewBox::SetBaseValueString(const nsAString& aValue,
nsSVGElement *aSVGElement)
nsSVGElement *aSVGElement,
bool aDoSetAttr)
{
nsSVGViewBoxRect viewBox;
nsresult res = ToSVGViewBoxRect(aValue, &viewBox);
if (NS_SUCCEEDED(res)) {
nsAttrValue emptyOrOldValue;
if (aDoSetAttr) {
emptyOrOldValue = aSVGElement->WillChangeViewBox();
}
mBaseVal = nsSVGViewBoxRect(viewBox.x, viewBox.y, viewBox.width, viewBox.height);
mHasBaseVal = true;
if (aDoSetAttr) {
aSVGElement->DidChangeViewBox(emptyOrOldValue);
}
if (mAnimVal) {
aSVGElement->AnimationNeedsResample();
}
// We don't need to call Will/DidChange* here - we're only called by
// nsSVGElement::ParseAttribute under nsGenericElement::SetAttr,
// which takes care of notifying.
}
return res;
}

View File

@ -64,7 +64,8 @@ public:
nsSVGElement *aSVGElement);
nsresult SetBaseValueString(const nsAString& aValue,
nsSVGElement *aSVGElement);
nsSVGElement *aSVGElement,
bool aDoSetAttr);
void GetBaseValueString(nsAString& aValue) const;
nsresult ToDOMAnimatedRect(nsIDOMSVGAnimatedRect **aResult,

View File

@ -19,9 +19,14 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=759124
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
function Test(svgFragmentIdentifier, valid) {
function Test(svgFragmentIdentifier, valid, viewBoxString,
preserveAspectRatioString, zoomAndPanString)
{
this.svgFragmentIdentifier = svgFragmentIdentifier;
this.valid = valid;
this.viewBoxString = viewBoxString;
this.preserveAspectRatioString = preserveAspectRatioString;
this.zoomAndPanString = zoomAndPanString;
}
function runTests()
@ -30,25 +35,43 @@ function runTests()
var doc = svg.contentWindow.document;
var tests = [
new Test("view", true),
new Test("unknown", false),
new Test("svgView(viewBox(0,0,200,200)))", true),
new Test("svgView(preserveAspectRatio(xMaxYMin slice))", true),
new Test("svgView(viewBox(1,2,3,4);preserveAspectRatio(xMaxYMin))", true),
new Test("svgView(zoomAndPan(disable))", true),
new Test("svgView", false),
new Test("svgView(", false),
new Test("svgView()", false)
new Test("view", true, "0 200 100 100", "none", null),
new Test("unknown", false, null, null, null),
new Test("svgView(viewBox(0,0,200,200))", true, "0 0 200 200", null, null),
new Test("svgView(preserveAspectRatio(xMaxYMin slice))", true, null, "xMaxYMin slice", null),
new Test("svgView(viewBox(1,2,3,4);preserveAspectRatio(xMinYMax))", true, "1 2 3 4", "xMinYMax meet", null),
new Test("svgView(zoomAndPan(disable))", true, null, null, "disable"),
new Test("svgView(viewBox(bad)", false, null, null, null),
new Test("svgView(preserveAspectRatio(bad))", false, null, null, null),
new Test("svgView(zoomAndPan(bad))", false, null, null, null),
new Test("svgView", false, null, null, null),
new Test("svgView(", false, null, null, null),
new Test("svgView()", false, null, null, null),
// Be sure we verify that there's a closing paren for svgView()
// (and not too many closing parens)
new Test("svgView(zoomAndPan(disable)", false, null, null, null),
new Test("svgView(zoomAndPan(disable) ", false, null, null, null),
new Test("svgView(zoomAndPan(disable)]", false, null, null, null),
new Test("svgView(zoomAndPan(disable)))", false, null, null, null)
];
var src = svg.getAttribute("src");
for (var i = 0; i < tests.length; i++) {
var test = tests[i];
svg.setAttribute("src", src + "#" + test.svgFragmentIdentifier);
is(doc.rootElement.useCurrentView, test.valid,
"Expected " + test.svgFragmentIdentifier + " to be " +
(test.valid ? "valid" : "invalid"));
}
var src = svg.getAttribute("src");
for (var i = 0; i < tests.length; i++) {
var test = tests[i];
svg.setAttribute("src", src + "#" + test.svgFragmentIdentifier);
is(doc.rootElement.useCurrentView, test.valid,
"Expected " + test.svgFragmentIdentifier + " to be " +
(test.valid ? "valid" : "invalid"));
is(doc.rootElement.getAttribute("viewBox"),
test.viewBoxString, "unexpected viewBox");
is(doc.rootElement.getAttribute("preserveAspectRatio"),
test.preserveAspectRatioString, "unexpected preserveAspectRatio");
is(doc.rootElement.getAttribute("zoomAndPan"),
test.zoomAndPanString, "unexpected zoomAndPan");
}
SimpleTest.finish();
}