new file mode 100644
--- /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>
new file mode 100644
--- /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>
new file mode 100644
--- /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>
--- a/dom/svg/test/mochitest.ini
+++ b/dom/svg/test/mochitest.ini
@@ -1,15 +1,18 @@
[DEFAULT]
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
--- a/dom/svg/test/test_a_href_01.xhtml
+++ b/dom/svg/test/test_a_href_01.xhtml
@@ -1,27 +1,29 @@
<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</title>
+ <title>Test that activating SVG 'a' elements navigate to their xlink:href or href</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" 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 = 4;
+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;
}
@@ -46,16 +48,22 @@ 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() {
@@ -75,11 +83,14 @@ 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>
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -37773,16 +37773,40 @@
}
],
"html/semantics/forms/the-form-element/form-submission-sandbox.html": [
{
"path": "html/semantics/forms/the-form-element/form-submission-sandbox.html",
"url": "/html/semantics/forms/the-form-element/form-submission-sandbox.html"
}
],
+ "svg/linking/scripted/href-animate-element.html": [
+ {
+ "path": "svg/linking/scripted/href-animate-element.html",
+ "url": "/svg/linking/scripted/href-animate-element.html"
+ }
+ ],
+ "svg/linking/scripted/href-mpath-element.html": [
+ {
+ "path": "svg/linking/scripted/href-mpath-element.html",
+ "url": "/svg/linking/scripted/href-mpath-element.html"
+ }
+ ],
+ "svg/linking/scripted/href-script-element-markup.html": [
+ {
+ "path": "svg/linking/scripted/href-script-element-markup.html",
+ "url": "/svg/linking/scripted/href-script-element-markup.html"
+ }
+ ],
+ "svg/linking/scripted/href-script-element.html": [
+ {
+ "path": "svg/linking/scripted/href-script-element.html",
+ "url": "/svg/linking/scripted/href-script-element.html"
+ }
+ ],
"web-animations/interfaces/Animation/effect.html": [
{
"path": "web-animations/interfaces/Animation/effect.html",
"url": "/web-animations/interfaces/Animation/effect.html"
}
],
"web-animations/timing-model/animation-effects/phases-and-states.html": [
{
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/linking/scripted/href-animate-element.html
@@ -0,0 +1,138 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Href - animate element tests</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='testcommon.js'></script>
+<body>
+<div id='log'></div>
+<svg id='svg' width='100' height='100' viewBox='0 0 100 100'></svg>
+<script>
+'use strict';
+
+promise_test(function(t) {
+ var svg = document.getElementById('svg');
+ var rect1 = createSVGElement(t, 'rect', svg,
+ { 'width': '10px',
+ 'height': '10px',
+ 'id': 'rect1' });
+ var rect2 = createSVGElement(t, 'rect', svg,
+ { 'width': '10px',
+ 'height': '10px',
+ 'id': 'rect2' });
+ var animate = createSVGElement(t, 'animate', svg,
+ { 'attributeName': 'x',
+ 'from': '0',
+ 'to': '100',
+ 'dur': '10s' });
+ animate.setAttribute('href', '#rect1');
+ animate.setAttributeNS(XLINKNS, 'xlink:href', '#rect2');
+ assert_equals(animate.targetElement, rect1);
+
+ return waitEvent(animate, 'begin').then(function() {
+ svg.pauseAnimations();
+ svg.setCurrentTime(5);
+ assert_equals(rect1.x.animVal.value, 50);
+ assert_equals(rect2.x.animVal.value, 0);
+ });
+}, 'Test for animate element when setting both href and xlink:href');
+
+promise_test(function(t) {
+ var svg = document.getElementById('svg');
+ var rect1 = createSVGElement(t, 'rect', svg,
+ { 'width': '10px',
+ 'height': '10px',
+ 'id': 'rect1' });
+ var rect2 = createSVGElement(t, 'rect', svg,
+ { 'width': '10px',
+ 'height': '10px',
+ 'id': 'rect2' });
+ var transform = createSVGElement(t, 'animateTransform', svg,
+ { 'attributeName': 'transform',
+ 'type': 'translate',
+ 'from': '0',
+ 'to': '100',
+ 'dur': '10s' });
+
+ transform.setAttribute('href', '#rect1');
+ transform.setAttributeNS(XLINKNS, 'xlink:href', '#rect2');
+ assert_equals(transform.targetElement, rect1);
+
+ return waitEvent(transform, 'begin').then(function() {
+ svg.pauseAnimations();
+ svg.setCurrentTime(5);
+ assert_equals(rect1.getCTM().e, 50);
+ assert_equals(rect2.getCTM().e, 0);
+ });
+}, 'Test for animateTransform element when setting both href and xlink:href');
+
+promise_test(function(t) {
+ var svg = document.getElementById('svg');
+ var circle1 = createSVGElement(t, 'circle', svg,
+ { 'cx': '50',
+ 'cy': '50',
+ 'r': '40',
+ 'id': 'circle1' });
+ var circle2 = createSVGElement(t, 'circle', svg,
+ { 'cx': '50',
+ 'cy': '50',
+ 'r': '40',
+ 'id': 'circle2' });
+ var animate = createSVGElement(t, 'animate', svg,
+ { 'attributeName': 'cx',
+ 'from': '50',
+ 'to': '150',
+ 'dur': '10s' });
+ animate.setAttribute('href', '#circle1');
+ animate.setAttributeNS(XLINKNS, 'xlink:href', '#circle2');
+ assert_equals(animate.targetElement, circle1);
+
+ return waitEvent(animate, 'begin').then(function() {
+ svg.pauseAnimations();
+ svg.setCurrentTime(5);
+ assert_equals(circle1.cx.animVal.value, 100);
+ assert_equals(circle2.cx.animVal.value, 50);
+
+ animate.removeAttribute('href');
+ assert_equals(animate.targetElement, circle2);
+ assert_equals(circle1.cx.animVal.value, 50);
+ assert_equals(circle2.cx.animVal.value, 100);
+ });
+}, 'Test for animate element when removing href but we still have xlink:href');
+
+promise_test(function(t) {
+ var svg = document.getElementById('svg');
+ var circle1 = createSVGElement(t, 'circle', svg,
+ { 'cx': '50',
+ 'cy': '50',
+ 'r': '40',
+ 'id': 'circle1' });
+ var circle2 = createSVGElement(t, 'circle', svg,
+ { 'cx': '50',
+ 'cy': '50',
+ 'r': '40',
+ 'id': 'circle2' });
+ var animate = createSVGElement(t, 'animate', svg,
+ { 'attributeName': 'cx',
+ 'from': '50',
+ 'to': '150',
+ 'dur': '10s' });
+ animate.setAttribute('href', '#circle1');
+ animate.setAttributeNS(XLINKNS, 'xlink:href', '#circle2');
+ assert_equals(animate.targetElement, circle1);
+
+ return waitEvent(animate, 'begin').then(function() {
+ svg.pauseAnimations();
+ svg.setCurrentTime(5);
+ assert_equals(circle1.cx.animVal.value, 100);
+ assert_equals(circle2.cx.animVal.value, 50);
+
+ animate.removeAttributeNS(XLINKNS, 'href');
+ assert_equals(animate.targetElement, circle1);
+ assert_equals(circle1.cx.animVal.value, 100);
+ assert_equals(circle2.cx.animVal.value, 50);
+ });
+}, 'Test for animate element when removing xlink:href but we still have href');
+
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/linking/scripted/href-mpath-element.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Href - mpath element tests</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='testcommon.js'></script>
+<body>
+<div id='log'></div>
+<svg id='svg' width='100' height='100' viewBox='0 0 100 100'></svg>
+<script>
+'use strict';
+
+promise_test(function(t) {
+ var svg = document.getElementById('svg');
+ var path1 = createSVGElement(t, 'path', svg,
+ { 'id': 'MyPath1', 'd': 'M 0,0 L 100,0' });
+ var path2 = createSVGElement(t, 'path', svg,
+ { 'id': 'MyPath2', 'd': 'M 0,0 L 0,100' });
+ var rect = createSVGElement(t, 'rect', svg,
+ { 'width': '10px', 'height': '10px' });
+ var animateMotion = createSVGElement(t, 'animateMotion', rect,
+ { 'dur': '10s' });
+ var mpath = createSVGElement(t, 'mpath', animateMotion);
+ mpath.setAttribute('href', '#MyPath1');
+ mpath.setAttributeNS(XLINKNS, 'xlink:href', '#MyPath2');
+ assert_equals(mpath.href.baseVal, '#MyPath1');
+
+ return waitEvent(animateMotion, 'begin').then(function() {
+ svg.pauseAnimations();
+ svg.setCurrentTime(1);
+ var ctm = rect.getCTM();
+ assert_equals(ctm.e, 10);
+ assert_equals(ctm.f, 0);
+
+ svg.setCurrentTime(5);
+ ctm = rect.getCTM();
+ assert_equals(ctm.e, 50);
+ assert_equals(ctm.f, 0);
+ });
+}, 'Test for mpath when setting both href and xlink:href');
+
+promise_test(function(t) {
+ var svg = document.getElementById('svg');
+ var path1 = createSVGElement(t, 'path', svg,
+ { 'id': 'MyPath1', 'd': 'M 0,0 L 100,0' });
+ var path2 = createSVGElement(t, 'path', svg,
+ { 'id': 'MyPath2', 'd': 'M 0,0 L 0,100' });
+ var rect = createSVGElement(t, 'rect', svg,
+ { 'width': '10px', 'height': '10px' });
+ var animateMotion = createSVGElement(t, 'animateMotion', rect,
+ { 'dur': '10s' });
+ var mpath = createSVGElement(t, 'mpath', animateMotion);
+ mpath.setAttribute('href', '#MyPath1');
+ mpath.setAttributeNS(XLINKNS, 'xlink:href', '#MyPath2');
+
+ return waitEvent(animateMotion, 'begin').then(function() {
+ svg.pauseAnimations();
+ svg.setCurrentTime(5);
+ var ctm = rect.getCTM();
+ assert_equals(ctm.e, 50);
+ assert_equals(ctm.f, 0);
+
+ mpath.removeAttribute('href');
+ assert_equals(mpath.href.baseVal, '#MyPath2');
+
+ ctm = rect.getCTM();
+ assert_equals(ctm.e, 0);
+ assert_equals(ctm.f, 50);
+ });
+}, 'Test for mpath when removing href but we still have xlink:href');
+
+promise_test(function(t) {
+ var svg = document.getElementById('svg');
+ var path1 = createSVGElement(t, 'path', svg,
+ { 'id': 'MyPath1', 'd': 'M 0,0 L 100,0' });
+ var path2 = createSVGElement(t, 'path', svg,
+ { 'id': 'MyPath2', 'd': 'M 0,0 L 0,100' });
+ var rect = createSVGElement(t, 'rect', svg,
+ { 'width': '10px', 'height': '10px' });
+ var animateMotion = createSVGElement(t, 'animateMotion', rect,
+ { 'dur': '10s' });
+ var mpath = createSVGElement(t, 'mpath', animateMotion);
+ mpath.setAttribute('href', '#MyPath1');
+ mpath.setAttributeNS(XLINKNS, 'xlink:href', '#MyPath2');
+
+ return waitEvent(animateMotion, 'begin').then(function() {
+ svg.pauseAnimations();
+ svg.setCurrentTime(5);
+ var ctm = rect.getCTM();
+ assert_equals(ctm.e, 50);
+ assert_equals(ctm.f, 0);
+
+ mpath.removeAttributeNS(XLINKNS, 'href');
+ assert_equals(mpath.href.baseVal, '#MyPath1');
+
+ ctm = rect.getCTM();
+ assert_equals(ctm.e, 50);
+ assert_equals(ctm.f, 0);
+ });
+}, 'Test for mpath when removing xlink:href but we still have href');
+
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/linking/scripted/href-script-element-markup.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Href - script element tests on markup</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='testcommon.js'></script>
+<body>
+<div id='log'></div>
+<svg id='svg' width='100' height='100' viewBox='0 0 100 100'>
+ <script id='script' href='testScripts/externalScript1.js'
+ xlink:href='testScripts/externalScript2.js'></script>
+</svg>
+<script>
+'use strict';
+
+// Use an independent test file for markup testing because it may affect other
+// tests.
+
+test(function(t) {
+ var script = document.getElementById('script');
+ assert_equals(script.href.baseVal, 'testScripts/externalScript1.js');
+ assert_equals(loadedScript(), 'externalScript1');
+}, 'Test for loading external script by markup when setting both href and ' +
+ 'xlink:href');
+
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/linking/scripted/href-script-element.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Href - script element tests</title>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<script src='testcommon.js'></script>
+<body>
+<div id='log'></div>
+<svg id='svg' width='100' height='100' viewBox='0 0 100 100'>
+</svg>
+<script>
+'use strict';
+
+// Note:
+// The order of these tests shouldn't be changed because we don't unload
+// the external script file even if we expect the <script> element will be
+// removed by childNode.remove() and Garbage Collection after a test has been
+// finished. Therefore, I intentionally make them load externalScript1 and
+// externalScript2 alternately, and we can check if the results are changed
+// after reloading the other script.
+// Throughout this test, we periodically need to verify that a script
+// *does not load* after we've made a tweak. To do that, we have to
+// wait "long enough for it to have loaded", and then make sure nothing
+// has changed. We estimate "long enough" by adding an extra dummy
+// <script> element and watching for its load event.
+
+promise_test(function(t) {
+ var svg = document.getElementById('svg');
+ var script = createSVGElement(t, 'script', svg);
+
+ script.setAttribute('type', 'text/javascript');
+ script.setAttribute('href', 'testScripts/externalScript1.js');
+ assert_equals(script.href.baseVal, 'testScripts/externalScript1.js');
+
+ return waitEvent(script, 'load').then(function() {
+ assert_equals(loadedScript(), 'externalScript1',
+ 'Link to correct external script');
+
+ script.setAttributeNS(XLINKNS, 'xlink:href',
+ 'testScripts/externalScript2.js');
+
+ // Load an dummy script to trigger a load event.
+ var dummyScript = createSVGElement(t, 'script', svg);
+ dummyScript.setAttribute('href', 'testScripts/dummyScript.js');
+ return waitEvent(dummyScript, 'load');
+ }).then(function() {
+ assert_equals(script.href.baseVal, 'testScripts/externalScript1.js');
+ assert_equals(loadedScript(), 'externalScript1',
+ 'Still link to the external script from href');
+ });
+}, 'Test for loading external script from href when setting href and ' +
+ 'then xlink:href');
+
+promise_test(function(t) {
+ var svg = document.getElementById('svg');
+ var script = createSVGElement(t, 'script', svg);
+
+ script.setAttribute('type', 'text/javascript');
+ script.setAttributeNS(XLINKNS, 'xlink:href',
+ 'testScripts/externalScript2.js');
+ assert_equals(script.href.baseVal, 'testScripts/externalScript2.js');
+
+ return waitEvent(script, 'load').then(function() {
+ assert_equals(loadedScript(), 'externalScript2',
+ 'Link to the external script from xlink:href');
+
+ script.setAttribute('href', 'testScripts/externalScript1.js');
+
+ // Load an dummy script to trigger a load event.
+ var dummyScript = createSVGElement(t, 'script', svg);
+ dummyScript.setAttribute('href', 'testScripts/dummyScript.js');
+ return waitEvent(dummyScript, 'load');
+ }).then(function() {
+ assert_equals(script.href.baseVal, 'testScripts/externalScript1.js',
+ 'href() should prefer href attribute over xlink:href');
+ assert_equals(loadedScript(), 'externalScript2',
+ 'Still link to the external script from xlink:href');
+ });
+}, 'Test for loading external script from xlnk:href by adding xlink:href and ' +
+ 'then href');
+
+promise_test(function(t) {
+ var svg = document.getElementById('svg');
+ var script = createSVGElement(t, 'script', svg);
+
+ script.setAttribute('type', 'text/javascript');
+ script.setAttribute('href', 'testScripts/externalScript1.js');
+ script.setAttributeNS(XLINKNS, 'xlink:href',
+ 'testScripts/externalScript2.js');
+ assert_equals(script.href.baseVal, 'testScripts/externalScript1.js');
+
+ return waitEvent(script, 'load').then(function() {
+ assert_equals(loadedScript(), 'externalScript1',
+ 'Link to the external script by href');
+
+ script.removeAttribute('href');
+ assert_equals(script.href.baseVal, 'testScripts/externalScript2.js',
+ 'href() returns xlink:href attribute because href was unset');
+
+ // Load an dummy script to trigger a load event.
+ var dummyScript = createSVGElement(t, 'script', svg);
+ dummyScript.setAttribute('href', 'testScripts/dummyScript.js');
+ return waitEvent(dummyScript, 'load');
+ }).then(function() {
+ assert_equals(loadedScript(), 'externalScript1',
+ 'The external script loaded from href is still loaded');
+ });
+}, 'Test for loading external script from href by adding href and ' +
+ 'then xlink:href, and then removing href');
+
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/linking/scripted/testScripts/dummyScript.js
@@ -0,0 +1,3 @@
+function dummyScript() {
+ return "This is a dummy script";
+}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/linking/scripted/testScripts/externalScript1.js
@@ -0,0 +1,3 @@
+function loadedScript() {
+ return "externalScript1";
+}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/linking/scripted/testScripts/externalScript2.js
@@ -0,0 +1,3 @@
+function loadedScript() {
+ return "externalScript2";
+}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/svg/linking/scripted/testcommon.js
@@ -0,0 +1,42 @@
+/**
+ * The const var for SVG and xlink namespaces
+ */
+const SVGNS = 'http://www.w3.org/2000/svg';
+const XLINKNS = 'http://www.w3.org/1999/xlink';
+
+/**
+ * Appends a svg element to the parent.
+ *
+ * @param test The testharness.js Test object. If provided, this will be used
+ * to register a cleanup callback to remove the div when the test
+ * finishes.
+ * @param tag The element tag name.
+ * @param parent The parent element of this new created svg element.
+ * @param attrs A dictionary object with attribute names and values to set on
+ * the div.
+ */
+function createSVGElement(test, tag, parent, attrs) {
+ var elem = document.createElementNS(SVGNS, tag);
+ if (attrs) {
+ for (var attrName in attrs) {
+ elem.setAttribute(attrName, attrs[attrName]);
+ }
+ }
+ parent.appendChild(elem);
+ test.add_cleanup(function() {
+ elem.remove();
+ });
+ return elem;
+}
+
+/**
+ * Create a Promise object which resolves when a specific event fires.
+ *
+ * @param object The event target.
+ * @param name The event name.
+ */
+function waitEvent(object, name) {
+ return new Promise(function(resolve) {
+ object.addEventListener(name, resolve, { once: true });
+ });
+}