Bug 1943761 - Add class alignment to the mozsearch analysis file. r=asuth
[gecko.git] / dom / svg / test / test_SVGxxxList.xhtml
blob611c687dd6bb7093bca983710c961ba58c0ce9ba
1 <html xmlns="http://www.w3.org/1999/xhtml">
2 <!--
3 https://bugzilla.mozilla.org/show_bug.cgi?id=515116
4 -->
5 <head>
6 <title>Generic tests for SVG animated length lists</title>
7 <script src="/tests/SimpleTest/SimpleTest.js"></script>
8 <script type="text/javascript" src="matrixUtils.js"></script>
9 <script type="text/javascript" src="MutationEventChecker.js"></script>
10 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
11 </head>
12 <body>
13 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=515116">Mozilla Bug 515116</a>
14 <p id="display"></p>
15 <div id="content">
16 <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100"
17 onload="this.pauseAnimations();">
18 <defs>
19 <filter>
20 <feComponentTransfer>
21 <feFuncR id="feFuncR" type="table"/>
22 </feComponentTransfer>
23 </filter>
24 </defs>
25 <text id="text">text</text>
26 <path id="path"/>
27 <polyline id="polyline"/>
28 <g id="g"/>
29 </svg>
30 </div>
31 <pre id="test">
32 <script class="testbody" type="text/javascript">
33 <![CDATA[
36 SimpleTest.waitForExplicitFinish();
39 This file runs a series of type-agnostic tests to check the state of the mini DOM trees that represent various SVG 'list' attributes (including checking the "object identity" of the objects in those trees) in the face of various changes, both with and without the complication of SMIL animation being active.
41 For additional high level information on the tests that are run, see the comment for 'create_animate_elements' below.
43 To have the battery of generic tests run for a new list attribute, add an element with that attribute to the document, then add a JavaScript object literal to the following 'tests' array with the following properties:
45 target_element_id
46 The ID of the element that has the attribute that is to be tested.
47 attr_name
48 The name of the attribute that is to be tested.
49 prop_name
50 The name of the DOM property that corresponds to the attribute that is to
51 be tested. For some list types the SVGAnimatedXxxList interface is
52 inherited by the element interface rather than the element having a
53 property of that type, and in others the list type is not animatable so
54 there is no SVGAnimatedXxxList interface for that list type. In these
55 cases this property should be set to null.
56 bv_name
57 The name of the DOM base value property for the attribute that is to be
58 tested. This is usually 'baseVal', but not always. In the case of
59 SVGStringList, which is not animatable, this is the name of the
60 SVGStringList property.
61 av_name
62 The name of the DOM anim value property for the attribute that is to be
63 tested. This is usually 'animVal' but not always. In the case of
64 SVGStringList, which is not animatable, this should be set to null.
65 el_type
66 The name of the SVGXxxElement interface on which the property corresponding
67 to the attribute being tested is defined.
68 prop_type
69 The name of the SVGAnimatedXxxList interface (e.g. SVGAnimatedLengthList),
70 if it exists, and if the element has a property is of this type (as
71 opposed to the element interface inheriting it).
72 list_type
73 The name of the SVGXxxList interface implemented by the baseVal and
74 animVal objects.
75 item_type
76 The name of the SVGXxx interface implemented by the list items.
77 attr_val_3a:
78 attr_val_3b:
79 Two attribute values containing three different items.
80 attr_val_4
81 An attribute value containing four items.
82 attr_val_5a:
83 attr_val_5b:
84 Two attribute values containing five different items.
85 attr_val_5b_firstItem_x3_constructor:
86 Function to construct a list-item that should match the first item in a
87 SVGXxxList after three repeats of a cumulative animation to attr_val_5b.
88 This function takes t.item_constructor as its only argument.
89 item_constructor:
90 Function to create a dummy list item.
91 item_is:
92 Function to compare two list items for equality, like "is()". If this
93 property is omitted, it is assumed that we can just compare
94 "item.value" (which is the case for most list types).
96 // helper method
97 function keys(obj) {
98 var rval = [];
99 for (var prop in obj) {
100 rval.push(prop);
102 return rval;
105 var tests = [
107 // SVGLengthList test:
108 target_element_id: "text",
109 attr_name: "x",
110 prop_name: "x",
111 bv_name: "baseVal",
112 av_name: "animVal",
113 el_type: "SVGTextElement",
114 prop_type: "SVGAnimatedLengthList",
115 list_type: "SVGLengthList",
116 item_type: "SVGLength",
117 attr_val_3a: "10 20ex, 30in",
118 attr_val_3b: "30in 10, 20ex",
119 attr_val_4: "10 20ex, 30in ,40cm",
120 attr_val_5a: "10 20ex, 30in ,40cm , 50%",
121 attr_val_5b: "20 50%, 20ex ,30in , 40cm",
122 attr_val_5b_firstItem_x3_constructor(constructor) {
123 var expected = constructor();
124 expected.value = 60;
125 return expected;
127 item_constructor() {
128 // We need this function literal to avoid "Illegal operation on
129 // WrappedNative prototype object" NS_ERROR_XPC_BAD_OP_ON_WN_PROTO.
130 return document.getElementById("svg").createSVGLength();
134 // SVGNumberList test:
135 target_element_id: "text",
136 attr_name: "rotate",
137 prop_name: "rotate",
138 bv_name: "baseVal",
139 av_name: "animVal",
140 el_type: "SVGTextElement",
141 prop_type: "SVGAnimatedNumberList",
142 list_type: "SVGNumberList",
143 item_type: "SVGNumber",
144 attr_val_3a: "0 20 40",
145 attr_val_3b: "60 40 20",
146 attr_val_4: "40 20 10 80",
147 attr_val_5a: "90 30 60 20 70",
148 attr_val_5b: "30 20 70 30 90",
149 attr_val_5b_firstItem_x3_constructor(constructor) {
150 var expected = constructor();
151 expected.value = 90;
152 return expected;
154 item_constructor() {
155 // We need this function literal to avoid "Illegal operation on
156 // WrappedNative prototype object" NS_ERROR_XPC_BAD_OP_ON_WN_PROTO.
157 return document.getElementById("svg").createSVGNumber();
161 // SVGNumberList test:
162 target_element_id: "feFuncR",
163 attr_name: "tableValues",
164 prop_name: "tableValues",
165 bv_name: "baseVal",
166 av_name: "animVal",
167 el_type: "SVGFEComponentTransferElement",
168 prop_type: "SVGAnimatedNumberList",
169 list_type: "SVGNumberList",
170 item_type: "SVGNumber",
171 attr_val_3a: "0 .5 .2",
172 attr_val_3b: "1 .7 .1",
173 attr_val_4: ".5 .3 .8 .2",
174 attr_val_5a: "3 4 5 6 7",
175 attr_val_5b: "7 6 5 4 3",
176 attr_val_5b_firstItem_x3_constructor(constructor) {
177 var expected = constructor();
178 expected.value = 21;
179 return expected;
181 item_constructor() {
182 // We need this function literal to avoid "Illegal operation on
183 // WrappedNative prototype object" NS_ERROR_XPC_BAD_OP_ON_WN_PROTO.
184 return document.getElementById("svg").createSVGNumber();
188 // SVGPointList test:
189 target_element_id: "polyline",
190 attr_name: "points",
191 prop_name: null, // SVGAnimatedPoints is an inherited interface!
192 bv_name: "points",
193 av_name: "animatedPoints",
194 el_type: "SVGPolylineElement",
195 prop_type: null,
196 list_type: "SVGPointList",
197 item_type: "SVGPoint",
198 attr_val_3a: " 10,10 50,50 90,10 ",
199 attr_val_3b: " 10,50 50,10 90,50 ",
200 attr_val_4: " 10,10 50,50 90,10 200,100 ",
201 attr_val_5a: " 10,10 50,50 90,10 130,50 170,10 ",
202 attr_val_5b: " 50,10 50,10 90,50 130,10 170,50 ",
203 attr_val_5b_firstItem_x3_constructor(constructor) {
204 var expected = constructor();
205 expected.x = 150;
206 expected.y = 30;
207 return expected;
209 item_constructor() {
210 // XXX return different values each time
211 return document.getElementById("svg").createSVGPoint();
213 item_is(itemA, itemB, message) {
214 ok(typeof(itemA.x) != "undefined" &&
215 typeof(itemB.x) != "undefined",
216 "expecting x property");
217 ok(typeof(itemA.y) != "undefined" &&
218 typeof(itemB.y) != "undefined",
219 "expecting y property");
221 is(itemA.x, itemB.x, message);
222 is(itemA.y, itemB.y, message);
226 // SVGStringList test:
227 target_element_id: "g",
228 attr_name: "requiredExtensions", // systemLanguage, viewTarget
229 prop_name: null, // SVGStringList attributes are not animatable
230 bv_name: "requiredExtensions",
231 av_name: null,
232 el_type: "SVGGElement",
233 prop_type: null,
234 list_type: "SVGStringList",
235 item_type: "DOMString",
236 attr_val_3a: "http://www.w3.org/TR/SVG11/feature#Shape http://www.w3.org/TR/SVG11/feature#Image " +
237 "http://www.w3.org/TR/SVG11/feature#Style",
238 attr_val_3b: "http://www.w3.org/TR/SVG11/feature#CoreAttribute http://www.w3.org/TR/SVG11/feature#Structure " +
239 "http://www.w3.org/TR/SVG11/feature#Gradient",
240 attr_val_4: "http://www.w3.org/TR/SVG11/feature#Pattern http://www.w3.org/TR/SVG11/feature#Clip " +
241 "http://www.w3.org/TR/SVG11/feature#Mask http://www.w3.org/TR/SVG11/feature#Extensibility",
242 attr_val_5a: "http://www.w3.org/TR/SVG11/feature#BasicStructure http://www.w3.org/TR/SVG11/feature#BasicText " +
243 "http://www.w3.org/TR/SVG11/feature#BasicPaintAttribute http://www.w3.org/TR/SVG11/feature#BasicGraphicsAttribute " +
244 "http://www.w3.org/TR/SVG11/feature#BasicClip",
245 attr_val_5b: "http://www.w3.org/TR/SVG11/feature#DocumentEventsAttribute http://www.w3.org/TR/SVG11/feature#GraphicalEventsAttribute " +
246 "http://www.w3.org/TR/SVG11/feature#AnimationEventsAttribute http://www.w3.org/TR/SVG11/feature#Hyperlinking " +
247 "http://www.w3.org/TR/SVG11/feature#XlinkAttribute",
248 item_constructor() {
249 return "http://www.w3.org/TR/SVG11/feature#XlinkAttribute";
253 // SVGTransformList test:
254 target_element_id: "g",
255 attr_name: "transform", // gradientTransform, patternTransform
256 prop_name: "transform",
257 bv_name: "baseVal",
258 av_name: "animVal",
259 el_type: "SVGGElement",
260 prop_type: "SVGAnimatedTransformList",
261 list_type: "SVGTransformList",
262 item_type: "SVGTransform",
263 attr_val_3a: "translate(20 10) rotate(90 10 10) skewX(45)",
264 attr_val_3b: "translate(30 40) scale(2) matrix(1 2 3 4 5 6)",
265 attr_val_4: "scale(3 2) translate(19) skewY(2) rotate(-10)",
266 attr_val_5a:
267 "translate(20) rotate(-10) skewY(3) matrix(1 2 3 4 5 6) scale(0.5)",
268 attr_val_5b:
269 "skewX(45) rotate(45 -10 -10) skewX(-45) scale(2) matrix(6 5 4 3 2 1)",
270 // SVGTransformList animation addition is tested in
271 // test_SVGTransformListAddition.xhtml so we don't need:
272 // - attr_val_3b
273 // - attr_val_3b
274 // - attr_val_5b_firstItem_x3_constructor
275 // But we populate the first two anyway just in case they are later used for
276 // something other than testing animation.
277 // attr_val_5b_firstItem_x3_constructor is only used for animation
278 item_constructor() {
279 // XXX populate the matrix with different values each time
280 return document.getElementById("svg").createSVGTransform();
282 item_is(itemA, itemB, message) {
283 ok(typeof(itemA.type) != "undefined" &&
284 typeof(itemB.type) != "undefined",
285 "expecting type property");
286 ok(typeof(itemA.matrix) != "undefined" &&
287 typeof(itemB.matrix) != "undefined",
288 "expecting matrix property");
289 ok(typeof(itemA.angle) != "undefined" &&
290 typeof(itemB.angle) != "undefined",
291 "expecting matrix property");
293 is(itemA.type, itemB.type, message);
294 is(itemA.angle, itemB.angle, message);
295 cmpMatrix(itemA.matrix, itemB.matrix, message);
302 This function returns a DocumentFragment with three 'animate' element children. The duration of the three animations is as follows:
304 animation 1: | *-----------*-----------*-----------*
305 animation 2: | *--*
306 animation 3: | *--*
307 |___________________________________________> time (s)
308 | | | | | | | | | | | | | | |
309 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
311 The first animation repeats once so that we can test state on a repeat animation.
313 The second animation overrides the first animation for a short time, and has fewer list items than the first animation. This allows us to test object identity and other state on and after an overriding animation. Specifically, it allows us to check whether animVal list items are kept or discarded after the end of an overriding animation that has fewer items.
315 The third animation has additive="sum", with fewer items than the lower priority animation 1, allowing us to test object identity and other state in that scenario. TODO: some type aware tests to check whether the composite fails or works?
317 At t=0s and t=1s we test the effect of an attribute value changes in the absence and presence of SMIL animation respectively.
319 At t=10s we programmatically remove the fill="freeze" from animation 1.
321 function create_animate_elements(test) {
322 var SVG_NS = "http://www.w3.org/2000/svg";
323 var df = document.createDocumentFragment();
325 if (is_transform_attr(test.attr_name)) {
326 // animateTransform is "special". Although it targets an
327 // SVGAnimatedTransformList it only takes SVGTransform values as
328 // animation values. Therefore all the assumptions we're testing about the
329 // length of lists don't apply. We simply have to test it separately.
330 // This is done in test_SVGTransformListAddition.xhtml.
331 return df; // Return the empty document fragment
334 var animate1 = document.createElementNS(SVG_NS, "animate");
335 var animate2 = document.createElementNS(SVG_NS, "animate");
336 var animate3 = document.createElementNS(SVG_NS, "animate");
338 animate1.setAttribute("attributeName", test.attr_name);
339 animate1.setAttribute("from", test.attr_val_5a);
340 animate1.setAttribute("to", test.attr_val_5b);
341 animate1.setAttribute("begin", "1s");
342 animate1.setAttribute("dur", "4s");
343 animate1.setAttribute("repeatCount", "3");
344 animate1.setAttribute("accumulate", "sum");
345 animate1.setAttribute("fill", "freeze");
346 df.appendChild(animate1);
348 animate2.setAttribute("attributeName", test.attr_name);
349 animate2.setAttribute("from", test.attr_val_3a);
350 animate2.setAttribute("to", test.attr_val_3b);
351 animate2.setAttribute("begin", "2s");
352 animate2.setAttribute("dur", "1s");
353 df.appendChild(animate2);
355 animate3.setAttribute("attributeName", test.attr_name);
356 animate3.setAttribute("from", test.attr_val_3a);
357 animate3.setAttribute("to", test.attr_val_3b);
358 animate3.setAttribute("begin", "7s");
359 animate3.setAttribute("dur", "1s");
360 animate3.setAttribute("additive", "sum");
361 df.appendChild(animate3);
363 return df;
366 function is_transform_attr(attr_name) {
367 return attr_name == "transform" ||
368 attr_name == "gradientTransform" ||
369 attr_name == "patternTransform";
372 function get_array_of_list_items(list) {
373 let array = [];
374 for (var i = 0; i < list.numberOfItems; ++i) {
375 array.push(list.getItem(i));
377 return array;
382 * This function tests the SVGXxxList API for the base val list. This means
383 * running tests for the following property and methods:
385 * numberOfItems
386 * clear()
387 * SVGLength initialize(in SVGLength newItem)
388 * SVGLength getItem(in unsigned long index)
389 * SVGLength insertItemBefore(in SVGLength newItem, in unsigned long index)
390 * SVGLength replaceItem(in SVGLength newItem, in unsigned long index)
391 * SVGLength removeItem(in unsigned long index)
392 * SVGLength appendItem(in SVGLength newItem)
394 * @param t A test from the 'tests' array.
396 function run_baseVal_API_tests() {
397 var res, threw;
398 var eventChecker = new MutationEventChecker;
400 for (var t of tests) {
401 // Test .clear():
403 t.element.setAttribute(t.attr_name, t.attr_val_4);
405 is(t.baseVal.numberOfItems, 4,
406 "The " + t.list_type + " object should contain four list items.");
408 eventChecker.watchAttr(t.element, t.attr_name);
409 eventChecker.expect("modify");
410 res = t.baseVal.clear();
412 is(t.baseVal.numberOfItems, 0,
413 "The method " + t.list_type + ".clear() should clear the " + t.list_type +
414 " object.");
415 is(res, undefined,
416 "The method " + t.list_type + ".clear() should not return a value.");
417 ok(t.element.hasAttribute(t.attr_name),
418 "The method " + t.list_type + ".clear() should not remove the attribute.");
419 ok(t.element.getAttribute(t.attr_name) === "",
420 "Cleared " + t.attr_name + " (" + t.list_type + ") but did not get an " +
421 "empty string back.");
423 eventChecker.expect("");
424 t.baseVal.clear();
425 eventChecker.ignoreEvents();
427 // Test empty strings
429 t.element.setAttribute(t.attr_name, "");
430 ok(t.element.getAttribute(t.attr_name) === "",
431 "Set an empty attribute value for " + t.attr_name + " (" + t.list_type +
432 ") but did not get an empty string back.");
434 // Test removed attributes
436 t.element.removeAttribute(t.attr_name);
437 ok(t.element.getAttribute(t.attr_name) === null,
438 "Removed attribute value for " + t.attr_name + " (" + t.list_type +
439 ") but did not get null back.");
440 ok(!t.element.hasAttribute(t.attr_name),
441 "Removed attribute value for " + t.attr_name + " (" + t.list_type +
442 ") but hasAttribute still returns true.");
444 // Test .initialize():
446 t.element.setAttribute(t.attr_name, t.attr_val_4);
448 var item = t.item_constructor();
449 // Our current implementation of 'initialize' for most list types performs
450 // a 'clear' followed by an 'insertItemBefore'. This results in two
451 // modification events being dispatched. SVGStringList however avoids the
452 // additional clear.
453 var expectedModEvents =
454 t.item_type == "DOMString" ? "modify" : "modify modify";
455 eventChecker.expect(expectedModEvents);
456 res = t.baseVal.initialize(item);
457 eventChecker.ignoreEvents();
460 is(t.baseVal.numberOfItems, 1,
461 "The " + t.list_type + " object should contain one list item.");
462 ok(res === item,
463 "The list item returned by " + t.list_type + ".initialize() should be the " +
464 "exact same object as the item that was passed to that method, since " +
465 "the item that was passed to that method did not already belong to a " +
466 "list.");
467 ok(t.baseVal.getItem(0) === item,
468 "The list item at index 0 should be the exact same object as the " +
469 "object that was passed to the " + t.list_type + ".initialize() method, " +
470 "since the item that was passed to that method did not already " +
471 "belong to a list.");
473 t.element.setAttribute(t.attr_name, t.attr_val_4);
475 if (t.item_type != "DOMString") {
476 var old_items = get_array_of_list_items(t.baseVal);
477 item = t.baseVal.getItem(3);
478 res = t.baseVal.initialize(item);
480 ok(res !== item &&
481 t.baseVal.getItem(0) !== item &&
482 t.baseVal.getItem(0) !== old_items[0] &&
483 res === t.baseVal.getItem(0),
484 "The method " + t.list_type + ".initialize() should clone the object that " +
485 "is passed in if that object is already in a list.");
486 // [SVGWG issue] not what the spec currently says
489 item = t.baseVal.getItem(0);
490 res = t.baseVal.initialize(item);
492 ok(res !== item &&
493 t.baseVal.getItem(0) !== item,
494 "The method " + t.list_type + ".initialize() should clone the object that " +
495 "is passed in, even if that object is the only item in that list.");
496 // [SVGWG issue] not what the spec currently says
498 eventChecker.expect("");
499 threw = false;
500 try {
501 t.baseVal.initialize({});
502 } catch (e) {
503 threw = true;
505 ok(threw,
506 "The method " + t.list_type + ".initialize() should throw if passed an " +
507 "object of the wrong type.");
508 eventChecker.ignoreEvents();
511 // Test .insertItemBefore():
513 t.element.setAttribute(t.attr_name, t.attr_val_4);
515 old_items = get_array_of_list_items(t.baseVal);
516 item = t.item_constructor();
517 eventChecker.expect("modify");
518 res = t.baseVal.insertItemBefore(item, 2);
519 eventChecker.ignoreEvents();
521 is(t.baseVal.numberOfItems, 5,
522 "The " + t.list_type + " object should contain five list items.");
523 ok(res === item,
524 "The list item returned by " + t.list_type + ".insertItemBefore() should " +
525 "be the exact same object as the item that was passed to that method, " +
526 "since the item that was passed to that method did not already belong " +
527 "to a list.");
528 ok(t.baseVal.getItem(2) === item,
529 "The list item at index 2 should be the exact same object as the " +
530 "object that was passed to the " + t.list_type + ".insertItemBefore() " +
531 "method, since the item that was passed to that method did not " +
532 "already belong to a list.");
533 ok(t.baseVal.getItem(3) === old_items[2],
534 "The list item that was at index 2 should be at index 3 after " +
535 "inserting a new item at index 2 using the " + t.list_type +
536 ".insertItemBefore() method.");
538 item = t.item_constructor();
539 t.baseVal.insertItemBefore(item, 100);
541 ok(t.baseVal.getItem(5) === item,
542 "When the index passed to the " + t.list_type + ".insertItemBefore() " +
543 "method is out of bounds, the supplied list item should be appended " +
544 "to the list.");
546 item = t.baseVal.getItem(4);
547 res = t.baseVal.insertItemBefore(item, 2);
549 is(t.baseVal.numberOfItems, 7,
550 "The " + t.list_type + " object should contain seven list items.");
551 if (t.item_type != "DOMString") {
552 ok(res !== item &&
553 t.baseVal.getItem(2) !== item &&
554 t.baseVal.getItem(2) !== old_items[2] &&
555 res === t.baseVal.getItem(2),
556 "The method " + t.list_type + ".insertItemBefore() should clone the " +
557 "object that is passed in if that object is already in a list.");
558 // [SVGWG issue] not what the spec currently says
561 item = t.baseVal.getItem(2);
562 res = t.baseVal.insertItemBefore(item, 2);
564 is(t.baseVal.numberOfItems, 8,
565 "The " + t.list_type + " object should contain eight list items.");
566 if (t.item_type != "DOMString") {
567 ok(res !== item &&
568 t.baseVal.getItem(2) !== item,
569 "The method " + t.list_type + ".insertItemBefore() should clone the " +
570 "object that is passed in, even if that object is the item in " +
571 "the list at the index specified.");
572 // [SVGWG issue] not what the spec currently says
574 eventChecker.expect("");
575 threw = false;
576 try {
577 t.baseVal.insertItemBefore({}, 2);
578 } catch (e) {
579 threw = true;
581 ok(threw,
582 "The method " + t.list_type + ".insertItemBefore() should throw if passed " +
583 "an object of the wrong type.");
584 eventChecker.ignoreEvents();
587 // Test .replaceItem():
589 t.element.setAttribute(t.attr_name, t.attr_val_4);
591 old_items = get_array_of_list_items(t.baseVal);
592 item = t.item_constructor();
593 eventChecker.expect("modify");
594 res = t.baseVal.replaceItem(item, 2);
595 eventChecker.ignoreEvents();
597 is(t.baseVal.numberOfItems, 4,
598 "The " + t.list_type + " object should contain four list items.");
599 if (t.item_type != "DOMString") {
600 ok(res === item,
601 "The list item returned by " + t.list_type + ".replaceItem() should be " +
602 "the exact same object as the item that was passed to that method, " +
603 "since the item that was passed to that method did not already belong " +
604 "to a list.");
606 ok(t.baseVal.getItem(2) === item,
607 "The list item at index 2 should be the exact same object as the " +
608 "object that was passed to the " + t.list_type + ".replaceItem() method, " +
609 "since the item that was passed to that method did not already belong " +
610 "to a list.");
611 ok(t.baseVal.getItem(3) === old_items[3],
612 "The list item that was at index 3 should still be at index 3 after " +
613 "the item at index 2 was replaced using the " + t.list_type +
614 ".replaceItem() method.");
616 item = t.item_constructor();
618 eventChecker.expect("");
619 threw = false;
620 try {
621 t.baseVal.replaceItem(item, 100);
622 } catch (e) {
623 threw = true;
625 ok(threw,
626 "The method " + t.list_type + ".replaceItem() should throw if passed " +
627 "an index that is out of bounds.");
628 eventChecker.ignoreEvents();
630 old_items = get_array_of_list_items(t.baseVal);
631 item = t.baseVal.getItem(3);
632 res = t.baseVal.replaceItem(item, 1);
634 is(t.baseVal.numberOfItems, 4,
635 "The " + t.list_type + " object should contain four list items.");
636 if (t.item_type != "DOMString") {
637 ok(res !== item &&
638 t.baseVal.getItem(1) !== item &&
639 t.baseVal.getItem(1) !== old_items[1] &&
640 res === t.baseVal.getItem(1),
641 "The method " + t.list_type + ".replaceItem() should clone the object " +
642 "that is passed in if that object is already in a list.");
643 // [SVGWG issue] not what the spec currently says
646 item = t.baseVal.getItem(1);
647 res = t.baseVal.replaceItem(item, 1);
649 is(t.baseVal.numberOfItems, 4,
650 "The " + t.list_type + " object should contain four list items.");
651 if (t.item_type != "DOMString") {
652 ok(res !== item &&
653 t.baseVal.getItem(1) !== item,
654 "The method " + t.list_type + ".replaceItem() should clone the object " +
655 "that is passed in, even if the object that object and the object " +
656 "that is being replaced are the exact same objects.");
657 // [SVGWG issue] not what the spec currently says
659 threw = false;
660 try {
661 t.baseVal.replaceItem({}, 2);
662 } catch (e) {
663 threw = true;
665 ok(threw,
666 "The method " + t.list_type + ".replaceItem() should throw if passed " +
667 "an object of the wrong type.");
670 // Test .removeItem():
672 t.element.setAttribute(t.attr_name, t.attr_val_4);
674 old_items = get_array_of_list_items(t.baseVal);
675 item = t.baseVal.getItem(2);
676 eventChecker.expect("modify");
677 res = t.baseVal.removeItem(2);
678 eventChecker.ignoreEvents();
680 is(t.baseVal.numberOfItems, 3,
681 "The " + t.list_type + " object should contain three list items.");
682 if (t.item_type != "DOMString") {
683 ok(res === item,
684 "The list item returned by " + t.list_type + ".removeItem() should be the " +
685 "exact same object as the item that was at the specified index.");
687 ok(t.baseVal.getItem(1) === old_items[1],
688 "The list item that was at index 1 should still be at index 1 after " +
689 "the item at index 2 was removed using the " + t.list_type +
690 ".replaceItem() method.");
691 ok(t.baseVal.getItem(2) === old_items[3],
692 "The list item that was at index 3 should still be at index 2 after " +
693 "the item at index 2 was removed using the " + t.list_type +
694 ".replaceItem() method.");
696 eventChecker.expect("");
697 threw = false;
698 try {
699 t.baseVal.removeItem(100);
700 } catch (e) {
701 threw = true;
703 ok(threw,
704 "The method " + t.list_type + ".removeItem() should throw if passed " +
705 "an index that is out of bounds.");
706 eventChecker.ignoreEvents();
708 // Test .appendItem():
710 t.element.setAttribute(t.attr_name, t.attr_val_4);
712 old_items = get_array_of_list_items(t.baseVal);
713 item = t.item_constructor();
714 eventChecker.expect("modify");
715 res = t.baseVal.appendItem(item);
716 eventChecker.ignoreEvents();
718 is(t.baseVal.numberOfItems, 5,
719 "The " + t.list_type + " object should contain five list items.");
720 ok(res === item,
721 "The list item returned by " + t.list_type + ".appendItem() should be the " +
722 "exact same object as the item that was passed to that method, since " +
723 "the item that was passed to that method did not already belong " +
724 "to a list.");
725 ok(t.baseVal.getItem(4) === item,
726 "The last list item should be the exact same object as the object " +
727 "that was passed to the " + t.list_type + ".appendItem() method, since " +
728 "the item that was passed to that method did not already belong to " +
729 "a list.");
730 ok(t.baseVal.getItem(3) === old_items[3],
731 "The list item that was at index 4 should still be at index 4 after " +
732 "appending a new item using the " + t.list_type + ".appendItem() " +
733 "method.");
735 item = t.baseVal.getItem(2);
736 res = t.baseVal.appendItem(item);
738 is(t.baseVal.numberOfItems, 6,
739 "The " + t.list_type + " object should contain six list items.");
740 if (t.item_type != "DOMString") {
741 ok(res !== item &&
742 t.baseVal.getItem(5) !== item &&
743 res === t.baseVal.getItem(5),
744 "The method " + t.list_type + ".appendItem() should clone the object " +
745 "that is passed in if that object is already in a list.");
746 // [SVGWG issue] not what the spec currently says
749 item = t.baseVal.getItem(5);
750 res = t.baseVal.appendItem(item);
752 is(t.baseVal.numberOfItems, 7,
753 "The " + t.list_type + " object should contain seven list items.");
754 if (t.item_type != "DOMString") {
755 ok(res !== item &&
756 t.baseVal.getItem(6) !== item,
757 "The method " + t.list_type + ".appendItem() should clone the object " +
758 "that is passed in, if that object is already the last item in " +
759 "that list.");
760 // [SVGWG issue] not what the spec currently says
762 eventChecker.expect("");
763 threw = false;
764 try {
765 t.baseVal.appendItem({});
766 } catch (e) {
767 threw = true;
769 ok(threw,
770 "The method " + t.list_type + ".appendItem() should throw if passed " +
771 "an object of the wrong type.");
772 eventChecker.ignoreEvents();
775 // Test removal and addition events
777 eventChecker.expect("remove add");
778 t.element.removeAttribute(t.attr_name);
779 t.element.removeAttributeNS(null, t.attr_name);
780 res = t.baseVal.appendItem(item);
781 eventChecker.finish();
787 * This function tests the SVGXxxList API for the anim val list (see also the
788 * comment for test_baseVal_API).
790 function run_animVal_API_tests() {
791 var threw, item;
793 for (var t of tests) {
794 if (!t.animVal)
795 continue; // SVGStringList isn't animatable
797 item = t.item_constructor();
799 t.element.setAttribute(t.attr_name, t.attr_val_4);
801 is(t.animVal.numberOfItems, 4,
802 "The " + t.list_type + " object should contain four list items.");
804 // Test .clear():
806 threw = false;
807 try {
808 t.animVal.clear();
809 } catch (e) {
810 threw = true;
812 ok(threw,
813 "The method " + t.list_type + ".clear() should throw when called on an " +
814 "anim val list, since anim val lists should be readonly.");
816 // Test .getItem():
818 item = t.animVal.getItem(2);
819 ok(item != null && item === t.animVal.getItem(2),
820 "The method " + t.list_type + ".getItem() should work when called on an " +
821 "anim val list, and always return the exact same object.");
823 // .initialize()
825 threw = false;
826 try {
827 t.animVal.initialize(item);
828 } catch (e) {
829 threw = true;
831 ok(threw,
832 "The method " + t.list_type + ".initialize() should throw when called on " +
833 "an anim val list, since anim val lists should be readonly.");
835 // Test .insertItemBefore():
837 threw = false;
838 try {
839 t.animVal.insertItemBefore(item, 2);
840 } catch (e) {
841 threw = true;
843 ok(threw,
844 "The method " + t.list_type + ".insertItemBefore() should throw when " +
845 "called on an anim val list, since anim val lists should be readonly.");
847 // Test .replaceItem():
849 threw = false;
850 try {
851 t.animVal.replaceItem(item, 2);
852 } catch (e) {
853 threw = true;
855 ok(threw,
856 "The method " + t.list_type + ".replaceItem() should throw when called " +
857 "on an anim val list, since anim val lists should be readonly.");
859 // Test .removeItem():
861 threw = false;
862 try {
863 t.animVal.removeItem(2);
864 } catch (e) {
865 threw = true;
867 ok(threw,
868 "The method " + t.list_type + ".removeItem() should throw when called " +
869 "on an anim val list, since anim val lists should be readonly.");
871 // Test .appendItem():
873 threw = false;
874 try {
875 t.animVal.appendItem(item);
876 } catch (e) {
877 threw = true;
879 ok(threw,
880 "The method " + t.list_type + ".appendItem() should throw when called " +
881 "on an anim val list, since anim val lists should be readonly.");
887 * This function runs some basic tests to check the effect of setAttribute()
888 * calls on object identity, without taking SMIL animation into consideration.
890 function run_basic_setAttribute_tests() {
891 for (var t of tests) {
892 // Since the t.prop, t.baseVal and t.animVal objects should never ever
893 // change, we leave testing of them to our caller so that it can check
894 // them after all the other mutations such as SMIL changes.
896 t.element.setAttribute(t.attr_name, t.attr_val_4);
898 ok(t.baseVal.numberOfItems == 4 && t.baseVal.getItem(3) != null,
899 "The length of the " + t.list_type + " object for " + t.bv_path + " should " +
900 "have been set to 4 by the setAttribute() call.");
902 if (t.animVal) {
903 ok(t.baseVal.numberOfItems == t.animVal.numberOfItems,
904 "When no animations are active, the " + t.list_type + " objects for " +
905 t.bv_path + " and " + t.av_path + " should be the same length (4).");
907 ok(t.baseVal !== t.animVal,
908 "The " + t.list_type + " objects for " + t.bv_path + " and " + t.av_path +
909 " should be different objects.");
911 ok(t.baseVal.getItem(0) !== t.animVal.getItem(0),
912 "The " + t.item_type + " list items in the " + t.list_type + " objects for " +
913 t.bv_path + " and " + t.av_path + " should be different objects.");
916 // eslint-disable-next-line no-self-compare
917 ok(t.baseVal.getItem(0) === t.baseVal.getItem(0),
918 "The exact same " + t.item_type + " DOM object should be returned each " +
919 "time the item at a given index in the " + t.list_type + " for " +
920 t.bv_path + " is accessed, given that the index was not made invalid " +
921 "by a change in list length between the successive accesses.");
923 if (t.animVal) {
924 // eslint-disable-next-line no-self-compare
925 ok(t.animVal.getItem(0) === t.animVal.getItem(0),
926 "The exact same " + t.item_type + " DOM object should be returned each " +
927 "time the item at a given index in the " + t.list_type + " for " +
928 t.av_path + " is accessed, given that the index was not made invalid " +
929 "by a change in list length between the successive accesses.");
932 // Test the effect of setting the attribute to new values:
934 t.old_baseVal_items = get_array_of_list_items(t.baseVal);
935 if (t.animVal) {
936 t.old_animVal_items = get_array_of_list_items(t.animVal);
939 t.element.setAttribute(t.attr_name, t.attr_val_3a);
940 t.element.setAttribute(t.attr_name, t.attr_val_5a);
942 ok(t.baseVal.numberOfItems == 5 && t.baseVal.getItem(4) != null,
943 "The length of the " + t.list_type + " object for " + t.bv_path + " should " +
944 "have been set to 5 by the setAttribute() call.");
946 if (t.animVal) {
947 ok(t.baseVal.numberOfItems == t.animVal.numberOfItems,
948 "Since no animations are active, the length of the " + t.list_type + " " +
949 "objects for " + t.bv_path + " and " + t.av_path + " should be the same " +
950 "(5).");
953 if (t.item_type != "DOMString") {
954 ok(t.baseVal.getItem(2) === t.old_baseVal_items[2],
955 "After its attribute changes, list items in the " + t.list_type + " for " +
956 t.bv_path + " that are at indexes that existed prior to the attribute " +
957 "change should be the exact same objects as the objects that were " +
958 "at those indexes prior to the attribute change.");
960 ok(t.baseVal.getItem(3) !== t.old_baseVal_items[3],
961 "After its attribute changes, list items in the " + t.list_type + " for " +
962 t.bv_path + " that are at indexes that did not exist prior to the " +
963 "attribute change should not be the same objects as any objects that " +
964 "were at those indexes at some earlier time.");
967 if (t.animVal) {
968 ok(t.animVal.getItem(2) === t.old_animVal_items[2],
969 "After its attribute changes, list items in the " + t.list_type + " for " +
970 t.av_path + " that are at indexes that existed prior to the attribute " +
971 "change should be the exact same objects as the objects that were " +
972 "at those indexes prior to the attribute change.");
974 ok(t.animVal.getItem(3) !== t.old_animVal_items[3],
975 "After its attribute changes, list items in the " + t.list_type + " for " +
976 t.av_path + " that are at indexes that did not exist prior to the " +
977 "attribute change should not be the same objects as any objects " +
978 "that were at those indexes at some earlier time.");
984 * This function verifies that a list's animVal is kept in sync with its
985 * baseVal, when we add & remove items from the baseVal.
987 function run_list_mutation_tests() {
988 for (var t of tests) {
989 if (t.animVal) {
990 // Test removeItem()
991 // =================
992 // Save second item in baseVal list; then make it the first item, and
993 // check that animVal is updated accordingly.
994 t.element.setAttribute(t.attr_name, t.attr_val_4);
996 var secondVal = t.baseVal.getItem(1);
997 var removedFirstVal = t.baseVal.removeItem(0);
998 t.item_is(t.animVal.getItem(0), secondVal,
999 "animVal for " + t.attr_name + " needs update after first item " +
1000 "removed");
1002 // Repeat with last item
1003 var secondToLastVal = t.baseVal.getItem(1);
1004 var removedLastVal = t.baseVal.removeItem(2);
1006 var threw = false;
1007 try {
1008 t.animVal.getItem(2);
1009 } catch (e) {
1010 threw = true;
1012 ok(threw,
1013 "The method " + t.attr_name + ".animVal.getItem() for previously-final " +
1014 "index should throw after final item is removed from baseVal.");
1016 t.item_is(t.animVal.getItem(1), secondToLastVal,
1017 "animVal for " + t.attr_name + " needs update after last item " +
1018 "removed");
1020 // Test insertItemBefore()
1021 // =======================
1022 // Reset base value, insert value @ start, check that animVal is updated.
1023 t.element.setAttribute(t.attr_name, t.attr_val_3a);
1024 t.baseVal.insertItemBefore(removedLastVal, 0);
1025 t.item_is(t.animVal.getItem(0), removedLastVal,
1026 "animVal for " + t.attr_name + " needs update after insert at " +
1027 "beginning");
1029 // Repeat with insert at end
1030 t.element.setAttribute(t.attr_name, t.attr_val_3a);
1031 t.baseVal.insertItemBefore(removedFirstVal, t.baseVal.numberOfItems);
1032 t.item_is(t.animVal.getItem(t.baseVal.numberOfItems - 1),
1033 removedFirstVal,
1034 "animVal for " + t.attr_name + " needs update after insert at end");
1036 // Test appendItem()
1037 // =================
1038 var dummy = t.item_constructor();
1039 t.baseVal.appendItem(dummy);
1040 t.item_is(t.animVal.getItem(t.baseVal.numberOfItems - 1), dummy,
1041 "animVal for " + t.attr_name + " needs update after appendItem");
1043 // Test clear()
1044 // ============
1045 t.baseVal.clear();
1046 threw = false;
1047 try {
1048 t.animVal.getItem(0);
1049 } catch (e) {
1050 threw = true;
1052 ok(threw,
1053 "The method " + t.attr_name + ".animVal.getItem() should throw after " +
1054 "we've cleared baseVal.");
1056 is(t.animVal.numberOfItems, 0,
1057 "animVal for " + t.attr_name + " should be empty after baseVal cleared");
1059 // Test initialize()
1060 // =================
1061 t.element.setAttribute(t.attr_name, t.attr_val_3a);
1062 t.baseVal.initialize(dummy);
1064 is(t.animVal.numberOfItems, 1,
1065 "animVal for " + t.attr_name + " should have length 1 after initialize");
1066 t.item_is(t.animVal.getItem(0), dummy,
1067 "animVal for " + t.attr_name + " needs update after initialize");
1073 * In this function we run a series of tests at various points along the SMIL
1074 * animation timeline, using SVGSVGElement.setCurrentTime() to move forward
1075 * along the timeline.
1077 * See the comment for create_animate_elements() for details of the animations
1078 * and their timings.
1080 function run_animation_timeline_tests() {
1081 var svg = document.getElementById("svg");
1083 for (var t of tests) {
1084 // Skip if there is no animVal for this test or if it is a transform list
1085 // since these are handled specially
1086 if (!t.animVal || is_transform_attr(t.attr_name))
1087 continue;
1089 svg.setCurrentTime(0); // reset timeline
1091 // Reset attributes before moving along the timeline and triggering SMIL:
1092 t.element.setAttribute(t.attr_name, t.attr_val_4);
1093 t.old_baseVal_items = get_array_of_list_items(t.baseVal);
1094 t.old_animVal_items = get_array_of_list_items(t.animVal);
1097 /** ****************** t = 1s ********************/
1099 svg.setCurrentTime(1); // begin first animation
1101 ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
1102 t.baseVal.getItem(3) === t.old_baseVal_items[3],
1103 "The start of an animation should never affect the " + t.list_type +
1104 " for " + t.bv_path + ", or its list items.");
1106 ok(t.animVal.numberOfItems == 5 && t.animVal.getItem(4) != null,
1107 "The start of the animation should have changed the number of items " +
1108 "in the " + t.list_type + " for " + t.bv_path + " to 5.");
1110 // TODO
1111 ok(t.animVal.getItem(3) === t.old_animVal_items[3],
1112 "When affected by SMIL animation, list items in the " + t.list_type +
1113 " for " + t.bv_path + " that are at indexes that existed prior to the " +
1114 "start of the animation should be the exact same objects as the " +
1115 "objects that were at those indexes prior to the start of the " +
1116 "animation.");
1118 t.old_animVal_items = get_array_of_list_items(t.animVal);
1120 t.element.setAttribute(t.attr_name, t.attr_val_3a);
1122 ok(t.baseVal.numberOfItems == 3 &&
1123 t.baseVal.getItem(2) === t.old_baseVal_items[2],
1124 "Setting the underlying attribute should change the items in the " +
1125 t.list_type + " for " + t.bv_path + ", including when an animation is " +
1126 "in progress.");
1128 ok(t.animVal.numberOfItems == 5 &&
1129 t.animVal.getItem(4) === t.old_animVal_items[4],
1130 "Setting the underlying attribute should not change the " + t.list_type +
1131 " for " + t.bv_path + " when an animation that does not depend on the " +
1132 "base val is in progress.");
1134 t.element.setAttribute(t.attr_name, t.attr_val_4); // reset
1136 t.old_baseVal_items = get_array_of_list_items(t.baseVal);
1137 t.old_animVal_items = get_array_of_list_items(t.animVal);
1140 /** ****************** t = 2s ********************/
1142 svg.setCurrentTime(2); // begin override animation
1144 ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
1145 t.baseVal.getItem(3) === t.old_baseVal_items[3],
1146 "The start of an override animation should never affect the " +
1147 t.list_type + " for " + t.bv_path + ", or its list items.");
1149 is(t.animVal.numberOfItems, 3,
1150 "The start of the override animation should have changed the number " +
1151 "of items in the " + t.list_type + " for " + t.bv_path + " to 3.");
1153 ok(t.animVal.getItem(2) === t.old_animVal_items[2],
1154 "When affected by an override SMIL animation, list items in the " +
1155 t.list_type + " for " + t.bv_path + " that are at indexes that existed " +
1156 "prior to the start of the animation should be the exact same " +
1157 "objects as the objects that were at those indexes prior to the " +
1158 "start of that animation.");
1160 t.old_animVal_items = get_array_of_list_items(t.animVal);
1163 /** ****************** t = 3s ********************/
1165 svg.setCurrentTime(3); // end of override animation
1167 ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
1168 t.baseVal.getItem(3) === t.old_baseVal_items[3],
1169 "The end of an override animation should never affect the " +
1170 t.list_type + " for " + t.bv_path + ", or its list items.");
1172 is(t.animVal.numberOfItems, 5,
1173 "At the end of the override animation, the number of items in the " +
1174 t.list_type + " for " + t.bv_path + " should have reverted to 5.");
1176 ok(t.animVal.getItem(2) === t.old_animVal_items[2],
1177 "At the end of the override animation, list items in the " +
1178 t.list_type + " for " + t.bv_path + " that are at indexes that existed " +
1179 "prior to the end of the animation should be the exact same " +
1180 "objects as the objects that were at those indexes prior to the " +
1181 "end of that animation.");
1183 t.old_animVal_items = get_array_of_list_items(t.animVal);
1186 /** ****************** t = 5s ********************/
1188 svg.setCurrentTime(5); // animation repeat point
1190 ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
1191 t.baseVal.getItem(3) === t.old_baseVal_items[3],
1192 "When a SMIL animation repeats, it should never affect the " +
1193 t.list_type + " for " + t.bv_path + ", or its list items.");
1195 ok(t.animVal.numberOfItems == t.old_animVal_items.length &&
1196 t.animVal.getItem(4) === t.old_animVal_items[4],
1197 "When an animation repeats, the list items that are at a given " +
1198 "index in the " + t.list_type + " for " + t.av_path + " should be the exact " +
1199 "same objects as were at that index before the repeat occurred.");
1202 /** ****************** t = 6s ********************/
1204 svg.setCurrentTime(6); // inside animation repeat
1206 ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
1207 t.baseVal.getItem(3) === t.old_baseVal_items[3],
1208 "When a SMIL animation repeats, it should never affect the " +
1209 t.list_type + " for " + t.bv_path + ", or its list items.");
1211 ok(t.animVal.numberOfItems == t.old_animVal_items.length &&
1212 t.animVal.getItem(4) === t.old_animVal_items[4],
1213 "When an animation repeats, the list items that are at a given " +
1214 "index in the " + t.list_type + " for " + t.av_path + " should be the exact " +
1215 "same objects as were at that index before the repeat occurred.");
1218 /** ****************** t = 7s ********************/
1220 svg.setCurrentTime(7); // start of additive="sum" animation
1222 ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
1223 t.baseVal.getItem(3) === t.old_baseVal_items[3],
1224 "When a new SMIL animation starts and should blend with an " +
1225 "underlying animation, it should never affect the " +
1226 t.list_type + " for " + t.bv_path + ", or its list items.");
1228 if (t.list_type == "SVGLengthList") {
1229 // Length lists are a special case where it makes sense to allow shorter
1230 // lists to be composed on top of longer lists (but not necessarily vice
1231 // versa - see comment below).
1233 ok(t.animVal.numberOfItems == t.old_animVal_items.length &&
1234 t.animVal.getItem(3) === t.old_animVal_items[3],
1235 'When an animation with additive="sum" is added on top of an ' +
1236 "existing animation that has more list items, the length of the " +
1237 t.list_type + " for " + t.av_path + " should not change.");
1238 } else {
1240 /* TODO
1241 ok(false,
1242 'Decide what to do here - see ' +
1243 'https://bugzilla.mozilla.org/show_bug.cgi?id=573716 - we ' +
1244 'probably should be discarding any animation sandwich layers from ' +
1245 'a layer that fails to add, on up.');
1248 // In other words, we wouldn't need the if-else check here.
1252 // XXX what if the higher priority sandwich layer has *more* list items
1253 // than the underlying animation? In that case we would need to
1254 // distinguish between different SVGLengthList attributes, since although
1255 // all SVGLengthList attributes allow a short list to be added to longer
1256 // list, they do not all allow a longer list to be added to shorter list.
1257 // Specifically that would not be good for 'x' and 'y' on <text> since
1258 // lengths there are not naturally zero. See the comment in
1259 // SVGLengthListSMILAttr::Add().
1262 /** ****************** t = 13s ********************/
1264 svg.setCurrentTime(13); // all animations have finished, but one is frozen
1266 ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
1267 t.baseVal.getItem(3) === t.old_baseVal_items[3],
1268 "When a SMIL animation ends, it should never affect the " +
1269 t.list_type + " for " + t.bv_path + ", or its list items.");
1271 is(t.animVal.numberOfItems, 5,
1272 "Even though all SMIL animation have finished, the number " +
1273 "of items in the " + t.list_type + " for " + t.av_path +
1274 " should still be more than the same as the number of items in " +
1275 t.bv_path + " since one of the animations is still frozen.");
1277 var expected = t.attr_val_5b_firstItem_x3_constructor(t.item_constructor);
1278 t.item_is(t.animVal.getItem(0), expected,
1279 'animation with accumulate="sum" and repeatCount="3" for attribute "' +
1280 t.attr_name + '" should end up at 3x the "to" value.');
1282 // Unfreeze frozen animation (removing its effects)
1283 var frozen_animate_element =
1284 t.element.querySelector('animate[fill][attributeName="' + t.attr_name + '"]');
1285 frozen_animate_element.removeAttribute("fill");
1287 ok(t.animVal.numberOfItems == t.baseVal.numberOfItems,
1288 "Once all SMIL animation have finished and been un-frozen, the number " +
1289 "of items in the " + t.list_type + " for " + t.av_path +
1290 " should be the same as the number of items in " + t.bv_path + ".");
1292 ok(t.animVal.getItem(2) === t.old_animVal_items[2],
1293 "Even after an animation finishes and is un-frozen, the list items " +
1294 "that are at a given index in the " + t.list_type + " for " + t.av_path +
1295 " should be the exact same objects as were at that index before the " +
1296 "end and unfreezing of the animation occurred.");
1301 function run_tests() {
1302 // Initialize each test object with some useful properties, and create their
1303 // 'animate' elements. Note that 'prop' and 'animVal' may be null.
1304 for (let t of tests) {
1305 t.element = document.getElementById(t.target_element_id);
1306 t.prop = t.prop_name ? t.element[t.prop_name] : null;
1307 t.baseVal = ( t.prop || t.element )[t.bv_name];
1308 t.animVal = t.av_name ? ( t.prop || t.element )[t.av_name] : null;
1309 t.bv_path = t.el_type + "." +
1310 (t.prop ? t.prop_name + "." : "") +
1311 t.bv_name; // e.g. 'SVGTextElement.x.baseVal'
1312 if (t.animVal) {
1313 t.av_path = t.el_type + "." +
1314 (t.prop ? t.prop_name + "." : "") +
1315 t.av_name;
1317 t.prop_type = t.prop_type || null;
1319 // use fallback 'is' function, if none was provided.
1320 if (!t.item_is) {
1321 t.item_is = function(itemA, itemB, message) {
1322 ok(typeof(itemA.value) != "undefined" &&
1323 typeof(itemB.value) != "undefined",
1324 "expecting value property");
1325 is(itemA.value, itemB.value, message);
1329 if (t.animVal) {
1330 t.element.appendChild(create_animate_elements(t));
1334 // Run the major test groups:
1336 run_baseVal_API_tests();
1337 run_animVal_API_tests();
1338 run_basic_setAttribute_tests();
1339 run_list_mutation_tests();
1340 run_animation_timeline_tests();
1342 // After all the other test manipulations, we check that the following
1343 // objects have still not changed, since they never should:
1345 for (let t of tests) {
1346 if (t.prop) {
1347 ok(t.prop === t.element[t.prop_name],
1348 "The same " + t.prop_type + " object should ALWAYS be returned for " +
1349 t.el_type + "." + t.prop_name + " each time it is accessed.");
1352 ok(t.baseVal === ( t.prop || t.element )[t.bv_name],
1353 "The same " + t.list_type + " object should ALWAYS be returned for " +
1354 t.el_type + "." + t.prop_name + "." + t.bv_name + " each time it is accessed.");
1356 if (t.animVal) {
1357 ok(t.animVal === ( t.prop || t.element )[t.av_name],
1358 "The same " + t.list_type + " object should ALWAYS be returned for " +
1359 t.el_type + "." + t.prop_name + "." + t.av_name + " each time it is accessed.");
1363 SimpleTest.finish();
1366 window.addEventListener("load",
1367 () => SpecialPowers.pushPrefEnv({"set": [["dom.mutation_events.enabled", true]]}, run_tests));
1370 </script>
1371 </pre>
1372 </body>
1373 </html>