diff options
Diffstat (limited to 'dom/svg/test/test_SVGTransformList.xhtml')
-rw-r--r-- | dom/svg/test/test_SVGTransformList.xhtml | 461 |
1 files changed, 461 insertions, 0 deletions
diff --git a/dom/svg/test/test_SVGTransformList.xhtml b/dom/svg/test/test_SVGTransformList.xhtml new file mode 100644 index 0000000000..3e1cd7b8bb --- /dev/null +++ b/dom/svg/test/test_SVGTransformList.xhtml @@ -0,0 +1,461 @@ +<html xmlns="http://www.w3.org/1999/xhtml"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=602759 +--> +<head> + <title>Tests specific to SVGTransformList</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="matrixUtils.js"></script> + <script type="text/javascript" src="MutationEventChecker.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=602759"> + Mozilla Bug 602759</a> +<p id="display"></p> +<div id="content" style="display:none;"> +<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100" + onload="this.pauseAnimations();"> + <g id="g"/> +</svg> +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> +<![CDATA[ + +SimpleTest.waitForExplicitFinish(); + +/* +This file runs a series of SVGTransformList specific tests. Generic SVGXxxList +tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized +to other list types belongs there. +*/ + +function main() { + var g = $("g"); + var tests = + [ testConsolidateMatrix, + testConsolidateMatrixOneElem, + testConsolidateMatrixZeroElem, + testCreateSVGTransformFromMatrix, + testReadOnly, + testOrphan, + testFailedSet, + testMutationEvents, + ]; + for (var i = 0; i < tests.length; i++) { + tests[i](g); + } + SimpleTest.finish(); +} + +function testConsolidateMatrix(g) { + // This is the example from SVG 1.1 section 7.5 + g.setAttribute("transform", + "translate(50 90) rotate(-45) translate(130 160)"); + var list = g.transform.baseVal; + is(list.numberOfItems, 3, "Unexpected length of unconsolidated list"); + + // Sanity check -- take ref to first item in list and validate it + var first_item = list.getItem(0); + is(first_item.type, SVGTransform.SVG_TRANSFORM_TRANSLATE, + "Unexpected type of first item in list"); + cmpMatrix(first_item.matrix, [1, 0, 0, 1, 50, 90], + "Unexpected value for first item in list"); + + // Consolidate + var consolidated = list.consolidate(); + is(list.numberOfItems, 1, "Unexpected length of consolidated list"); + ok(consolidated === list.getItem(0), + "Consolidate return value should be first item in list, not a copy"); + is(consolidated.type, SVGTransform.SVG_TRANSFORM_MATRIX, + "Consolidated transform not of type matrix"); + const angle = -Math.PI / 4; + roughCmpMatrix(consolidated.matrix, + [Math.cos(angle), Math.sin(angle), + -Math.sin(angle), Math.cos(angle), + 130 * Math.cos(angle) - 160 * Math.sin(angle) + 50, + 160 * Math.cos(angle) + 130 * Math.sin(angle) + 90], + "Unexpected result after consolidating matrices"); + + // Check ref to first item in list + // a) should not have changed + is(first_item.type, SVGTransform.SVG_TRANSFORM_TRANSLATE, + "Unexpected type of cached first item in list after consolidating"); + cmpMatrix(first_item.matrix, [1, 0, 0, 1, 50, 90], + "Unexpected value for cached first item in list after consolidating"); + // b) should still be usable + first_item.setScale(2, 3); + is(first_item.type, SVGTransform.SVG_TRANSFORM_SCALE, + "Cached first item in list not usable after consolidating"); + + // Check consolidated is live + // a) Changes to 'consolidated' affect list + consolidated.setSkewX(45); + is(list.getItem(0).type, SVGTransform.SVG_TRANSFORM_SKEWX, + "Changing return value from consolidate doesn't affect list"); + // b) Changes to list affect 'consolidated' + list.getItem(0).setRotate(90, 0, 0); + is(consolidated.type, SVGTransform.SVG_TRANSFORM_ROTATE, + "Changing list doesn't affect return value from consolidate"); +} + +function testConsolidateMatrixOneElem(g) { + // Check that even if we only have one item in the list it becomes a matrix + // transform (as per the spec) + g.setAttribute("transform", "translate(50 90)"); + var list = g.transform.baseVal; + is(list.numberOfItems, 1, "Unexpected length of unconsolidated list"); + var first_item = list.getItem(0); + is(first_item.type, SVGTransform.SVG_TRANSFORM_TRANSLATE, + "Unexpected type of first item in list"); + cmpMatrix(first_item.matrix, [1, 0, 0, 1, 50, 90], + "Unexpected value for first item in list"); + + // Consolidate + var consolidated = list.consolidate(); + is(list.numberOfItems, 1, "Unexpected length of consolidated list"); + ok(consolidated === list.getItem(0), + "Consolidate return value should be first item in list, not a copy"); + is(consolidated.type, SVGTransform.SVG_TRANSFORM_MATRIX, + "Consolidated transform not of type matrix"); + cmpMatrix(consolidated.matrix, [1, 0, 0, 1, 50, 90], + "Unexpected consolidated matrix value"); +} + +function testConsolidateMatrixZeroElem(g) { + // Check that zero items returns null + g.setAttribute("transform", ""); + var list = g.transform.baseVal; + is(list.numberOfItems, 0, "Unexpected length of unconsolidated list"); + var consolidated = list.consolidate(); + ok(consolidated === null, + "consolidate() should return null for a zero-length transform list"); +} + +function testCreateSVGTransformFromMatrix(g) { + var m = createMatrix(1, 2, 3, 4, 5, 6); + + // "Creates an SVGTransform object which is initialized to transform of type + // SVG_TRANSFORM_MATRIX and whose values are the given matrix. The values from + // the parameter matrix are copied, the matrix parameter is not adopted as + // SVGTransform::matrix." + var list = g.transform.baseVal; + list.clear(); + var t = list.createSVGTransformFromMatrix(m); + + // Check that list hasn't changed + is(list.numberOfItems, 0, + "Transform list changed after calling createSVGTransformFromMatrix"); + + // Check return value + is(t.type, SVGTransform.SVG_TRANSFORM_MATRIX, + "Returned transform not of type matrix"); + cmpMatrix(t.matrix, [1, 2, 3, 4, 5, 6], + "Unexpected returned matrix value"); + + // Check values are copied + ok(t.matrix != m, "Matrix should be copied not adopted"); + m.a = 2; + is(t.matrix.a, 1, + "Changing source matrix should not affect newly created transform"); + + // null should give us an identity matrix + t = list.createSVGTransformFromMatrix(null); + cmpMatrix(t.matrix, [1, 0, 0, 1, 0, 0], + "Unexpected returned matrix value"); + + // Try passing in bad values ("undefined" etc.) + let exception = null; + try { + t = list.createSVGTransformFromMatrix("undefined"); + } catch (e) { exception = e; } + ok(exception, + "Failed to throw for string input to createSVGTransformFromMatrix"); + exception = null; + try { + t = list.createSVGTransformFromMatrix(SVGMatrix(t)); + } catch (e) { exception = e; } + ok(exception, + "Failed to throw for bad input to createSVGTransformFromMatrix"); + exception = null; +} + +function testReadOnly(g) { + var SVG_NS = "http://www.w3.org/2000/svg"; + + // Just some data to work with + g.setAttribute("transform", "translate(50 90)"); + + // baseVal / animVal are readonly attributes + // Create another (empty) transform list + var otherg = document.createElementNS(SVG_NS, "g"); + g.parentNode.appendChild(otherg); + is(g.transform.baseVal.numberOfItems, 1, + "Unexpected number of items in transform list before attempting to set"); + is(otherg.transform.baseVal.numberOfItems, 0, + "Unexpected number of items in source transform list before attempting to" + + " set"); + // Attempt to set the base value and check nothing changes + g.transform.baseVal = otherg.transform.baseVal; + is(g.transform.baseVal.numberOfItems, 1, + "baseVal should be read-only but its value has changed"); + is(otherg.transform.baseVal.numberOfItems, 0, + "baseVal changed after attempting to use it set another value"); + + // Read-only SVGTransformList: + // Standard list methods are covered in test_SVGxxxList.xhtml so here we + // just add tests for SVGTransformList-specific methods + var roList = g.transform.animVal; + // consolidate() + var threw = false; + try { + roList.consolidate(); + } catch (e) { + is(e.name, "NoModificationAllowedError", + "Got unexpected exception " + e + + ", expected NoModificationAllowedError"); + is(e.code, DOMException.NO_MODIFICATION_ALLOWED_ERR, + "Got unexpected exception " + e + + ", expected NO_MODIFICATION_ALLOWED_ERR"); + threw = true; + } + ok(threw, + "Failed to throw exception when calling consolidate on read-only list"); + + // Read-only SVGTransform: + // read-only attributes are tested in test_transform.xhtml. Here we are + // concerned with methods that throw because this *object* is read-only + // (since it belongs to a read-only transform list) + var roTransform = roList.getItem(0); + // setMatrix + threw = false; + try { + var m = createMatrix(1, 2, 3, 4, 5, 6); + roTransform.setMatrix(m); + } catch (e) { + is(e.name, "NoModificationAllowedError", + "Got unexpected exception " + e + + ", expected NoModificationAllowedError"); + is(e.code, DOMException.NO_MODIFICATION_ALLOWED_ERR, + "Got unexpected exception " + e + + ", expected NO_MODIFICATION_ALLOWED_ERR"); + threw = true; + } + ok(threw, "Failed to throw exception when calling setMatrix on read-only" + + " transform"); + // setTranslate + threw = false; + try { + roTransform.setTranslate(2, 3); + } catch (e) { + threw = true; + } + ok(threw, "Failed to throw when calling setTranslate on read-only" + + " transform"); + // setScale + threw = false; + try { + roTransform.setScale(2, 3); + } catch (e) { + threw = true; + } + ok(threw, "Failed to throw when calling setScale on read-only transform"); + // setRotate + threw = false; + try { + roTransform.setRotate(1, 2, 3); + } catch (e) { + threw = true; + } + ok(threw, "Failed to throw when calling setRotate on read-only transform"); + // setSkewX + threw = false; + try { + roTransform.setSkewX(2); + } catch (e) { + threw = true; + } + ok(threw, "Failed to throw when calling setSkewX on read-only transform"); + // setSkewY + threw = false; + try { + roTransform.setSkewY(2); + } catch (e) { + threw = true; + } + ok(threw, "Failed to throw when calling setSkewY on read-only transform"); + + // Read-only SVGMatrix + var roMatrix = roTransform.matrix; + threw = false; + try { + roMatrix.a = 1; + } catch (e) { + is(e.name, "NoModificationAllowedError", + "Got unexpected exception " + e + + ", expected NoModificationAllowedError"); + is(e.code, DOMException.NO_MODIFICATION_ALLOWED_ERR, + "Got unexpected exception " + e + + ", expected NO_MODIFICATION_ALLOWED_ERR"); + threw = true; + } + ok(threw, "Failed to throw exception when modifying read-only matrix"); +} + +function testOrphan(g) { + // Although this isn't defined, if a read-only object becomes orphaned + // (detached from it's parent), then presumably it should become editable + // again. + + // As with the read-only test set a value to test with + g.setAttribute("transform", "translate(50 90)"); + + var roList = g.transform.animVal; + var roTransform = roList.getItem(0); + var roMatrix = roTransform.matrix; + + // Orphan transform list contents by re-setting transform attribute + g.setAttribute("transform", ""); + + // Transform should now be editable + var exception = null; + try { + roTransform.setTranslate(5, 3); + } catch (e) { + exception = e; + } + ok(exception === null, + "Unexpected exception " + exception + " modifying orphaned transform"); + + // So should matrix + exception = null; + try { + roMatrix.a = 1; + } catch (e) { + exception = e; + } + ok(exception === null, + "Unexpected exception " + exception + " modifying orphaned matrix"); +} + +function testFailedSet(g) { + // Check that a parse failure results in the attribute being empty + + // Set initial value + g.setAttribute("transform", "translate(50 90)"); + var list = g.transform.baseVal; + is(list.numberOfItems, 1, "Unexpected initial length of list"); + + // Attempt to set bad value + g.setAttribute("transform", "translate(40 50) scale(a)"); + is(list.numberOfItems, 0, + "Transform list should be empty after setting bad value"); + is(g.transform.animVal.numberOfItems, 0, + "Animated transform list should also be empty after setting bad value"); +} + +function testMutationEvents(g) { + // Check mutation events + + // Set initial value + g.setAttribute("transform", "translate(50 90)"); + var list = g.transform.baseVal; + is(list.numberOfItems, 1, "Unexpected initial length of list"); + var eventChecker = new MutationEventChecker; + eventChecker.watchAttr(g, "transform"); + + // consolidate + // + // Consolidate happens to generate two modification events in our + // implementation--it's not ideal but it's better than none + eventChecker.expect("modify modify modify"); + g.setAttribute("transform", "translate(10 10) translate(10 10)"); + list.consolidate(); + + // In the following, each of the operations is performed twice but only one + // mutation event is expected. This is to check that redundant mutation + // events are not sent. + + // transform.setMatrix + eventChecker.expect("modify"); + var mx = $("svg").createSVGMatrix(); + list[0].setMatrix(mx); + list[0].setMatrix(mx); + [ + {a: 1, m11: 2}, + {a: Infinity}, + {b: 0, m12: -1}, + {c: Infinity, m21: -Infinity}, + {d: 0, m22: NaN}, + {e: 1, m41: 1.00000001}, + {f: 0, m42: Number.MIN_VALUE}, + ].forEach(dict => { + let exception = null; + try { + list[0].setMatrix(dict); + } catch (e) { + exception = e; + } + ok(exception, + "Failed to throw for invalid input to setMatrix"); + }); + + // transform.setTranslate + eventChecker.expect("modify"); + list[0].setTranslate(10, 10); + list[0].setTranslate(10, 10); + + // transform.setScale + eventChecker.expect("modify"); + list[0].setScale(2, 2); + list[0].setScale(2, 2); + + // transform.setRotate + eventChecker.expect("modify"); + list[0].setRotate(45, 1, 2); + list[0].setRotate(45, 1, 2); + + // transform.setSkewX + eventChecker.expect("modify"); + list[0].setSkewX(45); + list[0].setSkewX(45); + + // transform.setSkewY + eventChecker.expect("modify"); + list[0].setSkewY(25); + list[0].setSkewY(25); + + // transform.matrix + eventChecker.expect("modify modify"); + list[0].matrix.a = 1; + list[0].matrix.a = 1; + list[0].matrix.e = 5; + list[0].matrix.e = 5; + + // setAttribute interaction + eventChecker.expect("modify"); + list[0].setMatrix(mx); + eventChecker.expect(""); + g.setAttribute("transform", "matrix(1, 0, 0, 1, 0, 0)"); + list[0].setMatrix(mx); + + // Attribute removal + eventChecker.expect("remove"); + g.removeAttribute("transform"); + + // Non-existent attribute removal + eventChecker.expect(""); + g.removeAttribute("transform"); + g.removeAttributeNS(null, "transform"); + + eventChecker.finish(); +} + +window.addEventListener("load", main); + +]]> +</script> +</pre> +</body> +</html> |