mirror of
https://github.com/servo/servo.git
synced 2025-06-30 12:03:38 +01:00
Auto merge of #20918 - servo-wpt-sync:wpt_update_04-06-2018, r=jdm
Sync WPT with upstream (04-06-2018) Automated downstream sync of changes from upstream as of 04-06-2018. [no-wpt-sync] <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/20918) <!-- Reviewable:end -->
This commit is contained in:
commit
eb1dfd0775
690 changed files with 6588 additions and 1564 deletions
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,4 @@
|
||||||
|
[CSSAnimation-animationName.tentative.html]
|
||||||
|
[CSSAnimation.animationName]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[CSSAnimation-canceling.tentative.html]
|
||||||
|
[Canceling a CSS animation]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[CSSAnimation-effect.tentative.html]
|
||||||
|
[CSSAnimation.effect]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[CSSAnimation-finished.tentative.html]
|
||||||
|
[CSSAnimation.finished]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[CSSAnimation-getComputedTiming.tentative.html]
|
||||||
|
[CSSAnimation.getComputedTiming()]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[CSSAnimation-getCurrentTime.tentative.html]
|
||||||
|
[CSSAnimation.currentTime]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[CSSAnimation-id.tentative.html]
|
||||||
|
[CSSAnimation.id]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[CSSAnimation-pausing.tentative.html]
|
||||||
|
[Pausing a CSSAnimation]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[CSSAnimation-playState.tentative.html]
|
||||||
|
[CSSAnimation.playState]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[CSSAnimation-ready.tentative.html]
|
||||||
|
[CSSAnimation.ready]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[CSSAnimation-startTime.tentative.html]
|
||||||
|
[CSSAnimation.startTime]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[CSSPseudoElement-getAnimations.tentative.html]
|
||||||
|
[CSSPseudoElement.getAnimations() for CSS animations]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[Document-getAnimations.tentative.html]
|
||||||
|
[Document.getAnimations() for CSS animations]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[Element-getAnimations-dynamic-changes.tentative.html]
|
||||||
|
[\nElement.getAnimations() - Dynamic changes to the list of CSS animations\n]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[Element-getAnimations.tentative.html]
|
||||||
|
[Element.getAnimations() for CSS animations]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[KeyframeEffect-getKeyframes.tentative.html]
|
||||||
|
[KeyframeEffect.getKeyframes() for CSS animations]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[KeyframeEffect-target.tentative.html]
|
||||||
|
[CSSAnimation.effect.target]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[event-dispatch.tentative.html]
|
||||||
|
[Tests for CSS animation event dispatch]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
[event-order.tentative.html]
|
||||||
|
[Tests for CSS animation event order]
|
||||||
|
expected: FAIL
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
[registered-properties-in-custom-paint.https.html]
|
[registered-properties-in-custom-paint.https.html]
|
||||||
type: reftest
|
type: reftest
|
||||||
expected: TIMEOUT
|
expected: FAIL
|
||||||
bug: https://github.com/servo/servo/issues/17677
|
bug: https://github.com/servo/servo/issues/17677
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[vh_not_refreshing_on_chrome.html]
|
|
||||||
expected: FAIL
|
|
|
@ -1,6 +1,5 @@
|
||||||
[005.html]
|
[005.html]
|
||||||
type: testharness
|
type: testharness
|
||||||
expected: ERROR
|
|
||||||
[dedicated worker in shared worker in dedicated worker]
|
[dedicated worker in shared worker in dedicated worker]
|
||||||
expected: TIMEOUT
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[shader-with-non-reserved-words.html]
|
|
||||||
expected: TIMEOUT
|
|
||||||
[Overall test]
|
|
||||||
expected: NOTRUN
|
|
||||||
|
|
|
@ -1,56 +1,28 @@
|
||||||
importScripts("/resources/testharness.js");
|
importScripts("/resources/testharness.js");
|
||||||
|
|
||||||
var blob, empty_blob, readerSync;
|
var blob, readerSync;
|
||||||
setup(() => {
|
setup(function() {
|
||||||
readerSync = new FileReaderSync();
|
readerSync = new FileReaderSync();
|
||||||
blob = new Blob(["test"]);
|
blob = new Blob(["test"]);
|
||||||
empty_blob = new Blob();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test(() => {
|
test(function() {
|
||||||
assert_true(readerSync instanceof FileReaderSync);
|
assert_true(readerSync instanceof FileReaderSync);
|
||||||
}, "Interface");
|
}, "Interface");
|
||||||
|
|
||||||
test(() => {
|
test(function() {
|
||||||
var text = readerSync.readAsText(blob);
|
var text = readerSync.readAsText(blob);
|
||||||
assert_equals(text, "test");
|
assert_equals(text, "test");
|
||||||
}, "readAsText");
|
}, "readAsText");
|
||||||
|
|
||||||
test(() => {
|
test(function() {
|
||||||
var text = readerSync.readAsText(empty_blob);
|
|
||||||
assert_equals(text, "");
|
|
||||||
}, "readAsText with empty blob");
|
|
||||||
|
|
||||||
test(() => {
|
|
||||||
var data = readerSync.readAsDataURL(blob);
|
var data = readerSync.readAsDataURL(blob);
|
||||||
assert_equals(data.indexOf("data:"), 0);
|
assert_equals(data.indexOf("data:"), 0);
|
||||||
}, "readAsDataURL");
|
}, "readAsDataURL");
|
||||||
|
|
||||||
test(() => {
|
test(function() {
|
||||||
var data = readerSync.readAsDataURL(empty_blob);
|
|
||||||
assert_equals(data.indexOf("data:"), 0);
|
|
||||||
}, "readAsDataURL with empty blob");
|
|
||||||
|
|
||||||
test(() => {
|
|
||||||
var data = readerSync.readAsBinaryString(blob);
|
|
||||||
assert_equals(data, "test");
|
|
||||||
}, "readAsBinaryString");
|
|
||||||
|
|
||||||
test(() => {
|
|
||||||
var data = readerSync.readAsBinaryString(empty_blob);
|
|
||||||
assert_equals(data, "");
|
|
||||||
}, "readAsBinaryString with empty blob");
|
|
||||||
|
|
||||||
test(() => {
|
|
||||||
var data = readerSync.readAsArrayBuffer(blob);
|
var data = readerSync.readAsArrayBuffer(blob);
|
||||||
assert_true(data instanceof ArrayBuffer);
|
assert_true(data instanceof ArrayBuffer);
|
||||||
assert_equals(data.byteLength, "test".length);
|
|
||||||
}, "readAsArrayBuffer");
|
}, "readAsArrayBuffer");
|
||||||
|
|
||||||
test(() => {
|
|
||||||
var data = readerSync.readAsArrayBuffer(empty_blob);
|
|
||||||
assert_true(data instanceof ArrayBuffer);
|
|
||||||
assert_equals(data.byteLength, 0);
|
|
||||||
}, "readAsArrayBuffer with empty blob");
|
|
||||||
|
|
||||||
done();
|
done();
|
||||||
|
|
|
@ -1962,9 +1962,9 @@
|
||||||
"html/elements/style/type-novalid.html": " The only allowed value for the \u201ctype\u201d attribute for the \u201cstyle\u201d element is \u201ctext/css\u201d (with no parameters). (But the attribute is not needed and should be omitted altogether.)",
|
"html/elements/style/type-novalid.html": " The only allowed value for the \u201ctype\u201d attribute for the \u201cstyle\u201d element is \u201ctext/css\u201d (with no parameters). (But the attribute is not needed and should be omitted altogether.)",
|
||||||
"html/elements/sub/model-novalid.html": "End tag \u201cp\u201d implied, but there were open elements.",
|
"html/elements/sub/model-novalid.html": "End tag \u201cp\u201d implied, but there were open elements.",
|
||||||
"html/elements/sup/model-novalid.html": "End tag \u201cp\u201d implied, but there were open elements.",
|
"html/elements/sup/model-novalid.html": "End tag \u201cp\u201d implied, but there were open elements.",
|
||||||
"html/elements/table/integrity/Alexis_of_Russia-novalid.html": "Bad value \u201ccopyright\u201d for attribute \u201crel\u201d on element \u201clink\u201d: Bad list of link-type keywords: The keyword \u201ccopyright\u201d for the \u201crel\u201d attribute should not be used. Consider using \u201clicense\u201d instead.",
|
"html/elements/table/integrity/Alexis_of_Russia-novalid.html": "The \u201calign\u201d attribute on the \u201ctable\u201d element is obsolete. Use CSS instead.",
|
||||||
"html/elements/table/integrity/Feodor_I_of_Russia-novalid.html": "Bad value \u201ccopyright\u201d for attribute \u201crel\u201d on element \u201clink\u201d: Bad list of link-type keywords: The keyword \u201ccopyright\u201d for the \u201crel\u201d attribute should not be used. Consider using \u201clicense\u201d instead.",
|
"html/elements/table/integrity/Feodor_I_of_Russia-novalid.html": "The \u201calign\u201d attribute on the \u201ctable\u201d element is obsolete. Use CSS instead.",
|
||||||
"html/elements/table/integrity/Naser_al-Din_Shah_Qajar-novalid.html": "Bad value \u201ccopyright\u201d for attribute \u201crel\u201d on element \u201clink\u201d: Bad list of link-type keywords: The keyword \u201ccopyright\u201d for the \u201crel\u201d attribute should not be used. Consider using \u201clicense\u201d instead.",
|
"html/elements/table/integrity/Naser_al-Din_Shah_Qajar-novalid.html": "An \u201cimg\u201d element must have an \u201calt\u201d attribute, except under certain conditions. For details, consult guidance on providing text alternatives for images.",
|
||||||
"html/elements/table/integrity/vertical-novalid.html": "Table cell is overlapped by later table cell.",
|
"html/elements/table/integrity/vertical-novalid.html": "Table cell is overlapped by later table cell.",
|
||||||
"html/elements/table/model-input-child-hidden-novalid.html": "Start tag \u201cinput\u201d seen in \u201ctable\u201d.",
|
"html/elements/table/model-input-child-hidden-novalid.html": "Start tag \u201cinput\u201d seen in \u201ctable\u201d.",
|
||||||
"html/elements/table/model-input-child-novalid.html": "Start tag \u201cinput\u201d seen in \u201ctable\u201d.",
|
"html/elements/table/model-input-child-novalid.html": "Start tag \u201cinput\u201d seen in \u201ctable\u201d.",
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
x = document.getElementById('x');
|
x = document.getElementById('x');
|
||||||
|
x.onload = function() {
|
||||||
x.location = "";
|
x.location = "";
|
||||||
|
|
||||||
// While document.write is deprecated I did not find another way to reproduce
|
// While document.write is deprecated I did not find another way to reproduce
|
||||||
|
@ -33,6 +34,7 @@
|
||||||
'<iframe src="../support/fail.html"></iframe>'
|
'<iframe src="../support/fail.html"></iframe>'
|
||||||
);
|
);
|
||||||
x.contentDocument.close();
|
x.contentDocument.close();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=frame-src%20%27none%27''></script>
|
<script async defer src='../support/checkReport.sub.js?reportField=violated-directive&reportValue=frame-src%20%27none%27''></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -28,7 +28,7 @@ var testCookiePathFromDOM = function (testCase, test) {
|
||||||
if (testCase.match === false) {
|
if (testCase.match === false) {
|
||||||
assert_equals(cookieSet, null);
|
assert_equals(cookieSet, null);
|
||||||
} else {
|
} else {
|
||||||
assert_not_equals(cookieSet, null);
|
assert_not_equals(cookieSet, null, "Cookie path from DOM should not be `null`");
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe.contentWindow.expireCookie('dom-' + testCase.name, testCase.path);
|
iframe.contentWindow.expireCookie('dom-' + testCase.name, testCase.path);
|
||||||
|
@ -45,7 +45,7 @@ var testCookiePathFromHeader = function (testCase, test) {
|
||||||
if (testCase.match === false) {
|
if (testCase.match === false) {
|
||||||
assert_equals(cookieSet, null);
|
assert_equals(cookieSet, null);
|
||||||
} else {
|
} else {
|
||||||
assert_not_equals(cookieSet, null);
|
assert_not_equals(cookieSet, null, "Cookie path from header should not be `null`");
|
||||||
}
|
}
|
||||||
|
|
||||||
test.done();
|
test.done();
|
||||||
|
|
95
tests/wpt/web-platform-tests/core-aam/blockquote-manual.html
Normal file
95
tests/wpt/web-platform-tests/core-aam/blockquote-manual.html
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>blockquote</title>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
|
||||||
|
<link rel="stylesheet" href="/wai-aria/scripts/manual.css">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="/wai-aria/scripts/ATTAcomm.js"></script>
|
||||||
|
<script>
|
||||||
|
setup({explicit_timeout: true, explicit_done: true });
|
||||||
|
|
||||||
|
var theTest = new ATTAcomm(
|
||||||
|
{
|
||||||
|
"steps" : [
|
||||||
|
{
|
||||||
|
"element" : "test",
|
||||||
|
"test" : {
|
||||||
|
"ATK" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"role",
|
||||||
|
"is",
|
||||||
|
"ROLE_BLOCK_QUOTE"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"AXAPI" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"AXRole",
|
||||||
|
"is",
|
||||||
|
"AXGroup"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"AXSubrole",
|
||||||
|
"is",
|
||||||
|
"<nil>"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"AXRoleDescription",
|
||||||
|
"is",
|
||||||
|
"group"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"IAccessible2" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"role",
|
||||||
|
"is",
|
||||||
|
"IA2_ROLE_SECTION"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"MSAA" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"role",
|
||||||
|
"is",
|
||||||
|
"ROLE_SYSTEM_GROUPING"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"UIA" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"ControlType",
|
||||||
|
"is",
|
||||||
|
"Group"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"LocalizedControlType",
|
||||||
|
"is",
|
||||||
|
"blockquote"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"title" : "step 1",
|
||||||
|
"type" : "test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title" : "blockquote"
|
||||||
|
}
|
||||||
|
|
||||||
|
) ;
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>This test examines the ARIA properties for blockquote.</p>
|
||||||
|
<div role="blockquote" id="test">content</div>
|
||||||
|
<div id="manualMode"></div>
|
||||||
|
<div id="log"></div>
|
||||||
|
<div id="ATTAmessages"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
89
tests/wpt/web-platform-tests/core-aam/caption-manual.html
Normal file
89
tests/wpt/web-platform-tests/core-aam/caption-manual.html
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>caption</title>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
|
||||||
|
<link rel="stylesheet" href="/wai-aria/scripts/manual.css">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="/wai-aria/scripts/ATTAcomm.js"></script>
|
||||||
|
<script>
|
||||||
|
setup({explicit_timeout: true, explicit_done: true });
|
||||||
|
|
||||||
|
var theTest = new ATTAcomm(
|
||||||
|
{
|
||||||
|
"steps" : [
|
||||||
|
{
|
||||||
|
"element" : "test",
|
||||||
|
"test" : {
|
||||||
|
"ATK" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"role",
|
||||||
|
"is",
|
||||||
|
"ROLE_CAPTION"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"AXAPI" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"AXRole",
|
||||||
|
"is",
|
||||||
|
"AXGroup"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"AXSubrole",
|
||||||
|
"is",
|
||||||
|
"<nil>"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"AXRoleDescription",
|
||||||
|
"is",
|
||||||
|
"group"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"IAccessible2" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"role",
|
||||||
|
"is",
|
||||||
|
"IA2_ROLE_CAPTION"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"MSAA" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"role",
|
||||||
|
"is",
|
||||||
|
"ROLE_SYSTEM_TEXT"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"UIA" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"ControlType",
|
||||||
|
"is",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"title" : "step 1",
|
||||||
|
"type" : "test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title" : "caption"
|
||||||
|
}
|
||||||
|
|
||||||
|
) ;
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>This test examines the ARIA properties for caption.</p>
|
||||||
|
<div role="caption" id="test">content</div>
|
||||||
|
<div id="manualMode"></div>
|
||||||
|
<div id="log"></div>
|
||||||
|
<div id="ATTAmessages"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
89
tests/wpt/web-platform-tests/core-aam/paragraph-manual.html
Normal file
89
tests/wpt/web-platform-tests/core-aam/paragraph-manual.html
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>paragraph</title>
|
||||||
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type"/>
|
||||||
|
<link rel="stylesheet" href="/wai-aria/scripts/manual.css">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="/wai-aria/scripts/ATTAcomm.js"></script>
|
||||||
|
<script>
|
||||||
|
setup({explicit_timeout: true, explicit_done: true });
|
||||||
|
|
||||||
|
var theTest = new ATTAcomm(
|
||||||
|
{
|
||||||
|
"steps" : [
|
||||||
|
{
|
||||||
|
"element" : "test",
|
||||||
|
"test" : {
|
||||||
|
"ATK" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"role",
|
||||||
|
"is",
|
||||||
|
"ROLE_PARAGRAPH"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"AXAPI" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"AXRole",
|
||||||
|
"is",
|
||||||
|
"AXGroup"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"AXSubrole",
|
||||||
|
"is",
|
||||||
|
"<nil>"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"AXRoleDescription",
|
||||||
|
"is",
|
||||||
|
"group"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"IAccessible2" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"role",
|
||||||
|
"is",
|
||||||
|
"IA2_ROLE_PARAGRAPH"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"MSAA" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"role",
|
||||||
|
"is",
|
||||||
|
"ROLE_SYSTEM_TEXT"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"UIA" : [
|
||||||
|
[
|
||||||
|
"property",
|
||||||
|
"ControlType",
|
||||||
|
"is",
|
||||||
|
"Text"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"title" : "step 1",
|
||||||
|
"type" : "test"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title" : "paragraph"
|
||||||
|
}
|
||||||
|
|
||||||
|
) ;
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<p>This test examines the ARIA properties for paragraph.</p>
|
||||||
|
<div role="paragraph" id="test">content</div>
|
||||||
|
<div id="manualMode"></div>
|
||||||
|
<div id="log"></div>
|
||||||
|
<div id="ATTAmessages"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Chris Lilley" href="http://www.w3.org/People" />
|
<link rel="author" title="Chris Lilley" href="http://www.w3.org/People" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#General" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#General" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-css3font-available" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-css3font-available" />
|
||||||
<meta name="assert" content="Linked fonts are only available to the documents that reference them" />
|
<meta name="assert" content="Linked fonts are only available to the documents that reference them." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
body {
|
body {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<title>WOFF Test: No Metadata Present</title>
|
<title>WOFF Test: No Metadata Present</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="The file has no metadata." />
|
<meta name="assert" content="The file has no metadata." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<title>WOFF Test: No Metadata Present</title>
|
<title>WOFF Test: No Metadata Present</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="metadata-noeffect-001-ref.xht" />
|
<link rel="match" href="metadata-noeffect-001-ref.xht" />
|
||||||
<meta name="assert" content="The file has no metadata." />
|
<meta name="assert" content="The file has no metadata." />
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
<title>WOFF Test: Metadata Present</title>
|
<title>WOFF Test: Metadata Present</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-maydisplay" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-maydisplay" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="The file has metadata." />
|
<meta name="assert" content="The file has metadata." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
<title>WOFF Test: Metadata Present</title>
|
<title>WOFF Test: Metadata Present</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-maydisplay" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-maydisplay" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="metadata-noeffect-002-ref.xht" />
|
<link rel="match" href="metadata-noeffect-002-ref.xht" />
|
||||||
<meta name="assert" content="The file has metadata." />
|
<meta name="assert" content="The file has metadata." />
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-authoritative" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-authoritative" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="The name table and metadata fields are out of sync. The name table contains FAIL and the metadata contains PASS for unique id, vendor name, vendor url, credit name, credit url, description, license, license url, copyright and trademark." />
|
<meta name="assert" content="The name table and metadata fields are out of sync. The name table contains FAIL and the metadata contains PASS for unique id, vendor name, vendor url, credit name, credit url, description, license, license url, copyright and trademark." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-authoritative" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-authoritative" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="metadatadisplay-authoritative-001-ref.xht" />
|
<link rel="match" href="metadatadisplay-authoritative-001-ref.xht" />
|
||||||
<meta name="assert" content="The name table and metadata fields are out of sync. The name table contains FAIL and the metadata contains PASS for unique id, vendor name, vendor url, credit name, credit url, description, license, license url, copyright and trademark." />
|
<meta name="assert" content="The name table and metadata fields are out of sync. The name table contains FAIL and the metadata contains PASS for unique id, vendor name, vendor url, credit name, credit url, description, license, license url, copyright and trademark." />
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="The xml encoding is set to ISO-8859-1." />
|
<meta name="assert" content="The xml encoding is set to ISO-8859-1." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="metadatadisplay-encoding-003-ref.xht" />
|
<link rel="match" href="metadatadisplay-encoding-003-ref.xht" />
|
||||||
<meta name="assert" content="The xml encoding is set to ISO-8859-1." />
|
<meta name="assert" content="The xml encoding is set to ISO-8859-1." />
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="The xml encoding is not declared and there is a UTF-16 BOM." />
|
<meta name="assert" content="The xml encoding is not declared and there is a UTF-16 BOM." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="metadatadisplay-encoding-006-ref.xht" />
|
<link rel="match" href="metadatadisplay-encoding-006-ref.xht" />
|
||||||
<meta name="assert" content="The xml encoding is not declared and there is a UTF-16 BOM." />
|
<meta name="assert" content="The xml encoding is not declared and there is a UTF-16 BOM." />
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="The text element in the description element contains an unescaped <." />
|
<meta name="assert" content="The text element in the description element contains an unescaped <." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="metadatadisplay-well-formed-001-ref.xht" />
|
<link rel="match" href="metadatadisplay-well-formed-001-ref.xht" />
|
||||||
<meta name="assert" content="The text element in the description element contains an unescaped <." />
|
<meta name="assert" content="The text element in the description element contains an unescaped <." />
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="The text element in the description element contains an unescaped &." />
|
<meta name="assert" content="The text element in the description element contains an unescaped &." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="metadatadisplay-well-formed-002-ref.xht" />
|
<link rel="match" href="metadatadisplay-well-formed-002-ref.xht" />
|
||||||
<meta name="assert" content="The text element in the description element contains an unescaped &." />
|
<meta name="assert" content="The text element in the description element contains an unescaped &." />
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="One element begins with <description> but ends with </mismatch>." />
|
<meta name="assert" content="One element begins with <description> but ends with </mismatch>." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="metadatadisplay-well-formed-003-ref.xht" />
|
<link rel="match" href="metadatadisplay-well-formed-003-ref.xht" />
|
||||||
<meta name="assert" content="One element begins with <description> but ends with </mismatch>." />
|
<meta name="assert" content="One element begins with <description> but ends with </mismatch>." />
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="The text element element in the description element is not closed." />
|
<meta name="assert" content="The text element element in the description element is not closed." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="metadatadisplay-well-formed-004-ref.xht" />
|
<link rel="match" href="metadatadisplay-well-formed-004-ref.xht" />
|
||||||
<meta name="assert" content="The text element element in the description element is not closed." />
|
<meta name="assert" content="The text element element in the description element is not closed." />
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="The <description> element is closed with <DESCRIPTION>." />
|
<meta name="assert" content="The <description> element is closed with <DESCRIPTION>." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="metadatadisplay-well-formed-005-ref.xht" />
|
<link rel="match" href="metadatadisplay-well-formed-005-ref.xht" />
|
||||||
<meta name="assert" content="The <description> element is closed with <DESCRIPTION>." />
|
<meta name="assert" content="The <description> element is closed with <DESCRIPTION>." />
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="The metadata root element occurs twice." />
|
<meta name="assert" content="The metadata root element occurs twice." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="metadatadisplay-well-formed-006-ref.xht" />
|
<link rel="match" href="metadatadisplay-well-formed-006-ref.xht" />
|
||||||
<meta name="assert" content="The metadata root element occurs twice." />
|
<meta name="assert" content="The metadata root element occurs twice." />
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="The xml encoding is set to 'VSCACS-GFV-X-CQ34QTAB2Q-IS-NOT-A-VALID-ENCODING'." />
|
<meta name="assert" content="The xml encoding is set to 'VSCACS-GFV-X-CQ34QTAB2Q-IS-NOT-A-VALID-ENCODING'." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#Metadata" />
|
||||||
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-invalid-mustignore" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-invalid-mustignore" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="metadatadisplay-well-formed-007-ref.xht" />
|
<link rel="match" href="metadatadisplay-well-formed-007-ref.xht" />
|
||||||
<meta name="assert" content="The xml encoding is set to 'VSCACS-GFV-X-CQ34QTAB2Q-IS-NOT-A-VALID-ENCODING'." />
|
<meta name="assert" content="The xml encoding is set to 'VSCACS-GFV-X-CQ34QTAB2Q-IS-NOT-A-VALID-ENCODING'." />
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<title>WOFF Test: Empty Glyph With Bounding Box</title>
|
<title>WOFF Test: Empty Glyph With Bounding Box</title>
|
||||||
<link rel="author" title="Khaled Hosny" href="http://khaledhosny.org" />
|
<link rel="author" title="Khaled Hosny" href="http://khaledhosny.org" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#DataTables" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#DataTables" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-mustRejectNonEmptyBBox" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-mustRejectNonEmptyBBox2" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="Invalid TTF flavored WOFF due to empty glyph with bounding box" />
|
<meta name="assert" content="Invalid TTF flavored WOFF due to empty glyph with bounding box" />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<title>WOFF Test: Empty Glyph With Bounding Box</title>
|
<title>WOFF Test: Empty Glyph With Bounding Box</title>
|
||||||
<link rel="author" title="Khaled Hosny" href="http://khaledhosny.org" />
|
<link rel="author" title="Khaled Hosny" href="http://khaledhosny.org" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#DataTables" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#DataTables" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-mustRejectNonEmptyBBox" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-mustRejectNonEmptyBBox2" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="tabledata-glyf-bbox-003-ref.xht" />
|
<link rel="match" href="tabledata-glyf-bbox-003-ref.xht" />
|
||||||
<meta name="assert" content="Invalid TTF flavored WOFF due to empty glyph with bounding box" />
|
<meta name="assert" content="Invalid TTF flavored WOFF due to empty glyph with bounding box" />
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
|
<meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
|
||||||
<title>WOFF Test: Transformed Hmtx Table With Bad Flags 1</title>
|
<title>WOFF Test: Transformed Hmtx Table With All Flags Set</title>
|
||||||
<link rel="author" title="Khaled Hosny" href="http://khaledhosny.org" />
|
<link rel="author" title="Khaled Hosny" href="http://khaledhosny.org" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#DataTables" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#DataTables" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-mustCheckLSBFlags" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-mustCheckLSBFlags" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="Invalid TTF flavored WOFF with transformed hmtx table with non-zero reserved bits of the flags field." />
|
<meta name="assert" content="Invalid TTF flavored WOFF with transformed hmtx table that has all flags bits (including reserved bits) set." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
@import url("support/test-fonts.css");
|
@import url("support/test-fonts.css");
|
||||||
body {
|
body {
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
|
<meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
|
||||||
<title>WOFF Test: Transformed Hmtx Table With Bad Flags 1</title>
|
<title>WOFF Test: Transformed Hmtx Table With All Flags Set</title>
|
||||||
<link rel="author" title="Khaled Hosny" href="http://khaledhosny.org" />
|
<link rel="author" title="Khaled Hosny" href="http://khaledhosny.org" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#DataTables" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#DataTables" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-mustCheckLSBFlags" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-mustCheckLSBFlags" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="tabledata-transform-hmtx-003-ref.xht" />
|
<link rel="match" href="tabledata-transform-hmtx-003-ref.xht" />
|
||||||
<meta name="assert" content="Invalid TTF flavored WOFF with transformed hmtx table with non-zero reserved bits of the flags field." />
|
<meta name="assert" content="Invalid TTF flavored WOFF with transformed hmtx table that has all flags bits (including reserved bits) set." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
@import url("support/test-fonts.css");
|
@import url("support/test-fonts.css");
|
||||||
@font-face {
|
@font-face {
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
|
<meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
|
||||||
<title>WOFF Test: Transformed Hmtx Table With Bad Flags 2</title>
|
<title>WOFF Test: Transformed Hmtx Table With 0 Flags</title>
|
||||||
<link rel="author" title="Khaled Hosny" href="http://khaledhosny.org" />
|
<link rel="author" title="Khaled Hosny" href="http://khaledhosny.org" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#DataTables" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#DataTables" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-mustCheckLSBFlags" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-mustCheckLSBFlags" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="Invalid TTF flavored WOFF with transformed hmtx table with all flags bits set to 0" />
|
<meta name="assert" content="Invalid TTF flavored WOFF with transformed hmtx table that has 0 flags (null transform)." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
@import url("support/test-fonts.css");
|
@import url("support/test-fonts.css");
|
||||||
body {
|
body {
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
|
<meta http-equiv="content-type" content="text/html;charset=UTF-8"/>
|
||||||
<title>WOFF Test: Transformed Hmtx Table With Bad Flags 2</title>
|
<title>WOFF Test: Transformed Hmtx Table With 0 Flags</title>
|
||||||
<link rel="author" title="Khaled Hosny" href="http://khaledhosny.org" />
|
<link rel="author" title="Khaled Hosny" href="http://khaledhosny.org" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#DataTables" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#DataTables" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-mustCheckLSBFlags" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-mustCheckLSBFlags" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="tabledata-transform-hmtx-004-ref.xht" />
|
<link rel="match" href="tabledata-transform-hmtx-004-ref.xht" />
|
||||||
<meta name="assert" content="Invalid TTF flavored WOFF with transformed hmtx table with all flags bits set to 0" />
|
<meta name="assert" content="Invalid TTF flavored WOFF with transformed hmtx table that has 0 flags (null transform)." />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
@import url("support/test-fonts.css");
|
@import url("support/test-fonts.css");
|
||||||
@font-face {
|
@font-face {
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<title>WOFF Test: Valid WOFF 1</title>
|
<title>WOFF Test: Valid WOFF 1</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="Valid CFF flavored WOFF with no metadata and no private data" />
|
<meta name="assert" content="Valid CFF flavored WOFF with no metadata and no private data" />
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<title>WOFF Test: Valid WOFF 1</title>
|
<title>WOFF Test: Valid WOFF 1</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="valid-001-ref.xht" />
|
<link rel="match" href="valid-001-ref.xht" />
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
<title>WOFF Test: Valid WOFF 2</title>
|
<title>WOFF Test: Valid WOFF 2</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-maydisplay" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-maydisplay" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="Valid CFF flavored WOFF with metadata" />
|
<meta name="assert" content="Valid CFF flavored WOFF with metadata" />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
<title>WOFF Test: Valid WOFF 2</title>
|
<title>WOFF Test: Valid WOFF 2</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-maydisplay" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-maydisplay" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="valid-002-ref.xht" />
|
<link rel="match" href="valid-002-ref.xht" />
|
||||||
<meta name="assert" content="Valid CFF flavored WOFF with metadata" />
|
<meta name="assert" content="Valid CFF flavored WOFF with metadata" />
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<title>WOFF Test: Valid WOFF 3</title>
|
<title>WOFF Test: Valid WOFF 3</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="Valid CFF flavored WOFF with private data" />
|
<meta name="assert" content="Valid CFF flavored WOFF with private data" />
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<title>WOFF Test: Valid WOFF 3</title>
|
<title>WOFF Test: Valid WOFF 3</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="valid-003-ref.xht" />
|
<link rel="match" href="valid-003-ref.xht" />
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
<title>WOFF Test: Valid WOFF 4</title>
|
<title>WOFF Test: Valid WOFF 4</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-maydisplay" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-maydisplay" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="Valid CFF flavored WOFF with metadata and private data" />
|
<meta name="assert" content="Valid CFF flavored WOFF with metadata and private data" />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
<title>WOFF Test: Valid WOFF 4</title>
|
<title>WOFF Test: Valid WOFF 4</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-maydisplay" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-maydisplay" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="valid-004-ref.xht" />
|
<link rel="match" href="valid-004-ref.xht" />
|
||||||
<meta name="assert" content="Valid CFF flavored WOFF with metadata and private data" />
|
<meta name="assert" content="Valid CFF flavored WOFF with metadata and private data" />
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<title>WOFF Test: Valid WOFF 5</title>
|
<title>WOFF Test: Valid WOFF 5</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="Valid TTF flavored WOFF with no metadata and no private data" />
|
<meta name="assert" content="Valid TTF flavored WOFF with no metadata and no private data" />
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<title>WOFF Test: Valid WOFF 5</title>
|
<title>WOFF Test: Valid WOFF 5</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="valid-005-ref.xht" />
|
<link rel="match" href="valid-005-ref.xht" />
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
<title>WOFF Test: Valid WOFF 6</title>
|
<title>WOFF Test: Valid WOFF 6</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-maydisplay" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-maydisplay" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="Valid TTF flavored WOFF with metadata" />
|
<meta name="assert" content="Valid TTF flavored WOFF with metadata" />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
<title>WOFF Test: Valid WOFF 6</title>
|
<title>WOFF Test: Valid WOFF 6</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-maydisplay" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-maydisplay" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="valid-006-ref.xht" />
|
<link rel="match" href="valid-006-ref.xht" />
|
||||||
<meta name="assert" content="Valid TTF flavored WOFF with metadata" />
|
<meta name="assert" content="Valid TTF flavored WOFF with metadata" />
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<title>WOFF Test: Valid WOFF 7</title>
|
<title>WOFF Test: Valid WOFF 7</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="Valid TTF flavored WOFF with private data" />
|
<meta name="assert" content="Valid TTF flavored WOFF with private data" />
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<title>WOFF Test: Valid WOFF 7</title>
|
<title>WOFF Test: Valid WOFF 7</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="valid-007-ref.xht" />
|
<link rel="match" href="valid-007-ref.xht" />
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
<title>WOFF Test: Valid WOFF 8</title>
|
<title>WOFF Test: Valid WOFF 8</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-maydisplay" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-maydisplay" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<meta name="assert" content="Valid TTF flavored WOFF with metadata and private data" />
|
<meta name="assert" content="Valid TTF flavored WOFF with metadata and private data" />
|
||||||
<style type="text/css"><![CDATA[
|
<style type="text/css"><![CDATA[
|
||||||
|
|
|
@ -5,9 +5,9 @@
|
||||||
<title>WOFF Test: Valid WOFF 8</title>
|
<title>WOFF Test: Valid WOFF 8</title>
|
||||||
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
<link rel="author" title="Tal Leming" href="http://typesupply.com" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#FileStructure" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-noeffect" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-private-noeffect" />
|
||||||
<link rel="help" href="http://dev.w3.org/webfonts/WOFF2/spec/#conform-metadata-maydisplay" />
|
<link rel="help" href="http://www.w3.org/TR/WOFF/#conform-metadata-maydisplay" />
|
||||||
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
<link rel="reviewer" title="Chris Lilley" href="mailto:chris@w3.org" />
|
||||||
<link rel="match" href="valid-008-ref.xht" />
|
<link rel="match" href="valid-008-ref.xht" />
|
||||||
<meta name="assert" content="Valid TTF flavored WOFF with metadata and private data" />
|
<meta name="assert" content="Valid TTF flavored WOFF with metadata and private data" />
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSSAnimation.animationName</title>
|
||||||
|
<link rel="help"
|
||||||
|
href="https://drafts.csswg.org/css-animations-2/#dom-cssanimation-animationname">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes xyz {
|
||||||
|
to { left: 100px }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'xyz 100s';
|
||||||
|
assert_equals(div.getAnimations()[0].animationName, 'xyz',
|
||||||
|
'Animation name matches keyframes rule name');
|
||||||
|
}, 'Animation name makes keyframe rule');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'x\\yz 100s';
|
||||||
|
assert_equals(div.getAnimations()[0].animationName, 'xyz',
|
||||||
|
'Escaped animation name matches keyframes rule name');
|
||||||
|
}, 'Escaped animation name');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'x\\79 z 100s';
|
||||||
|
assert_equals(div.getAnimations()[0].animationName, 'xyz',
|
||||||
|
'Hex-escaped animation name matches keyframes rule'
|
||||||
|
+ ' name');
|
||||||
|
}, 'Animation name with hex-escape');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,199 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Canceling a CSS animation</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes translateAnim {
|
||||||
|
to { transform: translate(100px) }
|
||||||
|
}
|
||||||
|
@keyframes marginLeftAnim {
|
||||||
|
to { margin-left: 100px }
|
||||||
|
}
|
||||||
|
@keyframes marginLeftAnim100To200 {
|
||||||
|
from { margin-left: 100px }
|
||||||
|
to { margin-left: 200px }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: translateAnim 100s' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
assert_not_equals(getComputedStyle(div).transform, 'none',
|
||||||
|
'transform style is animated before canceling');
|
||||||
|
animation.cancel();
|
||||||
|
assert_equals(getComputedStyle(div).transform, 'none',
|
||||||
|
'transform style is no longer animated after canceling');
|
||||||
|
}, 'Animated style is cleared after canceling a running CSS animation');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: translateAnim 100s forwards' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
animation.finish();
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
assert_not_equals(getComputedStyle(div).transform, 'none',
|
||||||
|
'transform style is filling before canceling');
|
||||||
|
animation.cancel();
|
||||||
|
assert_equals(getComputedStyle(div).transform, 'none',
|
||||||
|
'fill style is cleared after canceling');
|
||||||
|
}, 'Animated style is cleared after canceling a filling CSS animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: marginLeftAnim 100s linear' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
animation.cancel();
|
||||||
|
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||||
|
'margin-left style is not animated after canceling');
|
||||||
|
|
||||||
|
animation.currentTime = 50 * 1000;
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '50px',
|
||||||
|
'margin-left style is updated when canceled animation is'
|
||||||
|
+ ' seeked');
|
||||||
|
}, 'After canceling an animation, it can still be seeked');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div =
|
||||||
|
addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
animation.cancel();
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||||
|
'margin-left style is not animated after canceling');
|
||||||
|
animation.play();
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '100px',
|
||||||
|
'margin-left style is animated after re-starting animation');
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
assert_equals(animation.playState, 'running',
|
||||||
|
'Animation succeeds in running after being re-started');
|
||||||
|
}, 'After canceling an animation, it can still be re-used');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div =
|
||||||
|
addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
animation.cancel();
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||||
|
'margin-left style is not animated after canceling');
|
||||||
|
|
||||||
|
// Trigger a change to some animation properties and check that this
|
||||||
|
// doesn't cause the animation to become live again
|
||||||
|
div.style.animationDuration = '200s';
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||||
|
'margin-left style is still not animated after updating'
|
||||||
|
+ ' animation-duration');
|
||||||
|
assert_equals(animation.playState, 'idle',
|
||||||
|
'Animation is still idle after updating animation-duration');
|
||||||
|
}, 'After canceling an animation, updating animation properties doesn\'t make'
|
||||||
|
+ ' it live again');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div =
|
||||||
|
addDiv(t, { style: 'animation: marginLeftAnim100To200 100s linear' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
animation.cancel();
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '0px',
|
||||||
|
'margin-left style is not animated after canceling');
|
||||||
|
|
||||||
|
// Make some changes to animation-play-state and check that the
|
||||||
|
// animation doesn't become live again. This is because it should be
|
||||||
|
// possible to cancel an animation from script such that all future
|
||||||
|
// changes to style are ignored.
|
||||||
|
|
||||||
|
// Redundant change
|
||||||
|
div.style.animationPlayState = 'running';
|
||||||
|
assert_equals(animation.playState, 'idle',
|
||||||
|
'Animation is still idle after a redundant change to'
|
||||||
|
+ ' animation-play-state');
|
||||||
|
|
||||||
|
// Pause
|
||||||
|
div.style.animationPlayState = 'paused';
|
||||||
|
assert_equals(animation.playState, 'idle',
|
||||||
|
'Animation is still idle after setting'
|
||||||
|
+ ' animation-play-state: paused');
|
||||||
|
|
||||||
|
// Play
|
||||||
|
div.style.animationPlayState = 'running';
|
||||||
|
assert_equals(animation.playState, 'idle',
|
||||||
|
'Animation is still idle after re-setting'
|
||||||
|
+ ' animation-play-state: running');
|
||||||
|
|
||||||
|
}, 'After canceling an animation, updating animation-play-state doesn\'t'
|
||||||
|
+ ' make it live again');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: translateAnim 10s both' });
|
||||||
|
div.style.marginLeft = '0px';
|
||||||
|
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
assert_equals(animation.playState, 'running');
|
||||||
|
|
||||||
|
div.style.animationName = 'none';
|
||||||
|
flushComputedStyle(div);
|
||||||
|
|
||||||
|
await waitForFrame();
|
||||||
|
|
||||||
|
assert_equals(animation.playState, 'idle');
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '0px');
|
||||||
|
}, 'Setting animation-name to \'none\' cancels the animation');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: translateAnim 10s both' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
assert_equals(animation.playState, 'running');
|
||||||
|
|
||||||
|
div.style.display = 'none';
|
||||||
|
|
||||||
|
await waitForFrame();
|
||||||
|
|
||||||
|
assert_equals(animation.playState, 'idle');
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '0px');
|
||||||
|
}, 'Setting display:none on an element cancel its animations');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const parentDiv = addDiv(t);
|
||||||
|
const childDiv = document.createElement('div');
|
||||||
|
parentDiv.appendChild(childDiv);
|
||||||
|
|
||||||
|
childDiv.setAttribute('style', 'animation: translateAnim 10s both');
|
||||||
|
flushComputedStyle(childDiv);
|
||||||
|
|
||||||
|
const animation = childDiv.getAnimations()[0];
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
assert_equals(animation.playState, 'running');
|
||||||
|
|
||||||
|
parentDiv.style.display = 'none';
|
||||||
|
await waitForFrame();
|
||||||
|
|
||||||
|
assert_equals(animation.playState, 'idle');
|
||||||
|
assert_equals(getComputedStyle(childDiv).marginLeft, '0px');
|
||||||
|
}, 'Setting display:none on an ancestor element cancels animations on ' +
|
||||||
|
'descendants');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,132 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSSAnimation.effect</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes anim {
|
||||||
|
from {
|
||||||
|
margin-left: 0px;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
margin-left: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim 100s';
|
||||||
|
|
||||||
|
const watcher = new EventWatcher(t, div, [ 'animationend',
|
||||||
|
'animationcancel' ]);
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
animation.currentTime = 50 * MS_PER_SEC;
|
||||||
|
animation.effect = null;
|
||||||
|
assert_equals(animation.playState, 'finished');
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '0px');
|
||||||
|
await watcher.wait_for('animationend');
|
||||||
|
}, 'Setting a null effect on a running animation fires an animationend event');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim 100s';
|
||||||
|
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
animation.currentTime = 50 * MS_PER_SEC;
|
||||||
|
animation.effect = new KeyframeEffect(div,
|
||||||
|
{ left: [ '0px' , '100px'] },
|
||||||
|
100 * MS_PER_SEC);
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '0px');
|
||||||
|
assert_equals(getComputedStyle(div).left, '50px');
|
||||||
|
}, 'Replacing an animation\'s effect with an effect that targets a different ' +
|
||||||
|
'property should update both properties');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim 100s';
|
||||||
|
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
animation.currentTime = 50 * MS_PER_SEC;
|
||||||
|
animation.effect = new KeyframeEffect(div,
|
||||||
|
{ left: [ '0px' , '100px'] },
|
||||||
|
20 * MS_PER_SEC);
|
||||||
|
assert_equals(animation.playState, 'finished');
|
||||||
|
}, 'Replacing an animation\'s effect with a shorter one that should have ' +
|
||||||
|
'already finished, the animation finishes immediately');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim 100s';
|
||||||
|
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_true(animation.pending);
|
||||||
|
|
||||||
|
animation.effect = new KeyframeEffect(div,
|
||||||
|
{ left: [ '0px' , '100px'] },
|
||||||
|
100 * MS_PER_SEC);
|
||||||
|
assert_true(animation.pending);
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
assert_false(animation.pending);
|
||||||
|
}, 'A play-pending animation\'s effect whose effect is replaced still exits ' +
|
||||||
|
'the pending state');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div1 = addDiv(t);
|
||||||
|
const div2 = addDiv(t);
|
||||||
|
|
||||||
|
const watcher1 = new EventWatcher(t, div1, 'animationstart');
|
||||||
|
// Watch |div2| as well to ensure it does *not* get events.
|
||||||
|
const watcher2 = new EventWatcher(t, div2, 'animationstart');
|
||||||
|
|
||||||
|
div1.style.animation = 'anim 100s';
|
||||||
|
|
||||||
|
const animation = div1.getAnimations()[0];
|
||||||
|
animation.effect = new KeyframeEffect(div2,
|
||||||
|
{ left: [ '0px', '100px' ] },
|
||||||
|
100 * MS_PER_SEC);
|
||||||
|
|
||||||
|
await watcher1.wait_for('animationstart');
|
||||||
|
|
||||||
|
assert_equals(animation.effect.target, div2);
|
||||||
|
|
||||||
|
// Then wait a couple of frames and check that no event was dispatched.
|
||||||
|
await waitForAnimationFrames(2);
|
||||||
|
}, 'CSS animation events are dispatched at the original element even after'
|
||||||
|
+ ' setting an effect with a different target element');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
const watcher = new EventWatcher(t, div, [ 'animationstart',
|
||||||
|
'animationend',
|
||||||
|
'animationcancel' ]);
|
||||||
|
div.style.animation = 'anim 100s';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
animation.finish();
|
||||||
|
|
||||||
|
await watcher.wait_for([ 'animationstart', 'animationend' ]);
|
||||||
|
// Set a longer effect
|
||||||
|
animation.effect = new KeyframeEffect(div,
|
||||||
|
{ left: [ '0px', '100px' ] },
|
||||||
|
200 * MS_PER_SEC);
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
}, 'After replacing a finished animation\'s effect with a longer one ' +
|
||||||
|
'it fires an animationstart event');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,87 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSSAnimation.finished</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes abc {
|
||||||
|
to { transform: translate(10px) }
|
||||||
|
}
|
||||||
|
@keyframes def {}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const ANIM_PROP_VAL = 'abc 100s';
|
||||||
|
const ANIM_DURATION = 100 * MS_PER_SEC;
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
// Set up pending animation
|
||||||
|
div.style.animation = ANIM_PROP_VAL;
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
const originalFinishedPromise = animation.finished;
|
||||||
|
|
||||||
|
// Cancel the animation and flush styles
|
||||||
|
div.style.animation = '';
|
||||||
|
getComputedStyle(div).animation;
|
||||||
|
|
||||||
|
await promise_rejects(t, 'AbortError', originalFinishedPromise,
|
||||||
|
'finished promise is rejected with AbortError');
|
||||||
|
|
||||||
|
assert_not_equals(animation.finished, originalFinishedPromise,
|
||||||
|
'Finished promise should change after the original is ' +
|
||||||
|
'rejected');
|
||||||
|
}, 'finished promise is rejected when an animation is canceled by resetting ' +
|
||||||
|
'the animation property');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
// As before, but this time instead of removing all animations, simply update
|
||||||
|
// the list of animations. At least for Firefox, updating is a different
|
||||||
|
// code path.
|
||||||
|
|
||||||
|
// Set up pending animation
|
||||||
|
div.style.animation = ANIM_PROP_VAL;
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
const originalFinishedPromise = animation.finished;
|
||||||
|
|
||||||
|
// Update the animation and flush styles
|
||||||
|
div.style.animation = 'def 100s';
|
||||||
|
getComputedStyle(div).animation;
|
||||||
|
|
||||||
|
await promise_rejects(t, 'AbortError', originalFinishedPromise,
|
||||||
|
'finished promise is rejected with AbortError');
|
||||||
|
|
||||||
|
assert_not_equals(animation.finished, originalFinishedPromise,
|
||||||
|
'Finished promise should change after the original is ' +
|
||||||
|
'rejected');
|
||||||
|
}, 'finished promise is rejected when an animation is canceled by changing ' +
|
||||||
|
'the animation property');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = ANIM_PROP_VAL;
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
const originalFinishedPromise = animation.finished;
|
||||||
|
animation.currentTime = ANIM_DURATION;
|
||||||
|
|
||||||
|
await animation.finished;
|
||||||
|
|
||||||
|
div.style.animationPlayState = 'running';
|
||||||
|
await waitForAnimationFrames(2);
|
||||||
|
|
||||||
|
assert_equals(animation.finished, originalFinishedPromise,
|
||||||
|
'The finished promise should NOT be reset');
|
||||||
|
assert_equals(animation.currentTime, ANIM_DURATION,
|
||||||
|
'Sanity check: the current time should not change');
|
||||||
|
}, 'finished promise is not reset when animationPlayState is set to running');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,609 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSSAnimation.getComputedTiming()</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes moveAnimation {
|
||||||
|
from { margin-left: 100px }
|
||||||
|
to { margin-left: 200px }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// delay
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().delay, 0, 'Initial value of delay');
|
||||||
|
}, 'delay of a new animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s -10s' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().delay, -10 * MS_PER_SEC,
|
||||||
|
'Initial value of delay');
|
||||||
|
}, 'Negative delay of a new animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s 10s' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().delay, 10 * MS_PER_SEC,
|
||||||
|
'Initial value of delay');
|
||||||
|
}, 'Positive delay of a new animation');
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// endDelay
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().endDelay, 0,
|
||||||
|
'Initial value of endDelay');
|
||||||
|
}, 'endDelay of a new animation');
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// fill
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const getEffectWithFill = fill => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s ' + fill });
|
||||||
|
return div.getAnimations()[0].effect;
|
||||||
|
};
|
||||||
|
|
||||||
|
let effect = getEffectWithFill('');
|
||||||
|
assert_equals(effect.getComputedTiming().fill, 'none',
|
||||||
|
'Initial value of fill');
|
||||||
|
effect = getEffectWithFill('forwards');
|
||||||
|
assert_equals(effect.getComputedTiming().fill, 'forwards',
|
||||||
|
'Fill forwards');
|
||||||
|
effect = getEffectWithFill('backwards');
|
||||||
|
assert_equals(effect.getComputedTiming().fill, 'backwards',
|
||||||
|
'Fill backwards');
|
||||||
|
effect = getEffectWithFill('both');
|
||||||
|
assert_equals(effect.getComputedTiming().fill, 'both',
|
||||||
|
'Fill forwards and backwards');
|
||||||
|
}, 'fill of a new animation');
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// iterationStart
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().iterationStart, 0,
|
||||||
|
'Initial value of iterationStart');
|
||||||
|
}, 'iterationStart of a new animation');
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// iterations
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().iterations, 1,
|
||||||
|
'Initial value of iterations');
|
||||||
|
}, 'iterations of a new animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s 2016.5' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().iterations, 2016.5,
|
||||||
|
'Initial value of iterations');
|
||||||
|
}, 'iterations of a finitely repeating animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s infinite' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().iterations, Infinity,
|
||||||
|
'Initial value of iterations');
|
||||||
|
}, 'iterations of an infinitely repeating animation');
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// duration
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, {
|
||||||
|
style: 'animation: moveAnimation 100s -10s infinite'
|
||||||
|
});
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().duration, 100 * MS_PER_SEC,
|
||||||
|
'Initial value of duration');
|
||||||
|
}, 'duration of a new animation');
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// direction
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const getEffectWithDir = dir => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s ' + dir });
|
||||||
|
return div.getAnimations()[0].effect;
|
||||||
|
};
|
||||||
|
|
||||||
|
let effect = getEffectWithDir('');
|
||||||
|
assert_equals(effect.getComputedTiming().direction, 'normal',
|
||||||
|
'Initial value of normal direction');
|
||||||
|
effect = getEffectWithDir('reverse');
|
||||||
|
assert_equals(effect.getComputedTiming().direction, 'reverse',
|
||||||
|
'Initial value of reverse direction');
|
||||||
|
effect = getEffectWithDir('alternate');
|
||||||
|
assert_equals(effect.getComputedTiming().direction, 'alternate',
|
||||||
|
'Initial value of alternate direction');
|
||||||
|
effect = getEffectWithDir('alternate-reverse');
|
||||||
|
assert_equals(effect.getComputedTiming().direction, 'alternate-reverse',
|
||||||
|
'Initial value of alternate-reverse direction');
|
||||||
|
}, 'direction of a new animation');
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// easing
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().easing, 'linear',
|
||||||
|
'Initial value of easing');
|
||||||
|
}, 'easing of a new animation');
|
||||||
|
|
||||||
|
|
||||||
|
// ------------------------------
|
||||||
|
// endTime
|
||||||
|
// = max(start delay + active duration + end delay, 0)
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().endTime, 100 * MS_PER_SEC,
|
||||||
|
'Initial value of endTime');
|
||||||
|
}, 'endTime of an new animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s -5s' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
const answer = (100 - 5) * MS_PER_SEC;
|
||||||
|
assert_equals(effect.getComputedTiming().endTime, answer,
|
||||||
|
'Initial value of endTime');
|
||||||
|
}, 'endTime of an animation with a negative delay');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, {
|
||||||
|
style: 'animation: moveAnimation 10s -100s infinite'
|
||||||
|
});
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().endTime, Infinity,
|
||||||
|
'Initial value of endTime');
|
||||||
|
}, 'endTime of an infinitely repeating animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 0s 100s infinite' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().endTime, 100 * MS_PER_SEC,
|
||||||
|
'Initial value of endTime');
|
||||||
|
}, 'endTime of an infinitely repeating zero-duration animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
// Fill forwards so div.getAnimations()[0] won't return an
|
||||||
|
// undefined value.
|
||||||
|
const div = addDiv(t, {
|
||||||
|
style: 'animation: moveAnimation 10s -100s forwards'
|
||||||
|
});
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().endTime, 0,
|
||||||
|
'Initial value of endTime');
|
||||||
|
}, 'endTime of an animation that finishes before its startTime');
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// activeDuration
|
||||||
|
// = iteration duration * iteration count
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s 5' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
const answer = 100 * MS_PER_SEC * 5;
|
||||||
|
assert_equals(effect.getComputedTiming().activeDuration, answer,
|
||||||
|
'Initial value of activeDuration');
|
||||||
|
}, 'activeDuration of a new animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s infinite' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().activeDuration, Infinity,
|
||||||
|
'Initial value of activeDuration');
|
||||||
|
}, 'activeDuration of an infinitely repeating animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 0s 1s infinite' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
// If either the iteration duration or iteration count are zero,
|
||||||
|
// the active duration is zero.
|
||||||
|
assert_equals(effect.getComputedTiming().activeDuration, 0,
|
||||||
|
'Initial value of activeDuration');
|
||||||
|
}, 'activeDuration of an infinitely repeating zero-duration animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s 1s 0' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
// If either the iteration duration or iteration count are zero,
|
||||||
|
// the active duration is zero.
|
||||||
|
assert_equals(effect.getComputedTiming().activeDuration, 0,
|
||||||
|
'Initial value of activeDuration');
|
||||||
|
}, 'activeDuration of an animation with zero iterations');
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// localTime
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().localTime, 0,
|
||||||
|
'Initial value of localTime');
|
||||||
|
}, 'localTime of a new animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
anim.currentTime = 5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
|
||||||
|
'current localTime after setting currentTime');
|
||||||
|
}, 'localTime of an animation is always equal to currentTime');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
|
||||||
|
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
anim.playbackRate = 2; // 2 times faster
|
||||||
|
|
||||||
|
await anim.ready;
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
|
||||||
|
'localTime is equal to currentTime');
|
||||||
|
|
||||||
|
await waitForFrame();
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
|
||||||
|
'localTime is equal to currentTime');
|
||||||
|
}, 'localTime reflects playbackRate immediately');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
const effect = new KeyframeEffect(div, {left: ["0px", "100px"]});
|
||||||
|
|
||||||
|
assert_equals(effect.getComputedTiming().localTime, null,
|
||||||
|
'localTime for orphaned effect');
|
||||||
|
}, 'localTime of an AnimationEffect without an Animation');
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// progress
|
||||||
|
//
|
||||||
|
// Note: Even though CSS animations have a default animation-timing-function of
|
||||||
|
// "ease", this only applies between keyframes (often referred to as the
|
||||||
|
// keyframe-level easing). The progress value returned by getComputedTiming(),
|
||||||
|
// however, only reflects effect-level easing and this defaults to "linear",
|
||||||
|
// even for CSS animations.
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const tests = [
|
||||||
|
{ fill: '', progress: [null, null] },
|
||||||
|
{ fill: 'none', progress: [null, null] },
|
||||||
|
{ fill: 'forwards', progress: [null, 1.0] },
|
||||||
|
{ fill: 'backwards', progress: [0.0, null] },
|
||||||
|
{ fill: 'both', progress: [0.0, 1.0] },
|
||||||
|
];
|
||||||
|
for (const test of tests) {
|
||||||
|
const div = addDiv(t, {
|
||||||
|
style: 'animation: moveAnimation 100s 10s ' + test.fill
|
||||||
|
});
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
assert_true(anim.effect.getComputedTiming().progress === test.progress[0],
|
||||||
|
`Initial progress with "${test.fill}" fill`);
|
||||||
|
anim.finish();
|
||||||
|
assert_true(anim.effect.getComputedTiming().progress === test.progress[1],
|
||||||
|
`Initial progress with "${test.fill}" fill`);
|
||||||
|
}
|
||||||
|
}, 'progress of an animation with different fill modes');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 10s 10 both' });
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.0,
|
||||||
|
'Initial value of progress');
|
||||||
|
anim.currentTime += 2.5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.25,
|
||||||
|
'Value of progress');
|
||||||
|
anim.currentTime += 5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.75,
|
||||||
|
'Value of progress');
|
||||||
|
anim.currentTime += 5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.25,
|
||||||
|
'Value of progress');
|
||||||
|
anim.finish()
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 1.0,
|
||||||
|
'Value of progress');
|
||||||
|
}, 'progress of an integral repeating animation with normal direction');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
// Note: FillMode here is "both" because
|
||||||
|
// 1. Since this a zero-duration animation, it will already have finished
|
||||||
|
// so it won't be returned by getAnimations() unless it fills forwards.
|
||||||
|
// 2. Fill backwards, so the progress before phase wouldn't be
|
||||||
|
// unresolved (null value).
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 0s infinite both' });
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 1.0,
|
||||||
|
'Initial value of progress in after phase');
|
||||||
|
|
||||||
|
// Seek backwards
|
||||||
|
anim.currentTime -= 1 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.0,
|
||||||
|
'Value of progress before phase');
|
||||||
|
}, 'progress of an infinitely repeating zero-duration animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
// Default iterations = 1
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 0s both' });
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 1.0,
|
||||||
|
'Initial value of progress in after phase');
|
||||||
|
|
||||||
|
// Seek backwards
|
||||||
|
anim.currentTime -= 1 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.0,
|
||||||
|
'Value of progress before phase');
|
||||||
|
}, 'progress of a finitely repeating zero-duration animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 0s 5s 10.25 both' });
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.0,
|
||||||
|
'Initial value of progress (before phase)');
|
||||||
|
|
||||||
|
// Using iteration duration of 1 now.
|
||||||
|
// currentIteration now is floor(10.25) = 10, so progress should be 25%.
|
||||||
|
anim.finish();
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.25,
|
||||||
|
'Value of progress in after phase');
|
||||||
|
}, 'progress of a non-integral repeating zero-duration animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, {
|
||||||
|
style: 'animation: moveAnimation 0s 5s 10.25 both reverse',
|
||||||
|
});
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 1.0,
|
||||||
|
'Initial value of progress (before phase)');
|
||||||
|
|
||||||
|
// Seek forwards
|
||||||
|
anim.finish();
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.75,
|
||||||
|
'Value of progress in after phase');
|
||||||
|
}, 'Progress of a non-integral repeating zero-duration animation ' +
|
||||||
|
'with reversing direction');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, {
|
||||||
|
style: 'animation: moveAnimation 10s 10.25 both alternate',
|
||||||
|
});
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.0,
|
||||||
|
'Initial value of progress');
|
||||||
|
anim.currentTime += 2.5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.25,
|
||||||
|
'Value of progress');
|
||||||
|
anim.currentTime += 5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.75,
|
||||||
|
'Value of progress');
|
||||||
|
anim.currentTime += 5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.75,
|
||||||
|
'Value of progress');
|
||||||
|
anim.finish()
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.25,
|
||||||
|
'Value of progress');
|
||||||
|
}, 'progress of a non-integral repeating animation ' +
|
||||||
|
'with alternate direction');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, {
|
||||||
|
style: 'animation: moveAnimation 10s 10.25 both alternate-reverse',
|
||||||
|
});
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 1.0,
|
||||||
|
'Initial value of progress');
|
||||||
|
anim.currentTime += 2.5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.75,
|
||||||
|
'Value of progress');
|
||||||
|
anim.currentTime += 5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.25,
|
||||||
|
'Value of progress');
|
||||||
|
anim.currentTime += 5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.25,
|
||||||
|
'Value of progress');
|
||||||
|
anim.finish()
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.75,
|
||||||
|
'Value of progress');
|
||||||
|
}, 'progress of a non-integral repeating animation ' +
|
||||||
|
'with alternate-reversing direction');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, {
|
||||||
|
style: 'animation: moveAnimation 0s 10.25 both alternate',
|
||||||
|
});
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.25,
|
||||||
|
'Initial value of progress');
|
||||||
|
anim.currentTime += 2.5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.25,
|
||||||
|
'Value of progress');
|
||||||
|
anim.currentTime -= 5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.0,
|
||||||
|
'Value of progress');
|
||||||
|
anim.finish()
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.25,
|
||||||
|
'Value of progress');
|
||||||
|
}, 'progress of a non-integral repeating zero-duration animation ' +
|
||||||
|
'with alternate direction');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, {
|
||||||
|
style: 'animation: moveAnimation 0s 10.25 both alternate-reverse',
|
||||||
|
});
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.75,
|
||||||
|
'Initial value of progress');
|
||||||
|
anim.currentTime += 2.5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.75,
|
||||||
|
'Value of progress');
|
||||||
|
anim.currentTime -= 5 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 1.0,
|
||||||
|
'Value of progress');
|
||||||
|
anim.finish()
|
||||||
|
assert_equals(anim.effect.getComputedTiming().progress, 0.75,
|
||||||
|
'Value of progress');
|
||||||
|
}, 'progress of a non-integral repeating zero-duration animation ' +
|
||||||
|
'with alternate-reverse direction');
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------
|
||||||
|
// currentIteration
|
||||||
|
// --------------------
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s 2s' });
|
||||||
|
const effect = div.getAnimations()[0].effect;
|
||||||
|
assert_equals(effect.getComputedTiming().currentIteration, null,
|
||||||
|
'Initial value of currentIteration before phase');
|
||||||
|
}, 'currentIteration of a new animation with no backwards fill is unresolved ' +
|
||||||
|
'in before phase');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s' });
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
|
||||||
|
'Initial value of currentIteration');
|
||||||
|
}, 'currentIteration of a new animation is zero');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
// Note: FillMode here is "both" because
|
||||||
|
// 1. Since this a zero-duration animation, it will already have finished
|
||||||
|
// so it won't be returned by getAnimations() unless it fills forwards.
|
||||||
|
// 2. Fill backwards, so the currentIteration (before phase) wouldn't be
|
||||||
|
// unresolved (null value).
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 0s infinite both' });
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().currentIteration, Infinity,
|
||||||
|
'Initial value of currentIteration in after phase');
|
||||||
|
|
||||||
|
// Seek backwards
|
||||||
|
anim.currentTime -= 2 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
|
||||||
|
'Value of currentIteration count during before phase');
|
||||||
|
}, 'currentIteration of an infinitely repeating zero-duration animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 0s 10.5 both' });
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
// Note: currentIteration = ceil(iteration start + iteration count) - 1
|
||||||
|
assert_equals(anim.effect.getComputedTiming().currentIteration, 10,
|
||||||
|
'Initial value of currentIteration');
|
||||||
|
|
||||||
|
// Seek backwards
|
||||||
|
anim.currentTime -= 2 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
|
||||||
|
'Value of currentIteration count during before phase');
|
||||||
|
}, 'currentIteration of a finitely repeating zero-duration animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, {
|
||||||
|
style: 'animation: moveAnimation 100s 5.5 forwards'
|
||||||
|
});
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
|
||||||
|
'Initial value of currentIteration');
|
||||||
|
// The 3rd iteration
|
||||||
|
// Note: currentIteration = floor(scaled active time / iteration duration)
|
||||||
|
anim.currentTime = 250 * MS_PER_SEC;
|
||||||
|
assert_equals(anim.effect.getComputedTiming().currentIteration, 2,
|
||||||
|
'Value of currentIteration during the 3rd iteration');
|
||||||
|
// Finish
|
||||||
|
anim.finish();
|
||||||
|
assert_equals(anim.effect.getComputedTiming().currentIteration, 5,
|
||||||
|
'Value of currentIteration in after phase');
|
||||||
|
}, 'currentIteration of an animation with a non-integral iteration count');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s 2 forwards' });
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
|
||||||
|
'Initial value of currentIteration');
|
||||||
|
// Finish
|
||||||
|
anim.finish();
|
||||||
|
assert_equals(anim.effect.getComputedTiming().currentIteration, 1,
|
||||||
|
'Value of currentIteration in after phase');
|
||||||
|
}, 'currentIteration of an animation with an integral iteration count');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: moveAnimation 100s forwards' });
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
|
||||||
|
'Initial value of currentIteration');
|
||||||
|
// Finish
|
||||||
|
anim.finish();
|
||||||
|
assert_equals(anim.effect.getComputedTiming().currentIteration, 0,
|
||||||
|
'Value of currentIteration in after phase');
|
||||||
|
}, 'currentIteration of an animation with a default iteration count');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
const effect = new KeyframeEffect(div, {left: ["0px", "100px"]});
|
||||||
|
|
||||||
|
assert_equals(effect.getComputedTiming().currentIteration, null,
|
||||||
|
'currentIteration for orphaned effect');
|
||||||
|
}, 'currentIteration of an AnimationEffect without an Animation');
|
||||||
|
|
||||||
|
// TODO: If iteration duration is Infinity, currentIteration is 0.
|
||||||
|
// However, we cannot set iteration duration to Infinity in CSS Animation now.
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,74 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSSAnimation.currentTime</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.animated-div {
|
||||||
|
margin-left: 10px;
|
||||||
|
/* Make it easier to calculate expected values: */
|
||||||
|
animation-timing-function: linear ! important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim {
|
||||||
|
from { margin-left: 100px; }
|
||||||
|
to { margin-left: 200px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t, { class: 'animated-div' });
|
||||||
|
div.style.animation = 'anim 100s';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
assert_equals(
|
||||||
|
animation.currentTime,
|
||||||
|
0,
|
||||||
|
'Animation.currentTime should be zero when an animation ' +
|
||||||
|
'is initially created'
|
||||||
|
);
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
animation.currentTime = 50 * MS_PER_SEC;
|
||||||
|
|
||||||
|
assert_time_equals_literal(
|
||||||
|
animation.currentTime,
|
||||||
|
50 * MS_PER_SEC,
|
||||||
|
'Check setting of currentTime actually works'
|
||||||
|
);
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '150px');
|
||||||
|
}, 'currentTime can be used to seek a CSS animation');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t, { class: 'animated-div' });
|
||||||
|
div.style.animation = 'anim 100s';
|
||||||
|
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
assert_throws(
|
||||||
|
new TypeError(),
|
||||||
|
() => {
|
||||||
|
animation.currentTime = null;
|
||||||
|
},
|
||||||
|
'Expect TypeError exception on trying to set Animation.currentTime to null'
|
||||||
|
);
|
||||||
|
}, 'Setting currentTime to null on a CSS animation throws');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,29 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSSAnimation.id</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes abc { }
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'abc 100s';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_equals(animation.id, '', 'id for CSS Animation is initially empty');
|
||||||
|
|
||||||
|
animation.id = 'anim'
|
||||||
|
assert_equals(animation.id, 'anim', 'animation.id reflects the value set');
|
||||||
|
}, 'Animation.id for CSS Animations');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,172 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Pausing a CSSAnimation</title>
|
||||||
|
<link rel="help"
|
||||||
|
href="https://drafts.csswg.org/css-animations-2/#animation-play-state">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes anim {
|
||||||
|
0% { margin-left: 0px }
|
||||||
|
100% { margin-left: 10000px }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const getMarginLeft = cs => parseFloat(cs.marginLeft);
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
const cs = getComputedStyle(div);
|
||||||
|
div.style.animation = 'anim 1000s paused';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_equals(getMarginLeft(cs), 0,
|
||||||
|
'Initial value of margin-left is zero');
|
||||||
|
animation.play();
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
await waitForNextFrame();
|
||||||
|
|
||||||
|
assert_greater_than(getMarginLeft(cs), 0,
|
||||||
|
'Playing value of margin-left is greater than zero');
|
||||||
|
}, 'play() overrides animation-play-state');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
const cs = getComputedStyle(div);
|
||||||
|
div.style.animation = 'anim 1000s paused';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_equals(getMarginLeft(cs), 0,
|
||||||
|
'Initial value of margin-left is zero');
|
||||||
|
|
||||||
|
animation.pause();
|
||||||
|
div.style.animationPlayState = 'running';
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
await waitForNextFrame();
|
||||||
|
|
||||||
|
assert_equals(cs.animationPlayState, 'running',
|
||||||
|
'animation-play-state is running');
|
||||||
|
assert_equals(getMarginLeft(cs), 0,
|
||||||
|
'Paused value of margin-left is zero');
|
||||||
|
}, 'pause() overrides animation-play-state');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
const cs = getComputedStyle(div);
|
||||||
|
div.style.animation = 'anim 1000s paused';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_equals(getMarginLeft(cs), 0,
|
||||||
|
'Initial value of margin-left is zero');
|
||||||
|
animation.play();
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
div.style.animationPlayState = 'running';
|
||||||
|
cs.animationPlayState; // Trigger style resolution
|
||||||
|
await waitForNextFrame();
|
||||||
|
|
||||||
|
assert_equals(cs.animationPlayState, 'running',
|
||||||
|
'animation-play-state is running');
|
||||||
|
div.style.animationPlayState = 'paused';
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
assert_equals(cs.animationPlayState, 'paused',
|
||||||
|
'animation-play-state is paused');
|
||||||
|
const previousAnimVal = getMarginLeft(cs);
|
||||||
|
await waitForNextFrame();
|
||||||
|
|
||||||
|
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||||
|
'Animated value of margin-left does not change when'
|
||||||
|
+ ' paused by style');
|
||||||
|
}, 'play() is overridden by later setting "animation-play-state: paused"');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
const cs = getComputedStyle(div);
|
||||||
|
div.style.animation = 'anim 1000s';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_equals(getMarginLeft(cs), 0,
|
||||||
|
'Initial value of margin-left is zero');
|
||||||
|
|
||||||
|
// Set the specified style first. If implementations fail to
|
||||||
|
// apply the style changes first, they will ignore the redundant
|
||||||
|
// call to play() and fail to correctly override the pause style.
|
||||||
|
div.style.animationPlayState = 'paused';
|
||||||
|
animation.play();
|
||||||
|
const previousAnimVal = getMarginLeft(cs);
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
await waitForNextFrame();
|
||||||
|
|
||||||
|
assert_equals(cs.animationPlayState, 'paused',
|
||||||
|
'animation-play-state is paused');
|
||||||
|
assert_greater_than(getMarginLeft(cs), previousAnimVal,
|
||||||
|
'Playing value of margin-left is increasing');
|
||||||
|
}, 'play() flushes pending changes to animation-play-state first');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
const cs = getComputedStyle(div);
|
||||||
|
div.style.animation = 'anim 1000s paused';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_equals(getMarginLeft(cs), 0,
|
||||||
|
'Initial value of margin-left is zero');
|
||||||
|
|
||||||
|
// Unlike the previous test for play(), since calling pause() is sticky,
|
||||||
|
// we'll apply it even if the underlying style also says we're paused.
|
||||||
|
//
|
||||||
|
// We would like to test that implementations flush styles before running
|
||||||
|
// pause() but actually there's no style we can currently set that will
|
||||||
|
// change the behavior of pause(). That may change in the future
|
||||||
|
// (e.g. if we introduce animation-timeline or animation-playback-rate etc.).
|
||||||
|
//
|
||||||
|
// For now this just serves as a sanity check that we do the same thing
|
||||||
|
// even if we set style before calling the API.
|
||||||
|
div.style.animationPlayState = 'running';
|
||||||
|
animation.pause();
|
||||||
|
const previousAnimVal = getMarginLeft(cs);
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
await waitForNextFrame();
|
||||||
|
|
||||||
|
assert_equals(cs.animationPlayState, 'running',
|
||||||
|
'animation-play-state is running');
|
||||||
|
assert_equals(getMarginLeft(cs), previousAnimVal,
|
||||||
|
'Paused value of margin-left does not change');
|
||||||
|
}, 'pause() applies pending changes to animation-play-state first');
|
||||||
|
// (Note that we can't actually test for this; see comment above, in test-body.)
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: anim 1000s' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
let readyPromiseRun = false;
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
div.style.animationPlayState = 'paused';
|
||||||
|
assert_true(animation.pending && animation.playState === 'paused',
|
||||||
|
'Animation is pause-pending');
|
||||||
|
|
||||||
|
// Set current time
|
||||||
|
animation.currentTime = 5 * MS_PER_SEC;
|
||||||
|
assert_equals(animation.playState, 'paused',
|
||||||
|
'Animation is paused immediately after setting currentTime');
|
||||||
|
assert_equals(animation.startTime, null,
|
||||||
|
'Animation startTime is unresolved immediately after ' +
|
||||||
|
'setting currentTime');
|
||||||
|
assert_equals(animation.currentTime, 5 * MS_PER_SEC,
|
||||||
|
'Animation currentTime does not change when forcing a ' +
|
||||||
|
'pause operation to complete');
|
||||||
|
|
||||||
|
// The ready promise should now be resolved. If it's not then test will
|
||||||
|
// probably time out before anything else happens that causes it to resolve.
|
||||||
|
await animation.ready;
|
||||||
|
}, 'Setting the current time completes a pending pause');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,57 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSSAnimation.playState</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes anim { }
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { 'style': 'animation: anim 100s' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_true(animation.pending);
|
||||||
|
assert_equals(animation.playState, 'running');
|
||||||
|
assert_equals(animation.startTime, null);
|
||||||
|
}, 'A new CSS animation is initially play-pending');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { 'style': 'animation: anim 1000s paused' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_equals(animation.playState, 'paused');
|
||||||
|
}, 'Animation returns correct playState when paused');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { 'style': 'animation: anim 1000s' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
animation.pause();
|
||||||
|
assert_equals(animation.playState, 'paused');
|
||||||
|
}, 'Animation.playState updates when paused by script');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { 'style': 'animation: anim 1000s paused' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
div.style.animationPlayState = 'running';
|
||||||
|
|
||||||
|
// This test also checks that calling playState flushes style
|
||||||
|
assert_equals(animation.playState, 'running',
|
||||||
|
'Animation.playState reports running after updating'
|
||||||
|
+ ' animation-play-state (got: ' + animation.playState + ')');
|
||||||
|
}, 'Animation.playState updates when resumed by setting style');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { 'style': 'animation: anim 1000s' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
animation.cancel();
|
||||||
|
assert_equals(animation.playState, 'idle');
|
||||||
|
}, 'Animation returns correct playState when canceled');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,100 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSSAnimation.ready</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes abc {
|
||||||
|
to { transform: translate(10px) }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'abc 100s paused';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
const originalReadyPromise = animation.ready;
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
div.style.animationPlayState = 'running';
|
||||||
|
assert_not_equals(animation.ready, originalReadyPromise,
|
||||||
|
'After updating animation-play-state a new ready promise'
|
||||||
|
+ ' object is created');
|
||||||
|
}, 'A new ready promise is created when setting animation-play-state: running');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
// Set up pending animation
|
||||||
|
div.style.animation = 'abc 100s';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_true(animation.pending, 'Animation is initially pending');
|
||||||
|
const readyPromise = animation.ready;
|
||||||
|
|
||||||
|
// Cancel the animation and flush styles
|
||||||
|
div.style.animation = '';
|
||||||
|
getComputedStyle(div).animation;
|
||||||
|
|
||||||
|
await promise_rejects(t, 'AbortError', readyPromise,
|
||||||
|
'ready promise is rejected with AbortError');
|
||||||
|
}, 'ready promise is rejected when an animation is canceled by resetting'
|
||||||
|
+ ' the animation property');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
// As before, but this time instead of removing all animations, simply update
|
||||||
|
// the list of animations. At least for Firefox, updating is a different
|
||||||
|
// code path.
|
||||||
|
|
||||||
|
// Set up pending animation
|
||||||
|
div.style.animation = 'abc 100s';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_true(animation.pending, 'Animation is initially pending');
|
||||||
|
const readyPromise = animation.ready;
|
||||||
|
|
||||||
|
// Update the animation and flush styles
|
||||||
|
div.style.animation = 'def 100s';
|
||||||
|
getComputedStyle(div).animation;
|
||||||
|
|
||||||
|
await promise_rejects(t, 'AbortError', readyPromise,
|
||||||
|
'ready promise is rejected with AbortError');
|
||||||
|
}, 'ready promise is rejected when an animation is canceled by updating'
|
||||||
|
+ ' the animation property');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: abc 100s' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
const originalReadyPromise = animation.ready;
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
div.style.animationPlayState = 'paused';
|
||||||
|
assert_not_equals(animation.ready, originalReadyPromise,
|
||||||
|
'A new Promise object is generated when setting'
|
||||||
|
+ ' animation-play-state: paused');
|
||||||
|
}, 'A new ready promise is created when setting animation-play-state: paused');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: abc 100s' });
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
div.style.animationPlayState = 'paused';
|
||||||
|
const firstReadyPromise = animation.ready;
|
||||||
|
animation.pause();
|
||||||
|
assert_equals(animation.ready, firstReadyPromise,
|
||||||
|
'Ready promise objects are identical after redundant pause');
|
||||||
|
}, 'Pausing twice re-uses the same Promise');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,75 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSSAnimation.startTime</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#cssanimation">
|
||||||
|
<style>
|
||||||
|
|
||||||
|
.animated-div {
|
||||||
|
margin-left: 10px;
|
||||||
|
/* Make it easier to calculate expected values: */
|
||||||
|
animation-timing-function: linear ! important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim {
|
||||||
|
from { margin-left: 100px; }
|
||||||
|
to { margin-left: 200px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { 'class': 'animated-div' });
|
||||||
|
div.style.animation = 'anim 100s 100s';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
const timelineTime = animation.timeline.currentTime;
|
||||||
|
animation.startTime = timelineTime;
|
||||||
|
|
||||||
|
assert_times_equal(animation.startTime, timelineTime,
|
||||||
|
'Check setting of startTime actually works');
|
||||||
|
}, 'The start time of a CSS animation can be set');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t, { 'class': 'animated-div' });
|
||||||
|
div.style.animation = 'anim 100s 100s';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
// Seek to the half-way point
|
||||||
|
animation.startTime = animation.timeline.currentTime - 150 * MS_PER_SEC;
|
||||||
|
|
||||||
|
assert_equals(getComputedStyle(div).marginLeft, '150px');
|
||||||
|
}, 'The start time can be set to seek a CSS animation');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t, { class: 'animated-div' });
|
||||||
|
const eventWatcher = new EventWatcher(t, div, [
|
||||||
|
'animationstart',
|
||||||
|
'animationend',
|
||||||
|
]);
|
||||||
|
div.style.animation = 'anim 100s 100s';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
animation.startTime = animation.timeline.currentTime - 100 * MS_PER_SEC;
|
||||||
|
await eventWatcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
animation.startTime = animation.timeline.currentTime - 200 * MS_PER_SEC;
|
||||||
|
await eventWatcher.wait_for('animationend');
|
||||||
|
}, 'Seeking a CSS animation using the start time dispatches animation events');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,77 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSSPseudoElement.getAnimations() for CSS animations</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes anim1 { }
|
||||||
|
@keyframes anim2 { }
|
||||||
|
.before::before {
|
||||||
|
animation: anim1 10s;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
.after-with-mix-anims-trans::after {
|
||||||
|
content: '';
|
||||||
|
animation: anim1 10s, anim2 10s;
|
||||||
|
width: 0px;
|
||||||
|
height: 0px;
|
||||||
|
transition: all 100s;
|
||||||
|
}
|
||||||
|
.after-change::after {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { class: 'before' });
|
||||||
|
const pseudoTarget = document.getAnimations()[0].effect.target;
|
||||||
|
assert_equals(pseudoTarget.getAnimations().length, 1,
|
||||||
|
'Expected number of animations are returned');
|
||||||
|
assert_equals(pseudoTarget.getAnimations()[0].animationName, 'anim1',
|
||||||
|
'CSS animation name matches');
|
||||||
|
}, 'getAnimations returns CSSAnimation objects');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { class: 'after-with-mix-anims-trans' });
|
||||||
|
// Trigger transitions
|
||||||
|
flushComputedStyle(div);
|
||||||
|
div.classList.add('after-change');
|
||||||
|
|
||||||
|
// Create additional animation on the pseudo-element from script
|
||||||
|
const pseudoTarget = document.getAnimations()[0].effect.target;
|
||||||
|
const effect = new KeyframeEffect(pseudoTarget,
|
||||||
|
{ background: ["blue", "red"] },
|
||||||
|
3 * MS_PER_SEC);
|
||||||
|
const newAnimation = new Animation(effect, document.timeline);
|
||||||
|
newAnimation.id = 'scripted-anim';
|
||||||
|
newAnimation.play();
|
||||||
|
|
||||||
|
// Check order - the script-generated animation should appear later
|
||||||
|
const anims = pseudoTarget.getAnimations();
|
||||||
|
assert_equals(anims.length, 5,
|
||||||
|
'Got expected number of animations/trnasitions running on ' +
|
||||||
|
'::after pseudo element');
|
||||||
|
assert_equals(anims[0].transitionProperty, 'height',
|
||||||
|
'1st animation is the 1st transition sorted by name');
|
||||||
|
assert_equals(anims[1].transitionProperty, 'width',
|
||||||
|
'2nd animation is the 2nd transition sorted by name ');
|
||||||
|
assert_equals(anims[2].animationName, 'anim1',
|
||||||
|
'3rd animation is the 1st animation in animation-name list');
|
||||||
|
assert_equals(anims[3].animationName, 'anim2',
|
||||||
|
'4rd animation is the 2nd animation in animation-name list');
|
||||||
|
assert_equals(anims[4].id, 'scripted-anim',
|
||||||
|
'Animation added by script appears last');
|
||||||
|
}, 'getAnimations returns CSS transitions/animations, and script-generated ' +
|
||||||
|
'animations in the expected order');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,286 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Document.getAnimations() for CSS animations</title>
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#animation-composite-order">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes animLeft {
|
||||||
|
to { left: 100px }
|
||||||
|
}
|
||||||
|
@keyframes animTop {
|
||||||
|
to { top: 100px }
|
||||||
|
}
|
||||||
|
@keyframes animBottom {
|
||||||
|
to { bottom: 100px }
|
||||||
|
}
|
||||||
|
@keyframes animRight {
|
||||||
|
to { right: 100px }
|
||||||
|
}
|
||||||
|
::before {
|
||||||
|
content: ''
|
||||||
|
}
|
||||||
|
::after {
|
||||||
|
content: ''
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
assert_equals(document.getAnimations().length, 0,
|
||||||
|
'getAnimations returns an empty sequence for a document'
|
||||||
|
+ ' with no animations');
|
||||||
|
}, 'getAnimations for non-animated content');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
// Add an animation
|
||||||
|
div.style.animation = 'animLeft 100s';
|
||||||
|
assert_equals(document.getAnimations().length, 1,
|
||||||
|
'getAnimations returns a running CSS Animation');
|
||||||
|
|
||||||
|
// Add another animation
|
||||||
|
div.style.animation = 'animLeft 100s, animTop 100s';
|
||||||
|
assert_equals(document.getAnimations().length, 2,
|
||||||
|
'getAnimations returns two running CSS Animations');
|
||||||
|
|
||||||
|
// Remove both
|
||||||
|
div.style.animation = '';
|
||||||
|
assert_equals(document.getAnimations().length, 0,
|
||||||
|
'getAnimations returns no running CSS Animations');
|
||||||
|
}, 'getAnimations for CSS Animations');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'animLeft 100s, animTop 100s, animRight 100s, ' +
|
||||||
|
'animBottom 100s';
|
||||||
|
|
||||||
|
const animations = document.getAnimations();
|
||||||
|
assert_equals(animations.length, 4,
|
||||||
|
'getAnimations returns all running CSS Animations');
|
||||||
|
assert_equals(animations[0].animationName, 'animLeft',
|
||||||
|
'Order of first animation returned');
|
||||||
|
assert_equals(animations[1].animationName, 'animTop',
|
||||||
|
'Order of second animation returned');
|
||||||
|
assert_equals(animations[2].animationName, 'animRight',
|
||||||
|
'Order of third animation returned');
|
||||||
|
assert_equals(animations[3].animationName, 'animBottom',
|
||||||
|
'Order of fourth animation returned');
|
||||||
|
}, 'Order of CSS Animations - within an element');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div1 = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||||
|
const div2 = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||||
|
const div3 = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||||
|
const div4 = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||||
|
|
||||||
|
let animations = document.getAnimations();
|
||||||
|
assert_equals(animations.length, 4,
|
||||||
|
'getAnimations returns all running CSS Animations');
|
||||||
|
assert_equals(animations[0].effect.target, div1,
|
||||||
|
'Order of first animation returned');
|
||||||
|
assert_equals(animations[1].effect.target, div2,
|
||||||
|
'Order of second animation returned');
|
||||||
|
assert_equals(animations[2].effect.target, div3,
|
||||||
|
'Order of third animation returned');
|
||||||
|
assert_equals(animations[3].effect.target, div4,
|
||||||
|
'Order of fourth animation returned');
|
||||||
|
|
||||||
|
// Order should be depth-first pre-order so add some depth as follows:
|
||||||
|
//
|
||||||
|
// <parent>
|
||||||
|
// / |
|
||||||
|
// 2 3
|
||||||
|
// / \
|
||||||
|
// 1 4
|
||||||
|
//
|
||||||
|
// Which should give: 2, 1, 4, 3
|
||||||
|
div2.appendChild(div1);
|
||||||
|
div2.appendChild(div4);
|
||||||
|
animations = document.getAnimations();
|
||||||
|
assert_equals(animations[0].effect.target, div2,
|
||||||
|
'Order of first animation returned after tree surgery');
|
||||||
|
assert_equals(animations[1].effect.target, div1,
|
||||||
|
'Order of second animation returned after tree surgery');
|
||||||
|
assert_equals(animations[2].effect.target, div4,
|
||||||
|
'Order of third animation returned after tree surgery');
|
||||||
|
assert_equals(animations[3].effect.target, div3,
|
||||||
|
'Order of fourth animation returned after tree surgery');
|
||||||
|
|
||||||
|
}, 'Order of CSS Animations - across elements');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div1 = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
|
||||||
|
const div2 = addDiv(t, { style: 'animation: animBottom 100s' });
|
||||||
|
|
||||||
|
let expectedResults = [ [ div1, 'animLeft' ],
|
||||||
|
[ div1, 'animTop' ],
|
||||||
|
[ div2, 'animBottom' ] ];
|
||||||
|
let animations = document.getAnimations();
|
||||||
|
assert_equals(animations.length, expectedResults.length,
|
||||||
|
'getAnimations returns all running CSS Animations');
|
||||||
|
animations.forEach((anim, i) => {
|
||||||
|
assert_equals(anim.effect.target, expectedResults[i][0],
|
||||||
|
'Target of animation in position ' + i);
|
||||||
|
assert_equals(anim.animationName, expectedResults[i][1],
|
||||||
|
'Name of animation in position ' + i);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Modify tree structure and animation list
|
||||||
|
div2.appendChild(div1);
|
||||||
|
div1.style.animation = 'animLeft 100s, animRight 100s, animTop 100s';
|
||||||
|
|
||||||
|
expectedResults = [ [ div2, 'animBottom' ],
|
||||||
|
[ div1, 'animLeft' ],
|
||||||
|
[ div1, 'animRight' ],
|
||||||
|
[ div1, 'animTop' ] ];
|
||||||
|
animations = document.getAnimations();
|
||||||
|
assert_equals(animations.length, expectedResults.length,
|
||||||
|
'getAnimations returns all running CSS Animations after ' +
|
||||||
|
'making changes');
|
||||||
|
animations.forEach((anim, i) => {
|
||||||
|
assert_equals(anim.effect.target, expectedResults[i][0],
|
||||||
|
'Target of animation in position ' + i + ' after changes');
|
||||||
|
assert_equals(anim.animationName, expectedResults[i][1],
|
||||||
|
'Name of animation in position ' + i + ' after changes');
|
||||||
|
});
|
||||||
|
}, 'Order of CSS Animations - across and within elements');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
|
||||||
|
const animLeft = document.getAnimations()[0];
|
||||||
|
assert_equals(animLeft.animationName, 'animLeft',
|
||||||
|
'Originally, animLeft animation comes first');
|
||||||
|
|
||||||
|
// Disassociate animLeft from markup and restart
|
||||||
|
div.style.animation = 'animTop 100s';
|
||||||
|
animLeft.play();
|
||||||
|
|
||||||
|
const animations = document.getAnimations();
|
||||||
|
assert_equals(animations.length, 2,
|
||||||
|
'getAnimations returns markup-bound and free animations');
|
||||||
|
assert_equals(animations[0].animationName, 'animTop',
|
||||||
|
'Markup-bound animations come first');
|
||||||
|
assert_equals(animations[1], animLeft, 'Free animations come last');
|
||||||
|
}, 'Order of CSS Animations - markup-bound vs free animations');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: animLeft 100s, animTop 100s' });
|
||||||
|
const animLeft = document.getAnimations()[0];
|
||||||
|
const animTop = document.getAnimations()[1];
|
||||||
|
|
||||||
|
// Disassociate both animations from markup and restart in opposite order
|
||||||
|
div.style.animation = '';
|
||||||
|
animTop.play();
|
||||||
|
animLeft.play();
|
||||||
|
|
||||||
|
const animations = document.getAnimations();
|
||||||
|
assert_equals(animations.length, 2,
|
||||||
|
'getAnimations returns free animations');
|
||||||
|
assert_equals(animations[0], animTop,
|
||||||
|
'Free animations are returned in the order they are started');
|
||||||
|
assert_equals(animations[1], animLeft,
|
||||||
|
'Animations started later are returned later');
|
||||||
|
|
||||||
|
// Restarting an animation should have no effect
|
||||||
|
animTop.cancel();
|
||||||
|
animTop.play();
|
||||||
|
assert_equals(document.getAnimations()[0], animTop,
|
||||||
|
'After restarting, the ordering of free animations' +
|
||||||
|
' does not change');
|
||||||
|
}, 'Order of CSS Animations - free animations');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
// Add an animation first
|
||||||
|
const div = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||||
|
div.style.top = '0px';
|
||||||
|
div.style.transition = 'all 100s';
|
||||||
|
flushComputedStyle(div);
|
||||||
|
|
||||||
|
// *Then* add a transition
|
||||||
|
div.style.top = '100px';
|
||||||
|
flushComputedStyle(div);
|
||||||
|
|
||||||
|
// Although the transition was added later, it should come first in the list
|
||||||
|
const animations = document.getAnimations();
|
||||||
|
assert_equals(animations.length, 2,
|
||||||
|
'Both CSS animations and transitions are returned');
|
||||||
|
assert_class_string(animations[0], 'CSSTransition', 'Transition comes first');
|
||||||
|
assert_class_string(animations[1], 'CSSAnimation', 'Animation comes second');
|
||||||
|
}, 'Order of CSS Animations and CSS Transitions');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: animLeft 100s forwards' });
|
||||||
|
div.getAnimations()[0].finish();
|
||||||
|
assert_equals(document.getAnimations().length, 1,
|
||||||
|
'Forwards-filling CSS animations are returned');
|
||||||
|
}, 'Finished but filling CSS Animations are returned');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||||
|
div.getAnimations()[0].finish();
|
||||||
|
assert_equals(document.getAnimations().length, 0,
|
||||||
|
'Non-filling finished CSS animations are not returned');
|
||||||
|
}, 'Finished but not filling CSS Animations are not returned');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: animLeft 100s 100s' });
|
||||||
|
assert_equals(document.getAnimations().length, 1,
|
||||||
|
'Yet-to-start CSS animations are returned');
|
||||||
|
}, 'Yet-to-start CSS Animations are returned');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||||
|
div.getAnimations()[0].cancel();
|
||||||
|
assert_equals(document.getAnimations().length, 0,
|
||||||
|
'CSS animations canceled by the API are not returned');
|
||||||
|
}, 'CSS Animations canceled via the API are not returned');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: animLeft 100s' });
|
||||||
|
const anim = div.getAnimations()[0];
|
||||||
|
anim.cancel();
|
||||||
|
anim.play();
|
||||||
|
assert_equals(document.getAnimations().length, 1,
|
||||||
|
'CSS animations canceled and restarted by the API are ' +
|
||||||
|
'returned');
|
||||||
|
}, 'CSS Animations canceled and restarted via the API are returned');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
addStyle(t, { '#parent::after': 'animation: animLeft 10s;',
|
||||||
|
'#parent::before': 'animation: animRight 10s;' });
|
||||||
|
// create two divs with these arrangement:
|
||||||
|
// parent
|
||||||
|
// ::before,
|
||||||
|
// ::after
|
||||||
|
// |
|
||||||
|
// child
|
||||||
|
const parent = addDiv(t, { 'id': 'parent' });
|
||||||
|
const child = addDiv(t);
|
||||||
|
parent.appendChild(child);
|
||||||
|
for (const div of [parent, child]) {
|
||||||
|
div.setAttribute('style', 'animation: animBottom 10s');
|
||||||
|
}
|
||||||
|
|
||||||
|
const anims = document.getAnimations();
|
||||||
|
assert_equals(anims.length, 4,
|
||||||
|
'CSS animations on both pseudo-elements and elements ' +
|
||||||
|
'are returned');
|
||||||
|
assert_equals(anims[0].effect.target, parent,
|
||||||
|
'The animation targeting the parent element comes first');
|
||||||
|
assert_equals(anims[1].effect.target.type, '::before',
|
||||||
|
'The animation targeting the ::before element comes second');
|
||||||
|
assert_equals(anims[2].effect.target.type, '::after',
|
||||||
|
'The animation targeting the ::after element comes third');
|
||||||
|
assert_equals(anims[3].effect.target, child,
|
||||||
|
'The animation targeting the child element comes last');
|
||||||
|
}, 'CSS Animations targetting (pseudo-)elements should have correct order ' +
|
||||||
|
'after sorting');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,165 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>
|
||||||
|
Element.getAnimations() - Dynamic changes to the list of CSS animations
|
||||||
|
</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes anim1 {
|
||||||
|
to { left: 100px }
|
||||||
|
}
|
||||||
|
@keyframes anim2 { }
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim1 100s';
|
||||||
|
const originalAnimation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
// Wait a moment so we can confirm the startTime doesn't change (and doesn't
|
||||||
|
// simply reflect the current time).
|
||||||
|
await originalAnimation.ready;
|
||||||
|
|
||||||
|
const originalStartTime = originalAnimation.startTime;
|
||||||
|
const originalCurrentTime = originalAnimation.currentTime;
|
||||||
|
|
||||||
|
// Wait a moment so we can confirm the startTime doesn't change (and
|
||||||
|
// doesn't simply reflect the current time).
|
||||||
|
await waitForNextFrame();
|
||||||
|
|
||||||
|
div.style.animationDuration = '200s';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_equals(animation, originalAnimation,
|
||||||
|
'The same Animation is returned after updating'
|
||||||
|
+ ' animation duration');
|
||||||
|
assert_equals(animation.startTime, originalStartTime,
|
||||||
|
'Animations returned by getAnimations preserve'
|
||||||
|
+ ' their startTime even when they are updated');
|
||||||
|
// Sanity check
|
||||||
|
assert_not_equals(animation.currentTime, originalCurrentTime,
|
||||||
|
'Animation.currentTime has updated in next'
|
||||||
|
+ ' requestAnimationFrame callback');
|
||||||
|
}, 'Animations preserve their startTime when changed');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim1 100s, anim1 100s';
|
||||||
|
|
||||||
|
// Store original state
|
||||||
|
let animations = div.getAnimations();
|
||||||
|
const animation1 = animations[0];
|
||||||
|
const animation2 = animations[1];
|
||||||
|
|
||||||
|
// Update first in list
|
||||||
|
div.style.animationDuration = '200s, 100s';
|
||||||
|
animations = div.getAnimations();
|
||||||
|
assert_equals(animations[0], animation1,
|
||||||
|
'First Animation is in same position after update');
|
||||||
|
assert_equals(animations[1], animation2,
|
||||||
|
'Second Animation is in same position after update');
|
||||||
|
}, 'Updated Animations maintain their order in the list');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim1 200s, anim1 100s';
|
||||||
|
|
||||||
|
// Store original state
|
||||||
|
let animations = div.getAnimations();
|
||||||
|
const animation1 = animations[0];
|
||||||
|
const animation2 = animations[1];
|
||||||
|
|
||||||
|
// Wait before continuing so we can compare start times (otherwise the
|
||||||
|
// new Animation objects and existing Animation objects will all have the same
|
||||||
|
// start time).
|
||||||
|
await waitForAllAnimations(animations);
|
||||||
|
await waitForFrame();
|
||||||
|
|
||||||
|
// Swap duration of first and second in list and prepend animation at the
|
||||||
|
// same time
|
||||||
|
div.style.animation = 'anim1 100s, anim1 100s, anim1 200s';
|
||||||
|
animations = div.getAnimations();
|
||||||
|
assert_true(animations[0] !== animation1 && animations[0] !== animation2,
|
||||||
|
'New Animation is prepended to start of list');
|
||||||
|
assert_equals(animations[1], animation1,
|
||||||
|
'First animation is in second position after update');
|
||||||
|
assert_equals(animations[2], animation2,
|
||||||
|
'Second animation is in third position after update');
|
||||||
|
assert_equals(animations[1].startTime, animations[2].startTime,
|
||||||
|
'Old animations have the same start time');
|
||||||
|
assert_equals(animations[0].startTime, null,
|
||||||
|
'New animation has a null start time');
|
||||||
|
|
||||||
|
await animations[0].ready;
|
||||||
|
|
||||||
|
assert_greater_than(animations[0].startTime, animations[1].startTime,
|
||||||
|
'New animation has later start time');
|
||||||
|
}, 'Only the startTimes of existing animations are preserved');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim1 100s, anim1 100s';
|
||||||
|
const secondAnimation = div.getAnimations()[1];
|
||||||
|
|
||||||
|
// Wait before continuing so we can compare start times
|
||||||
|
await secondAnimation.ready;
|
||||||
|
await waitForNextFrame();
|
||||||
|
|
||||||
|
// Trim list of animations
|
||||||
|
div.style.animationName = 'anim1';
|
||||||
|
const animations = div.getAnimations();
|
||||||
|
assert_equals(animations.length, 1, 'List of Animations was trimmed');
|
||||||
|
assert_equals(animations[0], secondAnimation,
|
||||||
|
'Remaining Animation is the second one in the list');
|
||||||
|
assert_equals(typeof(animations[0].startTime), 'number',
|
||||||
|
'Remaining Animation has resolved startTime');
|
||||||
|
assert_less_than(animations[0].startTime,
|
||||||
|
animations[0].timeline.currentTime,
|
||||||
|
'Remaining Animation preserves startTime');
|
||||||
|
}, 'Animations are removed from the start of the list while preserving'
|
||||||
|
+ ' the state of existing Animations');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim1 100s';
|
||||||
|
const firstAddedAnimation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
// Wait and add second Animation
|
||||||
|
await firstAddedAnimation.ready;
|
||||||
|
await waitForFrame();
|
||||||
|
|
||||||
|
div.style.animation = 'anim1 100s, anim1 100s';
|
||||||
|
const secondAddedAnimation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
// Wait again and add another Animation
|
||||||
|
await secondAddedAnimation.ready;
|
||||||
|
await waitForFrame();
|
||||||
|
|
||||||
|
div.style.animation = 'anim1 100s, anim2 100s, anim1 100s';
|
||||||
|
const animations = div.getAnimations();
|
||||||
|
assert_not_equals(firstAddedAnimation, secondAddedAnimation,
|
||||||
|
'New Animations are added to start of the list');
|
||||||
|
assert_equals(animations[0], secondAddedAnimation,
|
||||||
|
'Second Animation remains in same position after'
|
||||||
|
+ ' interleaving');
|
||||||
|
assert_equals(animations[2], firstAddedAnimation,
|
||||||
|
'First Animation remains in same position after'
|
||||||
|
+ ' interleaving');
|
||||||
|
await animations[1].ready;
|
||||||
|
|
||||||
|
assert_greater_than(animations[1].startTime, animations[0].startTime,
|
||||||
|
'Interleaved animation starts later than existing ' +
|
||||||
|
'animations');
|
||||||
|
assert_greater_than(animations[0].startTime, animations[2].startTime,
|
||||||
|
'Original animations retain their start time');
|
||||||
|
}, 'Animation state is preserved when interleaving animations in list');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,449 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Element.getAnimations() for CSS animations</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes anim1 {
|
||||||
|
to { left: 100px }
|
||||||
|
}
|
||||||
|
@keyframes anim2 {
|
||||||
|
to { top: 100px }
|
||||||
|
}
|
||||||
|
@keyframes multiPropAnim {
|
||||||
|
to { background: green, opacity: 0.5, left: 100px, top: 100px }
|
||||||
|
}
|
||||||
|
::before {
|
||||||
|
content: ''
|
||||||
|
}
|
||||||
|
::after {
|
||||||
|
content: ''
|
||||||
|
}
|
||||||
|
@keyframes empty { }
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
assert_equals(div.getAnimations().length, 0,
|
||||||
|
'getAnimations returns an empty sequence for an element'
|
||||||
|
+ ' with no animations');
|
||||||
|
}, 'getAnimations for non-animated content');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
// Add an animation
|
||||||
|
div.style.animation = 'anim1 100s';
|
||||||
|
let animations = div.getAnimations();
|
||||||
|
assert_equals(animations.length, 1,
|
||||||
|
'getAnimations returns an Animation running CSS Animations');
|
||||||
|
await animations[0].ready;
|
||||||
|
|
||||||
|
// Add a second animation
|
||||||
|
div.style.animation = 'anim1 100s, anim2 100s';
|
||||||
|
animations = div.getAnimations();
|
||||||
|
assert_equals(animations.length, 2,
|
||||||
|
'getAnimations returns one CSSAnimation for each value of animation-name');
|
||||||
|
// (We don't check the order of the Animations since that is covered by tests
|
||||||
|
// later in this file.)
|
||||||
|
}, 'getAnimations for CSS Animations');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t, { style: 'animation: anim1 100s' });
|
||||||
|
assert_class_string(div.getAnimations()[0], 'CSSAnimation',
|
||||||
|
'Interface of returned animation is CSSAnimation');
|
||||||
|
}, 'getAnimations returns CSSAnimation objects for CSS Animations');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
// Add an animation that targets multiple properties
|
||||||
|
div.style.animation = 'multiPropAnim 100s';
|
||||||
|
assert_equals(div.getAnimations().length, 1,
|
||||||
|
'getAnimations returns only one Animation for a CSS Animation'
|
||||||
|
+ ' that targets multiple properties');
|
||||||
|
}, 'getAnimations for multi-property animations');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
// Add an animation
|
||||||
|
div.style.backgroundColor = 'red';
|
||||||
|
div.style.animation = 'anim1 100s';
|
||||||
|
getComputedStyle(div).backgroundColor;
|
||||||
|
|
||||||
|
// Wait until a frame after the animation starts, then add a transition
|
||||||
|
let animations = div.getAnimations();
|
||||||
|
await animations[0].ready;
|
||||||
|
await waitForFrame();
|
||||||
|
|
||||||
|
div.style.transition = 'all 100s';
|
||||||
|
div.style.backgroundColor = 'green';
|
||||||
|
|
||||||
|
animations = div.getAnimations();
|
||||||
|
assert_equals(animations.length, 2,
|
||||||
|
'getAnimations returns Animations for both animations and'
|
||||||
|
+ ' transitions that run simultaneously');
|
||||||
|
assert_class_string(animations[0], 'CSSTransition',
|
||||||
|
'First-returned animation is the CSS Transition');
|
||||||
|
assert_class_string(animations[1], 'CSSAnimation',
|
||||||
|
'Second-returned animation is the CSS Animation');
|
||||||
|
}, 'getAnimations for both CSS Animations and CSS Transitions at once');
|
||||||
|
|
||||||
|
async_test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
// Set up event listener
|
||||||
|
div.addEventListener('animationend', t.step_func(() => {
|
||||||
|
assert_equals(div.getAnimations().length, 0,
|
||||||
|
'getAnimations does not return Animations for finished '
|
||||||
|
+ ' (and non-forwards-filling) CSS Animations');
|
||||||
|
t.done();
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Add a very short animation
|
||||||
|
div.style.animation = 'anim1 0.01s';
|
||||||
|
}, 'getAnimations for CSS Animations that have finished');
|
||||||
|
|
||||||
|
async_test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
// Set up event listener
|
||||||
|
div.addEventListener('animationend', t.step_func(() => {
|
||||||
|
assert_equals(div.getAnimations().length, 1,
|
||||||
|
'getAnimations returns Animations for CSS Animations that have'
|
||||||
|
+ ' finished but are filling forwards');
|
||||||
|
t.done();
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Add a very short animation
|
||||||
|
div.style.animation = 'anim1 0.01s forwards';
|
||||||
|
}, 'getAnimations for CSS Animations that have finished but are'
|
||||||
|
+ ' forwards filling');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'none 100s';
|
||||||
|
|
||||||
|
let animations = div.getAnimations();
|
||||||
|
assert_equals(animations.length, 0,
|
||||||
|
'getAnimations returns an empty sequence for an element'
|
||||||
|
+ ' with animation-name: none');
|
||||||
|
|
||||||
|
div.style.animation = 'none 100s, anim1 100s';
|
||||||
|
animations = div.getAnimations();
|
||||||
|
assert_equals(animations.length, 1,
|
||||||
|
'getAnimations returns Animations only for those CSS Animations whose'
|
||||||
|
+ ' animation-name is not none');
|
||||||
|
}, 'getAnimations for CSS Animations with animation-name: none');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'missing 100s';
|
||||||
|
let animations = div.getAnimations();
|
||||||
|
assert_equals(animations.length, 0,
|
||||||
|
'getAnimations returns an empty sequence for an element'
|
||||||
|
+ ' with animation-name: missing');
|
||||||
|
|
||||||
|
div.style.animation = 'anim1 100s, missing 100s';
|
||||||
|
animations = div.getAnimations();
|
||||||
|
assert_equals(animations.length, 1,
|
||||||
|
'getAnimations returns Animations only for those CSS Animations whose'
|
||||||
|
+ ' animation-name is found');
|
||||||
|
}, 'getAnimations for CSS Animations with animation-name: missing');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim1 100s, notyet 100s';
|
||||||
|
let animations = div.getAnimations();
|
||||||
|
assert_equals(animations.length, 1,
|
||||||
|
'getAnimations initally only returns Animations for CSS Animations whose'
|
||||||
|
+ ' animation-name is found');
|
||||||
|
|
||||||
|
await animations[0].ready;
|
||||||
|
await waitForFrame();
|
||||||
|
|
||||||
|
const keyframes = '@keyframes notyet { to { left: 100px; } }';
|
||||||
|
document.styleSheets[0].insertRule(keyframes, 0);
|
||||||
|
animations = div.getAnimations();
|
||||||
|
assert_equals(animations.length, 2,
|
||||||
|
'getAnimations includes Animation when @keyframes rule is added'
|
||||||
|
+ ' later');
|
||||||
|
await waitForAllAnimations(animations);
|
||||||
|
|
||||||
|
assert_true(animations[0].startTime < animations[1].startTime,
|
||||||
|
'Newly added animation has a later start time');
|
||||||
|
document.styleSheets[0].deleteRule(0);
|
||||||
|
}, 'getAnimations for CSS Animations where the @keyframes rule is added'
|
||||||
|
+ ' later');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim1 100s, anim1 100s';
|
||||||
|
assert_equals(div.getAnimations().length, 2,
|
||||||
|
'getAnimations returns one Animation for each CSS animation-name'
|
||||||
|
+ ' even if the names are duplicated');
|
||||||
|
}, 'getAnimations for CSS Animations with duplicated animation-name');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'empty 100s';
|
||||||
|
assert_equals(div.getAnimations().length, 1,
|
||||||
|
'getAnimations returns Animations for CSS animations with an'
|
||||||
|
+ ' empty keyframes rule');
|
||||||
|
}, 'getAnimations for CSS Animations with empty keyframes rule');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim1 100s 100s';
|
||||||
|
const animations = div.getAnimations();
|
||||||
|
assert_equals(animations.length, 1,
|
||||||
|
'getAnimations returns animations for CSS animations whose'
|
||||||
|
+ ' delay makes them start later');
|
||||||
|
await animations[0].ready;
|
||||||
|
await waitForFrame();
|
||||||
|
|
||||||
|
assert_true(animations[0].startTime <= document.timeline.currentTime,
|
||||||
|
'For CSS Animations in delay phase, the start time of the Animation is'
|
||||||
|
+ ' not in the future');
|
||||||
|
}, 'getAnimations for CSS animations in delay phase');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim1 0s 100s';
|
||||||
|
assert_equals(div.getAnimations().length, 1,
|
||||||
|
'getAnimations returns animations for CSS animations whose'
|
||||||
|
+ ' duration is zero');
|
||||||
|
div.remove();
|
||||||
|
}, 'getAnimations for zero-duration CSS Animations');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim1 100s';
|
||||||
|
const originalAnimation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
// Update pause state (an Animation change)
|
||||||
|
div.style.animationPlayState = 'paused';
|
||||||
|
const pendingAnimation = div.getAnimations()[0];
|
||||||
|
assert_equals(pendingAnimation.playState, 'paused',
|
||||||
|
'animation\'s play state is updated');
|
||||||
|
assert_equals(originalAnimation, pendingAnimation,
|
||||||
|
'getAnimations returns the same objects even when their'
|
||||||
|
+ ' play state changes');
|
||||||
|
|
||||||
|
// Update duration (an Animation change)
|
||||||
|
div.style.animationDuration = '200s';
|
||||||
|
const extendedAnimation = div.getAnimations()[0];
|
||||||
|
assert_equals(
|
||||||
|
extendedAnimation.effect.getTiming().duration,
|
||||||
|
200 * MS_PER_SEC,
|
||||||
|
'animation\'s duration has been updated'
|
||||||
|
);
|
||||||
|
assert_equals(originalAnimation, extendedAnimation,
|
||||||
|
'getAnimations returns the same objects even when their'
|
||||||
|
+ ' duration changes');
|
||||||
|
}, 'getAnimations returns objects with the same identity');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim1 100s';
|
||||||
|
|
||||||
|
assert_equals(div.getAnimations().length, 1,
|
||||||
|
'getAnimations returns an animation before canceling');
|
||||||
|
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
animation.cancel();
|
||||||
|
assert_equals(div.getAnimations().length, 0,
|
||||||
|
'getAnimations does not return canceled animations');
|
||||||
|
|
||||||
|
animation.play();
|
||||||
|
assert_equals(div.getAnimations().length, 1,
|
||||||
|
'getAnimations returns canceled animations that have been re-started');
|
||||||
|
|
||||||
|
}, 'getAnimations for CSS Animations that are canceled');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim2 100s';
|
||||||
|
|
||||||
|
await div.getAnimations()[0].ready;
|
||||||
|
|
||||||
|
// Prepend to the list and test that even though anim1 was triggered
|
||||||
|
// *after* anim2, it should come first because it appears first
|
||||||
|
// in the animation-name property.
|
||||||
|
div.style.animation = 'anim1 100s, anim2 100s';
|
||||||
|
let anims = div.getAnimations();
|
||||||
|
assert_equals(anims[0].animationName, 'anim1',
|
||||||
|
'animation order after prepending to list');
|
||||||
|
assert_equals(anims[1].animationName, 'anim2',
|
||||||
|
'animation order after prepending to list');
|
||||||
|
|
||||||
|
// Normally calling cancel and play would this push anim1 to the top of
|
||||||
|
// the stack but it shouldn't for CSS animations that map an the
|
||||||
|
// animation-name property.
|
||||||
|
const anim1 = anims[0];
|
||||||
|
anim1.cancel();
|
||||||
|
anim1.play();
|
||||||
|
anims = div.getAnimations();
|
||||||
|
assert_equals(anims[0].animationName, 'anim1',
|
||||||
|
'animation order after canceling and restarting');
|
||||||
|
assert_equals(anims[1].animationName, 'anim2',
|
||||||
|
'animation order after canceling and restarting');
|
||||||
|
}, 'getAnimations for CSS Animations follows animation-name order');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
addStyle(t, { '#target::after': 'animation: anim1 10s;',
|
||||||
|
'#target::before': 'animation: anim1 10s;' });
|
||||||
|
const target = addDiv(t, { 'id': 'target' });
|
||||||
|
target.style.animation = 'anim1 100s';
|
||||||
|
|
||||||
|
const animations = target.getAnimations({ subtree: false });
|
||||||
|
assert_equals(animations.length, 1,
|
||||||
|
'Should find only the element');
|
||||||
|
assert_equals(animations[0].effect.target, target,
|
||||||
|
'Effect target should be the element');
|
||||||
|
}, '{ subtree: false } on a leaf element returns the element\'s animations'
|
||||||
|
+ ' and ignore pseudo-elements');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
addStyle(t, { '#target::after': 'animation: anim1 10s;',
|
||||||
|
'#target::before': 'animation: anim1 10s;' });
|
||||||
|
const target = addDiv(t, { 'id': 'target' });
|
||||||
|
target.style.animation = 'anim1 100s';
|
||||||
|
|
||||||
|
const animations = target.getAnimations({ subtree: true });
|
||||||
|
assert_equals(animations.length, 3,
|
||||||
|
'getAnimations({ subtree: true }) ' +
|
||||||
|
'should return animations on pseudo-elements');
|
||||||
|
assert_equals(animations[0].effect.target, target,
|
||||||
|
'The animation targeting the parent element ' +
|
||||||
|
'should be returned first');
|
||||||
|
assert_equals(animations[1].effect.target.type, '::before',
|
||||||
|
'The animation targeting the ::before pseudo-element ' +
|
||||||
|
'should be returned second');
|
||||||
|
assert_equals(animations[2].effect.target.type, '::after',
|
||||||
|
'The animation targeting the ::after pesudo-element ' +
|
||||||
|
'should be returned last');
|
||||||
|
}, '{ subtree: true } on a leaf element returns the element\'s animations'
|
||||||
|
+ ' and its pseudo-elements\' animations');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
addStyle(t, { '#parent::after': 'animation: anim1 10s;',
|
||||||
|
'#parent::before': 'animation: anim1 10s;',
|
||||||
|
'#child::after': 'animation: anim1 10s;',
|
||||||
|
'#child::before': 'animation: anim1 10s;' });
|
||||||
|
const parent = addDiv(t, { 'id': 'parent' });
|
||||||
|
parent.style.animation = 'anim1 100s';
|
||||||
|
const child = addDiv(t, { 'id': 'child' });
|
||||||
|
child.style.animation = 'anim1 100s';
|
||||||
|
parent.appendChild(child);
|
||||||
|
|
||||||
|
const animations = parent.getAnimations({ subtree: false });
|
||||||
|
assert_equals(animations.length, 1,
|
||||||
|
'Should find only the element even if it has a child');
|
||||||
|
assert_equals(animations[0].effect.target, parent,
|
||||||
|
'Effect target shuld be the element');
|
||||||
|
}, '{ subtree: false } on an element with a child returns only the element\'s'
|
||||||
|
+ ' animations');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
addStyle(t, { '#parent::after': 'animation: anim1 10s;',
|
||||||
|
'#parent::before': 'animation: anim1 10s;',
|
||||||
|
'#child::after': 'animation: anim1 10s;',
|
||||||
|
'#child::before': 'animation: anim1 10s;' });
|
||||||
|
const parent = addDiv(t, { 'id': 'parent' });
|
||||||
|
const child = addDiv(t, { 'id': 'child' });
|
||||||
|
parent.style.animation = 'anim1 100s';
|
||||||
|
child.style.animation = 'anim1 100s';
|
||||||
|
parent.appendChild(child);
|
||||||
|
|
||||||
|
const animations = parent.getAnimations({ subtree: true });
|
||||||
|
assert_equals(animations.length, 6,
|
||||||
|
'Should find all elements, pesudo-elements that parent has');
|
||||||
|
|
||||||
|
assert_equals(animations[0].effect.target, parent,
|
||||||
|
'The animation targeting the parent element ' +
|
||||||
|
'should be returned first');
|
||||||
|
assert_equals(animations[1].effect.target.type, '::before',
|
||||||
|
'The animation targeting the ::before pseudo-element ' +
|
||||||
|
'should be returned second');
|
||||||
|
assert_equals(animations[1].effect.target.parentElement, parent,
|
||||||
|
'This ::before element should be child of parent element');
|
||||||
|
assert_equals(animations[2].effect.target.type, '::after',
|
||||||
|
'The animation targeting the ::after pesudo-element ' +
|
||||||
|
'should be returned third');
|
||||||
|
assert_equals(animations[2].effect.target.parentElement, parent,
|
||||||
|
'This ::after element should be child of parent element');
|
||||||
|
|
||||||
|
assert_equals(animations[3].effect.target, child,
|
||||||
|
'The animation targeting the child element ' +
|
||||||
|
'should be returned fourth');
|
||||||
|
assert_equals(animations[4].effect.target.type, '::before',
|
||||||
|
'The animation targeting the ::before pseudo-element ' +
|
||||||
|
'should be returned fifth');
|
||||||
|
assert_equals(animations[4].effect.target.parentElement, child,
|
||||||
|
'This ::before element should be child of child element');
|
||||||
|
assert_equals(animations[5].effect.target.type, '::after',
|
||||||
|
'The animation targeting the ::after pesudo-element ' +
|
||||||
|
'should be returned last');
|
||||||
|
assert_equals(animations[5].effect.target.parentElement, child,
|
||||||
|
'This ::after element should be child of child element');
|
||||||
|
}, '{ subtree: true } on an element with a child returns animations from the'
|
||||||
|
+ ' element, its pseudo-elements, its child and its child pseudo-elements');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const parent = addDiv(t, { 'id': 'parent' });
|
||||||
|
const child1 = addDiv(t, { 'id': 'child1' });
|
||||||
|
const grandchild1 = addDiv(t, { 'id': 'grandchild1' });
|
||||||
|
const grandchild2 = addDiv(t, { 'id': 'grandchild2' });
|
||||||
|
const child2 = addDiv(t, { 'id': 'child2' });
|
||||||
|
|
||||||
|
parent.style.animation = 'anim1 100s';
|
||||||
|
child1.style.animation = 'anim1 100s';
|
||||||
|
grandchild1.style.animation = 'anim1 100s';
|
||||||
|
grandchild2.style.animation = 'anim1 100s';
|
||||||
|
child2.style.animation = 'anim1 100s';
|
||||||
|
|
||||||
|
parent.appendChild(child1);
|
||||||
|
child1.appendChild(grandchild1);
|
||||||
|
child1.appendChild(grandchild2);
|
||||||
|
parent.appendChild(child2);
|
||||||
|
|
||||||
|
const animations = parent.getAnimations({ subtree: true });
|
||||||
|
assert_equals(
|
||||||
|
parent.getAnimations({ subtree: true }).length, 5,
|
||||||
|
'Should find all descendants of the element');
|
||||||
|
|
||||||
|
assert_equals(animations[0].effect.target, parent,
|
||||||
|
'The animation targeting the parent element ' +
|
||||||
|
'should be returned first');
|
||||||
|
|
||||||
|
assert_equals(animations[1].effect.target, child1,
|
||||||
|
'The animation targeting the child1 element ' +
|
||||||
|
'should be returned second');
|
||||||
|
|
||||||
|
assert_equals(animations[2].effect.target, grandchild1,
|
||||||
|
'The animation targeting the grandchild1 element ' +
|
||||||
|
'should be returned third');
|
||||||
|
|
||||||
|
assert_equals(animations[3].effect.target, grandchild2,
|
||||||
|
'The animation targeting the grandchild2 element ' +
|
||||||
|
'should be returned fourth');
|
||||||
|
|
||||||
|
assert_equals(animations[4].effect.target, child2,
|
||||||
|
'The animation targeting the child2 element ' +
|
||||||
|
'should be returned last');
|
||||||
|
|
||||||
|
}, '{ subtree: true } on an element with many descendants returns animations'
|
||||||
|
+ ' from all the descendants');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,757 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>KeyframeEffect.getKeyframes() for CSS animations</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes anim-empty { }
|
||||||
|
|
||||||
|
@keyframes anim-empty-frames {
|
||||||
|
from { }
|
||||||
|
to { }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-only-timing {
|
||||||
|
from { animation-timing-function: linear; }
|
||||||
|
to { }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-only-non-animatable {
|
||||||
|
from { animation-duration: 3s; }
|
||||||
|
to { animation-duration: 5s; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-simple {
|
||||||
|
from { color: rgb(0, 0, 0); }
|
||||||
|
to { color: rgb(255, 255, 255); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-simple-three {
|
||||||
|
from { color: rgb(0, 0, 0); }
|
||||||
|
50% { color: rgb(0, 0, 255); }
|
||||||
|
to { color: rgb(255, 255, 255); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-simple-timing {
|
||||||
|
from { color: rgb(0, 0, 0); animation-timing-function: linear; }
|
||||||
|
50% { color: rgb(0, 0, 255); animation-timing-function: ease-in-out; }
|
||||||
|
to { color: rgb(255, 255, 255); animation-timing-function: step-end; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-simple-timing-some {
|
||||||
|
from { color: rgb(0, 0, 0); animation-timing-function: linear; }
|
||||||
|
50% { color: rgb(0, 0, 255); }
|
||||||
|
to { color: rgb(255, 255, 255); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-simple-shorthand {
|
||||||
|
from { margin: 8px; }
|
||||||
|
to { margin: 16px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-omit-to {
|
||||||
|
from { color: rgb(0, 0, 255); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-omit-from {
|
||||||
|
to { color: rgb(0, 0, 255); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-omit-from-to {
|
||||||
|
50% { color: rgb(0, 0, 255); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-partially-omit-to {
|
||||||
|
from { margin-top: 50px;
|
||||||
|
margin-bottom: 100px; }
|
||||||
|
to { margin-top: 150px !important; /* ignored */
|
||||||
|
margin-bottom: 200px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-different-props {
|
||||||
|
from { color: rgb(0, 0, 0); margin-top: 8px; }
|
||||||
|
25% { color: rgb(0, 0, 255); }
|
||||||
|
75% { margin-top: 12px; }
|
||||||
|
to { color: rgb(255, 255, 255); margin-top: 16px }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-different-props-and-easing {
|
||||||
|
from { color: rgb(0, 0, 0); margin-top: 8px; animation-timing-function: linear; }
|
||||||
|
25% { color: rgb(0, 0, 255); animation-timing-function: step-end; }
|
||||||
|
75% { margin-top: 12px; animation-timing-function: ease-in; }
|
||||||
|
to { color: rgb(255, 255, 255); margin-top: 16px }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-merge-offset {
|
||||||
|
from { color: rgb(0, 0, 0); }
|
||||||
|
to { color: rgb(255, 255, 255); }
|
||||||
|
from { margin-top: 8px; }
|
||||||
|
to { margin-top: 16px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-merge-offset-and-easing {
|
||||||
|
from { color: rgb(0, 0, 0); animation-timing-function: step-end; }
|
||||||
|
to { color: rgb(255, 255, 255); }
|
||||||
|
from { margin-top: 8px; animation-timing-function: linear; }
|
||||||
|
to { margin-top: 16px; }
|
||||||
|
from { font-size: 16px; animation-timing-function: step-end; }
|
||||||
|
to { font-size: 32px; }
|
||||||
|
from { padding-left: 2px; animation-timing-function: linear; }
|
||||||
|
to { padding-left: 4px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-no-merge-equiv-easing {
|
||||||
|
from { margin-top: 0px; animation-timing-function: steps(1, end); }
|
||||||
|
from { margin-right: 0px; animation-timing-function: step-end; }
|
||||||
|
from { margin-bottom: 0px; animation-timing-function: steps(1); }
|
||||||
|
50% { margin-top: 10px; animation-timing-function: step-end; }
|
||||||
|
50% { margin-right: 10px; animation-timing-function: step-end; }
|
||||||
|
50% { margin-bottom: 10px; animation-timing-function: step-end; }
|
||||||
|
to { margin-top: 20px; margin-right: 20px; margin-bottom: 20px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-overriding {
|
||||||
|
from { padding-top: 50px }
|
||||||
|
50%, from { padding-top: 30px } /* wins: 0% */
|
||||||
|
75%, 85%, 50% { padding-top: 20px } /* wins: 75%, 50% */
|
||||||
|
100%, 85% { padding-top: 70px } /* wins: 100% */
|
||||||
|
85.1% { padding-top: 60px } /* wins: 85.1% */
|
||||||
|
85% { padding-top: 30px } /* wins: 85% */
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-filter {
|
||||||
|
to { filter: blur(5px) sepia(60%) saturate(30%); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-filter-drop-shadow {
|
||||||
|
from { filter: drop-shadow(10px 10px 10px rgb(0, 255, 0)); }
|
||||||
|
to { filter: drop-shadow(50px 30px 10px rgb(255, 0, 0)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-text-shadow {
|
||||||
|
to { text-shadow: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes anim-background-size {
|
||||||
|
to { background-size: 50%, 6px, contain }
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--var-100px: 100px;
|
||||||
|
--end-color: rgb(255, 0, 0);
|
||||||
|
}
|
||||||
|
@keyframes anim-variables {
|
||||||
|
to { transform: translate(var(--var-100px), 0) }
|
||||||
|
}
|
||||||
|
@keyframes anim-variables-shorthand {
|
||||||
|
to { margin: var(--var-100px) }
|
||||||
|
}
|
||||||
|
@keyframes anim-custom-property-in-keyframe {
|
||||||
|
to { --end-color: rgb(0, 255, 0); color: var(--end-color) }
|
||||||
|
}
|
||||||
|
@keyframes anim-only-custom-property-in-keyframe {
|
||||||
|
from { transform: translate(100px, 0) }
|
||||||
|
to { --not-used: 200px }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const getKeyframes = elem => elem.getAnimations()[0].effect.getKeyframes();
|
||||||
|
|
||||||
|
const assert_frames_equal = (a, b, name) => {
|
||||||
|
assert_equals(Object.keys(a).sort().toString(),
|
||||||
|
Object.keys(b).sort().toString(),
|
||||||
|
"properties on " + name);
|
||||||
|
for (const p in a) {
|
||||||
|
if (p === 'offset' || p === 'computedOffset') {
|
||||||
|
assert_approx_equals(a[p], b[p], 0.00001,
|
||||||
|
"value for '" + p + "' on " + name);
|
||||||
|
} else {
|
||||||
|
assert_equals(a[p], b[p], "value for '" + p + "' on " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// animation-timing-function values to test with, where the value
|
||||||
|
// is exactly the same as its serialization, sorted by the order
|
||||||
|
// getKeyframes() will group frames with the same easing function
|
||||||
|
// together (by nsTimingFunction::Compare).
|
||||||
|
const kTimingFunctionValues = [
|
||||||
|
"ease",
|
||||||
|
"linear",
|
||||||
|
"ease-in",
|
||||||
|
"ease-out",
|
||||||
|
"ease-in-out",
|
||||||
|
"steps(1, start)",
|
||||||
|
"steps(2, start)",
|
||||||
|
"steps(1)",
|
||||||
|
"steps(2)",
|
||||||
|
"cubic-bezier(0, 0, 1, 1)",
|
||||||
|
"cubic-bezier(0, 0.25, 0.75, 1)"
|
||||||
|
];
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-empty 100s';
|
||||||
|
assert_equals(getKeyframes(div).length, 0,
|
||||||
|
"number of frames with empty @keyframes");
|
||||||
|
|
||||||
|
div.style.animation = 'anim-empty-frames 100s';
|
||||||
|
assert_equals(getKeyframes(div).length, 0,
|
||||||
|
"number of frames when @keyframes has empty keyframes");
|
||||||
|
|
||||||
|
div.style.animation = 'anim-only-timing 100s';
|
||||||
|
assert_equals(getKeyframes(div).length, 0,
|
||||||
|
"number of frames when @keyframes only has keyframes with " +
|
||||||
|
"animation-timing-function");
|
||||||
|
|
||||||
|
div.style.animation = 'anim-only-non-animatable 100s';
|
||||||
|
assert_equals(getKeyframes(div).length, 0,
|
||||||
|
"number of frames when @keyframes only has frames with " +
|
||||||
|
"non-animatable properties");
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns no frames for various kinds'
|
||||||
|
+ ' of empty enimations');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-simple 100s';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease",
|
||||||
|
color: "rgb(0, 0, 0)", composite: null },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease",
|
||||||
|
color: "rgb(255, 255, 255)", composite: null },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected frames for a simple'
|
||||||
|
+ ' animation');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
for (const easing of kTimingFunctionValues) {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-simple-three 100s ' + easing;
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 3, "number of frames");
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_equals(frames[i].easing, easing,
|
||||||
|
"value for 'easing' on ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns frames with expected easing'
|
||||||
|
+ ' values, when the easing comes from animation-timing-function on the'
|
||||||
|
+ ' element');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-simple-timing 100s';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 3, "number of frames");
|
||||||
|
assert_equals(frames[0].easing, "linear",
|
||||||
|
"value of 'easing' on ComputedKeyframe #0");
|
||||||
|
assert_equals(frames[1].easing, "ease-in-out",
|
||||||
|
"value of 'easing' on ComputedKeyframe #1");
|
||||||
|
assert_equals(frames[2].easing, "steps(1)",
|
||||||
|
"value of 'easing' on ComputedKeyframe #2");
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns frames with expected easing'
|
||||||
|
+ ' values, when the easing is specified on each keyframe');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-simple-timing-some 100s step-start';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 3, "number of frames");
|
||||||
|
assert_equals(frames[0].easing, "linear",
|
||||||
|
"value of 'easing' on ComputedKeyframe #0");
|
||||||
|
assert_equals(frames[1].easing, "steps(1, start)",
|
||||||
|
"value of 'easing' on ComputedKeyframe #1");
|
||||||
|
assert_equals(frames[2].easing, "steps(1, start)",
|
||||||
|
"value of 'easing' on ComputedKeyframe #2");
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns frames with expected easing'
|
||||||
|
+ ' values, when the easing is specified on some keyframes');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-simple-shorthand 100s';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
marginBottom: "8px", marginLeft: "8px",
|
||||||
|
marginRight: "8px", marginTop: "8px" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
marginBottom: "16px", marginLeft: "16px",
|
||||||
|
marginRight: "16px", marginTop: "16px" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected frames for a simple'
|
||||||
|
+ ' animation that specifies a single shorthand property');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-omit-to 100s';
|
||||||
|
div.style.color = 'rgb(255, 255, 255)';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
color: "rgb(0, 0, 255)" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
color: "rgb(255, 255, 255)" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
|
||||||
|
'animation with a 0% keyframe and no 100% keyframe');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-omit-from 100s';
|
||||||
|
div.style.color = 'rgb(255, 255, 255)';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
color: "rgb(255, 255, 255)" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
color: "rgb(0, 0, 255)" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
|
||||||
|
'animation with a 100% keyframe and no 0% keyframe');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-omit-from-to 100s';
|
||||||
|
div.style.color = 'rgb(255, 255, 255)';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 3, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
color: "rgb(255, 255, 255)" },
|
||||||
|
{ offset: 0.5, computedOffset: 0.5, easing: "ease", composite: null,
|
||||||
|
color: "rgb(0, 0, 255)" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
color: "rgb(255, 255, 255)" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
|
||||||
|
'animation with no 0% or 100% keyframe but with a 50% keyframe');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-partially-omit-to 100s';
|
||||||
|
div.style.marginTop = '250px';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
marginTop: '50px', marginBottom: '100px' },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
marginTop: '250px', marginBottom: '200px' },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
|
||||||
|
'animation with a partially complete 100% keyframe (because the ' +
|
||||||
|
'!important rule is ignored)');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-different-props 100s';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 4, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
color: "rgb(0, 0, 0)", marginTop: "8px" },
|
||||||
|
{ offset: 0.25, computedOffset: 0.25, easing: "ease", composite: null,
|
||||||
|
color: "rgb(0, 0, 255)" },
|
||||||
|
{ offset: 0.75, computedOffset: 0.75, easing: "ease", composite: null,
|
||||||
|
marginTop: "12px" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
color: "rgb(255, 255, 255)", marginTop: "16px" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
|
||||||
|
'animation with different properties on different keyframes, all ' +
|
||||||
|
'with the same easing function');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-different-props-and-easing 100s';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 4, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "linear", composite: null,
|
||||||
|
color: "rgb(0, 0, 0)", marginTop: "8px" },
|
||||||
|
{ offset: 0.25, computedOffset: 0.25, easing: "steps(1)", composite: null,
|
||||||
|
color: "rgb(0, 0, 255)" },
|
||||||
|
{ offset: 0.75, computedOffset: 0.75, easing: "ease-in", composite: null,
|
||||||
|
marginTop: "12px" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
color: "rgb(255, 255, 255)", marginTop: "16px" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
|
||||||
|
'animation with different properties on different keyframes, with ' +
|
||||||
|
'a different easing function on each');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-merge-offset 100s';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
color: "rgb(0, 0, 0)", marginTop: "8px" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
color: "rgb(255, 255, 255)", marginTop: "16px" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
|
||||||
|
'animation with multiple keyframes for the same time, and all with ' +
|
||||||
|
'the same easing function');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-merge-offset-and-easing 100s';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 3, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "steps(1)", composite: null,
|
||||||
|
color: "rgb(0, 0, 0)", fontSize: "16px" },
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "linear", composite: null,
|
||||||
|
marginTop: "8px", paddingLeft: "2px" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
color: "rgb(255, 255, 255)", fontSize: "32px", marginTop: "16px",
|
||||||
|
paddingLeft: "4px" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
|
||||||
|
'animation with multiple keyframes for the same time and with ' +
|
||||||
|
'different easing functions');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-no-merge-equiv-easing 100s';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 3, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "steps(1)", composite: null,
|
||||||
|
marginTop: "0px", marginRight: "0px", marginBottom: "0px" },
|
||||||
|
{ offset: 0.5, computedOffset: 0.5, easing: "steps(1)", composite: null,
|
||||||
|
marginTop: "10px", marginRight: "10px", marginBottom: "10px" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
marginTop: "20px", marginRight: "20px", marginBottom: "20px" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
|
||||||
|
'animation with multiple keyframes for the same time and with ' +
|
||||||
|
'different but equivalent easing functions');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-overriding 100s';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 6, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
paddingTop: "30px" },
|
||||||
|
{ offset: 0.5, computedOffset: 0.5, easing: "ease", composite: null,
|
||||||
|
paddingTop: "20px" },
|
||||||
|
{ offset: 0.75, computedOffset: 0.75, easing: "ease", composite: null,
|
||||||
|
paddingTop: "20px" },
|
||||||
|
{ offset: 0.85, computedOffset: 0.85, easing: "ease", composite: null,
|
||||||
|
paddingTop: "30px" },
|
||||||
|
{ offset: 0.851, computedOffset: 0.851, easing: "ease", composite: null,
|
||||||
|
paddingTop: "60px" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
paddingTop: "70px" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected frames for ' +
|
||||||
|
'overlapping keyframes');
|
||||||
|
|
||||||
|
// Gecko-specific test case: We are specifically concerned here that the
|
||||||
|
// computed value for filter, "none", is correctly represented.
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-filter 100s';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
filter: "none" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
filter: "blur(5px) sepia(60%) saturate(30%)" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
|
||||||
|
'animations with filter properties and missing keyframes');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-filter-drop-shadow 100s';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
filter: "drop-shadow(rgb(0, 255, 0) 10px 10px 10px)" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
filter: "drop-shadow(rgb(255, 0, 0) 50px 30px 10px)" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
|
||||||
|
'animation with drop-shadow of filter property');
|
||||||
|
|
||||||
|
// Gecko-specific test case: We are specifically concerned here that the
|
||||||
|
// computed value for text-shadow and a "none" specified on a keyframe
|
||||||
|
// are correctly represented.
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.textShadow = '1px 1px 2px rgb(0, 0, 0), ' +
|
||||||
|
'0 0 16px rgb(0, 0, 255), ' +
|
||||||
|
'0 0 3.2px rgb(0, 0, 255)';
|
||||||
|
div.style.animation = 'anim-text-shadow 100s';
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
textShadow: "rgb(0, 0, 0) 1px 1px 2px,"
|
||||||
|
+ " rgb(0, 0, 255) 0px 0px 16px,"
|
||||||
|
+ " rgb(0, 0, 255) 0px 0px 3.2px" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
textShadow: "none" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
|
||||||
|
'animations with text-shadow properties and missing keyframes');
|
||||||
|
|
||||||
|
// Gecko-specific test case: We are specifically concerned here that the
|
||||||
|
// initial value for background-size and the specified list are correctly
|
||||||
|
// represented.
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
|
||||||
|
div.style.animation = 'anim-background-size 100s';
|
||||||
|
let frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
backgroundSize: "auto auto" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
backgroundSize: "50% auto, 6px auto, contain" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test inheriting a background-size value
|
||||||
|
|
||||||
|
expected[0].backgroundSize = div.style.backgroundSize =
|
||||||
|
"30px auto, 40% auto, auto auto";
|
||||||
|
frames = getKeyframes(div);
|
||||||
|
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i
|
||||||
|
+ " after updating current style");
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
|
||||||
|
'animations with background-size properties and missing keyframes');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim-variables 100s';
|
||||||
|
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
transform: "none" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
transform: "translate(100px, 0px)" },
|
||||||
|
];
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
|
||||||
|
'animations with CSS variables as keyframe values');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim-variables-shorthand 100s';
|
||||||
|
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
marginBottom: "0px",
|
||||||
|
marginLeft: "0px",
|
||||||
|
marginRight: "0px",
|
||||||
|
marginTop: "0px" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
marginBottom: "100px",
|
||||||
|
marginLeft: "100px",
|
||||||
|
marginRight: "100px",
|
||||||
|
marginTop: "100px" },
|
||||||
|
];
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
|
||||||
|
'animations with CSS variables as keyframe values in a shorthand property');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim-custom-property-in-keyframe 100s';
|
||||||
|
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
color: "rgb(0, 0, 0)" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
color: "rgb(0, 255, 0)" },
|
||||||
|
];
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
|
||||||
|
'animations with a CSS variable which is overriden by the value in keyframe');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim-only-custom-property-in-keyframe 100s';
|
||||||
|
|
||||||
|
const frames = getKeyframes(div);
|
||||||
|
|
||||||
|
assert_equals(frames.length, 2, "number of frames");
|
||||||
|
|
||||||
|
const expected = [
|
||||||
|
{ offset: 0, computedOffset: 0, easing: "ease", composite: null,
|
||||||
|
transform: "translate(100px, 0px)" },
|
||||||
|
{ offset: 1, computedOffset: 1, easing: "ease", composite: null,
|
||||||
|
transform: "none" },
|
||||||
|
];
|
||||||
|
for (let i = 0; i < frames.length; i++) {
|
||||||
|
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
|
||||||
|
}
|
||||||
|
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
|
||||||
|
'animations with only custom property in a keyframe');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,65 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>CSSAnimation.effect.target</title>
|
||||||
|
<!-- TODO: Add a more specific link for this once it is specified. -->
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes anim { }
|
||||||
|
::before {
|
||||||
|
content: ''
|
||||||
|
}
|
||||||
|
::after {
|
||||||
|
content: ''
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
const div = addDiv(t);
|
||||||
|
div.style.animation = 'anim 100s';
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
assert_equals(animation.effect.target, div,
|
||||||
|
'Animation.target is the animatable div');
|
||||||
|
}, 'Returned CSS animations have the correct effect target');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
addStyle(t, { '.after::after': 'animation: anim 10s, anim 100s;' });
|
||||||
|
const div = addDiv(t, { class: 'after' });
|
||||||
|
const anims = document.getAnimations();
|
||||||
|
assert_equals(anims.length, 2,
|
||||||
|
'Got animations running on ::after pseudo element');
|
||||||
|
assert_equals(anims[0].effect.target, anims[1].effect.target,
|
||||||
|
'Both animations return the same target object');
|
||||||
|
}, 'effect.target should return the same CSSPseudoElement object each time');
|
||||||
|
|
||||||
|
test(t => {
|
||||||
|
addStyle(t, { '.after::after': 'animation: anim 10s;' });
|
||||||
|
const div = addDiv(t, { class: 'after' });
|
||||||
|
const pseudoTarget = document.getAnimations()[0].effect.target;
|
||||||
|
const effect = new KeyframeEffect(pseudoTarget,
|
||||||
|
{ background: ["blue", "red"] },
|
||||||
|
3 * MS_PER_SEC);
|
||||||
|
const newAnim = new Animation(effect, document.timeline);
|
||||||
|
newAnim.play();
|
||||||
|
const anims = document.getAnimations();
|
||||||
|
assert_equals(anims.length, 2,
|
||||||
|
'Got animations running on ::after pseudo element');
|
||||||
|
assert_not_equals(anims[0], newAnim,
|
||||||
|
'The scriped-generated animation appears last');
|
||||||
|
assert_equals(newAnim.effect.target, pseudoTarget,
|
||||||
|
'The effect.target of the scripted-generated animation is ' +
|
||||||
|
'the same as the one from the argument of ' +
|
||||||
|
'KeyframeEffect constructor');
|
||||||
|
assert_equals(anims[0].effect.target, newAnim.effect.target,
|
||||||
|
'Both animations return the same target object');
|
||||||
|
}, 'effect.target from the script-generated animation should return the same ' +
|
||||||
|
'CSSPseudoElement object as that from the CSS generated animation');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
|
@ -0,0 +1,423 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Tests for CSS animation event dispatch</title>
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes anim {
|
||||||
|
from { margin-left: 0px; }
|
||||||
|
to { margin-left: 100px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const setupAnimation = (t, animationStyle) => {
|
||||||
|
const div = addDiv(t, { style: 'animation: ' + animationStyle });
|
||||||
|
const watcher = new EventWatcher(t, div, [ 'animationstart',
|
||||||
|
'animationiteration',
|
||||||
|
'animationend',
|
||||||
|
'animationcancel' ]);
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
return { animation, watcher, div };
|
||||||
|
};
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
// Add 1ms delay to ensure that the delay is not included in the elapsedTime.
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 1ms');
|
||||||
|
|
||||||
|
const evt = await watcher.wait_for('animationstart');
|
||||||
|
assert_equals(evt.elapsedTime, 0.0);
|
||||||
|
}, 'Idle -> Active');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s');
|
||||||
|
|
||||||
|
// Seek to After phase.
|
||||||
|
animation.finish();
|
||||||
|
|
||||||
|
const events = await watcher.wait_for(['animationstart', 'animationend'], {
|
||||||
|
record: 'all',
|
||||||
|
});
|
||||||
|
assert_equals(events[0].elapsedTime, 0.0);
|
||||||
|
assert_equals(events[1].elapsedTime, 100);
|
||||||
|
}, 'Idle -> After');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
// Seek to Active phase.
|
||||||
|
animation.currentTime = 100 * MS_PER_SEC;
|
||||||
|
|
||||||
|
const evt = await watcher.wait_for('animationstart');
|
||||||
|
assert_equals(evt.elapsedTime, 0.0);
|
||||||
|
}, 'Before -> Active');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
|
||||||
|
const allEvents = watcher.wait_for(['animationstart', 'animationend'], {
|
||||||
|
record: 'all',
|
||||||
|
});
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
// Seek to After phase.
|
||||||
|
animation.finish();
|
||||||
|
|
||||||
|
const events = await allEvents;
|
||||||
|
assert_equals(events[0].elapsedTime, 0.0);
|
||||||
|
assert_equals(events[1].elapsedTime, 100.0);
|
||||||
|
}, 'Before -> After');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher, div } = setupAnimation(t, 'anim 100s paused');
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
// Make idle
|
||||||
|
div.style.display = 'none';
|
||||||
|
|
||||||
|
const evt = await watcher.wait_for('animationcancel');
|
||||||
|
assert_equals(evt.elapsedTime, 0.0);
|
||||||
|
}, 'Active -> Idle, display: none');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s');
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
animation.currentTime = 100.0;
|
||||||
|
|
||||||
|
// Make idle
|
||||||
|
animation.timeline = null;
|
||||||
|
|
||||||
|
const evt = await watcher.wait_for('animationcancel');
|
||||||
|
assert_time_equals_literal(evt.elapsedTime, 0.1);
|
||||||
|
}, 'Active -> Idle, setting Animation.timeline = null');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
// We should NOT pause animation since calling cancel synchronously.
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s');
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
animation.currentTime = 50.0;
|
||||||
|
animation.cancel();
|
||||||
|
|
||||||
|
const evt = await watcher.wait_for('animationcancel');
|
||||||
|
assert_time_equals_literal(evt.elapsedTime, 0.05);
|
||||||
|
}, 'Active -> Idle, calling Animation.cancel()');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
|
||||||
|
|
||||||
|
// Seek to Active phase.
|
||||||
|
animation.currentTime = 100 * MS_PER_SEC;
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
// Seek to Before phase.
|
||||||
|
animation.currentTime = 0;
|
||||||
|
|
||||||
|
const evt = await watcher.wait_for('animationend');
|
||||||
|
assert_equals(evt.elapsedTime, 0.0);
|
||||||
|
}, 'Active -> Before');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s paused');
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
// Seek to After phase.
|
||||||
|
animation.finish();
|
||||||
|
|
||||||
|
const evt = await watcher.wait_for('animationend');
|
||||||
|
assert_equals(evt.elapsedTime, 100.0);
|
||||||
|
}, 'Active -> After');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
|
||||||
|
|
||||||
|
// Seek to After phase.
|
||||||
|
animation.finish();
|
||||||
|
await watcher.wait_for([ 'animationstart', 'animationend' ]);
|
||||||
|
|
||||||
|
// Seek to Before phase.
|
||||||
|
animation.currentTime = 0;
|
||||||
|
|
||||||
|
const events = await watcher.wait_for(['animationstart', 'animationend'], {
|
||||||
|
record: 'all',
|
||||||
|
});
|
||||||
|
assert_equals(events[0].elapsedTime, 100.0);
|
||||||
|
assert_equals(events[1].elapsedTime, 0.0);
|
||||||
|
}, 'After -> Before');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s paused');
|
||||||
|
|
||||||
|
// Seek to After phase.
|
||||||
|
animation.finish();
|
||||||
|
await watcher.wait_for([ 'animationstart', 'animationend' ]);
|
||||||
|
|
||||||
|
// Seek to Active phase.
|
||||||
|
animation.currentTime = 100 * MS_PER_SEC;
|
||||||
|
|
||||||
|
const evt = await watcher.wait_for('animationstart');
|
||||||
|
assert_equals(evt.elapsedTime, 100.0);
|
||||||
|
}, 'After -> Active');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s 3 paused');
|
||||||
|
|
||||||
|
await animation.ready;
|
||||||
|
|
||||||
|
// Seek to iteration 0 (no animationiteration event should be dispatched)
|
||||||
|
animation.currentTime = 100 * MS_PER_SEC;
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
// Seek to iteration 2
|
||||||
|
animation.currentTime = 300 * MS_PER_SEC;
|
||||||
|
let evt = await watcher.wait_for('animationiteration');
|
||||||
|
assert_equals(evt.elapsedTime, 200);
|
||||||
|
|
||||||
|
// Seek to After phase (no animationiteration event should be dispatched)
|
||||||
|
animation.currentTime = 400 * MS_PER_SEC;
|
||||||
|
evt = await watcher.wait_for('animationend');
|
||||||
|
assert_equals(evt.elapsedTime, 300);
|
||||||
|
}, 'Active -> Active (forwards)');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s 3');
|
||||||
|
|
||||||
|
// Seek to After phase.
|
||||||
|
animation.finish();
|
||||||
|
await watcher.wait_for([ 'animationstart', 'animationend' ]);
|
||||||
|
|
||||||
|
// Seek to iteration 2 (no animationiteration event should be dispatched)
|
||||||
|
animation.pause();
|
||||||
|
animation.currentTime = 300 * MS_PER_SEC;
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
// Seek to mid of iteration 0 phase.
|
||||||
|
animation.currentTime = 200 * MS_PER_SEC;
|
||||||
|
|
||||||
|
const evt = await watcher.wait_for('animationiteration');
|
||||||
|
assert_equals(evt.elapsedTime, 200.0);
|
||||||
|
|
||||||
|
// Seek to before phase (no animationiteration event should be dispatched)
|
||||||
|
animation.currentTime = 0;
|
||||||
|
await watcher.wait_for('animationend');
|
||||||
|
}, 'Active -> Active (backwards)');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher, div } = setupAnimation(t, 'anim 100s paused');
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
// Seek to Idle phase.
|
||||||
|
div.style.display = 'none';
|
||||||
|
flushComputedStyle(div);
|
||||||
|
|
||||||
|
await watcher.wait_for('animationcancel');
|
||||||
|
|
||||||
|
// Restart this animation.
|
||||||
|
div.style.display = '';
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
}, 'Active -> Idle -> Active: animationstart is fired by restarting animation');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s 2 paused');
|
||||||
|
|
||||||
|
// Make After.
|
||||||
|
animation.finish();
|
||||||
|
|
||||||
|
await watcher.wait_for([ 'animationstart', 'animationend' ]);
|
||||||
|
animation.playbackRate = -1;
|
||||||
|
|
||||||
|
let evt = await watcher.wait_for('animationstart');
|
||||||
|
assert_equals(evt.elapsedTime, 200);
|
||||||
|
|
||||||
|
// Seek to 1st iteration
|
||||||
|
animation.currentTime = 200 * MS_PER_SEC - 1;
|
||||||
|
|
||||||
|
evt = await watcher.wait_for('animationiteration');
|
||||||
|
assert_equals(evt.elapsedTime, 100);
|
||||||
|
|
||||||
|
// Seek to before
|
||||||
|
animation.currentTime = 100 * MS_PER_SEC - 1;
|
||||||
|
|
||||||
|
evt = await watcher.wait_for('animationend');
|
||||||
|
assert_equals(evt.elapsedTime, 0);
|
||||||
|
assert_equals(animation.playState, 'running'); // delay
|
||||||
|
}, 'Negative playbackRate sanity test(Before -> Active -> Before)');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
|
||||||
|
|
||||||
|
animation.currentTime = 150 * MS_PER_SEC;
|
||||||
|
animation.currentTime = 50 * MS_PER_SEC;
|
||||||
|
|
||||||
|
// Then wait a couple of frames and check that no event was dispatched.
|
||||||
|
await waitForAnimationFrames(2);
|
||||||
|
}, 'Redundant change, before -> active, then back');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
|
||||||
|
|
||||||
|
animation.currentTime = 250 * MS_PER_SEC;
|
||||||
|
animation.currentTime = 50 * MS_PER_SEC;
|
||||||
|
|
||||||
|
// Then wait a couple of frames and check that no event was dispatched.
|
||||||
|
await waitForAnimationFrames(2);
|
||||||
|
}, 'Redundant change, before -> after, then back');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
|
||||||
|
|
||||||
|
// Get us into the initial state:
|
||||||
|
animation.currentTime = 150 * MS_PER_SEC;
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
animation.currentTime = 50 * MS_PER_SEC;
|
||||||
|
animation.currentTime = 150 * MS_PER_SEC;
|
||||||
|
|
||||||
|
// Then wait a couple of frames and check that no event was dispatched.
|
||||||
|
await waitForAnimationFrames(2);
|
||||||
|
}, 'Redundant change, active -> before, then back');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
|
||||||
|
|
||||||
|
// Get us into the initial state:
|
||||||
|
animation.currentTime = 150 * MS_PER_SEC;
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
animation.currentTime = 250 * MS_PER_SEC;
|
||||||
|
animation.currentTime = 150 * MS_PER_SEC;
|
||||||
|
|
||||||
|
// Then wait a couple of frames and check that no event was dispatched.
|
||||||
|
await waitForAnimationFrames(2);
|
||||||
|
}, 'Redundant change, active -> after, then back');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
|
||||||
|
|
||||||
|
// Get us into the initial state:
|
||||||
|
animation.currentTime = 250 * MS_PER_SEC;
|
||||||
|
|
||||||
|
await watcher.wait_for(['animationstart', 'animationend']);
|
||||||
|
|
||||||
|
animation.currentTime = 50 * MS_PER_SEC;
|
||||||
|
animation.currentTime = 250 * MS_PER_SEC;
|
||||||
|
|
||||||
|
// Then wait a couple of frames and check that no event was dispatched.
|
||||||
|
await waitForAnimationFrames(2);
|
||||||
|
}, 'Redundant change, after -> before, then back');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s 100s');
|
||||||
|
|
||||||
|
// Get us into the initial state:
|
||||||
|
animation.currentTime = 250 * MS_PER_SEC;
|
||||||
|
|
||||||
|
await watcher.wait_for(['animationstart', 'animationend']);
|
||||||
|
|
||||||
|
animation.currentTime = 150 * MS_PER_SEC;
|
||||||
|
animation.currentTime = 250 * MS_PER_SEC;
|
||||||
|
|
||||||
|
// Then wait a couple of frames and check that no event was dispatched.
|
||||||
|
await waitForAnimationFrames(2);
|
||||||
|
}, 'Redundant change, after -> active, then back');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s');
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
// Make idle
|
||||||
|
animation.cancel();
|
||||||
|
await watcher.wait_for('animationcancel');
|
||||||
|
|
||||||
|
animation.cancel();
|
||||||
|
// Then wait a couple of frames and check that no event was dispatched.
|
||||||
|
await waitForAnimationFrames(2);
|
||||||
|
}, 'Call Animation.cancel after canceling animation.');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s');
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
// Make idle
|
||||||
|
animation.cancel();
|
||||||
|
animation.play();
|
||||||
|
await watcher.wait_for([ 'animationcancel', 'animationstart' ]);
|
||||||
|
}, 'Restart animation after canceling animation immediately.');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s');
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
// Make idle
|
||||||
|
animation.cancel();
|
||||||
|
animation.play();
|
||||||
|
animation.cancel();
|
||||||
|
await watcher.wait_for('animationcancel');
|
||||||
|
|
||||||
|
// Then wait a couple of frames and check that no event was dispatched.
|
||||||
|
await waitForAnimationFrames(2);
|
||||||
|
}, 'Call Animation.cancel after restarting animation immediately.');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s');
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
// Make idle
|
||||||
|
animation.timeline = null;
|
||||||
|
await watcher.wait_for('animationcancel');
|
||||||
|
|
||||||
|
animation.timeline = document.timeline;
|
||||||
|
animation.play();
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
}, 'Set timeline and play transition after clearing the timeline.');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s');
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
// Make idle
|
||||||
|
animation.cancel();
|
||||||
|
await watcher.wait_for('animationcancel');
|
||||||
|
|
||||||
|
animation.effect = null;
|
||||||
|
// Then wait a couple of frames and check that no event was dispatched.
|
||||||
|
await waitForAnimationFrames(2);
|
||||||
|
}, 'Set null target effect after canceling the animation.');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
const { animation, watcher } = setupAnimation(t, 'anim 100s');
|
||||||
|
|
||||||
|
await watcher.wait_for('animationstart');
|
||||||
|
|
||||||
|
animation.effect = null;
|
||||||
|
await watcher.wait_for('animationend');
|
||||||
|
|
||||||
|
animation.cancel();
|
||||||
|
// Then wait a couple of frames and check that no event was dispatched.
|
||||||
|
await waitForAnimationFrames(2);
|
||||||
|
}, 'Cancel the animation after clearing the target effect.');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,164 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Tests for CSS animation event order</title>
|
||||||
|
<link rel="help" href="https://drafts.csswg.org/css-animations-2/#event-dispatch"/>
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<script src="support/testcommon.js"></script>
|
||||||
|
<style>
|
||||||
|
@keyframes anim {
|
||||||
|
from { margin-left: 0px; }
|
||||||
|
to { margin-left: 100px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<body>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script type='text/javascript'>
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts that the set of actual and received events match.
|
||||||
|
* @param actualEvents An array of the received AnimationEvent objects.
|
||||||
|
* @param expectedEvents A series of array objects representing the expected
|
||||||
|
* events, each having the form:
|
||||||
|
* [ event type, target element, elapsed time ]
|
||||||
|
*/
|
||||||
|
const checkEvents = (actualEvents, ...expectedEvents) => {
|
||||||
|
assert_equals(actualEvents.length, expectedEvents.length,
|
||||||
|
`Number of actual events (${actualEvents.length}: \
|
||||||
|
${actualEvents.map(event => event.type).join(', ')}) should match expected \
|
||||||
|
events (${expectedEvents.map(event => event.type).join(', ')})`);
|
||||||
|
|
||||||
|
actualEvents.forEach((actualEvent, i) => {
|
||||||
|
assert_equals(expectedEvents[i][0], actualEvent.type,
|
||||||
|
'Event type should match');
|
||||||
|
assert_equals(expectedEvents[i][1], actualEvent.target,
|
||||||
|
'Event target should match');
|
||||||
|
assert_equals(expectedEvents[i][2], actualEvent.elapsedTime,
|
||||||
|
'Event\'s elapsed time should match');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setupAnimation = (t, animationStyle, receiveEvents) => {
|
||||||
|
const div = addDiv(t, { style: "animation: " + animationStyle });
|
||||||
|
|
||||||
|
for (const name of ['start', 'iteration', 'end']) {
|
||||||
|
div['onanimation' + name] = evt => {
|
||||||
|
receiveEvents.push({ type: evt.type,
|
||||||
|
target: evt.target,
|
||||||
|
elapsedTime: evt.elapsedTime });
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const watcher = new EventWatcher(t, div, [ 'animationstart',
|
||||||
|
'animationiteration',
|
||||||
|
'animationend' ]);
|
||||||
|
|
||||||
|
const animation = div.getAnimations()[0];
|
||||||
|
|
||||||
|
return [animation, watcher, div];
|
||||||
|
};
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
let events = [];
|
||||||
|
const [animation1, watcher1, div1] =
|
||||||
|
setupAnimation(t, 'anim 100s 2 paused', events);
|
||||||
|
const [animation2, watcher2, div2] =
|
||||||
|
setupAnimation(t, 'anim 100s 2 paused', events);
|
||||||
|
|
||||||
|
await Promise.all([ watcher1.wait_for('animationstart'),
|
||||||
|
watcher2.wait_for('animationstart') ]);
|
||||||
|
|
||||||
|
checkEvents(events, ['animationstart', div1, 0],
|
||||||
|
['animationstart', div2, 0]);
|
||||||
|
|
||||||
|
events.length = 0; // Clear received event array
|
||||||
|
|
||||||
|
animation1.currentTime = 100 * MS_PER_SEC;
|
||||||
|
animation2.currentTime = 100 * MS_PER_SEC;
|
||||||
|
|
||||||
|
await Promise.all([ watcher1.wait_for('animationiteration'),
|
||||||
|
watcher2.wait_for('animationiteration') ]);
|
||||||
|
|
||||||
|
checkEvents(events, ['animationiteration', div1, 100],
|
||||||
|
['animationiteration', div2, 100]);
|
||||||
|
|
||||||
|
events.length = 0; // Clear received event array
|
||||||
|
|
||||||
|
animation1.finish();
|
||||||
|
animation2.finish();
|
||||||
|
|
||||||
|
await Promise.all([ watcher1.wait_for('animationend'),
|
||||||
|
watcher2.wait_for('animationend') ]);
|
||||||
|
|
||||||
|
checkEvents(events, ['animationend', div1, 200],
|
||||||
|
['animationend', div2, 200]);
|
||||||
|
}, 'Test same events are ordered by elements.');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
let events = [];
|
||||||
|
const [animation1, watcher1, div1] =
|
||||||
|
setupAnimation(t, 'anim 200s 400s', events);
|
||||||
|
const [animation2, watcher2, div2] =
|
||||||
|
setupAnimation(t, 'anim 300s 2', events);
|
||||||
|
|
||||||
|
await watcher2.wait_for('animationstart');
|
||||||
|
|
||||||
|
animation1.currentTime = 400 * MS_PER_SEC;
|
||||||
|
animation2.currentTime = 400 * MS_PER_SEC;
|
||||||
|
|
||||||
|
events.length = 0; // Clear received event array
|
||||||
|
|
||||||
|
await Promise.all([ watcher1.wait_for('animationstart'),
|
||||||
|
watcher2.wait_for('animationiteration') ]);
|
||||||
|
|
||||||
|
checkEvents(events, ['animationiteration', div2, 300],
|
||||||
|
['animationstart', div1, 0]);
|
||||||
|
}, 'Test start and iteration events are ordered by time.');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
let events = [];
|
||||||
|
const [animation1, watcher1, div1] =
|
||||||
|
setupAnimation(t, 'anim 150s', events);
|
||||||
|
const [animation2, watcher2, div2] =
|
||||||
|
setupAnimation(t, 'anim 100s 2', events);
|
||||||
|
|
||||||
|
await Promise.all([ watcher1.wait_for('animationstart'),
|
||||||
|
watcher2.wait_for('animationstart') ]);
|
||||||
|
|
||||||
|
animation1.currentTime = 150 * MS_PER_SEC;
|
||||||
|
animation2.currentTime = 150 * MS_PER_SEC;
|
||||||
|
|
||||||
|
events.length = 0; // Clear received event array
|
||||||
|
|
||||||
|
await Promise.all([ watcher1.wait_for('animationend'),
|
||||||
|
watcher2.wait_for('animationiteration') ]);
|
||||||
|
|
||||||
|
checkEvents(events, ['animationiteration', div2, 100],
|
||||||
|
['animationend', div1, 150]);
|
||||||
|
}, 'Test iteration and end events are ordered by time.');
|
||||||
|
|
||||||
|
promise_test(async t => {
|
||||||
|
let events = [];
|
||||||
|
const [animation1, watcher1, div1] =
|
||||||
|
setupAnimation(t, 'anim 100s 100s', events);
|
||||||
|
const [animation2, watcher2, div2] =
|
||||||
|
setupAnimation(t, 'anim 100s 2', events);
|
||||||
|
|
||||||
|
animation1.finish();
|
||||||
|
animation2.finish();
|
||||||
|
|
||||||
|
await Promise.all([ watcher1.wait_for([ 'animationstart',
|
||||||
|
'animationend' ]),
|
||||||
|
watcher2.wait_for([ 'animationstart',
|
||||||
|
'animationend' ]) ]);
|
||||||
|
|
||||||
|
checkEvents(events, ['animationstart', div2, 0],
|
||||||
|
['animationstart', div1, 0],
|
||||||
|
['animationend', div1, 100],
|
||||||
|
['animationend', div2, 200]);
|
||||||
|
}, 'Test start and end events are sorted correctly when fired simultaneously');
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,177 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this variable if you specify duration or some other properties
|
||||||
|
* for script animation.
|
||||||
|
* E.g., div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
|
||||||
|
*
|
||||||
|
* NOTE: Creating animations with short duration may cause intermittent
|
||||||
|
* failures in asynchronous test. For example, the short duration animation
|
||||||
|
* might be finished when animation.ready has been fulfilled because of slow
|
||||||
|
* platforms or busyness of the main thread.
|
||||||
|
* Setting short duration to cancel its animation does not matter but
|
||||||
|
* if you don't want to cancel the animation, consider using longer duration.
|
||||||
|
*/
|
||||||
|
const MS_PER_SEC = 1000;
|
||||||
|
|
||||||
|
/* The recommended minimum precision to use for time values[1].
|
||||||
|
*
|
||||||
|
* [1] https://drafts.csswg.org/web-animations/#precision-of-time-values
|
||||||
|
*/
|
||||||
|
var TIME_PRECISION = 0.0005; // ms
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allow implementations to substitute an alternative method for comparing
|
||||||
|
* times based on their precision requirements.
|
||||||
|
*/
|
||||||
|
function assert_times_equal(actual, expected, description) {
|
||||||
|
assert_approx_equals(actual, expected, TIME_PRECISION * 2, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compare a time value based on its precision requirements with a fixed value.
|
||||||
|
*/
|
||||||
|
function assert_time_equals_literal(actual, expected, description) {
|
||||||
|
assert_approx_equals(actual, expected, TIME_PRECISION, description);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a div to the document body.
|
||||||
|
*
|
||||||
|
* @param t 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 attrs A dictionary object with attribute names and values to set on
|
||||||
|
* the div.
|
||||||
|
*/
|
||||||
|
function addDiv(t, attrs) {
|
||||||
|
var div = document.createElement('div');
|
||||||
|
if (attrs) {
|
||||||
|
for (var attrName in attrs) {
|
||||||
|
div.setAttribute(attrName, attrs[attrName]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.body.appendChild(div);
|
||||||
|
if (t && typeof t.add_cleanup === 'function') {
|
||||||
|
t.add_cleanup(function() {
|
||||||
|
if (div.parentNode) {
|
||||||
|
div.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends a style div to the document head.
|
||||||
|
*
|
||||||
|
* @param t The testharness.js Test object. If provided, this will be used
|
||||||
|
* to register a cleanup callback to remove the style element
|
||||||
|
* when the test finishes.
|
||||||
|
*
|
||||||
|
* @param rules A dictionary object with selector names and rules to set on
|
||||||
|
* the style sheet.
|
||||||
|
*/
|
||||||
|
function addStyle(t, rules) {
|
||||||
|
var extraStyle = document.createElement('style');
|
||||||
|
document.head.appendChild(extraStyle);
|
||||||
|
if (rules) {
|
||||||
|
var sheet = extraStyle.sheet;
|
||||||
|
for (var selector in rules) {
|
||||||
|
sheet.insertRule(selector + '{' + rules[selector] + '}',
|
||||||
|
sheet.cssRules.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (t && typeof t.add_cleanup === 'function') {
|
||||||
|
t.add_cleanup(function() {
|
||||||
|
extraStyle.remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Promise wrapper for requestAnimationFrame.
|
||||||
|
*/
|
||||||
|
function waitForFrame() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
window.requestAnimationFrame(resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Waits for a requestAnimationFrame callback in the next refresh driver tick.
|
||||||
|
* Note that 'dom.animations-api.core.enabled' pref should be true to use this
|
||||||
|
* function.
|
||||||
|
*/
|
||||||
|
function waitForNextFrame() {
|
||||||
|
const timeAtStart = document.timeline.currentTime;
|
||||||
|
return new Promise(resolve => {
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
|
if (timeAtStart === document.timeline.currentTime) {
|
||||||
|
window.requestAnimationFrame(resolve);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Promise that is resolved after the given number of consecutive
|
||||||
|
* animation frames have occured (using requestAnimationFrame callbacks).
|
||||||
|
*
|
||||||
|
* @param frameCount The number of animation frames.
|
||||||
|
* @param onFrame An optional function to be processed in each animation frame.
|
||||||
|
*/
|
||||||
|
function waitForAnimationFrames(frameCount, onFrame) {
|
||||||
|
const timeAtStart = document.timeline.currentTime;
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
function handleFrame() {
|
||||||
|
if (onFrame && typeof onFrame === 'function') {
|
||||||
|
onFrame();
|
||||||
|
}
|
||||||
|
if (timeAtStart != document.timeline.currentTime &&
|
||||||
|
--frameCount <= 0) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
window.requestAnimationFrame(handleFrame); // wait another frame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.requestAnimationFrame(handleFrame);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper that takes a sequence of N animations and returns:
|
||||||
|
*
|
||||||
|
* Promise.all([animations[0].ready, animations[1].ready, ... animations[N-1].ready]);
|
||||||
|
*/
|
||||||
|
function waitForAllAnimations(animations) {
|
||||||
|
return Promise.all(animations.map(animation => animation.ready));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the computed style for the given element. This is useful, for example,
|
||||||
|
* when we are testing a transition and need the initial value of a property
|
||||||
|
* to be computed so that when we synchronouslyet set it to a different value
|
||||||
|
* we actually get a transition instead of that being the initial value.
|
||||||
|
*/
|
||||||
|
function flushComputedStyle(elem) {
|
||||||
|
var cs = getComputedStyle(elem);
|
||||||
|
cs.marginLeft;
|
||||||
|
}
|
||||||
|
// Waits for a given animation being ready to restyle.
|
||||||
|
async function waitForAnimationReadyToRestyle(aAnimation) {
|
||||||
|
await aAnimation.ready;
|
||||||
|
// If |aAnimation| begins at the current timeline time, we will not process
|
||||||
|
// restyling in the initial frame because of aligning with the refresh driver,
|
||||||
|
// the animation frame in which the ready promise is resolved happens to
|
||||||
|
// coincide perfectly with the start time of the animation. In this case no
|
||||||
|
// restyling is needed in the frame so we have to wait one more frame.
|
||||||
|
if (animationStartsRightNow(aAnimation)) {
|
||||||
|
await waitForNextFrame();
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,10 +53,15 @@ registerPaint('geometry', class {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
try {
|
||||||
CSS.registerProperty({name: '--length', syntax: '<length>', initialValue: '0px'});
|
CSS.registerProperty({name: '--length', syntax: '<length>', initialValue: '0px'});
|
||||||
CSS.registerProperty({name: '--length-initial', syntax: '<length>', initialValue: '20px'});
|
CSS.registerProperty({name: '--length-initial', syntax: '<length>', initialValue: '20px'});
|
||||||
CSS.registerProperty({name: '--number', syntax: '<number>', initialValue: '0'});
|
CSS.registerProperty({name: '--number', syntax: '<number>', initialValue: '0'});
|
||||||
importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
|
importWorkletAndTerminateTestAfterAsyncPaint(CSS.paintWorklet, document.getElementById('code').textContent);
|
||||||
|
} catch(e) {
|
||||||
|
document.body.textContent = e;
|
||||||
|
takeScreenshot();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<title>Reference for sticky hyperlink should work on high dpi devices</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scroller {
|
||||||
|
overflow: scroll;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.positioned {
|
||||||
|
position: relative;
|
||||||
|
top: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spacer {
|
||||||
|
height: 700px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.addEventListener('load', function() {
|
||||||
|
document.querySelector('.scroller').scrollTop = 100;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<div class='scroller'>
|
||||||
|
<a href='#' class='positioned'>Link</a>
|
||||||
|
<div class='spacer'></div>
|
||||||
|
</div>
|
||||||
|
<div>You should see a sticky link at the top of the scrollable area.</div>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue