<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 type="text/javascript" src="/MochiKit/packed.js"></script> <script type="text/javascript" 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 useable first_item.setScale(2, 3); is(first_item.type, SVGTransform.SVG_TRANSFORM_SCALE, "Cached first item in list not useable 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"); // Try passing in bad values (null, "undefined" etc.) var exception = null; try { t = list.createSVGTransformFromMatrix(null); } catch(e) { exception = e; } ok(exception, "Failed to throw for null input to createSVGTransformFromMatrix"); 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"); uexception = null; // 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"); 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); // 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, false); ]]> </script> </pre> </body> </html>