summaryrefslogtreecommitdiffstats
path: root/dom/svg/test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 19:33:14 +0000
commit36d22d82aa202bb199967e9512281e9a53db42c9 (patch)
tree105e8c98ddea1c1e4784a60a5a6410fa416be2de /dom/svg/test
parentInitial commit. (diff)
downloadfirefox-esr-upstream.tar.xz
firefox-esr-upstream.zip
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--dom/svg/test/MutationEventChecker.js278
-rw-r--r--dom/svg/test/a_href_destination.svg3
-rw-r--r--dom/svg/test/a_href_helper_01.svg5
-rw-r--r--dom/svg/test/a_href_helper_02_03.svg5
-rw-r--r--dom/svg/test/a_href_helper_04.svg6
-rw-r--r--dom/svg/test/a_href_helper_05.svg5
-rw-r--r--dom/svg/test/a_href_helper_06.svg5
-rw-r--r--dom/svg/test/a_href_helper_07.svg6
-rw-r--r--dom/svg/test/animated-svg-image-helper.html3
-rw-r--r--dom/svg/test/animated-svg-image-helper.svg3
-rw-r--r--dom/svg/test/bbox-helper.svg42
-rw-r--r--dom/svg/test/bounds-helper.svg86
-rw-r--r--dom/svg/test/dataTypes-helper.svg20
-rw-r--r--dom/svg/test/fragments-helper.svg4
-rw-r--r--dom/svg/test/getBBox-method-helper.svg304
-rw-r--r--dom/svg/test/getCTM-helper.svg47
-rw-r--r--dom/svg/test/getSubStringLength-helper.svg7
-rw-r--r--dom/svg/test/matrixUtils.js78
-rw-r--r--dom/svg/test/mochitest.ini113
-rw-r--r--dom/svg/test/object-delayed-intrinsic-size.sjs24
-rw-r--r--dom/svg/test/pointer-events.js328
-rw-r--r--dom/svg/test/scientific-helper.svg5
-rw-r--r--dom/svg/test/selectSubString-helper.svg7
-rw-r--r--dom/svg/test/switch-helper.svg12
-rw-r--r--dom/svg/test/tearoff_with_cc_helper.html36
-rw-r--r--dom/svg/test/test_SVGLengthList-2.xhtml64
-rw-r--r--dom/svg/test/test_SVGLengthList.xhtml158
-rw-r--r--dom/svg/test/test_SVGMatrix.xhtml180
-rw-r--r--dom/svg/test/test_SVGNumberList.xhtml74
-rw-r--r--dom/svg/test/test_SVGPointList.xhtml129
-rw-r--r--dom/svg/test/test_SVGStringList.xhtml118
-rw-r--r--dom/svg/test/test_SVGStyleElement.xhtml33
-rw-r--r--dom/svg/test/test_SVGTransformList.xhtml461
-rw-r--r--dom/svg/test/test_SVGTransformListAddition.xhtml185
-rw-r--r--dom/svg/test/test_SVG_namespace_ids.html113
-rw-r--r--dom/svg/test/test_SVGxxxList.xhtml1372
-rw-r--r--dom/svg/test/test_SVGxxxListIndexing.xhtml93
-rw-r--r--dom/svg/test/test_a_href_01.xhtml96
-rw-r--r--dom/svg/test/test_a_href_02.xhtml37
-rw-r--r--dom/svg/test/test_animLengthObjectIdentity.xhtml86
-rw-r--r--dom/svg/test/test_animLengthReadonly.xhtml219
-rw-r--r--dom/svg/test/test_animLengthUnits.xhtml125
-rw-r--r--dom/svg/test/test_bbox-changes.xhtml77
-rw-r--r--dom/svg/test/test_bbox-with-invalid-viewBox.xhtml38
-rw-r--r--dom/svg/test/test_bbox.xhtml91
-rw-r--r--dom/svg/test/test_bounds.html317
-rw-r--r--dom/svg/test/test_bug1426594.html34
-rw-r--r--dom/svg/test/test_bug872812.html29
-rw-r--r--dom/svg/test/test_dataTypes.html377
-rw-r--r--dom/svg/test/test_dataTypesModEvents.html257
-rw-r--r--dom/svg/test/test_fragments.html92
-rw-r--r--dom/svg/test/test_getBBox-method.html248
-rw-r--r--dom/svg/test/test_getCTM.html124
-rw-r--r--dom/svg/test/test_getElementById.xhtml65
-rw-r--r--dom/svg/test/test_getPathSegListAtLength_with_d_property.html55
-rw-r--r--dom/svg/test/test_getSubStringLength.xhtml91
-rw-r--r--dom/svg/test/test_getTotalLength.xhtml57
-rw-r--r--dom/svg/test/test_hit-testing-and-viewbox.xhtml81
-rw-r--r--dom/svg/test/test_lang.xhtml90
-rw-r--r--dom/svg/test/test_length.xhtml58
-rw-r--r--dom/svg/test/test_lengthParsing.html82
-rw-r--r--dom/svg/test/test_markerOrient.xhtml110
-rw-r--r--dom/svg/test/test_non-scaling-stroke.html52
-rw-r--r--dom/svg/test/test_nonAnimStrings.xhtml78
-rw-r--r--dom/svg/test/test_object-delayed-intrinsic-size.html39
-rw-r--r--dom/svg/test/test_onerror.xhtml35
-rw-r--r--dom/svg/test/test_onload.xhtml35
-rw-r--r--dom/svg/test/test_onload2.xhtml48
-rw-r--r--dom/svg/test/test_pairParsing.html43
-rw-r--r--dom/svg/test/test_pathAnimInterpolation.xhtml341
-rw-r--r--dom/svg/test/test_pointAtLength.xhtml49
-rw-r--r--dom/svg/test/test_pointer-events-1a.xhtml27
-rw-r--r--dom/svg/test/test_pointer-events-1b.xhtml27
-rw-r--r--dom/svg/test/test_pointer-events-2.xhtml71
-rw-r--r--dom/svg/test/test_pointer-events-3.xhtml54
-rw-r--r--dom/svg/test/test_pointer-events-4.xhtml109
-rw-r--r--dom/svg/test/test_pointer-events-6.xhtml69
-rw-r--r--dom/svg/test/test_pointer-events-7.xhtml65
-rw-r--r--dom/svg/test/test_scientific.html82
-rw-r--r--dom/svg/test/test_selectSubString.xhtml74
-rw-r--r--dom/svg/test/test_stroke-hit-testing.xhtml66
-rw-r--r--dom/svg/test/test_stroke-linecap-hit-testing.xhtml45
-rw-r--r--dom/svg/test/test_style_sheet.html27
-rw-r--r--dom/svg/test/test_switch.xhtml99
-rw-r--r--dom/svg/test/test_tabindex.html103
-rw-r--r--dom/svg/test/test_tearoff_with_cc.html48
-rw-r--r--dom/svg/test/test_text.html189
-rw-r--r--dom/svg/test/test_text_2.html63
-rw-r--r--dom/svg/test/test_text_dirty.html47
-rw-r--r--dom/svg/test/test_text_lengthAdjust.html106
-rw-r--r--dom/svg/test/test_text_scaled.html135
-rw-r--r--dom/svg/test/test_text_selection.html139
-rw-r--r--dom/svg/test/test_text_update.html31
-rw-r--r--dom/svg/test/test_transform.xhtml190
-rw-r--r--dom/svg/test/test_transformParsing.html103
-rw-r--r--dom/svg/test/test_use_with_hsts.html132
-rw-r--r--dom/svg/test/test_valueAsString.xhtml64
-rw-r--r--dom/svg/test/test_valueLeaks.xhtml84
-rw-r--r--dom/svg/test/test_viewBox.html86
-rw-r--r--dom/svg/test/test_viewport.html59
-rw-r--r--dom/svg/test/text-helper-scaled.svg8
-rw-r--r--dom/svg/test/text-helper-selection.svg23
-rw-r--r--dom/svg/test/text-helper.svg19
-rw-r--r--dom/svg/test/use-with-hsts-helper.html30
-rw-r--r--dom/svg/test/use-with-hsts-helper.html^headers^2
-rw-r--r--dom/svg/test/viewport-helper.svg26
106 files changed, 10570 insertions, 0 deletions
diff --git a/dom/svg/test/MutationEventChecker.js b/dom/svg/test/MutationEventChecker.js
new file mode 100644
index 0000000000..860b8da567
--- /dev/null
+++ b/dom/svg/test/MutationEventChecker.js
@@ -0,0 +1,278 @@
+// Helper class to check DOM MutationEvents
+//
+// Usage:
+//
+// * Create a new event checker:
+// var eventChecker = new MutationEventChecker;
+// * Set the attribute to watch
+// eventChecker.watchAttr(<DOM element>, "<attribute name>");
+// * Set the events to expect (0..n)
+// eventChecker.expect("add", "modify");
+// OR
+// eventChecker.expect("add modify");
+// OR
+// eventChecker.expect(MutationEvent.ADDITION, MutationEvent.MODIFICATION);
+//
+// An empty string or empty set of arguments is also fine as a way of checking
+// that all expected events have been received and indicating no events are
+// expected from the following code, e.g.
+//
+// eventChecker.expect("");
+// // changes that are not expected to generate events
+// eventChecker.expect("modify");
+// // change that is expected to generate an event
+// ...
+//
+// * Either finish listening or set the next attribute to watch
+// eventChecker.finish();
+// eventChecker.watchAttr(element, "nextAttribute");
+//
+// In either case a check is performed that all expected events have been
+// received.
+//
+// * Event checking can be temporarily disabled with ignoreEvents(). The next
+// call to expect() will cause it to resume.
+
+function MutationEventChecker() {
+ this.expectedEvents = [];
+
+ this.watchAttr = function (element, attr) {
+ if (this.attr) {
+ this.finish();
+ }
+
+ this.expectedEvents = [];
+ this.element = element;
+ this.attr = attr;
+ this.oldValue = element.getAttribute(attr);
+ this.giveUp = false;
+ this.ignore = false;
+
+ this.element.addEventListener("DOMAttrModified", this._listener);
+ };
+
+ this.expect = function () {
+ if (this.giveUp) {
+ return;
+ }
+
+ ok(
+ !this.expectedEvents.length,
+ "Expecting new events for " +
+ this.attr +
+ " but the following previously expected events have still not been " +
+ "received: " +
+ this._stillExpecting()
+ );
+ if (this.expectedEvents.length) {
+ this.giveUp = true;
+ return;
+ }
+
+ this.ignore = false;
+
+ if (!arguments.length || (arguments.length == 1 && arguments[0] == "")) {
+ return;
+ }
+
+ // Turn arguments object into an array
+ var args = Array.prototype.slice.call(arguments);
+ // Check for whitespace separated keywords
+ if (
+ args.length == 1 &&
+ typeof args[0] === "string" &&
+ args[0].indexOf(" ") > 0
+ ) {
+ args = args[0].split(" ");
+ }
+ // Convert strings to event Ids
+ this.expectedEvents = args.map(this._argToEventId);
+ };
+
+ // Temporarily disable event checking
+ this.ignoreEvents = function () {
+ // Check all events have been received
+ ok(
+ this.giveUp || !this.expectedEvents.length,
+ "Going to ignore subsequent events on " +
+ this.attr +
+ " attribute, but we're still expecting the following events: " +
+ this._stillExpecting()
+ );
+
+ this.ignore = true;
+ };
+
+ this.finish = function () {
+ // Check all events have been received
+ ok(
+ this.giveUp || !this.expectedEvents.length,
+ "Finishing listening to " +
+ this.attr +
+ " attribute, but we're still expecting the following events: " +
+ this._stillExpecting()
+ );
+
+ this.element.removeEventListener("DOMAttrModified", this._listener);
+ this.attr = "";
+ };
+
+ this._receiveEvent = function (e) {
+ if (this.giveUp || this.ignore) {
+ this.oldValue = e.newValue;
+ return;
+ }
+
+ // Make sure we're expecting something at all
+ if (!this.expectedEvents.length) {
+ ok(
+ false,
+ "Unexpected " +
+ this._eventToName(e.attrChange) +
+ " event when none expected on " +
+ this.attr +
+ " attribute."
+ );
+ return;
+ }
+
+ var expectedEvent = this.expectedEvents.shift();
+
+ // Make sure we got the event we expected
+ if (e.attrChange != expectedEvent) {
+ ok(
+ false,
+ "Unexpected " +
+ this._eventToName(e.attrChange) +
+ " on " +
+ this.attr +
+ " attribute. Expected " +
+ this._eventToName(expectedEvent) +
+ " (followed by: " +
+ this._stillExpecting() +
+ ")"
+ );
+ // If we get events out of sequence, it doesn't make sense to do any
+ // further testing since we don't really know what to expect
+ this.giveUp = true;
+ return;
+ }
+
+ // Common param checking
+ is(
+ e.target,
+ this.element,
+ "Unexpected node for mutation event on " + this.attr + " attribute"
+ );
+ is(e.attrName, this.attr, "Unexpected attribute name for mutation event");
+
+ // Don't bother testing e.relatedNode since Attr nodes are on the way
+ // out anyway (but then, so are mutation events...)
+
+ // Event-specific checking
+ if (e.attrChange == MutationEvent.MODIFICATION) {
+ ok(
+ this.element.hasAttribute(this.attr),
+ "Attribute not set after modification"
+ );
+ is(
+ e.prevValue,
+ this.oldValue,
+ "Unexpected old value for modification to " + this.attr + " attribute"
+ );
+ isnot(
+ e.newValue,
+ this.oldValue,
+ "Unexpected new value for modification to " + this.attr + " attribute"
+ );
+ } else if (e.attrChange == MutationEvent.REMOVAL) {
+ ok(!this.element.hasAttribute(this.attr), "Attribute set after removal");
+ is(
+ e.prevValue,
+ this.oldValue,
+ "Unexpected old value for removal of " + this.attr + " attribute"
+ );
+ // DOM 3 Events doesn't say what value newValue will be for a removal
+ // event but generally empty strings are used for other events when an
+ // attribute isn't relevant
+ ok(
+ e.newValue === "",
+ "Unexpected new value for removal of " + this.attr + " attribute"
+ );
+ } else if (e.attrChange == MutationEvent.ADDITION) {
+ ok(
+ this.element.hasAttribute(this.attr),
+ "Attribute not set after addition"
+ );
+ // DOM 3 Events doesn't say what value prevValue will be for an addition
+ // event but generally empty strings are used for other events when an
+ // attribute isn't relevant
+ ok(
+ e.prevValue === "",
+ "Unexpected old value for addition of " + this.attr + " attribute"
+ );
+ ok(
+ typeof e.newValue == "string" && e.newValue !== "",
+ "Unexpected new value for addition of " + this.attr + " attribute"
+ );
+ } else {
+ ok(false, "Unexpected mutation event type: " + e.attrChange);
+ this.giveUp = true;
+ }
+ this.oldValue = e.newValue;
+ };
+ this._listener = this._receiveEvent.bind(this);
+
+ this._stillExpecting = function () {
+ if (!this.expectedEvents.length) {
+ return "(nothing)";
+ }
+ var eventNames = [];
+ for (var i = 0; i < this.expectedEvents.length; i++) {
+ eventNames.push(this._eventToName(this.expectedEvents[i]));
+ }
+ return eventNames.join(", ");
+ };
+
+ this._eventToName = function (evtId) {
+ switch (evtId) {
+ case MutationEvent.MODIFICATION:
+ return "modification";
+ case MutationEvent.ADDITION:
+ return "addition";
+ case MutationEvent.REMOVAL:
+ return "removal";
+ }
+ return "Unknown MutationEvent Type";
+ };
+
+ this._argToEventId = function (arg) {
+ if (typeof arg === "number") {
+ return arg;
+ }
+
+ if (typeof arg !== "string") {
+ ok(false, "Unexpected event type: " + arg);
+ return 0;
+ }
+
+ switch (arg.toLowerCase()) {
+ case "mod":
+ case "modify":
+ case "modification":
+ return MutationEvent.MODIFICATION;
+
+ case "add":
+ case "addition":
+ return MutationEvent.ADDITION;
+
+ case "removal":
+ case "remove":
+ return MutationEvent.REMOVAL;
+
+ default:
+ ok(false, "Unexpected event name: " + arg);
+ return 0;
+ }
+ };
+}
diff --git a/dom/svg/test/a_href_destination.svg b/dom/svg/test/a_href_destination.svg
new file mode 100644
index 0000000000..43e4c812f4
--- /dev/null
+++ b/dom/svg/test/a_href_destination.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+ <rect width="100%" height="100%" fill="green"/>
+</svg>
diff --git a/dom/svg/test/a_href_helper_01.svg b/dom/svg/test/a_href_helper_01.svg
new file mode 100644
index 0000000000..8f33cea404
--- /dev/null
+++ b/dom/svg/test/a_href_helper_01.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <a id="a" xlink:href="a_href_destination.svg">
+ <rect width="100%" height="100%"/>
+ </a>
+</svg>
diff --git a/dom/svg/test/a_href_helper_02_03.svg b/dom/svg/test/a_href_helper_02_03.svg
new file mode 100644
index 0000000000..af4b7e2736
--- /dev/null
+++ b/dom/svg/test/a_href_helper_02_03.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <a id="a" xlink:href="initial.svg">
+ <rect width="100%" height="100%"/>
+ </a>
+</svg>
diff --git a/dom/svg/test/a_href_helper_04.svg b/dom/svg/test/a_href_helper_04.svg
new file mode 100644
index 0000000000..50aca28898
--- /dev/null
+++ b/dom/svg/test/a_href_helper_04.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <a id="a" xlink:href="initial.svg">
+ <set attributeName="xlink:href" to="a_href_destination.svg"/>
+ <rect width="100%" height="100%"/>
+ </a>
+</svg>
diff --git a/dom/svg/test/a_href_helper_05.svg b/dom/svg/test/a_href_helper_05.svg
new file mode 100644
index 0000000000..8600960b8d
--- /dev/null
+++ b/dom/svg/test/a_href_helper_05.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <a id="a" href="a_href_destination.svg" xlink:href="a_href_fake_destination.svg">
+ <rect width="100%" height="100%"/>
+ </a>
+</svg>
diff --git a/dom/svg/test/a_href_helper_06.svg b/dom/svg/test/a_href_helper_06.svg
new file mode 100644
index 0000000000..9414c39892
--- /dev/null
+++ b/dom/svg/test/a_href_helper_06.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+ <a id="a" href="initial.svg">
+ <rect width="100%" height="100%"/>
+ </a>
+</svg>
diff --git a/dom/svg/test/a_href_helper_07.svg b/dom/svg/test/a_href_helper_07.svg
new file mode 100644
index 0000000000..9fe1d23f7f
--- /dev/null
+++ b/dom/svg/test/a_href_helper_07.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+ <a id="a" href="initial.svg">
+ <set attributeName="href" to="a_href_destination.svg"/>
+ <rect width="100%" height="100%"/>
+ </a>
+</svg>
diff --git a/dom/svg/test/animated-svg-image-helper.html b/dom/svg/test/animated-svg-image-helper.html
new file mode 100644
index 0000000000..94af2098bc
--- /dev/null
+++ b/dom/svg/test/animated-svg-image-helper.html
@@ -0,0 +1,3 @@
+<html>
+ <img src="animated-svg-image-helper.svg">
+</html>
diff --git a/dom/svg/test/animated-svg-image-helper.svg b/dom/svg/test/animated-svg-image-helper.svg
new file mode 100644
index 0000000000..5f6f564e92
--- /dev/null
+++ b/dom/svg/test/animated-svg-image-helper.svg
@@ -0,0 +1,3 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+ <set attributeName="font-size" to="50"/>
+</svg>
diff --git a/dom/svg/test/bbox-helper.svg b/dom/svg/test/bbox-helper.svg
new file mode 100644
index 0000000000..40aa01cb2a
--- /dev/null
+++ b/dom/svg/test/bbox-helper.svg
@@ -0,0 +1,42 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg">
+ <g transform="scale(0.5)">
+ <foreignObject id="fO" x="10" y="10" width="100" height="100"/>
+ <image id="i" x="10" y="10" width="100" height="100"/>
+ </g>
+ <text id="b" x="100" y="100">abcdef</text>
+ <text id="a" x="20" y="30">a</text>
+ <text id="y" x="20" y="40">y</text>
+ <text id="tspantext1">
+ <tspan id="tspan1" x="100" y="100">abcdef</tspan>
+ </text>
+ <text id="tspantext2" x="100" y="100">ABCEDF<tspan id="tspan2">ABCEDF</tspan></text>
+ <text id="text" x="20" y="60">text</text>
+ <!-- &#8206; is the same as the HTML &lrm; -->
+ <text id="lrmText" x="20" y="60">&#8206;text</text>
+ <g id="v">
+ <circle cx="100" cy="50" r="5"/>
+ <path d="M 100,100 L 100,200"/>
+ </g>
+ <g id="h">
+ <circle cx="200" cy="50" r="5"/>
+ <path d="M 200,100 L 300,100"/>
+ </g>
+ <g id="e">
+ <!-- empty container should not affect parent's bbox -->
+ <g/>
+ <!-- neither should a path, -->
+ <path/>
+ <!-- a polygon -->
+ <polygon/>
+ <!-- or an empty text element -->
+ <text x="185" y="25"/>
+ <circle cx="100" cy="100" r="5"/>
+ <g/>
+ <circle cx="100" cy="100" r="5"/>
+ <g/>
+ </g>
+ <use x="100" y="100" id="use_v" href="#v"/>
+ <use x="100" y="100" id="use_h" href="#h"/>
+ <use x="100" y="100" id="use_e" href="#e"/>
+</svg>
diff --git a/dom/svg/test/bounds-helper.svg b/dom/svg/test/bounds-helper.svg
new file mode 100644
index 0000000000..2d526f533c
--- /dev/null
+++ b/dom/svg/test/bounds-helper.svg
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="750"
+ xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
+ <style type="text/css">
+text { font: 20px monospace; }
+#css-trans-rect-2 { transform: scaleX(2) }
+ </style>
+
+<g id="g">
+ <svg id="svg1" x="10" y="10" width="25" height="30"/>
+ <svg id="svg2" width="1" height="1" overflow="visible">
+ <rect width="2" height="2" fill="yellow"/>
+ </svg>
+ <svg id="svg3" width="1" height="1" overflow="hidden">
+ <rect width="2" height="2" fill="yellow"/>
+ </svg>
+ <symbol>
+ <rect id="use-test" width="50" height="10"/>
+ </symbol>
+ <use id="use1" href="#use-test" x="100" y="50" fill="yellow"/>
+ <a id="a-use">
+ <use href="#use-test" x="100" y="50" fill="yellow"/>
+ </a>
+ <text id="text1" x="25" y="25">abc</text>
+ <text id="text1a" x="85" y="25" stroke="black" stroke-width="4">abc</text>
+ <text id="text1b" x="25" y="25" style="text-shadow: 5px 5px 3em rgba(0, 0, 0, 0.98);">abc</text>
+ <rect id="rect1" x="50" y="50" width="50" height="50" fill="green"/>
+ <rect id="rect1a" x="50" y="50" width="50" height="50" fill="none" stroke-width="4" stroke="yellow"/>
+ <text id="text2" x="125" y="25">abc</text>
+ <text id="text2a" x="185" y="25" stroke="black" stroke-width="10">abc</text>
+ <g transform="rotate(45 175 75)">
+ <rect id="rect2" x="150" y="50" width="50" height="50" fill="yellow"/>
+ <rect id="rect2a" x="150" y="50" width="50" height="50" fill="none" stroke-width="4" stroke="blue"/>
+ <text id="text3" x="150" y="50" text-anchor="middle">abc</text>
+ </g>
+ <g transform="scale(2)">
+ <rect id="rect3" x="25" y="80" width="50" height="50" fill="green"/>
+ <rect id="rect3a" x="25" y="80" width="50" height="50" fill="none" stroke-width="4" stroke="blue"/>
+ <rect id="rect3b" vector-effect="non-scaling-stroke" x="100" y="100" width="25" height="25" fill="orange" stroke-width="4" stroke="yellow"/>
+ <image id="i" x="10" y="10" width="100" height="100"/>
+ </g>
+ <g transform="scale(2) rotate(45 175 75)">
+ <rect id="rect4" x="150" y="50" width="50" height="50" fill="yellow"/>
+ <rect id="rect4a" x="150" y="50" width="50" height="50" fill="none" stroke-width="4" stroke="blue"/>
+ </g>
+ <text id="text4" x="185" y="25"/>
+ <g id="g2">
+ <rect x="100" y="100" width="50" height="50" fill="pink"/>
+ <text x="200" y="200"/>
+ </g>
+ <circle id="nonScalingStrokedCircle1" cx="0" cy="0" r="10"
+ transform="translate(45 130) scale(3 -2)"
+ fill="none" stroke="gray" stroke-width="10"
+ vector-effect="non-scaling-stroke"/>
+ <ellipse id="nonScalingStrokedEllipse1" cx="20" cy="-10" rx="5" ry="5"
+ transform="matrix(0 3 -2 0 0 0)"
+ fill="none" stroke="steelblue" stroke-width="10"
+ vector-effect="non-scaling-stroke" />
+ <line id="nonScalingStrokedLine1" x1="120" y1="5" x2="120" y2="10"
+ transform="scale(2 3)"
+ stroke-width="10" stroke-linecap="round" stroke="orange"
+ vector-effect="non-scaling-stroke" />
+ <line id="nonScalingStrokedLine2" x1="130" y1="5" x2="140" y2="5"
+ transform="rotate(45 260 15) scale(2 3)"
+ stroke-width="10" stroke-linecap="square" stroke="crimson"
+ vector-effect="non-scaling-stroke" />
+ <line id="nonScalingStrokedLine3" x1="140" y1="5" x2="150" y2="5"
+ transform="rotate(45 280 15) scale(2 3)"
+ stroke-width="10" stroke-linecap="butt" stroke="indigo"
+ vector-effect="non-scaling-stroke" />
+
+ <marker id="marker1" markerWidth="100" markerHeight="100"
+ refX="0" refY="50" markerUnits="userSpaceOnUse">
+ <line x1="0" y1="50" x2="50" y2="100" stroke="aqua" stroke-width="20"
+ transform="rotate(-45 0 50)" />
+ </marker>
+ <line id="shapeWithMarker1" x1="160" y1="130" x2="170" y2="130"
+ stroke="black" stroke-width="3" marker-end="url(#marker1)"/>
+
+ <line id="rotatedLine1" x1="160" y1="150" x2="180" y2="170"
+ stroke="darkmagenta" stroke-width="10"
+ transform="rotate(-45 160 150)" />
+
+ <rect id="css-trans-rect-2" x="5" y="5" width="6" height="6"></rect>
+</g>
+</svg>
diff --git a/dom/svg/test/dataTypes-helper.svg b/dom/svg/test/dataTypes-helper.svg
new file mode 100644
index 0000000000..e2a17e31c3
--- /dev/null
+++ b/dom/svg/test/dataTypes-helper.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="750">
+ <defs>
+ <filter id="filter">
+ <!-- <boolean> (preserveAlpha) -->
+ <!-- <enum> (edgeMode) -->
+ <!-- <number> (divisor) -->
+ <!-- <integer> (targetX) -->
+ <!-- <integer-optional-integer> (order) -->
+ <!-- <string> (result) -->
+ <feConvolveMatrix id="convolve"/>
+ <!-- <number-optional-number> (stdDeviation) -->
+ <feGaussianBlur id="blur"/>
+ </filter>
+ <!-- <angle> (orient) -->
+ <!-- <length> (markerWidth) -->
+ <!-- <preserveAspectRatio> (preserveAspectRatio) -->
+ <marker id="marker"/>
+ </defs>
+</svg>
diff --git a/dom/svg/test/fragments-helper.svg b/dom/svg/test/fragments-helper.svg
new file mode 100644
index 0000000000..c7fa8fca7a
--- /dev/null
+++ b/dom/svg/test/fragments-helper.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg">
+ <view id="view" viewBox="0 200 100 100" preserveAspectRatio="none"/>
+</svg>
diff --git a/dom/svg/test/getBBox-method-helper.svg b/dom/svg/test/getBBox-method-helper.svg
new file mode 100644
index 0000000000..8172b2d864
--- /dev/null
+++ b/dom/svg/test/getBBox-method-helper.svg
@@ -0,0 +1,304 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
+ viewBox="0 0 500 500" width="500px" height="500px">
+ <defs>
+ <clipPath id="rect01" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
+ <rect x="0" y="0" width="0.5" height="1.0"/>
+ </clipPath>
+ <clipPath id="rect02" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
+ <rect x="0.5" y="0" width="0.5" height="1.0"/>
+ </clipPath>
+ <clipPath id="rect03" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
+ <rect x="0.5" y="0" width="0.5" height="1.0"/>
+ </clipPath>
+ <clipPath id="rect04" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
+ <rect x="0" y="0" width="0.5" height="1.0"/>
+ </clipPath>
+ <clipPath id="rect05" clip-rule="evenodd">
+ <rect x="0" y="60" width="10px" height="23px"/>
+ </clipPath>
+ <clipPath id="rect06" clip-rule="evenodd">
+ <rect x="10" y="60" width="10px" height="23px"/>
+ </clipPath>
+ <clipPath id="rect4" clip-rule="evenodd">
+ <rect x="200" y="200" width="200" height="200"/>
+ </clipPath>
+ <clipPath id="rect-none" clip-rule="evenodd">
+ </clipPath>
+ <clipPath id="rect5" clip-rule="evenodd">
+ <rect x="0" y="0" width="100" height="100"/>
+ </clipPath>
+ <clipPath id="rect6" clip-rule="evenodd">
+ <rect x="150" y="0" width="100" height="100"/>
+ </clipPath>
+ <clipPath id="rect7" clip-rule="evenodd">
+ <rect x="0" y="100" width="100" height="100"/>
+ </clipPath>
+ <clipPath id="rect8" clip-rule="evenodd">
+ <rect x="10" y="10" width="180" height="180"/>
+ </clipPath>
+ <clipPath id="rect9" clip-rule="evenodd">
+ <rect x="100" y="100" width="200" height="200"/>
+ </clipPath>
+
+ <clipPath id="circle1" clip-rule="evenodd">
+ <circle cx="203" cy="203" r="150"/>
+ </clipPath>
+ <clipPath id="circle2" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
+ <circle cx="0.5" cy="0.5" r="0.25"/>
+ </clipPath>
+ <clipPath id="circle3" clip-rule="evenodd">
+ <circle cx="100" cy="100" r="50"/>
+ <circle cx="300" cy="300" r="50"/>
+ </clipPath>
+
+ <clipPath id="circle4" clip-rule="evenodd">
+ <circle cx="50" cy="50" r="50"/>
+ </clipPath>
+ <clipPath id="circle5" clip-rule="evenodd">
+ <circle cx="150" cy="50" r="50"/>
+ </clipPath>
+ <clipPath id="circle6" clip-rule="evenodd">
+ <circle cx="50" cy="200" r="50"/>
+ </clipPath>
+ <clipPath id="circle7" clip-rule="evenodd" clipPathUnits="objectBoundingBox">
+ <circle cx="0.5" cy="0.5" r="0.5"/>
+ </clipPath>
+
+ <clipPath id="circle8" clip-rule="evenodd" clipPathUnits="userSpaceOnUse">
+ <circle cx="110" cy="20" r="90"/>
+ </clipPath>
+
+ <clipPath id="circle9" clip-rule="evenodd" clipPathUnits="userSpaceOnUse">
+ <circle cx="290" cy="20" r="90"/>
+ </clipPath>
+
+ <clipPath id="circle10" clip-rule="evenodd" clipPathUnits="userSpaceOnUse">
+ <circle cx="110" cy="200" r="90"/>
+ </clipPath>
+
+ <clipPath id="circle11" clip-rule="evenodd">
+ <circle cx="0" cy="0" r="150"/>
+ </clipPath>
+
+ <clipPath id="star" clip-rule="evenodd">
+ <path d="M400,25 L619,703 43,283 757,283 181,703 z" />
+ </clipPath>
+
+ <marker id="m_atr" markerUnits="strokeWidth" markerWidth="3" markerHeight="3" viewBox="0 0 10 10" refX="5" refY="5">
+ <polygon points="0,0 5,5 0,10 10,5" fill="red"/>
+ </marker>
+
+ <switch>
+ <rect id="rect-10" x="20" y="20" width="180" height="180" fill="blue" stroke="cyan" stroke-width="8"/>
+ <rect id="rect-11" x="200" y="20" width="180" height="180" fill="lightgreen" stroke="none" />
+ <rect id="rect-12" x="20" y="200" width="180" height="180" fill="darkcyan" stroke="none" />
+ </switch>
+
+ <clipPath id="clipCircle1">
+ <circle id="c1" cx="100" cy="100" r="50"/>
+ </clipPath>
+
+ <clipPath id="clipCircle2">
+ <circle id="c2" cx="150" cy="150" r="50"/>
+ </clipPath>
+
+ <clipPath id="clipPath1">
+ <path id="p1" d="M10 10l100 0 0 100 -100 0ZM50 50l40 0 0 40 -40 0Z" clip-rule="evenodd"/>
+ </clipPath>
+
+ <!-- "If a valid 'clip-path' reference is placed on one of the children of a 'clipPath' element,
+ then the given child element is clipped by the referenced clipping path before OR'ing the
+ silhouette of the child element with the silhouettes of the other child elements." -->
+
+ <clipPath id="clipRects1">
+ <rect x="50" y="30" width="25" height="100"/>
+ <rect x="25" y="50" width="10" height="10" clip-path="url(#clipTwoCircles)"/>
+ </clipPath>
+
+ <!-- Test use in a clipPath -->
+ <clipPath id="clipTwoCircles">
+ <use xlink:href="#c1"/>
+ <use xlink:href="#c2"/>
+ </clipPath>
+
+ <clipPath id="clipInClip1">
+ <use xlink:href="#c2" clip-path="url(#clipCircle1)"/>
+ <use xlink:href="#p1"/>
+ </clipPath>
+
+ <clipPath id="clipOnClip1" clip-path="url(#clipCircle1)">
+ <use xlink:href="#c2"/>
+ <use xlink:href="#p1"/>
+ </clipPath>
+
+ </defs>
+
+ <!-- text -->
+ <text id="text1" font-size="20px" font-familiy="monospace" fill="red" x="0" y="50" clip-path="url('#rect01')">99</text>
+ <text id="text2" font-size="20px" font-familiy="monospace" fill="blue" x="100" y="120" clip-path="url('#rect02')">99</text>
+ <text id="text3" font-size="20px" font-familiy="monospace" clip-path="url('#rect03')" x="0" y="120">
+ <tspan x="0" y="50" fill="red">99</tspan>
+ </text>
+ <text id="text4" font-size="20px" font-familiy="monospace" clip-path="url('#rect04')" x="0" y="120">
+ <tspan x="100" y="120" fill="blue">99</tspan>
+ </text>
+ <text id="text5" font-size="20px" font-familiy="monospace" fill="red" x="0" y="80" clip-path="url('#rect05')">99</text>
+ <text id="text6" font-size="20px" font-familiy="monospace" fill="blue" x="0" y="80" clip-path="url('#rect06')">99</text>
+
+ <!-- image -->
+ <image id="image1" x="150" y="150" width="200" height="200" preserveApectRatio="none" clip="rect(200,300,300,200)"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image2" x="2" y="2" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image3" x="205" y="2" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image4" x="2" y="205" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image5" x="205" y="205" width="200" height="200" clip-path="url('#circle1')" preserveApectRatio="none"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image6" x="2" y="2" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image7" x="205" y="2" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image8" x="2" y="205" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image9" x="205" y="205" width="200" height="200" clip-path="url('#circle2')" preserveApectRatio="none"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image10" x="0" y="0" width="400" height="400" clip-path="url('#rect4')"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image11" x="0" y="0" width="400" height="400" clip-path="url('#rect-none')"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image12" x="25" y="43" width="768" height="768" clip-path="url('#star')" preserveApectRatio="none"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image13" x="0" y="0" width="400" height="400" clip-path="url('#circle3')"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <image id="image14" x="0" y="0" width="400" height="400" clip-path="url('#m_atr')"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAAKXRFWHRDcmVhdGlvbiBUaW1lAJCFIDMwIDQgMjAxNCAwOToyODo0MyArMDkwMH73aTcAAAAHdElNRQfeBB4AHhEfV06+AAAACXBIWXMAAB7BAAAewQHDaVRTAAAABGdBTUEAALGPC/xhBQAAABlJREFUeNpjZPj/n4EUwESS6lENoxqGlAYASU8CHux7qQ4AAAAASUVORK5CYII="/>
+
+ <!-- path -->
+ <path id="path1" d="M10,50 L25,100 H110 V50 Q60,0 10,50" stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan" marker-mid="url(#m_atr)"/>
+ <path id="path2" d="M160,50 L175,100 H260 V50 Q210,0 160,50" stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan" marker-mid="url(#m_atr)"/>
+ <path id="path3" d="M10,150 L25,200 H110 V150 Q60,100 10,150" stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan" marker-mid="url(#m_atr)"/>
+
+
+ <path id="path4" d="M10,50 L25,100 H110 V50 Q60,0 10,50" stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan"
+ marker-mid="url(#m_atr)" clip-path="url(#circle4)"/>
+
+ <path id="path5" d="M160,50 L175,100 H260 V50 Q210,0 160,50" stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan"
+ marker-mid="url(#m_atr)" clip-path="url(#circle5)"/>
+
+ <path id="path6" d="M10,150 L25,200 H110 V150 Q60,100 10,150" stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan"
+ marker-mid="url(#m_atr)" clip-path="url(#circle6)"/>
+
+ <path id="path7" d="M10,50 L25,100 H110 V50 Q60,0 10,50"
+ stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan"
+ clip-path="url('#rect5')" marker-mid="url(#m_atr)"/>
+
+ <path id="path8" d="M160,50 L175,100 H260 V50 Q210,0 160,50"
+ stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan"
+ clip-path="url('#rect6')" marker-mid="url(#m_atr)"/>
+
+ <path id="path9" d="M10,150 L25,200 H110 V150 Q60,100 10,150"
+ stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan"
+ clip-path="url('#rect7')" marker-mid="url(#m_atr)"/>
+
+ <path id="path10" d="M10,50 L25,100 H110 V50 Q60,0 10,50"
+ stroke="black" stroke-width="8" stroke-miterlimit="1" stroke-linejoin="miter" fill="lightcyan"
+ clip-path="url('#circle7')" marker-mid="url(#m_atr)"/>
+
+ <path id="path11" d="M160,50 L175,100 H260 V50 Q210,0 160,50"
+ stroke="black" stroke-width="8" stroke-linejoin="bevel" fill="lightcyan"
+ clip-path="url('#circle7')" marker-mid="url(#m_atr)"/>
+
+ <path id="path12" d="M10,150 L25,200 H110 V150 Q60,100 10,150"
+ stroke="black" stroke-width="8" stroke-linejoin="round" fill="lightcyan"
+ clip-path="url('#circle7')" marker-mid="url(#m_atr)"/>
+
+ <path id="path13" d="M50,0 C 130,0 50,0 100,50
+ C 100,130 100,50 50,100
+ C -30,100 50,100 0,50
+ C 0,-30 0,50 50,0Z" />
+
+ <!-- use -->
+ <use id="use1" xlink:href="#rect-10" x="50" y="50" clip-path="url('#circle8')"/>
+ <use id="use2" xlink:href="#rect-11" x="50" y="50" clip-path="url('#circle9')"/>
+ <use id="use3" xlink:href="#rect-12" x="50" y="50" clip-path="url('#circle10')"/>
+
+ <use id="use4" xlink:href="#rect-10" x="2" y="2" width="200" height="200" clip-path="url('#circle11')"/>
+ <use id="use5" xlink:href="#rect-10" x="205" y="2" width="200" height="200" clip-path="url('#circle11')"/>
+ <use id="use6" xlink:href="#rect-10" x="2" y="205" width="200" height="200" clip-path="url('#circle11')"/>
+ <use id="use7" xlink:href="#rect-10" x="205" y="205" width="200" height="200" clip-path="url('#circle11')"/>
+
+ <use id="use8" xlink:href="#rect-10" x="50" y="50" clip-path="url('#m_atr')"/>
+
+ <!-- foreignObject -->
+ <foreignObject id="fo1" x="2" y="2" width="200" height="200" clip-path="url('#circle1')" clip="rect(2,102,102,2)">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+ </div>
+ </foreignObject>
+ <foreignObject id="fo2" x="205" y="2" width="200" height="200" clip-path="url('#circle1')" >
+ <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+ </div>
+ </foreignObject>
+ <foreignObject id="fo3" x="2" y="205" width="200" height="200" clip-path="url('#circle1')" >
+ <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+ </div>
+ </foreignObject>
+ <foreignObject id="fo4" x="205" y="205" width="200" height="200" clip-path="url('#circle1')" clip="rect(2,102,102,2)">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+ </div>
+ </foreignObject>
+
+ <foreignObject id="fo5" x="250" y="250" width="200" height="200" clip-path="url('#rect8')">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+ </div>
+ </foreignObject>
+
+ <foreignObject id="fo6" x="0" y="0" width="200" height="200" clip-path="url('#rect9')">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+ </div>
+ </foreignObject>
+
+ <foreignObject id="fo7" x="0" y="0" width="200" height="200" clip-path="url('#rect8')">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+ </div>
+ </foreignObject>
+
+ <foreignObject id="fo8" x="0" y="0" width="200" height="200" clip-path="url('#m_atr')">
+ <div xmlns="http://www.w3.org/1999/xhtml" style="background-color:pink;width:100%;height:100%;">
+There are issues of dynamic loading required for tiling. Accroding to 'postpone' attribute of Resource Priorities, the dynamic loading is controlled by positional relation with bounding box of embedded contents and container's viewport. However, each bounding boxes of embedded contents should be whole earth basically when this method is used. (green part on Example) Tiling is impossible unless this situation is changed.
+ </div>
+ </foreignObject>
+
+ <!-- -->
+ <rect id="rect-1" width="200" height="200" fill="blue" clip-path="url(#clipInClip1)"/>
+ <rect id="rect-2" width="200" height="200" fill="blue" clip-path="url(#clipRects1)"/>
+ <rect id="rect-3" width="300" height="300" fill="blue" clip-path="url(#clipOnClip1)"/>
+
+ <g clip-path="url(#clipCircle1)" id="g1">
+ <use xlink:href="#c2" fill="red"/>
+ <use xlink:href="#p1" fill="red" fill-rule="evenodd"/>
+ </g>
+
+</svg>
diff --git a/dom/svg/test/getCTM-helper.svg b/dom/svg/test/getCTM-helper.svg
new file mode 100644
index 0000000000..835efc5067
--- /dev/null
+++ b/dom/svg/test/getCTM-helper.svg
@@ -0,0 +1,47 @@
+<?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>
+ <svg id="inner-2" viewBox="0 0 10 10" width="-10" height="10">
+ <g id="g5"/>
+ </svg>
+ <foreignObject id="fO" 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>
+ <foreignObject x="30" y="40" width="100" height="100" transform="translate(1, 1)">
+ <html xmlns="http://www.w3.org/1999/xhtml" style="width: 100%; height: 100%">
+ <svg xmlns="http://www.w3.org/2000/svg" id="outer2"
+ width="100" height="100" viewBox="100 100 200 200"/>
+ </html>
+ </foreignObject>
+ <!-- something invalid -->
+ <foreignObject>
+ <g id="g3"/>
+ </foreignObject>
+ <image>
+ <g id="g4"/>
+ </image>
+ <use xlink:href="#sym" id="use"/>
+ </g>
+ </svg>
+ </g>
+</svg>
diff --git a/dom/svg/test/getSubStringLength-helper.svg b/dom/svg/test/getSubStringLength-helper.svg
new file mode 100644
index 0000000000..6c80d9d46b
--- /dev/null
+++ b/dom/svg/test/getSubStringLength-helper.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="750">
+ <style type="text/css">
+.t { font: 20px monospace; }
+ </style>
+ <text id="text" x="5" class="t" y="25">abc</text>
+</svg>
diff --git a/dom/svg/test/matrixUtils.js b/dom/svg/test/matrixUtils.js
new file mode 100644
index 0000000000..ed9b8297b9
--- /dev/null
+++ b/dom/svg/test/matrixUtils.js
@@ -0,0 +1,78 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Utilities for testing SVG matrices
+ */
+
+function createMatrix(a, b, c, d, e, f) {
+ var svg = document.getElementsByTagName("svg")[0];
+ var m = svg.createSVGMatrix();
+ m.a = a;
+ m.b = b;
+ m.c = c;
+ m.d = d;
+ m.e = e;
+ m.f = f;
+ return m;
+}
+
+// Lightweight dummy Matrix class for representing arrays that get passed in
+function MatrixFromArray(a) {
+ this.a = a[0];
+ this.b = a[1];
+ this.c = a[2];
+ this.d = a[3];
+ this.e = a[4];
+ this.f = a[5];
+}
+
+function cmpMatrix(a, b, msg) {
+ if (a.constructor === Array) {
+ a = new MatrixFromArray(a);
+ }
+ if (b.constructor === Array) {
+ b = new MatrixFromArray(b);
+ }
+
+ ok(
+ a.a == b.a &&
+ a.b == b.b &&
+ a.c == b.c &&
+ a.d == b.d &&
+ a.e == b.e &&
+ a.f == b.f,
+ msg + " - got " + formatMatrix(a) + ", expected " + formatMatrix(b)
+ );
+}
+
+function roughCmpMatrix(a, b, msg) {
+ if (a.constructor === Array) {
+ a = new MatrixFromArray(a);
+ }
+ if (b.constructor === Array) {
+ b = new MatrixFromArray(b);
+ }
+
+ const tolerance = 1 / 65535;
+ ok(
+ Math.abs(b.a - a.a) < tolerance &&
+ Math.abs(b.b - a.b) < tolerance &&
+ Math.abs(b.c - a.c) < tolerance &&
+ Math.abs(b.d - a.d) < tolerance &&
+ Math.abs(b.e - a.e) < tolerance &&
+ Math.abs(b.f - a.f) < tolerance,
+ msg + " - got " + formatMatrix(a) + ", expected " + formatMatrix(b)
+ );
+}
+
+function formatMatrix(m) {
+ if (m.constructor != Array) {
+ return "(" + [m.a, m.b, m.c, m.d, m.e, m.f].join(", ") + ")";
+ }
+
+ return "(" + m.join(", ") + ")";
+}
diff --git a/dom/svg/test/mochitest.ini b/dom/svg/test/mochitest.ini
new file mode 100644
index 0000000000..1aa753f1ff
--- /dev/null
+++ b/dom/svg/test/mochitest.ini
@@ -0,0 +1,113 @@
+[DEFAULT]
+prefs =
+ dom.svg.pathSeg.enabled=true
+support-files =
+ MutationEventChecker.js
+ a_href_destination.svg
+ a_href_helper_01.svg
+ a_href_helper_02_03.svg
+ a_href_helper_04.svg
+ a_href_helper_05.svg
+ a_href_helper_06.svg
+ a_href_helper_07.svg
+ animated-svg-image-helper.html
+ animated-svg-image-helper.svg
+ bbox-helper.svg
+ bounds-helper.svg
+ dataTypes-helper.svg
+ fragments-helper.svg
+ getBBox-method-helper.svg
+ getCTM-helper.svg
+ getSubStringLength-helper.svg
+ matrixUtils.js
+ object-delayed-intrinsic-size.sjs
+ pointer-events.js
+ scientific-helper.svg
+ selectSubString-helper.svg
+ switch-helper.svg
+ text-helper-scaled.svg
+ text-helper-selection.svg
+ text-helper.svg
+ viewport-helper.svg
+
+[test_SVGLengthList-2.xhtml]
+[test_SVGLengthList.xhtml]
+[test_SVGMatrix.xhtml]
+[test_SVGNumberList.xhtml]
+[test_SVGPointList.xhtml]
+[test_SVGStringList.xhtml]
+[test_SVGStyleElement.xhtml]
+[test_SVGTransformList.xhtml]
+[test_SVGTransformListAddition.xhtml]
+[test_SVG_namespace_ids.html]
+[test_SVGxxxList.xhtml]
+[test_SVGxxxListIndexing.xhtml]
+[test_a_href_01.xhtml]
+[test_a_href_02.xhtml]
+[test_animLengthObjectIdentity.xhtml]
+[test_animLengthReadonly.xhtml]
+[test_animLengthUnits.xhtml]
+[test_bbox-changes.xhtml]
+[test_bbox-with-invalid-viewBox.xhtml]
+[test_bbox.xhtml]
+[test_bounds.html]
+[test_bug1426594.html]
+[test_bug872812.html]
+[test_dataTypes.html]
+[test_dataTypesModEvents.html]
+[test_fragments.html]
+[test_getBBox-method.html]
+[test_getCTM.html]
+[test_getElementById.xhtml]
+[test_getPathSegListAtLength_with_d_property.html]
+[test_getSubStringLength.xhtml]
+[test_getTotalLength.xhtml]
+[test_hit-testing-and-viewbox.xhtml]
+[test_lang.xhtml]
+skip-if = true # disabled-for-intermittent-failures--bug-701060
+[test_length.xhtml]
+[test_lengthParsing.html]
+[test_markerOrient.xhtml]
+[test_non-scaling-stroke.html]
+[test_nonAnimStrings.xhtml]
+[test_object-delayed-intrinsic-size.html]
+[test_onerror.xhtml]
+[test_onload.xhtml]
+[test_onload2.xhtml]
+[test_pairParsing.html]
+[test_pathAnimInterpolation.xhtml]
+skip-if = true # We need to polyfill the SVG DOM for path data
+[test_pointAtLength.xhtml]
+[test_pointer-events-1a.xhtml]
+[test_pointer-events-1b.xhtml]
+[test_pointer-events-2.xhtml]
+[test_pointer-events-3.xhtml]
+[test_pointer-events-4.xhtml]
+[test_pointer-events-6.xhtml]
+[test_pointer-events-7.xhtml]
+[test_scientific.html]
+[test_selectSubString.xhtml]
+[test_stroke-hit-testing.xhtml]
+[test_stroke-linecap-hit-testing.xhtml]
+[test_style_sheet.html]
+[test_switch.xhtml]
+[test_tabindex.html]
+[test_tearoff_with_cc.html]
+support-files = tearoff_with_cc_helper.html
+[test_text.html]
+[test_text_2.html]
+[test_text_dirty.html]
+[test_text_lengthAdjust.html]
+[test_text_scaled.html]
+[test_text_selection.html]
+[test_text_update.html]
+[test_transform.xhtml]
+[test_transformParsing.html]
+[test_use_with_hsts.html]
+support-files = use-with-hsts-helper.html use-with-hsts-helper.html^headers^
+scheme = https
+[test_valueAsString.xhtml]
+[test_valueLeaks.xhtml]
+[test_viewBox.html]
+[test_viewport.html]
+
diff --git a/dom/svg/test/object-delayed-intrinsic-size.sjs b/dom/svg/test/object-delayed-intrinsic-size.sjs
new file mode 100644
index 0000000000..c7cd5cb9ff
--- /dev/null
+++ b/dom/svg/test/object-delayed-intrinsic-size.sjs
@@ -0,0 +1,24 @@
+var timer = null;
+
+function handleRequest(request, response) {
+ response.processAsync();
+
+ response.setStatusLine(null, 200, "OK");
+ response.setHeader("Content-Type", "image/svg+xml", false);
+
+ // We need some body output or else gecko will not do an initial reflow
+ // while waiting for the rest of the document to load:
+ response.bodyOutputStream.write("\n", 1);
+
+ timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback(
+ function () {
+ var body =
+ "<svg xmlns='http://www.w3.org/2000/svg' width='70' height='0'></svg>";
+ response.bodyOutputStream.write(body, body.length);
+ response.finish();
+ },
+ 1000 /* milliseconds */,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ );
+}
diff --git a/dom/svg/test/pointer-events.js b/dom/svg/test/pointer-events.js
new file mode 100644
index 0000000000..e65fd3d390
--- /dev/null
+++ b/dom/svg/test/pointer-events.js
@@ -0,0 +1,328 @@
+SimpleTest.waitForExplicitFinish();
+
+var pointer_events_values = [
+ "auto",
+ "visiblePainted",
+ "visibleFill",
+ "visibleStroke",
+ "visible",
+ "painted",
+ "fill",
+ "stroke",
+ "all",
+ "none",
+];
+
+var paint_values = ["blue", "transparent", "none"];
+
+var opacity_values = ["1", "0.5", "0"];
+
+var visibility_values = ["visible", "hidden", "collapse"];
+
+/**
+ * List of attributes and various values for which we want to test permutations
+ * when hit testing a pointer event that is over an element's fill area,
+ * stroke area, or both (where they overlap).
+ *
+ * We're using an array of objects so that we have control over the order in
+ * which permutations are tested.
+ *
+ * TODO: test the effect of clipping, masking, filters, markers, etc.
+ */
+var hit_test_inputs = {
+ fill: [
+ { name: "pointer-events", values: pointer_events_values },
+ { name: "fill", values: paint_values },
+ { name: "fill-opacity", values: opacity_values },
+ { name: "opacity", values: opacity_values },
+ { name: "visibility", values: visibility_values },
+ ],
+ stroke: [
+ { name: "pointer-events", values: pointer_events_values },
+ { name: "stroke", values: paint_values },
+ { name: "stroke-opacity", values: opacity_values },
+ { name: "opacity", values: opacity_values },
+ { name: "visibility", values: visibility_values },
+ ],
+ both: [
+ { name: "pointer-events", values: pointer_events_values },
+ { name: "fill", values: paint_values },
+ { name: "fill-opacity", values: opacity_values },
+ { name: "stroke", values: paint_values },
+ { name: "stroke-opacity", values: opacity_values },
+ { name: "opacity", values: opacity_values },
+ { name: "visibility", values: visibility_values },
+ ],
+};
+
+/**
+ * The following object contains a list of 'pointer-events' property values,
+ * each with an object detailing the conditions under which the fill and stroke
+ * of a graphical object will intercept pointer events for the given value. If
+ * the object contains a 'fill-intercepts-iff' property then the fill is
+ * expected to intercept pointer events for that value of 'pointer-events' if
+ * and only if the conditions listed in the 'fill-intercepts-iff' object are
+ * met. If there are no conditions in the 'fill-intercepts-iff' object then the
+ * fill should always intercept pointer events. However, if the
+ * 'fill-intercepts-iff' property is not set at all then it indicates that the
+ * fill should never intercept pointer events. The same rules apply for
+ * 'stroke-intercepts-iff'.
+ *
+ * If an attribute name in the conditions list is followed by the "!"
+ * character then the requirement for a hit is that its value is NOT any
+ * of the values listed in the given array.
+ */
+var hit_conditions = {
+ auto: {
+ "fill-intercepts-iff": {
+ visibility: ["visible"],
+ "fill!": ["none"],
+ },
+ "stroke-intercepts-iff": {
+ visibility: ["visible"],
+ "stroke!": ["none"],
+ },
+ },
+ visiblePainted: {
+ "fill-intercepts-iff": {
+ visibility: ["visible"],
+ "fill!": ["none"],
+ },
+ "stroke-intercepts-iff": {
+ visibility: ["visible"],
+ "stroke!": ["none"],
+ },
+ },
+ visibleFill: {
+ "fill-intercepts-iff": {
+ visibility: ["visible"],
+ },
+ // stroke never intercepts pointer events
+ },
+ visibleStroke: {
+ // fill never intercepts pointer events
+ "stroke-intercepts-iff": {
+ visibility: ["visible"],
+ },
+ },
+ visible: {
+ "fill-intercepts-iff": {
+ visibility: ["visible"],
+ },
+ "stroke-intercepts-iff": {
+ visibility: ["visible"],
+ },
+ },
+ painted: {
+ "fill-intercepts-iff": {
+ "fill!": ["none"],
+ },
+ "stroke-intercepts-iff": {
+ "stroke!": ["none"],
+ },
+ },
+ fill: {
+ "fill-intercepts-iff": {
+ // fill always intercepts pointer events
+ },
+ // stroke never intercepts pointer events
+ },
+ stroke: {
+ // fill never intercepts pointer events
+ "stroke-intercepts-iff": {
+ // stroke always intercepts pointer events
+ },
+ },
+ all: {
+ "fill-intercepts-iff": {
+ // fill always intercepts pointer events
+ },
+ "stroke-intercepts-iff": {
+ // stroke always intercepts pointer events
+ },
+ },
+ none: {
+ // neither fill nor stroke intercept pointer events
+ },
+};
+
+// bit flags
+var POINT_OVER_FILL = 0x1;
+var POINT_OVER_STROKE = 0x2;
+
+/**
+ * Examine the element's attribute values and, based on the area(s) of the
+ * element that the pointer event is over (fill and/or stroke areas), return
+ * true if the element is expected to intercept the event, otherwise false.
+ */
+function hit_expected(
+ element,
+ over /* bit flags indicating which area(s) of the element the pointer is over */
+) {
+ function expect_hit(target) {
+ var intercepts_iff =
+ hit_conditions[element.getAttribute("pointer-events")][
+ target + "-intercepts-iff"
+ ];
+
+ if (!intercepts_iff) {
+ return false; // never intercepts events
+ }
+
+ for (var attr in intercepts_iff) {
+ var vals = intercepts_iff[attr]; // must get this before we adjust 'attr'
+ var invert = false;
+ if (attr.substr(-1) == "!") {
+ invert = true;
+ attr = attr.substr(0, attr.length - 1);
+ }
+ var match = vals.indexOf(element.getAttribute(attr)) > -1;
+ if (invert) {
+ match = !match;
+ }
+ if (!match) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ return (
+ ((over & POINT_OVER_FILL) != 0 && expect_hit("fill")) ||
+ ((over & POINT_OVER_STROKE) != 0 && expect_hit("stroke"))
+ );
+}
+
+function for_all_permutations(inputs, callback) {
+ var current_permutation = arguments[2] || {};
+ var index = arguments[3] || 0;
+
+ if (index < inputs.length) {
+ var name = inputs[index].name;
+ var values = inputs[index].values;
+ for (var i = 0; i < values.length; ++i) {
+ current_permutation[name] = values[i];
+ for_all_permutations(inputs, callback, current_permutation, index + 1);
+ }
+ return;
+ }
+
+ callback(current_permutation);
+}
+
+function make_log_msg(over, tag, attributes) {
+ var target;
+ if (over == (POINT_OVER_FILL | POINT_OVER_STROKE)) {
+ target = "fill and stroke";
+ } else if (over == POINT_OVER_FILL) {
+ target = "fill";
+ } else if (over == POINT_OVER_STROKE) {
+ target = "stroke";
+ } else {
+ throw new Error("unexpected bit combination in 'over'");
+ }
+ var msg =
+ "Check if events are intercepted at a point over the " +
+ target +
+ " on <" +
+ tag +
+ "> for";
+ for (var attr in attributes) {
+ msg += " " + attr + "=" + attributes[attr];
+ }
+ return msg;
+}
+
+var dx, dy; // offset of <svg> element from pointer coordinates origin
+
+function test_element(
+ id,
+ x,
+ y,
+ over /* bit flags indicating which area(s) of the element the pointer is over */
+) {
+ var element = document.getElementById(id);
+ var tag = element.tagName;
+
+ function test_permutation(attributes) {
+ for (var attr in attributes) {
+ element.setAttribute(attr, attributes[attr]);
+ }
+ var hits = document.elementFromPoint(dx + x, dy + y) == element;
+ var msg = make_log_msg(over, tag, attributes);
+
+ is(hits, hit_expected(element, over), msg);
+ }
+
+ var inputs;
+ if (over == (POINT_OVER_FILL | POINT_OVER_STROKE)) {
+ inputs = hit_test_inputs.both;
+ } else if (over == POINT_OVER_FILL) {
+ inputs = hit_test_inputs.fill;
+ } else if (over == POINT_OVER_STROKE) {
+ inputs = hit_test_inputs.stroke;
+ } else {
+ throw new Error("unexpected bit combination in 'over'");
+ }
+
+ for_all_permutations(inputs, test_permutation);
+
+ // To reduce the chance of bogus results in subsequent tests:
+ element.setAttribute("fill", "none");
+ element.setAttribute("stroke", "none");
+}
+
+function run_tests(subtest) {
+ var div = document.getElementById("div");
+ dx = div.offsetLeft;
+ dy = div.offsetTop;
+
+ // Run the test with only a subset of pointer-events values, to avoid
+ // running over the mochitest time limit. The subtest argument indicates
+ // whether to use the first half of the pointer-events values (0)
+ // or the second half (1).
+ var partition = Math.floor(pointer_events_values.length / 2);
+ switch (subtest) {
+ case 0:
+ pointer_events_values.splice(partition);
+ break;
+ case 1:
+ pointer_events_values.splice(0, partition);
+ break;
+ case 2:
+ throw new Error("unexpected subtest number");
+ }
+
+ test_element("rect", 30, 30, POINT_OVER_FILL);
+ test_element("rect", 5, 5, POINT_OVER_STROKE);
+
+ // The SVG 1.1 spec essentially says that, for text, hit testing is done
+ // against the character cells of the text, and not the fill and stroke as
+ // you might expect for a normal graphics element like <path>. See the
+ // paragraph starting "For text elements..." in this section:
+ //
+ // http://www.w3.org/TR/SVG11/interact.html#PointerEventsProperty
+ //
+ // This requirement essentially means that for the purposes of hit testing
+ // the fill and stroke areas are the same area - the character cell. (At
+ // least until we support having any fill or stroke that lies outside the
+ // character cells intercept events like Opera does - see below.) Thus, for
+ // text, when a pointer event is over a character cell it is essentially over
+ // both the fill and stroke at the same time. That's the reason we pass both
+ // the POINT_OVER_FILL and POINT_OVER_STROKE bits in test_element's 'over'
+ // argument below. It's also the reason why we only test one point in the
+ // text rather than having separate tests for fill and stroke.
+ //
+ // For hit testing of text, Opera essentially treats fill and stroke like it
+ // would on any normal element, but it adds the character cells of glyhs to
+ // both the glyphs' fill AND stroke. I think this is what we should do too.
+ // It's compatible with the letter of the SVG 1.1 rules, and it allows any
+ // parts of a glyph that are outside the glyph's character cells to also
+ // intercept events in the normal way. When we make that change we'll be able
+ // to add separate fill and stroke tests for text below.
+
+ test_element("text", 210, 30, POINT_OVER_FILL | POINT_OVER_STROKE);
+
+ SimpleTest.finish();
+}
diff --git a/dom/svg/test/scientific-helper.svg b/dom/svg/test/scientific-helper.svg
new file mode 100644
index 0000000000..8ac2227e62
--- /dev/null
+++ b/dom/svg/test/scientific-helper.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="750">
+ <rect id="rect" stroke-width="10"/>
+ <text id="text">x</text>
+</svg>
diff --git a/dom/svg/test/selectSubString-helper.svg b/dom/svg/test/selectSubString-helper.svg
new file mode 100644
index 0000000000..6c80d9d46b
--- /dev/null
+++ b/dom/svg/test/selectSubString-helper.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="750">
+ <style type="text/css">
+.t { font: 20px monospace; }
+ </style>
+ <text id="text" x="5" class="t" y="25">abc</text>
+</svg>
diff --git a/dom/svg/test/switch-helper.svg b/dom/svg/test/switch-helper.svg
new file mode 100644
index 0000000000..842296efa7
--- /dev/null
+++ b/dom/svg/test/switch-helper.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/licenses/publicdomain/
+-->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg">
+ <switch id="s">
+ <rect systemLanguage="fr" id="first" x="75" y="100" width="70" height="70" fill="yellow"/>
+ <rect id="second" x="75" y="100" width="50" height="50" fill="lime"/>
+ <rect id="third" x="75" y="100" width="80" height="80" fill="red"/>
+ </switch>
+</svg>
diff --git a/dom/svg/test/tearoff_with_cc_helper.html b/dom/svg/test/tearoff_with_cc_helper.html
new file mode 100644
index 0000000000..6d63e939fe
--- /dev/null
+++ b/dom/svg/test/tearoff_with_cc_helper.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+<body onload="go()">
+ <svg id="outerSvg" width="50%" height="50%"
+ style="border: 1px solid black">
+ </svg>
+ <script type="application/javascript">
+ /* I'm not sure what exactly was required to trigger bug 1288228's crash,
+ * but it involved tweaking a length's specified units and cycle-collecting
+ * and reloading (in some combination). So, we'll tweak the units and
+ * cycle-collect a few times, and message the outer page to reload us
+ * after we've made the first tweak.
+ */
+ const maxTweaks = 5;
+ let remainingTweaks = maxTweaks;
+
+ var savedBaseVal = document.getElementById("outerSvg").width.baseVal;
+ function go() {
+ window.parent.SpecialPowers.DOMWindowUtils.cycleCollect();
+ tweak();
+ }
+
+ function tweak() {
+ console.log("tweaked");
+ savedBaseVal.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX);
+ savedBaseVal.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PERCENTAGE);
+ if (remainingTweaks == maxTweaks) {
+ window.parent.postMessage("ping", "*"); // only do this on first tweak
+ }
+ if (--remainingTweaks) {
+ setTimeout(tweak, 0);
+ }
+ }
+</script>
+</body>
+</html>
diff --git a/dom/svg/test/test_SVGLengthList-2.xhtml b/dom/svg/test/test_SVGLengthList-2.xhtml
new file mode 100644
index 0000000000..0d8b3a8737
--- /dev/null
+++ b/dom/svg/test/test_SVGLengthList-2.xhtml
@@ -0,0 +1,64 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=630760
+-->
+<head>
+ <title>Tests specific to SVGLengthList</title>
+ <script 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=515116">Mozilla Bug 630760</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">
+ <text id="text">
+ <set attributeName="x" to="10 20 30 40" begin="0" dur="indefinite"/>
+ </text>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run_tests() {
+ var svg = document.getElementById("svg");
+ svg.pauseAnimations();
+
+ // Check that the animVal list for 'x' on <text> gives the correct number of
+ // items when examined for the FIRST time DURING animation:
+
+ var text = document.getElementById("text");
+ var list = text.x.animVal;
+
+ is(list.numberOfItems, 4, "Checking numberOfItems");
+
+ // Check that items at an index larger than 255 (max value for PRUint8) are
+ // returning the correct values:
+
+ var item;
+ list = text.x.baseVal;
+ for (var i = 0; i < 256; ++i) {
+ item = svg.createSVGLength();
+ item.value = 1;
+ list.appendItem(item);
+ }
+ item = svg.createSVGLength();
+ item.value = 2;
+ list.appendItem(item);
+
+ is(list.getItem(0).value, 1, "Check value of first item");
+ is(list.getItem(256).value, 2, "Check value of item at index > 255");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run_tests);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_SVGLengthList.xhtml b/dom/svg/test/test_SVGLengthList.xhtml
new file mode 100644
index 0000000000..87b5c40d6b
--- /dev/null
+++ b/dom/svg/test/test_SVGLengthList.xhtml
@@ -0,0 +1,158 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=515116
+-->
+<head>
+ <title>Tests specific to SVGLengthList</title>
+ <script src="/tests/SimpleTest/SimpleTest.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=515116">Mozilla Bug 515116</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">
+ <text id="text" x="10cm 20cm 30mc"/>
+ <rect id="rect" x="40" y="50"/>
+ <text id="text2" x="60"/>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+/*
+This file runs a series of SVGLengthList specific tests. Generic SVGXxxList
+tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized
+to other list types belongs there.
+*/
+
+function run_tests() {
+ document.getElementById("svg").pauseAnimations();
+
+ var text = document.getElementById("text");
+ var lengths = text.x.baseVal;
+
+ is(lengths.numberOfItems, 0, "Checking numberOfItems");
+
+ // Test mutation events
+ // --- Initialization
+ var eventChecker = new MutationEventChecker;
+ eventChecker.watchAttr(text, "x");
+ eventChecker.expect("modify");
+ text.textContent = "abc";
+ text.setAttribute("x", "10 20 30");
+ is(lengths.numberOfItems, 3, "Checking numberOfItems");
+ // -- Actual changes
+ eventChecker.expect("modify modify modify modify modify");
+ lengths[0].value = 8;
+ lengths[0].valueInSpecifiedUnits = 9;
+ lengths[0].valueAsString = "10";
+ lengths[0].convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_CM);
+ lengths[0].newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_MM, 11);
+ // -- Redundant changes
+ eventChecker.expect("modify");
+ lengths[0].valueAsString = "10";
+ eventChecker.expect("");
+ lengths[0].value = 10;
+ lengths[0].valueInSpecifiedUnits = 10;
+ lengths[0].valueAsString = "10";
+ lengths[0].convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER);
+ lengths[0].newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER, 10);
+ // -- Invalid attribute
+ eventChecker.expect("modify");
+ text.setAttribute("x", ",20");
+ is(lengths.numberOfItems, 0, "Checking that parsing stops at invalid token");
+ // -- Attribute removal
+ eventChecker.expect("remove");
+ text.removeAttribute("x");
+ // -- Non-existent attribute removal
+ eventChecker.expect("");
+ text.removeAttribute("x");
+ text.removeAttributeNS(null, "x");
+ eventChecker.finish();
+
+ // Test that the addition of an owned SVGLength to an SVGLengthList creates a
+ // copy of the SVGLength, and an unowned SVGLength does not make a copy
+ var text2 = document.getElementById("text2");
+ var rect = document.getElementById("rect");
+ var subtests = [
+ function initialize(aItem) {
+ text.removeAttribute("x");
+ return lengths.initialize(aItem);
+ },
+ function insertItemBefore(aItem) {
+ text.removeAttribute("x");
+ return lengths.insertItemBefore(aItem, 0);
+ },
+ function replaceItem(aItem) {
+ text.setAttribute("x", "10");
+ return lengths.replaceItem(aItem, 0);
+ },
+ function appendItem(aItem) {
+ text.removeAttribute("x");
+ return lengths.appendItem(aItem);
+ },
+ ];
+ subtests.forEach(function(aFunction) {
+ // -- Adding an unowned SVGLength
+ var name = aFunction.name;
+ var existingItem = document.getElementById("svg").createSVGLength();
+ var newItem = aFunction(existingItem);
+ is(newItem, lengths.getItem(0), name + " return value is correct when passed an unowned object");
+ is(newItem, existingItem, name + " did not make a copy when passed an unowned object");
+ });
+ subtests.forEach(function(aFunction) {
+ // -- Adding an SVGLength that is a baseVal
+ var name = aFunction.name;
+ var existingItem = rect.x.baseVal;
+ var newItem = aFunction(existingItem);
+ is(newItem, lengths.getItem(0), name + " return value is correct when passed a baseVal");
+ isnot(newItem, existingItem, name + " made a copy when passed a baseVal");
+ is(newItem.value, existingItem.value, name + " made a copy with the right values when passed a baseVal");
+ is(rect.x.baseVal, existingItem, name + " left the original object alone when passed a baseVal");
+ });
+ subtests.forEach(function(aFunction) {
+ // -- Adding an SVGLength that is an animVal
+ var name = aFunction.name;
+ var existingItem = rect.x.animVal;
+ var newItem = aFunction(existingItem);
+ is(newItem, lengths.getItem(0), name + " return value is correct when passed an animVal");
+ isnot(newItem, existingItem, name + " made a copy when passed an animVal");
+ is(newItem.value, existingItem.value, name + " made a copy with the right values when passed an animVal");
+ is(rect.x.animVal, existingItem, name + " left the original object alone when passed an animVal");
+ });
+ subtests.forEach(function(aFunction) {
+ // -- Adding an SVGLength that is in a baseVal list
+ var name = aFunction.name;
+ var existingItem = text2.x.baseVal.getItem(0);
+ var newItem = aFunction(existingItem);
+ is(newItem, lengths.getItem(0), name + " return value is correct when passed a baseVal list item");
+ isnot(newItem, existingItem, name + " made a copy when passed a baseVal list item");
+ is(newItem.value, existingItem.value, name + " made a copy with the right values when passed a baseVal list item");
+ is(text2.x.baseVal.getItem(0), existingItem, name + " left the original object alone when passed a baseVal list item");
+ });
+ subtests.forEach(function(aFunction) {
+ // -- Adding an SVGLength that is in a animVal list
+ var name = aFunction.name;
+ var existingItem = text2.x.animVal.getItem(0);
+ var newItem = aFunction(existingItem);
+ is(newItem, lengths.getItem(0), name + " return value is correct when passed a animVal list item");
+ isnot(newItem, existingItem, name + " made a copy when passed a animVal list item");
+ is(newItem.value, existingItem.value, name + " made a copy with the right values when passed a animVal list item");
+ is(text2.x.animVal.getItem(0), existingItem, name + " left the original object alone when passed a animVal list item");
+ });
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run_tests);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_SVGMatrix.xhtml b/dom/svg/test/test_SVGMatrix.xhtml
new file mode 100644
index 0000000000..9f5b50b555
--- /dev/null
+++ b/dom/svg/test/test_SVGMatrix.xhtml
@@ -0,0 +1,180 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Test SVGMatrix behavior</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="matrixUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="1" id="svg">
+ <g id="g" transform="translate(10, 20)"/>
+ </svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function main() {
+ var tests =
+ [ testCreateMatrix,
+ testMultiply,
+ testInverse,
+ testTranslate,
+ testScale,
+ testScaleNonUniform,
+ testRotate,
+ testRotateFromVector,
+ testFlipX,
+ testFlipY,
+ testSkewX,
+ testSkewY,
+ ];
+ for (var i = 0; i < tests.length; i++) {
+ tests[i]();
+ }
+ SimpleTest.finish();
+}
+
+function testCreateMatrix() {
+ var svg = $("svg");
+ var m = svg.createSVGMatrix();
+
+ // Should be initialised to identity
+ cmpMatrix(m, [1, 0, 0, 1, 0, 0],
+ "createMatrix should produce identity matrix");
+
+ // Should return a new object each time;
+ ok(m != svg.createSVGMatrix(),
+ "Got identical objects when creating new matrix");
+}
+
+// SVGMatrix multiply(in SVGMatrix secondMatrix);
+function testMultiply() {
+ // This is the example from SVG 1.1 section 7.5
+ var m1 = createMatrix(1, 0, 0, 1, 50, 90);
+ var m2 = createMatrix(0.707, -0.707, 0.707, 0.707, 0, 0);
+ var m3 = createMatrix(1, 0, 0, 1, 130, 160);
+ var result = m1.multiply(m2).multiply(m3);
+ roughCmpMatrix(result, [0.707, -0.707, 0.707, 0.707, 255.03, 111.21],
+ "Unexpected result after multiplying matrices");
+
+ // Check orig matrices are unchanged
+ cmpMatrix(m1, [1, 0, 0, 1, 50, 90], "Matrix changed after multiplication");
+ roughCmpMatrix(m2, [0.707, -0.707, 0.707, 0.707, 0, 0],
+ "Matrix changed after multiplication");
+ cmpMatrix(m3, [1, 0, 0, 1, 130, 160], "Matrix changed after multiplication");
+}
+
+// SVGMatrix inverse() raises(SVGException);
+function testInverse() {
+ // Test inversion
+ var m = createMatrix(2, 0, 0, 4, 110, -50);
+ roughCmpMatrix(m.inverse(), [0.5, 0, 0, 0.25, -55, 12.5],
+ "Unexpected result after inverting matrix");
+
+ // Test non-invertable
+ m = createMatrix(0, 0, 1, 0, 0, 0);
+ try {
+ m.inverse();
+ ok(false, "Failed to throw exception when inverting singular matrix");
+ } catch (e) {
+ is(e.name, "InvalidStateError",
+ "Got unexpected exception " + e + ", expected InvalidStateError");
+ }
+}
+
+// SVGMatrix translate(in float x, in float y);
+function testTranslate() {
+ var m = createMatrix(2, 0, 0, 1, 120, 100);
+ roughCmpMatrix(m.translate(100, -50), [2, 0, 0, 1, 320, 50],
+ "Unexpected result after translate");
+}
+
+// SVGMatrix scale(in float scaleFactor);
+function testScale() {
+ var m = createMatrix(2, 0, 0, 1, 120, 100);
+ roughCmpMatrix(m.scale(0.5), [1, 0, 0, 0.5, 120, 100],
+ "Unexpected result after scale");
+}
+
+// SVGMatrix scaleNonUniform(in float scaleFactorX, in float scaleFactorY);
+function testScaleNonUniform() {
+ var m = createMatrix(2, 0, 0, 1, 120, 100);
+ roughCmpMatrix(m.scaleNonUniform(0.5, -3), [1, 0, 0, -3, 120, 100],
+ "Unexpected result after scaleNonUniform");
+}
+
+// SVGMatrix rotate(in float angle);
+function testRotate() {
+ var m = createMatrix(2, 0, 0, 1, 120, 100);
+ roughCmpMatrix(m.rotate(45),
+ [2 * Math.cos(Math.PI / 4), Math.sin(Math.PI / 4),
+ 2 * -Math.sin(Math.PI / 4), Math.cos(Math.PI / 4),
+ 120, 100],
+ "Unexpected result after rotate");
+}
+
+// SVGMatrix rotateFromVector(in float x, in float y) raises(SVGException);
+function testRotateFromVector() {
+ var m = createMatrix(2, 0, 0, 1, 120, 100);
+ // Make a 150 degree angle
+ var result = m.rotateFromVector(-2, 1.1547);
+ roughCmpMatrix(result,
+ [2 * Math.cos(5 * Math.PI / 6), Math.sin(5 * Math.PI / 6),
+ 2 * -Math.sin(5 * Math.PI / 6), Math.cos(5 * Math.PI / 6),
+ 120, 100],
+ "Unexpected result after rotateFromVector");
+
+ // Test bad input (1)
+ try {
+ m.rotateFromVector(1, 0);
+ ok(false, "Failed to throw exception with zero coord for rotateFromVector");
+ } catch (e) {
+ is(e.name, "InvalidAccessError",
+ "Got unexpected exception " + e + ", expected TypeError");
+ }
+
+ // Test bad input (2)
+ try {
+ m.rotateFromVector(0, 1);
+ ok(false, "Failed to throw exception with zero coord for rotateFromVector");
+ } catch (e) { }
+}
+
+// SVGMatrix flipX();
+function testFlipX() {
+ var m = createMatrix(1, 2, 3, 4, 5, 6);
+ cmpMatrix(m.flipX(), [-1, -2, 3, 4, 5, 6], "Unexpected result after flipX");
+}
+
+// SVGMatrix flipY();
+function testFlipY() {
+ var m = createMatrix(1, 2, 3, 4, 5, 6);
+ cmpMatrix(m.flipY(), [1, 2, -3, -4, 5, 6], "Unexpected result after flipY");
+}
+
+// SVGMatrix skewX(in float angle);
+function testSkewX() {
+ var m = createMatrix(2, 0, 0, 1, 120, 100);
+ roughCmpMatrix(m.skewX(30), [2, 0, 2 * Math.tan(Math.PI / 6), 1, 120, 100],
+ "Unexpected result after skewX");
+}
+
+// SVGMatrix skewY(in float angle);
+function testSkewY() {
+ var m = createMatrix(2, 0, 0, 1, 120, 100);
+ roughCmpMatrix(m.skewY(30), [2, Math.tan(Math.PI / 6), 0, 1, 120, 100],
+ "Unexpected result after skewY");
+}
+
+window.addEventListener("load", main);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_SVGNumberList.xhtml b/dom/svg/test/test_SVGNumberList.xhtml
new file mode 100644
index 0000000000..15198b5783
--- /dev/null
+++ b/dom/svg/test/test_SVGNumberList.xhtml
@@ -0,0 +1,74 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=629200
+-->
+<head>
+ <title>Tests specific to SVGNumberList</title>
+ <script src="/tests/SimpleTest/SimpleTest.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=629200">Mozilla Bug 629200</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">
+ <text id="text" rotate="10 20 30">abc</text>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+/*
+This file runs a series of SVGNumberList specific tests. Generic SVGXxxList
+tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized
+to other list types belongs there.
+*/
+
+function run_tests() {
+ document.getElementById("svg").pauseAnimations();
+
+ var text = document.getElementById("text");
+ var numbers = text.rotate.baseVal;
+
+ is(numbers.numberOfItems, 3, "Checking numberOfItems");
+
+ // Test mutation events
+ // --- Initialization
+ var eventChecker = new MutationEventChecker;
+ eventChecker.watchAttr(text, "rotate");
+ // -- Actual changes
+ eventChecker.expect("modify modify");
+ numbers[0].value = 15;
+ text.setAttribute("rotate", "17 20 30");
+ // -- Redundant changes
+ eventChecker.expect("");
+ numbers[0].value = 17;
+ numbers[1].value = 20;
+ text.setAttribute("rotate", "17 20 30");
+ // -- Invalid attribute
+ eventChecker.expect("modify");
+ text.setAttribute("rotate", ",20");
+ is(numbers.numberOfItems, 0, "Checking that parsing stops at invalid token");
+ // -- Attribute removal
+ eventChecker.expect("remove");
+ text.removeAttribute("rotate");
+ // -- Non-existent attribute removal
+ eventChecker.expect("");
+ text.removeAttribute("rotate");
+ text.removeAttributeNS(null, "rotate");
+ eventChecker.finish();
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run_tests);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_SVGPointList.xhtml b/dom/svg/test/test_SVGPointList.xhtml
new file mode 100644
index 0000000000..9a0d661eca
--- /dev/null
+++ b/dom/svg/test/test_SVGPointList.xhtml
@@ -0,0 +1,129 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=629200
+-->
+<head>
+ <title>Tests specific to SVGPointList</title>
+ <script src="/tests/SimpleTest/SimpleTest.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=629200">Mozilla Bug 629200</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">
+ <polyline id="polyline" points="50,375 150,380"/>
+ <polyline id="polyline2" points="10,20"/>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+/*
+This file runs a series of SVGPointList specific tests. Generic SVGXxxList
+tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized
+to other list types belongs there.
+*/
+
+function run_tests() {
+ document.getElementById("svg").pauseAnimations();
+
+ var polyline = document.getElementById("polyline");
+ var points = polyline.points;
+
+ is(points.numberOfItems, 2, "Checking numberOfItems");
+
+ // Test mutation events
+ // --- Initialization
+ var eventChecker = new MutationEventChecker;
+ eventChecker.watchAttr(polyline, "points");
+ // -- Actual changes
+ eventChecker.expect("modify modify");
+ points[0].x = 40;
+ polyline.setAttribute("points", "30,375 150,380");
+ // -- Redundant changes
+ eventChecker.expect("");
+ points[0].x = 30;
+ points[1].y = 380;
+ polyline.setAttribute("points", "30,375 150,380");
+ // -- Invalid attribute
+ eventChecker.expect("modify");
+ polyline.setAttribute("points", ",30,375");
+ is(points.numberOfItems, 0, "Checking that parsing stops at invalid token");
+ // -- Attribute removal
+ eventChecker.expect("remove");
+ polyline.removeAttribute("points");
+ // -- Non-existent attribute removal
+ eventChecker.expect("");
+ polyline.removeAttribute("points");
+ polyline.removeAttributeNS(null, "points");
+ eventChecker.finish();
+
+ // Test that the addition of an owned SVGPoint to an SVGPointList creates a
+ // copy of the SVGPoint
+ var polyline2 = document.getElementById("polyline2");
+ var subtests = [
+ function initialize(aItem) {
+ polyline.removeAttribute("points");
+ return points.initialize(aItem);
+ },
+ function insertItemBefore(aItem) {
+ polyline.removeAttribute("points");
+ return points.insertItemBefore(aItem, 0);
+ },
+ function replaceItem(aItem) {
+ polyline.setAttribute("points", "10,20");
+ return points.replaceItem(aItem, 0);
+ },
+ function appendItem(aItem) {
+ polyline.removeAttribute("points");
+ return points.appendItem(aItem);
+ },
+ ];
+ subtests.forEach(function(aFunction) {
+ // -- Adding SVGSVGElement.currentTranslate, which is the only instance
+ // of an owned, single SVGPoint
+ var svg = document.getElementById("svg");
+ var name = aFunction.name;
+ var existingItem = svg.currentTranslate;
+ var newItem = aFunction(existingItem);
+ is(newItem, points.getItem(0), name + " return value is correct when passed currentTranslate");
+ isnot(newItem, existingItem, name + " made a copy when passed currentTranslate");
+ is(newItem.value, existingItem.value, name + " made a copy with the right values when passed currentTranslate");
+ is(svg.currentTranslate, existingItem, name + " left the original object alone when passed currentTranslate");
+ });
+ subtests.forEach(function(aFunction) {
+ // -- Adding an SVGPoint that is in a baseVal list
+ var name = aFunction.name;
+ var existingItem = polyline2.points.getItem(0);
+ var newItem = aFunction(existingItem);
+ is(newItem, points.getItem(0), name + " return value is correct when passed a baseVal list item");
+ isnot(newItem, existingItem, name + " made a copy when passed a baseVal list item");
+ is(newItem.value, existingItem.value, name + " made a copy with the right values when passed a baseVal list item");
+ is(polyline2.points.getItem(0), existingItem, name + " left the original object alone when passed a baseVal list item");
+ });
+ subtests.forEach(function(aFunction) {
+ // -- Adding an SVGPoint that is in a animVal list
+ var name = aFunction.name;
+ var existingItem = polyline2.animatedPoints.getItem(0);
+ var newItem = aFunction(existingItem);
+ is(newItem, points.getItem(0), name + " return value is correct when passed a animVal list item");
+ isnot(newItem, existingItem, name + " made a copy when passed a animVal list item");
+ is(newItem.value, existingItem.value, name + " made a copy with the right values when passed a animVal list item");
+ is(polyline2.animatedPoints.getItem(0), existingItem, name + " left the original object alone when passed a animVal list item");
+ });
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run_tests);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_SVGStringList.xhtml b/dom/svg/test/test_SVGStringList.xhtml
new file mode 100644
index 0000000000..faba904376
--- /dev/null
+++ b/dom/svg/test/test_SVGStringList.xhtml
@@ -0,0 +1,118 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=724993-->
+<head>
+ <title>Tests specific to SVGStringList</title>
+ <script 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=724993">Mozilla Bug 724993</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">
+ <g id="g" requiredExtensions="foo bar baz"/>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+/*
+This file runs a series of SVGStringList specific tests. Generic SVGXxxList
+tests can be found in test_SVGxxxList.xhtml. Anything that can be generalized
+to other list types belongs there.
+*/
+
+function initializeThrowsFor(stringList, value) {
+ try {
+ stringList.initialize(value);
+ } catch (e) {
+ return true;
+ }
+ return false;
+}
+
+function insertItemBeforeThrowsFor(stringList, value) {
+ try {
+ stringList.insertItemBefore(value, 0);
+ } catch (e) {
+ return true;
+ }
+ return false;
+}
+
+function replaceItemThrowsFor(stringList, value) {
+ try {
+ stringList.replaceItem(value, 0);
+ } catch (e) {
+ return true;
+ }
+ return false;
+}
+
+function appendItemThrowsFor(stringList, value) {
+ try {
+ stringList.appendItem(value);
+ } catch (e) {
+ return true;
+ }
+ return false;
+}
+
+function run_tests() {
+ var g = document.getElementById("g");
+ var strings = g.requiredExtensions;
+
+ // sanity check:
+ is(strings.numberOfItems, 3, "numberOfItems should be 3");
+
+
+ ok(!initializeThrowsFor(strings, null),
+ "SVGStringList.initialize() should not throw when passed null");
+ ok(initializeThrowsFor(strings, ""),
+ "SVGStringList.initialize() should throw when passed the empty string");
+ is(strings.length, 0, "length should be 0");
+
+ ok(!insertItemBeforeThrowsFor(strings, null),
+ "SVGStringList.insertItemBefore() should not throw when passed null");
+ ok(insertItemBeforeThrowsFor(strings, ""),
+ "SVGStringList.insertItemBefore() should throw when passed the empty string");
+ is(strings.length, 1, "length should be 1");
+
+ ok(!replaceItemThrowsFor(strings, null),
+ "SVGStringList.replaceItem() should not throw when passed null");
+ ok(replaceItemThrowsFor(strings, ""),
+ "SVGStringList.replaceItem() should throw when passed the empty string");
+ is(strings.length, 1, "length should be 1");
+
+ ok(!appendItemThrowsFor(strings, null),
+ "SVGStringList.appendItem() should not throw when passed null");
+ ok(appendItemThrowsFor(strings, ""),
+ "SVGStringList.appendItem() should throw when passed the empty string");
+ is(strings.length, 2, "length should be 2");
+
+
+ // more sanity checks:
+ ok(!initializeThrowsFor(strings, "valid-string"),
+ "SVGStringList.initialize() should not throw when passed a valid string");
+ ok(!insertItemBeforeThrowsFor(strings, "valid-string"),
+ "SVGStringList.insertItemBefore() should not throw when passed a valid string");
+ ok(!replaceItemThrowsFor(strings, "valid-string"),
+ "SVGStringList.replaceItem() should not throw when passed a valid string");
+ ok(!appendItemThrowsFor(strings, "valid-string"),
+ "SVGStringList.appendItem() should not throw when passed a valid string");
+ is(strings.length, 3, "numberOfItems should be 3");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run_tests);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_SVGStyleElement.xhtml b/dom/svg/test/test_SVGStyleElement.xhtml
new file mode 100644
index 0000000000..41d449dee5
--- /dev/null
+++ b/dom/svg/test/test_SVGStyleElement.xhtml
@@ -0,0 +1,33 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=559024
+-->
+<head>
+ <title>Test SVGStyleElement</title>
+ <script 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=559024">Mozilla Bug 559024</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<svg xmlns="http://www.w3.org/2000/svg">
+<style></style>
+</svg>
+</div>
+<pre id="test">
+<script type="application/javascript">
+<![CDATA[
+/** Test for Bug 559024 **/
+var el = document.getElementsByTagNameNS("http://www.w3.org/2000/svg", "style")[0];
+el.media = "ssss";
+is(el.media, "ssss");
+is(el.getAttributeNS("", "media"), "ssss");
+el.title = "tttt";
+is(el.title, "tttt");
+is(el.getAttributeNS("", "title"), "tttt");
+]]>
+</script>
+</pre>
+</body>
+</html>
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>
diff --git a/dom/svg/test/test_SVGTransformListAddition.xhtml b/dom/svg/test/test_SVGTransformListAddition.xhtml
new file mode 100644
index 0000000000..b3b908466c
--- /dev/null
+++ b/dom/svg/test/test_SVGTransformListAddition.xhtml
@@ -0,0 +1,185 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=602759
+-->
+<head>
+ <title>Tests specific to SVGLengthList addition</title>
+ <script 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=602759">
+ Mozilla Bug 602759</a>
+<p id="display"></p>
+<div id="content">
+<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 tests specific to addition of SVGTransformList in
+animation.
+*/
+
+function AdditionTestCase(desc, baseVal, animSpecs, expectedTransformList) {
+ this.desc = desc;
+ this.baseVal = baseVal;
+ this.animSpecs = animSpecs;
+ this.expectedTransformList = expectedTransformList;
+}
+
+function Transform(type, angle) {
+ this.type = type;
+ this.angle = angle;
+}
+
+function main(g) {
+ var cases = [
+ new AdditionTestCase("Not additive",
+ "translate(150 50)",
+ {type: "rotate", from: "0", to: "90"},
+ [new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 90)]
+ ),
+ new AdditionTestCase("To animation",
+ "rotate(-90)",
+ {type: "rotate", to: "90"},
+ [new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 90)]
+ ),
+ new AdditionTestCase("By animation",
+ "rotate(-90)",
+ {type: "rotate", by: "180"},
+ [new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, -90),
+ new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 180)]
+ ),
+ new AdditionTestCase("Normal additive: same type",
+ "rotate(45)",
+ {type: "rotate", from: "0", to: "45", additive: "sum"},
+ [new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45),
+ new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45)]
+ ),
+ new AdditionTestCase("Normal additive: different type",
+ "translate(50)",
+ {type: "rotate", from: "0", to: "90", additive: "sum"},
+ [new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0),
+ new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 90)]
+ ),
+ new AdditionTestCase("Stacked additive: same type",
+ "rotate(-90)",
+ [{type: "rotate", from: "0", to: "90", additive: "sum"},
+ {type: "rotate", from: "0", to: "90", additive: "sum"}],
+ [new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, -90),
+ new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 90),
+ new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 90)]
+ ),
+ new AdditionTestCase("Stacked additive: different types #1",
+ "translate(50)",
+ [{type: "rotate", from: "0", to: "45", additive: "sum"},
+ {type: "rotate", from: "0", to: "45", additive: "sum"}],
+ [new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0),
+ new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45),
+ new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45)]
+ ),
+ new AdditionTestCase("Stacked additive: different types #2",
+ "skewX(20) translate(50)",
+ [{type: "rotate", from: "0", to: "45", additive: "sum"},
+ {type: "rotate", from: "0", to: "45", additive: "sum"}],
+ [new Transform(SVGTransform.SVG_TRANSFORM_SKEWX, 20),
+ new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0),
+ new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45),
+ new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45)]
+ ),
+ new AdditionTestCase("Stacked additive: different types #3",
+ "skewX(20) translate(50)",
+ [{type: "rotate", from: "0", to: "45", additive: "sum"},
+ {type: "translate", from: "0", to: "30", additive: "sum"},
+ {type: "translate", from: "0", to: "-30", additive: "sum"},
+ {type: "rotate", from: "0", to: "45", additive: "sum"}],
+ [new Transform(SVGTransform.SVG_TRANSFORM_SKEWX, 20),
+ new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0),
+ new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45),
+ new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0),
+ new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0),
+ new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 45)]
+ ),
+ new AdditionTestCase("Base value with rotation around a centre",
+ "rotate(90 50 50)",
+ {type: "translate", from: "0 0", to: "0 -50", additive: "sum"},
+ [new Transform(SVGTransform.SVG_TRANSFORM_ROTATE, 90),
+ new Transform(SVGTransform.SVG_TRANSFORM_TRANSLATE, 0)]
+ ),
+ ];
+
+ for (var i = 0; i < cases.length; i++) {
+ runAdditionTestCase(cases[i], $("g"), $("svg"));
+ }
+
+ SimpleTest.finish();
+}
+
+function runAdditionTestCase(test, elem, svg) {
+ var anims = createAnims(test.animSpecs);
+
+ elem.setAttribute("transform", test.baseVal);
+ elem.appendChild(anims);
+
+ svg.setCurrentTime(1);
+ var expected = test.expectedTransformList; // Array of Transform objects
+ var actual = elem.transform.animVal; // SVGTransformList
+ is(actual.numberOfItems, expected.length,
+ "Unexpected number of transforms");
+
+ if (actual.numberOfItems == expected.length) {
+ for (var i = 0; i < actual.numberOfItems; i++) {
+ var transform = actual.getItem(i);
+ var testDesc = " for transform " + i + " in '" + test.desc + "' test";
+ is(transform.type, expected[i].type,
+ "Unexpected transform type" + testDesc);
+ is(transform.angle, expected[i].angle,
+ "Unexpected transform angle" + testDesc);
+ }
+ }
+ // We assume the only children of elem are the animation elements we've just
+ // added.
+ while (elem.firstChild) {
+ elem.firstChild.remove();
+ }
+}
+
+function createAnims(specs) {
+ if (specs.constructor == Array) {
+ var frag = document.createDocumentFragment();
+ for (var i = 0; i < specs.length; ++i) {
+ frag.appendChild(createAnim(specs[i]));
+ }
+ return frag;
+ }
+
+ return createAnim(specs);
+}
+
+function createAnim(attrs) {
+ var SVG_NS = "http://www.w3.org/2000/svg";
+ var anim = document.createElementNS(SVG_NS, "animateTransform");
+ anim.setAttribute("attributeName", "transform");
+ anim.setAttribute("dur", "1s");
+ anim.setAttribute("fill", "freeze");
+ for (let attr in attrs) {
+ anim.setAttribute(attr, attrs[attr]);
+ }
+ return anim;
+}
+
+window.addEventListener("load", main);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_SVG_namespace_ids.html b/dom/svg/test/test_SVG_namespace_ids.html
new file mode 100644
index 0000000000..43b2684649
--- /dev/null
+++ b/dom/svg/test/test_SVG_namespace_ids.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=589640
+-->
+<head>
+ <title>Test for Bug 589640</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ function debug(message) {
+ document.getElementById("debug").appendChild(document.createTextNode(message + "\n"));
+ }
+
+ function runTests() {
+ var svg = document.getElementById("svg1");
+ for (var el = 0; el < svg.children.length; el++) {
+ is(svg.children[el].id, svg.children[el].localName, svg.children[el].localName + " in the SVG namespace has a valid ID");
+ debug(svg.children[el].localName + ".id = " + svg.children[el].id);
+ }
+
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body onload="runTests()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=589640">Mozilla Bug 589640</a>
+<pre id="debug"></pre>
+<!-- NOTE: This test relies on the ids being the same as the element names -->
+<svg id="svg1">
+ <a id="a" />
+ <animate id="animate" />
+ <animateColor id="animateColor" />
+ <animateMotion id="animateMotion" />
+ <animateTransform id="animateTransform" />
+ <circle id="circle" />
+ <clipPath id="clipPath" />
+ <color-profile id="color-profile" />
+ <cursor id="cursor" />
+ <defs id="defs" />
+ <desc id="desc" />
+ <ellipse id="ellipse" />
+ <feBlend id="feBlend" />
+ <feColorMatrix id="feColorMatrix" />
+ <feComponentTransfer id="feComponentTransfer" />
+ <feComposite id="feComposite" />
+ <feConvolveMatrix id="feConvolveMatrix" />
+ <feDiffuseLighting id="feDiffuseLighting" />
+ <feDisplacementMap id="feDisplacementMap" />
+ <feDistantLight id="feDistantLight" />
+ <feDropShadow id="feDropShadow" />
+ <feFlood id="feFlood" />
+ <feFuncA id="feFuncA" />
+ <feFuncB id="feFuncB" />
+ <feFuncG id="feFuncG" />
+ <feFuncR id="feFuncR" />
+ <feGaussianBlur id="feGaussianBlur" />
+ <feImage id="feImage" />
+ <feMerge id="feMerge" />
+ <feMergeNode id="feMergeNode" />
+ <feMorphology id="feMorphology" />
+ <feOffset id="feOffset" />
+ <fePointLight id="fePointLight" />
+ <feSpecularLighting id="feSpecularLighting" />
+ <feSpotLight id="feSpotLight" />
+ <feTile id="feTile" />
+ <feTurbulence id="feTurbulence" />
+ <filter id="filter" />
+ <font id="font" />
+ <font-face id="font-face" />
+ <font-face-format id="font-face-format" />
+ <font-face-name id="font-face-name" />
+ <font-face-src id="font-face-src" />
+ <font-face-uri id="font-face-uri" />
+ <foreignObject id="foreignObject" />
+ <g id="g" />
+ <glyph id="glyph" />
+ <glyphRef id="glyphRef" />
+ <hkern id="hkern" />
+ <image id="image" />
+ <line id="line" />
+ <linearGradient id="linearGradient" />
+ <marker id="marker" />
+ <mask id="mask" />
+ <metadata id="metadata" />
+ <missing-glyph id="missing-glyph" />
+ <mpath id="mpath" />
+ <path id="path" />
+ <pattern id="pattern" />
+ <polygon id="polygon" />
+ <polyline id="polyline" />
+ <radialGradient id="radialGradient" />
+ <rect id="rect" />
+ <script id="script" />
+ <set id="set" />
+ <stop id="stop" />
+ <style id="style" />
+ <svg id="svg" />
+ <switch id="switch" />
+ <symbol id="symbol" />
+ <text id="text" />
+ <textPath id="textPath" />
+ <title id="title" />
+ <tref id="tref" />
+ <tspan id="tspan" />
+ <use id="use" />
+ <view id="view" />
+ <vkern id="vkern" />
+</svg>
+</body>
+</html>
diff --git a/dom/svg/test/test_SVGxxxList.xhtml b/dom/svg/test/test_SVGxxxList.xhtml
new file mode 100644
index 0000000000..0465ddb4bb
--- /dev/null
+++ b/dom/svg/test/test_SVGxxxList.xhtml
@@ -0,0 +1,1372 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=515116
+-->
+<head>
+ <title>Generic tests for SVG animated length lists</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=515116">Mozilla Bug 515116</a>
+<p id="display"></p>
+<div id="content">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="100" height="100"
+ onload="this.pauseAnimations();">
+ <defs>
+ <filter>
+ <feComponentTransfer>
+ <feFuncR id="feFuncR" type="table"/>
+ </feComponentTransfer>
+ </filter>
+ </defs>
+ <text id="text">text</text>
+ <path id="path"/>
+ <polyline id="polyline"/>
+ <g id="g"/>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+
+SimpleTest.waitForExplicitFinish();
+
+/*
+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.
+
+For additional high level information on the tests that are run, see the comment for 'create_animate_elements' below.
+
+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:
+
+ target_element_id
+ The ID of the element that has the attribute that is to be tested.
+ attr_name
+ The name of the attribute that is to be tested.
+ prop_name
+ The name of the DOM property that corresponds to the attribute that is to
+ be tested. For some list types the SVGAnimatedXxxList interface is
+ inherited by the element interface rather than the element having a
+ property of that type, and in others the list type is not animatable so
+ there is no SVGAnimatedXxxList interface for that list type. In these
+ cases this property should be set to null.
+ bv_name
+ The name of the DOM base value property for the attribute that is to be
+ tested. This is usually 'baseVal', but not always. In the case of
+ SVGStringList, which is not animatable, this is the name of the
+ SVGStringList property.
+ av_name
+ The name of the DOM anim value property for the attribute that is to be
+ tested. This is usually 'animVal' but not always. In the case of
+ SVGStringList, which is not animatable, this should be set to null.
+ el_type
+ The name of the SVGXxxElement interface on which the property corresponding
+ to the attribute being tested is defined.
+ prop_type
+ The name of the SVGAnimatedXxxList interface (e.g. SVGAnimatedLengthList),
+ if it exists, and if the element has a property is of this type (as
+ opposed to the element interface inheriting it).
+ list_type
+ The name of the SVGXxxList interface implemented by the baseVal and
+ animVal objects.
+ item_type
+ The name of the SVGXxx interface implemented by the list items.
+ attr_val_3a:
+ attr_val_3b:
+ Two attribute values containing three different items.
+ attr_val_4
+ An attribute value containing four items.
+ attr_val_5a:
+ attr_val_5b:
+ Two attribute values containing five different items.
+ attr_val_5b_firstItem_x3_constructor:
+ Function to construct a list-item that should match the first item in a
+ SVGXxxList after three repeats of a cumulative animation to attr_val_5b.
+ This function takes t.item_constructor as its only argument.
+ item_constructor:
+ Function to create a dummy list item.
+ item_is:
+ Function to compare two list items for equality, like "is()". If this
+ property is omitted, it is assumed that we can just compare
+ "item.value" (which is the case for most list types).
+*/
+// helper method
+function keys(obj) {
+ var rval = [];
+ for (var prop in obj) {
+ rval.push(prop);
+ }
+ return rval;
+}
+
+var tests = [
+ {
+ // SVGLengthList test:
+ target_element_id: "text",
+ attr_name: "x",
+ prop_name: "x",
+ bv_name: "baseVal",
+ av_name: "animVal",
+ el_type: "SVGTextElement",
+ prop_type: "SVGAnimatedLengthList",
+ list_type: "SVGLengthList",
+ item_type: "SVGLength",
+ attr_val_3a: "10 20ex, 30in",
+ attr_val_3b: "30in 10, 20ex",
+ attr_val_4: "10 20ex, 30in ,40cm",
+ attr_val_5a: "10 20ex, 30in ,40cm , 50%",
+ attr_val_5b: "20 50%, 20ex ,30in , 40cm",
+ attr_val_5b_firstItem_x3_constructor(constructor) {
+ var expected = constructor();
+ expected.value = 60;
+ return expected;
+ },
+ item_constructor() {
+ // We need this function literal to avoid "Illegal operation on
+ // WrappedNative prototype object" NS_ERROR_XPC_BAD_OP_ON_WN_PROTO.
+ return document.getElementById("svg").createSVGLength();
+ },
+ },
+ {
+ // SVGNumberList test:
+ target_element_id: "text",
+ attr_name: "rotate",
+ prop_name: "rotate",
+ bv_name: "baseVal",
+ av_name: "animVal",
+ el_type: "SVGTextElement",
+ prop_type: "SVGAnimatedNumberList",
+ list_type: "SVGNumberList",
+ item_type: "SVGNumber",
+ attr_val_3a: "0 20 40",
+ attr_val_3b: "60 40 20",
+ attr_val_4: "40 20 10 80",
+ attr_val_5a: "90 30 60 20 70",
+ attr_val_5b: "30 20 70 30 90",
+ attr_val_5b_firstItem_x3_constructor(constructor) {
+ var expected = constructor();
+ expected.value = 90;
+ return expected;
+ },
+ item_constructor() {
+ // We need this function literal to avoid "Illegal operation on
+ // WrappedNative prototype object" NS_ERROR_XPC_BAD_OP_ON_WN_PROTO.
+ return document.getElementById("svg").createSVGNumber();
+ },
+ },
+ {
+ // SVGNumberList test:
+ target_element_id: "feFuncR",
+ attr_name: "tableValues",
+ prop_name: "tableValues",
+ bv_name: "baseVal",
+ av_name: "animVal",
+ el_type: "SVGFEComponentTransferElement",
+ prop_type: "SVGAnimatedNumberList",
+ list_type: "SVGNumberList",
+ item_type: "SVGNumber",
+ attr_val_3a: "0 .5 .2",
+ attr_val_3b: "1 .7 .1",
+ attr_val_4: ".5 .3 .8 .2",
+ attr_val_5a: "3 4 5 6 7",
+ attr_val_5b: "7 6 5 4 3",
+ attr_val_5b_firstItem_x3_constructor(constructor) {
+ var expected = constructor();
+ expected.value = 21;
+ return expected;
+ },
+ item_constructor() {
+ // We need this function literal to avoid "Illegal operation on
+ // WrappedNative prototype object" NS_ERROR_XPC_BAD_OP_ON_WN_PROTO.
+ return document.getElementById("svg").createSVGNumber();
+ },
+ },
+ {
+ // SVGPointList test:
+ target_element_id: "polyline",
+ attr_name: "points",
+ prop_name: null, // SVGAnimatedPoints is an inherited interface!
+ bv_name: "points",
+ av_name: "animatedPoints",
+ el_type: "SVGPolylineElement",
+ prop_type: null,
+ list_type: "SVGPointList",
+ item_type: "SVGPoint",
+ attr_val_3a: " 10,10 50,50 90,10 ",
+ attr_val_3b: " 10,50 50,10 90,50 ",
+ attr_val_4: " 10,10 50,50 90,10 200,100 ",
+ attr_val_5a: " 10,10 50,50 90,10 130,50 170,10 ",
+ attr_val_5b: " 50,10 50,10 90,50 130,10 170,50 ",
+ attr_val_5b_firstItem_x3_constructor(constructor) {
+ var expected = constructor();
+ expected.x = 150;
+ expected.y = 30;
+ return expected;
+ },
+ item_constructor() {
+ // XXX return different values each time
+ return document.getElementById("svg").createSVGPoint();
+ },
+ item_is(itemA, itemB, message) {
+ ok(typeof(itemA.x) != "undefined" &&
+ typeof(itemB.x) != "undefined",
+ "expecting x property");
+ ok(typeof(itemA.y) != "undefined" &&
+ typeof(itemB.y) != "undefined",
+ "expecting y property");
+
+ is(itemA.x, itemB.x, message);
+ is(itemA.y, itemB.y, message);
+ },
+ },
+ {
+ // SVGStringList test:
+ target_element_id: "g",
+ attr_name: "requiredExtensions", // systemLanguage, viewTarget
+ prop_name: null, // SVGStringList attributes are not animatable
+ bv_name: "requiredExtensions",
+ av_name: null,
+ el_type: "SVGGElement",
+ prop_type: null,
+ list_type: "SVGStringList",
+ item_type: "DOMString",
+ attr_val_3a: "http://www.w3.org/TR/SVG11/feature#Shape http://www.w3.org/TR/SVG11/feature#Image " +
+ "http://www.w3.org/TR/SVG11/feature#Style",
+ attr_val_3b: "http://www.w3.org/TR/SVG11/feature#CoreAttribute http://www.w3.org/TR/SVG11/feature#Structure " +
+ "http://www.w3.org/TR/SVG11/feature#Gradient",
+ attr_val_4: "http://www.w3.org/TR/SVG11/feature#Pattern http://www.w3.org/TR/SVG11/feature#Clip " +
+ "http://www.w3.org/TR/SVG11/feature#Mask http://www.w3.org/TR/SVG11/feature#Extensibility",
+ attr_val_5a: "http://www.w3.org/TR/SVG11/feature#BasicStructure http://www.w3.org/TR/SVG11/feature#BasicText " +
+ "http://www.w3.org/TR/SVG11/feature#BasicPaintAttribute http://www.w3.org/TR/SVG11/feature#BasicGraphicsAttribute " +
+ "http://www.w3.org/TR/SVG11/feature#BasicClip",
+ attr_val_5b: "http://www.w3.org/TR/SVG11/feature#DocumentEventsAttribute http://www.w3.org/TR/SVG11/feature#GraphicalEventsAttribute " +
+ "http://www.w3.org/TR/SVG11/feature#AnimationEventsAttribute http://www.w3.org/TR/SVG11/feature#Hyperlinking " +
+ "http://www.w3.org/TR/SVG11/feature#XlinkAttribute",
+ item_constructor() {
+ return "http://www.w3.org/TR/SVG11/feature#XlinkAttribute";
+ },
+ },
+ {
+ // SVGTransformList test:
+ target_element_id: "g",
+ attr_name: "transform", // gradientTransform, patternTransform
+ prop_name: "transform",
+ bv_name: "baseVal",
+ av_name: "animVal",
+ el_type: "SVGGElement",
+ prop_type: "SVGAnimatedTransformList",
+ list_type: "SVGTransformList",
+ item_type: "SVGTransform",
+ attr_val_3a: "translate(20 10) rotate(90 10 10) skewX(45)",
+ attr_val_3b: "translate(30 40) scale(2) matrix(1 2 3 4 5 6)",
+ attr_val_4: "scale(3 2) translate(19) skewY(2) rotate(-10)",
+ attr_val_5a:
+ "translate(20) rotate(-10) skewY(3) matrix(1 2 3 4 5 6) scale(0.5)",
+ attr_val_5b:
+ "skewX(45) rotate(45 -10 -10) skewX(-45) scale(2) matrix(6 5 4 3 2 1)",
+ // SVGTransformList animation addition is tested in
+ // test_SVGTransformListAddition.xhtml so we don't need:
+ // - attr_val_3b
+ // - attr_val_3b
+ // - attr_val_5b_firstItem_x3_constructor
+ // But we populate the first two anyway just in case they are later used for
+ // something other than testing animation.
+ // attr_val_5b_firstItem_x3_constructor is only used for animation
+ item_constructor() {
+ // XXX populate the matrix with different values each time
+ return document.getElementById("svg").createSVGTransform();
+ },
+ item_is(itemA, itemB, message) {
+ ok(typeof(itemA.type) != "undefined" &&
+ typeof(itemB.type) != "undefined",
+ "expecting type property");
+ ok(typeof(itemA.matrix) != "undefined" &&
+ typeof(itemB.matrix) != "undefined",
+ "expecting matrix property");
+ ok(typeof(itemA.angle) != "undefined" &&
+ typeof(itemB.angle) != "undefined",
+ "expecting matrix property");
+
+ is(itemA.type, itemB.type, message);
+ is(itemA.angle, itemB.angle, message);
+ cmpMatrix(itemA.matrix, itemB.matrix, message);
+ },
+ },
+];
+
+
+/*
+This function returns a DocumentFragment with three 'animate' element children. The duration of the three animations is as follows:
+
+ animation 1: | *-----------*-----------*-----------*
+ animation 2: | *--*
+ animation 3: | *--*
+ |___________________________________________> time (s)
+ | | | | | | | | | | | | | | |
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
+
+The first animation repeats once so that we can test state on a repeat animation.
+
+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.
+
+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?
+
+At t=0s and t=1s we test the effect of an attribute value changes in the absence and presence of SMIL animation respectively.
+
+At t=10s we programmatically remove the fill="freeze" from animation 1.
+*/
+function create_animate_elements(test) {
+ var SVG_NS = "http://www.w3.org/2000/svg";
+ var df = document.createDocumentFragment();
+
+ if (is_transform_attr(test.attr_name)) {
+ // animateTransform is "special". Although it targets an
+ // SVGAnimatedTransformList it only takes SVGTransform values as
+ // animation values. Therefore all the assumptions we're testing about the
+ // length of lists don't apply. We simply have to test it separately.
+ // This is done in test_SVGTransformListAddition.xhtml.
+ return df; // Return the empty document fragment
+ }
+
+ var animate1 = document.createElementNS(SVG_NS, "animate");
+ var animate2 = document.createElementNS(SVG_NS, "animate");
+ var animate3 = document.createElementNS(SVG_NS, "animate");
+
+ animate1.setAttribute("attributeName", test.attr_name);
+ animate1.setAttribute("from", test.attr_val_5a);
+ animate1.setAttribute("to", test.attr_val_5b);
+ animate1.setAttribute("begin", "1s");
+ animate1.setAttribute("dur", "4s");
+ animate1.setAttribute("repeatCount", "3");
+ animate1.setAttribute("accumulate", "sum");
+ animate1.setAttribute("fill", "freeze");
+ df.appendChild(animate1);
+
+ animate2.setAttribute("attributeName", test.attr_name);
+ animate2.setAttribute("from", test.attr_val_3a);
+ animate2.setAttribute("to", test.attr_val_3b);
+ animate2.setAttribute("begin", "2s");
+ animate2.setAttribute("dur", "1s");
+ df.appendChild(animate2);
+
+ animate3.setAttribute("attributeName", test.attr_name);
+ animate3.setAttribute("from", test.attr_val_3a);
+ animate3.setAttribute("to", test.attr_val_3b);
+ animate3.setAttribute("begin", "7s");
+ animate3.setAttribute("dur", "1s");
+ animate3.setAttribute("additive", "sum");
+ df.appendChild(animate3);
+
+ return df;
+}
+
+function is_transform_attr(attr_name) {
+ return attr_name == "transform" ||
+ attr_name == "gradientTransform" ||
+ attr_name == "patternTransform";
+}
+
+function get_array_of_list_items(list) {
+ let array = [];
+ for (var i = 0; i < list.numberOfItems; ++i) {
+ array.push(list.getItem(i));
+ }
+ return array;
+}
+
+
+/**
+ * This function tests the SVGXxxList API for the base val list. This means
+ * running tests for the following property and methods:
+ *
+ * numberOfItems
+ * clear()
+ * SVGLength initialize(in SVGLength newItem)
+ * SVGLength getItem(in unsigned long index)
+ * SVGLength insertItemBefore(in SVGLength newItem, in unsigned long index)
+ * SVGLength replaceItem(in SVGLength newItem, in unsigned long index)
+ * SVGLength removeItem(in unsigned long index)
+ * SVGLength appendItem(in SVGLength newItem)
+ *
+ * @param t A test from the 'tests' array.
+ */
+function run_baseVal_API_tests() {
+ var res, threw;
+ var eventChecker = new MutationEventChecker;
+
+ for (var t of tests) {
+ // Test .clear():
+
+ t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+ is(t.baseVal.numberOfItems, 4,
+ "The " + t.list_type + " object should contain four list items.");
+
+ eventChecker.watchAttr(t.element, t.attr_name);
+ eventChecker.expect("modify");
+ res = t.baseVal.clear();
+
+ is(t.baseVal.numberOfItems, 0,
+ "The method " + t.list_type + ".clear() should clear the " + t.list_type +
+ " object.");
+ is(res, undefined,
+ "The method " + t.list_type + ".clear() should not return a value.");
+ ok(t.element.hasAttribute(t.attr_name),
+ "The method " + t.list_type + ".clear() should not remove the attribute.");
+ ok(t.element.getAttribute(t.attr_name) === "",
+ "Cleared " + t.attr_name + " (" + t.list_type + ") but did not get an " +
+ "empty string back.");
+
+ eventChecker.expect("");
+ t.baseVal.clear();
+ eventChecker.ignoreEvents();
+
+ // Test empty strings
+
+ t.element.setAttribute(t.attr_name, "");
+ ok(t.element.getAttribute(t.attr_name) === "",
+ "Set an empty attribute value for " + t.attr_name + " (" + t.list_type +
+ ") but did not get an empty string back.");
+
+ // Test removed attributes
+
+ t.element.removeAttribute(t.attr_name);
+ ok(t.element.getAttribute(t.attr_name) === null,
+ "Removed attribute value for " + t.attr_name + " (" + t.list_type +
+ ") but did not get null back.");
+ ok(!t.element.hasAttribute(t.attr_name),
+ "Removed attribute value for " + t.attr_name + " (" + t.list_type +
+ ") but hasAttribute still returns true.");
+
+ // Test .initialize():
+
+ t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+ var item = t.item_constructor();
+ // Our current implementation of 'initialize' for most list types performs
+ // a 'clear' followed by an 'insertItemBefore'. This results in two
+ // modification events being dispatched. SVGStringList however avoids the
+ // additional clear.
+ var expectedModEvents =
+ t.item_type == "DOMString" ? "modify" : "modify modify";
+ eventChecker.expect(expectedModEvents);
+ res = t.baseVal.initialize(item);
+ eventChecker.ignoreEvents();
+
+
+ is(t.baseVal.numberOfItems, 1,
+ "The " + t.list_type + " object should contain one list item.");
+ ok(res === item,
+ "The list item returned by " + t.list_type + ".initialize() should be the " +
+ "exact same object as the item that was passed to that method, since " +
+ "the item that was passed to that method did not already belong to a " +
+ "list.");
+ ok(t.baseVal.getItem(0) === item,
+ "The list item at index 0 should be the exact same object as the " +
+ "object that was passed to the " + t.list_type + ".initialize() method, " +
+ "since the item that was passed to that method did not already " +
+ "belong to a list.");
+
+ t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+ if (t.item_type != "DOMString") {
+ var old_items = get_array_of_list_items(t.baseVal);
+ item = t.baseVal.getItem(3);
+ res = t.baseVal.initialize(item);
+
+ ok(res !== item &&
+ t.baseVal.getItem(0) !== item &&
+ t.baseVal.getItem(0) !== old_items[0] &&
+ res === t.baseVal.getItem(0),
+ "The method " + t.list_type + ".initialize() should clone the object that " +
+ "is passed in if that object is already in a list.");
+ // [SVGWG issue] not what the spec currently says
+
+
+ item = t.baseVal.getItem(0);
+ res = t.baseVal.initialize(item);
+
+ ok(res !== item &&
+ t.baseVal.getItem(0) !== item,
+ "The method " + t.list_type + ".initialize() should clone the object that " +
+ "is passed in, even if that object is the only item in that list.");
+ // [SVGWG issue] not what the spec currently says
+
+ eventChecker.expect("");
+ threw = false;
+ try {
+ t.baseVal.initialize({});
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.list_type + ".initialize() should throw if passed an " +
+ "object of the wrong type.");
+ eventChecker.ignoreEvents();
+ }
+
+ // Test .insertItemBefore():
+
+ t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+ old_items = get_array_of_list_items(t.baseVal);
+ item = t.item_constructor();
+ eventChecker.expect("modify");
+ res = t.baseVal.insertItemBefore(item, 2);
+ eventChecker.ignoreEvents();
+
+ is(t.baseVal.numberOfItems, 5,
+ "The " + t.list_type + " object should contain five list items.");
+ ok(res === item,
+ "The list item returned by " + t.list_type + ".insertItemBefore() should " +
+ "be the exact same object as the item that was passed to that method, " +
+ "since the item that was passed to that method did not already belong " +
+ "to a list.");
+ ok(t.baseVal.getItem(2) === item,
+ "The list item at index 2 should be the exact same object as the " +
+ "object that was passed to the " + t.list_type + ".insertItemBefore() " +
+ "method, since the item that was passed to that method did not " +
+ "already belong to a list.");
+ ok(t.baseVal.getItem(3) === old_items[2],
+ "The list item that was at index 2 should be at index 3 after " +
+ "inserting a new item at index 2 using the " + t.list_type +
+ ".insertItemBefore() method.");
+
+ item = t.item_constructor();
+ t.baseVal.insertItemBefore(item, 100);
+
+ ok(t.baseVal.getItem(5) === item,
+ "When the index passed to the " + t.list_type + ".insertItemBefore() " +
+ "method is out of bounds, the supplied list item should be appended " +
+ "to the list.");
+
+ item = t.baseVal.getItem(4);
+ res = t.baseVal.insertItemBefore(item, 2);
+
+ is(t.baseVal.numberOfItems, 7,
+ "The " + t.list_type + " object should contain seven list items.");
+ if (t.item_type != "DOMString") {
+ ok(res !== item &&
+ t.baseVal.getItem(2) !== item &&
+ t.baseVal.getItem(2) !== old_items[2] &&
+ res === t.baseVal.getItem(2),
+ "The method " + t.list_type + ".insertItemBefore() should clone the " +
+ "object that is passed in if that object is already in a list.");
+ // [SVGWG issue] not what the spec currently says
+ }
+
+ item = t.baseVal.getItem(2);
+ res = t.baseVal.insertItemBefore(item, 2);
+
+ is(t.baseVal.numberOfItems, 8,
+ "The " + t.list_type + " object should contain eight list items.");
+ if (t.item_type != "DOMString") {
+ ok(res !== item &&
+ t.baseVal.getItem(2) !== item,
+ "The method " + t.list_type + ".insertItemBefore() should clone the " +
+ "object that is passed in, even if that object is the item in " +
+ "the list at the index specified.");
+ // [SVGWG issue] not what the spec currently says
+
+ eventChecker.expect("");
+ threw = false;
+ try {
+ t.baseVal.insertItemBefore({}, 2);
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.list_type + ".insertItemBefore() should throw if passed " +
+ "an object of the wrong type.");
+ eventChecker.ignoreEvents();
+ }
+
+ // Test .replaceItem():
+
+ t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+ old_items = get_array_of_list_items(t.baseVal);
+ item = t.item_constructor();
+ eventChecker.expect("modify");
+ res = t.baseVal.replaceItem(item, 2);
+ eventChecker.ignoreEvents();
+
+ is(t.baseVal.numberOfItems, 4,
+ "The " + t.list_type + " object should contain four list items.");
+ if (t.item_type != "DOMString") {
+ ok(res === item,
+ "The list item returned by " + t.list_type + ".replaceItem() should be " +
+ "the exact same object as the item that was passed to that method, " +
+ "since the item that was passed to that method did not already belong " +
+ "to a list.");
+ }
+ ok(t.baseVal.getItem(2) === item,
+ "The list item at index 2 should be the exact same object as the " +
+ "object that was passed to the " + t.list_type + ".replaceItem() method, " +
+ "since the item that was passed to that method did not already belong " +
+ "to a list.");
+ ok(t.baseVal.getItem(3) === old_items[3],
+ "The list item that was at index 3 should still be at index 3 after " +
+ "the item at index 2 was replaced using the " + t.list_type +
+ ".replaceItem() method.");
+
+ item = t.item_constructor();
+
+ eventChecker.expect("");
+ threw = false;
+ try {
+ t.baseVal.replaceItem(item, 100);
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.list_type + ".replaceItem() should throw if passed " +
+ "an index that is out of bounds.");
+ eventChecker.ignoreEvents();
+
+ old_items = get_array_of_list_items(t.baseVal);
+ item = t.baseVal.getItem(3);
+ res = t.baseVal.replaceItem(item, 1);
+
+ is(t.baseVal.numberOfItems, 4,
+ "The " + t.list_type + " object should contain four list items.");
+ if (t.item_type != "DOMString") {
+ ok(res !== item &&
+ t.baseVal.getItem(1) !== item &&
+ t.baseVal.getItem(1) !== old_items[1] &&
+ res === t.baseVal.getItem(1),
+ "The method " + t.list_type + ".replaceItem() should clone the object " +
+ "that is passed in if that object is already in a list.");
+ // [SVGWG issue] not what the spec currently says
+ }
+
+ item = t.baseVal.getItem(1);
+ res = t.baseVal.replaceItem(item, 1);
+
+ is(t.baseVal.numberOfItems, 4,
+ "The " + t.list_type + " object should contain four list items.");
+ if (t.item_type != "DOMString") {
+ ok(res !== item &&
+ t.baseVal.getItem(1) !== item,
+ "The method " + t.list_type + ".replaceItem() should clone the object " +
+ "that is passed in, even if the object that object and the object " +
+ "that is being replaced are the exact same objects.");
+ // [SVGWG issue] not what the spec currently says
+
+ threw = false;
+ try {
+ t.baseVal.replaceItem({}, 2);
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.list_type + ".replaceItem() should throw if passed " +
+ "an object of the wrong type.");
+ }
+
+ // Test .removeItem():
+
+ t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+ old_items = get_array_of_list_items(t.baseVal);
+ item = t.baseVal.getItem(2);
+ eventChecker.expect("modify");
+ res = t.baseVal.removeItem(2);
+ eventChecker.ignoreEvents();
+
+ is(t.baseVal.numberOfItems, 3,
+ "The " + t.list_type + " object should contain three list items.");
+ if (t.item_type != "DOMString") {
+ ok(res === item,
+ "The list item returned by " + t.list_type + ".removeItem() should be the " +
+ "exact same object as the item that was at the specified index.");
+ }
+ ok(t.baseVal.getItem(1) === old_items[1],
+ "The list item that was at index 1 should still be at index 1 after " +
+ "the item at index 2 was removed using the " + t.list_type +
+ ".replaceItem() method.");
+ ok(t.baseVal.getItem(2) === old_items[3],
+ "The list item that was at index 3 should still be at index 2 after " +
+ "the item at index 2 was removed using the " + t.list_type +
+ ".replaceItem() method.");
+
+ eventChecker.expect("");
+ threw = false;
+ try {
+ t.baseVal.removeItem(100);
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.list_type + ".removeItem() should throw if passed " +
+ "an index that is out of bounds.");
+ eventChecker.ignoreEvents();
+
+ // Test .appendItem():
+
+ t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+ old_items = get_array_of_list_items(t.baseVal);
+ item = t.item_constructor();
+ eventChecker.expect("modify");
+ res = t.baseVal.appendItem(item);
+ eventChecker.ignoreEvents();
+
+ is(t.baseVal.numberOfItems, 5,
+ "The " + t.list_type + " object should contain five list items.");
+ ok(res === item,
+ "The list item returned by " + t.list_type + ".appendItem() should be the " +
+ "exact same object as the item that was passed to that method, since " +
+ "the item that was passed to that method did not already belong " +
+ "to a list.");
+ ok(t.baseVal.getItem(4) === item,
+ "The last list item should be the exact same object as the object " +
+ "that was passed to the " + t.list_type + ".appendItem() method, since " +
+ "the item that was passed to that method did not already belong to " +
+ "a list.");
+ ok(t.baseVal.getItem(3) === old_items[3],
+ "The list item that was at index 4 should still be at index 4 after " +
+ "appending a new item using the " + t.list_type + ".appendItem() " +
+ "method.");
+
+ item = t.baseVal.getItem(2);
+ res = t.baseVal.appendItem(item);
+
+ is(t.baseVal.numberOfItems, 6,
+ "The " + t.list_type + " object should contain six list items.");
+ if (t.item_type != "DOMString") {
+ ok(res !== item &&
+ t.baseVal.getItem(5) !== item &&
+ res === t.baseVal.getItem(5),
+ "The method " + t.list_type + ".appendItem() should clone the object " +
+ "that is passed in if that object is already in a list.");
+ // [SVGWG issue] not what the spec currently says
+ }
+
+ item = t.baseVal.getItem(5);
+ res = t.baseVal.appendItem(item);
+
+ is(t.baseVal.numberOfItems, 7,
+ "The " + t.list_type + " object should contain seven list items.");
+ if (t.item_type != "DOMString") {
+ ok(res !== item &&
+ t.baseVal.getItem(6) !== item,
+ "The method " + t.list_type + ".appendItem() should clone the object " +
+ "that is passed in, if that object is already the last item in " +
+ "that list.");
+ // [SVGWG issue] not what the spec currently says
+
+ eventChecker.expect("");
+ threw = false;
+ try {
+ t.baseVal.appendItem({});
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.list_type + ".appendItem() should throw if passed " +
+ "an object of the wrong type.");
+ eventChecker.ignoreEvents();
+ }
+
+ // Test removal and addition events
+
+ eventChecker.expect("remove add");
+ t.element.removeAttribute(t.attr_name);
+ t.element.removeAttributeNS(null, t.attr_name);
+ res = t.baseVal.appendItem(item);
+ eventChecker.finish();
+ }
+}
+
+
+/**
+ * This function tests the SVGXxxList API for the anim val list (see also the
+ * comment for test_baseVal_API).
+ */
+function run_animVal_API_tests() {
+ var threw, item;
+
+ for (var t of tests) {
+ if (!t.animVal)
+ continue; // SVGStringList isn't animatable
+
+ item = t.item_constructor();
+
+ t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+ is(t.animVal.numberOfItems, 4,
+ "The " + t.list_type + " object should contain four list items.");
+
+ // Test .clear():
+
+ threw = false;
+ try {
+ t.animVal.clear();
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.list_type + ".clear() should throw when called on an " +
+ "anim val list, since anim val lists should be readonly.");
+
+ // Test .getItem():
+
+ item = t.animVal.getItem(2);
+ ok(item != null && item === t.animVal.getItem(2),
+ "The method " + t.list_type + ".getItem() should work when called on an " +
+ "anim val list, and always return the exact same object.");
+
+ // .initialize()
+
+ threw = false;
+ try {
+ t.animVal.initialize(item);
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.list_type + ".initialize() should throw when called on " +
+ "an anim val list, since anim val lists should be readonly.");
+
+ // Test .insertItemBefore():
+
+ threw = false;
+ try {
+ t.animVal.insertItemBefore(item, 2);
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.list_type + ".insertItemBefore() should throw when " +
+ "called on an anim val list, since anim val lists should be readonly.");
+
+ // Test .replaceItem():
+
+ threw = false;
+ try {
+ t.animVal.replaceItem(item, 2);
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.list_type + ".replaceItem() should throw when called " +
+ "on an anim val list, since anim val lists should be readonly.");
+
+ // Test .removeItem():
+
+ threw = false;
+ try {
+ t.animVal.removeItem(2);
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.list_type + ".removeItem() should throw when called " +
+ "on an anim val list, since anim val lists should be readonly.");
+
+ // Test .appendItem():
+
+ threw = false;
+ try {
+ t.animVal.appendItem(item);
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.list_type + ".appendItem() should throw when called " +
+ "on an anim val list, since anim val lists should be readonly.");
+ }
+}
+
+
+/**
+ * This function runs some basic tests to check the effect of setAttribute()
+ * calls on object identity, without taking SMIL animation into consideration.
+ */
+function run_basic_setAttribute_tests() {
+ for (var t of tests) {
+ // Since the t.prop, t.baseVal and t.animVal objects should never ever
+ // change, we leave testing of them to our caller so that it can check
+ // them after all the other mutations such as SMIL changes.
+
+ t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+ ok(t.baseVal.numberOfItems == 4 && t.baseVal.getItem(3) != null,
+ "The length of the " + t.list_type + " object for " + t.bv_path + " should " +
+ "have been set to 4 by the setAttribute() call.");
+
+ if (t.animVal) {
+ ok(t.baseVal.numberOfItems == t.animVal.numberOfItems,
+ "When no animations are active, the " + t.list_type + " objects for " +
+ t.bv_path + " and " + t.av_path + " should be the same length (4).");
+
+ ok(t.baseVal !== t.animVal,
+ "The " + t.list_type + " objects for " + t.bv_path + " and " + t.av_path +
+ " should be different objects.");
+
+ ok(t.baseVal.getItem(0) !== t.animVal.getItem(0),
+ "The " + t.item_type + " list items in the " + t.list_type + " objects for " +
+ t.bv_path + " and " + t.av_path + " should be different objects.");
+ }
+
+ // eslint-disable-next-line no-self-compare
+ ok(t.baseVal.getItem(0) === t.baseVal.getItem(0),
+ "The exact same " + t.item_type + " DOM object should be returned each " +
+ "time the item at a given index in the " + t.list_type + " for " +
+ t.bv_path + " is accessed, given that the index was not made invalid " +
+ "by a change in list length between the successive accesses.");
+
+ if (t.animVal) {
+ // eslint-disable-next-line no-self-compare
+ ok(t.animVal.getItem(0) === t.animVal.getItem(0),
+ "The exact same " + t.item_type + " DOM object should be returned each " +
+ "time the item at a given index in the " + t.list_type + " for " +
+ t.av_path + " is accessed, given that the index was not made invalid " +
+ "by a change in list length between the successive accesses.");
+ }
+
+ // Test the effect of setting the attribute to new values:
+
+ t.old_baseVal_items = get_array_of_list_items(t.baseVal);
+ if (t.animVal) {
+ t.old_animVal_items = get_array_of_list_items(t.animVal);
+ }
+
+ t.element.setAttribute(t.attr_name, t.attr_val_3a);
+ t.element.setAttribute(t.attr_name, t.attr_val_5a);
+
+ ok(t.baseVal.numberOfItems == 5 && t.baseVal.getItem(4) != null,
+ "The length of the " + t.list_type + " object for " + t.bv_path + " should " +
+ "have been set to 5 by the setAttribute() call.");
+
+ if (t.animVal) {
+ ok(t.baseVal.numberOfItems == t.animVal.numberOfItems,
+ "Since no animations are active, the length of the " + t.list_type + " " +
+ "objects for " + t.bv_path + " and " + t.av_path + " should be the same " +
+ "(5).");
+ }
+
+ if (t.item_type != "DOMString") {
+ ok(t.baseVal.getItem(2) === t.old_baseVal_items[2],
+ "After its attribute changes, list items in the " + t.list_type + " for " +
+ t.bv_path + " that are at indexes that existed prior to the attribute " +
+ "change should be the exact same objects as the objects that were " +
+ "at those indexes prior to the attribute change.");
+
+ ok(t.baseVal.getItem(3) !== t.old_baseVal_items[3],
+ "After its attribute changes, list items in the " + t.list_type + " for " +
+ t.bv_path + " that are at indexes that did not exist prior to the " +
+ "attribute change should not be the same objects as any objects that " +
+ "were at those indexes at some earlier time.");
+ }
+
+ if (t.animVal) {
+ ok(t.animVal.getItem(2) === t.old_animVal_items[2],
+ "After its attribute changes, list items in the " + t.list_type + " for " +
+ t.av_path + " that are at indexes that existed prior to the attribute " +
+ "change should be the exact same objects as the objects that were " +
+ "at those indexes prior to the attribute change.");
+
+ ok(t.animVal.getItem(3) !== t.old_animVal_items[3],
+ "After its attribute changes, list items in the " + t.list_type + " for " +
+ t.av_path + " that are at indexes that did not exist prior to the " +
+ "attribute change should not be the same objects as any objects " +
+ "that were at those indexes at some earlier time.");
+ }
+ }
+}
+
+/**
+ * This function verifies that a list's animVal is kept in sync with its
+ * baseVal, when we add & remove items from the baseVal.
+ */
+function run_list_mutation_tests() {
+ for (var t of tests) {
+ if (t.animVal) {
+ // Test removeItem()
+ // =================
+ // Save second item in baseVal list; then make it the first item, and
+ // check that animVal is updated accordingly.
+ t.element.setAttribute(t.attr_name, t.attr_val_4);
+
+ var secondVal = t.baseVal.getItem(1);
+ var removedFirstVal = t.baseVal.removeItem(0);
+ t.item_is(t.animVal.getItem(0), secondVal,
+ "animVal for " + t.attr_name + " needs update after first item " +
+ "removed");
+
+ // Repeat with last item
+ var secondToLastVal = t.baseVal.getItem(1);
+ var removedLastVal = t.baseVal.removeItem(2);
+
+ var threw = false;
+ try {
+ t.animVal.getItem(2);
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.attr_name + ".animVal.getItem() for previously-final " +
+ "index should throw after final item is removed from baseVal.");
+
+ t.item_is(t.animVal.getItem(1), secondToLastVal,
+ "animVal for " + t.attr_name + " needs update after last item " +
+ "removed");
+
+ // Test insertItemBefore()
+ // =======================
+ // Reset base value, insert value @ start, check that animVal is updated.
+ t.element.setAttribute(t.attr_name, t.attr_val_3a);
+ t.baseVal.insertItemBefore(removedLastVal, 0);
+ t.item_is(t.animVal.getItem(0), removedLastVal,
+ "animVal for " + t.attr_name + " needs update after insert at " +
+ "beginning");
+
+ // Repeat with insert at end
+ t.element.setAttribute(t.attr_name, t.attr_val_3a);
+ t.baseVal.insertItemBefore(removedFirstVal, t.baseVal.numberOfItems);
+ t.item_is(t.animVal.getItem(t.baseVal.numberOfItems - 1),
+ removedFirstVal,
+ "animVal for " + t.attr_name + " needs update after insert at end");
+
+ // Test appendItem()
+ // =================
+ var dummy = t.item_constructor();
+ t.baseVal.appendItem(dummy);
+ t.item_is(t.animVal.getItem(t.baseVal.numberOfItems - 1), dummy,
+ "animVal for " + t.attr_name + " needs update after appendItem");
+
+ // Test clear()
+ // ============
+ t.baseVal.clear();
+ threw = false;
+ try {
+ t.animVal.getItem(0);
+ } catch (e) {
+ threw = true;
+ }
+ ok(threw,
+ "The method " + t.attr_name + ".animVal.getItem() should throw after " +
+ "we've cleared baseVal.");
+
+ is(t.animVal.numberOfItems, 0,
+ "animVal for " + t.attr_name + " should be empty after baseVal cleared");
+
+ // Test initialize()
+ // =================
+ t.element.setAttribute(t.attr_name, t.attr_val_3a);
+ t.baseVal.initialize(dummy);
+
+ is(t.animVal.numberOfItems, 1,
+ "animVal for " + t.attr_name + " should have length 1 after initialize");
+ t.item_is(t.animVal.getItem(0), dummy,
+ "animVal for " + t.attr_name + " needs update after initialize");
+ }
+ }
+}
+
+/**
+ * In this function we run a series of tests at various points along the SMIL
+ * animation timeline, using SVGSVGElement.setCurrentTime() to move forward
+ * along the timeline.
+ *
+ * See the comment for create_animate_elements() for details of the animations
+ * and their timings.
+ */
+function run_animation_timeline_tests() {
+ var svg = document.getElementById("svg");
+
+ for (var t of tests) {
+ // Skip if there is no animVal for this test or if it is a transform list
+ // since these are handled specially
+ if (!t.animVal || is_transform_attr(t.attr_name))
+ continue;
+
+ svg.setCurrentTime(0); // reset timeline
+
+ // Reset attributes before moving along the timeline and triggering SMIL:
+ t.element.setAttribute(t.attr_name, t.attr_val_4);
+ t.old_baseVal_items = get_array_of_list_items(t.baseVal);
+ t.old_animVal_items = get_array_of_list_items(t.animVal);
+
+
+ /** ****************** t = 1s ********************/
+
+ svg.setCurrentTime(1); // begin first animation
+
+ ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+ t.baseVal.getItem(3) === t.old_baseVal_items[3],
+ "The start of an animation should never affect the " + t.list_type +
+ " for " + t.bv_path + ", or its list items.");
+
+ ok(t.animVal.numberOfItems == 5 && t.animVal.getItem(4) != null,
+ "The start of the animation should have changed the number of items " +
+ "in the " + t.list_type + " for " + t.bv_path + " to 5.");
+
+ // TODO
+ ok(t.animVal.getItem(3) === t.old_animVal_items[3],
+ "When affected by SMIL animation, list items in the " + t.list_type +
+ " for " + t.bv_path + " that are at indexes that existed prior to the " +
+ "start of the animation should be the exact same objects as the " +
+ "objects that were at those indexes prior to the start of the " +
+ "animation.");
+
+ t.old_animVal_items = get_array_of_list_items(t.animVal);
+
+ t.element.setAttribute(t.attr_name, t.attr_val_3a);
+
+ ok(t.baseVal.numberOfItems == 3 &&
+ t.baseVal.getItem(2) === t.old_baseVal_items[2],
+ "Setting the underlying attribute should change the items in the " +
+ t.list_type + " for " + t.bv_path + ", including when an animation is " +
+ "in progress.");
+
+ ok(t.animVal.numberOfItems == 5 &&
+ t.animVal.getItem(4) === t.old_animVal_items[4],
+ "Setting the underlying attribute should not change the " + t.list_type +
+ " for " + t.bv_path + " when an animation that does not depend on the " +
+ "base val is in progress.");
+
+ t.element.setAttribute(t.attr_name, t.attr_val_4); // reset
+
+ t.old_baseVal_items = get_array_of_list_items(t.baseVal);
+ t.old_animVal_items = get_array_of_list_items(t.animVal);
+
+
+ /** ****************** t = 2s ********************/
+
+ svg.setCurrentTime(2); // begin override animation
+
+ ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+ t.baseVal.getItem(3) === t.old_baseVal_items[3],
+ "The start of an override animation should never affect the " +
+ t.list_type + " for " + t.bv_path + ", or its list items.");
+
+ is(t.animVal.numberOfItems, 3,
+ "The start of the override animation should have changed the number " +
+ "of items in the " + t.list_type + " for " + t.bv_path + " to 3.");
+
+ ok(t.animVal.getItem(2) === t.old_animVal_items[2],
+ "When affected by an override SMIL animation, list items in the " +
+ t.list_type + " for " + t.bv_path + " that are at indexes that existed " +
+ "prior to the start of the animation should be the exact same " +
+ "objects as the objects that were at those indexes prior to the " +
+ "start of that animation.");
+
+ t.old_animVal_items = get_array_of_list_items(t.animVal);
+
+
+ /** ****************** t = 3s ********************/
+
+ svg.setCurrentTime(3); // end of override animation
+
+ ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+ t.baseVal.getItem(3) === t.old_baseVal_items[3],
+ "The end of an override animation should never affect the " +
+ t.list_type + " for " + t.bv_path + ", or its list items.");
+
+ is(t.animVal.numberOfItems, 5,
+ "At the end of the override animation, the number of items in the " +
+ t.list_type + " for " + t.bv_path + " should have reverted to 5.");
+
+ ok(t.animVal.getItem(2) === t.old_animVal_items[2],
+ "At the end of the override animation, list items in the " +
+ t.list_type + " for " + t.bv_path + " that are at indexes that existed " +
+ "prior to the end of the animation should be the exact same " +
+ "objects as the objects that were at those indexes prior to the " +
+ "end of that animation.");
+
+ t.old_animVal_items = get_array_of_list_items(t.animVal);
+
+
+ /** ****************** t = 5s ********************/
+
+ svg.setCurrentTime(5); // animation repeat point
+
+ ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+ t.baseVal.getItem(3) === t.old_baseVal_items[3],
+ "When a SMIL animation repeats, it should never affect the " +
+ t.list_type + " for " + t.bv_path + ", or its list items.");
+
+ ok(t.animVal.numberOfItems == t.old_animVal_items.length &&
+ t.animVal.getItem(4) === t.old_animVal_items[4],
+ "When an animation repeats, the list items that are at a given " +
+ "index in the " + t.list_type + " for " + t.av_path + " should be the exact " +
+ "same objects as were at that index before the repeat occurred.");
+
+
+ /** ****************** t = 6s ********************/
+
+ svg.setCurrentTime(6); // inside animation repeat
+
+ ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+ t.baseVal.getItem(3) === t.old_baseVal_items[3],
+ "When a SMIL animation repeats, it should never affect the " +
+ t.list_type + " for " + t.bv_path + ", or its list items.");
+
+ ok(t.animVal.numberOfItems == t.old_animVal_items.length &&
+ t.animVal.getItem(4) === t.old_animVal_items[4],
+ "When an animation repeats, the list items that are at a given " +
+ "index in the " + t.list_type + " for " + t.av_path + " should be the exact " +
+ "same objects as were at that index before the repeat occurred.");
+
+
+ /** ****************** t = 7s ********************/
+
+ svg.setCurrentTime(7); // start of additive="sum" animation
+
+ ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+ t.baseVal.getItem(3) === t.old_baseVal_items[3],
+ "When a new SMIL animation starts and should blend with an " +
+ "underlying animation, it should never affect the " +
+ t.list_type + " for " + t.bv_path + ", or its list items.");
+
+ if (t.list_type == "SVGLengthList") {
+ // Length lists are a special case where it makes sense to allow shorter
+ // lists to be composed on top of longer lists (but not necessarily vice
+ // versa - see comment below).
+
+ ok(t.animVal.numberOfItems == t.old_animVal_items.length &&
+ t.animVal.getItem(3) === t.old_animVal_items[3],
+ 'When an animation with additive="sum" is added on top of an ' +
+ "existing animation that has more list items, the length of the " +
+ t.list_type + " for " + t.av_path + " should not change.");
+ } else {
+
+/* TODO
+ ok(false,
+ 'Decide what to do here - see ' +
+ 'https://bugzilla.mozilla.org/show_bug.cgi?id=573716 - we ' +
+ 'probably should be discarding any animation sandwich layers from ' +
+ 'a layer that fails to add, on up.');
+*/
+
+ // In other words, we wouldn't need the if-else check here.
+
+ }
+
+ // XXX what if the higher priority sandwich layer has *more* list items
+ // than the underlying animation? In that case we would need to
+ // distinguish between different SVGLengthList attributes, since although
+ // all SVGLengthList attributes allow a short list to be added to longer
+ // list, they do not all allow a longer list to be added to shorter list.
+ // Specifically that would not be good for 'x' and 'y' on <text> since
+ // lengths there are not naturally zero. See the comment in
+ // SVGLengthListSMILAttr::Add().
+
+
+ /** ****************** t = 13s ********************/
+
+ svg.setCurrentTime(13); // all animations have finished, but one is frozen
+
+ ok(t.baseVal.numberOfItems == t.old_baseVal_items.length &&
+ t.baseVal.getItem(3) === t.old_baseVal_items[3],
+ "When a SMIL animation ends, it should never affect the " +
+ t.list_type + " for " + t.bv_path + ", or its list items.");
+
+ is(t.animVal.numberOfItems, 5,
+ "Even though all SMIL animation have finished, the number " +
+ "of items in the " + t.list_type + " for " + t.av_path +
+ " should still be more than the same as the number of items in " +
+ t.bv_path + " since one of the animations is still frozen.");
+
+ var expected = t.attr_val_5b_firstItem_x3_constructor(t.item_constructor);
+ t.item_is(t.animVal.getItem(0), expected,
+ 'animation with accumulate="sum" and repeatCount="3" for attribute "' +
+ t.attr_name + '" should end up at 3x the "to" value.');
+
+ // Unfreeze frozen animation (removing its effects)
+ var frozen_animate_element =
+ t.element.querySelector('animate[fill][attributeName="' + t.attr_name + '"]');
+ frozen_animate_element.removeAttribute("fill");
+
+ ok(t.animVal.numberOfItems == t.baseVal.numberOfItems,
+ "Once all SMIL animation have finished and been un-frozen, the number " +
+ "of items in the " + t.list_type + " for " + t.av_path +
+ " should be the same as the number of items in " + t.bv_path + ".");
+
+ ok(t.animVal.getItem(2) === t.old_animVal_items[2],
+ "Even after an animation finishes and is un-frozen, the list items " +
+ "that are at a given index in the " + t.list_type + " for " + t.av_path +
+ " should be the exact same objects as were at that index before the " +
+ "end and unfreezing of the animation occurred.");
+ }
+}
+
+
+function run_tests() {
+ // Initialize each test object with some useful properties, and create their
+ // 'animate' elements. Note that 'prop' and 'animVal' may be null.
+ for (let t of tests) {
+ t.element = document.getElementById(t.target_element_id);
+ t.prop = t.prop_name ? t.element[t.prop_name] : null;
+ t.baseVal = ( t.prop || t.element )[t.bv_name];
+ t.animVal = t.av_name ? ( t.prop || t.element )[t.av_name] : null;
+ t.bv_path = t.el_type + "." +
+ (t.prop ? t.prop_name + "." : "") +
+ t.bv_name; // e.g. 'SVGTextElement.x.baseVal'
+ if (t.animVal) {
+ t.av_path = t.el_type + "." +
+ (t.prop ? t.prop_name + "." : "") +
+ t.av_name;
+ }
+ t.prop_type = t.prop_type || null;
+
+ // use fallback 'is' function, if none was provided.
+ if (!t.item_is) {
+ t.item_is = function(itemA, itemB, message) {
+ ok(typeof(itemA.value) != "undefined" &&
+ typeof(itemB.value) != "undefined",
+ "expecting value property");
+ is(itemA.value, itemB.value, message);
+ };
+ }
+
+ if (t.animVal) {
+ t.element.appendChild(create_animate_elements(t));
+ }
+ }
+
+ // Run the major test groups:
+
+ run_baseVal_API_tests();
+ run_animVal_API_tests();
+ run_basic_setAttribute_tests();
+ run_list_mutation_tests();
+ run_animation_timeline_tests();
+
+ // After all the other test manipulations, we check that the following
+ // objects have still not changed, since they never should:
+
+ for (let t of tests) {
+ if (t.prop) {
+ ok(t.prop === t.element[t.prop_name],
+ "The same " + t.prop_type + " object should ALWAYS be returned for " +
+ t.el_type + "." + t.prop_name + " each time it is accessed.");
+ }
+
+ ok(t.baseVal === ( t.prop || t.element )[t.bv_name],
+ "The same " + t.list_type + " object should ALWAYS be returned for " +
+ t.el_type + "." + t.prop_name + "." + t.bv_name + " each time it is accessed.");
+
+ if (t.animVal) {
+ ok(t.animVal === ( t.prop || t.element )[t.av_name],
+ "The same " + t.list_type + " object should ALWAYS be returned for " +
+ t.el_type + "." + t.prop_name + "." + t.av_name + " each time it is accessed.");
+ }
+ }
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run_tests);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_SVGxxxListIndexing.xhtml b/dom/svg/test/test_SVGxxxListIndexing.xhtml
new file mode 100644
index 0000000000..b1dec4df31
--- /dev/null
+++ b/dom/svg/test/test_SVGxxxListIndexing.xhtml
@@ -0,0 +1,93 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=631437
+-->
+<head>
+ <title>Tests the array indexing and .length on SVGXXXList objects</title>
+ <script 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=631437">Mozilla Bug 631437</a>
+<svg xmlns="http://www.w3.org/2000/svg" id="svg">
+ <text id="text" x="10 20 30" rotate="40 50 60">abcde</text>
+ <path id="path" d="M0,0 L100,100"/>
+ <polygon id="poly" points="50,50 70,70 90,50"/>
+ <g id="g" transform="translate(20 30) rotate(50 60 70) scale(2)"
+ requiredExtensions="foo bar baz"/>
+</svg>
+<script type="text/javascript"><![CDATA[
+
+var text = document.getElementById("text"),
+ path = document.getElementById("path"),
+ poly = document.getElementById("poly"),
+ g = document.getElementById("g");
+
+function CheckList(aListObject, aExpectedListLength, aListDescription) {
+ is(aListObject.numberOfItems, aExpectedListLength, aListDescription + ".numberOfItems");
+ is(aListObject.length, aExpectedListLength, aListDescription + ".length");
+ for (let i = 0; i < aListObject.length; i++) {
+ let item = aListObject.getItem(i);
+ ok(aListObject[i] === item, aListDescription + "[" + i + "]");
+ }
+ is(typeof(aListObject[aListObject.length]), "undefined", aListDescription + "[outOfBounds]");
+}
+
+var tests = [
+ { element: text,
+ attribute: "x",
+ listProperty: "x.baseVal",
+ type: "SVGLengthList",
+ subtests: [ { values: null, length: 3 },
+ { values: "40", length: 1 },
+ { values: "1em 2em 3em 4em 5em", length: 5 } ] },
+ { element: text,
+ attribute: "rotate",
+ listProperty: "rotate.baseVal",
+ type: "SVGNumberList",
+ subtests: [ { values: null, length: 3 },
+ { values: "10", length: 1 },
+ { values: "1 2 3 4 5", length: 5 } ] },
+ { element: poly,
+ attribute: "points",
+ listProperty: "animatedPoints",
+ type: "SVGPointList",
+ subtests: [ { values: null, length: 3 },
+ { values: "100,100", length: 1 },
+ { values: "0,0 10,10 20,0 30,10 40,0", length: 5 } ] },
+ { element: g,
+ attribute: "transform",
+ listProperty: "transform.baseVal",
+ type: "SVGTransformList",
+ subtests: [ { values: null, length: 3 },
+ { values: "skewX(45)", length: 1 },
+ { values: "translate(1 2) rotate(3) scale(4) skewY(5) skewX(6)",
+ length: 5 } ] },
+ { element: g,
+ attribute: "requiredExtensions",
+ listProperty: "requiredExtensions",
+ type: "SVGStringList",
+ subtests: [ { values: null, length: 3 },
+ { values: "foo", length: 1 },
+ { values: "foo bar baz qux", length: 4 } ] },
+];
+
+for (let test of tests) {
+ let list = test.element;
+ for (let property of test.listProperty.split(".")) {
+ list = list[property];
+ }
+
+ for (let subtest of test.subtests) {
+ if (subtest.values) {
+ test.element.setAttribute(test.attribute, subtest.values);
+ }
+
+ CheckList(list, subtest.length,
+ test.type + ": " + test.element.localName + "." +
+ test.listProperty);
+ }
+}
+]]></script>
+</body>
+</html>
diff --git a/dom/svg/test/test_a_href_01.xhtml b/dom/svg/test/test_a_href_01.xhtml
new file mode 100644
index 0000000000..a1ff8d7227
--- /dev/null
+++ b/dom/svg/test/test_a_href_01.xhtml
@@ -0,0 +1,96 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=620295
+https://bugzilla.mozilla.org/show_bug.cgi?id=1245751
+-->
+<head>
+ <title>Test that activating SVG 'a' elements navigate to their xlink:href or href</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.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=620295">Mozilla Bug 620295</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1245751">Mozilla Bug 1245751</a>
+<p id="display"></p>
+<pre id="test">
+<script class="testbody" type="text/javascript"><![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+var testCount = 7;
+var didWindowLoad = false;
+var frameLoadCount = 0;
+var navigationCount = 0;
+
+function endsWith(s1, s2) {
+ s1 = String(s1);
+ return s1.length >= s2.length && s1.substring(s1.length - s2.length) == s2;
+}
+
+function windowLoaded() {
+ didWindowLoad = true;
+ doNavigationIfReady();
+}
+
+function frameLoaded() {
+ frameLoadCount++;
+ doNavigationIfReady();
+}
+
+function doNavigationIfReady() {
+ if (didWindowLoad && frameLoadCount == testCount) {
+ doNavigation();
+ }
+}
+
+function doNavigation() {
+ // Test clicking on an unmodified <a>.
+ doNavigationTest(1, "a_href_helper_01.svg");
+ // Test clicking on an <a> whose xlink:href is modified by assigning to href.baseVal.
+ doNavigationTest(2, "a_href_helper_02_03.svg", function(a) { a.href.baseVal = "a_href_destination.svg"; });
+ // Test clicking on an <a> whose xlink:href is modified by a setAttributeNS call.
+ doNavigationTest(3, "a_href_helper_02_03.svg", function(a) { a.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "a_href_destination.svg"); });
+ // Test clicking on an <a> whose xlink:href is modified by animation.
+ doNavigationTest(4, "a_href_helper_04.svg");
+ // Test clicking on an unmodified <a> with both href and xlink:href.
+ doNavigationTest(5, "a_href_helper_05.svg");
+ // Test clicking on an <a> whose href is modified by a setAttribute call.
+ doNavigationTest(6, "a_href_helper_06.svg", function(a) { a.setAttribute("href", "a_href_destination.svg"); });
+ // Test clicking on an <a> whose href is modified by animation.
+ doNavigationTest(7, "a_href_helper_07.svg");
+}
+
+function doNavigationTest(testNumber, initialHref, f) {
+ var iframe = document.getElementById("iframe" + testNumber);
+ var a = iframe.contentDocument.getElementById("a");
+ ok(endsWith(iframe.contentWindow.location, initialHref), "Initial href of test " + testNumber);
+ is("pointer", window.getComputedStyle(a).getPropertyValue("cursor"), "expected pointer cursor");
+ iframe.onload = function() {
+ ok(endsWith(iframe.contentWindow.location, "a_href_destination.svg"), "Final href of test " + testNumber);
+ if (++navigationCount == testCount) {
+ SimpleTest.finish();
+ }
+ };
+ if (f) {
+ f(a);
+ }
+ sendMouseEvent({type: "click"}, a);
+}
+
+window.onload = windowLoaded;
+
+]]></script>
+</pre>
+<div id="content" style="visibility: hidden">
+<!-- These must come after frameLoaded is defined -->
+<iframe id="iframe1" src="a_href_helper_01.svg" onload="frameLoaded()"></iframe>
+<iframe id="iframe2" src="a_href_helper_02_03.svg" onload="frameLoaded()"></iframe>
+<iframe id="iframe3" src="a_href_helper_02_03.svg" onload="frameLoaded()"></iframe>
+<iframe id="iframe4" src="a_href_helper_04.svg" onload="frameLoaded()"></iframe>
+<iframe id="iframe5" src="a_href_helper_05.svg" onload="frameLoaded()"></iframe>
+<iframe id="iframe6" src="a_href_helper_06.svg" onload="frameLoaded()"></iframe>
+<iframe id="iframe7" src="a_href_helper_07.svg" onload="frameLoaded()"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/svg/test/test_a_href_02.xhtml b/dom/svg/test/test_a_href_02.xhtml
new file mode 100644
index 0000000000..0d2277674e
--- /dev/null
+++ b/dom/svg/test/test_a_href_02.xhtml
@@ -0,0 +1,37 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=620295
+-->
+<head>
+ <title>Test that the href property reflects xlink:href="" on 'a' elements</title>
+ <script 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=620295">Mozilla Bug 620295</a>
+<p id="display"></p>
+<div id="content">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <a id="a" xlink:href="a"/>
+</svg>
+</div>
+<pre id="test">
+<script><![CDATA[
+
+var a = document.getElementById("a");
+
+// Initial attribute value should be reflected in the href property
+is(a.href.baseVal, "a", "Initial property value");
+
+// Updated attribute value should be reflected in the href property
+a.setAttributeNS("http://www.w3.org/1999/xlink", "xlink:href", "b");
+is(a.href.baseVal, "b", "Updated property value");
+
+// Modifying the href property should cause the attribute to be updated
+a.href.baseVal = "c";
+is(a.getAttributeNS("http://www.w3.org/1999/xlink", "href"), "c", "Updated attribute value");
+
+]]></script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_animLengthObjectIdentity.xhtml b/dom/svg/test/test_animLengthObjectIdentity.xhtml
new file mode 100644
index 0000000000..b4bce755b9
--- /dev/null
+++ b/dom/svg/test/test_animLengthObjectIdentity.xhtml
@@ -0,0 +1,86 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=508496
+-->
+<head>
+ <title>Test for object identity of SVG animated lengths</title>
+ <script 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=506856">Mozilla Bug 508496</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="120px" height="120px"
+ onload="this.pauseAnimations()">
+ <circle cx="-100" cy="-100" r="15" fill="blue" id="circle">
+ <animate attributeName="cx" from="0" to="100" dur="4s" begin="1s; 10s"
+ fill="freeze" id="animate"/>
+ </circle>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+/** Test object identity of animated lengths **/
+
+/* Global Variables */
+const svgns = "http://www.w3.org/2000/svg";
+var svg = document.getElementById("svg");
+var circle = document.getElementById("circle");
+
+SimpleTest.waitForExplicitFinish();
+
+function main() {
+ ok(svg.animationsPaused(), "should be paused by <svg> load handler");
+ is(svg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler");
+
+ var animLength = circle.cx;
+ ok(animLength === circle.cx,
+ "Got different SVGAnimatedLength objects at startup");
+
+ var baseVal = circle.cx.baseVal;
+ ok(baseVal === circle.cx.baseVal,
+ "Got different baseVal SVGLength objects at startup");
+
+ var animVal = circle.cx.animVal;
+ ok(animVal === circle.cx.animVal,
+ "Got different animVal SVGLength objects at startup");
+
+ var animate = document.getElementById("animate");
+ if (animate && animate.targetElement) {
+ // Sample mid-way through the animation
+ svg.setCurrentTime(5);
+
+ ok(animLength === circle.cx,
+ "Got different SVGAnimatedLength objects during animation");
+ ok(baseVal === circle.cx.baseVal,
+ "Got different baseVal SVGLength objects during animation");
+ ok(animVal === circle.cx.animVal,
+ "Got different animVal SVGLength objects during animation");
+ }
+
+ // Drop all references to the tear off objects
+ var oldValue = circle.cx.animVal.value; // Just a float, not an object ref
+ animLength = null;
+ baseVal = null;
+ animVal = null;
+ SpecialPowers.gc();
+
+ // The tearoff objects should no longer exist and we should create new ones.
+ // If somehow, the tearoff objects have died and yet not been removed from the
+ // hashmap we'll end up in all sorts of trouble when we try to access them.
+ // So in the following, we're not really interested in the value, just that we
+ // don't crash.
+ is(circle.cx.animVal.value, oldValue,
+ "Unexpected result accessing new(?) length object.");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", main);
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_animLengthReadonly.xhtml b/dom/svg/test/test_animLengthReadonly.xhtml
new file mode 100644
index 0000000000..f9c16e7752
--- /dev/null
+++ b/dom/svg/test/test_animLengthReadonly.xhtml
@@ -0,0 +1,219 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=506856
+-->
+<head>
+ <title>Test for read-only times of SVG animated lengths</title>
+ <script 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=506856">Mozilla Bug 506856</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg"
+ width="100px" height="100px" viewBox="0 0 100 100"
+ onload="this.pauseAnimations()">
+ <!-- Note: Need a viewBox on the SVG element, or else our percent-length
+ basis will be '0' (based off of the viewport width, which is 0 in this
+ case since we're in a display:none iframe. We use viewport width because
+ the lack of a viewbox forces us into the not-mViewBox::IsValid() case of
+ SVGSVGElement::GetLength).
+
+ And we don't want a percent-length basis of 0, because then when we call
+ convertToSpecifiedUnits to convert out of percent units, we divide by 0
+ and get unexpected results.
+ -->
+ <circle cx="-100" cy="-100" r="15" fill="blue" id="circle">
+ <animate attributeName="cx" from="0" to="100" dur="4s" begin="1s; 10s"
+ fill="freeze" id="animate"/>
+ <animate attributeName="cy" from="-100" to="-100" dur="4s" begin="1s; 10s"
+ fill="remove"/>
+ </circle>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+/** Test read-only times of animated lengths **/
+
+/* Global Variables */
+const svgns = "http://www.w3.org/2000/svg";
+var svg = document.getElementById("svg");
+var circle = document.getElementById("circle");
+
+SimpleTest.waitForExplicitFinish();
+
+function main() {
+ ok(svg.animationsPaused(), "should be paused by <svg> load handler");
+ is(svg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler");
+
+ // Sanity check: check initial values
+ is(circle.cx.baseVal.value, -100);
+ is(circle.cx.animVal.value, -100);
+ is(circle.cy.baseVal.value, -100);
+ is(circle.cy.animVal.value, -100);
+
+ // (1): Check before animation that animVal's are readonly
+ ok(checkReadOnly(circle.cx),
+ "(1) animVal cx is editable when not animated");
+ ok(checkReadOnly(circle.cy),
+ "(1) animVal cy is editable when not animated");
+
+ // Skip to mid-way through the animation
+ svg.setCurrentTime(4);
+
+ // (2): Check that whilst animations are active the animVal's are readonly
+ ok(checkReadOnly(circle.cx),
+ "(2) animVal cx is editable when animated");
+ ok(checkReadOnly(circle.cy),
+ "(2) animVal cy is editable when animated");
+
+ // Skip to past end
+ svg.setCurrentTime(6);
+
+ // (3): Check that frozen animations are readonly and have different values
+ ok(checkReadOnly(circle.cx),
+ "(3) animVal cx is editable when frozen");
+ checkDiffValue(circle.cx);
+
+ // (4): Check that finished animations are readonly and have same values
+ ok(checkReadOnly(circle.cy),
+ "(4) animVal cy is editable when inactive");
+ checkSameValue(circle.cy);
+
+ SimpleTest.finish();
+}
+
+function checkReadOnly(animLength) {
+ var exceptionCaught = false;
+ var oldAnimValue = animLength.animVal.value;
+
+ // Try changing value
+ try {
+ animLength.animVal.value = (animLength.animVal.value == 77) ? 88 : 77;
+ } catch (e) {
+ if (e.name == "NoModificationAllowedError" &&
+ e.code == DOMException.NO_MODIFICATION_ALLOWED_ERR) {
+ exceptionCaught = true;
+ } else {
+ ok(false, "Got unexpected exception " + e);
+ return false;
+ }
+ }
+ is(animLength.animVal.value, oldAnimValue,
+ "No exception thrown, but animVal was changed.");
+ if (animLength.animVal.value != oldAnimValue) return false;
+
+ // valueInSpecifiedUnits
+ try {
+ exceptionCaught = false;
+ animLength.animVal.valueInSpecifiedUnits = -100;
+ } catch (e) { exceptionCaught = true; }
+ ok(exceptionCaught, "animVal.valueInSpecifiedUnits failed to throw.");
+ if (!exceptionCaught) return false;
+
+ // valueAsString
+ try {
+ exceptionCaught = false;
+ animLength.animVal.valueAsString = "-100px";
+ } catch (e) { exceptionCaught = true; }
+ ok(exceptionCaught, "animVal.valueAsString failed to throw.");
+ if (!exceptionCaught) return false;
+
+ // newValueSpecifiedUnits
+ try {
+ exceptionCaught = false;
+ animLength.animVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX, -100);
+ } catch (e) { exceptionCaught = true; }
+ ok(exceptionCaught, "animVal.newValueSpecifiedUnits failed to throw.");
+ if (!exceptionCaught) return false;
+
+ // convertToSpecifiedUnits
+ try {
+ exceptionCaught = false;
+ animLength.animVal.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_NUMBER);
+ } catch (e) { exceptionCaught = true; }
+ ok(exceptionCaught, "animVal.convertToSpecifiedUnits failed to throw.");
+
+ return exceptionCaught;
+}
+
+function checkSameValue(animLength) {
+ // value
+ animLength.baseVal.value = 1;
+ is(animLength.animVal.value, 1,
+ "un-animated animVal.value not changed after setting baseValue.value");
+
+ // valueInSpecifiedUnits
+ animLength.baseVal.valueInSpecifiedUnits = 2;
+ is(animLength.animVal.value, 2,
+ "un-animated animVal.value not changed after setting "
+ + "baseValue.valueInSpecifiedUnits");
+
+ // valueAsString
+ animLength.baseVal.valueAsString = "3";
+ is(animLength.animVal.value, 3,
+ "un-animated animVal.value not changed after setting "
+ + "baseValue.valueAsString");
+
+ // newValueSpecifiedUnits
+ animLength.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_CM, 4);
+ is(animLength.animVal.valueInSpecifiedUnits, 4,
+ "un-animated animVal.value not changed after setting "
+ + "baseValue.newValueSpecifiedUnits");
+
+ // convertToSpecifiedUnits
+ animLength.baseVal.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_MM);
+ is(animLength.animVal.valueInSpecifiedUnits, 40,
+ "un-animated animVal.value not changed after calling "
+ + "baseValue.convertToSpecifiedUnits");
+}
+
+function checkDiffValue(animLength) {
+ // We assume here that the animation is not additive and hence changing the
+ // baseValue will not be reflected in the animValue
+ var origValue = animLength.animVal.value;
+
+ // value
+ animLength.baseVal.value = 1;
+ is(animLength.animVal.value, origValue,
+ "animated animVal.value changed after setting baseValue.value");
+
+ // valueInSpecifiedUnits
+ animLength.baseVal.valueInSpecifiedUnits = 2;
+ is(animLength.animVal.value, origValue,
+ "animated animVal.value changed after setting "
+ + "baseValue.valueInSpecifiedUnits");
+
+ // valueAsString
+ animLength.baseVal.valueAsString = "3";
+ is(animLength.animVal.value, origValue,
+ "animated animVal.value changed after setting baseValue.valueAsString");
+
+ // newValueSpecifiedUnits
+ // (Note: we'd like to convert to MM here and CM in the next step for
+ // consistency with the other tests. However, internally that will cause the
+ // animVal to be converted to MM and back again which, given certain dpi
+ // values such as we get in Linux, this may lead to rounding errors so that
+ // 100 becomes 99.99999237060547. So instead we convert to something
+ // independent of dpi)
+ animLength.baseVal.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_PERCENTAGE, 40);
+ is(animLength.animVal.value, origValue,
+ "animated animVal.value changed after setting "
+ + "baseValue.newValueSpecifiedUnits");
+
+ // convertToSpecifiedUnits
+ animLength.baseVal.convertToSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX);
+ is(animLength.animVal.value, origValue,
+ "animated animVal.value changed after calling "
+ + "baseValue.convertToSpecifiedUnits");
+}
+
+window.addEventListener("load", main);
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_animLengthUnits.xhtml b/dom/svg/test/test_animLengthUnits.xhtml
new file mode 100644
index 0000000000..59faaed1f0
--- /dev/null
+++ b/dom/svg/test/test_animLengthUnits.xhtml
@@ -0,0 +1,125 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=507067
+-->
+<head>
+ <title>Test for units of SVG animated lengths</title>
+ <script 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=507067">Mozilla Bug 507067</a>
+<p id="display"></p>
+<div id="content">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="120px" height="120px"
+ onload="this.pauseAnimations()">
+ <g font-size="10px">
+ <circle cx="-100" cy="20" r="15" fill="blue" id="circle">
+ <animate attributeName="cx" from="0em" to="10em" dur="8s" begin="1s"
+ fill="freeze" id="animate"/>
+ </circle>
+ </g>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+/** Test units of animated lengths **/
+
+/* Global Variables */
+const svgns = "http://www.w3.org/2000/svg";
+var svg = document.getElementById("svg");
+var circle = document.getElementById("circle");
+var animate = document.getElementById("animate");
+
+SimpleTest.waitForExplicitFinish();
+
+// Interop comments are based on:
+//
+// Opera -- 10 beta 2
+// WebKit -- July 09 trunk build
+// Batik -- 1.7
+// Firefox -- July 09 trunk build
+//
+
+function main() {
+ ok(svg.animationsPaused(), "should be paused by <svg> load handler");
+ is(svg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler");
+
+ // Sanity check: check initial values
+ is(circle.cx.baseVal.valueInSpecifiedUnits, -100,
+ "Unexpected initial baseVal");
+ is(circle.cx.baseVal.unitType, SVGLength.SVG_LENGTHTYPE_NUMBER,
+ "Unexpected initial baseVal units");
+ is(circle.cx.animVal.valueInSpecifiedUnits, -100,
+ "Unexpected initial animVal");
+ is(circle.cx.animVal.unitType, SVGLength.SVG_LENGTHTYPE_NUMBER,
+ "Unexpected initial animVal units");
+
+ // Sample mid-way through the animation
+ svg.setCurrentTime(5);
+
+ // (1) Check the absolute value is right
+ //
+ // We're not too worried about the units. Based on our testing we get:
+ // Opera: Will use user units for the animVal
+ // Safari: Doesn't work
+ // Batik: Will use the units specified on the animation function provided they
+ // are the same
+ // FF: Will use the units of the baseVal for the animVal
+ //
+ is(circle.cx.baseVal.value, -100,
+ "(1) Unexpected value for baseVal during animation");
+ is(circle.cx.animVal.value, 50,
+ "(1) Unexpected value for animVal during animation");
+
+ // Change font-size and check
+ circle.parentNode.setAttribute("font-size", "5px");
+
+ // Currently, changing the font-size on a parent doesn't force a resample (see
+ // bug 508206) so we have to give the animation a chance to run
+ window.requestAnimationFrame(checkAfterChangeFontSize);
+}
+
+function checkAfterChangeFontSize() {
+ // (2) Check that changing the font-size of the parent element is reflected in
+ // the anim val
+ is(circle.cx.baseVal.value, -100,
+ "(2) Unexpected value for baseVal after changing font-size during " +
+ "animation");
+ is(circle.cx.animVal.value, 25,
+ "(2) Unexpected value for animVal after changing font-size during " +
+ "animation");
+
+ // Do the same again, when the animation is frozen
+ svg.setCurrentTime(10);
+ circle.parentNode.setAttribute("font-size", "7px");
+
+ // Again, due to bug 508206 we need to give the animation a chance to resample
+ window.requestAnimationFrame(checkWhilstFrozen);
+}
+
+function checkWhilstFrozen() {
+ // (3) Check that changing the font-size of the parent element is reflected in
+ // the anim val
+ is(circle.cx.baseVal.value, -100,
+ "(3) Unexpected value for baseVal after changing font-size whilst " +
+ "frozen");
+ is(circle.cx.animVal.value, 70,
+ "(3) Unexpected value for animVal after changing font-size whilst " +
+ "frozen");
+
+ SimpleTest.finish();
+}
+
+if (animate && animate.targetElement) {
+ window.addEventListener("load", main);
+} else {
+ ok(true); // Skip tests but don't report 'todo' either
+ SimpleTest.finish();
+}
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_bbox-changes.xhtml b/dom/svg/test/test_bbox-changes.xhtml
new file mode 100644
index 0000000000..1877754a21
--- /dev/null
+++ b/dom/svg/test/test_bbox-changes.xhtml
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1159053
+-->
+<head>
+ <title>Test that the results of getBBox update for changes</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<p id="display">
+ <svg xmlns="http://www.w3.org/2000/svg">
+ <rect id="rect1" x="10" y="10" width="10" height="10"/>
+ <rect id="rect2" x="30" y="10" width="10" height="10"/>
+ <g id="g">
+ <circle id="circle1" cx="60" cy="20" r="5"/>
+ <circle id="circle2" cx="120" cy="20" r="5"/>
+ </g>
+ </svg>
+</p>
+
+<div id="content" style="display: none"></div>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">//<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function init_and_run() {
+ SpecialPowers.pushPrefEnv({"set": [["svg.new-getBBox.enabled", true]]}, run);
+}
+
+function checkBBox(id, options, x, y, width, height, msg) {
+ var bbox = document.getElementById(id).getBBox(options);
+ is(bbox.x, x, id + ".getBBox().x" + msg);
+ is(bbox.y, y, id + ".getBBox().y" + msg);
+ is(bbox.width, width, id + ".getBBox().width" + msg);
+ is(bbox.height, height, id + ".getBBox().height" + msg);
+}
+
+function run() {
+ // First call getBBox on 'rect1' with stroke included:
+ $("rect1").setAttribute("stroke", "black");
+ $("rect1").setAttribute("stroke-width", "10");
+ checkBBox("rect1", { fill: true, stroke: true }, 5, 5, 20, 20, " with stroke");
+
+ // Now remove the stroke from 'rect1' and check again:
+ $("rect1").removeAttribute("stroke");
+ $("rect1").removeAttribute("stroke-width");
+ checkBBox("rect1", { fill: true }, 10, 10, 10, 10, " after stroke removed");
+
+ // First call getBBox on 'rect2' without a stroke included:
+ checkBBox("rect2", { fill: true }, 30, 10, 10, 10, " with stroke");
+
+ // Now add a stroke to 'rect2' and check again:
+ $("rect2").setAttribute("stroke", "black");
+ $("rect2").setAttribute("stroke-width", "10");
+ checkBBox("rect2", { fill: true, stroke: true }, 25, 5, 20, 20, " with stroke");
+
+ // Check the initial result for getBBox on the group:
+ checkBBox("g", { fill: true }, 55, 15, 70, 10, " before child moves");
+
+ // Now move one of the circle children and check again:
+ $("circle2").setAttribute("cx", "110");
+ checkBBox("g", { fill: true }, 55, 15, 60, 10, " after child moves");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", init_and_run);
+
+//]]></script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_bbox-with-invalid-viewBox.xhtml b/dom/svg/test/test_bbox-with-invalid-viewBox.xhtml
new file mode 100644
index 0000000000..c962b469f6
--- /dev/null
+++ b/dom/svg/test/test_bbox-with-invalid-viewBox.xhtml
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=433063
+-->
+<head>
+ <title>Test for getBBox when the viewBox is invalid</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<svg id="svg" xmlns="http://www.w3.org/2000/svg"
+ style="width:200px; height:200px;" viewBox="0 0 200 0">
+ <rect width="120" height="120"/>
+</svg>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var bbox = $("svg").getBBox();
+ is(bbox.x, 0, "Check bbox.x");
+ is(bbox.y, 0, "Check bbox.y");
+ is(bbox.width, 120, "Check bbox.width");
+ is(bbox.height, 120, "Check bbox.height");
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_bbox.xhtml b/dom/svg/test/test_bbox.xhtml
new file mode 100644
index 0000000000..19e1aadf2c
--- /dev/null
+++ b/dom/svg/test/test_bbox.xhtml
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=449327
+-->
+<head>
+ <title>Test for getBBox</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg" src="bbox-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">//<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var doc = $("svg").contentDocument;
+
+ function isFuzzy(a, b, error, name) {
+ ok(!(Math.abs(a - b) > error),
+ name + " - got " + a + ", expected " + b + " (within " + error + ")");
+ }
+
+ function getBBox(id) {
+ return doc.getElementById(id).getBBox();
+ }
+ function checkBBox(id, x, y, width, height, error) {
+ var bbox = getBBox(id);
+ isFuzzy(bbox.x, x, error, id + ".getBBox().x");
+ isFuzzy(bbox.y, y, error, id + ".getBBox().y");
+ isFuzzy(bbox.width, width, error, id + ".getBBox().width");
+ isFuzzy(bbox.height, height, error, id + ".getBBox().height");
+ }
+ function compareBBox(id1, id2) {
+ var bbox1 = getBBox(id1);
+ var bbox2 = getBBox(id2);
+ is(bbox1.x, bbox2.x, id1 + ".getBBox().x");
+ is(bbox1.y, bbox2.y, id1 + ".getBBox().y");
+ isFuzzy(bbox1.width, bbox2.width, 0.0002, id1 + ".getBBox().width");
+ is(bbox1.height, bbox2.height, id1 + ".getBBox().height");
+ }
+ function compareBBoxFuzzy(id1, id2, err) {
+ var bbox1 = getBBox(id1);
+ var bbox2 = getBBox(id2);
+ isfuzzy(bbox1.x, bbox2.x, err, id1 + ".getBBox().x");
+ isfuzzy(bbox1.y, bbox2.y, err, id1 + ".getBBox().y");
+ isfuzzy(bbox1.width, bbox2.width, err, id1 + ".getBBox().width");
+ isfuzzy(bbox1.height, bbox2.height, err, id1 + ".getBBox().height");
+ }
+ function compareBBoxHeight(id1, id2) {
+ var bbox1 = getBBox(id1);
+ var bbox2 = getBBox(id2);
+ is(bbox1.height, bbox2.height, id1 + ".getBBox().height");
+ }
+ function compareBBoxWidthWithScaleFuzzy(id1, id2, scaleOfId2, err) {
+ var bbox1 = getBBox(id1);
+ var bbox2 = getBBox(id2);
+ isfuzzy(bbox1.width, bbox2.width * scaleOfId2, err, id1 + ".getBBox().width");
+ }
+
+ checkBBox("fO", 10, 10, 100, 100, 0.0);
+ checkBBox("i", 10, 10, 100, 100, 0.0);
+ compareBBoxHeight("a", "b");
+ compareBBoxHeight("a", "y");
+ compareBBox("b", "tspantext1");
+ compareBBoxFuzzy("tspantext1", "tspan1", 5);
+ compareBBoxHeight("tspantext2", "tspan2");
+ compareBBoxWidthWithScaleFuzzy("tspantext2", "tspan2", 2, 5);
+ compareBBoxHeight("text", "lrmText");
+ checkBBox("v", 95, 45, 10, 155, 0.001);
+ checkBBox("h", 195, 45, 105, 55, 0.001);
+ checkBBox("e", 95, 95, 10, 10, 0.001);
+ checkBBox("use_v", 195, 145, 10, 155, 0.001);
+ checkBBox("use_h", 295, 145, 105, 55, 0.001);
+ checkBBox("use_e", 195, 195, 10, 10, 0.001);
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run);
+
+//]]></script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_bounds.html b/dom/svg/test/test_bounds.html
new file mode 100644
index 0000000000..0cf0001065
--- /dev/null
+++ b/dom/svg/test/test_bounds.html
@@ -0,0 +1,317 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=463934
+-->
+<head>
+ <title>Test for Bug 463934</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>
+ #css-trans-rect-1 {
+ transform: scaleX(2)
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=463934">Mozilla Bug 463934</a>
+<p id="display"></p>
+<div id="content">
+ <svg id="outer-1" xmlns="http://www.w3.org/2000/svg" width="30" height="30"></svg>
+ <svg id="outer-2" xmlns="http://www.w3.org/2000/svg" width="30" height="30"
+ style="padding: 10px;">
+ </svg>
+ <svg id="outer-3" xmlns="http://www.w3.org/2000/svg" width="30" height="30"
+ style="border: 10px solid black;">
+ </svg>
+ <svg id="outer-4" xmlns="http://www.w3.org/2000/svg" width="30" height="30"
+ style="border: 10px solid black; padding: 10px">
+ </svg>
+ <svg id="outer-5" xmlns="http://www.w3.org/2000/svg" width="30" height="30">
+ <rect id="css-trans-rect-1" width="6" height="6"></rect>
+ </svg>
+</div>
+
+<iframe id="svg" src="bounds-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function Rect(left, top, width, height) {
+ this.left = left;
+ this.top = top;
+ this.width = width;
+ this.height = height;
+}
+
+Rect.prototype.roundOut = function() {
+ this.width = Math.ceil(this.left + this.width) - Math.floor(this.left);
+ this.height = Math.ceil(this.top + this.height) - Math.floor(this.top);
+ this.left = Math.floor(this.left);
+ this.top = Math.floor(this.top);
+};
+
+function runTest() {
+ var bounds;
+
+ bounds = document.getElementById("outer-1").getBoundingClientRect();
+ is(bounds.width, 30, "outer-svg 1 getBoundingClientRect().width");
+ is(bounds.height, 30, "outer-svg 1 getBoundingClientRect().height");
+
+ bounds = document.getElementById("outer-2").getBoundingClientRect();
+ is(bounds.width, 50, "outer-svg 2 getBoundingClientRect().width");
+ is(bounds.height, 50, "outer-svg 2 getBoundingClientRect().height");
+
+ bounds = document.getElementById("outer-3").getBoundingClientRect();
+ is(bounds.width, 50, "outer-svg 3 getBoundingClientRect().width");
+ is(bounds.height, 50, "outer-svg 3 getBoundingClientRect().height");
+
+ bounds = document.getElementById("outer-4").getBoundingClientRect();
+ is(bounds.width, 70, "outer-svg 4 getBoundingClientRect().width");
+ is(bounds.height, 70, "outer-svg 4 getBoundingClientRect().height");
+
+ bounds = document.getElementById("css-trans-rect-1").getBoundingClientRect();
+ is(bounds.width, 12, "css-trans-rect getBoundingClientRect().width");
+ is(bounds.height, 6, "css-trans-rect getBoundingClientRect().height");
+
+ var doc = $("svg").contentWindow.document;
+
+ var svg1Bounds = doc.getElementById("svg1").getBoundingClientRect();
+ is(svg1Bounds.left, 10, "svg1.getBoundingClientRect().left");
+ is(svg1Bounds.top, 10, "svg1.getBoundingClientRect().top");
+ is(svg1Bounds.width, 25, "svg1.getBoundingClientRect().width");
+ is(svg1Bounds.height, 30, "svg1.getBoundingClientRect().height");
+
+ var svg2Bounds = doc.getElementById("svg2").getBoundingClientRect();
+ is(svg2Bounds.left, 0, "svg2.getBoundingClientRect().left");
+ is(svg2Bounds.top, 0, "svg2.getBoundingClientRect().top");
+ is(svg2Bounds.width, 2, "svg2.getBoundingClientRect().width");
+ is(svg2Bounds.height, 2, "svg2.getBoundingClientRect().height");
+
+ var svg3Bounds = doc.getElementById("svg3").getBoundingClientRect();
+ is(svg3Bounds.left, 0, "svg3.getBoundingClientRect().left");
+ is(svg3Bounds.top, 0, "svg3.getBoundingClientRect().top");
+ is(svg3Bounds.width, 1, "svg3.getBoundingClientRect().width");
+ is(svg3Bounds.height, 1, "svg3.getBoundingClientRect().height");
+
+ var use1Bounds = doc.getElementById("use1").getBoundingClientRect();
+ is(use1Bounds.left, 100, "use1.getBoundingClientRect().left");
+ is(use1Bounds.top, 50, "use1.getBoundingClientRect().top");
+ is(use1Bounds.width, 50, "use1.getBoundingClientRect().width");
+ is(use1Bounds.height, 10, "use1.getBoundingClientRect().height");
+
+ var useChildBounds = doc.getElementById("a-use").getBoundingClientRect();
+ is(useChildBounds.left, 100, "useChild.getBoundingClientRect().left");
+ is(useChildBounds.top, 50, "useChild.getBoundingClientRect().top");
+ is(useChildBounds.width, 50, "useChild.getBoundingClientRect().width");
+ is(useChildBounds.height, 10, "useChild.getBoundingClientRect().height");
+
+ var text1 = doc.getElementById("text1");
+
+ var text1Bounds = text1.getBoundingClientRect();
+ var text2Bounds = doc.getElementById("text2").getBoundingClientRect();
+ var text3Bounds = doc.getElementById("text3").getBoundingClientRect();
+
+ const sin45 = Math.sin(Math.PI / 4);
+
+ isfuzzy(text1Bounds.left, 24, 1, "text1.getBoundingClientRect().left");
+
+ is(text2Bounds.left, text1Bounds.left + 100, "text2.getBoundingClientRect().left");
+ is(text2Bounds.top, text1Bounds.top, "text2.getBoundingClientRect().top");
+ isfuzzy(text2Bounds.width, text1Bounds.width, 0.1, "text2.getBoundingClientRect().width");
+ is(text2Bounds.height, text1Bounds.height, "text2.getBoundingClientRect().height");
+
+ var r = (text1Bounds.width + text1Bounds.height) * sin45;
+ isfuzzy(text3Bounds.width, Math.ceil(r), 1, "text3.getBoundingClientRect().width");
+ isfuzzy(text3Bounds.height, Math.ceil(r), 1, "text3.getBoundingClientRect().height");
+
+ var rect1Bounds = doc.getElementById("rect1").getBoundingClientRect();
+ var rect2Bounds = doc.getElementById("rect2").getBoundingClientRect();
+ var rect3Bounds = doc.getElementById("rect3").getBoundingClientRect();
+ var rect4Bounds = doc.getElementById("rect4").getBoundingClientRect();
+
+ is(rect1Bounds.left, 50, "rect1.getBoundingClientRect().left");
+ is(rect1Bounds.top, 50, "rect1.getBoundingClientRect().top");
+ is(rect1Bounds.width, 50, "rect1.getBoundingClientRect().width");
+ is(rect1Bounds.height, 50, "rect1.getBoundingClientRect().height");
+
+ var rect = new Rect(175 - 50 * sin45, 75 - 50 * sin45, 50 * sin45 * 2, 50 * sin45 * 2);
+ isfuzzy(rect2Bounds.left, rect.left, 0.1, "rect2.getBoundingClientRect().left");
+ isfuzzy(rect2Bounds.top, rect.top, 0.1, "rect2.getBoundingClientRect().top");
+ isfuzzy(rect2Bounds.width, rect.width, 0.1, "rect2.getBoundingClientRect().width");
+ isfuzzy(rect2Bounds.height, rect.height, 0.1, "rect2.getBoundingClientRect().height");
+
+ is(rect3Bounds.left, 50, "rect3.getBoundingClientRect().left");
+ is(rect3Bounds.top, 160, "rect3.getBoundingClientRect().top");
+ is(rect3Bounds.width, 100, "rect3.getBoundingClientRect().width");
+ is(rect3Bounds.height, 100, "rect3.getBoundingClientRect().height");
+
+ rect = new Rect(350 - 100 * sin45, 150 - 100 * sin45, 100 * sin45 * 2, 100 * sin45 * 2);
+ isfuzzy(rect4Bounds.left, rect.left, 0.1, "rect4.getBoundingClientRect().left");
+ isfuzzy(rect4Bounds.top, rect.top, 0.1, "rect4.getBoundingClientRect().top");
+ isfuzzy(rect4Bounds.width, rect.width, 0.1, "rect4.getBoundingClientRect().width");
+ isfuzzy(rect4Bounds.height, rect.height, 0.1, "rect4.getBoundingClientRect().height");
+
+ var rect1aBounds = doc.getElementById("rect1a").getBoundingClientRect();
+ var rect2aBounds = doc.getElementById("rect2a").getBoundingClientRect();
+ var rect3aBounds = doc.getElementById("rect3a").getBoundingClientRect();
+ var rect3bBounds = doc.getElementById("rect3b").getBoundingClientRect();
+ var rect4aBounds = doc.getElementById("rect4a").getBoundingClientRect();
+
+ is(rect1aBounds.left, 48, "rect1a.getBoundingClientRect().left");
+ is(rect1aBounds.top, 48, "rect1a.getBoundingClientRect().top");
+ is(rect1aBounds.width, 54, "rect1a.getBoundingClientRect().width");
+ is(rect1aBounds.height, 54, "rect1a.getBoundingClientRect().height");
+
+ rect = new Rect(175 - 54 * sin45, 75 - 54 * sin45, 54 * sin45 * 2, 54 * sin45 * 2);
+ isfuzzy(rect2aBounds.left, rect.left, 0.1, "rect2a.getBoundingClientRect().left");
+ isfuzzy(rect2aBounds.top, rect.top, 0.1, "rect2a.getBoundingClientRect().top");
+ isfuzzy(rect2aBounds.width, rect.width, 0.1, "rect2a.getBoundingClientRect().width");
+ isfuzzy(rect2aBounds.height, rect.height, 0.1, "rect2a.getBoundingClientRect().height");
+
+ is(rect3aBounds.left, 46, "rect3a.getBoundingClientRect().left");
+ is(rect3aBounds.top, 156, "rect3a.getBoundingClientRect().top");
+ is(rect3aBounds.width, 108, "rect3a.getBoundingClientRect().width");
+ is(rect3aBounds.height, 108, "rect3a.getBoundingClientRect().height");
+
+ isfuzzy(rect3bBounds.left, 198, 0.1, "rect3b.getBoundingClientRect().left");
+ isfuzzy(rect3bBounds.top, 198, 0.1, "rect3b.getBoundingClientRect().top");
+ isfuzzy(rect3bBounds.width, 54, 0.1, "rect3b.getBoundingClientRect().width");
+ isfuzzy(rect3bBounds.height, 54, 0.1, "rect3b.getBoundingClientRect().height");
+
+ rect = new Rect(350 - 108 * sin45, 150 - 108 * sin45, 108 * sin45 * 2, 108 * sin45 * 2);
+ isfuzzy(rect4aBounds.left, rect.left, 0.1, "rect4a.getBoundingClientRect().left");
+ isfuzzy(rect4aBounds.top, rect.top, 0.1, "rect4a.getBoundingClientRect().top");
+ isfuzzy(rect4aBounds.width, rect.width, 0.1, "rect4a.getBoundingClientRect().width");
+ isfuzzy(rect4aBounds.height, rect.height, 0.1, "rect4a.getBoundingClientRect().height");
+
+ var text1a = doc.getElementById("text1a");
+ var text1aBounds = text1a.getBoundingClientRect();
+
+ var text1b = doc.getElementById("text1b");
+ var text1bBounds = text1b.getBoundingClientRect();
+
+ var text2aBounds = doc.getElementById("text2a").getBoundingClientRect();
+
+ isfuzzy(text1aBounds.left, 82, 1, "text1a.getBoundingClientRect().left");
+ is(text1aBounds.width, text1Bounds.width + 4, "text1a.getBoundingClientRect().width");
+ is(text1bBounds.width, text1Bounds.width, "text1b.getBoundingClientRect().width");
+ isfuzzy(text1bBounds.height, 196, 5, "text1b.getBoundingClientRect().height");
+
+ is(text2aBounds.left, text1aBounds.left + 100 - 3, "text2a.getBoundingClientRect().left");
+ isfuzzy(text2aBounds.width, text1aBounds.width + 6, 0.1, "text2a.getBoundingClientRect().width");
+
+ var iBounds = doc.getElementById("i").getBoundingClientRect();
+ is(iBounds.left, 20, "i.getBoundingClientRect().left");
+ is(iBounds.top, 20, "i.getBoundingClientRect().top");
+ is(iBounds.width, 200, "i.getBoundingClientRect().width");
+ is(iBounds.height, 200, "i.getBoundingClientRect().height");
+
+ var text4Bounds = doc.getElementById("text4").getBoundingClientRect();
+ is(text4Bounds.left, 0, "text4.getBoundingClientRect().left");
+ is(text4Bounds.top, 0, "text4.getBoundingClientRect().top");
+ is(text4Bounds.width, 0, "text4.getBoundingClientRect().width");
+ is(text4Bounds.height, 0, "text4.getBoundingClientRect().height");
+
+ var gBounds = doc.getElementById("g2").getBoundingClientRect();
+ is(gBounds.left, 100, "g2.getBoundingClientRect().left");
+ is(gBounds.top, 100, "g2.getBoundingClientRect().top");
+ is(gBounds.width, 50, "g2.getBoundingClientRect().width");
+ is(gBounds.height, 50, "g2.getBoundingClientRect().height");
+
+ var nonScalingStrokedCircle1Bounds =
+ doc.getElementById("nonScalingStrokedCircle1").getBoundingClientRect();
+ isfuzzy(nonScalingStrokedCircle1Bounds.left, 10, 0.15,
+ "nonScalingStrokedCircle1.getBoundingClientRect().left");
+ isfuzzy(nonScalingStrokedCircle1Bounds.top, 105, 0.15,
+ "nonScalingStrokedCircle1.getBoundingClientRect().top");
+ isfuzzy(nonScalingStrokedCircle1Bounds.width, 70, 0.15,
+ "nonScalingStrokedCircle1.getBoundingClientRect().width");
+ isfuzzy(nonScalingStrokedCircle1Bounds.height, 50, 0.15,
+ "nonScalingStrokedCircle1.getBoundingClientRect().height");
+
+ var nonScalingStrokedEllipse1Bounds =
+ doc.getElementById("nonScalingStrokedEllipse1").getBoundingClientRect();
+ isfuzzy(nonScalingStrokedEllipse1Bounds.left, 5, 0.15,
+ "nonScalingStrokedEllipse1.getBoundingClientRect().left");
+ isfuzzy(nonScalingStrokedEllipse1Bounds.top, 40, 0.15,
+ "nonScalingStrokedEllipse1.getBoundingClientRect().top");
+ isfuzzy(nonScalingStrokedEllipse1Bounds.width, 30, 0.15,
+ "nonScalingStrokedEllipse1.getBoundingClientRect().width");
+ isfuzzy(nonScalingStrokedEllipse1Bounds.height, 40, 0.15,
+ "nonScalingStrokedEllipse1.getBoundingClientRect().height");
+
+ var nonScalingStrokedLine1Bounds =
+ doc.getElementById("nonScalingStrokedLine1").getBoundingClientRect();
+ isfuzzy(nonScalingStrokedLine1Bounds.left, 235, 0.1,
+ "nonScalingStrokedLine1.getBoundingClientRect().left");
+ isfuzzy(nonScalingStrokedLine1Bounds.top, 10, 0.1,
+ "nonScalingStrokedLine1.getBoundingClientRect().top");
+ isfuzzy(nonScalingStrokedLine1Bounds.width, 10, 0.1,
+ "nonScalingStrokedLine1.getBoundingClientRect().width");
+ isfuzzy(nonScalingStrokedLine1Bounds.height, 25, 0.1,
+ "nonScalingStrokedLine1.getBoundingClientRect().height");
+
+ var nonScalingStrokedLine2Bounds =
+ doc.getElementById("nonScalingStrokedLine2").getBoundingClientRect();
+ var capDelta = 5 / Math.SQRT2 + 5 / Math.SQRT2;
+ rect = new Rect(260 - capDelta, 15 - capDelta, 20 / Math.SQRT2 + 2 * capDelta,
+ 20 / Math.SQRT2 + 2 * capDelta);
+ isfuzzy(nonScalingStrokedLine2Bounds.left, rect.left, 0.1,
+ "nonScalingStrokedLine2.getBoundingClientRect().left");
+ isfuzzy(nonScalingStrokedLine2Bounds.top, rect.top, 0.1,
+ "nonScalingStrokedLine2.getBoundingClientRect().top");
+ isfuzzy(nonScalingStrokedLine2Bounds.width, rect.width, 0.15,
+ "nonScalingStrokedLine2.getBoundingClientRect().width");
+ isfuzzy(nonScalingStrokedLine2Bounds.height, rect.height, 0.15,
+ "nonScalingStrokedLine2.getBoundingClientRect().height");
+
+ var nonScalingStrokedLine3Bounds =
+ doc.getElementById("nonScalingStrokedLine3").getBoundingClientRect();
+ capDelta = 5 / Math.SQRT2;
+ rect = new Rect(280 - capDelta, 15 - capDelta, 20 / Math.SQRT2 + 2 * capDelta,
+ 20 / Math.SQRT2 + 2 * capDelta);
+ isfuzzy(nonScalingStrokedLine3Bounds.left, rect.left, 0.1,
+ "nonScalingStrokedLine3.getBoundingClientRect().left");
+ isfuzzy(nonScalingStrokedLine3Bounds.top, rect.top, 0.1,
+ "nonScalingStrokedLine3.getBoundingClientRect().top");
+ isfuzzy(nonScalingStrokedLine3Bounds.width, rect.width, 0.1,
+ "nonScalingStrokedLine3.getBoundingClientRect().width");
+ isfuzzy(nonScalingStrokedLine3Bounds.height, rect.height, 0.1,
+ "nonScalingStrokedLine3.getBoundingClientRect().height");
+
+ var shapeWithMarker1Bounds =
+ doc.getElementById("shapeWithMarker1").getBoundingClientRect();
+ isfuzzy(shapeWithMarker1Bounds.left, 160, 0.1,
+ "shapeWithMarker1Bounds.left");
+ isfuzzy(shapeWithMarker1Bounds.top, 120, 0.1,
+ "shapeWithMarker1Bounds.top");
+ isfuzzy(shapeWithMarker1Bounds.width, 10 + Math.SQRT2 * 50, 0.1,
+ "shapeWithMarker1Bounds.width");
+ isfuzzy(shapeWithMarker1Bounds.height, 20, 0.1,
+ "shapeWithMarker1Bounds.height");
+
+ var rotatedLine1Bounds =
+ doc.getElementById("rotatedLine1").getBoundingClientRect();
+ isfuzzy(rotatedLine1Bounds.left, 160, 0.1, "rotatedLine1Bounds.left");
+ isfuzzy(rotatedLine1Bounds.top, 145, 0.1, "rotatedLine1Bounds.top");
+ isfuzzy(rotatedLine1Bounds.width, Math.SQRT2 * 20, 0.1,
+ "rotatedLine1Bounds.width");
+ isfuzzy(rotatedLine1Bounds.height, 10, 0.1, "rotatedLine1Bounds.height");
+
+ var cssTransRect2Bounds =
+ doc.getElementById("css-trans-rect-2").getBoundingClientRect();
+ is(cssTransRect2Bounds.left, 10, "cssTransRect2Bounds.left");
+ is(cssTransRect2Bounds.top, 5, "cssTransRect2Bounds.top");
+ is(cssTransRect2Bounds.width, 12, "cssTransRect2Bounds.width");
+ is(cssTransRect2Bounds.height, 6, "cssTransRect2Bounds.height");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", runTest);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_bug1426594.html b/dom/svg/test/test_bug1426594.html
new file mode 100644
index 0000000000..ac975093c8
--- /dev/null
+++ b/dom/svg/test/test_bug1426594.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1426594
+-->
+<head>
+ <title>Test for Bug 1426594</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ function runTests() {
+ let textElement = document.getElementById("textId"),
+ tspanElement = document.getElementById("tspanId");
+
+ isfuzzy(textElement.getBoundingClientRect().width, tspanElement.getBoundingClientRect().width, 5);
+ isfuzzy(textElement.getBoundingClientRect().height, tspanElement.getBoundingClientRect().height, 5);
+
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body onload="runTests()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=589640">Mozilla Bug 1426594</a>
+<svg height="1.5em" width="200px">
+<text id="textId" y="1em"><tspan id="tspanId">ABCDEF</tspan></text>
+</svg>
+<div style="pointer-events: none; border: 1px solid red; position: absolute;
+ z-index: 1"
+ id="highlight">
+</div>
+</body>
+</html>
diff --git a/dom/svg/test/test_bug872812.html b/dom/svg/test/test_bug872812.html
new file mode 100644
index 0000000000..5369504c8f
--- /dev/null
+++ b/dom/svg/test/test_bug872812.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=872812
+-->
+<head>
+ <title>Test for Bug 872812</title>
+ <script 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=872812">Mozilla Bug 872812</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg" src="viewport-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ok(svg, "SVG exists");
+var a = document.createEvent("CustomEvent").initCustomEvent("", "", "", svg.viewBox);
+ok(true, "CustomEvent exists and we are not crashed!");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_dataTypes.html b/dom/svg/test/test_dataTypes.html
new file mode 100644
index 0000000000..a02a52e2a1
--- /dev/null
+++ b/dom/svg/test/test_dataTypes.html
@@ -0,0 +1,377 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=437448
+-->
+<head>
+ <title>Test for Bug 437448</title>
+ <script 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=437448">Mozilla Bug 437448</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg" src="dataTypes-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ var doc = $("svg").contentWindow.document;
+ var filter = doc.getElementById("filter");
+ var convolve = doc.getElementById("convolve");
+ var blur = doc.getElementById("blur");
+ var marker = doc.getElementById("marker");
+
+ // class attribute
+ filter.setAttribute("class", "foo");
+ is(filter.getAttribute("class"), "foo", "class attribute");
+ is(filter.className.baseVal, "foo", "className baseVal");
+ is(filter.className.animVal, "foo", "className animVal");
+ filter.className.baseVal = "bar";
+ is(filter.getAttribute("class"), "bar", "class attribute");
+ is(filter.className.baseVal, "bar", "className baseVal");
+ is(filter.className.animVal, "bar", "className animVal");
+ filter.removeAttribute("class");
+ is(filter.hasAttribute("class"), false, "class attribute");
+ ok(filter.getAttribute("class") === null, "removed class attribute");
+ is(filter.className.baseVal, "", "className baseVal");
+ is(filter.className.animVal, "", "className animVal");
+ filter.setAttribute("class", "");
+ ok(filter.getAttribute("class") === "", "empty class attribute");
+
+ // length attribute
+
+ marker.setAttribute("markerWidth", "12.5");
+ is(marker.markerWidth.baseVal.value, 12.5, "length baseVal");
+ is(marker.markerWidth.animVal.value, 12.5, "length animVal");
+
+ var baseMarkerWidth = marker.markerWidth.baseVal;
+ var animMarkerWidth = marker.markerWidth.animVal;
+ marker.setAttribute("markerWidth", "15.5");
+ is(baseMarkerWidth.value, 15.5, "length baseVal");
+ is(animMarkerWidth.value, 15.5, "length animVal");
+
+ marker.markerWidth.baseVal.value = 7.5;
+ is(marker.markerWidth.animVal.value, 7.5, "length animVal");
+ is(marker.getAttribute("markerWidth"), "7.5", "length attribute");
+
+ marker.setAttribute("markerWidth", "");
+ ok(marker.getAttribute("markerWidth") === "", "empty length attribute");
+ marker.removeAttribute("markerWidth");
+ ok(marker.getAttribute("markerWidth") === null, "removed length attribute");
+
+ // number attribute
+
+ convolve.setAttribute("divisor", "12.5");
+ is(convolve.divisor.baseVal, 12.5, "number baseVal");
+ is(convolve.divisor.animVal, 12.5, "number animVal");
+
+ convolve.divisor.baseVal = 7.5;
+ is(convolve.divisor.animVal, 7.5, "number animVal");
+ is(convolve.getAttribute("divisor"), "7.5", "number attribute");
+
+ convolve.setAttribute("divisor", "");
+ ok(convolve.getAttribute("divisor") === "", "empty number attribute");
+ convolve.removeAttribute("divisor");
+ ok(convolve.getAttribute("divisor") === null, "removed number attribute");
+
+ // number-optional-number attribute
+
+ blur.setAttribute("stdDeviation", "20.5");
+ is(blur.stdDeviationX.baseVal, 20.5, "number-optional-number first baseVal");
+ is(blur.stdDeviationX.animVal, 20.5, "number-optional-number first animVal");
+ is(blur.stdDeviationY.baseVal, 20.5, "number-optional-number second baseVal");
+ is(blur.stdDeviationY.animVal, 20.5, "number-optional-number second animVal");
+
+ blur.stdDeviationX.baseVal = 8.5;
+ is(blur.stdDeviationX.animVal, 8.5, "number-optional-number first animVal");
+ is(blur.stdDeviationY.animVal, 20.5, "number-optional-number second animVal");
+ is(blur.getAttribute("stdDeviation"), "8.5, 20.5", "number-optional-number attribute");
+
+ blur.stdDeviationY.baseVal = 8.5;
+ is(blur.getAttribute("stdDeviation"), "8.5", "number-optional-number attribute");
+
+ blur.setStdDeviation(24.5, 0.5);
+ is(blur.stdDeviationX.baseVal, 24.5, "number-optional-number first baseVal");
+ is(blur.stdDeviationX.animVal, 24.5, "number-optional-number first animVal");
+ is(blur.stdDeviationY.baseVal, 0.5, "number-optional-number second baseVal");
+ is(blur.stdDeviationY.animVal, 0.5, "number-optional-number second animVal");
+
+ blur.setAttribute("stdDeviation", "");
+ ok(blur.getAttribute("stdDeviation") === "",
+ "empty number-optional-number attribute");
+ blur.removeAttribute("stdDeviation");
+ ok(blur.getAttribute("stdDeviation") === null,
+ "removed number-optional-number attribute");
+
+ // integer attribute
+
+ convolve.setAttribute("targetX", "12");
+ is(convolve.targetX.baseVal, 12, "integer baseVal");
+ is(convolve.targetX.animVal, 12, "integer animVal");
+ convolve.targetX.baseVal = 7;
+ is(convolve.targetX.animVal, 7, "integer animVal");
+ is(convolve.getAttribute("targetX"), "7", "integer attribute");
+ convolve.setAttribute("targetX", "");
+ ok(convolve.getAttribute("targetX") === "", "empty integer attribute");
+ convolve.removeAttribute("targetX");
+ ok(convolve.getAttribute("targetX") === null, "removed integer attribute");
+
+ // integer-optional-integer attribute
+
+ convolve.setAttribute("order", "5");
+ is(convolve.orderX.baseVal, 5, "integer-optional-integer first baseVal");
+ is(convolve.orderX.animVal, 5, "integer-optional-integer first animVal");
+ is(convolve.orderY.baseVal, 5, "integer-optional-integer second baseVal");
+ is(convolve.orderY.animVal, 5, "integer-optional-integer second animVal");
+
+ convolve.orderX.baseVal = 7;
+ is(convolve.orderX.animVal, 7, "integer-optional-integer first animVal");
+ is(convolve.orderY.animVal, 5, "integer-optional-integer second animVal");
+ is(convolve.getAttribute("order"), "7, 5", "integer-optional-integer attribute");
+
+ convolve.orderY.baseVal = 7;
+ is(convolve.getAttribute("order"), "7", "integer-optional-integer attribute");
+
+ convolve.setAttribute("order", "11, 13");
+ is(convolve.orderX.baseVal, 11, "integer-optional-integer first baseVal");
+ is(convolve.orderX.animVal, 11, "integer-optional-integer first animVal");
+ is(convolve.orderY.baseVal, 13, "integer-optional-integer second baseVal");
+ is(convolve.orderY.animVal, 13, "integer-optional-integer second animVal");
+
+ // 32 bit integer range
+ convolve.setAttribute("order", "-2147483648, 2147483647");
+ is(convolve.orderX.baseVal, -2147483648, "integer-optional-integer first baseVal");
+ is(convolve.orderX.animVal, -2147483648, "integer-optional-integer first animVal");
+ is(convolve.orderY.baseVal, 2147483647, "integer-optional-integer second baseVal");
+ is(convolve.orderY.animVal, 2147483647, "integer-optional-integer second animVal");
+
+ // too big, clamp
+ convolve.setAttribute("order", "-2147483649, 2147483648");
+ is(convolve.orderX.baseVal, -2147483648, "integer-optional-integer first baseVal");
+ is(convolve.orderX.animVal, -2147483648, "integer-optional-integer first animVal");
+ is(convolve.orderY.baseVal, 2147483647, "integer-optional-integer second baseVal");
+ is(convolve.orderY.animVal, 2147483647, "integer-optional-integer second animVal");
+
+ // invalid
+ convolve.setAttribute("order", "-00000000000invalid, 214748364720invalid");
+ is(convolve.orderX.baseVal, 3, "integer-optional-integer first baseVal");
+ is(convolve.orderX.animVal, 3, "integer-optional-integer first animVal");
+ is(convolve.orderY.baseVal, 3, "integer-optional-integer second baseVal");
+ is(convolve.orderY.animVal, 3, "integer-optional-integer second animVal");
+
+ convolve.setAttribute("order", "");
+ ok(convolve.getAttribute("order") === "",
+ "empty integer-optional-integer attribute");
+ convolve.removeAttribute("order");
+ ok(convolve.getAttribute("order") === null,
+ "removed integer-optional-integer attribute");
+
+ // angle attribute
+
+ marker.setAttribute("orient", "90deg");
+ is(marker.orientAngle.baseVal.value, 90, "angle baseVal");
+ is(marker.orientAngle.animVal.value, 90, "angle animVal");
+
+ var baseAngle = marker.orientAngle.baseVal;
+ var animAngle = marker.orientAngle.animVal;
+ marker.setAttribute("orient", "45deg");
+ is(baseAngle.value, 45, "angle baseVal");
+ is(animAngle.value, 45, "angle animVal");
+
+ marker.orientAngle.baseVal.value = 30;
+ is(marker.orientAngle.animVal.value, 30, "angle animVal");
+ is(marker.getAttribute("orient"), "30deg", "angle attribute");
+
+ marker.setAttribute("orient", "auto");
+ is(marker.getAttribute("orient"), "auto", "checking 'auto' string preserved");
+ is(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_AUTO, "type baseVal");
+ is(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_AUTO, "type animVal");
+
+ marker.setAttribute("orient", "auto-start-reverse");
+ is(marker.getAttribute("orient"), "auto-start-reverse", "checking 'auto-start-reverse' string preserved");
+ is(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_AUTO_START_REVERSE, "type baseVal");
+ is(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_AUTO_START_REVERSE, "type animVal");
+
+ marker.setAttribute("orient", "");
+ ok(marker.getAttribute("orient") === "", "empty angle attribute");
+ marker.removeAttribute("orient");
+ ok(marker.getAttribute("orient") === null, "removed angle attribute");
+
+ // boolean attribute
+
+ convolve.setAttribute("preserveAlpha", "false");
+ is(convolve.preserveAlpha.baseVal, false, "boolean baseVal");
+ is(convolve.preserveAlpha.animVal, false, "boolean animVal");
+ convolve.preserveAlpha.baseVal = true;
+ is(convolve.preserveAlpha.animVal, true, "boolean animVal");
+ is(convolve.getAttribute("preserveAlpha"), "true", "boolean attribute");
+ convolve.setAttribute("preserveAlpha", "");
+ ok(convolve.getAttribute("preserveAlpha") === "", "empty boolean attribute");
+ convolve.removeAttribute("preserveAlpha");
+ ok(convolve.getAttribute("preserveAlpha") === null,
+ "removed boolean attribute");
+
+ // enum attribute
+
+ is(1, SVGFEConvolveMatrixElement.SVG_EDGEMODE_DUPLICATE, "SVG_EDGEMODE_DUPLICATE value");
+ is(2, SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP, "SVG_EDGEMODE_WRAP value");
+
+ convolve.setAttribute("edgeMode", "wrap");
+ is(convolve.edgeMode.baseVal, SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP, "enum baseVal");
+ is(convolve.edgeMode.animVal, SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP, "enum animVal");
+ convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_DUPLICATE;
+ is(convolve.edgeMode.animVal, SVGFEConvolveMatrixElement.SVG_EDGEMODE_DUPLICATE, "enum animVal");
+ is(convolve.getAttribute("edgeMode"), "duplicate", "enum attribute");
+ convolve.setAttribute("edgeMode", "");
+ ok(convolve.getAttribute("edgeMode") === "", "empty enum attribute");
+ convolve.removeAttribute("edgeMode");
+ ok(convolve.getAttribute("edgeMode") === null, "removed enum attribute");
+
+ // string attribute
+
+ convolve.setAttribute("result", "foo");
+ is(convolve.result.baseVal, "foo", "string baseVal");
+ is(convolve.result.animVal, "foo", "string animVal");
+ convolve.result.baseVal = "bar";
+ is(convolve.result.animVal, "bar", "string animVal");
+ is(convolve.getAttribute("result"), "bar", "string attribute");
+ convolve.setAttribute("result", "");
+ ok(convolve.getAttribute("result") === "", "empty string attribute");
+ convolve.removeAttribute("result");
+ ok(convolve.getAttribute("result") === null, "removed string attribute");
+
+ // preserveAspectRatio attribute
+
+ is(0, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_UNKNOWN, "SVG_PRESERVEASPECTRATIO_UNKNOWN value");
+ is(1, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_NONE, "SVG_PRESERVEASPECTRATIO_NONE value");
+ is(3, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN, "SVG_PRESERVEASPECTRATIO_XMIDYMIN value");
+ is(5, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMID, "SVG_PRESERVEASPECTRATIO_XMINYMID value");
+ is(7, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMID, "SVG_PRESERVEASPECTRATIO_XMAXYMID value");
+ is(10, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMAX, "SVG_PRESERVEASPECTRATIO_XMAXYMAX value");
+
+ is(0, SVGPreserveAspectRatio.SVG_MEETORSLICE_UNKNOWN, "SVG_MEETORSLICE_UNKNOWN value");
+ is(1, SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET, "SVG_MEETORSLICE_MEET value");
+ is(2, SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "SVG_MEETORSLICE_SLICE value");
+
+ marker.setAttribute("preserveAspectRatio", "xMinYMid slice");
+ is(marker.preserveAspectRatio.baseVal.align,
+ SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMID, "preserveAspectRatio.align baseVal");
+ is(marker.preserveAspectRatio.animVal.align,
+ SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMINYMID, "preserveAspectRatio.align animVal");
+ is(marker.preserveAspectRatio.baseVal.meetOrSlice,
+ SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice baseVal");
+ is(marker.preserveAspectRatio.animVal.meetOrSlice,
+ SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice animVal");
+ marker.preserveAspectRatio.baseVal.align =
+ SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN;
+ is(marker.preserveAspectRatio.animVal.align,
+ SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN, "preserveAspectRatio animVal");
+ is(marker.preserveAspectRatio.animVal.meetOrSlice,
+ SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice animVal");
+ marker.preserveAspectRatio.baseVal.meetOrSlice = SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET;
+ is(marker.preserveAspectRatio.animVal.align,
+ SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN, "preserveAspectRatio animVal");
+ is(marker.preserveAspectRatio.animVal.meetOrSlice,
+ SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET, "preserveAspectRatio.meetOrSlice animVal");
+ is(marker.getAttribute("preserveAspectRatio"), "xMidYMin meet", "preserveAspectRatio attribute");
+
+ var basePreserveAspectRatio = marker.preserveAspectRatio.baseVal;
+ var animPreserveAspectRatio = marker.preserveAspectRatio.animVal;
+ marker.setAttribute("preserveAspectRatio", "xMaxYMid slice");
+ is(basePreserveAspectRatio.align,
+ SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMID, "preserveAspectRatio.align baseVal");
+ is(animPreserveAspectRatio.align,
+ SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMID, "preserveAspectRatio.align animVal");
+ is(basePreserveAspectRatio.meetOrSlice,
+ SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice baseVal");
+ is(animPreserveAspectRatio.meetOrSlice,
+ SVGPreserveAspectRatio.SVG_MEETORSLICE_SLICE, "preserveAspectRatio.meetOrSlice animVal");
+
+ marker.setAttribute("preserveAspectRatio", " none"); // invalid, space at beginning
+ is(basePreserveAspectRatio.align, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMID,
+ "default preserveAspectRatio attribute");
+
+ marker.setAttribute("preserveAspectRatio", "none "); // invalid, space at end
+ is(basePreserveAspectRatio.align, SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMID,
+ "default preserveAspectRatio attribute");
+
+ marker.setAttribute("preserveAspectRatio", "");
+ ok(marker.getAttribute("preserveAspectRatio") === "",
+ "empty preserveAspectRatio attribute");
+ marker.removeAttribute("preserveAspectRatio");
+ ok(marker.getAttribute("preserveAspectRatio") === null,
+ "removed preserveAspectRatio attribute");
+
+ // viewBox attribute
+ var baseViewBox = marker.viewBox.baseVal;
+ var animViewBox = marker.viewBox.animVal;
+ is(baseViewBox, null, "viewBox baseVal");
+ is(animViewBox, null, "viewBox animVal");
+
+ marker.setAttribute("viewBox", "1 2 3 4");
+ is(marker.viewBox.baseVal.x, 1, "viewBox.x baseVal");
+ is(marker.viewBox.animVal.x, 1, "viewBox.x animVal");
+ is(marker.viewBox.baseVal.y, 2, "viewbox.y baseVal");
+ is(marker.viewBox.animVal.y, 2, "viewbox.y animVal");
+ is(marker.viewBox.baseVal.width, 3, "viewbox.width baseVal");
+ is(marker.viewBox.animVal.width, 3, "viewbox.width animVal");
+ is(marker.viewBox.baseVal.height, 4, "viewbox.height baseVal");
+ is(marker.viewBox.animVal.height, 4, "viewbox.height animVal");
+ marker.viewBox.baseVal.x = 5;
+ is(marker.viewBox.animVal.x, 5, "viewBox.x animVal");
+ marker.viewBox.baseVal.y = 6;
+ is(marker.viewBox.animVal.y, 6, "viewBox.y animVal");
+ marker.viewBox.baseVal.width = 7;
+ is(marker.viewBox.animVal.width, 7, "viewBox.width animVal");
+ marker.viewBox.baseVal.height = 8;
+ is(marker.viewBox.animVal.height, 8, "viewBox.height animVal");
+ is(marker.getAttribute("viewBox"), "5 6 7 8", "viewBox attribute");
+ var storedViewBox = marker.viewBox.baseVal;
+ marker.removeAttribute("viewBox");
+ is(marker.hasAttribute("viewBox"), false, "viewBox hasAttribute");
+ ok(marker.getAttribute("viewBox") === null, "removed viewBox attribute");
+ is(marker.viewBox.baseVal, null, "viewBox baseVal");
+ is(marker.viewBox.animVal, null, "viewBox animVal");
+
+ is(storedViewBox.width, 7, "Should not lose values");
+ storedViewBox.width = 200;
+ is(storedViewBox.width, 200, "Should be able to change detached viewBox rect");
+ is(marker.hasAttribute("viewBox"), false, "viewBox hasAttribute should still be false");
+ ok(marker.getAttribute("viewBox") === null, "viewBox attribute should still be null");
+ is(marker.viewBox.baseVal, null, "viewBox baseVal");
+ is(marker.viewBox.animVal, null, "viewBox animVal");
+
+ marker.setAttribute("viewBox", "none");
+ is(marker.hasAttribute("viewBox"), true, "viewBox hasAttribute");
+ is(marker.viewBox.baseVal, null, "viewBox baseVal");
+ is(marker.viewBox.animVal, null, "viewBox animVal");
+
+ marker.setAttribute("viewBox", "");
+ is(marker.hasAttribute("viewBox"), true, "viewBox hasAttribute");
+ ok(marker.getAttribute("viewBox") === "", "empty viewBox attribute");
+
+ marker.setAttribute("viewBox", "9 10 11 12");
+ marker.removeAttribute("viewBox");
+ marker.setAttribute("viewBox", "9 10 11 12");
+ is(marker.viewBox.baseVal.x, 9, "viewBox.x baseVal after re-setting attribute to same rect value");
+ is(marker.viewBox.baseVal.y, 10, "viewBox.y baseVal after re-setting attribute to same rect value");
+ is(marker.viewBox.baseVal.width, 11, "viewBox.width baseVal after re-setting attribute to same rect value");
+ is(marker.viewBox.baseVal.height, 12, "viewBox.height baseVal after re-setting attribute to same rect value");
+ is(marker.viewBox.animVal.x, 9, "viewBox.x animVal after re-setting attribute to same rect value");
+ is(marker.viewBox.animVal.y, 10, "viewBox.y animVal after re-setting attribute to same rect value");
+ is(marker.viewBox.animVal.width, 11, "viewBox.width animVal after re-setting attribute to same rect value");
+ is(marker.viewBox.animVal.height, 12, "viewBox.height animVal after re-setting attribute to same rect value");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", runTests);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_dataTypesModEvents.html b/dom/svg/test/test_dataTypesModEvents.html
new file mode 100644
index 0000000000..e85165db44
--- /dev/null
+++ b/dom/svg/test/test_dataTypesModEvents.html
@@ -0,0 +1,257 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=629200
+-->
+<head>
+ <title>Test for Bug 629200</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/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=629200">Mozilla
+ Bug 629200</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg" src="dataTypes-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ var doc = $("svg").contentWindow.document;
+ var filter = doc.getElementById("filter");
+ var convolve = doc.getElementById("convolve");
+ var blur = doc.getElementById("blur");
+ var marker = doc.getElementById("marker");
+ var eventChecker = new MutationEventChecker;
+
+ // class attribute
+
+ eventChecker.watchAttr(filter, "class");
+ eventChecker.expect("add modify remove add");
+ filter.setAttribute("class", "foo");
+ filter.className.baseVal = "bar";
+ filter.removeAttribute("class");
+ filter.removeAttributeNS(null, "class");
+ filter.className.baseVal = "foo";
+
+ eventChecker.expect("");
+ filter.className.baseVal = "foo";
+ filter.setAttribute("class", "foo");
+
+ // length attribute
+
+ eventChecker.watchAttr(marker, "markerWidth");
+ eventChecker.expect("add modify modify modify modify modify remove add");
+ marker.setAttribute("markerWidth", "12.5");
+ marker.markerWidth.baseVal.value = 8;
+ marker.markerWidth.baseVal.valueInSpecifiedUnits = 9;
+ marker.markerWidth.baseVal.valueAsString = "10";
+ marker.markerWidth.baseVal.convertToSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_CM);
+ marker.markerWidth.baseVal.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_MM, 11);
+ marker.removeAttribute("markerWidth");
+ marker.removeAttributeNS(null, "markerWidth");
+ marker.markerWidth.baseVal.value = 8;
+
+ eventChecker.expect("remove add modify");
+ marker.removeAttribute("markerWidth");
+ console.log(marker.getAttribute("markerWidth"));
+ marker.markerWidth.baseVal.convertToSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_NUMBER);
+ console.log(marker.getAttribute("markerWidth"));
+ marker.markerWidth.baseVal.value = 8;
+
+ eventChecker.expect("");
+ marker.markerWidth.baseVal.value = 8;
+ marker.setAttribute("markerWidth", "8");
+ marker.markerWidth.baseVal.value = 8;
+ marker.markerWidth.baseVal.valueAsString = "8";
+ marker.markerWidth.baseVal.convertToSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_NUMBER);
+ marker.markerWidth.baseVal.newValueSpecifiedUnits(
+ SVGLength.SVG_LENGTHTYPE_NUMBER, 8);
+
+ // number attribute
+
+ eventChecker.watchAttr(convolve, "divisor");
+ eventChecker.expect("add modify remove add");
+ convolve.setAttribute("divisor", "12.5");
+ convolve.divisor.baseVal = 6;
+ convolve.removeAttribute("divisor");
+ convolve.removeAttributeNS(null, "divisor");
+ convolve.divisor.baseVal = 8;
+
+ eventChecker.expect("");
+ convolve.divisor.baseVal = 8;
+ convolve.setAttribute("divisor", "8");
+
+ // number-optional-number attribute
+
+ eventChecker.watchAttr(blur, "stdDeviation");
+ eventChecker.expect("add modify remove add");
+ blur.setAttribute("stdDeviation", "5, 6");
+ blur.stdDeviationX.baseVal = 8;
+ blur.removeAttribute("stdDeviation");
+ blur.removeAttributeNS(null, "stdDeviation");
+ blur.setAttribute("stdDeviation", "2, 3");
+
+ eventChecker.expect("");
+ blur.stdDeviationX.baseVal = 2;
+ blur.stdDeviationY.baseVal = 3;
+ blur.setAttribute("stdDeviation", "2, 3");
+
+ // integer attribute
+
+ eventChecker.watchAttr(convolve, "targetX");
+ eventChecker.expect("add modify remove add");
+ convolve.setAttribute("targetX", "12");
+ convolve.targetX.baseVal = 6;
+ convolve.removeAttribute("targetX");
+ convolve.removeAttributeNS(null, "targetX");
+ convolve.targetX.baseVal = 8;
+
+ // Check redundant change when comparing typed value to attribute value
+ eventChecker.expect("");
+ convolve.setAttribute("targetX", "8");
+ // Check redundant change when comparing attribute value to typed value
+ eventChecker.expect("remove add");
+ convolve.removeAttribute("targetX");
+ convolve.setAttribute("targetX", "8");
+ convolve.targetX.baseVal = 8;
+
+ // integer-optional-integer attribute
+
+ eventChecker.watchAttr(convolve, "order");
+ eventChecker.expect("add modify remove add");
+ convolve.setAttribute("order", "5, 7");
+ convolve.orderX.baseVal = 9;
+ convolve.removeAttribute("order");
+ convolve.removeAttributeNS(null, "order");
+ convolve.setAttribute("order", "9, 5");
+
+ eventChecker.expect("");
+ convolve.orderX.baseVal = 9;
+ convolve.setAttribute("order", "9, 5");
+ convolve.orderY.baseVal = 5;
+
+ // angle attribute
+
+ eventChecker.watchAttr(marker, "orient");
+ eventChecker.expect("add modify modify modify modify modify remove add");
+ marker.setAttribute("orient", "90deg");
+ marker.orientAngle.baseVal.value = 12;
+ marker.orientAngle.baseVal.valueInSpecifiedUnits = 23;
+ marker.orientAngle.baseVal.valueAsString = "34";
+ marker.orientAngle.baseVal.newValueSpecifiedUnits(
+ SVGAngle.SVG_ANGLETYPE_GRAD, 34);
+ marker.orientAngle.baseVal.newValueSpecifiedUnits(
+ SVGAngle.SVG_ANGLETYPE_GRAD, 45);
+ marker.removeAttribute("orient");
+ marker.removeAttributeNS(null, "orient");
+ marker.orientAngle.baseVal.value = 40;
+
+ eventChecker.expect("");
+ marker.orientAngle.baseVal.value = 40;
+ marker.orientAngle.baseVal.valueInSpecifiedUnits = 40;
+ marker.orientAngle.baseVal.valueAsString = "40";
+ marker.orientAngle.baseVal.convertToSpecifiedUnits(
+ SVGAngle.SVG_ANGLETYPE_UNSPECIFIED);
+ marker.orientAngle.baseVal.newValueSpecifiedUnits(
+ SVGAngle.SVG_ANGLETYPE_UNSPECIFIED, 40);
+
+ // boolean attribute
+
+ eventChecker.watchAttr(convolve, "preserveAlpha");
+ eventChecker.expect("add modify remove add");
+ convolve.setAttribute("preserveAlpha", "true");
+ convolve.preserveAlpha.baseVal = false;
+ convolve.removeAttribute("preserveAlpha");
+ convolve.removeAttributeNS(null, "preserveAlpha");
+ convolve.preserveAlpha.baseVal = true;
+
+ eventChecker.expect("");
+ convolve.preserveAlpha.baseVal = true;
+ convolve.setAttribute("preserveAlpha", "true");
+
+ // enum attribute
+
+ eventChecker.watchAttr(convolve, "edgeMode");
+ eventChecker.expect("add modify remove add");
+ convolve.setAttribute("edgeMode", "none");
+ convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_WRAP;
+ convolve.removeAttribute("edgeMode");
+ convolve.removeAttributeNS(null, "edgeMode");
+ convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
+
+ eventChecker.expect("");
+ convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
+ convolve.setAttribute("edgeMode", "none");
+ convolve.edgeMode.baseVal = SVGFEConvolveMatrixElement.SVG_EDGEMODE_NONE;
+
+ // string attribute
+
+ eventChecker.watchAttr(convolve, "result");
+ eventChecker.expect("add modify remove add");
+ convolve.setAttribute("result", "bar");
+ convolve.result.baseVal = "foo";
+ convolve.removeAttribute("result");
+ convolve.removeAttributeNS(null, "result");
+ convolve.result.baseVal = "bar";
+
+ eventChecker.expect("");
+ convolve.result.baseVal = "bar";
+ convolve.setAttribute("result", "bar");
+ convolve.result.baseVal = "bar";
+
+ // preserveAspectRatio attribute
+
+ eventChecker.watchAttr(marker, "preserveAspectRatio");
+ eventChecker.expect("add modify remove add");
+ marker.setAttribute("preserveAspectRatio", "xMaxYMid slice");
+ marker.preserveAspectRatio.baseVal.align =
+ SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMAXYMAX;
+ marker.removeAttribute("preserveAspectRatio");
+ marker.removeAttributeNS(null, "preserveAspectRatio");
+ marker.preserveAspectRatio.baseVal.align =
+ SVGPreserveAspectRatio.SVG_PRESERVEASPECTRATIO_XMIDYMIN;
+
+ eventChecker.expect("");
+ marker.preserveAspectRatio.baseVal.meetOrSlice =
+ SVGPreserveAspectRatio.SVG_MEETORSLICE_MEET;
+ marker.setAttribute("preserveAspectRatio", "xMidYMin meet");
+
+ // viewBox attribute
+
+ eventChecker.watchAttr(marker, "viewBox");
+ eventChecker.expect("add modify remove add");
+ marker.setAttribute("viewBox", "1 2 3 4");
+ marker.viewBox.baseVal.height = 5;
+ marker.removeAttribute("viewBox");
+ marker.removeAttributeNS(null, "viewBox");
+ marker.setAttribute("viewBox", "none");
+ marker.setAttribute("viewBox", "none");
+
+ eventChecker.ignoreEvents();
+ marker.setAttribute("viewBox", "1 2 3 4");
+ eventChecker.expect("");
+ marker.viewBox.baseVal.height = 4;
+ marker.viewBox.baseVal.x = 1;
+ marker.setAttribute("viewBox", "1 2 3 4");
+
+ eventChecker.finish();
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", runTests);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_fragments.html b/dom/svg/test/test_fragments.html
new file mode 100644
index 0000000000..b12833e899
--- /dev/null
+++ b/dom/svg/test/test_fragments.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=759124
+-->
+<head>
+ <title>Test for Bug 759124</title>
+ <script 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=759124">Mozilla Bug 759124</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+var svg = $("svg");
+
+SimpleTest.waitForExplicitFinish();
+
+function Test(svgFragmentIdentifier, viewBoxString,
+ preserveAspectRatioString, zoomAndPanString) {
+ this.svgFragmentIdentifier = svgFragmentIdentifier;
+}
+
+function runTests() {
+ var doc = svg.contentWindow.document;
+ var rootElement = doc.documentElement;
+
+ var tests = [
+ new Test("svgView(viewBox(0,0,200,200))"),
+ new Test("svgView(preserveAspectRatio(xMaxYMin slice))"),
+ new Test("svgView(viewBox(1,2,3,4);preserveAspectRatio(xMinYMax))"),
+ new Test("svgView(viewBox(none))"),
+ new Test("svgView(zoomAndPan(disable))"),
+ new Test("svgView(transform(translate(-10,-20) scale(2) rotate(45) translate(5,10)))"),
+ ];
+
+ var src = svg.getAttribute("src");
+
+ is(false, rootElement.hasAttribute("viewBox"),
+ "expecting to start without a viewBox set");
+ is(false, rootElement.hasAttribute("preserveAspectRatio"),
+ "expecting to start without preserveAspectRatio set");
+ is(false, rootElement.hasAttribute("zoomAndPan"),
+ "expecting to start without zoomAndPan set");
+
+ for (var j = 0; j < 2; j++) {
+ var initialViewBox = rootElement.getAttribute("viewBox");
+ var initialPreserveAspectRatio =
+ rootElement.getAttribute("preserveAspectRatio");
+ var initialZoomAndPan = rootElement.getAttribute("zoomAndPan");
+ var initialTransform = rootElement.getAttribute("transform");
+
+ for (var i = 0; i < tests.length; i++) {
+ var test = tests[i];
+ svg.setAttribute("src", src + "#" + test.svgFragmentIdentifier);
+
+ // check that assigning a viewSpec does not modify the underlying
+ // attribute values.
+ is(rootElement.getAttribute("viewBox"),
+ initialViewBox, "unexpected viewBox");
+
+ is(rootElement.getAttribute("preserveAspectRatio"),
+ initialPreserveAspectRatio, "unexpected preserveAspectRatio");
+
+ is(rootElement.getAttribute("zoomAndPan"),
+ initialZoomAndPan, "unexpected zoomAndPan");
+
+ is(rootElement.getAttribute("transform"),
+ initialTransform, "unexpected transform");
+ }
+
+ // repeat tests with underlying attributes set to values
+ rootElement.setAttribute("viewBox", "0 0 100 100");
+ rootElement.setAttribute("preserveAspectRatio", "none");
+ rootElement.setAttribute("zoomAndPan", "disable");
+ rootElement.setAttribute("transform", "translate(10,10)");
+ }
+
+ SimpleTest.finish();
+}
+
+svg.addEventListener("load", runTests);
+svg.setAttribute("src", "fragments-helper.svg");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_getBBox-method.html b/dom/svg/test/test_getBBox-method.html
new file mode 100644
index 0000000000..bad8f1ffed
--- /dev/null
+++ b/dom/svg/test/test_getBBox-method.html
@@ -0,0 +1,248 @@
+<!DOCTYPE HTML>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=999964
+-->
+<head>
+ <meta charset="utf-8"/>
+ <title>Test case for Bug 999964</title>
+ <script 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=999964">Mozilla Bug 999964</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg" src="getBBox-method-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+ /** Test case for Bug 999964 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var flag = SpecialPowers.getBoolPref("svg.new-getBBox.enabled");
+ if (!flag) {
+ ok(!flag, "skip test for bug999964.");
+ SimpleTest.finish();
+ return;
+ }
+
+ var doc = $("svg").contentDocument;
+
+ function isFuzzy(a, b, error, name) {
+ ok(!(Math.abs(a - b) > error), name, "got " + a + ", expected " + b + " (within " + error + ")");
+ }
+
+ function getBBox(id, opt) {
+ return doc.getElementById(id).getBBox(opt);
+ }
+
+ function checkBBox(id, opt, x, y, width, height, error) {
+ var bbox = getBBox(id, opt);
+ isFuzzy(bbox.x, x, error, id + ".getBBox().x");
+ isFuzzy(bbox.y, y, error, id + ".getBBox().y");
+ isFuzzy(bbox.width, width, error, id + ".getBBox().width");
+ isFuzzy(bbox.height, height, error, id + ".getBBox().height");
+ }
+
+ function compareBBox1(id1, id2) {
+ var bbox1 = getBBox(id1);
+ var bbox2 = getBBox(id2);
+ is(bbox1.x, bbox2.x, id1 + ".getBBox().x");
+ is(bbox1.y, bbox2.y, id1 + ".getBBox().y");
+ isFuzzy(bbox1.width, bbox2.width, 0.0002, id1 + ".getBBox().width");
+ isFuzzy(bbox1.height, bbox2.height, 0.0001, id1 + ".getBBox().height");
+ }
+
+ function compareBBox2(id1, id2) {
+ // without 'x'
+ var bbox1 = getBBox(id1);
+ var bbox2 = getBBox(id2);
+ is(bbox1.y, bbox2.y, id1 + ".getBBox().y");
+ isFuzzy(bbox1.width, bbox2.width, 0.0002, id1 + ".getBBox().width");
+ isFuzzy(bbox1.height, bbox2.height, 0.0001, id1 + ".getBBox().height");
+ }
+
+ var opt = { fill: true, stroke: true, markers: true, clipped: true };
+
+ // <text>
+ // fill
+ opt = { fill: true, stroke: false, markers: false, clipped: false };
+ compareBBox1("text1", "text3");
+ compareBBox1("text2", "text4");
+ compareBBox1("text5", "text6");
+ // all
+ opt = { fill: true, stroke: true, markers: true, clipped: true };
+ compareBBox2("text1", "text3");
+ compareBBox2("text2", "text4");
+ compareBBox2("text5", "text6");
+ // clipped
+ opt = { fill: false, stroke: false, markers: false, clipped: true };
+ compareBBox2("text1", "text3");
+ compareBBox2("text2", "text4");
+ compareBBox2("text5", "text6");
+
+ // <image>
+ opt = { fill: true, stroke: true, markers: true, clipped: true };
+ checkBBox("image1", opt, 250, 250, 100, 100);
+ checkBBox("image2", opt, 53, 53, 149, 149);
+ checkBBox("image3", opt, 205, 53, 148, 149);
+ checkBBox("image4", opt, 53, 205, 149, 148);
+ checkBBox("image5", opt, 205, 205, 148, 148);
+ checkBBox("image6", opt, 52, 52, 100, 100);
+ checkBBox("image7", opt, 255, 52, 100, 100);
+ checkBBox("image8", opt, 52, 255, 100, 100);
+ checkBBox("image9", opt, 255, 255, 100, 100);
+ checkBBox("image10", opt, 200, 200, 200, 200);
+ checkBBox("image11", opt, 0, 0, 0, 0);
+ checkBBox("image12", opt, 43, 43, 714, 660);
+ checkBBox("image13", opt, 50, 50, 300, 300);
+ checkBBox("image14", opt, 0, 0, 0, 0);
+
+ opt = { fill: true, stroke: false, markers: false, clipped: false };
+ checkBBox("image1", opt, 150, 150, 200, 200, 0);
+ checkBBox("image2", opt, 2, 2, 200, 200, 0);
+ checkBBox("image3", opt, 205, 2, 200, 200, 0);
+ checkBBox("image4", opt, 2, 205, 200, 200, 0);
+ checkBBox("image5", opt, 205, 205, 200, 200, 0);
+ checkBBox("image6", opt, 2, 2, 200, 200, 0);
+ checkBBox("image7", opt, 205, 2, 200, 200, 0);
+ checkBBox("image8", opt, 2, 205, 200, 200, 0);
+ checkBBox("image9", opt, 205, 205, 200, 200, 0);
+ checkBBox("image10", opt, 0, 0, 400, 400, 0);
+ checkBBox("image11", opt, 0, 0, 400, 400, 0);
+ checkBBox("image12", opt, 25, 43, 768, 768, 0);
+ checkBBox("image13", opt, 0, 0, 400, 400, 0);
+
+ // <path>
+ opt = { fill: true, stroke: true, markers: true, clipped: true };
+ checkBBox("path1", opt, 2, 17, 120, 95, 0);
+ checkBBox("path2", opt, 156, 21, 116, 91, 0);
+ checkBBox("path3", opt, 6, 121, 116, 91, 0);
+ checkBBox("path4", opt, 2, 17, 98, 83, 0);
+ checkBBox("path5", opt, 156, 21, 44, 79, 0);
+ checkBBox("path6", opt, 6, 150, 94, 62, 0);
+ checkBBox("path7", opt, 2, 17, 98, 83, 0);
+ checkBBox("path8", opt, 156, 21, 94, 79, 0);
+ checkBBox("path9", opt, 6, 121, 94, 79, 0);
+ checkBBox("path10", opt, 10, 25, 100, 75, 0);
+ checkBBox("path11", opt, 160, 25, 100, 75, 0);
+ checkBBox("path12", opt, 10, 125, 100, 75, 0);
+
+ opt = { fill: true, stroke: false, markers: false, clipped: true };
+ checkBBox("path1", opt, 10, 25, 100, 75, 0);
+ checkBBox("path2", opt, 160, 25, 100, 75, 0);
+ checkBBox("path3", opt, 10, 125, 100, 75, 0);
+ checkBBox("path4", opt, 10, 25, 90, 75, 0);
+ checkBBox("path5", opt, 160, 25, 40, 75, 0);
+ checkBBox("path6", opt, 10, 150, 90, 50, 0);
+ checkBBox("path7", opt, 10, 25, 90, 75, 0);
+ checkBBox("path8", opt, 160, 25, 90, 75, 0);
+ checkBBox("path9", opt, 10, 125, 90, 75, 0);
+ checkBBox("path10", opt, 10, 25, 100, 75, 0);
+ checkBBox("path11", opt, 160, 25, 100, 75, 0);
+ checkBBox("path12", opt, 10, 125, 100, 75, 0);
+
+ opt = { fill: true, stroke: false, markers: false, clipped: false };
+ checkBBox("path1", opt, 10, 25, 100, 75, 0);
+ checkBBox("path2", opt, 160, 25, 100, 75, 0);
+ checkBBox("path3", opt, 10, 125, 100, 75, 0);
+ checkBBox("path4", opt, 10, 25, 100, 75, 0);
+ checkBBox("path5", opt, 160, 25, 100, 75, 0);
+ checkBBox("path6", opt, 10, 125, 100, 75, 0);
+ checkBBox("path7", opt, 10, 25, 100, 75, 0);
+ checkBBox("path8", opt, 160, 25, 100, 75, 0);
+ checkBBox("path9", opt, 10, 125, 100, 75, 0);
+ checkBBox("path10", opt, 10, 25, 100, 75, 0);
+ checkBBox("path11", opt, 160, 25, 100, 75, 0);
+ checkBBox("path12", opt, 10, 125, 100, 75, 0);
+ checkBBox("path13", opt, 0, 0, 100, 100, 0);
+
+ opt = { fill: false, stroke: true, markers: false, clipped: false };
+ checkBBox("path1", opt, 2, 17, 116, 91, 0);
+ checkBBox("path2", opt, 156, 21, 108, 83, 0);
+ checkBBox("path3", opt, 6, 121, 108, 83, 0);
+ checkBBox("path4", opt, 2, 17, 116, 91, 0);
+ checkBBox("path5", opt, 156, 21, 108, 83, 0);
+ checkBBox("path6", opt, 6, 121, 108, 83, 0);
+ checkBBox("path7", opt, 2, 17, 116, 91, 0);
+ checkBBox("path8", opt, 156, 21, 108, 83, 0);
+ checkBBox("path9", opt, 6, 121, 108, 83, 0);
+ checkBBox("path10", opt, 2, 17, 116, 91, 0);
+ checkBBox("path11", opt, 156, 21, 108, 83, 0);
+ checkBBox("path12", opt, 6, 121, 108, 83, 0);
+
+ opt = { fill: false, stroke: false, markers: true, clipped: false };
+ checkBBox("path1", opt, 10, 25, 112, 87, 0);
+ checkBBox("path2", opt, 160, 25, 112, 87, 0);
+ checkBBox("path3", opt, 10, 125, 112, 87, 0);
+ checkBBox("path4", opt, 10, 25, 112, 87, 0);
+ checkBBox("path5", opt, 160, 25, 112, 87, 0);
+ checkBBox("path6", opt, 10, 125, 112, 87, 0);
+ checkBBox("path7", opt, 10, 25, 112, 87, 0);
+ checkBBox("path8", opt, 160, 25, 112, 87, 0);
+ checkBBox("path9", opt, 10, 125, 112, 87, 0);
+ checkBBox("path10", opt, 10, 25, 112, 87, 0);
+ checkBBox("path11", opt, 160, 25, 112, 87, 0);
+ checkBBox("path12", opt, 10, 125, 112, 87, 0);
+
+ // <use>
+ opt = { fill: true, stroke: false, markers: false, clipped: false };
+ checkBBox("use1", opt, 70, 70, 180, 180, 0);
+ checkBBox("use2", opt, 250, 70, 180, 180, 0);
+ checkBBox("use3", opt, 70, 250, 180, 180, 0);
+ checkBBox("use4", opt, 22, 22, 180, 180, 0);
+ checkBBox("use5", opt, 225, 22, 180, 180, 0);
+ checkBBox("use6", opt, 22, 225, 180, 180, 0);
+ checkBBox("use7", opt, 225, 225, 180, 180, 0);
+
+ opt = { fill: true, stroke: true, markers: true, clipped: true };
+ checkBBox("use1", opt, 70, 66, 180, 94, 0);
+ checkBBox("use2", opt, 250, 70, 180, 90, 0);
+ checkBBox("use3", opt, 70, 250, 180, 90, 0);
+ checkBBox("use4", opt, 18, 18, 134, 134, 0);
+ checkBBox("use5", opt, 221, 18, 134, 134, 0);
+ checkBBox("use6", opt, 18, 221, 134, 134, 0);
+ checkBBox("use7", opt, 221, 221, 134, 134, 0);
+ checkBBox("use8", opt, 0, 0, 0, 0, 0);
+
+ // <foreignObject>
+ opt = { fill: true, stroke: false, markers: false, clipped: false };
+ checkBBox("fo1", opt, 2, 2, 200, 200, 0);
+ checkBBox("fo2", opt, 205, 2, 200, 200, 0);
+ checkBBox("fo3", opt, 2, 205, 200, 200, 0);
+ checkBBox("fo4", opt, 205, 205, 200, 200, 0);
+ checkBBox("fo5", opt, 250, 250, 200, 200, 0);
+ checkBBox("fo6", opt, 0, 0, 200, 200, 0);
+ checkBBox("fo7", opt, 0, 0, 200, 200, 0);
+
+ opt = { fill: true, stroke: true, markers: true, clipped: true };
+ checkBBox("fo1", opt, 53, 53, 51, 51, 0);
+ checkBBox("fo2", opt, 205, 53, 148, 149, 0);
+ checkBBox("fo3", opt, 53, 205, 149, 148, 0);
+ checkBBox("fo4", opt, 207, 207, 100, 100, 0);
+ checkBBox("fo5", opt, 0, 0, 0, 0, 0);
+ checkBBox("fo6", opt, 100, 100, 100, 100, 0);
+ checkBBox("fo7", opt, 10, 10, 180, 180, 0);
+ checkBBox("fo8", opt, 0, 0, 0, 0, 0);
+
+ // from http://www.w3.org/Graphics/SVG/Test/20110816/harness/htmlObjectApproved/masking-path-07-b.html
+ opt = { fill: true, stroke: true, markers: true, clipped: true };
+ checkBBox("rect-1", opt, 10, 10, 140, 140, 0);
+ checkBBox("rect-2", opt, 50, 30, 25, 100, 0);
+ checkBBox("rect-3", opt, 50, 50, 100, 100, 0);
+ checkBBox("g1", opt, 50, 50, 100, 100, 0);
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_getCTM.html b/dom/svg/test/test_getCTM.html
new file mode 100644
index 0000000000..860b660867
--- /dev/null
+++ b/dom/svg/test/test_getCTM.html
@@ -0,0 +1,124 @@
+<!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 src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>
+ #padsvg1 { padding-left: 27px; padding-top: 43px; }
+ #transrect1 { transform: scale(2,3); }
+ </style>
+</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>
+
+<svg id="padsvg1" width="100" height="100">
+ <rect id="transrect1" width="10" height="10" />
+</svg>
+
+<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 transrect1 = document.getElementById("transrect1");
+ is(transrect1.getCTM().a, 2, "transrect1.getCTM().a");
+ is(transrect1.getCTM().d, 3, "transrect1.getCTM().d");
+
+ var padsvg1 = document.getElementById("padsvg1");
+ is(padsvg1.getScreenCTM().e - padsvg1.getBoundingClientRect().x, 27, "padsvg1.getScreenCTM().e");
+ is(padsvg1.getScreenCTM().f - padsvg1.getBoundingClientRect().y, 43, "padsvg1.getScreenCTM().f");
+
+ var root = doc.documentElement;
+ var inner = doc.getElementById("inner");
+ var g1 = doc.getElementById("g1");
+ var outer = doc.getElementById("outer");
+ var outer2 = doc.getElementById("outer2");
+ var g2 = doc.getElementById("g2");
+ var g3 = doc.getElementById("g3");
+ var g4 = doc.getElementById("g4");
+ var g5 = doc.getElementById("g5");
+ var symbolRect = doc.getElementById("symbolRect");
+ var fO = doc.getElementById("fO");
+ /* 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; } })(), 1, "inner.getCTM().e");
+ is((function() { try { return inner.getCTM().f; } catch (e) { return e; } })(), 2, "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()");
+ // g4.nearestViewportElement == null
+ is((function() { try { return g4.getCTM().e; } catch (e) { return e; } })(), 1, "g4.getCTM().e");
+ is((function() { try { return g4.getCTM().f; } catch (e) { return e; } })(), 2, "g4.getCTM().f");
+ // 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");
+ // fO.nearestViewportElement == <svg> with no 'id'
+ is((function() { try { return fO.getCTM().e; } catch (e) { return e; } })(), 2, "fO.getCTM().e");
+ is((function() { try { return fO.getCTM().f; } catch (e) { return e; } })(), 3, "fO.getCTM().f");
+ // g5.nearestViewportElement == inner-2
+ is((function() { try { return g5.getCTM(); } catch (e) { return e; } })(), null, "g5.getCTM()");
+
+ /* 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; } })(), 15, "inner.getScreenCTM().e");
+ is((function() { try { return inner.getScreenCTM().f; } catch (e) { return e; } })(), 28, "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");
+ // outer.farthestViewportElement == null (but actually == root)
+ is((function() { try { return outer2.getScreenCTM().e; } catch (e) { return e; } })(), -19, "outer2.getScreenCTM().e");
+ is((function() { try { return outer2.getScreenCTM().f; } catch (e) { return e; } })(), -8, "outer2.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");
+ // fO.farthestViewportElement == root
+ is((function() { try { return fO.getScreenCTM().e; } catch (e) { return e; } })(), 16, "symbolRect.getScreenCTM().e");
+ is((function() { try { return fO.getScreenCTM().f; } catch (e) { return e; } })(), 29, "symbolRect.getScreenCTM().f");
+ // g5.farthestViewportElement == root
+ is((function() { try { return g5.getScreenCTM(); } catch (e) { return e; } })(), null, "g5.getScreenCTM()");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", runTest);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_getElementById.xhtml b/dom/svg/test/test_getElementById.xhtml
new file mode 100644
index 0000000000..82050bea55
--- /dev/null
+++ b/dom/svg/test/test_getElementById.xhtml
@@ -0,0 +1,65 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Test getElementById behaviour</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="matrixUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="1" id="svg">
+ <!-- decoy element, same Id but not a <g> -->
+ <rect id="g"/>
+ <svg id="inner">
+ <!-- the one we want to find -->
+ <g id="g"/>
+ <!-- check we don't get confused by CSS selectors -->
+ <g id="foo bar"/>
+ <g id="goo > car"/>
+ <g id="hoo~dar"/>
+ <g id="ioo+ear"/>
+ </svg>
+ <g id="g2"/>
+ </svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function main() {
+ var svgns = "http://www.w3.org/2000/svg";
+
+ var svg = document.getElementById("inner");
+
+ is(svg.getElementById("g").nodeName, "g", "expected to find g element child");
+ is(svg.getElementById("foo bar").nodeName, "g", "expected to find foo bar element child");
+ is(svg.getElementById("goo > car").nodeName, "g", "expected to find goo > car element child");
+ is(svg.getElementById("hoo~dar").nodeName, "g", "expected to find hoo~dar element child");
+ is(svg.getElementById("ioo+ear").nodeName, "g", "expected to find ioo+ear element child");
+
+ is(svg.getElementById("g2"), null, "did not expect to find an element with id g2");
+
+ // no element with Id = "g3" in the document at all
+ is(svg.getElementById("g3"), null, "did not expect to find an element with id g3");
+
+ svg = document.createElementNS(svgns, "svg");
+
+ var c = document.createElementNS(svgns, "circle");
+ c.setAttribute("id", "c");
+ svg.appendChild(c);
+
+ is(svg.getElementById("c").nodeName, "circle", "expected to find circle element child");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", main);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_getPathSegListAtLength_with_d_property.html b/dom/svg/test/test_getPathSegListAtLength_with_d_property.html
new file mode 100644
index 0000000000..c16ad71adf
--- /dev/null
+++ b/dom/svg/test/test_getPathSegListAtLength_with_d_property.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<title>Test for getPathSegAtLength for d property</title>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+svg {
+ width: 10%;
+ height: 10%;
+ background: #eee;
+}
+svg path {
+ fill: none;
+ stroke: #000;
+}
+</style>
+
+<div id="log"></div>
+
+<svg viewBox="0 0 20 10">
+ <path id='target1' d="M2,2 L8,8 L12,4"/>
+</svg>
+<svg viewBox="0 0 20 10">
+ <path id='target2' style='d: path("M2,2 L8,8 L12,4")' />
+</svg>
+
+<script>
+/* global test, assert_equals */
+
+'use strict';
+
+test(function() {
+ let target1 = document.getElementById('target1');
+ let target2 = document.getElementById('target2');
+ assert_equals(target1.getPathSegAtLength(5), 1);
+ assert_equals(target2.getPathSegAtLength(5), 1);
+
+ assert_equals(target1.getPathSegAtLength(10), 2);
+ assert_equals(target2.getPathSegAtLength(10), 2);
+}, "getPathSegAtLength works properly on both d attribute and d property");
+
+test(function() {
+ let target = document.getElementById('target1');
+ // Note: in order to make sure we flush style properly, we call
+ // getComputedStyle after setting an initial value first, so if
+ // getPathSegAtLength(5) doesn't flush style, it returns 0.
+ target.style.d = 'none';
+ assert_equals(getComputedStyle(target).d, "none");
+
+ target.style.d = 'path("M2,2 h3 v5")';
+ assert_equals(target.getPathSegAtLength(5), 2);
+}, "getPathSegAtLength works properly after updating d property");
+
+</script>
diff --git a/dom/svg/test/test_getSubStringLength.xhtml b/dom/svg/test/test_getSubStringLength.xhtml
new file mode 100644
index 0000000000..1d9784e003
--- /dev/null
+++ b/dom/svg/test/test_getSubStringLength.xhtml
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=420243
+-->
+<head>
+ <title>Test for Bug 420243</title>
+ <script 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=420243">Mozilla Bug 420243</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg" src="getSubStringLength-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function runTests(text, charWidth) {
+ function chars(n) { return charWidth * n; }
+
+ function expectThrow(charnum, nchars) {
+ try {
+ text.getSubStringLength(charnum, nchars);
+ ok(false,
+ "text.getSubStringLength(" + charnum + "," + nchars + ") " +
+ "should have thrown");
+ } catch (e) {
+ is(e.name, "IndexSizeError",
+ "expected an index error for " +
+ "text.getSubStringLength(" + charnum + "," + nchars + ")");
+ is(e.code, DOMException.INDEX_SIZE_ERR,
+ "expected an index error for " +
+ "text.getSubStringLength(" + charnum + "," + nchars + ")");
+ }
+ }
+
+ function expectValue(charnum, nchars, expected) {
+ try {
+ isfuzzy(text.getSubStringLength(charnum, nchars), expected, 0.01,
+ "text.getSubStringLength(" + charnum + "," + nchars + ") " +
+ "returned wrong value");
+ } catch (e) {
+ ok(false,
+ "unexpected exception for " +
+ "text.getSubStringLength(" + charnum + "," + nchars + ")");
+ }
+ }
+
+ expectThrow(100, 2);
+ expectThrow(100, 0);
+ expectThrow(3, 0);
+ expectThrow(3, 1);
+
+ expectValue(1, 3, chars(2));
+ expectValue(0, 4, chars(3));
+ expectValue(0, 0, chars(0));
+ expectValue(1, 0, chars(0));
+ expectValue(2, 0, chars(0));
+ expectValue(0, 1, chars(1));
+ expectValue(1, 1, chars(1));
+ expectValue(2, 1, chars(1));
+ expectValue(0, 2, chars(2));
+ expectValue(1, 2, chars(2));
+ expectValue(0, 3, chars(3));
+ expectValue(1, 100, chars(2));
+ expectValue(2, 100, chars(1));
+}
+
+
+function run() {
+ try {
+ var document = $("svg").contentWindow.document;
+ var text = document.getElementById("text");
+
+ runTests(text, text.getSubStringLength(0, 1));
+ } catch (e) {
+ ok(false, "threw error: " + e);
+ }
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_getTotalLength.xhtml b/dom/svg/test/test_getTotalLength.xhtml
new file mode 100644
index 0000000000..0809f40ca1
--- /dev/null
+++ b/dom/svg/test/test_getTotalLength.xhtml
@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1474284
+-->
+<head>
+ <title>Test for Bug 1474284</title>
+ <script 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=1474284">Mozilla Bug 1474284</a>
+<p id="display"></p>
+
+<svg xmlns="http://www.w3.org/2000/svg">
+ <path id="path1" stroke="#000" fill="none"
+ d="M 50,40
+ C 50,40 0,60 30,20"/>
+ <symbol font-size="10" width="20em" height="20em">
+ <rect id="r1" x="5em" y="6em" width="20%" height="30%" />
+ </symbol>
+</svg>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ isfuzzy(document.getElementById("path1").getTotalLength(),
+ 55.19, 0.02,
+ 'getTotalLength() on element id="path1" returned the wrong value');
+
+ let r1 = document.getElementById("r1");
+ is(r1.getTotalLength(), 200, "getTotalLength() should work for non-rendered element");
+
+ let r2 = document.createElementNS("http://www.w3.org/2000/svg", "rect");
+ r2.setAttribute("width", 200);
+ r2.setAttribute("height", 300);
+ is(r2.getTotalLength(), 1000, "getTotalLength() should work for a rect element not in the document");
+
+ let c = document.createElementNS("http://www.w3.org/2000/svg", "circle");
+ c.setAttribute("r", 200);
+ isfuzzy(c.getTotalLength(), 2 * Math.PI * 200, 0.2, "getTotalLength() should work for a circle element not in the document");
+
+ let e = document.createElementNS("http://www.w3.org/2000/svg", "ellipse");
+ e.setAttribute("rx", 200);
+ e.setAttribute("ry", 200);
+ isfuzzy(e.getTotalLength(), 2 * Math.PI * 200, 0.2, "getTotalLength() should work for an ellipse element not in the document");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_hit-testing-and-viewbox.xhtml b/dom/svg/test/test_hit-testing-and-viewbox.xhtml
new file mode 100644
index 0000000000..6ab83e54be
--- /dev/null
+++ b/dom/svg/test/test_hit-testing-and-viewbox.xhtml
@@ -0,0 +1,81 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1486952
+-->
+<head>
+ <title>Test that hit-testing works after a viewBox update</title>
+
+ <style>
+ :hover { fill: lime; }
+ </style>
+
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+
+<body onload="run()">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ const div = $("div");
+ const offsetX = div.offsetLeft;
+ const offsetY = div.offsetTop;
+ const outerRect = $("outerRect");
+ const innerRect = $("innerRect");
+ const outerSVG = $("outerSVG");
+ const innerSVG = $("innerSVG");
+ let got;
+
+ // Update the inner SVG viewBox to "move" the inner rectangle off its current
+ // position on screen:
+ innerSVG.setAttribute("viewBox", "-25 0 50 50");
+ got = document.elementFromPoint(offsetX + 150, offsetY + 25);
+ is(got, innerRect, "Should hit inner rectangle (1)");
+
+ // Update the inner SVG viewBox again. (At the time of writing, a reflow is
+ // triggered the first time you change viewBox on an inner svg, so the
+ // previous test is expected to always pass. This next test will fail if we're
+ // updating overflows on the inner svg frame instead of its children).
+ innerSVG.setAttribute("viewBox", "0 -25 50 50");
+ got = document.elementFromPoint(offsetX + 100, offsetY + 75);
+ is(got, innerRect, "Should hit inner rectangle (2)");
+
+ // Now update the outer SVG viewBox and confirm that both rectangles are
+ // registered. (Note that in this case the overflow rectangle of the inner
+ // svg is the inner svg's viewport, so be sure to "move" the outer svg so that
+ // the "new" outer rectangle and inner svg are off the current outerRect
+ // union inner svg viewport - hit testing still works in that union.)
+ outerSVG.setAttribute("viewBox", "-200 0 400 100");
+ // Outer:
+ got = document.elementFromPoint(offsetX + 250, offsetY + 50);
+ is(got, outerRect, "Should hit outer rectangle");
+ // Inner:
+ got = document.elementFromPoint(offsetX + 300, offsetY + 75);
+ is(got, innerRect, "Should hit inner rectangle (3)");
+
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1486952">Mozilla Bug 1486952</a>
+<p id="display"></p>
+<div id="content">
+
+ <div width="100%" height="1" id="div"></div>
+ <svg xmlns="http://www.w3.org/2000/svg" id="outerSVG" width="400" height="100"
+ viewBox="0 0 400 100">
+ <rect x="25" y="25" width="50" height="50" fill="red" id="outerRect" />
+ <svg x="75" width="100" height="100" viewBox="0 0 50 50" id="innerSVG">
+ <rect width="25" height="25" fill="red" id="innerRect" />
+ </svg>
+ </svg>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_lang.xhtml b/dom/svg/test/test_lang.xhtml
new file mode 100644
index 0000000000..df11660df2
--- /dev/null
+++ b/dom/svg/test/test_lang.xhtml
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=721920
+-->
+<head>
+ <title>Test for Bug 721920</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style type="text/css">
+
+svg text { word-spacing: 1em; }
+
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=721920">Mozilla Bug 721920</a>
+<p id="display">
+ <svg xmlns="http://www.w3.org/2000/svg" width="400" height="300">
+ <g lang="zh-Hans">
+ <text id="s0" y="40" style="font-size: 0">汉字</text>
+ <text id="s4" y="80" style="font-size: 4px">汉字</text>
+ <text id="s12" y="120" style="font-size: 12px">汉字</text>
+ <text id="s28" y="160" style="font-size: 28px">汉字</text>
+ </g>
+ </svg>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+//<![CDATA[
+
+/** Test for Bug 721920 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var elts = [
+ document.getElementById("s0"),
+ document.getElementById("s4"),
+ document.getElementById("s12"),
+ document.getElementById("s28"),
+];
+
+function fs(idx) {
+ // The computed font size actually *doesn't* currently reflect the
+ // minimum font size preference, but things in em units do. Hence
+ // why we use word-spacing here.
+ // test_bug401046.html uses margin-bottom instead, but there's an
+ // SVG bug that prevents that working in SVG (bug 728723).
+ return getComputedStyle(elts[idx], "").wordSpacing;
+}
+
+SpecialPowers.pushPrefEnv({"clear": [["font.minimum-size.zh-CN"]]}, step1);
+
+function step1() {
+ is(fs(0), "0px", "at min font size 0, 0px should compute to 0px");
+ is(fs(1), "4px", "at min font size 0, 4px should compute to 4px");
+ is(fs(2), "12px", "at min font size 0, 12px should compute to 12px");
+ is(fs(3), "28px", "at min font size 0, 28px should compute to 28px");
+
+ SpecialPowers.pushPrefEnv({"set": [["font.minimum-size.zh-CN", 7]]}, step2);
+}
+
+function step2() {
+ is(fs(0), "0px", "at min font size 7, 0px should compute to 0px");
+ is(fs(1), "7px", "at min font size 7, 4px should compute to 7px");
+ is(fs(2), "12px", "at min font size 7, 12px should compute to 12px");
+ is(fs(3), "28px", "at min font size 7, 28px should compute to 28px");
+
+ SpecialPowers.pushPrefEnv({"set": [["font.minimum-size.zh-CN", 18]]}, step3);
+}
+
+function step3() {
+ is(fs(0), "0px", "at min font size 18, 0px should compute to 0px");
+ is(fs(1), "18px", "at min font size 18, 4px should compute to 18px");
+ is(fs(2), "18px", "at min font size 18, 12px should compute to 18px");
+ is(fs(3), "28px", "at min font size 18, 28px should compute to 28px");
+
+ SpecialPowers.pushPrefEnv({"clear": [["font.minimum-size.zh-CN"]]}, SimpleTest.finish);
+}
+
+//]]>
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/svg/test/test_length.xhtml b/dom/svg/test/test_length.xhtml
new file mode 100644
index 0000000000..513c65cbb2
--- /dev/null
+++ b/dom/svg/test/test_length.xhtml
@@ -0,0 +1,58 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=342513
+-->
+<head>
+ <title>Test SVG Length conversions</title>
+ <script 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=342513">Mozilla Bug 342513</a>
+<p id="display"></p>
+<div id="content">
+
+ <div width="100%" height="1" id="div">
+ </div>
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="1" id="svg">
+ </svg>
+ <svg xmlns="http://www.w3.org/2000/svg" width="600" height="400" font-size="5">
+ <svg font-size="10" width="20em" height="20em">
+ <rect id="r1" x="5em" y="6em" width="20%" height="30%" />
+ </svg>
+ </svg>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var svgDoc = document.getElementById("svg");
+ var divElem = document.getElementById("div");
+ var expectedWidth = divElem.clientWidth;
+ // test for the pixel width of the svg
+ is(svgDoc.width.baseVal.value, expectedWidth, "svgDoc.width.baseVal.value");
+
+ // set the SVG width to ~50% in pixels
+ svgDoc.width.baseVal.newValueSpecifiedUnits(svgDoc.width.baseVal.SVG_LENGTHTYPE_PX, Math.floor(expectedWidth / 2));
+ svgDoc.width.baseVal.convertToSpecifiedUnits(svgDoc.width.baseVal.SVG_LENGTHTYPE_PERCENTAGE);
+ // the valueInSpecifiedUnits should now be 50%
+ is(Math.round(svgDoc.width.baseVal.valueInSpecifiedUnits), 50, "valueInSpecifiedUnits after convertToSpecifiedUnits");
+
+ let r1 = document.getElementById("r1");
+ is(r1.width.baseVal.value, 40, "width in em for elements inside inner <svg> should be resolved against the inner <svg>");
+ is(r1.height.baseVal.value, 60, "height in em for elements inside inner <svg> should be resolved against the inner <svg>");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_lengthParsing.html b/dom/svg/test/test_lengthParsing.html
new file mode 100644
index 0000000000..8471b19855
--- /dev/null
+++ b/dom/svg/test/test_lengthParsing.html
@@ -0,0 +1,82 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=946529
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test transform parsing</title>
+ <script 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=946529">Mozilla Bug 946529</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <svg width="100%" height="1" id="svg">
+ <rect id="rect"/>
+ </svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+// Test cases
+checkParseOk("", 0);
+checkParseOk("-.1", -0.1);
+checkParseOk("1e1", 10);
+checkParseOk("1em", 1, "em");
+checkParseOk("1ex", 1, "ex");
+checkParseOk("1e1em", 10, "em");
+checkParseOk("1E+2", 100);
+checkParseOk(".1e-2", 0.001);
+checkParseOk(" 10", 10);
+checkParseOk("10 ", 10);
+checkParseOk(" 10 ", 10);
+checkParseOk(" 10em ", 10, "em");
+
+// Fail cases
+checkParseFail("1e");
+checkParseFail("1 e");
+checkParseFail("1 em");
+checkParseFail("1ee");
+checkParseFail(" 10 20");
+
+function checkParseOk(spec, valueInUnits, units) {
+ var rect = document.getElementById("rect");
+
+ // Clear previous value
+ rect.removeAttribute("x");
+ rect.setAttribute("x", spec);
+
+ // Check number part
+ const tolerance = 1 / 65535;
+ var actual = rect.x.baseVal.valueInSpecifiedUnits;
+ ok(Math.abs(actual - valueInUnits) < tolerance,
+ spec + " (value) - got " + actual + ", expected " + valueInUnits);
+
+ // Check unit part
+ var unitMapping = {
+ "unknown": SVGLength.SVG_LENGTHTYPE_UNKNOWN,
+ "": SVGLength.SVG_LENGTHTYPE_NUMBER,
+ "%": SVGLength.SVG_LENGTHTYPE_PERCENTAGE,
+ "em": SVGLength.SVG_LENGTHTYPE_EMS,
+ "ex": SVGLength.SVG_LENGTHTYPE_EXS,
+ "px": SVGLength.SVG_LENGTHTYPE_PX,
+ "cm": SVGLength.SVG_LENGTHTYPE_CM,
+ "mm": SVGLength.SVG_LENGTHTYPE_MM,
+ "in": SVGLength.SVG_LENGTHTYPE_IN,
+ "pt": SVGLength.SVG_LENGTHTYPE_PT,
+ "pc": SVGLength.SVG_LENGTHTYPE_PC,
+ };
+ if (typeof units == "undefined") {
+ units = "";
+ }
+ is(rect.x.baseVal.unitType, unitMapping[units], spec + " (unit)");
+}
+
+function checkParseFail(spec) {
+ checkParseOk(spec, 0);
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_markerOrient.xhtml b/dom/svg/test/test_markerOrient.xhtml
new file mode 100644
index 0000000000..1b91a9a62a
--- /dev/null
+++ b/dom/svg/test/test_markerOrient.xhtml
@@ -0,0 +1,110 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=892372
+-->
+<head>
+ <title>Test for Bug 892372</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 892372 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function testAutoIsSet(marker) {
+ is(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_AUTO,
+ "orientType baseVal for auto");
+ is(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_AUTO,
+ "orientType animVal for auto");
+ is(marker.orientAngle.baseVal.value, 0, "orientAngle baseVal for auto");
+ is(marker.orientAngle.animVal.value, 0, "orientAngle animVal for auto");
+ }
+
+ function testAutoStartReverseIsSet(marker) {
+ is(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_AUTO_START_REVERSE,
+ "orientType baseVal for auto-start-reverse");
+ is(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_AUTO_START_REVERSE,
+ "orientType animVal for auto-start-reverse");
+ is(marker.orientAngle.baseVal.value, 0,
+ "orientAngle baseVal for auto-start-reverse");
+ is(marker.orientAngle.animVal.value, 0,
+ "orientAngle animVal for auto-start-reverse");
+ }
+
+ function testAngleIsSet(marker, angleVal) {
+ is(marker.orientType.baseVal, SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE,
+ "orientType baseVal after numeric angle is set");
+ is(marker.orientType.animVal, SVGMarkerElement.SVG_MARKER_ORIENT_ANGLE,
+ "orientType animVal after numeric angle is set");
+ is(marker.orientAngle.baseVal.value, angleVal,
+ "orientAngle baseVal after numeric angle is set");
+ is(marker.orientAngle.animVal.value, angleVal,
+ "orientAngle animVal after numeric angle is set");
+ }
+
+ function run() {
+ var m = $("m");
+
+ // Testing two conditions:
+ // 1) If orient is set to a numeric angle and then set to auto or
+ // auto-start-reverse, orientAngle should return a value of 0
+ // 2) If orient is set to something of type other than
+ // SVG_MARKER_ORIENT_ANGLE and then set to a numeric angle,
+ // orientType should return SVG_MARKER_ORIENT_ANGLE
+
+ // default is orient="0"
+ testAngleIsSet(m, 0);
+
+ m.setOrientToAuto();
+ testAutoIsSet(m);
+
+ // testing condition 2 for an angle set using setOrientToAngle
+ var a = $("svg").createSVGAngle();
+ a.newValueSpecifiedUnits(SVGAngle.SVG_ANGLETYPE_DEG, 90);
+ m.setOrientToAngle(a);
+ testAngleIsSet(m, a.value);
+
+ // testing condition 1 for orient set using setOrientToAuto
+ m.setOrientToAuto();
+ testAutoIsSet(m);
+
+ // testing condition 2 for an angle set using setAttribute
+ m.setAttribute("orient", "180");
+ testAngleIsSet(m, 180);
+
+ // testing condition 1 for orient set to "auto" using setAttribute
+ m.setAttribute("orient", "auto");
+ testAutoIsSet(m);
+
+ m.setAttribute("orient", "270");
+
+ // testing condition 1 for orient set to "auto-start-reverse" using
+ // setAttribute
+ m.setAttribute("orient", "auto-start-reverse");
+ testAutoStartReverseIsSet(m);
+
+ SimpleTest.finish();
+ }
+
+ window.addEventListener("load", run);
+
+ ]]>
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=892372">Mozilla Bug 892372</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+ <svg xmlns="http://www.w3.org/2000/svg" id="svg">
+ <defs>
+ <marker id="m" />
+ </defs>
+ </svg>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_non-scaling-stroke.html b/dom/svg/test/test_non-scaling-stroke.html
new file mode 100644
index 0000000000..49010eed0a
--- /dev/null
+++ b/dom/svg/test/test_non-scaling-stroke.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=829085
+-->
+<head>
+ <title>Test for Bug 829085 - non-scaling-stroke hit testing</title>
+ <meta charset="utf-8"></meta>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.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=829085">Mozilla Bug 829085 - non-scaling-stroke hit testing</a>
+<p id="display"></p>
+<div id="content">
+
+ <svg id="svg" xmlns="http://www.w3.org/2000/svg" width="300" height="250">
+ <rect width="100%" height="100%" fill="none" stroke-width="4" stroke="blue"/>
+ <style>
+ line:hover { stroke: lime; }
+ </style>
+ <g transform="scale(1.3) translate(100, -80) rotate(45)">
+ <line id="line" stroke="teal" stroke-width="120px"
+ x1="50" y1="130" x2="200" y2="130"
+ vector-effect="non-scaling-stroke"></line>
+ </g>
+ </svg>
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+ function startTest() {
+ SimpleTest.waitForFocus(function() {
+ disableNonTestMouseEvents(true);
+ // Send a click
+ synthesizeMouseExpectEvent($("svg"), 170, 100, { },
+ $("line"), "click",
+ "Testing mouse event on non-scaling-stroke");
+ disableNonTestMouseEvents(false);
+ SimpleTest.finish();
+ });
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(startTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_nonAnimStrings.xhtml b/dom/svg/test/test_nonAnimStrings.xhtml
new file mode 100644
index 0000000000..5ceabed892
--- /dev/null
+++ b/dom/svg/test/test_nonAnimStrings.xhtml
@@ -0,0 +1,78 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=589436
+-->
+<head>
+ <title>Test for non-animated strings</title>
+ <script 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=589436">Mozilla Bug 589436</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<svg id="svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="120px" height="120px"
+ onload="this.pauseAnimations()">
+ <defs>
+ <path id="moveFarAway" d="M300,300 h0"/>
+ <path id="moveToUpperLeft" d="M100,100 h0"/>
+ </defs>
+ <script id="script">
+ <animate attributeName="xlink:href" from="" to="animated" dur="0.5s" begin="1s"
+ fill="freeze" id="animate"/>
+ </script>
+ <rect class="test" x="0" y="0" width="50" height="50">
+ <animateMotion begin="1" dur="1" fill="freeze">
+ <mpath id="mpath" xlink:href="#moveFarAway">
+ <animate attributeName="xlink:href" from="#moveFarAway" to="#moveToUpperLeft" dur="0.5s" begin="1s"
+ fill="freeze"/>
+ </mpath>
+ </animateMotion>
+ </rect>
+</svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+/** Test some strings are not animatable **/
+
+/* Global Variables */
+var svg = document.getElementById("svg");
+var script = document.getElementById("script");
+var mpath = document.getElementById("mpath");
+var animate = document.getElementById("animate");
+
+SimpleTest.waitForExplicitFinish();
+
+function main() {
+ ok(svg.animationsPaused(), "should be paused by <svg> load handler");
+ is(svg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler");
+
+ // Sanity check: check initial values
+ is(script.href.baseVal, "", "Unexpected initial script baseVal");
+ is(script.href.animVal, "", "Unexpected initial script animVal");
+ is(mpath.href.baseVal, "#moveFarAway", "Unexpected initial mpath baseVal");
+ is(mpath.href.animVal, "#moveFarAway", "Unexpected initial mpath animVal");
+
+ // Move to the end of the animation - should make no difference
+ svg.setCurrentTime(2);
+
+ is(script.href.baseVal, "", "Unexpected value for script baseVal after animation");
+ is(script.href.animVal, "", "Unexpected value for script animVal after animation");
+ is(mpath.href.baseVal, "#moveFarAway", "Unexpected value for mpath baseVal after animation");
+ is(mpath.href.animVal, "#moveFarAway", "Unexpected value for mpath animVal after animation");
+
+ SimpleTest.finish();
+}
+
+if (animate && animate.targetElement) {
+ window.addEventListener("load", main);
+} else {
+ ok(true); // Skip tests but don't report 'todo' either
+ SimpleTest.finish();
+}
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_object-delayed-intrinsic-size.html b/dom/svg/test/test_object-delayed-intrinsic-size.html
new file mode 100644
index 0000000000..18645892f7
--- /dev/null
+++ b/dom/svg/test/test_object-delayed-intrinsic-size.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1063073
+-->
+<html>
+ <head>
+ <title>Test that &lt;object&gt; embedding SVG and using its intrinsic
+ size will resize if the &lt;object&gt; gets a reflow before the
+ root-&lt;svg&gt; gets its nsSVGOuterSVGFrame
+ </title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script>
+
+// This test checks for a race condition. If it fails intermittently then it
+// may actually be a full failure.
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ var object = document.querySelector("object");
+ var cs = document.defaultView.getComputedStyle(object);
+ var width = cs.getPropertyValue("width");
+ is(width, "70px", "Check that the &lt;object&gt; size updated");
+ SimpleTest.finish();
+}
+
+ </script>
+ </head>
+ <body onload="runTest();">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1063073">Mozilla Bug 1063073</a>
+ <p id="display"></p>
+ <div id="content">
+ <object style="border:1px solid black" type="image/svg+xml"
+ data="object-delayed-intrinsic-size.sjs"></object>
+ </div>
+ </body>
+</html>
+
diff --git a/dom/svg/test/test_onerror.xhtml b/dom/svg/test/test_onerror.xhtml
new file mode 100644
index 0000000000..394c88c890
--- /dev/null
+++ b/dom/svg/test/test_onerror.xhtml
@@ -0,0 +1,35 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=500261
+-->
+<head>
+ <title>Test onerror behaviour</title>
+ <script 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=500261">Mozilla Bug 500261</a>
+<p id="display"></p>
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ ok(true, "onerror method called");
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+<div id="content">
+
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="1" id="svg">
+ <image width="1" height="1" xlink:href="http://localhost/serverGone.gif" onerror="run()"/>
+ </svg>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_onload.xhtml b/dom/svg/test/test_onload.xhtml
new file mode 100644
index 0000000000..982cbb52bf
--- /dev/null
+++ b/dom/svg/test/test_onload.xhtml
@@ -0,0 +1,35 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1668942
+-->
+<head>
+ <title>Test onload behaviour</title>
+ <script 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=1668942">Mozilla Bug 1668942</a>
+<p id="display"></p>
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ ok(true, "onload method called");
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+<div id="content">
+
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="1">
+ <image width="1" height="1" xlink:href="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" onload="run()"/>
+ </svg>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_onload2.xhtml b/dom/svg/test/test_onload2.xhtml
new file mode 100644
index 0000000000..6572fc6de8
--- /dev/null
+++ b/dom/svg/test/test_onload2.xhtml
@@ -0,0 +1,48 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1474311
+-->
+<head>
+ <title>Test onload behaviour</title>
+ <script 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=1474311">Mozilla Bug 1474311</a>
+<p id="display"></p>
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+
+ let doc = document.implementation.createHTMLDocument('');
+
+ doc.body.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><image id="image" xlink:href="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==" height="200" width="200"/></svg>';
+
+ let img = doc.body.firstChild.firstChild;
+
+ document.getElementById('svg').appendChild(img);
+
+ img.addEventListener('load', function () {
+ ok(true, "onload method called");
+ SimpleTest.finish();
+ });
+
+ img.addEventListener('error', function () {
+ ok(false, "onerror method called");
+ });
+}
+
+]]>
+</script>
+<div id="content">
+
+ <svg id="svg" onload="run()" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="1">
+ </svg>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_pairParsing.html b/dom/svg/test/test_pairParsing.html
new file mode 100644
index 0000000000..9a603dc1d5
--- /dev/null
+++ b/dom/svg/test/test_pairParsing.html
@@ -0,0 +1,43 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1512745
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test pair parsing</title>
+ <script 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=1512745">Mozilla Bug 1512745</a>
+
+<svg width="230" height="120"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+
+ <filter><feGaussianBlur id="x1" stdDeviation=" 5 " /></filter>
+ <filter><feGaussianBlur id="x2" stdDeviation=" 5 10 " /></filter>
+ <filter><feGaussianBlur id="x3" stdDeviation="5 10 " /></filter>
+ <filter><feGaussianBlur id="x4" stdDeviation=" 5,10 " /></filter>
+</svg>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ function checkValue(id, x, y) {
+ if (y === undefined) {
+ y = x;
+ }
+ let e = document.getElementById(id);
+ is(e.stdDeviationX.baseVal, x, "Wrong stdDeviationX");
+ is(e.stdDeviationY.baseVal, y, "Wrong stdDeviationY");
+ }
+
+ checkValue("x1", 5);
+ checkValue("x2", 5, 10);
+ checkValue("x3", 5, 10);
+ checkValue("x4", 5, 10);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_pathAnimInterpolation.xhtml b/dom/svg/test/test_pathAnimInterpolation.xhtml
new file mode 100644
index 0000000000..3f2e6659a9
--- /dev/null
+++ b/dom/svg/test/test_pathAnimInterpolation.xhtml
@@ -0,0 +1,341 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=619498
+-->
+<head>
+ <title>Test interpolation between different path segment types</title>
+ <script 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=619498">Mozilla Bug 619498</a>
+<svg xmlns="http://www.w3.org/2000/svg" id="svg" visibility="hidden"
+ onload="this.pauseAnimations()"/>
+<script type="application/javascript"><![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+var gSVG = document.getElementById("svg");
+
+// Array of all subtests to run. This is populated by addTest.
+var gTests = [];
+
+// Array of all path segment types.
+var gTypes = "zMmLlCcQqAaHhVvSsTt".split("");
+
+// Property names on the SVGPathSeg objects for the given segment type, in the
+// order that they would appear in a path data string.
+var gArgumentNames = {
+ Z: [],
+ M: ["x", "y"],
+ L: ["x", "y"],
+ C: ["x1", "y1", "x2", "y2", "x", "y"],
+ Q: ["x1", "y1", "x", "y"],
+ A: ["r1", "r2", "angle", "largeArcFlag", "sweepFlag", "x", "y"],
+ H: ["x"],
+ V: ["y"],
+ S: ["x2", "y2", "x", "y"],
+ T: ["x", "y"],
+};
+
+// All of these prefixes leave the current point at 100,100. Some of them
+// affect the implied control point if followed by a smooth quadratic or
+// cubic segment, but no valid interpolations depend on those control points.
+var gPrefixes = [
+ [1, "M100,100"],
+ [2, "M50,50 M100,100"],
+ [2, "M50,50 m50,50"],
+ [2, "M50,50 L100,100"],
+ [2, "M50,50 l50,50"],
+ [3, "M50,50 H100 V100"],
+ [3, "M50,50 h50 V100"],
+ [3, "M50,50 H100 v50"],
+ [2, "M50,50 A10,10,10,0,0,100,100"],
+ [2, "M50,50 a10,10,10,0,0,50,50"],
+ [4, "M50,50 l50,50 z m50,50"],
+
+ // These leave the quadratic implied control point at 125,125.
+ [2, "M50,50 Q75,75,100,100"],
+ [2, "M50,50 q25,25,50,50"],
+ [2, "M75,75 T100,100"],
+ [2, "M75,75 t25,25"],
+ [3, "M50,50 T62.5,62.5 t37.5,37.5"],
+ [3, "M50,50 T62.5,62.5 T100,100"],
+ [3, "M50,50 t12.5,12.5 t37.5,37.5"],
+ [3, "M50,50 t12.5,12.5 T100,100"],
+ [3, "M50,50 Q50,50,62.5,62.5 t37.5,37.5"],
+ [3, "M50,50 Q50,50,62.5,62.5 T100,100"],
+ [3, "M50,50 q0,0,12.5,12.5 t37.5,37.5"],
+ [3, "M50,50 q0,0,12.5,12.5 T100,100"],
+
+ // These leave the cubic implied control point at 125,125.
+ [2, "M50,50 C10,10,75,75,100,100"],
+ [2, "M50,50 c10,10,25,25,50,50"],
+ [2, "M50,50 S75,75,100,100"],
+ [2, "M50,50 s25,25,50,50"],
+ [3, "M50,50 S10,10,75,75 S75,75,100,100"],
+ [3, "M50,50 S10,10,75,75 s0,0,25,25"],
+ [3, "M50,50 s10,10,25,25 S75,75,100,100"],
+ [3, "M50,50 s10,10,25,25 s0,0,25,25"],
+ [3, "M50,50 C10,10,20,20,75,75 S75,75,100,100"],
+ [3, "M50,50 C10,10,20,20,75,75 s0,0,25,25"],
+ [3, "M50,50 c10,10,20,20,25,25 S75,75,100,100"],
+ [3, "M50,50 c10,10,20,20,25,25 s0,0,25,25"],
+];
+
+// These are all of the suffixes whose result is not dependent on whether the
+// preceding segment types are quadratic or cubic types. Each entry is:
+//
+// "<fromType><toType>": [fromArguments,
+// toArguments,
+// expectedArguments,
+// expectedArgumentsAdditive]
+//
+// As an example:
+//
+// "Mm": [[10, 20], [30, 40], [-30, -20], [-120, -100]]
+//
+// This will testing interpolating between "M10,20" and "m30,40". All of the
+// these tests assume that the current point is left at 100,100. So the above
+// entry represents two kinds of tests, one where additive and one not:
+//
+// <path d="... M10,20">
+// <animate attributeName="d" from="... M10,20" to="... m30,40"/>
+// </path>
+//
+// and
+//
+// <path d="... M10,20">
+// <animate attributeName="d" from="... M10,20" to="... m30,40"
+// additive="sum"/>
+// </path>
+//
+// where the "..." is some prefix that leaves the current point at 100,100.
+// Each of the suffixes here in gSuffixes will be paired with each of the
+// prefixes in gPrefixes, all of which leave the current point at 100,100.
+// (Thus the above two tests for interpolating between "M" and "m" will be
+// performed many times, with different preceding commands.)
+//
+// The expected result of the non-additive test is "m-30,-20". Since the
+// animation is from an absolute moveto to a relative moveto, we first
+// convert the "M10,20" into its relative form, which is "m-90,-80" due to the
+// current point being 100,100. Half way through the animation between
+// "m-90,-80" and "m30,40" is thus "m-30,-20".
+//
+// The expected result of the additive test is "m-120,-100". We take the
+// halfway value of the animation, "m-30,-20" and add it on to the underlying
+// value. Since the underlying value "M10,20" is an absolute moveto, we first
+// convert it to relative, "m-90,-80", and then add the "m-30,-20" to it,
+// giving us the result "m-120,-100".
+var gSuffixes = {
+ // Same path segment type, no conversion required.
+ MM: [[10, 20], [30, 40], [20, 30], [30, 50]],
+ mm: [[10, 20], [30, 40], [20, 30], [30, 50]],
+ LL: [[10, 20], [30, 40], [20, 30], [30, 50]],
+ ll: [[10, 20], [30, 40], [20, 30], [30, 50]],
+ CC: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
+ [40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]],
+ cc: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
+ [40, 50, 60, 70, 80, 90], [50, 70, 90, 110, 130, 150]],
+ QQ: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
+ qq: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
+ AA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
+ [35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]],
+ aa: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
+ [35, 45, 55, 0, 0, 65, 75], [45, 65, 85, 0, 0, 105, 125]],
+ HH: [[10], [20], [15], [25]],
+ hh: [[10], [20], [15], [25]],
+ VV: [[10], [20], [15], [25]],
+ vv: [[10], [20], [15], [25]],
+ SS: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
+ ss: [[10, 20, 30, 40], [50, 60, 70, 80], [30, 40, 50, 60], [40, 60, 80, 100]],
+ TT: [[10, 20], [30, 40], [20, 30], [30, 50]],
+ tt: [[10, 20], [30, 40], [20, 30], [30, 50]],
+
+ // Relative <-> absolute conversion.
+ Mm: [[10, 20], [30, 40], [-30, -20], [-120, -100]],
+ mM: [[10, 20], [30, 40], [70, 80], [180, 200]],
+ Ll: [[10, 20], [30, 40], [-30, -20], [-120, -100]],
+ lL: [[10, 20], [30, 40], [70, 80], [180, 200]],
+ Cc: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
+ [-10, 0, 10, 20, 30, 40], [-100, -80, -60, -40, -20, 0]],
+ cC: [[10, 20, 30, 40, 50, 60], [70, 80, 90, 100, 110, 120],
+ [90, 100, 110, 120, 130, 140], [200, 220, 240, 260, 280, 300]],
+ Qq: [[10, 20, 30, 40], [50, 60, 70, 80],
+ [-20, -10, 0, 10], [-110, -90, -70, -50]],
+ qQ: [[10, 20, 30, 40], [50, 60, 70, 80],
+ [80, 90, 100, 110], [190, 210, 230, 250]],
+ Aa: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
+ [35, 45, 55, 0, 0, 15, 25], [45, 65, 85, 0, 0, -45, -25]],
+ aA: [[10, 20, 30, 0, 0, 40, 50], [60, 70, 80, 0, 0, 90, 100],
+ [35, 45, 55, 0, 0, 115, 125], [45, 65, 85, 0, 0, 255, 275]],
+ Hh: [[10], [20], [-35], [-125]],
+ hH: [[10], [20], [65], [175]],
+ Vv: [[10], [20], [-35], [-125]],
+ vV: [[10], [20], [65], [175]],
+ Tt: [[10, 20], [30, 40], [-30, -20], [-120, -100]],
+ tT: [[10, 20], [30, 40], [70, 80], [180, 200]],
+ Ss: [[10, 20, 30, 40], [50, 60, 70, 80],
+ [-20, -10, 0, 10], [-110, -90, -70, -50]],
+ sS: [[10, 20, 30, 40], [50, 60, 70, 80],
+ [80, 90, 100, 110], [190, 210, 230, 250]],
+};
+
+// Returns an array of property names that exist on an SVGPathSeg object
+// corresponding to the given segment type, in the order that they would
+// be present in a path data string.
+function argumentNames(aType) {
+ return gArgumentNames[aType.toUpperCase()];
+}
+
+// Creates and returns a new element and sets some attributes on it.
+function newElement(aNamespaceURI, aLocalName, aAttributes) {
+ var e = document.createElementNS(aNamespaceURI, aLocalName);
+ if (aAttributes) {
+ for (let [name, value] of Object.entries(aAttributes)) {
+ e.setAttribute(name, value);
+ }
+ }
+ return e;
+}
+
+// Creates and returns a new SVG element and sets some attributes on it.
+function newSVGElement(aLocalName, aAttributes) {
+ return newElement("http://www.w3.org/2000/svg", aLocalName, aAttributes);
+}
+
+// Creates a subtest and adds it to the document.
+//
+// * aPrefixLength/aPrefix the prefix to use
+// * aFromType/aFromArguments the segment to interpolate from
+// * aToType/aToArguments the segment to interpolate to
+// * aExpectedType/aExpectedArguments the expected result of the interpolated
+// segment half way through the animation
+// duration
+// * aAdditive whether the subtest is for an additive
+// animation
+function addTest(aPrefixLength, aPrefix, aFromType, aFromArguments,
+ aToType, aToArguments, aExpectedType, aExpectedArguments,
+ aAdditive) {
+ var fromPath = aPrefix + aFromType + aFromArguments,
+ toPath = aPrefix + aToType + aToArguments;
+
+ var path = newSVGElement("path", { d: fromPath });
+ var animate =
+ newSVGElement("animate", { attributeName: "d",
+ from: fromPath,
+ to: toPath,
+ dur: "8s",
+ additive: aAdditive ? "sum" : "replace" });
+ path.appendChild(animate);
+ gSVG.appendChild(path);
+
+ gTests.push({ element: path,
+ prefixLength: aPrefixLength,
+ from: fromPath,
+ to: toPath,
+ toType: aToType,
+ expectedType: aExpectedType,
+ expected: aExpectedArguments,
+ usesAddition: aAdditive });
+}
+
+// Generates an array of path segment arguments for the given type. aOffset
+// is a number to add on to all non-Boolean segment arguments.
+function generatePathSegmentArguments(aType, aOffset) {
+ var args = new Array(argumentNames(aType).length);
+ for (let i = 0; i < args.length; i++) {
+ args[i] = i * 10 + aOffset;
+ }
+ if (aType == "A" || aType == "a") {
+ args[3] = 0;
+ args[4] = 0;
+ }
+ return args;
+}
+
+// Returns whether interpolating between the two given types is valid.
+function isValidInterpolation(aFromType, aToType) {
+ return aFromType.toUpperCase() == aToType.toUpperCase();
+}
+
+// Runs the test.
+function run() {
+ for (let additive of [false, true]) {
+ let indexOfExpectedArguments = additive ? 3 : 2;
+
+ // Add subtests for each combination of prefix and suffix, and additive
+ // or not.
+ for (let [typePair, suffixEntry] of Object.entries(gSuffixes)) {
+ let fromType = typePair[0],
+ toType = typePair[1],
+ fromArguments = suffixEntry[0],
+ toArguments = suffixEntry[1],
+ expectedArguments = suffixEntry[indexOfExpectedArguments];
+
+ for (let prefixEntry of gPrefixes) {
+ let [prefixLength, prefix] = prefixEntry;
+ addTest(prefixLength, prefix, fromType, fromArguments,
+ toType, toArguments, toType, expectedArguments, additive);
+ }
+ }
+
+ // Test that differences in arc flag parameters cause the
+ // interpolation/addition not to occur.
+ addTest(1, "M100,100",
+ "A", [10, 20, 30, 0, 0, 40, 50],
+ "a", [60, 70, 80, 0, 1, 90, 100],
+ "a", [60, 70, 80, 0, 1, 90, 100], additive);
+ addTest(1, "M100,100",
+ "A", [10, 20, 30, 0, 0, 40, 50],
+ "a", [60, 70, 80, 1, 0, 90, 100],
+ "a", [60, 70, 80, 1, 0, 90, 100], additive);
+
+ // Test all pairs of segment types that cannot be interpolated between.
+ for (let fromType of gTypes) {
+ let fromArguments = generatePathSegmentArguments(fromType, 0);
+ for (let toType of gTypes) {
+ if (!isValidInterpolation(fromType, toType)) {
+ let toArguments = generatePathSegmentArguments(toType, 1000);
+ addTest(1, "M100,100", fromType, fromArguments,
+ toType, toArguments, toType, toArguments, additive);
+ }
+ }
+ }
+ }
+
+ // Move the document time to half way through the animations.
+ gSVG.setCurrentTime(4);
+
+ // Inspect the results of each subtest.
+ for (let test of gTests) {
+ let list = test.element.animatedPathSegList;
+ is(list.numberOfItems, test.prefixLength + 1,
+ "Length of animatedPathSegList for interpolation " +
+ (test.usesAddition ? "with addition " : "") +
+ " from " + test.from + " to " + test.to);
+
+ let seg = list.getItem(list.numberOfItems - 1);
+ let propertyNames = argumentNames(test.expectedType);
+
+ let actual = [];
+ for (let i = 0; i < test.expected.length; i++) {
+ actual.push(+seg[propertyNames[i]]);
+ }
+
+ is(seg.pathSegTypeAsLetter + actual, test.expectedType + test.expected,
+ "Path segment for interpolation " +
+ (test.usesAddition ? "with addition " : "") +
+ " from " + test.from + " to " + test.to);
+ }
+
+ // Clear all the tests. We have tons of them attached to the DOM and refresh driver tick will
+ // go through them all by calling animation controller.
+ gSVG.remove();
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run);
+]]></script>
+</body>
+</html>
diff --git a/dom/svg/test/test_pointAtLength.xhtml b/dom/svg/test/test_pointAtLength.xhtml
new file mode 100644
index 0000000000..652fac0e69
--- /dev/null
+++ b/dom/svg/test/test_pointAtLength.xhtml
@@ -0,0 +1,49 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=643419
+-->
+<head>
+ <title>Test getPointAtLength</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var p1 = document.getElementById("p1");
+ var point = p1.getPointAtLength(200);
+ is(point.x, 200);
+ is(point.y, 50);
+
+ // set the pathLength to twice its actual length
+ // and check that makes no difference
+ p1.setAttribute("pathLength", "800");
+ point = p1.getPointAtLength(200);
+ is(point.x, 200);
+ is(point.y, 50);
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run);
+]]>
+</script>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=643419">Mozilla Bug 643419</a>
+<p id="display"></p>
+<div id="content">
+
+ <svg xmlns="http://www.w3.org/2000/svg" width="750">
+ <defs>
+ <path id="p1" d="M 0 50 h 400"/>
+ </defs>
+ </svg>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_pointer-events-1a.xhtml b/dom/svg/test/test_pointer-events-1a.xhtml
new file mode 100644
index 0000000000..b7bba026c2
--- /dev/null
+++ b/dom/svg/test/test_pointer-events-1a.xhtml
@@ -0,0 +1,27 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=619959
+-->
+<head>
+ <title>Test 'pointer-events' handling</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="run_tests(0)">
+<script class="testbody" src="pointer-events.js"></script>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=619959">Mozilla Bug 619959</a>
+<p id="display"></p>
+<div id="content">
+
+ <div width="100%" height="1" id="div"></div>
+
+ <svg xmlns="http://www.w3.org/2000/svg" id="svg">
+ <rect id="rect" x="10" y="10" width="40" height="40" stroke-width="20"/>
+ <text id="text" x="190" y="50" font-size="40px" stroke-width="20">X</text>
+ </svg>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_pointer-events-1b.xhtml b/dom/svg/test/test_pointer-events-1b.xhtml
new file mode 100644
index 0000000000..7cd353de4d
--- /dev/null
+++ b/dom/svg/test/test_pointer-events-1b.xhtml
@@ -0,0 +1,27 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=619959
+-->
+<head>
+ <title>Test 'pointer-events' handling</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="run_tests(1)">
+<script class="testbody" src="pointer-events.js"></script>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=619959">Mozilla Bug 619959</a>
+<p id="display"></p>
+<div id="content">
+
+ <div width="100%" height="1" id="div"></div>
+
+ <svg xmlns="http://www.w3.org/2000/svg" id="svg">
+ <rect id="rect" x="10" y="10" width="40" height="40" stroke-width="20"/>
+ <text id="text" x="190" y="50" font-size="40px" stroke-width="20">X</text>
+ </svg>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_pointer-events-2.xhtml b/dom/svg/test/test_pointer-events-2.xhtml
new file mode 100644
index 0000000000..7f2e828521
--- /dev/null
+++ b/dom/svg/test/test_pointer-events-2.xhtml
@@ -0,0 +1,71 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=500174
+-->
+<head>
+ <title>Test Pointer Events</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="run()">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ pass();
+
+ document.getElementById("circle").setAttribute("clip-path", "#(unknown)");
+
+ pass();
+
+ SimpleTest.finish();
+}
+
+function pass() {
+ let div = document.getElementById("div");
+ // Get the coords of the origin of the SVG canvas:
+ let originX = div.offsetLeft;
+ let originY = div.offsetTop;
+ let circle = document.getElementById("circle");
+
+ let elementFromPoint = document.elementFromPoint(originX + 55, originY + 55);
+ is(elementFromPoint, circle, 'Over circle stroke with pointer-events="all"');
+
+ elementFromPoint = document.elementFromPoint(originX + 205, originY + 55);
+ is(elementFromPoint, circle, "Over foreignObject, outside clip path");
+
+ elementFromPoint = document.elementFromPoint(originX + 225, originY + 75);
+ // XXX disabled. See See https://bugzilla.mozilla.org/show_bug.cgi?id=580983#c116
+ // is(elementFromPoint, path, 'Over foreignObject, inside clip path');
+}
+
+]]>
+</script>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500174">Mozilla Bug 500174</a>
+<p id="display"></p>
+<div id="content">
+
+ <div width="100%" height="1" id="div">
+ </div>
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" id="svg">
+ <defs>
+ <clipPath id="clip">
+ <rect x="20" y="20" width="30" height="30"/>
+ </clipPath>
+ </defs>
+ <rect id="bad" width="100%" height="100%" fill="blue"/>
+ <circle id="circle" cx="50%" cy="50%" r="500" stroke-width="500" fill="none" pointer-events="all"/>
+ <foreignObject id="fo" x="200" y="50" width="50" height="50" clip-path="url(#clip)">
+ <svg>
+ <path id="path" d="M0,0 H50 V50 H0 Z" fill="green"/>
+ </svg>
+ </foreignObject>
+ </svg>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_pointer-events-3.xhtml b/dom/svg/test/test_pointer-events-3.xhtml
new file mode 100644
index 0000000000..a66297fc45
--- /dev/null
+++ b/dom/svg/test/test_pointer-events-3.xhtml
@@ -0,0 +1,54 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=762679
+-->
+<head>
+ <title>Test pointer events for small objects scaled up</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="run()">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var div = document.getElementById("div");
+ // Get the coords of the origin of the SVG canvas:
+ var originX = div.offsetLeft;
+ var originY = div.offsetTop;
+ var circle = document.getElementById("circle");
+
+ var elementFromPoint = document.elementFromPoint(originX + 150, originY + 52);
+ is(elementFromPoint, circle, "Top of circle should hit");
+
+ elementFromPoint = document.elementFromPoint(originX + 249, originY + 150);
+ is(elementFromPoint, circle, "Right of circle should hit");
+
+ elementFromPoint = document.elementFromPoint(originX + 150, originY + 249);
+ is(elementFromPoint, circle, "Bottom of circle should hit");
+
+ elementFromPoint = document.elementFromPoint(originX + 51, originY + 150);
+ is(elementFromPoint, circle, "Left of circle should hit");
+
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=762679">Mozilla Bug 762679</a>
+<p id="display"></p>
+<div id="content">
+
+ <div width="100%" height="1" id="div">
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="400" id="svg">
+ <circle id="circle" cx="1.5" cy="1.5" r="1" transform="scale(100, 100)"/>
+ </svg>
+ </div>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_pointer-events-4.xhtml b/dom/svg/test/test_pointer-events-4.xhtml
new file mode 100644
index 0000000000..e6600bd0f8
--- /dev/null
+++ b/dom/svg/test/test_pointer-events-4.xhtml
@@ -0,0 +1,109 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=820506
+-->
+<head>
+ <title>Test pointer events with clipPath</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="run()">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var div = document.getElementById("div");
+ // Get the coords of the origin of the SVG canvas:
+ var originX = div.offsetLeft;
+ var originY = div.offsetTop;
+ var r1 = document.getElementById("r1");
+ var r2 = document.getElementById("r2");
+ var element;
+ var background = document.getElementById("background");
+
+ // Test r1 just outsite the clip area:
+
+ element = document.elementFromPoint(originX + 19, originY + 19);
+ is(element, background, "Should not hit top-left of r1");
+
+ element = document.elementFromPoint(originX + 101, originY + 19);
+ is(element, background, "Should not hit top-right of r1");
+
+ element = document.elementFromPoint(originX + 101, originY + 101);
+ is(element, background, "Should not hit bottom-right of r1");
+
+ element = document.elementFromPoint(originX + 19, originY + 101);
+ is(element, background, "Should not hit bottom-left of r1");
+
+ // Test r1 just inside the clip area:
+
+ element = document.elementFromPoint(originX + 21, originY + 21);
+ is(element, r1, "Should hit top-left of r1");
+
+ element = document.elementFromPoint(originX + 99, originY + 21);
+ is(element, r1, "Should hit top-right of r1");
+
+ element = document.elementFromPoint(originX + 99, originY + 99);
+ is(element, r1, "Should hit bottom-right of r1");
+
+ element = document.elementFromPoint(originX + 21, originY + 99);
+ is(element, r1, "Should hit bottom-left of r1");
+
+ // Test r2 just outsite the clip area:
+
+ element = document.elementFromPoint(originX + 109, originY + 19);
+ is(element, background, "Should not hit top-left of r2");
+
+ element = document.elementFromPoint(originX + 201, originY + 19);
+ is(element, background, "Should not hit top-right of r2");
+
+ element = document.elementFromPoint(originX + 201, originY + 101);
+ is(element, background, "Should not hit bottom-right of r2");
+
+ element = document.elementFromPoint(originX + 109, originY + 101);
+ is(element, background, "Should not hit bottom-left of r2");
+
+ // Test r2 just inside the clip area:
+
+ element = document.elementFromPoint(originX + 121, originY + 21);
+ is(element, r2, "Should hit top-left of r2");
+
+ element = document.elementFromPoint(originX + 199, originY + 21);
+ is(element, r2, "Should hit top-right of r2");
+
+ element = document.elementFromPoint(originX + 199, originY + 99);
+ is(element, r2, "Should hit bottom-right of r2");
+
+ element = document.elementFromPoint(originX + 121, originY + 99);
+ is(element, r2, "Should hit bottom-left of r2");
+
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500174">Mozilla Bug 500174</a>
+<p id="display"></p>
+<div id="content">
+
+ <div width="100%" height="1" id="div">
+ </div>
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" id="svg">
+ <clipPath id="cp1" clipPathUnits="userSpaceOnUse">
+ <rect x="20" y="20" width="80" height="80"/>
+ </clipPath>
+ <clipPath id="cp2" clipPathUnits="objectBoundingBox">
+ <rect x="0.1" y="0.1" width="0.8" height="0.8"/>
+ </clipPath>
+ <rect id="background" width="100%" height="100%" fill="blue"/>
+ <rect id="r1" x="10" y="10" width="100" height="100" clip-path="url(#cp1)"/>
+ <rect id="r2" x="110" y="10" width="100" height="100" clip-path="url(#cp2)"/>
+ </svg>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_pointer-events-6.xhtml b/dom/svg/test/test_pointer-events-6.xhtml
new file mode 100644
index 0000000000..e6e40aedb1
--- /dev/null
+++ b/dom/svg/test/test_pointer-events-6.xhtml
@@ -0,0 +1,69 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=500174
+-->
+<head>
+ <title>Test pointer events for clip-path on non-SVG elements</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="run()">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var div = document.getElementById("div");
+ // Get the coords of the origin of the SVG canvas:
+ var originX = div.offsetLeft;
+ var originY = div.offsetTop;
+ var elementFromPoint;
+
+ elementFromPoint = document.elementFromPoint(originX + 18, originY + 30);
+ isnot(elementFromPoint, div, "Outside left edge of clipped area");
+
+ elementFromPoint = document.elementFromPoint(originX + 22, originY + 30);
+ is(elementFromPoint, div, "Inside left edge of clipped area");
+
+ elementFromPoint = document.elementFromPoint(originX + 30, originY + 18);
+ isnot(elementFromPoint, div, "Outside top edge of clipped area");
+
+ elementFromPoint = document.elementFromPoint(originX + 30, originY + 22);
+ is(elementFromPoint, div, "Inside top edge of clipped area");
+
+ elementFromPoint = document.elementFromPoint(originX + 42, originY + 30);
+ isnot(elementFromPoint, div, "Outside right edge of clipped area");
+
+ elementFromPoint = document.elementFromPoint(originX + 38, originY + 30);
+ is(elementFromPoint, div, "Inside right edge of clipped area");
+
+ elementFromPoint = document.elementFromPoint(originX + 30, originY + 42);
+ isnot(elementFromPoint, div, "Outside bottom edge of clipped area");
+
+ elementFromPoint = document.elementFromPoint(originX + 30, originY + 38);
+ is(elementFromPoint, div, "Inside bottom edge of clipped area");
+
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500174">Mozilla Bug 500174</a>
+<p id="display"></p>
+<div id="content">
+
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" id="svg">
+ <defs>
+ <clipPath id="clip">
+ <rect x="20" y="20" width="20" height="20"/>
+ </clipPath>
+ </defs>
+ </svg>
+ <div id="div" style="width:100px; height:100px; clip-path:url(#clip)"></div>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_pointer-events-7.xhtml b/dom/svg/test/test_pointer-events-7.xhtml
new file mode 100644
index 0000000000..d254c40192
--- /dev/null
+++ b/dom/svg/test/test_pointer-events-7.xhtml
@@ -0,0 +1,65 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1119698
+-->
+<head>
+ <title>Test pointer events with image</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="run()">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var div = document.getElementById("div");
+ // Get the coords of the origin of the SVG canvas:
+ var originX = div.offsetLeft;
+ var originY = div.offsetTop;
+ var image4 = document.getElementById("image4");
+ var image5 = document.getElementById("image5");
+ var element;
+ var background = document.getElementById("background");
+
+ element = document.elementFromPoint(originX + 20, originY + 20);
+ is(element, background, "Should not hit visibility:hidden image by default");
+
+ element = document.elementFromPoint(originX + 120, originY + 20);
+ is(element, background, "Should not hit pointer-events:none image");
+
+ element = document.elementFromPoint(originX + 220, originY + 20);
+ is(element, background, "Should not hit pointer-events:visible visibility:hidden image");
+
+ element = document.elementFromPoint(originX + 320, originY + 20);
+ is(element, image4, "Should hit pointer-events:painted visibility:hidden image");
+
+ element = document.elementFromPoint(originX + 420, originY + 20);
+ is(element, image5, "Should hit pointer-events:stroke visibility:hidden image");
+
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1119698">Mozilla Bug 1119698</a>
+<p id="display"></p>
+<div id="content">
+
+ <div width="100%" height="1" id="div">
+ </div>
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" id="svg">
+ <rect id="background" width="100%" height="100%" fill="blue"/>
+ <image x="10" y="10" width="100" height="100" visibility="hidden" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj+M/AAAADAQEAGN2NsAAAAABJRU5ErkJggg"/>
+ <image x="110" y="10" width="100" height="100" pointer-events="none" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj+M/AAAADAQEAGN2NsAAAAABJRU5ErkJggg"/>
+ <image x="210" y="10" width="100" height="100" pointer-events="visible" visibility="hidden" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj+M/AAAADAQEAGN2NsAAAAABJRU5ErkJggg"/>
+ <image id="image4" x="310" y="10" width="100" height="100" pointer-events="painted" visibility="hidden" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj+M/AAAADAQEAGN2NsAAAAABJRU5ErkJggg"/>
+ <image id="image5" x="410" y="10" width="100" height="100" pointer-events="stroke" visibility="hidden" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAAxJREFUCNdj+M/AAAADAQEAGN2NsAAAAABJRU5ErkJggg"/>
+ </svg>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_scientific.html b/dom/svg/test/test_scientific.html
new file mode 100644
index 0000000000..8bc0d67631
--- /dev/null
+++ b/dom/svg/test/test_scientific.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=302971
+-->
+<head>
+ <title>Test for Bug 302971</title>
+ <script 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=302971">Mozilla Bug 302971</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg" src="scientific-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ function runTests() {
+ var doc = $("svg").contentWindow.document;
+ var rect = doc.getElementById("rect");
+
+ // ordinary
+
+ rect.setAttribute("stroke-width", "5");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), "5px", "Ordinary");
+
+ // valid exponential notation
+
+ rect.setAttribute("stroke-width", "4E1");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), "40px", "Exponent");
+
+ rect.setAttribute("stroke-width", "6e1");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), "60px", "Lower-case Exponent");
+
+ rect.setAttribute("stroke-width", "2E+1");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), "20px", "Positive Exponent");
+
+ rect.setAttribute("stroke-width", "100E-1");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), "10px", "Negative Exponent");
+
+ rect.setAttribute("stroke-width", "0.7E1");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), "7px", "Floating Point with Exponent");
+
+ rect.setAttribute("stroke-width", "50.0E-1");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), "5px", "Floating Point with Negative Exponent");
+
+ rect.setAttribute("stroke-width", "0.8E+1");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), "8px", "Floating Point with Positive Exponent");
+
+ rect.setAttribute("stroke-width", "4E1px");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), "40px", "Units");
+
+ // check units that begin with the letter e
+
+ var font_size = doc.defaultView.getComputedStyle(rect).getPropertyValue("font-size");
+
+ rect.setAttribute("stroke-width", "1em");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), font_size, "em Units");
+
+ // invalid exponential notation
+
+ rect.setAttribute("stroke-width", "1E1.1");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), "1px", "Floating Point Exponent");
+
+ rect.setAttribute("stroke-width", "E1");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), "1px", "No Mantissa");
+
+ rect.setAttribute("stroke-width", "1 e");
+ is(doc.defaultView.getComputedStyle(rect).getPropertyValue("stroke-width"), "1px", "Spaces");
+
+ SimpleTest.finish();
+ }
+
+ window.addEventListener("load", runTests);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_selectSubString.xhtml b/dom/svg/test/test_selectSubString.xhtml
new file mode 100644
index 0000000000..6755b65c56
--- /dev/null
+++ b/dom/svg/test/test_selectSubString.xhtml
@@ -0,0 +1,74 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=398825
+-->
+<head>
+ <title>Test for Bug 398825</title>
+ <script 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=398825">Mozilla Bug 398825</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg" src="selectSubString-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ var document = $("svg").contentWindow.document;
+ var text = document.getElementById("text");
+
+ function expectThrow(charnum, nchars) {
+ try {
+ text.selectSubString(charnum, nchars);
+ ok(false,
+ "text.selectSubString(" + charnum + "," + nchars + ") " +
+ "should have thrown");
+ } catch (e) {
+ is(e.name, "IndexSizeError",
+ "expected an index error for " +
+ "text.selectSubString(" + charnum + "," + nchars + ")");
+ is(e.code, DOMException.INDEX_SIZE_ERR,
+ "expected an index error for " +
+ "text.selectSubString(" + charnum + "," + nchars + ")");
+ }
+ }
+
+ function expectNoThrow(charnum, nchars, expected) {
+ try {
+ text.selectSubString(charnum, nchars);
+ ok(true,
+ "text.selectSubString(" + charnum + "," + nchars + ") " +
+ "should not have thrown");
+ } catch (e) {
+ ok(false,
+ "unexpected exception for " +
+ "text.selectSubString(" + charnum + "," + nchars + ")");
+ }
+ }
+
+ expectThrow(100, 2);
+ expectThrow(100, 0);
+ expectThrow(3, 0);
+ expectThrow(3, 100);
+ expectThrow(3, 100);
+ expectThrow(100, 100);
+
+ expectNoThrow(1, 100);
+ expectNoThrow(2, 100);
+ expectNoThrow(1, 3);
+ expectNoThrow(0, 4);
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", runTests);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_stroke-hit-testing.xhtml b/dom/svg/test/test_stroke-hit-testing.xhtml
new file mode 100644
index 0000000000..7fc9329260
--- /dev/null
+++ b/dom/svg/test/test_stroke-hit-testing.xhtml
@@ -0,0 +1,66 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=676001
+-->
+<head>
+ <title>Test hit-testing of stroke</title>
+ <style>
+
+:hover { stroke: lime; }
+
+ </style>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="run()">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var div = document.getElementById("div");
+ var line = document.getElementById("line");
+ var circle = document.getElementById("circle");
+ var offsetX = div.offsetLeft;
+ var offsetY = div.offsetTop;
+ var got;
+
+ // line
+ got = document.elementFromPoint(offsetX + 116, offsetY + 103);
+ is(got, line, "Should hit line (1)");
+
+ got = document.elementFromPoint(offsetX + 123, offsetY + 108);
+ is(got, line, "Should hit line (2)");
+
+ // circle
+ got = document.elementFromPoint(offsetX + 188, offsetY + 158);
+ is(got, circle, "Should hit circle (1)");
+
+ got = document.elementFromPoint(offsetX + 185, offsetY + 162);
+ is(got, circle, "Should hit circle (2)");
+
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=676001">Mozilla Bug 676001</a>
+<p id="display"></p>
+<div id="content">
+
+ <div width="100%" height="1" id="div"></div>
+ <svg xmlns="http://www.w3.org/2000/svg" id="svg" width="500" height="300">
+ <line id="line" x1="100" y1="100" x2="600" y2="180"
+ stroke="red" stroke-width="40"/>
+ <!-- the circle test points need to be within the mochitest test harness
+ viewport for test content in order for elementFromPoint to work -->
+ <circle id="circle" cx="100" cy="150" r="100"
+ fill="none" stroke="red" stroke-width="40"/>
+ </svg>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_stroke-linecap-hit-testing.xhtml b/dom/svg/test/test_stroke-linecap-hit-testing.xhtml
new file mode 100644
index 0000000000..7e08e88ad0
--- /dev/null
+++ b/dom/svg/test/test_stroke-linecap-hit-testing.xhtml
@@ -0,0 +1,45 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=589648
+-->
+<head>
+ <title>Test hit-testing of line caps</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="run()">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var div = document.getElementById("div");
+ var x = div.offsetLeft;
+ var y = div.offsetTop;
+ var got, expected;
+
+ got = document.elementFromPoint(5 + x, 5 + y);
+ expected = document.getElementById("zero-length-square-caps");
+ is(got, expected, "Check hit on zero length subpath's square caps");
+
+ SimpleTest.finish();
+}
+
+]]>
+</script>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500174">Mozilla Bug 500174</a>
+<p id="display"></p>
+<div id="content">
+
+ <div width="100%" height="1" id="div"></div>
+ <svg xmlns="http://www.w3.org/2000/svg" id="svg" width="400" height="300">
+ <path id="zero-length-square-caps" stroke="blue" stroke-width="50"
+ stroke-linecap="square" d="M25,25 L25,25"/>
+ </svg>
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_style_sheet.html b/dom/svg/test/test_style_sheet.html
new file mode 100644
index 0000000000..955dda1ac8
--- /dev/null
+++ b/dom/svg/test/test_style_sheet.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<title>Test for Bug 1239128</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1239128">Mozilla Bug 1239128</a>
+<p id="display"</p>
+
+<svg>
+ <style>svg { fill: blue; }</style>
+</svg>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+var style = document.querySelector("style");
+
+var exceptionThrown = false;
+try {
+ is(style.sheet.cssRules[0].cssText, "svg { fill: blue; }",
+ "Should get the fill: blue rule back");
+} catch (e) {
+ exceptionThrown = true;
+}
+
+ok(!exceptionThrown, "Should be able to access data: <style> stylesheet");
+</script>
+</pre>
diff --git a/dom/svg/test/test_switch.xhtml b/dom/svg/test/test_switch.xhtml
new file mode 100644
index 0000000000..d1fa546e26
--- /dev/null
+++ b/dom/svg/test/test_switch.xhtml
@@ -0,0 +1,99 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=484176
+-->
+<head>
+ <title>Test SVG Switch</title>
+ <script 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=484176">Mozilla Bug 484176</a>
+<p id="display"></p>
+<div id="content"></div>
+
+<iframe id="svg" src="switch-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ <![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+var test = 1;
+
+function checkBounds(element, x, y, w, h) {
+ var bbox = element.getBBox();
+ var name = element.nodeName;
+
+ is(bbox.x, x, test + " " + name + ".getBBox().x");
+ is(bbox.y, y, test + " " + name + ".getBBox().y");
+ is(bbox.width, w, test + " " + name + ".getBBox().width");
+ is(bbox.height, h, test + " " + name + ".getBBox().height");
+ ++test;
+}
+
+function checkWidth(element, w) {
+ var bbox = element.getBBox();
+ var name = element.nodeName;
+
+ is(bbox.width, w, test + " " + name + ".getBBox().width");
+ ++test;
+}
+
+function run() {
+ // Set accept_languages to something we know
+ SpecialPowers.pushPrefEnv({"set": [["intl.accept_languages", "en-gb,en,it"]]}, run1);
+}
+
+function run1() {
+ try {
+ var doc = $("svg").contentDocument;
+ var s = doc.getElementById("s");
+ var first = doc.getElementById("first");
+ var second = doc.getElementById("second");
+ var third = doc.getElementById("third");
+
+ first.setAttribute("systemLanguage", "fr");
+
+ /* test for an exact match */
+ second.setAttribute("systemLanguage", "en-gb");
+ checkWidth(s, 50);
+
+ /* test for a close match i.e. the same language prefix */
+ second.setAttribute("systemLanguage", "en-us");
+ checkWidth(s, 50);
+
+ /* test that we pick the best match */
+ second.setAttribute("systemLanguage", "it");
+ checkWidth(s, 50);
+
+ /* test that we use the default if nothing matches */
+ second.setAttribute("systemLanguage", "fr");
+ checkWidth(s, 80);
+
+ /* test we still ignore non-matches */
+ second.removeAttribute("systemLanguage");
+ third.setAttribute("systemLanguage", "fr");
+ checkWidth(s, 50);
+
+ /* check what happens if nothing matches */
+ second.setAttribute("systemLanguage", "fr");
+ checkWidth(s, 0);
+
+ /* test that we pick the best match */
+ first.setAttribute("systemLanguage", "en");
+ second.setAttribute("systemLanguage", "en-gb");
+ checkWidth(s, 50);
+ } finally {
+ SimpleTest.finish();
+ }
+}
+
+window.addEventListener("load", run);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_tabindex.html b/dom/svg/test/test_tabindex.html
new file mode 100644
index 0000000000..65315420bc
--- /dev/null
+++ b/dom/svg/test/test_tabindex.html
@@ -0,0 +1,103 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <title>Test for SVG tabIndex - Bug 778654</title>
+ <link rel="stylesheet" type="text/css"
+ href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" overflow="visible">
+ <foreignObject id="f" x="0" y="0" width="200" height="60" tabindex="0">
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <p>Here is a paragraph that requires word wrap</p>
+ </body>
+ </foreignObject>
+ <rect id="r" x="0" y="70" width="100" height="100" fill="yellow" tabindex="1"/>
+ <text id="t" x="0" y="200" tabindex="2">
+ This is SVG text
+ </text>
+ <a xlink:href="#" id="l1" tabindex="3">
+ <circle cx="10" cy="230" r="10"/>
+ </a>
+ <a id="l2" tabindex="4">
+ <circle cx="10" cy="260" r="10"/>
+ </a>
+ <rect id="r6" x="0" y="70" width="100" height="100" fill="yellow" tabindex="6"/>
+ <rect id="r7" x="0" y="70" width="100" height="100" fill="yellow" tabindex="7"/>
+</svg>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function main() {
+ var f = document.getElementById("f");
+ var r = document.getElementById("r");
+ var t = document.getElementById("t");
+ var l1 = document.getElementById("l1");
+ var l2 = document.getElementById("l2");
+ const isMac = ("nsILocalFileMac" in SpecialPowers.Ci);
+
+ try {
+ // Step 1: Checking by assigning tabIndex
+ is(f.tabIndex, 0, "foreignObject initial tabIndex");
+ f.tabIndex = 1;
+ is(f.tabIndex, 1, "foreignObject tabIndex is set to 1");
+
+ is(r.tabIndex, 1, "rect initial tabIndex");
+ r.tabIndex = 2;
+ is(r.tabIndex, 2, "rect tabIndex is set to 2");
+
+ is(t.tabIndex, 2, "text initial tabIndex");
+ t.tabIndex = 3;
+ is(t.tabIndex, 3, "text is set to 3");
+
+ is(l1.tabIndex, 3, "link initial tabIndex");
+ l1.tabIndex = 4;
+ is(l1.tabIndex, 4, "link is set to 4");
+
+ is(l2.tabIndex, 4, "non-link initial tabIndex");
+ l2.tabIndex = 5;
+ is(l2.tabIndex, 5, "non-link is set to 4");
+
+ // Step 2: Checking by triggering TAB event
+ is(document.activeElement.tabIndex, -1, "In the beginning, the active element tabindex is -1");
+
+ synthesizeKey("KEY_Tab");
+ is(document.activeElement.tabIndex, 1, "The active element tabindex is 1");
+
+ synthesizeKey("KEY_Tab");
+ is(document.activeElement.tabIndex, 2, "The active element tabindex is 2");
+
+ synthesizeKey("KEY_Tab");
+ is(document.activeElement.tabIndex, 3, "The active element tabindex is 3");
+
+ synthesizeKey("KEY_Tab");
+ // On Mac, SVG link elements should not be focused.
+ if (isMac) {
+ is(document.activeElement.tabIndex, 6, "The active element tabindex is 6");
+ } else {
+ is(document.activeElement.tabIndex, 4, "The active element tabindex is 4");
+ }
+
+ synthesizeKey("KEY_Tab");
+ // On Mac, SVG link elements should not be focused.
+ if (isMac) {
+ is(document.activeElement.tabIndex, 7, "The active element tabindex is 7");
+ } else {
+ is(document.activeElement.tabIndex, 5, "The active element tabindex is 5");
+ }
+ } catch (e) {
+ ok(false, "Got unexpected exception" + e);
+ }
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(main);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_tearoff_with_cc.html b/dom/svg/test/test_tearoff_with_cc.html
new file mode 100644
index 0000000000..86facf40da
--- /dev/null
+++ b/dom/svg/test/test_tearoff_with_cc.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1288228
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1288228</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ /** Test for Bug 1288228 **/
+ /* Note: the crash in bug 1288228 doesn't happen reliably (and only happens
+ * after several reloads). So, we reload the iframe 10 times, and then call
+ * it good if we haven't crashed.
+ */
+ const maxReloads = 10;
+ let remainingReloads = maxReloads;
+
+ /* The helper-file in the iframe will notify us after it's performed its
+ * potentially-crash-triggering tweak. At that point, we reload the iframe
+ * and wait for it to notify again (or we simply finish, if we've completed
+ * all of the reloads we planned to do).
+ */
+ window.addEventListener("message", reloadIframe);
+
+ function reloadIframe() {
+ if (--remainingReloads == 0) {
+ ok(true, "Didn't crash!");
+ SimpleTest.finish();
+ } else {
+ var frame = document.getElementById("testIframe");
+ frame.setAttribute("src", "");
+ frame.setAttribute("src", "tearoff_with_cc_helper.html");
+ }
+ }
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</head>
+<body onload="reloadIframe()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1288228">
+ Mozilla Bug 1288228
+</a>
+<p id="display">
+ <iframe id="testIframe"></iframe>
+</p>
+</body>
+</html>
diff --git a/dom/svg/test/test_text.html b/dom/svg/test/test_text.html
new file mode 100644
index 0000000000..c1b99a9175
--- /dev/null
+++ b/dom/svg/test/test_text.html
@@ -0,0 +1,189 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=392233
+-->
+<head>
+ <title>Test for Bug 392233</title>
+ <script 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=392233">Mozilla Bug 392233</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg" src="text-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ var doc = $("svg").contentWindow.document;
+ var text1 = doc.getElementById("text1");
+ var text2 = doc.getElementById("text2");
+ var text3 = doc.getElementById("text3");
+ var text4 = doc.getElementById("text4");
+
+ var charWidth = text1.getSubStringLength(0, 1);
+
+ function isPoint(pt1, x, y, str) {
+ is(pt1.x, x, str + " x");
+ is(pt1.y, y, str + " y");
+ }
+
+ function isPointFuzzy(pt1, x, y, str) {
+ isfuzzy(pt1.x, x, 0.05, str + " x");
+ isfuzzy(pt1.y, y, 0.05, str + " y");
+ }
+
+ function ymost(r) {
+ return r.y + r.height;
+ }
+
+ function xmost(r) {
+ return r.x + r.width;
+ }
+
+ var p = text1.getStartPositionOfChar(0);
+
+ // Simple horizontal string
+
+ is(text1.getNumberOfChars(), 3, "text1 length");
+ ok(text1.getComputedTextLength() > 0, "text1 measured length");
+ is(text1.getComputedTextLength(), text1.getSubStringLength(0, 3), "text1 substring length");
+ isPoint(text1.getStartPositionOfChar(0), 5, 25, "text1 char 0 start offset");
+ isPointFuzzy(text1.getStartPositionOfChar(1), 5 + charWidth, 25, "text1 char 1 start offset");
+ isPointFuzzy(text1.getStartPositionOfChar(2), 5 + 2 * charWidth, 25, "text1 char 2 start offset");
+ isPointFuzzy(text1.getEndPositionOfChar(0), 5 + charWidth, 25, "text1 char 0 end offset");
+ isPointFuzzy(text1.getEndPositionOfChar(1), 5 + 2 * charWidth, 25, "text1 char 1 end offset");
+ isPointFuzzy(text1.getEndPositionOfChar(2), 5 + 3 * charWidth, 25, "text1 char 2 end offset");
+ isfuzzy(text1.getExtentOfChar(0).x, 5, 0.01, "text1 char 0 extent x");
+ is(text1.getExtentOfChar(0).width, text1.getSubStringLength(0, 1), "text1 char 0 extent width");
+ ok(text1.getExtentOfChar(0).y < 25, "text1 char 0 extent y");
+ ok(ymost(text1.getExtentOfChar(0)) > 25, "text1 char 0 extent height");
+ isfuzzy(text1.getExtentOfChar(1).x, 5 + charWidth, 0.01, "text1 char 1 extent x");
+ is(text1.getExtentOfChar(1).width, text1.getSubStringLength(0, 1), "text1 char 1 extent width");
+ is(text1.getExtentOfChar(1).y, text1.getExtentOfChar(0).y, "text1 char 0/1 extent y");
+ is(text1.getExtentOfChar(1).height, text1.getExtentOfChar(0).height, "text1 char 0/1 extent height");
+ isfuzzy(text1.getExtentOfChar(2).x, 5 + 2 * charWidth, 0.02, "text1 char 2 extent x");
+ is(text1.getExtentOfChar(2).width, text1.getSubStringLength(0, 1), "text1 char 2 extent width");
+ is(text1.getExtentOfChar(2).y, text1.getExtentOfChar(0).y, "text1 char 0/2 extent y");
+ is(text1.getExtentOfChar(2).height, text1.getExtentOfChar(0).height, "text1 char 0/2 extent height");
+ is(text1.getRotationOfChar(0), 0, "text1 char 0 rotation");
+ is(text1.getRotationOfChar(1), 0, "text1 char 0 rotation");
+ is(text1.getRotationOfChar(2), 0, "text1 char 0 rotation");
+ p.x = 5 + 0.1;
+ p.y = 25;
+ is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 left edge");
+ p.x = 5 + charWidth - 0.1;
+ is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 on right");
+ p.x = 5 - 0.1;
+ is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on left");
+ p.x = 5 + 0.1;
+ p.y = text1.getExtentOfChar(0).y - 0.1;
+ is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on top");
+ p.y = text1.getExtentOfChar(0).y + 0.1;
+ is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 top edge");
+ p.x = 5 + 3 * charWidth - 0.1;
+ is(text1.getCharNumAtPosition(p), 2, "text1 finding char 2 top edge");
+ p.x = 5 + 3 * charWidth + 0.1;
+ is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on right");
+
+ // Simple rotated-90 string ... width might change because of hinting
+
+ charWidth = text2.getSubStringLength(0, 1);
+
+ is(text2.getNumberOfChars(), 3, "text2 length");
+ ok(text2.getComputedTextLength() > 0, "text2 measured length");
+ is(text2.getComputedTextLength(), text2.getSubStringLength(0, 3), "text2 substring length");
+ isPointFuzzy(text2.getStartPositionOfChar(0), 100, 125, "text2 char 0 start offset");
+ isPointFuzzy(text2.getStartPositionOfChar(1), 100, 125 + charWidth, "text2 char 1 start offset");
+ isPointFuzzy(text2.getStartPositionOfChar(2), 100, 125 + 2 * charWidth, "text2 char 2 start offset");
+ isPointFuzzy(text2.getEndPositionOfChar(0), 100, 125 + charWidth, "text2 char 0 end offset");
+ isPointFuzzy(text2.getEndPositionOfChar(1), 100, 125 + 2 * charWidth, "text2 char 1 end offset");
+ isPointFuzzy(text2.getEndPositionOfChar(2), 100, 125 + 3 * charWidth, "text2 char 2 end offset");
+ isfuzzy(text2.getExtentOfChar(0).y, 125, 0.01, "text2 char 0 extent y");
+ isfuzzy(text2.getExtentOfChar(0).height, charWidth, 0.000001, "text2 char 0 extent height");
+ ok(text2.getExtentOfChar(0).width < 100, "text2 char 0 extent x");
+ ok(xmost(text2.getExtentOfChar(0)) > 100, "text2 char 0 extent width");
+ isfuzzy(text2.getExtentOfChar(1).y, 125 + charWidth, 0.01, "text2 char 1 extent x");
+ isfuzzy(text2.getExtentOfChar(1).height, text2.getSubStringLength(0, 1), 0.000001, "text2 char 1 extent width");
+ is(text2.getExtentOfChar(1).x, text2.getExtentOfChar(0).x, "text2 char 0/1 extent y");
+ is(text2.getExtentOfChar(1).width, text2.getExtentOfChar(0).width, "text2 char 0/1 extent height");
+ isfuzzy(text2.getExtentOfChar(2).y, 125 + 2 * charWidth, 0.01, "text2 char 2 extent x");
+ isfuzzy(text2.getExtentOfChar(2).height, text2.getSubStringLength(0, 1), 0.000001, "text2 char 2 extent width");
+ is(text2.getExtentOfChar(2).x, text2.getExtentOfChar(0).x, "text2 char 0/2 extent y");
+ is(text2.getExtentOfChar(2).width, text2.getExtentOfChar(0).width, "text2 char 0/2 extent height");
+ is(text2.getRotationOfChar(0), 90, "text2 char 0 rotation");
+ is(text2.getRotationOfChar(1), 90, "text2 char 0 rotation");
+ is(text2.getRotationOfChar(2), 90, "text2 char 0 rotation");
+ p.y = 125 + 0.1;
+ p.x = 100;
+ is(text2.getCharNumAtPosition(p), 0, "text2 finding char 0 top edge");
+ p.y = 125 + charWidth - 0.1;
+ is(text2.getCharNumAtPosition(p), 0, "text2 finding char 0 on bottom");
+ p.y = 125 - 0.1;
+ is(text2.getCharNumAtPosition(p), -1, "text2 finding no char on top");
+ p.y = 125 + 0.1;
+ p.x = text2.getExtentOfChar(0).x - 0.1;
+ is(text2.getCharNumAtPosition(p), -1, "text2 finding no char on left");
+ p.x = text2.getExtentOfChar(0).x + 0.1;
+ is(text2.getCharNumAtPosition(p), 0, "text2 finding char 0 left edge");
+ p.y = 125 + 3 * charWidth - 0.1;
+ is(text2.getCharNumAtPosition(p), 2, "text2 finding char 2 bottom edge");
+ p.y = 1225 + 3 * charWidth + 0.1;
+ is(text2.getCharNumAtPosition(p), -1, "text2 finding no char on bottom");
+
+ // Text along a thin rectangle path
+
+ charWidth = text3.getSubStringLength(0, 1);
+
+ is(text3.getNumberOfChars(), 26, "text3 length");
+ ok(text3.getComputedTextLength() > 0, "text3 measured length");
+ is(text3.getComputedTextLength(), text3.getSubStringLength(0, 26), "text3 substring length");
+
+ // character 12 should be on the bottom side
+ is(text3.getStartPositionOfChar(12).y, 253, "text3 char 12 start offset");
+ is(text3.getEndPositionOfChar(12).y, 253, "text3 char 12 end offset");
+ ok(text3.getExtentOfChar(12).y < 253, "text3 char 12 extent y");
+ ok(ymost(text3.getExtentOfChar(12)) > 253, "text3 char 12 extent height");
+ isfuzzy(text3.getRotationOfChar(12), 180, 0.001, "text3 char 12 rotation");
+ p.x = text3.getExtentOfChar(12).x + 0.1;
+ p.y = ymost(text3.getExtentOfChar(12)) - 0.1;
+ is(text3.getCharNumAtPosition(p), 12, "text3 finding char 12");
+ // This next test is tricky. The glyph for character 3 may overlap from the above
+ // but character 12 wins because it's the last to render
+ p.y = text3.getExtentOfChar(12).y + 0.1;
+ is(text3.getCharNumAtPosition(p), 12, "text3 finding last rendered char");
+
+ // character 25 should be beyond the end of the path
+ // Not sure what should happen here. Currently we throw, which seems wrong
+ // is(text3.getStartPositionOfChar(25).x, 0, "text3 char 25 start offset");
+
+ // Display:none string
+
+ is(text4.getNumberOfChars(), 0, "text4 length");
+ is(text4.getComputedTextLength(), 0, "text4 measured length");
+ is(text4.getSubStringLength(0, 3), 0, "text4 substring length");
+ p = text1.getStartPositionOfChar(0);
+ is(text4.getCharNumAtPosition(p), -1, "text4 shouldn't find rendered char");
+}
+
+function runTests() {
+ runTest();
+
+ var doc = $("svg").contentWindow.document;
+ doc.getElementById("g").setAttribute("transform", "scale(2) rotate(90 200 200)");
+
+ runTest();
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", runTests);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_text_2.html b/dom/svg/test/test_text_2.html
new file mode 100644
index 0000000000..380de92dde
--- /dev/null
+++ b/dom/svg/test/test_text_2.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=655877
+-->
+<head>
+ <meta charset=UTF-8>
+ <title>Test for Bug 655877</title>
+ <script 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=655877">Mozilla Bug 655877</a>
+<p id="display"></p>
+<div id="content">
+ <svg width="400" height="200">
+ <text x="100" y="100" style="font: 16px sans-serif">abc אבג 123 דהו defg</text>
+ </svg>
+</div>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function close(x, y, message) {
+ if (Math.abs(x - y) < 1e-4) {
+ ok(true, message);
+ } else {
+ // Use is() so that the difference is actually printed in the error message
+ is(x, y, message);
+ }
+}
+
+function runTest() {
+ var text = document.querySelector("text");
+
+ // there are only 20 addressable characters
+ is(text.getNumberOfChars(), 20, "getNumberOfChars");
+
+ var sum, total = text.getComputedTextLength();
+
+ close(text.getSubStringLength(0, 20), total, "getSubStringLength all");
+
+ // add the advance of each glyph
+ sum = 0;
+ for (var i = 0; i < 20; i++) {
+ sum += text.getSubStringLength(i, 1);
+ }
+ close(sum, total, "sum getSubStringLength 1");
+
+ // split the text up into three chunks and add them together
+ close(text.getSubStringLength(0, 6) +
+ text.getSubStringLength(6, 8) +
+ text.getSubStringLength(14, 6), total, "sum getSubStringLength 2");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", runTest);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_text_dirty.html b/dom/svg/test/test_text_dirty.html
new file mode 100644
index 0000000000..dfcef6c811
--- /dev/null
+++ b/dom/svg/test/test_text_dirty.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=886230
+-->
+<head>
+ <title>Test for Bug 886230</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style type="text/css">
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=886230">Mozilla Bug 886230</a>
+<p id="display">
+ <svg>
+ <mask id="m"><text id="t">x</text></mask>
+ <rect width="600" height="400" mask="url(#m)"/>
+ </svg>
+</p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+async function runTest() {
+ var svgText = document.getElementById("t");
+
+ // Dirty the frames.
+ document.getElementById("display").style.width = "700px";
+ svgText.firstChild.remove();
+
+ // Paint without flushing layout. If the test fails, we'll trigger
+ // an assertion.
+ await SpecialPowers.snapshotWindowWithOptions(window, undefined, undefined, { DRAWWINDOW_DO_NOT_FLUSH: true });
+
+ ok(true);
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", runTest);
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_text_lengthAdjust.html b/dom/svg/test/test_text_lengthAdjust.html
new file mode 100644
index 0000000000..21c2454451
--- /dev/null
+++ b/dom/svg/test/test_text_lengthAdjust.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=569722
+-->
+<head>
+ <meta charset=UTF-8>
+ <title>Test for Bug 569722</title>
+ <script 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=569722">Mozilla Bug 569722</a>
+<p id="display"></p>
+<div id="content">
+ <svg width="400" height="200">
+ <text x="0" y="100" style="font: 16px sans-serif">aaa</text>
+ </svg>
+</div>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function close(x, y, message) {
+ ok(Math.abs(x - y) < 1e-4, message);
+}
+
+function runTest() {
+ var text = document.querySelector("text");
+
+ // get the original length
+ var length = text.getComputedTextLength();
+
+ // get the original glyph positions
+ var startPositions = [],
+ endPositions = [],
+ extents = [];
+ for (let i = 0; i < 3; i++) {
+ startPositions.push(text.getStartPositionOfChar(i));
+ endPositions.push(text.getEndPositionOfChar(i));
+ extents.push(text.getExtentOfChar(i));
+ }
+
+ // widths should all be the same
+ is(extents[0].width, extents[1].width);
+ is(extents[0].width, extents[2].width);
+
+ var checkCharNumAtPosition = function(x, y, i) {
+ var p = document.querySelector("svg").createSVGPoint();
+ p.x = x;
+ p.y = y;
+ is(text.getCharNumAtPosition(p), i, "getCharNumAtPosition(" + i + ")");
+ };
+
+ var checkPositions = function(start, end, width) {
+ for (let i = 0; i < 3; i++) {
+ // check their positions
+ close(text.getStartPositionOfChar(i).x, start[i], "start position of glyph " + i);
+ close(text.getEndPositionOfChar(i).x, end[i], "end position of glyph " + i);
+ close(text.getExtentOfChar(i).x, start[i], "left edge of extent of glyph " + i);
+ close(text.getExtentOfChar(i).width, width, "width of glyph " + i);
+ checkCharNumAtPosition((start[i] + end[i]) / 2, 100, i);
+ }
+ };
+
+ var w = extents[0].width;
+
+ var doLengthAdjustSpacingTest = function() {
+ // getComputedTextLength should return the sum of the advances, and since
+ // we are just changing the positions of the glyphs, it should be the same
+ // as without a textLength="" attribute
+ close(text.getComputedTextLength(), length, "getComputedTextLength when lengthAdjust=\"spacing\"");
+
+ // expected start and end positions of the glyphs
+ var start = [0, 50 - w / 2, 100 - w];
+ var end = [w, 50 + w / 2, 100];
+ checkPositions(start, end, w);
+ };
+
+ // switch to adjust glyph positions, using the default value of lengthAdjust=""
+ text.setAttribute("textLength", "100");
+ doLengthAdjustSpacingTest();
+ // then with an explicit lengthAdjust="spacing"
+ text.setAttribute("lengthAdjust", "spacing");
+ doLengthAdjustSpacingTest();
+
+ // now test with lengthAdjust="spacingAndGlyphs"
+ text.setAttribute("lengthAdjust", "spacingAndGlyphs");
+
+ // now that each glyph is stretched, the total advance should be the textLength
+ close(text.getComputedTextLength(), 100, "getComputedTextLength when lengthAdjust=\"spacingAndGlyphs\"");
+
+ // expected start and end positions of the glyphs
+ var start = [0, 33.3333, 66.6666];
+ var end = [33.3333, 66.6666, 100];
+ checkPositions(start, end, 33.3333);
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", runTest);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_text_scaled.html b/dom/svg/test/test_text_scaled.html
new file mode 100644
index 0000000000..eb64905157
--- /dev/null
+++ b/dom/svg/test/test_text_scaled.html
@@ -0,0 +1,135 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=655877
+-->
+<head>
+ <title>Test for Bug 655877</title>
+ <script 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=655877">Mozilla Bug 655877</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg" src="text-helper-scaled.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ var doc = $("svg").contentWindow.document;
+ var text1 = doc.getElementById("text1");
+ var text2 = doc.getElementById("text2");
+
+ var charWidth = text1.getSubStringLength(0, 1);
+
+ if (navigator.userAgent.indexOf("Linux") > -1 && charWidth == 241) {
+ // Workaround for a slight difference in 'charWidth' (i.e. the width of
+ // the 'a' char) on Linux build machines after bug 1342951. The issue
+ // doesn't reproduce locally on Ubuntu 17.04 so is particularly tricky to
+ // debug.
+ charWidth = 240;
+ }
+
+ var epsilon = 0.001;
+
+ function isClose(a, b, str) {
+ ok(Math.abs(a - b) < epsilon, str + " - " + b + " should be close to " + a);
+ }
+
+ function isPointCloseX(pt1, x, y, str) {
+ isClose(pt1.x, x, str + " x");
+ is(pt1.y, y, str + " y");
+ }
+
+ function ymost(r) {
+ return r.y + r.height;
+ }
+
+ var p = text1.getStartPositionOfChar(0);
+
+ // Simple horizontal string
+
+ is(text1.getNumberOfChars(), 3, "text1 length");
+ ok(text1.getComputedTextLength() > 0, "text1 measured length");
+ is(text1.getComputedTextLength(), text1.getSubStringLength(0, 3), "text1 substring length");
+ isPointCloseX(text1.getStartPositionOfChar(0), 10, 400, "text1 char 0 start offset");
+ isPointCloseX(text1.getStartPositionOfChar(1), 10 + charWidth, 400, "text1 char 1 start offset");
+ isPointCloseX(text1.getStartPositionOfChar(2), 10 + 2 * charWidth, 400, "text1 char 2 start offset");
+ isPointCloseX(text1.getEndPositionOfChar(0), 10 + charWidth, 400, "text1 char 0 end offset");
+ isPointCloseX(text1.getEndPositionOfChar(1), 10 + 2 * charWidth, 400, "text1 char 1 end offset");
+ isPointCloseX(text1.getEndPositionOfChar(2), 10 + 3 * charWidth, 400, "text1 char 2 end offset");
+ is(text1.getExtentOfChar(0).x, 10, "text1 char 0 extent x");
+ is(text1.getExtentOfChar(0).width, text1.getSubStringLength(0, 1), "text1 char 0 extent width");
+ ok(text1.getExtentOfChar(0).y < 400, "text1 char 0 extent y");
+ ok(ymost(text1.getExtentOfChar(0)) > 400, "text1 char 0 extent height");
+ isClose(text1.getExtentOfChar(1).x, 10 + charWidth, "text1 char 1 extent x");
+ is(text1.getExtentOfChar(1).width, text1.getSubStringLength(0, 1), "text1 char 1 extent width");
+ is(text1.getExtentOfChar(1).y, text1.getExtentOfChar(0).y, "text1 char 0/1 extent y");
+ is(text1.getExtentOfChar(1).height, text1.getExtentOfChar(0).height, "text1 char 0/1 extent height");
+ is(text1.getExtentOfChar(2).x, 10 + 2 * charWidth, "text1 char 2 extent x");
+ is(text1.getExtentOfChar(2).width, text1.getSubStringLength(0, 1), "text1 char 2 extent width");
+ is(text1.getExtentOfChar(2).y, text1.getExtentOfChar(0).y, "text1 char 0/2 extent y");
+ is(text1.getExtentOfChar(2).height, text1.getExtentOfChar(0).height, "text1 char 0/2 extent height");
+ is(text1.getRotationOfChar(0), 0, "text1 char 0 rotation");
+ is(text1.getRotationOfChar(1), 0, "text1 char 0 rotation");
+ is(text1.getRotationOfChar(2), 0, "text1 char 0 rotation");
+ p.x = 10 + 0.1;
+ p.y = 400;
+ is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 left edge");
+ p.x = 10 + charWidth - 0.1;
+ is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 on right");
+ p.x = 10 - 0.1;
+ is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on left");
+ p.x = 10 + 0.1;
+ p.y = text1.getExtentOfChar(0).y - 0.1;
+ is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on top");
+ p.y = text1.getExtentOfChar(0).y + 0.1;
+ is(text1.getCharNumAtPosition(p), 0, "text1 finding char 0 top edge");
+ p.x = 10 + 3 * charWidth - 0.1;
+ is(text1.getCharNumAtPosition(p), 2, "text1 finding char 2 top edge");
+ p.x = 10 + 3 * charWidth + 0.1;
+ is(text1.getCharNumAtPosition(p), -1, "text1 finding no char on right");
+
+ // Text along a thin rectangle path
+
+ charWidth = text2.getSubStringLength(0, 1);
+
+ is(text2.getNumberOfChars(), 26, "text2 length");
+ ok(text2.getComputedTextLength() > 0, "text2 measured length");
+ is(text2.getComputedTextLength(), text2.getSubStringLength(0, 26), "text2 substring length");
+
+ // character 12 should be on the bottom side
+ is(text2.getStartPositionOfChar(12).y, 860, "text2 char 12 start offset");
+ isfuzzy(text2.getEndPositionOfChar(12).y, 860, 0.001, "text2 char 12 end offset");
+ ok(text2.getExtentOfChar(12).y < 860, "text2 char 12 extent y");
+ ok(ymost(text2.getExtentOfChar(12)) > 860, "text2 char 12 extent height");
+ isfuzzy(text2.getRotationOfChar(12), 180, 0.001, "text2 char 12 rotation");
+ p.x = text2.getExtentOfChar(12).x + 0.1;
+ p.y = ymost(text2.getExtentOfChar(12)) - 0.1;
+ is(text2.getCharNumAtPosition(p), 12, "text2 finding char 12");
+ // This next test is tricky. The glyph for character 3 may overlap from the above
+ // but character 12 wins because it's the last to render
+ p.y = text2.getExtentOfChar(12).y + 0.1;
+ is(text2.getCharNumAtPosition(p), 12, "text2 finding last rendered char");
+}
+
+function runTests() {
+ runTest();
+
+ var doc = $("svg").contentWindow.document;
+ doc.getElementById("g").setAttribute("transform", "rotate(90 200 200)");
+
+ runTest();
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", runTests);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_text_selection.html b/dom/svg/test/test_text_selection.html
new file mode 100644
index 0000000000..7160461db9
--- /dev/null
+++ b/dom/svg/test/test_text_selection.html
@@ -0,0 +1,139 @@
+<!DOCTYPE html>
+<html>
+<meta charset=utf-8>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=655877
+-->
+<head>
+ <title>Test for Bug 655877</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.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=655877">Mozilla Bug 655877</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe src="text-helper-selection.svg" width="400" height="300"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var svg, doc, win, dragstart, dragend;
+
+function drag(fromX, fromY, toX, toY, show) {
+ synthesizeMouse(doc.documentElement, fromX, fromY, { type: "mousemove" }, win);
+ synthesizeMouse(doc.documentElement, fromX, fromY, { type: "mousedown" }, win);
+ synthesizeMouse(doc.documentElement, toX, toY, { type: "mousemove" }, win);
+ synthesizeMouse(doc.documentElement, toX, toY, { type: "mouseup" }, win);
+
+ if (show) {
+ dragstart.setAttribute("cx", fromX);
+ dragstart.setAttribute("cy", fromY);
+ dragstart.setAttribute("r", "4");
+ dragend.setAttribute("cx", toX);
+ dragend.setAttribute("cy", toY);
+ dragend.setAttribute("r", "4");
+ }
+}
+
+function click(x, y) {
+ synthesizeMouse(doc.documentElement, x, y, { type: "mousemove" }, win);
+ synthesizeMouse(doc.documentElement, x, y, { type: "mousedown" }, win);
+ synthesizeMouse(doc.documentElement, x, y, { type: "mouseup" }, win);
+}
+
+function selection_is(s, text) {
+ is(win.getSelection().toString(), s, text);
+}
+
+function deselect() {
+ // Click outside text (and outside all <rect> elements>) to deselect.
+ click(15, 15);
+ selection_is("", "deselecting by clicking outside text");
+}
+
+function testSelection() {
+ svg = document.getElementsByTagName("iframe")[0];
+ doc = svg.contentDocument;
+ win = svg.contentWindow;
+ dragstart = doc.getElementById("dragstart");
+ dragend = doc.getElementById("dragend");
+
+ var text = doc.getElementsByTagName("text");
+
+ // Drag to select the entire text element.
+ drag(101, 50, 99 + text[0].getComputedTextLength(), 50);
+ selection_is("hello there", "selecting entire simple text");
+
+ // Click within the text to deselect.
+ click(101, 50);
+ selection_is("", "deselecting by clicking on text");
+
+ // Drag to select part of a text element.
+ drag(101, 50, 99 + text[0].getSubStringLength(0, 5), 50);
+ selection_is("hello", "selecting part of simple text");
+ deselect();
+
+ // Drag from left of the text to the right of the text to select it.
+ drag(90, 50, 110 + text[0].getComputedTextLength(), 50);
+ selection_is("hello there", "selecting entire simple text by dragging around it");
+ deselect();
+
+ // Drag above the text to select part of it.
+ var bbox1 = text[0].getBBox();
+ drag(101 + text[0].getSubStringLength(0, 6), bbox1.y - 10, 101 + text[0].getSubStringLength(0, 9), bbox1.y - 10);
+ selection_is("the", "selecting part of simple text by dragging above it");
+ deselect();
+
+ // Drag between the first and second texts, but closer to the first.
+ var bbox2 = text[1].getBBox();
+ var mid = (bbox1.y + bbox1.height + bbox2.y) / 2;
+ drag(101, mid - 10, 99 + text[0].getSubStringLength(0, 2), mid - 10);
+ selection_is("he", "selecting closer text above");
+ deselect();
+
+ // Drag between the first and second texts, but closer to the second.
+ drag(101, mid + 10, 99 + text[1].getSubStringLength(0, 2), mid + 10);
+ selection_is("to", "selecting closer text below");
+ deselect();
+
+ // Drag starting in the first text and ending in the second.
+ drag(101 + text[0].getSubStringLength(0, 6), 50, 99 + text[1].getSubStringLength(0, 2), 100);
+ selection_is("there to", "selecting from first to second text");
+ deselect();
+
+ // Select across positioned glyphs.
+ drag(99 + text[2].getSubStringLength(3, 1), 150, 201, 150);
+ selection_is("abcd", "selecting across positioned glyphs");
+ deselect();
+
+ // Select bidi text, from the left of the "א" to the left of the "b".
+ drag(text[3].getExtentOfChar(0).x + 1, 200, text[3].getExtentOfChar(4).x + 1, 200);
+ selection_is("בגa", "selecting bidi text");
+ deselect();
+
+ // Select transformed text.
+ drag(101, 250, 99 + text[4].getSubStringLength(0, 6) / 2, 250);
+ selection_is("squash");
+ deselect();
+
+ SimpleTest.finish();
+}
+
+function runTest() {
+ SimpleTest.executeSoon(testSelection);
+}
+
+if (/Android/.test(navigator.userAgent)) {
+ ok(true, "No need to test text selection with the mouse on Android.");
+ SimpleTest.finish();
+} else {
+ window.addEventListener("load", runTest);
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_text_update.html b/dom/svg/test/test_text_update.html
new file mode 100644
index 0000000000..d4677aaeac
--- /dev/null
+++ b/dom/svg/test/test_text_update.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>Test for Bug 876831</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=876831">Mozilla Bug 876831</a>
+<p id="display"</p>
+
+<!--
+ Test that the frame tree will be reflowed after a DOM mutation
+ and just before an SVG DOM method does its work.
+ -->
+
+<svg>
+ <text>ab</text>
+</svg>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+var text = document.querySelector("text");
+
+var length = text.getComputedTextLength();
+ok(length > 0, "text.getComputedTextLength() > 0");
+
+text.firstChild.nodeValue += "cd";
+ok(text.getComputedTextLength() > length, "text.getComputedTextLength() changes directly after DOM mutation");
+
+text.firstChild.remove();
+is(text.getComputedTextLength(), 0, "text.getComputedTextLength() == 0 after removing child");
+</script>
+</pre>
diff --git a/dom/svg/test/test_transform.xhtml b/dom/svg/test/test_transform.xhtml
new file mode 100644
index 0000000000..6a139d3580
--- /dev/null
+++ b/dom/svg/test/test_transform.xhtml
@@ -0,0 +1,190 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=512636
+-->
+<head>
+ <title>Test SVGTransform behavior</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="matrixUtils.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=512636">Mozilla Bug 512636</a>
+<p id="display"></p>
+<div id="content">
+
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="1" id="svg">
+ <g id="g" transform="translate(10, 20)"/>
+ </svg>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var g, t, m, m2;
+
+ g = $("g");
+
+ t = g.transform.baseVal.getItem(0);
+ m = t.matrix;
+
+ // test that the SVGTransform correctly reflects the translate()
+ checkTransform(t, SVGTransform.SVG_TRANSFORM_TRANSLATE,
+ {a: 1, b: 0, c: 0, d: 1, e: 10, f: 20},
+ 0, "translate");
+
+ // set the SVGTransform to be a scale()
+ t.setScale(2, 3);
+
+ // test that the matrix is live and now reflects the scale()
+ checkTransform(t, SVGTransform.SVG_TRANSFORM_SCALE,
+ {a: 2, b: 0, c: 0, d: 3, e: 0, f: 0},
+ 0, "scale");
+
+ // set the SVGTransform to be a matrix()
+ m2 = createMatrix(1, 2, 3, 4, 5, 6);
+ t.setMatrix(m2);
+
+ // check that setMatrix() took a copy of m
+ ok(m != m2, "t.matrix identity");
+
+ // test that the SVGTransform now reflects the matrix value
+ checkTransform(t, SVGTransform.SVG_TRANSFORM_MATRIX,
+ {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6},
+ 0, "matrix");
+
+ m2 = {m11: 6, m12: 5, m21: 4, m22: 3, m41: 2, m42: 1};
+ t.setMatrix(m2);
+ checkTransform(t, SVGTransform.SVG_TRANSFORM_MATRIX,
+ {a: 6, b: 5, c: 4, d: 3, e: 2, f: 1},
+ 0, "matrix");
+
+ // set the SVGTransform to be a translate() then convert to a matrix
+ t.setTranslate(0, 10);
+ m.a = 2;
+
+ // test that the SVGTransform now reflects the matrix value
+ checkTransform(t, SVGTransform.SVG_TRANSFORM_MATRIX,
+ {a: 2, b: 0, c: 0, d: 1, e: 0, f: 10},
+ 0, "matrix");
+
+ // If ty is not supplied it is assumed to be zero
+ g.setAttribute("transform", "translate(5)");
+
+ // test that the SVGTransform now reflects the matrix value
+ checkTransform(t, SVGTransform.SVG_TRANSFORM_TRANSLATE,
+ {a: 1, b: 0, c: 0, d: 1, e: 5, f: 0},
+ 0, "transform");
+
+ // set the SVGTransform to be a rotate()
+ t.setRotate(90, 0, 0);
+
+ // test that the SVGTransform now reflects the matrix value
+ checkTransform(t, SVGTransform.SVG_TRANSFORM_ROTATE,
+ {a: Math.cos(Math.PI / 2), b: Math.sin(Math.PI / 2),
+ c: -Math.sin(Math.PI / 2), d: Math.cos(Math.PI / 2),
+ e: 0, f: 0},
+ 90, "rotate");
+
+ // set the SVGTransform to be a skewX()
+ t.setSkewX(45);
+
+ // test that the SVGTransform now reflects the matrix value
+ checkTransform(t, SVGTransform.SVG_TRANSFORM_SKEWX,
+ {a: 1, b: 0,
+ c: Math.tan(Math.PI / 4), d: Math.tan(Math.PI / 4),
+ e: 0, f: 0},
+ 45, "skewX");
+
+ // set the SVGTransform to be a skewY()
+ t.setSkewY(45);
+
+ // test that the SVGTransform now reflects the matrix value
+ checkTransform(t, SVGTransform.SVG_TRANSFORM_SKEWY,
+ {a: Math.tan(Math.PI / 4), b: Math.tan(Math.PI / 4),
+ c: 0, d: 1,
+ e: 0, f: 0},
+ 45, "skewY");
+
+ // check angle is reset after changing type
+ t.setTranslate(10, 20);
+ is(t.angle, 0, "Angle not reset after changing to translate type");
+
+ // check read-only properties
+ t.angle = 40;
+ is(t.angle, 0, "t.angle should be read-only");
+ t.type = 7;
+ is(t.type, SVGTransform.SVG_TRANSFORM_TRANSLATE,
+ "t.type should be read-only");
+ t.matrix = m2;
+ ok(t.matrix != m2 && t.matrix.b == 0, "t.matrix should be read-only");
+
+ // check transform object identity after manipulation
+ ok(t === g.transform.baseVal.getItem(0),
+ "Got different transform objects after manipulation");
+ ok(t.matrix === m,
+ "Got different matrix objects after manipulation");
+
+ testCreateTransform();
+ testMatrixTransform();
+
+ SimpleTest.finish();
+}
+
+function testMatrixTransform() {
+ let svg = $("svg");
+ const epsilon = 1 / 65536;
+
+ let point = svg.createSVGPoint();
+ point.x = 5;
+ point.y = 4;
+ let matrix = createMatrix(2, 0, 0, 2, 10, 10);
+ let result = point.matrixTransform(matrix);
+ let expected = DOMPoint.fromPoint(point).matrixTransform(matrix);
+ isfuzzy(result.x, expected.x, epsilon, "matrix transformation x");
+ isfuzzy(result.y, expected.y, epsilon, "matrix transformation y");
+
+ svg.currentTranslate.x = 5;
+ svg.currentTranslate.y = 4;
+ result = svg.currentTranslate.matrixTransform(matrix);
+ isfuzzy(result.x, expected.x, epsilon, "svg matrix transformation x");
+ isfuzzy(result.y, expected.y, epsilon, "svg matrix transformation y");
+ svg.currentTranslate.x = 0;
+ svg.currentTranslate.y = 0;
+}
+
+function testCreateTransform() {
+ let svg = $("svg");
+ let t = svg.createSVGTransform();
+ ok(t != svg.createSVGTransform(),
+ "Got identical objects when creating new transform");
+ checkTransform(t, SVGTransform.SVG_TRANSFORM_MATRIX,
+ createMatrix(1, 0, 0, 1, 0, 0), 0, "createSVGTransform");
+
+ let m = createMatrix(1, 2, 3, 4, 5, 6);
+ t = svg.createSVGTransformFromMatrix(m);
+ ok(t.matrix != m,
+ "createSVGTransformFromMatrix should copy matrix not adopt it");
+ m.a = 7; // Just to be sure, changing m should not affect t
+ checkTransform(t, SVGTransform.SVG_TRANSFORM_MATRIX,
+ {a: 1, b: 2, c: 3, d: 4, e: 5, f: 6},
+ 0, "createSVGTransformFromMatrix");
+}
+
+function checkTransform(transform, type, matrix, angle, forWhat) {
+ roughCmpMatrix(transform.matrix, matrix, forWhat);
+ is(transform.type, type, `transform.type for ${forWhat}`);
+ is(transform.angle, angle, `transform.angle for ${forWhat}`);
+}
+
+window.addEventListener("load", run);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_transformParsing.html b/dom/svg/test/test_transformParsing.html
new file mode 100644
index 0000000000..bd6bb61f42
--- /dev/null
+++ b/dom/svg/test/test_transformParsing.html
@@ -0,0 +1,103 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=946529
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test transform parsing</title>
+ <script 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=946529">Mozilla Bug 946529</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <svg width="100%" height="1" id="svg">
+ <g id="g"/>
+ </svg>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+// Test cases
+checkParseOk("", [ ]);
+checkParseOk("matrix(-.7235 .6903 .6903 .7235 -2050 1.14e4)",
+ [ { type: "matrix", a: -0.7235, b: 0.6903, c: 0.6903,
+ d: 0.7235, e: -2050, f: 11400 } ]);
+checkParseOk("matrix(0e0 1e0 1e1 1e-1 1E+2 -.1e1)",
+ [ { type: "matrix", a: 0, b: 1, c: 10,
+ d: 0.1, e: 100, f: -1 } ]);
+checkParseOk("matrix(-0e-0 1e+0 0e-5 1e-10 12.3e+4 .12e2)",
+ [ { type: "matrix", a: 0, b: 1, c: 0,
+ d: 0.0000000001, e: 123000, f: 12 } ]);
+
+// Fail cases
+checkParseFail("matrix(1e+ 0 0 0 0 0)");
+checkParseFail("matrix(e2 0 0 0 0 0)");
+checkParseFail("matrix(1 e2 0 0 0 0 0)");
+checkParseFail("matrix(1e 2 0 0 0 0 0)");
+checkParseFail("matrix(1e+-2 0 0 0 0 0)");
+checkParseFail("matrix(1e 0 0 0 0 0)");
+checkParseFail("matrix(1e1.1 0 0 0 0 0)");
+checkParseFail("scale(2) matrix(1e1.1 0 0 0 0 0)");
+
+function checkParseOk(spec, expected) {
+ var g = document.getElementById("g");
+
+ // Clear previous value
+ g.removeAttribute("transform");
+
+ g.setAttribute("transform", spec);
+
+ // Check length
+ var transformList = g.transform.baseVal;
+ is(transformList.numberOfItems, expected.length, spec + " - length");
+ if (transformList.numberOfItems != expected.length)
+ return;
+
+ // Check each item
+ for (var i = 0; i < transformList.numberOfItems; i++) {
+ checkTransform(transformList.getItem(i), expected[i], spec, i);
+ }
+}
+
+function checkTransform(transform, expected, spec, index) {
+ var typeMapping = {
+ "unknown": SVGTransform.SVG_TRANSFORM_UNKNOWN,
+ "matrix": SVGTransform.SVG_TRANSFORM_MATRIX,
+ "translate": SVGTransform.SVG_TRANSFORM_TRANSLATE,
+ "scale": SVGTransform.SVG_TRANSFORM_SCALE,
+ "rotate": SVGTransform.SVG_TRANSFORM_ROTATE,
+ "skewx": SVGTransform.SVG_TRANSFORM_SKEWX,
+ "skewy": SVGTransform.SVG_TRANSFORM_SKEWY,
+ };
+ var name = "Item " + index + " of '" + spec + "'";
+
+ // Compare type
+ if (typeof expected.type != "undefined") {
+ is(transform.type, typeMapping[expected.type], name + " - transform type");
+ }
+
+ // Compare angle
+ if (typeof expected.angle != "undefined") {
+ is(transform.angle, expected.angle, name + " - angle");
+ }
+
+ // Compare matrix values (roughly)
+ ["a", "b", "c", "d", "e", "f"].forEach(function(item) {
+ var actual = transform.matrix[item];
+ var msg = name + " - matrix:" + item;
+ const tolerance = 1 / 65535;
+ ok(Math.abs(actual - expected[item]) < tolerance,
+ msg + " - got " + actual + ", expected " + expected[item]);
+ });
+}
+
+function checkParseFail(spec) {
+ checkParseOk(spec, []);
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_use_with_hsts.html b/dom/svg/test/test_use_with_hsts.html
new file mode 100644
index 0000000000..2c82d93569
--- /dev/null
+++ b/dom/svg/test/test_use_with_hsts.html
@@ -0,0 +1,132 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1247733
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1247733</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.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=1247733">Mozilla Bug 1247733</a>
+<p id="display">
+ <iframe id="myIframe"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test"></pre>
+<script type="application/javascript">
+ /** Test for Bug 1247733 **/
+
+ /**
+ * This test ensures that we render the SVG 'use' element correctly, in
+ * pages that have been upgraded from HTTP to HTTPS using strict transport
+ * security (HSTS)
+ *
+ * Specifically:
+ * (1) We load a file using HTTPS, in an iframe. The file gets sent
+ * with a Strict-Transport-Security flag.
+ * (2) We load the same file again, but now over HTTP (which should get
+ * upgraded to HTTPS, since we received the Strict-Transport-Security
+ * flag during the first load).
+ * (3) After each of the above loads, we take a snapshot of the iframe
+ * and ensure that it renders as fully lime (which the 'use' element
+ * is responsible for). If the 'use' element fails to render, the iframe
+ * will be fully red, and we'll fail an "assertSnapshots" check.
+ */
+ SimpleTest.waitForExplicitFinish();
+
+ const iframe = document.getElementById("myIframe");
+ const iframeWin = iframe.contentWindow;
+
+ // URI for our testcase with 'use' element, via HTTP and HTTPS:
+ const insecureURI = "http://example.com/tests/dom/svg/test/use-with-hsts-helper.html";
+ const secureURI = "https://example.com/tests/dom/svg/test/use-with-hsts-helper.html";
+
+ // Bookkeeping to be sure receiveMessage is called as many times as we expect:
+ var numPostMessageCalls = 0;
+ const expectedNumPostMessageCalls = 2; // (We load the helper file twice.)
+
+ // Helper function, called via postMessage, to check iframe's actual location:
+ function receiveMessage(event) {
+ is(event.data, secureURI, "iframe should end up viewing secure URI");
+ numPostMessageCalls++;
+ }
+
+ // Convenience helper which makes |iframe| load the given |uri|. Returns
+ // a promise that resolves when the load completes. This makes it handy to
+ // use with 'await', to avoid onload callback hell.
+ async function LoadIframeAsync(uri) {
+ return new Promise(resolve => {
+ iframe.addEventListener("load", resolve, {once: true});
+ // Kick off the requested load:
+ iframe.src = uri;
+ });
+ }
+
+ // MAIN TEST CODE BEGINS HERE.
+ async function runTest() {
+ // Capture a snapshot with nothing in the iframe, so we can do a
+ // sanity-check not-equal comparison against our reference case, to be
+ // sure we're rendering anything at all:
+ let blankSnapshot = await snapshotWindow(iframeWin);
+
+ // Load & snapshot a reference case (fully lime):
+ await LoadIframeAsync("data:text/html,<body style='background:lime'>");
+ let refSnapshot = await snapshotWindow(iframeWin);
+
+ // Ensure reference snapshot looks different from blank snapshot:
+ assertSnapshots(refSnapshot, blankSnapshot,
+ false /* not equal*/, null /* no fuzz*/,
+ "refSnapshot", "blankSnapshot");
+
+ // OK, assuming we've got a valid refSnapshot, we can now proceed to
+ // capture test screenshots.
+
+ // Register a postMessage handler, so that iframe can report its location:
+ window.addEventListener("message", receiveMessage);
+
+ // Load & snapshot secure (HTTPS) version of testcase, & check against ref:
+ await LoadIframeAsync(secureURI);
+ let secureSnapshot = await snapshotWindow(iframeWin);
+ assertSnapshots(secureSnapshot, refSnapshot,
+ true /* equal*/, null /* no fuzz*/,
+ "secureSnapshot", "refSnapshot");
+
+ // Load insecure (HTTP) version of testcase (which should get
+ // automatically upgraded to secure (HTTPS) under the hood):
+ await LoadIframeAsync(insecureURI);
+
+ // Double-check that iframe is really pointed at insecure URI, to be sure
+ // we're actually exercising HSTS. (Note that receiveMessage() will make
+ // sure it's been upgraded to a secure HTTPS URI under the hood.)
+ is(iframe.src, insecureURI,
+ "test should've attempted to load insecure HTTP URI, to exercise HSTS");
+
+ // Capture snapshot of iframe showing upgraded-to-HTTPS version of testcase:
+ let upgradedSnapshot = await snapshotWindow(iframeWin);
+ assertSnapshots(upgradedSnapshot, refSnapshot,
+ true /* equal*/, null /* no fuzz*/,
+ "upgradedSnapshot", "refSnapshot");
+
+ // Check that the iframe did actually invoke our postMessage handler (which
+ // is where we verify that the HSTS upgrade actually happened):
+ is(numPostMessageCalls, expectedNumPostMessageCalls,
+ "didn't receive as many messages from child iframe as expected");
+
+ // We're done! Clear the STS headers that we set, and finish.
+ SpecialPowers.cleanUpSTSData("http://example.com");
+ SimpleTest.finish();
+ }
+
+ SpecialPowers.pushPrefEnv(
+ { 'set': [["security.mixed_content.block_active_content", false]] },
+ function() { runTest(); }
+ );
+</script>
+</body>
+</html>
diff --git a/dom/svg/test/test_valueAsString.xhtml b/dom/svg/test/test_valueAsString.xhtml
new file mode 100644
index 0000000000..ab22ae146c
--- /dev/null
+++ b/dom/svg/test/test_valueAsString.xhtml
@@ -0,0 +1,64 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=539697
+-->
+<head>
+ <title>Test valueAsString behavior</title>
+ <script 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=539697">Mozilla Bug 539697</a>
+<p id="display"></p>
+<div id="content">
+
+ <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="1" id="svg">
+ <circle id='c' r='1em' display='none'/>
+ <marker id='m' orient='20rad' display='none'/>
+ </svg>
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var c = document.getElementById("c");
+ var m = document.getElementById("m");
+
+ is(SVGLength.SVG_LENGTHTYPE_EMS, c.r.baseVal.unitType, "unexpected units");
+ c.r.baseVal.valueAsString = "2px";
+ is(SVGLength.SVG_LENGTHTYPE_PX, c.r.baseVal.unitType, "unexpected units");
+
+ try {
+ c.r.baseVal.valueAsString = "rubbish";
+ ok(false, "setting a length to rubbish should fail");
+ } catch (e) {
+ is(e.name, "SyntaxError", "syntax error expected");
+ is(e.code, DOMException.SYNTAX_ERR, "syntax error expected");
+ }
+
+ is(SVGAngle.SVG_ANGLETYPE_RAD, m.orientAngle.baseVal.unitType, "unexpected units");
+ m.orientAngle.baseVal.valueAsString = "2grad";
+ is(SVGAngle.SVG_ANGLETYPE_GRAD, m.orientAngle.baseVal.unitType, "unexpected units");
+
+ try {
+ m.orientAngle.baseVal.valueAsString = "rubbish";
+ ok(false, "setting an angle to rubbish should fail");
+ } catch (e) {
+ is(e.name, "SyntaxError", "syntax error expected");
+ is(e.code, DOMException.SYNTAX_ERR, "syntax error expected");
+ }
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", run);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_valueLeaks.xhtml b/dom/svg/test/test_valueLeaks.xhtml
new file mode 100644
index 0000000000..ce816efc1c
--- /dev/null
+++ b/dom/svg/test/test_valueLeaks.xhtml
@@ -0,0 +1,84 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=467671
+-->
+<head>
+ <title>Test for Bug 467671</title>
+ <script 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=467671">Mozilla Bug 467671</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 467671 **/
+
+function storeSVGPropertyAsExpando(localName, prop) {
+ var elem = document.createElementNS("http://www.w3.org/2000/svg", localName);
+
+ elem.addEventListener("click", function() {});
+
+ var propVal = elem[prop];
+ Object.prototype.valueOf[prop + "_expando"] = propVal;
+ if (propVal instanceof SVGAnimatedAngle || propVal instanceof SVGAnimatedLength ||
+ propVal instanceof SVGAnimatedRect || propVal instanceof SVGAnimatedPreserveAspectRatio) {
+ Object.prototype.valueOf[prop + "_baseVal_expando"] = propVal.baseVal;
+ Object.prototype.valueOf[prop + "_animVal_expando"] = propVal.animVal;
+ }
+}
+
+// class
+storeSVGPropertyAsExpando("marker", "class");
+
+// angle
+storeSVGPropertyAsExpando("marker", "orientAngle");
+
+// viewBox
+storeSVGPropertyAsExpando("marker", "viewBox");
+
+// preserveAspectRatio
+storeSVGPropertyAsExpando("marker", "preserveAspectRatio");
+
+// boolean
+storeSVGPropertyAsExpando("feConvolveMatrix", "preserveAlpha");
+
+// enum
+storeSVGPropertyAsExpando("feConvolveMatrix", "edgeMode");
+
+// special marker enum
+storeSVGPropertyAsExpando("marker", "orientType");
+
+// integer
+storeSVGPropertyAsExpando("feConvolveMatrix", "orderX");
+
+// length
+storeSVGPropertyAsExpando("feConvolveMatrix", "x");
+
+// number
+storeSVGPropertyAsExpando("feConvolveMatrix", "divisor");
+
+// string
+storeSVGPropertyAsExpando("feConvolveMatrix", "in1");
+
+var elem1 = document.createElementNS("http://www.w3.org/2000/svg", "switch");
+var elem2 = document.createElementNS("http://www.w3.org/2000/svg", "rect");
+elem1.appendChild(elem2);
+document.getElementById("content").appendChild(elem1);
+
+elem2.addEventListener("click", function() {});
+
+Object.prototype.valueOf.expando = elem1;
+
+ok(true, "SVG shouldn't leak.");
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_viewBox.html b/dom/svg/test/test_viewBox.html
new file mode 100644
index 0000000000..d09f5110ef
--- /dev/null
+++ b/dom/svg/test/test_viewBox.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1396642
+-->
+<head>
+ <title>Test for Bug 1396642</title>
+ <script 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=1396642">Mozilla Bug 1396642</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<div id="svg"></div>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ var testsElement = $("svg");
+
+ // Turn for instance `2.3` into `230` (px). Round to avoid floating point
+ // issues.
+ const scale = (number) => Math.round(100 * number);
+
+ const widths = [2, 2.3, 2.5, 2.8];
+ const heights = [3, 3.3, 3.5, 3.8];
+
+ for (const width of widths) {
+ for (const height of heights) {
+ const variations = [
+ {width, height},
+ {width: "auto", height},
+ {width, height: "auto"},
+ {width: "auto", height: "auto"},
+ ];
+
+ for (const variation of variations) {
+ const svgWrapperElement = document.createElement("div");
+ svgWrapperElement.style.width =
+ variation.width === "auto" && variation.height === "auto"
+ ? `${scale(width)}px`
+ : "auto";
+
+ const svgElement = document.createElementNS("http://www.w3.org/2000/svg", "svg");
+ svgElement.setAttribute("viewBox", `0 0 ${width} ${height}`);
+ svgElement.style.width =
+ typeof variation.width === "number"
+ ? `${scale(variation.width)}px`
+ : variation.width;
+ svgElement.style.height =
+ typeof variation.height === "number"
+ ? `${scale(variation.height)}px`
+ : variation.height;
+
+ svgWrapperElement.appendChild(svgElement);
+
+ testsElement.appendChild(svgWrapperElement);
+
+ const rect = svgElement.getBoundingClientRect();
+ const actual = {
+ width: Math.round(rect.width),
+ height: Math.round(rect.height),
+ };
+ const expected = {
+ width: scale(width),
+ height: scale(height),
+ };
+
+ isfuzzy(expected.width, actual.width, 0.001, "checking width");
+ isfuzzy(expected.height, actual.height, 0.001, "checking height");
+ }
+ }
+ }
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", runTest);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/test_viewport.html b/dom/svg/test/test_viewport.html
new file mode 100644
index 0000000000..66f8926fe0
--- /dev/null
+++ b/dom/svg/test/test_viewport.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=483389
+-->
+<head>
+ <title>Test for Bug 483389</title>
+ <script 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=483389">Mozilla Bug 483389</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+
+<iframe id="svg" src="viewport-helper.svg"></iframe>
+
+<pre id="test">
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ var doc = $("svg").contentWindow.document;
+
+ 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");
+
+ <!-- ownerSVGElement -->
+ is(root.ownerSVGElement, null, "root.ownerSVGElement");
+ is(inner.ownerSVGElement, root, "inner.ownerSVGElement");
+ is(g1.ownerSVGElement, inner, "g1.ownerSVGElement");
+ is(outer.ownerSVGElement, null, "outer.ownerSVGElement");
+ is(g2.ownerSVGElement, outer, "g2.ownerSVGElement");
+ is(g3.ownerSVGElement, null, "g3.ownerSVGElement");
+ is(symbolRect.ownerSVGElement, root, "symbolRect.ownerSVGElement");
+
+ <!-- viewportElement -->
+ is(root.viewportElement, null, "root.viewportElement");
+ is(inner.viewportElement, root, "inner.viewportElement");
+ is(g1.viewportElement, inner, "g1.viewportElement");
+ is(outer.viewportElement, null, "outer.viewportElement");
+ is(g2.viewportElement, outer, "g2.viewportElement");
+ is(g3.viewportElement, null, "g3.viewportElement");
+ is(symbolRect.viewportElement, sym, "symbolRect.viewportElement");
+
+ SimpleTest.finish();
+}
+
+window.addEventListener("load", runTest);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/svg/test/text-helper-scaled.svg b/dom/svg/test/text-helper-scaled.svg
new file mode 100644
index 0000000000..3d534e3fd3
--- /dev/null
+++ b/dom/svg/test/text-helper-scaled.svg
@@ -0,0 +1,8 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="3000" height="2000">
+ <g id="g" style="font: 400px monospace">
+ <text id="text1" x="10" y="400">abc</text>
+
+ <path id="MyPath" d="M 5 800 h 2000 v 60 h -2000 z" stroke="red" fill="none"/>
+ <text id="text2"><textPath xlink:href="#MyPath">abcdefghijklmnopqrstuvwxyz</textPath></text>
+ </g>
+</svg>
diff --git a/dom/svg/test/text-helper-selection.svg b/dom/svg/test/text-helper-selection.svg
new file mode 100644
index 0000000000..df84a19ac4
--- /dev/null
+++ b/dom/svg/test/text-helper-selection.svg
@@ -0,0 +1,23 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="400" height="300"
+ style="font: 24px monospace">
+
+ <!-- We need these two rects so that getBoundingClientRect of the <svg> does
+ not just return the region covered by the <text>, which would result in
+ the synthesizeMouse calls using the wrong positions. We don't use one
+ big rect because that could interfere with text selection when dragging
+ outside the bounds of text elements. -->
+ <rect width="10" height="10" fill="white"/>
+ <rect x="350" y="250" width="10" height="10" fill="white"/>
+
+ <text x="100" y="50">hello there</text>
+ <text x="100" y="100">to you all!</text>
+ <text x="200" y="150">abc<tspan x="100" dy="10 -10">def</tspan></text>
+ <text x="100" y="200">אבגabc</text>
+ <text x="100" y="250" transform="scale(0.5,1)translate(100)">squashed</text>
+
+ <!-- These two circles are just used for debugging the test; passing true
+ as the last argument to drag() will place these circles at the drag
+ start and end points. -->
+ <circle id="dragstart" fill="blue"/>
+ <circle id="dragend" fill="red"/>
+</svg>
diff --git a/dom/svg/test/text-helper.svg b/dom/svg/test/text-helper.svg
new file mode 100644
index 0000000000..1bdff86f26
--- /dev/null
+++ b/dom/svg/test/text-helper.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="750"
+ xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
+ <style type="text/css">
+text { font: 20px monospace; }
+ </style>
+
+<g id="g">
+ <text id="text1" x="5" y="25">abc</text>
+
+ <path id="MyPath2" d="M 100 125 L 100 200" stroke="red" fill="none"/>
+ <text id="text2"><textPath xlink:href="#MyPath2">abc</textPath></text>
+
+ <path id="MyPath" d="M 5 250 L 105 250 L 105 253 L 5 253 z" stroke="red" fill="none"/>
+ <text id="text3"><textPath xlink:href="#MyPath">abcdefghijklmnopqrstuvwxyz</textPath></text>
+
+ <text display="none" id="text4" x="5" y="25">abc</text>
+</g>
+</svg>
diff --git a/dom/svg/test/use-with-hsts-helper.html b/dom/svg/test/use-with-hsts-helper.html
new file mode 100644
index 0000000000..409dade7c6
--- /dev/null
+++ b/dom/svg/test/use-with-hsts-helper.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script>
+ // Notify parent of our final URI:
+ window.parent.postMessage(window.location.href, "*");
+ </script>
+ <style>
+ html, body {
+ margin: 0;
+ height: 100%;
+ }
+ svg {
+ display: block;
+ height: 100%;
+ }
+ </style>
+</head>
+<body>
+ <svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ version="1.1">
+ <defs>
+ <rect id="limeRect" width="100%" height="100%" fill="lime"/>
+ </defs>
+ <rect width="100%" height="100%" fill="red"/>
+ <use xlink:href="#limeRect"/>
+ </svg>
+</body>
+</html>
diff --git a/dom/svg/test/use-with-hsts-helper.html^headers^ b/dom/svg/test/use-with-hsts-helper.html^headers^
new file mode 100644
index 0000000000..a46bf65bd9
--- /dev/null
+++ b/dom/svg/test/use-with-hsts-helper.html^headers^
@@ -0,0 +1,2 @@
+Cache-Control: no-cache
+Strict-Transport-Security: max-age=60
diff --git a/dom/svg/test/viewport-helper.svg b/dom/svg/test/viewport-helper.svg
new file mode 100644
index 0000000000..c553b43a1f
--- /dev/null
+++ b/dom/svg/test/viewport-helper.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="750"
+ xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
+
+ <defs>
+ <symbol id="sym">
+ <rect width="0" height="0" id="symbolRect"/>
+ </symbol>
+ </defs>
+ <svg id="inner">
+ <g id="g1">
+ </g>
+ </svg>
+ <foreignObject>
+ <svg id="outer">
+ <g id="g2">
+ </g>
+ </svg>
+ </foreignObject>
+ <!-- something invalid -->
+ <foreignObject>
+ <g id="g3">
+ </g>
+ </foreignObject>
+ <use xlink:href="#sym" id="use"/>
+</svg>