Auto merge of #10777 - servo:wpt-20160421, r=Ms2ger

Update web-platform-tests to revision 0a518aaff73532a26e175789f7e75fa99593ac64

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/10777)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-04-21 16:53:18 +05:30
commit 1eec5d9f72
92 changed files with 2869 additions and 642 deletions

View file

@ -1,5 +0,0 @@
[EventObject.after.dispatchEvent.html]
type: testharness
[Test Description: As the final step of the event dispatch, the implementation must reset the event object's default-action-prevention state. ]
expected: FAIL

View file

@ -1,5 +0,0 @@
[EventObject.after.dispatchEvent.html]
type: testharness
[Test Description: As the final step of the event dispatch, the implementation must reset the event object's default-action-prevention state. ]
expected: FAIL

View file

@ -42,6 +42,11 @@
[Array with mixed types]
expected: FAIL
[no-argument Blob constructor without 'new']
[Blob constructor with no arguments, without 'new']
bug: https://github.com/servo/servo/issues/10744
expected: FAIL
[Blob constructor with undefined as first argument]
bug: https://github.com/servo/servo/issues/10779
expected: FAIL

View file

@ -3225,6 +3225,14 @@
"path": "touch-events/single-touch-manual.html",
"url": "/touch-events/single-touch-manual.html"
},
{
"path": "uievents/order-of-events/focus-events/focus-manual.html",
"url": "/uievents/order-of-events/focus-events/focus-manual.html"
},
{
"path": "uievents/order-of-events/focus-events/legacy-manual.html",
"url": "/uievents/order-of-events/focus-events/legacy-manual.html"
},
{
"path": "uievents/order-of-events/mouse-events/click-on-body-manual.html",
"url": "/uievents/order-of-events/mouse-events/click-on-body-manual.html"
@ -3695,6 +3703,56 @@
],
"url": "/compat/webkit-text-fill-color-property-001d.html"
},
{
"path": "compat/webkit-text-fill-color-property-002.html",
"references": [
[
"/compat/webkit-text-fill-color-property-002-ref.html",
"=="
]
],
"url": "/compat/webkit-text-fill-color-property-002.html"
},
{
"path": "compat/webkit-text-fill-color-property-003.html",
"references": [
[
"/compat/webkit-text-fill-color-property-003-ref.html",
"=="
]
],
"url": "/compat/webkit-text-fill-color-property-003.html"
},
{
"path": "compat/webkit-text-fill-color-property-004.html",
"references": [
[
"/compat/webkit-text-fill-color-property-004-ref.html",
"=="
]
],
"url": "/compat/webkit-text-fill-color-property-004.html"
},
{
"path": "compat/webkit-text-fill-color-property-005.html",
"references": [
[
"/compat/webkit-text-fill-color-property-005-ref.html",
"=="
]
],
"url": "/compat/webkit-text-fill-color-property-005.html"
},
{
"path": "compat/webkit-text-fill-color-property-006.html",
"references": [
[
"/compat/webkit-text-fill-color-property-006-ref.html",
"=="
]
],
"url": "/compat/webkit-text-fill-color-property-006.html"
},
{
"path": "custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-css-test-custom-tag.html",
"references": [
@ -11067,10 +11125,6 @@
"path": "DOMEvents/tests/approved/EventListener.eventHandler.html",
"url": "/DOMEvents/tests/approved/EventListener.eventHandler.html"
},
{
"path": "DOMEvents/tests/approved/EventObject.after.dispatchEvent.html",
"url": "/DOMEvents/tests/approved/EventObject.after.dispatchEvent.html"
},
{
"path": "DOMEvents/tests/approved/EventObject.multiple.dispatchEvent.html",
"url": "/DOMEvents/tests/approved/EventObject.multiple.dispatchEvent.html"
@ -11107,10 +11161,6 @@
"path": "DOMEvents/tests/submissions/Microsoft/converted/EventListener.eventHandler.html",
"url": "/DOMEvents/tests/submissions/Microsoft/converted/EventListener.eventHandler.html"
},
{
"path": "DOMEvents/tests/submissions/Microsoft/converted/EventObject.after.dispatchEvent.html",
"url": "/DOMEvents/tests/submissions/Microsoft/converted/EventObject.after.dispatchEvent.html"
},
{
"path": "DOMEvents/tests/submissions/Microsoft/converted/EventObject.multiple.dispatchEvent.html",
"url": "/DOMEvents/tests/submissions/Microsoft/converted/EventObject.multiple.dispatchEvent.html"
@ -14047,6 +14097,10 @@
"path": "dom/events/Event-constructors.html",
"url": "/dom/events/Event-constructors.html"
},
{
"path": "dom/events/Event-defaultPrevented-after-dispatch.html",
"url": "/dom/events/Event-defaultPrevented-after-dispatch.html"
},
{
"path": "dom/events/Event-defaultPrevented.html",
"url": "/dom/events/Event-defaultPrevented.html"
@ -18731,6 +18785,10 @@
"path": "html/semantics/embedded-content/the-embed-element/embed-document.html",
"url": "/html/semantics/embedded-content/the-embed-element/embed-document.html"
},
{
"path": "html/semantics/embedded-content/the-embed-element/embed-ignored-in-media-element.html",
"url": "/html/semantics/embedded-content/the-embed-element/embed-ignored-in-media-element.html"
},
{
"path": "html/semantics/embedded-content/the-iframe-element/iframe_javascript_url_01.htm",
"url": "/html/semantics/embedded-content/the-iframe-element/iframe_javascript_url_01.htm"
@ -19991,6 +20049,22 @@
"path": "html/webappapis/scripting/processing-model-2/window-onerror-runtime-error.html",
"url": "/html/webappapis/scripting/processing-model-2/window-onerror-runtime-error.html"
},
{
"path": "html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1.html",
"url": "/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-1.html"
},
{
"path": "html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2.html",
"url": "/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-2.html"
},
{
"path": "html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-3.html",
"url": "/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-3.html"
},
{
"path": "html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html",
"url": "/html/webappapis/scripting/processing-model-2/window-onerror-with-cross-frame-event-listeners-4.html"
},
{
"path": "html/webappapis/system-state-and-capabilities/the-navigator-object/NavigatorID.html",
"url": "/html/webappapis/system-state-and-capabilities/the-navigator-object/NavigatorID.html"
@ -28719,10 +28793,18 @@
"path": "web-animations/animation-effect-timing/duration.html",
"url": "/web-animations/animation-effect-timing/duration.html"
},
{
"path": "web-animations/animation-effect-timing/easing.html",
"url": "/web-animations/animation-effect-timing/easing.html"
},
{
"path": "web-animations/animation-effect-timing/endDelay.html",
"url": "/web-animations/animation-effect-timing/endDelay.html"
},
{
"path": "web-animations/animation-effect-timing/fill.html",
"url": "/web-animations/animation-effect-timing/fill.html"
},
{
"path": "web-animations/animation-effect-timing/getAnimations.html",
"url": "/web-animations/animation-effect-timing/getAnimations.html"
@ -28739,6 +28821,18 @@
"path": "web-animations/animation-effect-timing/iterations.html",
"url": "/web-animations/animation-effect-timing/iterations.html"
},
{
"path": "web-animations/animation-model/animation-types/discrete-animation.html",
"url": "/web-animations/animation-model/animation-types/discrete-animation.html"
},
{
"path": "web-animations/animation-model/animation-types/not-animatable.html",
"url": "/web-animations/animation-model/animation-types/not-animatable.html"
},
{
"path": "web-animations/animation-model/keyframes/effect-value-context.html",
"url": "/web-animations/animation-model/keyframes/effect-value-context.html"
},
{
"path": "web-animations/animation-timeline/document-timeline.html",
"url": "/web-animations/animation-timeline/document-timeline.html"
@ -28759,6 +28853,26 @@
"path": "web-animations/animation/finish.html",
"url": "/web-animations/animation/finish.html"
},
{
"path": "web-animations/animation/finished.html",
"url": "/web-animations/animation/finished.html"
},
{
"path": "web-animations/animation/id.html",
"url": "/web-animations/animation/id.html"
},
{
"path": "web-animations/animation/oncancel.html",
"url": "/web-animations/animation/oncancel.html"
},
{
"path": "web-animations/animation/onfinish.html",
"url": "/web-animations/animation/onfinish.html"
},
{
"path": "web-animations/animation/pause.html",
"url": "/web-animations/animation/pause.html"
},
{
"path": "web-animations/animation/play.html",
"url": "/web-animations/animation/play.html"
@ -28771,6 +28885,14 @@
"path": "web-animations/animation/playbackRate.html",
"url": "/web-animations/animation/playbackRate.html"
},
{
"path": "web-animations/animation/ready.html",
"url": "/web-animations/animation/ready.html"
},
{
"path": "web-animations/animation/reverse.html",
"url": "/web-animations/animation/reverse.html"
},
{
"path": "web-animations/keyframe-effect/constructor.html",
"url": "/web-animations/keyframe-effect/constructor.html"
@ -35091,6 +35213,7 @@
},
"local_changes": {
"deleted": [],
"deleted_reftests": {},
"items": {},
"reftest_nodes": {}
},
@ -35587,6 +35710,66 @@
"url": "/compat/webkit-text-fill-color-property-001d.html"
}
],
"compat/webkit-text-fill-color-property-002.html": [
{
"path": "compat/webkit-text-fill-color-property-002.html",
"references": [
[
"/compat/webkit-text-fill-color-property-002-ref.html",
"=="
]
],
"url": "/compat/webkit-text-fill-color-property-002.html"
}
],
"compat/webkit-text-fill-color-property-003.html": [
{
"path": "compat/webkit-text-fill-color-property-003.html",
"references": [
[
"/compat/webkit-text-fill-color-property-003-ref.html",
"=="
]
],
"url": "/compat/webkit-text-fill-color-property-003.html"
}
],
"compat/webkit-text-fill-color-property-004.html": [
{
"path": "compat/webkit-text-fill-color-property-004.html",
"references": [
[
"/compat/webkit-text-fill-color-property-004-ref.html",
"=="
]
],
"url": "/compat/webkit-text-fill-color-property-004.html"
}
],
"compat/webkit-text-fill-color-property-005.html": [
{
"path": "compat/webkit-text-fill-color-property-005.html",
"references": [
[
"/compat/webkit-text-fill-color-property-005-ref.html",
"=="
]
],
"url": "/compat/webkit-text-fill-color-property-005.html"
}
],
"compat/webkit-text-fill-color-property-006.html": [
{
"path": "compat/webkit-text-fill-color-property-006.html",
"references": [
[
"/compat/webkit-text-fill-color-property-006-ref.html",
"=="
]
],
"url": "/compat/webkit-text-fill-color-property-006.html"
}
],
"custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-css-test-custom-tag.html": [
{
"path": "custom-elements/registering-custom-elements/unresolved-element-pseudoclass/unresolved-element-pseudoclass-css-test-custom-tag.html",
@ -41336,7 +41519,7 @@
}
]
},
"rev": "20fa4a3a71ab7a2f75b4febbe2e98aeeaf022c2b",
"rev": "0a518aaff73532a26e175789f7e75fa99593ac64",
"url_base": "/",
"version": 3
}

View file

@ -2,3 +2,4 @@
type: testharness
[Element scroll maximum test]
expected: FAIL

View file

@ -0,0 +1,6 @@
[window-onerror-with-cross-frame-event-listeners-1.html]
type: testharness
bug: https://github.com/servo/servo/issues/3311
[The error event from an event listener should fire on that listener's global]
expected: FAIL

View file

@ -0,0 +1,6 @@
[window-onerror-with-cross-frame-event-listeners-2.html]
type: testharness
bug: https://github.com/servo/servo/issues/3311
[The error event from an event listener should fire on that listener's global]
expected: FAIL

View file

@ -0,0 +1,6 @@
[window-onerror-with-cross-frame-event-listeners-3.html]
type: testharness
bug: https://github.com/servo/servo/issues/3311
[The error event from an event listener should fire on that listener's global]
expected: FAIL

View file

@ -0,0 +1,6 @@
[window-onerror-with-cross-frame-event-listeners-4.html]
type: testharness
bug: https://github.com/servo/servo/issues/3311
[The error event from an event listener should fire on that listener's global]
expected: FAIL

View file

@ -1 +1 @@
3d4416e1b0ae758e68900f725979238cc0128f8b
9c172f49d08fe9019b0ba193ea4d75c6ddb95cda

View file

@ -0,0 +1,5 @@
[018.html]
type: testharness
[WebSockets: NULL char in url]
expected: FAIL

View file

@ -8,6 +8,7 @@
},
"local_changes": {
"deleted": [],
"deleted_reftests": {},
"items": {
"reftest": {
"css/abs-overflow-stackingcontext.html": [

View file

@ -1,44 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title> Event.defaultPrevented is reset after dipatchEvent() </title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div id=log></div>
<input id="target" type="hidden" value=""/>
<script>
var EVENT = "foo";
var TARGET = document.getElementById("target");
var PreState;
var PosState;
var description = "Test Description: " +
"As the final step of the event dispatch, the implementation must reset the event " +
"object's default-action-prevention state. ";
test(function()
{
var evt = document.createEvent("Event");
evt.initEvent(EVENT, true, true);
TARGET.addEventListener(EVENT, TestEvent, true);
TARGET.dispatchEvent(evt);
PosState = evt.defaultPrevented;
assert_array_equals([evt.target, PreState, PosState], [TARGET, true, false]);
}, description);
function TestEvent(evt)
{
evt.preventDefault();
PreState = evt.defaultPrevented;
}
</script>
</body>
</html>

View file

@ -1,45 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title> Event.defaultPrevented is reset after dipatchEvent() </title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div id=log></div>
<input id="target" type="hidden" value=""/>
<script>
var EVENT = "foo";
var TARGET = document.getElementById("target");
var PreState;
var PosState;
var description = "Test Description: " +
"As the final step of the event dispatch, the implementation must reset the event " +
"object's default-action-prevention state. ";
test(function()
{
var evt = document.createEvent("Event");
evt.initEvent(EVENT, true, true);
TARGET.addEventListener(EVENT, TestEvent, true);
TARGET.dispatchEvent(evt);
PosState = evt.defaultPrevented;
assert_array_equals([evt.target, PreState, PosState], [TARGET, true, false]);
}, description);
function TestEvent(evt)
{
evt.preventDefault();
PreState = evt.defaultPrevented;
}
</script>
</body>
</html>

View file

@ -23,22 +23,28 @@ test(function() {
assert_equals(String(blob), '[object Blob]');
assert_equals(blob.size, 0);
assert_equals(blob.type, "");
}, "no-argument Blob constructor");
}, "Blob constructor with no arguments");
test(function() {
assert_throws(new TypeError(), function() { var blob = Blob(); });
}, "no-argument Blob constructor without 'new'");
}, "Blob constructor with no arguments, without 'new'");
test(function() {
var blob = new Blob;
assert_true(blob instanceof Blob);
assert_equals(blob.size, 0);
assert_equals(blob.type, "");
}, "no-argument Blob constructor without brackets");
}, "Blob constructor without brackets");
test(function() {
var blob = new Blob(undefined);
assert_true(blob instanceof Blob);
assert_equals(String(blob), '[object Blob]');
assert_equals(blob.size, 0);
assert_equals(blob.type, "");
}, "Blob constructor with undefined as first argument");
// blobParts argument (WebIDL).
test(function() {
var args = [
null,
undefined,
true,
false,
0,

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html class="reftest-wait">
<meta charset="utf-8">
<title>webkit-text-fill-color on selected text reference</title>
<link rel="author" title="Jeremy Chen" href="jeremychen@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
<style>
p {
color: green;
}
</style>
<body onload="onload()">
<p>Pass if color of <span id="selectMe">selected text</span> is green or inverted (depending on the platform convention), but not red</p>
<script type="text/javascript">
function onload() {
var elt = document.getElementById("selectMe");
var range = document.createRange();
range.selectNode(elt);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.documentElement.classList.remove('reftest-wait');
}
</script>
</body>
</html>

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<html class="reftest-wait">
<meta charset="utf-8">
<title>webkit-text-fill-color should take effect while rendering selected text</title>
<link rel="author" title="Jeremy Chen" href="jeremychen@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
<link rel="help" href="https://compat.spec.whatwg.org/#the-webkit-text-fill-color">
<meta name="assert" content="The color of selected text should be green">
<link rel="match" href="webkit-text-fill-color-property-002-ref.html">
<style>
p {
color: red;
-webkit-text-fill-color: green;
}
</style>
<body onload="onload()">
<p>Pass if color of <span id="selectMe">selected text</span> is green or inverted (depending on the platform convention), but not red</p>
<script type="text/javascript">
function onload() {
var elt = document.getElementById("selectMe");
var range = document.createRange();
range.selectNode(elt);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.documentElement.classList.remove('reftest-wait');
}
</script>
</body>
</html>

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html class="reftest-wait">
<meta charset="utf-8">
<title>webkit-text-fill-color on ::-moz-selection selected text reference</title>
<link rel="author" title="Jeremy Chen" href="jeremychen@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
<style>
::-moz-selection {
color: green; background: skyblue;
}
::selection {
color: green; background: skyblue;
}
</style>
<body onload="onload()">
<p>Pass if color of <span id="selectMe">selected text</span> is green!!!</p>
<script type="text/javascript">
function onload() {
var elt = document.getElementById("selectMe");
var range = document.createRange();
range.selectNode(elt);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.documentElement.classList.remove('reftest-wait');
}
</script>
</body>
</html>

View file

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html class="reftest-wait">
<meta charset="utf-8">
<title>webkit-text-fill-color should take effect while rendering ::-moz-selection selected text</title>
<link rel="author" title="Jeremy Chen" href="jeremychen@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
<link rel="help" href="https://compat.spec.whatwg.org/#the-webkit-text-fill-color">
<meta name="assert" content="The color of selected text should be green">
<link rel="match" href="webkit-text-fill-color-property-003-ref.html">
<style>
::-moz-selection {
background: skyblue;
color: red;
-webkit-text-fill-color: green;
}
::selection {
background: skyblue;
color: red;
-webkit-text-fill-color: green;
}
</style>
<body onload="onload()">
<p>Pass if color of <span id="selectMe">selected text</span> is green!!!</p>
<script type="text/javascript">
function onload() {
var elt = document.getElementById("selectMe");
var range = document.createRange();
range.selectNode(elt);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
document.documentElement.classList.remove('reftest-wait');
}
</script>
</body>
</html>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>webkit-text-fill-color on MathML reference</title>
<link rel="author" title="Jeremy Chen" href="jeremychen@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
<style>
math {
font-size: 50px;
color: green;
}
</style>
<body>
<p>Pass if color of operators and operands are all green!!!</p>
<math>
<mrow>
<mrow>
<msup>
<mi>a</mi>
<mn>2</mn>
</msup>
<mo>+</mo>
<msup>
<mi>b</mi>
<mn>2</mn>
</msup>
</mrow>
<mo>=</mo>
<msup>
<mi>c</mi>
<mn>2</mn>
</msup>
</mrow>
</math>
</body>

View file

@ -0,0 +1,38 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>webkit-text-fill-color should take effect while rendering MathML</title>
<link rel="author" title="Jeremy Chen" href="jeremychen@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
<link rel="help" href="https://compat.spec.whatwg.org/#the-webkit-text-fill-color">
<meta name="assert" content="The color of MathML should be green">
<link rel="match" href="webkit-text-fill-color-property-004-ref.html">
<style>
math {
font-size: 50px;
color: red;
-webkit-text-fill-color: green;
}
</style>
<body>
<p>Pass if color of operators and operands are all green!!!</p>
<math>
<mrow>
<mrow>
<msup>
<mi>a</mi>
<mn>2</mn>
</msup>
<mo>+</mo>
<msup>
<mi>b</mi>
<mn>2</mn>
</msup>
</mrow>
<mo>=</mo>
<msup>
<mi>c</mi>
<mn>2</mn>
</msup>
</mrow>
</math>
</body>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>webkit-text-fill-color on text-decoration underline reference</title>
<link rel="author" title="Jeremy Chen" href="jeremychen@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
<style type="text/css">
p {
font-size: 50px;
text-decoration: underline;
color: green;
}
</style>
<div><p>Pass if text underline is green!!!</p></div>

View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>webkit-text-fill-color should take effect while rendering text-decoration underline</title>
<link rel="author" title="Jeremy Chen" href="jeremychen@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
<link rel="help" href="https://compat.spec.whatwg.org/#the-webkit-text-fill-color">
<meta name="assert" content="The color of text-decoration underline should be green">
<link rel="match" href="webkit-text-fill-color-property-005-ref.html">
<style type="text/css">
p {
font-size: 50px;
text-decoration: underline;
color: red;
-webkit-text-fill-color: green;
}
</style>
<div><p>Pass if text underline is green!!!</p></div>

View file

@ -0,0 +1,24 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>webkit-text-fill-color on text-overflow ellipsis reference</title>
<link rel="author" title="Jeremy Chen" href="jeremychen@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
<style>
div {
font-size: 10px;
overflow: hidden;
text-overflow: ellipsis;
width: 150px;
color: green;
}
span {
font-family: Ahem;
font-size: 30px;
}
</style>
<body>
<p>Test passes if there is a <strong>green ellipsis</strong> after "TestChecks".</p>
<div>
<span>TestChecksThatTextColorAndEllipsisColorShouldBeTheSame</span>
</div>
</body>

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>webkit-text-fill-color should take effect while rendering text-overflow ellipsis</title>
<link rel="author" title="Jeremy Chen" href="jeremychen@mozilla.com">
<link rel="author" title="Mozilla" href="https://www.mozilla.org">
<link rel="help" href="https://compat.spec.whatwg.org/#the-webkit-text-fill-color">
<meta name="assert" content="The color of text-overflow ellipsis should be green">
<link rel="match" href="webkit-text-fill-color-property-006-ref.html">
<style>
div {
font-size: 10px;
overflow: hidden;
text-overflow: ellipsis;
width: 150px;
color: red;
-webkit-text-fill-color: green;
}
span {
font-family: Ahem;
font-size: 30px;
}
</style>
<body>
<p>Test passes if there is a <strong>green ellipsis</strong> after "TestChecks".</p>
<div>
<span>TestChecksThatTextColorAndEllipsisColorShouldBeTheSame</span>
</div>
</body>

View file

@ -32,7 +32,7 @@ form-action 'self'; script-src 'self' 'unsafe-inline'; connect-src 'self';
<input type="text" name="fieldname" value="fieldvalue">
<input type="submit" id="submit" value="submit">
</form>
<p>Tests that allowed form actions work correctly. If this test passes, you will see a page indicating a form was POSTed.</p>
<p>Tests that allowed form actions work correctly.</p>
<div id="log"></div>
<script async defer src="../support/checkReport.sub.js?reportExists=false"></script>
</body>

View file

@ -31,7 +31,7 @@ form-action 'none'; script-src 'self' 'unsafe-inline'; connect-src 'self';
<input type="text" name="fieldname" value="fieldvalue">
<input type="submit" id="submit" value="submit">
</form>
<p>Tests that blocking form actions works correctly. If this test passes, a CSP violation will be generated, and will not see a page indicating a form was POSTed.</p>
<p>Tests that blocking form actions works correctly.</p>
<div id="log"></div>
<script async defer src="../support/checkReport.sub.js?reportExists=true&amp;reportField=violated-directive&amp;reportValue=form-action%20&apos;none&apos;"></script>

View file

@ -33,7 +33,8 @@ form-action 'self'; script-src 'self' 'unsafe-inline'; connect-src 'self';
<input type="text" name="fieldname" value="fieldvalue">
<input type="submit" id="submit" value="submit">
</form>
<p>Tests that allowed form actions work correctly. If this test passes, you will see a page indicating a form was POSTed.</p>
<p>Tests that allowed form actions work correctly
with GET and a redirect.</p>
<div id="log"></div>
<script async defer src="../support/checkReport.sub.js?reportExists=false"></script>
</body>

View file

@ -33,7 +33,8 @@ form-action 'none'; script-src 'self' 'unsafe-inline'; connect-src 'self';
<input type="text" name="fieldname" value="fieldvalue">
<input type="submit" id="submit" value="submit">
</form>
<p>Tests that allowed form actions work correctly. If this test passes, you will see a page indicating a form was POSTed.</p>
<p>Tests that disallowed form actions are blocked
with GET and redirects.</p>
<div id="log"></div>
<script async defer src="../support/checkReport.sub.js?reportExists=true&amp;reportField=violated-directive&amp;reportValue=form-action%20&apos;none&apos;
"></script>

View file

@ -33,7 +33,7 @@ form-action 'self'; script-src 'self' 'unsafe-inline'; connect-src 'self';
<input type="text" name="fieldname" value="fieldvalue">
<input type="submit" id="submit" value="submit">
</form>
<p>Tests that blocking form redirect works correctly. If this test passes, a CSP violation will be generated, and will not see a page indicating a form was POSTed.</p>
<p>Tests that blocking a POST form with a redirect works correctly. If this test passes, a CSP violation will be generated.</p>
<div id="log"></div>
<script async defer src="../support/checkReport.sub.js?reportExists=true&amp;reportField=violated-directive&amp;reportValue=form-action%20'self'"></script>
</body>

View file

@ -14,14 +14,14 @@ script-src 'self' 'unsafe-inline' 'none'; connect-src 'self';
</head>
<body>
<meta http-equiv="Content-Security-Policy" content="script-src 'none'">
<meta http-equiv="Content-Security-Policy" content="script-src 'self'">
<p>This test checks that Content Security Policy delivered via a meta element is not enforced if the element is outside the document&apos;s head.</p>
<script>
alert_assert("PASS (1/1)");
var aa = "PASS (1/1)";
</script>
<script src="metaHelper.js"></script>
<div id="log"></div>
<script async defer src="../support/checkReport.sub.js?reportExists=false"></script>
<script src="../support/checkReport.sub.js?reportExists=false"></script>
</body>
</html>

View file

@ -0,0 +1,5 @@
if (typeof aa != 'undefined') {
alert_assert(aa);
} else {
alert_assert("Failed - allowed inline script blocked by meta policy outside head.");
}

View file

@ -6,7 +6,7 @@
<title>plugintypes-notype-data</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src='../support/logTest.sub.js?logs=["PASS"]'></script>
<script src='../support/logTest.sub.js?logs=["PASS: object tag onerror handler fired"]'></script>
<script src="../support/alertAssert.sub.js?alerts=[]"></script>
<!-- enforcing policy:
plugin-types application/x-invalid-type; script-src 'self' 'unsafe-inline'; connect-src 'self';
@ -15,7 +15,7 @@ plugin-types application/x-invalid-type; script-src 'self' 'unsafe-inline'; conn
<body>
Given a `plugin-types` directive, plugins have to declare a type explicitly. No declared type, no load. This test passes if there&apos;s a CSP report and &quot;FAIL!&quot; isn&apos;t logged.
<object data="data:application/x-webkit-test-netscape" onload="log('FAIL');" onerror="log('PASS');"></object>
<object data="data:application/x-webkit-test-netscape" onload="log('FAIL');" onerror="log('PASS: object tag onerror handler fired');"></object>
<div id="log"></div>
<script async defer src="../support/checkReport.sub.js?reportExists=true&amp;reportField=violated-directive&amp;reportValue=plugin-types+application/x-invalid-type"></script>
</body>

View file

@ -7,7 +7,6 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</script>
<!-- enforcing policy:
script-src 'self' 'nonce-nonceynonce' 'sha256-dWTP4Di8KBjaiXvQ5mRquI9OoBSo921ahYxLfYSiuT8='; connect-src 'self';
-->
@ -34,12 +33,9 @@ script-src 'self' 'nonce-nonceynonce' 'sha256-dWTP4Di8KBjaiXvQ5mRquI9OoBSo921ahY
var scriptContent2 = "window.finish('" + nonMatchingContent + "');";
var script1 = document.createElement('script');
script1.innerHTML = scriptContent1;
var script2 = document.createElement('script');
script2.innerHTML = scriptContent2;
script1.test = async_test("Inline script with hash in CSP");
script2.test = async_test("Inline script without hash in CSP");
script1.test = async_test("Only matching content runs even with NFC normalization.");
var failure = function() {
assert_unreached();
@ -51,16 +47,18 @@ script-src 'self' 'nonce-nonceynonce' 'sha256-dWTP4Di8KBjaiXvQ5mRquI9OoBSo921ahY
script1.test.done();
});
} else {
assert_unreached();
script1.test.step(function() {
assert_unreached("nonMatchingContent script ran");
});
}
}
script1.onerror = failure;
script2.onerror = script2.test.step_func(function() {
script2.test.done();
});
document.body.appendChild(script1);
document.body.appendChild(script2);
script2.textContent = scriptContent2;
document.body.appendChild(script1);
script1.textContent = scriptContent1;
</script>
<p>

View file

@ -6,49 +6,16 @@
<title>scriptnonce-basic-blocked</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script nonce='noncynonce'>
function log(msg) {
test(function() {
assert_unreached(msg)
});
}
</script>
<script nonce='noncynonce'>
var t_alert = async_test('Expecting alerts: ["PASS (1/2)","PASS (2/2)"]');
var expected_alerts = ["PASS (1/2)", "PASS (2/2)"];
function alert_assert(msg) {
t_alert.step(function() {
if (msg.match(/^FAIL/i)) {
assert_unreached(msg);
t_alert.done();
}
for (var i = 0; i < expected_alerts.length; i++) {
if (expected_alerts[i] == msg) {
assert_true(expected_alerts[i] == msg);
expected_alerts.splice(i, 1);
if (expected_alerts.length == 0) {
t_alert.done();
}
return;
}
}
assert_unreached('unexpected alert: ' + msg);
t_log.done();
});
}
</script>
<script src='../support/alertAssert.sub.js?alerts=["PASS (closely-quoted nonce)","PASS (nonce w/whitespace)"]'></script>
<!-- enforcing policy:
script-src 'self' 'unsafe-inline' 'nonce-noncynonce'; connect-src 'self';
-->
<script nonce="noncynonce">
alert_assert('PASS (1/2)');
alert_assert('PASS (closely-quoted nonce)');
</script>
<script nonce=" noncynonce ">
alert_assert('PASS (2/2)');
alert_assert('PASS (nonce w/whitespace)');
</script>
<script nonce="noncynonce noncynonce">

View file

@ -34,7 +34,7 @@
</script>
<!-- enforcing policy:
style-src 'sha1-eYyYGmKWdhpUewohaXk9o8IaLSw=' 'sha256-hndjYvzUzy2Ykuad81Cwsl1FOXX/qYs/aDVyUyNZwBw=' 'sha384-bSVm1i3sjPBRM4TwZtYTDjk9JxZMExYHWbFmP1SxDhJH4ue0Wu9OPOkY5hcqRcSt' 'sha512-440MmBLtj9Kp5Bqloogn9BqGDylY8vFsv5/zXL1zH2fJVssCoskRig4gyM+9KqwvCSapSz5CVoUGHQcxv43UQg=='; script-src 'self' 'unsafe-inline'; connect-src 'self';
style-src 'sha256-pAKi9r4/WB7fHydbE3F3t8i8602ij2JN8zHJpL2T5BM=' 'sha256-hndjYvzUzy2Ykuad81Cwsl1FOXX/qYs/aDVyUyNZwBw=' 'sha384-bSVm1i3sjPBRM4TwZtYTDjk9JxZMExYHWbFmP1SxDhJH4ue0Wu9OPOkY5hcqRcSt' 'sha512-440MmBLtj9Kp5Bqloogn9BqGDylY8vFsv5/zXL1zH2fJVssCoskRig4gyM+9KqwvCSapSz5CVoUGHQcxv43UQg=='; script-src 'self' 'unsafe-inline'; connect-src 'self';
-->
</head>

View file

@ -3,4 +3,4 @@ Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0, false
Pragma: no-cache
Set-Cookie: stylehash-allowed={{$id:uuid()}}; Path=/content-security-policy/blink-contrib-2
Content-Security-Policy: style-src 'self' 'sha1-eYyYGmKWdhpUewohaXk9o8IaLSw=' 'sha256-hndjYvzUzy2Ykuad81Cwsl1FOXX/qYs/aDVyUyNZwBw=' 'sha384-bSVm1i3sjPBRM4TwZtYTDjk9JxZMExYHWbFmP1SxDhJH4ue0Wu9OPOkY5hcqRcSt' 'sha512-440MmBLtj9Kp5Bqloogn9BqGDylY8vFsv5/zXL1zH2fJVssCoskRig4gyM+9KqwvCSapSz5CVoUGHQcxv43UQg=='; script-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}}
Content-Security-Policy: style-src 'self' 'sha256-pAKi9r4/WB7fHydbE3F3t8i8602ij2JN8zHJpL2T5BM=' 'sha256-hndjYvzUzy2Ykuad81Cwsl1FOXX/qYs/aDVyUyNZwBw=' 'sha384-bSVm1i3sjPBRM4TwZtYTDjk9JxZMExYHWbFmP1SxDhJH4ue0Wu9OPOkY5hcqRcSt' 'sha512-440MmBLtj9Kp5Bqloogn9BqGDylY8vFsv5/zXL1zH2fJVssCoskRig4gyM+9KqwvCSapSz5CVoUGHQcxv43UQg=='; script-src 'self' 'unsafe-inline'; connect-src 'self'; report-uri /content-security-policy/support/report.py?op=put&reportID={{$id}}

View file

@ -15,16 +15,27 @@ connect-src 'self' http://{{host}}:{{ports[http][0]}}; script-src 'self' 'unsafe
<body>
<script>
try {
var es = navigator.sendBeacon("http://{{host}}:{{ports[http][0]}}/cors/resources/status.py");
log("Pass");
} catch (e) {
log("Fail");
}
if (typeof navigator.sendBeacon != 'function') {
t_log.set_status(t_log.NOTRUN, "No navigator.sendBeacon, cannot run test.");
t_log.phase = t_log.phases.HAS_RESULT;
t_log.done();
} else {
try {
var es = navigator.sendBeacon("http://{{host}}:{{ports[http][0]}}/cors/resources/status.py");
log("Pass");
} catch (e) {
log("Fail");
}
var report = document.createElement("script");
report.src = "../support/checkReport.sub.js?reportExists=false";
report.async = true;
report.defer = true;
document.body.appendChild(report);
}
</script>
<div id="log"></div>
<script async defer src="../support/checkReport.sub.js?reportExists=false"></script>
</body>
</html>

View file

@ -15,16 +15,26 @@ connect-src 'self' http://{{host}}:{{ports[http][0]}}; script-src 'self' 'unsafe
<body>
<script>
try {
var es = navigator.sendBeacon("http://www1.{{host}}:{{ports[http][0]}}/security/contentSecurityPolicy/echo-report.php");
log("Fail");
} catch (e) {
log("Pass");
}
if (typeof navigator.sendBeacon != 'function') {
t_log.set_status(t_log.NOTRUN, "No navigator.sendBeacon, cannot run test.");
t_log.phase = t_log.phases.HAS_RESULT;
t_log.done();
} else {
try {
var es = navigator.sendBeacon("http://www1.{{host}}:{{ports[http][0]}}/security/contentSecurityPolicy/echo-report.php");
log("Fail");
} catch (e) {
log("Pass");
}
var report = document.createElement("script");
report.src = "../support/checkReport.sub.js?reportExists=true&amp;reportField=violated-directive&amp;reportValue=connect-src%20&apos;self&apos;";
report.async = true;
report.defer = true;
document.body.appendChild(report);
}
</script>
<div id="log"></div>
<script async defer src="../support/checkReport.sub.js?reportExists=true&amp;reportField=violated-directive&amp;reportValue=connect-src%20&apos;self&apos;"></script>
</body>
</html>

View file

@ -18,13 +18,24 @@ connect-src 'self'; script-src 'self' 'unsafe-inline';
<p>The beacon should not follow the redirect to http://www1.{{host}}:{{ports[http][0]}}/content-security-policy/support/fail.png and send a CSP violation report.</p>
<p>Verify that a CSP connect-src directive blocks redirects.</p>
<script>
navigator.sendBeacon(
"/common/redirect.py?location=http://www1.{{host}}:{{ports[http][0]}}/content-security-policy/support/fail.png",
"ping");
if (typeof navigator.sendBeacon != 'function') {
var t = async_test();
t.set_status(t.NOTRUN, "No navigator.sendBeacon, cannot run test.");
t.phase = t.phases.HAS_RESULT;
t.done();
} else {
navigator.sendBeacon(
"/common/redirect.py?location=http://www1.{{host}}:{{ports[http][0]}}/content-security-policy/support/fail.png",
"ping");
var report = document.createElement("script");
report.src = "../support/checkReport.sub.js?reportExists=true&amp;reportField=violated-directive&amp;reportValue=connect-src%20&apos;self&apos;";
report.async = true;
report.defer = true;
document.body.appendChild(report);
}
</script>
<div id="log"></div>
<script async defer src="../support/checkReport.sub.js?reportExists=true&amp;reportField=violated-directive&amp;reportValue=connect-src%20&apos;self&apos;"></script>
</body>
</html>

View file

@ -2,5 +2,5 @@ try {
importScripts("/content-security-policy/blink-contrib/resources/post-message.js");
postMessage("importScripts allowed");
} catch (e) {
postMessage("importScripts blocked: " + e);
postMessage("importScripts blocked");
}

View file

@ -16,6 +16,11 @@ connect-src 'self' http://{{host}}:{{ports[http][0]}}; script-src 'self' 'unsafe
<body>
<script>
if(typeof SharedWorker != 'function') {
t_alert.set_status(t_alert.NOTRUN, "No SharedWorker, cannot run test.");
t_alert.phase = t_alert.phases.HAS_RESULT;
t_alert.done();
} else {
try {
var worker = new SharedWorker('http://{{host}}:{{ports[http][0]}}/content-security-policy/blink-contrib/resources/shared-worker-make-xhr-allowed.sub.js');
worker.port.onmessage = function(event) {
@ -24,10 +29,15 @@ connect-src 'self' http://{{host}}:{{ports[http][0]}}; script-src 'self' 'unsafe
} catch (e) {
alert_assert(e);
}
var report = document.createElement("script");
report.src = "../support/checkReport.sub.js?reportExists=false";
report.async = true;
report.defer = true;
document.body.appendChild(report);
}
</script>
<div id="log"></div>
<script async defer src="../support/checkReport.sub.js?reportExists=false"></script>
</body>
</html>

View file

@ -22,6 +22,11 @@ connect-src *; script-src 'self' 'unsafe-inline';
should be sent since the worker's policy doesn't specify
a report-uri.</p>
<script>
if(typeof SharedWorker != 'function') {
t_alert.set_status(t_alert.NOTRUN, "No SharedWorker, cannot run test.");
t_alert.phase = t_alert.phases.HAS_RESULT;
t_alert.done();
} else {
try {
var worker = new SharedWorker('http://{{host}}:{{ports[http][0]}}/content-security-policy/blink-contrib/resources/shared-worker-make-xhr-blocked.sub.js');
worker.port.onmessage = function(event) {
@ -30,10 +35,16 @@ connect-src *; script-src 'self' 'unsafe-inline';
} catch (e) {
alert_assert(e);
}
var report = document.createElement("script");
report.src = "../support/checkReport.sub.js?reportExists=false";
report.async = true;
report.defer = true;
document.body.appendChild(report);
}
</script>
<div id="log"></div>
<script async defer src="../support/checkReport.sub.js?reportExists=false"></script>
</body>
</html>

View file

@ -23,7 +23,7 @@ script-src 'self' 'unsafe-inline' 'unsafe-eval' 'unsafe-inline' 127.0.0.1:8000;
worker.onmessage = function(event) {
result = event.data;
test(function() {
assert_equals(result, 'importScripts blocked: NetworkError: Failed to execute \'importScripts\' on \'WorkerGlobalScope\': The script at \'http://{{host}}:{{ports[http][0]}}/content-security-policy/blink-contrib/resources/post-message.js\' failed to load.')
assert_equals(result, 'importScripts blocked')
});
log("TEST COMPLETE");
};

View file

@ -17,6 +17,10 @@ child-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-inline'; connect-src
<script>
try {
var foo = new Worker('http://{{host}}:{{ports[http][0]}}/content-security-policy/blink-contrib/resources/post-message.js');
foo.onerror = function(event) {
event.preventDefault();
alert_assert("PASS");
}
foo.onmessage = function(event) {
alert_assert("FAIL");
};

View file

@ -52,10 +52,17 @@
source_test.step(function() {
source_test.set_status(source_test.FAIL);
});
setTimeout(function() {
if(source_test.phase != source_test.phases.COMPLETE) {
source_test.step( function () { assert_unreached("Onerror event never fired for track element."); });
source_test.done();
}
}, 2 * 1000);
</script>
<script async defer src="../support/checkReport.sub.js?reportField=violated-directive&reportValue=media-src%20%27self%27">
</script>
</body>
</html>
</html>

View file

@ -55,8 +55,8 @@
</video>
<video id="videoObject2" width="320" height="240" controls
onerror="media_error_handler(src_test)"
onloadeddata="media_loaded(src_test)"
onerror="media_error_handler(src_redir_test)"
onloadeddata="media_loaded(src_redir_test)"
src="/common/redirect.py?location=http://www2.{{host}}:{{ports[http][0]}}/media/white.mp4">
<script async defer src="../support/checkReport.sub.js?reportExists=false">

View file

@ -1,5 +1,9 @@
// note, this template substitution is XSS, but no way to avoid it in this framework
var expected_alerts = {{GET[alerts]}};
var timeout= "{{GET[timeout]}}";
if (timeout == "") {
timeout = 2;
}
if(expected_alerts.length == 0) {
function alert_assert(msg) {
@ -7,7 +11,13 @@ if(expected_alerts.length == 0) {
}
} else {
var t_alert = async_test('Expecting alerts: {{GET[alerts]}}');
function alert_assert(msg) {
setTimeout(function() {
if(t_alert.phase != t_alert.phases.COMPLETE) {
t_alert.step(function() { assert_unreached('Alert timeout, expected alerts ' + expected_alerts + ' not fired.') });
t_alert.done();
}
}, timeout * 100);
var alert_assert = function (msg) {
t_alert.step(function () {
if(msg && msg instanceof Error) {
msg = msg.message;
@ -29,5 +39,5 @@ if(expected_alerts.length == 0) {
assert_unreached('unexpected alert: ' + msg);
t_log.done();
});
}
}.bind(this);
}

View file

@ -1,5 +1,9 @@
// note, this template substitution is XSS, but no way to avoid it in this framework
var expected_logs = {{GET[logs]}};
var timeout = "{{GET[timeout]}}";
if (timeout == "") {
timeout = 2;
}
if (expected_logs.length == 0) {
function log_assert(msg) {
@ -7,6 +11,12 @@ if (expected_logs.length == 0) {
}
} else {
var t_log = async_test('Expecting logs: {{GET[logs]}}');
setTimeout(function() {
if(t_log.phase != t_log.phases.COMPLETE){
t_log.step(function () { assert_unreached('Logging timeout, expected logs ' + expected_logs + ' not sent.') });
t_log.done();
}
}, timeout * 1000);
function log(msg) {
//cons/**/ole.log(msg);
t_log.step(function () {

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Event.defaultPrevented is not reset after dipatchEvent()</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div id=log></div>
<input id="target" type="hidden" value=""/>
<script>
test(function() {
var EVENT = "foo";
var TARGET = document.getElementById("target");
var evt = document.createEvent("Event");
evt.initEvent(EVENT, true, true);
TARGET.addEventListener(EVENT, this.step_func(function(e) {
e.preventDefault();
assert_true(e.defaultPrevented, "during dispatch");
}), true);
TARGET.dispatchEvent(evt);
assert_true(evt.defaultPrevented, "after dispatch");
assert_equals(evt.target, TARGET);
});
</script>

View file

@ -1,3 +1,4 @@
@ayg
@Ms2ger
@foolip
@gsnedders

View file

@ -0,0 +1,22 @@
<!doctype html>
<meta charset="utf-8">
<title>HTML Test: The embed element represents a document</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<meta name="assert" content="Check if the embed element is ignored when used inside a media element">
<script type="application/javascript">
window.childLoaded = false;
async_test(function() {
addEventListener("load", this.step_func_done(function() {
assert_false(window.childLoaded);
}));
}, "Test embed being ignored inside media element");
</script>
<body>
<video>
<embed type="text/html" src="embed-iframe.html" />
</video>
<audio>
<embed type="text/html" src="embed-iframe.html" />
</audio>
</body>

View file

@ -0,0 +1,33 @@
<!doctype html>
<meta charset=utf-8>
<title>
When a listener from window A is added to an event target in window B via the
addEventListener function from window B, errors in that listener should be
reported to window A.
</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<iframe></iframe>
<iframe></iframe>
<script>
test(function() {
var f = new frames[0].Function("thereIsNoSuchCallable()");
frames[1].document.addEventListener("myevent", f);
var frame0ErrorFired = false;
var frame1ErrorFired = false;
var ourErrorFired = false;
frames[0].addEventListener("error", function() {
frame0ErrorFired = true;
});
frames[1].addEventListener("error", function() {
frame1ErrorFired = true;
});
addEventListener("error", function() {
ourErrorFired = true;
});
frames[1].document.dispatchEvent(new Event("myevent"));
assert_true(frame0ErrorFired);
assert_false(frame1ErrorFired);
assert_false(ourErrorFired);
}, "The error event from an event listener should fire on that listener's global");
</script>

View file

@ -0,0 +1,33 @@
<!doctype html>
<meta charset=utf-8>
<title>
When a listener from window A is added to an event target in window B via the
addEventListener function from window A, errors in that listener should be
reported to window A.
</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<iframe></iframe>
<iframe></iframe>
<script>
test(function() {
var f = new frames[0].Function("thereIsNoSuchCallable()");
frames[0].document.addEventListener.call(frames[1].document, "myevent", f);
var frame0ErrorFired = false;
var frame1ErrorFired = false;
var ourErrorFired = false;
frames[0].addEventListener("error", function() {
frame0ErrorFired = true;
});
frames[1].addEventListener("error", function() {
frame1ErrorFired = true;
});
addEventListener("error", function() {
ourErrorFired = true;
});
frames[1].document.dispatchEvent(new Event("myevent"));
assert_true(frame0ErrorFired);
assert_false(frame1ErrorFired);
assert_false(ourErrorFired);
}, "The error event from an event listener should fire on that listener's global");
</script>

View file

@ -0,0 +1,33 @@
<!doctype html>
<meta charset=utf-8>
<title>
When a listener from window A is added to an event target in window A via the
addEventListener function from window A, errors in that listener should be
reported to window A.
</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<iframe></iframe>
<iframe></iframe>
<script>
test(function() {
var f = new frames[1].Function("thereIsNoSuchCallable()");
frames[1].document.addEventListener("myevent", f);
var frame0ErrorFired = false;
var frame1ErrorFired = false;
var ourErrorFired = false;
frames[0].addEventListener("error", function() {
frame0ErrorFired = true;
});
frames[1].addEventListener("error", function() {
frame1ErrorFired = true;
});
addEventListener("error", function() {
ourErrorFired = true;
});
frames[1].document.dispatchEvent(new Event("myevent"));
assert_false(frame0ErrorFired);
assert_true(frame1ErrorFired);
assert_false(ourErrorFired);
}, "The error event from an event listener should fire on that listener's global");
</script>

View file

@ -0,0 +1,33 @@
<!doctype html>
<meta charset=utf-8>
<title>
When a listener from window A is added to an event target in window A via the
addEventListener function from window B, errors in that listener should be
reported to window A.
</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<iframe></iframe>
<iframe></iframe>
<script>
test(function() {
var f = new frames[1].Function("thereIsNoSuchCallable()");
frames[0].document.addEventListener.call(frames[1].document, "myevent", f);
var frame0ErrorFired = false;
var frame1ErrorFired = false;
var ourErrorFired = false;
frames[0].addEventListener("error", function() {
frame0ErrorFired = true;
});
frames[1].addEventListener("error", function() {
frame1ErrorFired = true;
});
addEventListener("error", function() {
ourErrorFired = true;
});
frames[1].document.dispatchEvent(new Event("myevent"));
assert_false(frame0ErrorFired);
assert_true(frame1ErrorFired);
assert_false(ourErrorFired);
}, "The error event from an event listener should fire on that listener's global");
</script>

View file

@ -3,3 +3,4 @@
@foolip
@shishimaru
@sideshowbarker
@wolenetz

View file

@ -161,6 +161,13 @@ function resource_load(expected)
t["timing_attrs"].step(function test() {
var actual = window.performance.getEntriesByName(expected.name)[0];
// Debugging bug 1263428
// Feel free to remove/overwrite this piece of code
if (actual.connectStart < actual.domainLookupEnd) {
assert_true(false, "actual: "+JSON.stringify(actual));
}
assert_equals(actual.redirectStart, 0, "redirectStart time");
assert_equals(actual.redirectEnd, 0, "redirectEnd time");
assert_true(actual.secureConnectionStart == undefined ||

View file

@ -4,7 +4,7 @@
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-workers/resources/test-helpers.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-add.js');
</script>

View file

@ -4,7 +4,7 @@
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-workers/resources/test-helpers.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-delete.js');
</script>

View file

@ -4,7 +4,7 @@
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-workers/resources/test-helpers.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-match.js');
</script>

View file

@ -4,7 +4,7 @@
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-workers/resources/test-helpers.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-matchAll.js');
</script>

View file

@ -4,7 +4,7 @@
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-workers/resources/test-helpers.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-put.js');
</script>

View file

@ -4,7 +4,7 @@
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-workers/resources/test-helpers.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-storage-keys.js');
</script>

View file

@ -4,7 +4,7 @@
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-workers/resources/test-helpers.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-storage-match.js');
</script>

View file

@ -4,7 +4,7 @@
<meta name="timeout" content="long">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-workers/resources/test-helpers.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<script>
service_worker_test('../script-tests/cache-storage.js');
</script>

View file

@ -4,7 +4,7 @@
<link rel="help" href="https://slightlyoff.github.io/ServiceWorker/spec/service_worker/#cache-storage">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../service-workers/resources/test-helpers.js"></script>
<script src="../../service-worker/resources/test-helpers.sub.js"></script>
<style>iframe { display: none; }</style>
<script>

View file

@ -440,8 +440,9 @@ async_test(function(t) {
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
assert_equals(frame.contentWindow.document.body.textContent, 'default');
var tests = cacheTypes.map(function(type) {
return new Promise(function(resolve) {
return new Promise(function(resolve, reject) {
return frame.contentWindow.fetch(scope + '=' + type,
{cache: type})
.then(function(response) { return response.text(); })
@ -450,10 +451,25 @@ async_test(function(t) {
assert_equals(response_text, expected,
'Service Worker should respond to fetch with the correct type');
})
.then(resolve);
.then(resolve)
.catch(reject);
});
});
return Promise.all(tests);
})
.then(function() {
return new Promise(function(resolve, reject) {
frame.addEventListener('load', function onLoad() {
frame.removeEventListener('load', onLoad);
try {
assert_equals(frame.contentWindow.document.body.textContent,
'no-cache');
resolve();
} catch (e) {
reject(e);
}
});
frame.contentWindow.location.reload();
});
})
.then(function() {
frame.remove();
@ -462,5 +478,55 @@ async_test(function(t) {
.catch(unreached_rejection(t));
}, 'Service Worker responds to fetch event with the correct cache types');
async_test(function(t) {
var scope = 'resources/simple.html?eventsource';
var frame;
function test_eventsource(opts) {
return new Promise(function(resolve, reject) {
var eventSource = new frame.contentWindow.EventSource(scope, opts);
eventSource.addEventListener('message', function(msg) {
eventSource.close();
try {
var data = JSON.parse(msg.data);
assert_equals(data.mode, 'cors',
'EventSource should make CORS requests.');
assert_equals(data.cache, 'no-store',
'EventSource should bypass the http cache.');
var expectedCredentials = opts.withCredentials ? 'include'
: 'same-origin';
assert_equals(data.credentials, expectedCredentials,
'EventSource should pass correct credentials mode.');
resolve();
} catch (e) {
reject(e);
}
});
eventSource.addEventListener('error', function(e) {
eventSource.close();
reject('The EventSource fired an error event.');
});
});
}
service_worker_unregister_and_register(t, worker, scope)
.then(function(reg) {
return wait_for_state(t, reg.installing, 'activated');
})
.then(function() { return with_iframe(scope); })
.then(function(f) {
frame = f;
return test_eventsource({ withCredentials: false });
})
.then(function() {
return test_eventsource({ withCredentials: true });
})
.then(function() {
frame.remove();
return service_worker_unregister_and_done(t, scope);
})
.catch(unreached_rejection(t));
}, 'Service Worker should intercept EventSource');
</script>
</body>

View file

@ -95,6 +95,22 @@ function handleCache(event) {
event.respondWith(new Response(event.request.cache));
}
function handleEventSource(event) {
if (event.request.mode === 'navigate') {
return;
}
var data = {
mode: event.request.mode,
cache: event.request.cache,
credentials: event.request.credentials
};
var body = 'data:' + JSON.stringify(data) + '\n\n';
event.respondWith(new Response(body, {
headers: { 'Content-Type': 'text/event-stream' }
}
));
}
self.addEventListener('fetch', function(event) {
var url = event.request.url;
var handlers = [
@ -112,6 +128,7 @@ self.addEventListener('fetch', function(event) {
{ pattern: '?used-check', fn: handleUsedCheck },
{ pattern: '?fragment-check', fn: handleFragmentCheck },
{ pattern: '?cache', fn: handleCache },
{ pattern: '?eventsource', fn: handleEventSource },
];
var handler = null;

View file

@ -1,222 +0,0 @@
// Adapter for testharness.js-style tests with Service Workers
function service_worker_unregister_and_register(test, url, scope) {
if (!scope || scope.length == 0)
return Promise.reject(new Error('tests must define a scope'));
var options = { scope: scope };
return service_worker_unregister(test, scope)
.then(function() {
return navigator.serviceWorker.register(url, options);
})
.catch(unreached_rejection(test,
'unregister and register should not fail'));
}
function service_worker_unregister(test, documentUrl) {
return navigator.serviceWorker.getRegistration(documentUrl)
.then(function(registration) {
if (registration)
return registration.unregister();
})
.catch(unreached_rejection(test, 'unregister should not fail'));
}
function service_worker_unregister_and_done(test, scope) {
return service_worker_unregister(test, scope)
.then(test.done.bind(test));
}
function unreached_fulfillment(test, prefix) {
return test.step_func(function(result) {
var error_prefix = prefix || 'unexpected fulfillment';
assert_unreached(error_prefix + ': ' + result);
});
}
// Rejection-specific helper that provides more details
function unreached_rejection(test, prefix) {
return test.step_func(function(error) {
var reason = error.message || error.name || error;
var error_prefix = prefix || 'unexpected rejection';
assert_unreached(error_prefix + ': ' + reason);
});
}
// Adds an iframe to the document and returns a promise that resolves to the
// iframe when it finishes loading. The caller is responsible for removing the
// iframe later if needed.
function with_iframe(url) {
return new Promise(function(resolve) {
var frame = document.createElement('iframe');
frame.src = url;
frame.onload = function() { resolve(frame); };
document.body.appendChild(frame);
});
}
function normalizeURL(url) {
return new URL(url, self.location).toString().replace(/#.*$/, '');
}
function wait_for_update(test, registration) {
if (!registration || registration.unregister == undefined) {
return Promise.reject(new Error(
'wait_for_update must be passed a ServiceWorkerRegistration'));
}
return new Promise(test.step_func(function(resolve) {
registration.addEventListener('updatefound', test.step_func(function() {
resolve(registration.installing);
}));
}));
}
function wait_for_state(test, worker, state) {
if (!worker || worker.state == undefined) {
return Promise.reject(new Error(
'wait_for_state must be passed a ServiceWorker'));
}
if (worker.state === state)
return Promise.resolve(state);
if (state === 'installing') {
switch (worker.state) {
case 'installed':
case 'activating':
case 'activated':
case 'redundant':
return Promise.reject(new Error(
'worker is ' + worker.state + ' but waiting for ' + state));
}
}
if (state === 'installed') {
switch (worker.state) {
case 'activating':
case 'activated':
case 'redundant':
return Promise.reject(new Error(
'worker is ' + worker.state + ' but waiting for ' + state));
}
}
if (state === 'activating') {
switch (worker.state) {
case 'activated':
case 'redundant':
return Promise.reject(new Error(
'worker is ' + worker.state + ' but waiting for ' + state));
}
}
if (state === 'activated') {
switch (worker.state) {
case 'redundant':
return Promise.reject(new Error(
'worker is ' + worker.state + ' but waiting for ' + state));
}
}
return new Promise(test.step_func(function(resolve) {
worker.addEventListener('statechange', test.step_func(function() {
if (worker.state === state)
resolve(state);
}));
}));
}
// Declare a test that runs entirely in the ServiceWorkerGlobalScope. The |url|
// is the service worker script URL. This function:
// - Instantiates a new test with the description specified in |description|.
// The test will succeed if the specified service worker can be successfully
// registered and installed.
// - Creates a new ServiceWorker registration with a scope unique to the current
// document URL. Note that this doesn't allow more than one
// service_worker_test() to be run from the same document.
// - Waits for the new worker to begin installing.
// - Imports tests results from tests running inside the ServiceWorker.
function service_worker_test(url, description) {
// If the document URL is https://example.com/document and the script URL is
// https://example.com/script/worker.js, then the scope would be
// https://example.com/script/scope/document.
var scope = new URL('scope' + window.location.pathname,
new URL(url, window.location)).toString();
promise_test(function(test) {
return service_worker_unregister_and_register(test, url, scope)
.then(function(registration) {
add_completion_callback(function() {
registration.unregister();
});
return wait_for_update(test, registration)
.then(function(worker) {
return fetch_tests_from_worker(worker);
});
});
}, description);
}
function get_host_info() {
var ORIGINAL_HOST = '127.0.0.1';
var REMOTE_HOST = 'localhost';
var UNAUTHENTICATED_HOST = 'example.test';
var HTTP_PORT = 8000;
var HTTPS_PORT = 8443;
try {
// In W3C test, we can get the hostname and port number in config.json
// using wptserve's built-in pipe.
// http://wptserve.readthedocs.org/en/latest/pipes.html#built-in-pipes
HTTP_PORT = eval('{{ports[http][0]}}');
HTTPS_PORT = eval('{{ports[https][0]}}');
ORIGINAL_HOST = eval('\'{{host}}\'');
REMOTE_HOST = 'www1.' + ORIGINAL_HOST;
} catch (e) {
}
return {
HTTP_ORIGIN: 'http://' + ORIGINAL_HOST + ':' + HTTP_PORT,
HTTPS_ORIGIN: 'https://' + ORIGINAL_HOST + ':' + HTTPS_PORT,
HTTP_REMOTE_ORIGIN: 'http://' + REMOTE_HOST + ':' + HTTP_PORT,
HTTPS_REMOTE_ORIGIN: 'https://' + REMOTE_HOST + ':' + HTTPS_PORT,
UNAUTHENTICATED_ORIGIN: 'http://' + UNAUTHENTICATED_HOST + ':' + HTTP_PORT
};
}
function base_path() {
return location.pathname.replace(/\/[^\/]*$/, '/');
}
function test_login(test, origin, username, password, cookie) {
return new Promise(function(resolve, reject) {
with_iframe(
origin +
'/serviceworker/resources/fetch-access-control-login.html')
.then(test.step_func(function(frame) {
var channel = new MessageChannel();
channel.port1.onmessage = test.step_func(function() {
frame.remove();
resolve();
});
frame.contentWindow.postMessage(
{username: username, password: password, cookie: cookie},
origin, [channel.port2]);
}));
});
}
function login(test) {
return test_login(test, 'http://127.0.0.1:8000',
'username1', 'password1', 'cookie1')
.then(function() {
return test_login(test, 'http://localhost:8000',
'username2', 'password2', 'cookie2');
});
}
function login_https(test) {
return test_login(test, 'https://127.0.0.1:8443',
'username1s', 'password1s', 'cookie1')
.then(function() {
return test_login(test, 'https://localhost:8443',
'username2s', 'password2s', 'cookie2');
});
}

View file

@ -42,6 +42,9 @@ class Manifest(object):
for path in self.local_changes.iterdeleted():
if path in paths:
del paths[path]
if item_type == "reftest":
for path, items in self.local_changes.iterdeletedreftests():
paths[path] -= items
yield item_type, paths
@ -170,14 +173,37 @@ class Manifest(object):
self.url_base = url_base
def update_reftests(self):
reftest_nodes = self.reftest_nodes.copy()
for path, items in self.local_changes.reftest_nodes.iteritems():
reftest_nodes[path] |= items
default_reftests = self.compute_reftests(self.reftest_nodes)
all_reftest_nodes = self.reftest_nodes.copy()
all_reftest_nodes.update(self.local_changes.reftest_nodes)
#TODO: remove locally deleted files
tests = set()
for items in reftest_nodes.values():
tests |= set(item for item in items if not item.is_reference)
for item in self.local_changes.iterdeleted():
if item in all_reftest_nodes:
del all_reftest_nodes[item]
modified_reftests = self.compute_reftests(all_reftest_nodes)
added_reftests = modified_reftests - default_reftests
# The interesting case here is not when the file is deleted,
# but when a reftest like A == B is changed to the form
# C == A == B, so that A still exists but is now a ref rather than
# a test.
removed_reftests = default_reftests - modified_reftests
dests = [(default_reftests, self._data["reftest"]),
(added_reftests, self.local_changes._data["reftest"]),
(removed_reftests, self.local_changes._deleted_reftests)]
#TODO: Warn if there exist unreachable reftest nodes
for source, target in dests:
for item in source:
target[item.path].add(item)
def compute_reftests(self, reftest_nodes):
"""Given a set of reftest_nodes, return a set of all the nodes that are top-level
tests i.e. don't have any incoming reference links."""
reftests = set()
has_inbound = set()
for path, items in reftest_nodes.iteritems():
@ -185,18 +211,13 @@ class Manifest(object):
for ref_url, ref_type in item.references:
has_inbound.add(ref_url)
if self.local_changes.reftest_nodes:
target = self.local_changes
else:
target = self
#TODO: Warn if there exist unreachable reftest nodes
for path, items in reftest_nodes.iteritems():
for item in items:
if item.url in has_inbound:
continue
target._data["reftest"][path].add(item)
reftests.add(item)
return reftests
def to_json(self):
out_items = {
@ -261,6 +282,7 @@ class Manifest(object):
source_files=source_files)
return self
class LocalChanges(object):
def __init__(self, manifest):
self.manifest = manifest
@ -268,6 +290,7 @@ class LocalChanges(object):
self._deleted = set()
self.reftest_nodes = defaultdict(set)
self.reftest_nodes_by_url = {}
self._deleted_reftests = defaultdict(set)
def add(self, item):
if item is None:
@ -305,6 +328,10 @@ class LocalChanges(object):
for item in self._deleted:
yield item
def iterdeletedreftests(self):
for item in self._deleted_reftests.iteritems():
yield item
def __getitem__(self, item_type):
return self._data[item_type]
@ -312,9 +339,13 @@ class LocalChanges(object):
reftest_nodes = {from_os_path(key): [v.to_json() for v in value]
for key, value in self.reftest_nodes.iteritems()}
deleted_reftests = {from_os_path(key): [v.to_json() for v in value]
for key, value in self._deleted_reftests.iteritems()}
rv = {"items": defaultdict(dict),
"reftest_nodes": reftest_nodes,
"deleted": [from_os_path(path) for path in self._deleted]}
"deleted": [from_os_path(path) for path in self._deleted],
"deleted_reftests": deleted_reftests}
for test_type, paths in self._data.iteritems():
for path, tests in paths.iteritems():
@ -355,6 +386,13 @@ class LocalChanges(object):
for item in obj["deleted"]:
self.add_deleted(to_os_path(item))
for path, values in obj.get("deleted_reftests", {}).iteritems():
path = to_os_path(path)
for v in values:
item = RefTest.from_json(self.manifest, tests_root, v,
source_files=source_files)
self._deleted_reftests[path].add(item)
return self
def load(tests_root, manifest):

View file

@ -0,0 +1,55 @@
from .. import manifest, item as manifestitem, sourcefile
def test_local_reftest_add():
m = manifest.Manifest()
s = sourcefile.SourceFile("/", "test", "/")
test = manifestitem.RefTest(s, "/test", [("/ref", "==")])
m.local_changes.add(test)
assert list(m) == [(test.path, {test})]
def test_local_reftest_delete_path():
m = manifest.Manifest()
s = sourcefile.SourceFile("/", "test", "/")
test = manifestitem.RefTest(s, "/test", [("/ref", "==")])
m.add(test)
m.local_changes.add_deleted(test.path)
assert list(m) == []
def test_local_reftest_adjusted():
m = manifest.Manifest()
s = sourcefile.SourceFile("/", "test", "/")
test = manifestitem.RefTest(s, "/test", [("/ref", "==")])
m.add(test)
assert list(m) == [(test.path, {test})]
assert m.compute_reftests({test.path: {test}}) == {test}
test_1 = manifestitem.RefTest(s, "/test-1", [("/test", "==")])
m.local_changes.add(test_1)
assert m.compute_reftests({test.path: {test}, test_1.path: {test_1}}) == {test_1}
m.local_changes._deleted_reftests[test.path] = {test}
assert list(m) == [(test_1.path, {test_1})]
def test_manifest_to_json():
m = manifest.Manifest()
s = sourcefile.SourceFile("/", "test", "/")
test = manifestitem.RefTest(s, "/test", [("/ref", "==")])
m.add(test)
test_1 = manifestitem.RefTest(s, "/test-1", [("/test", "==")])
m.local_changes.add(test_1)
m.local_changes._deleted_reftests[test.path] = {test}
json_str = m.to_json()
loaded = manifest.Manifest.from_json("/", json_str)
assert list(loaded) == list(m)
assert loaded.to_json() == json_str

View file

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Focus-related events should fire in the correct order</title>
<link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
<link rel="help" href="https://w3c.github.io/uievents/#events-focusevent-event-order">
<meta name="flags" content="interact">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<ol>
<li>Click into the first text input.</li>
<li>Click into the second text input.</li>
<li>Click the "Done" button.</li>
</ol>
<input type="text" id="a" value="First">
<br>
<input type="text" id="b" value="Second">
<br>
<button type="button" id="done">Done</button>
<script>
setup({explicit_timeout: true});
var done = false;
var events = [];
var targets = [];
function record(e) {
if (done) {
return;
}
events.push(e.type);
targets.push(e.target.id);
}
function finish() {
done = true;
}
var relevantEvents = [
'focus',
'blur',
'focusin',
'focusout'
];
window.onload = function () {
var a = document.getElementById('a');
var b = document.getElementById('b');
var inputs = [a, b];
b.addEventListener('blur', finish, false);
b.addEventListener('focusout', finish, false);
for (var i = 0; i < inputs.length; i++) {
for (var k = 0; k < relevantEvents.length; k++) {
inputs[i].addEventListener(relevantEvents[k], record, false);
}
}
async_test(function(t) {
document.getElementById('done').addEventListener('click', function () {
finish();
t.step(function () {
assert_array_equals(
events,
['focusin', 'focus', 'focusout', 'focusin', 'blur', 'focus'],
'Focus-related events should fire in this order: focusin, focus, focusout, focusin, blur, focus'
);
assert_array_equals(
targets,
[ 'a', 'a', 'a', 'b', 'a', 'b'],
'Focus-related events should fire at the correct targets'
);
t.done();
});
}, false);
}, 'Focus-related events should fire in the correct order');
};
</script>
</body>
</html>

View file

@ -0,0 +1,84 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Focus-related events (including legacy events) should fire in the correct order</title>
<link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
<link rel="help" href="https://w3c.github.io/uievents/#legacy-focusevent-event-order">
<meta name="flags" content="interact">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<ol>
<li>Click into the first text input.</li>
<li>Click into the second text input.</li>
<li>Click the "Done" button.</li>
</ol>
<input type="text" id="a" value="First">
<br>
<input type="text" id="b" value="Second">
<br>
<button type="button" id="done">Done</button>
<script>
setup({explicit_timeout: true});
var done = false;
var events = [];
var targets = [];
function record(e) {
if (done) {
return;
}
events.push(e.type);
targets.push(e.target.id);
}
function finish() {
done = true;
}
var relevantEvents = [
'focus',
'blur',
'focusin',
'focusout',
'DOMFocusIn',
'DOMFocusOut'
];
window.onload = function () {
var a = document.getElementById('a');
var b = document.getElementById('b');
var inputs = [a, b];
b.addEventListener('blur', finish, false);
b.addEventListener('focusout', finish, false);
b.addEventListener('DOMFocusOut', finish, false);
for (var i = 0; i < inputs.length; i++) {
for (var k = 0; k < relevantEvents.length; k++) {
inputs[i].addEventListener(relevantEvents[k], record, false);
}
}
async_test(function(t) {
document.getElementById('done').addEventListener('click', function () {
finish();
t.step(function () {
assert_array_equals(
events,
['focusin', 'focus', 'DOMFocusIn', 'focusout', 'focusin', 'blur', 'DOMFocusOut', 'focus', 'DOMFocusIn'],
'Focus-related events should fire in this order: focusin, focus, DOMFocusIn, focusout, focusin, blur, DOMFocusOut, focus, DOMFocusIn'
);
assert_array_equals(
targets,
[ 'a', 'a', 'a', 'a', 'b', 'a', 'a', 'b', 'b'],
'Focus-related events should fire at the correct targets'
);
t.done();
});
}, false);
}, 'Focus-related events should fire in the correct order');
};
</script>
</body>
</html>

View file

@ -0,0 +1,84 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>easing tests</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animationeffecttiming-easing">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<script src="../resources/effect-easing-tests.js"></script>
<body>
<div id="log"></div>
<script>
'use strict';
function assert_progress(animation, currentTime, easingFunction) {
animation.currentTime = currentTime;
var portion = currentTime / animation.effect.timing.duration;
assert_approx_equals(animation.effect.getComputedTiming().progress,
easingFunction(portion),
0.01,
'The progress of the animation should be approximately ' +
easingFunction(portion) + ' at ' + currentTime + 'ms');
}
gEffectEasingTests.forEach(function(options) {
test(function(t) {
var target = createDiv(t);
var anim = target.animate([ { opacity: 0 }, { opacity: 1 } ],
{ duration: 1000 * MS_PER_SEC,
fill: 'forwards' });
anim.effect.timing.easing = options.easing;
assert_equals(anim.effect.timing.easing, options.easing);
var easing = options.easingFunction;
assert_progress(anim, 0, easing);
assert_progress(anim, 250 * MS_PER_SEC, easing);
assert_progress(anim, 500 * MS_PER_SEC, easing);
assert_progress(anim, 750 * MS_PER_SEC, easing);
assert_progress(anim, 1000 * MS_PER_SEC, easing);
}, options.desc);
});
test(function(t) {
var div = createDiv(t);
var anim = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
assert_throws({ name: 'TypeError' },
function() {
anim.effect.timing.easing = '';
});
assert_throws({ name: 'TypeError' },
function() {
anim.effect.timing.easing = 'test';
});
}, 'Test invalid easing value');
test(function(t) {
var delay = 1000 * MS_PER_SEC;
var target = createDiv(t);
var anim = target.animate([ { opacity: 0 }, { opacity: 1 } ],
{ duration: 1000 * MS_PER_SEC,
fill: 'both',
delay: delay,
easing: 'steps(2, start)' });
anim.effect.timing.easing = 'steps(2, end)';
assert_equals(anim.effect.getComputedTiming().progress, 0,
'easing replace to steps(2, end) at before phase');
anim.currentTime = delay + 750 * MS_PER_SEC;
assert_equals(anim.effect.getComputedTiming().progress, 0.5,
'change currentTime to active phase');
anim.effect.timing.easing = 'steps(2, start)';
assert_equals(anim.effect.getComputedTiming().progress, 1,
'easing replace to steps(2, start) at active phase');
anim.currentTime = delay + 1500 * MS_PER_SEC;
anim.effect.timing.easing = 'steps(2, end)';
assert_equals(anim.effect.getComputedTiming().progress, 1,
'easing replace to steps(2, end) again at after phase');
}, 'Change the easing while the animation is running');
</script>
</body>

View file

@ -0,0 +1,25 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>fill tests</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animationeffecttiming-fill">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
'use strict';
["none", "forwards", "backwards", "both", ].forEach(function(fill){
test(function(t) {
var div = createDiv(t);
var anim = div.animate({ opacity: [ 0, 1 ] }, 100);
anim.effect.timing.fill = fill;
assert_equals(anim.effect.timing.fill, fill, 'set fill ' + fill);
assert_equals(anim.effect.getComputedTiming().fill, fill, 'getComputedTiming() after set fill ' + fill);
}, 'set fill ' + fill);
});
</script>
</body>

View file

@ -0,0 +1,136 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Tests for discrete animation</title>
<link rel="help" href="http://w3c.github.io/web-animations/#animatable-as-string-section">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
'use strict';
test(function(t) {
var div = createDiv(t);
var anim = div.animate({ fontStyle: [ 'normal', 'italic' ] },
{ duration: 1000, fill: 'forwards' });
assert_equals(getComputedStyle(div).fontStyle, 'normal',
'Animation produces \'from\' value at start of interval');
anim.currentTime = anim.effect.getComputedTiming().duration / 2 - 1;
assert_equals(getComputedStyle(div).fontStyle, 'normal',
'Animation produces \'from\' value just before the middle of'
+ ' the interval');
anim.currentTime++;
assert_equals(getComputedStyle(div).fontStyle, 'italic',
'Animation produces \'to\' value at exact middle of'
+ ' the interval');
anim.finish();
assert_equals(getComputedStyle(div).fontStyle, 'italic',
'Animation produces \'to\' value during forwards fill');
}, 'Test animating discrete values');
test(function(t) {
var div = createDiv(t);
var originalHeight = getComputedStyle(div).height;
var anim = div.animate({ height: [ 'auto', '200px' ] },
{ duration: 1000, fill: 'forwards' });
assert_equals(getComputedStyle(div).height, originalHeight,
'Animation produces \'from\' value at start of interval');
anim.currentTime = anim.effect.getComputedTiming().duration / 2 - 1;
assert_equals(getComputedStyle(div).height, originalHeight,
'Animation produces \'from\' value just before the middle of'
+ ' the interval');
anim.currentTime++;
assert_equals(getComputedStyle(div).height, '200px',
'Animation produces \'to\' value at exact middle of'
+ ' the interval');
anim.finish();
assert_equals(getComputedStyle(div).height, '200px',
'Animation produces \'to\' value during forwards fill');
}, 'Test discrete animation is used when interpolation fails');
test(function(t) {
var div = createDiv(t);
var originalHeight = getComputedStyle(div).height;
var anim = div.animate({ height: [ 'auto',
'200px',
'300px',
'auto',
'400px' ] },
{ duration: 1000, fill: 'forwards' });
// There are five values, so there are four pairs to try to interpolate.
// We test at the middle of each pair.
assert_equals(getComputedStyle(div).height, originalHeight,
'Animation produces \'from\' value at start of interval');
anim.currentTime = 125;
assert_equals(getComputedStyle(div).height, '200px',
'First non-interpolable pair uses discrete interpolation');
anim.currentTime += 250;
assert_equals(getComputedStyle(div).height, '250px',
'Second interpolable pair uses linear interpolation');
anim.currentTime += 250;
assert_equals(getComputedStyle(div).height, originalHeight,
'Third non-interpolable pair uses discrete interpolation');
anim.currentTime += 250;
assert_equals(getComputedStyle(div).height, '400px',
'Fourth non-interpolable pair uses discrete interpolation');
}, 'Test discrete animation is used only for pairs of values that cannot'
+ ' be interpolated');
test(function(t) {
var div = createDiv(t);
var originalHeight = getComputedStyle(div).height;
// Easing: http://cubic-bezier.com/#.68,0,1,.01
// With this curve, we don't reach the 50% point until about 95% of
// the time has expired.
var anim = div.animate({ fontStyle: [ 'italic', 'oblique' ] },
{ duration: 1000, fill: 'forwards',
easing: 'cubic-bezier(0.68,0,1,0.01)' });
assert_equals(getComputedStyle(div).fontStyle, 'italic',
'Animation produces \'from\' value at start of interval');
anim.currentTime = 940;
assert_equals(getComputedStyle(div).fontStyle, 'italic',
'Animation produces \'from\' value at 94% of the iteration'
+ ' time');
anim.currentTime = 960;
assert_equals(getComputedStyle(div).fontStyle, 'oblique',
'Animation produces \'to\' value at 96% of the iteration'
+ ' time');
}, 'Test the 50% switch point for discrete animation is based on the'
+ ' effect easing');
test(function(t) {
var div = createDiv(t);
var originalHeight = getComputedStyle(div).height;
// Easing: http://cubic-bezier.com/#.68,0,1,.01
// With this curve, we don't reach the 50% point until about 95% of
// the time has expired.
var anim = div.animate([ { fontStyle: 'italic',
easing: 'cubic-bezier(0.68,0,1,0.01)' },
{ fontStyle: 'oblique' } ],
{ duration: 1000, fill: 'forwards' });
assert_equals(getComputedStyle(div).fontStyle, 'italic',
'Animation produces \'from\' value at start of interval');
anim.currentTime = 940;
assert_equals(getComputedStyle(div).fontStyle, 'italic',
'Animation produces \'from\' value at 94% of the iteration'
+ ' time');
anim.currentTime = 960;
assert_equals(getComputedStyle(div).fontStyle, 'oblique',
'Animation produces \'to\' value at 96% of the iteration'
+ ' time');
}, 'Test the 50% switch point for discrete animation is based on the'
+ ' keyframe easing');
</script>

View file

@ -0,0 +1,120 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Tests for not animatable properties</title>
<link rel="help" href="https://w3c.github.io/web-animations/#not-animatable-section">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
<body>
<div id="log"></div>
<script>
'use strict';
test(function(t) {
var div = createDiv(t);
var anim = div.animate({ display: [ 'inline', 'inline-block' ] }, 1000);
assert_equals(anim.effect.getFrames().length, 0,
'Animation specified using property-indexed notation but'
+ ' consisting of only non-animatable properties should not'
+ ' contain any keyframes');
}, '\'display\' property cannot be animated using property-indexed notation');
test(function(t) {
var div = createDiv(t);
var anim = div.animate([ { display: 'inline' }, { display: 'inline-block' } ],
1000);
assert_equals(anim.effect.getFrames().length, 2,
'Animation specified using a keyframe sequence where each'
+ ' keyframe contains only non-animatable properties should'
+ ' return an equal number of (empty) keyframes');
assert_false(anim.effect.getFrames()[0].hasOwnProperty('display'),
'Initial keyframe should not have the \'display\' property');
assert_false(anim.effect.getFrames()[1].hasOwnProperty('display'),
'Final keyframe should not have the \'display\' property');
}, '\'display\' property cannot be animated using a keyframe sequence');
test(function(t) {
var properties = {
// CSS Animations properties
animation: [ 'anim 1s', 'anim 2s' ],
animationName: [ 'abc', 'xyz' ],
animationTimingFunction: [ 'ease', 'steps(2)' ],
animationDelay: [ '1s', '2s' ],
animationIterationCount: [ 1, 2 ],
animationDirection: [ 'normal', 'reverse' ],
animationFillMode: [ 'forwards', 'backwards' ],
animationPlayState: [ 'paused', 'running' ],
// CSS Transitions properties
transition: [ 'all 1s', 'all 2s' ],
transitionDelay: [ '1s', '2s' ],
transitionDuration: [ '1s', '2s' ],
transitionProperty: [ 'all', 'opacity' ],
transitionTimingFunction: [ 'ease', 'ease-out' ]
};
var div = createDiv(t);
var anim = div.animate(properties, 1000);
assert_equals(anim.effect.getFrames().length, 0,
'Animation specified using property-indexed notation but'
+ ' consisting of only non-animatable properties should not'
+ ' contain any keyframes');
}, 'CSS animations and CSS transitions properties cannot be animated using'
+ ' property-indexed notation');
test(function(t) {
var frames = [
{
animation: 'anim 1s',
animationName: 'abc',
animationTimingFunction: 'ease',
animationDelay: '1s',
animationIterationCount: 1,
animationDirection: 'normal',
animationFillMode: 'forwards',
animationPlayState: 'paused',
transition: 'all 1s',
transitionDelay: '1s',
transitionDuration: '1s',
transitionProperty: 'opacity',
transitionTimingFunction: 'ease'
},
{
animation: 'anim 2s',
animationName: 'xyz',
animationTimingFunction: 'steps(2)',
animationDelay: '2s',
animationIterationCount: 2,
animationDirection: 'reverse',
animationFillMode: 'backwards',
animationPlayState: 'running',
transition: 'all 2s',
transitionDelay: '2s',
transitionDuration: '2s',
transitionProperty: 'all',
transitionTimingFunction: 'ease-out'
}
];
var defaultKeyframeProperties = [ 'computedOffset', 'easing', 'offset' ];
var div = createDiv(t);
var anim = div.animate(frames, 1000);
assert_equals(anim.effect.getFrames().length, 2,
'Animation specified using a keyframe sequence where each'
+ ' keyframe contains only non-animatable properties should'
+ ' return an equal number of (empty) keyframes');
assert_array_equals(Object.keys(anim.effect.getFrames()[0]),
defaultKeyframeProperties,
'Initial keyframe should not contain any properties other'
+ ' than the default keyframe properties');
assert_array_equals(Object.keys(anim.effect.getFrames()[1]),
defaultKeyframeProperties,
'Final keyframe should not contain any properties other'
+ ' than the default keyframe properties');
}, 'CSS animations and CSS transitions properties cannot be animated using'
+ ' a sequence of keyframes');
</script>

View file

@ -0,0 +1,86 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Tests that property values respond to changes to their context</title>
<link rel="help" href="https://w3c.github.io/web-animations/#keyframes-section">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
test(function(t) {
var div = createDiv(t);
div.style.fontSize = '10px';
var animation = div.animate([ { marginLeft: '10em' },
{ marginLeft: '20em' } ], 1000);
animation.currentTime = 500;
assert_equals(getComputedStyle(div).marginLeft, '150px',
'Effect value before updating font-size');
div.style.fontSize = '20px';
assert_equals(getComputedStyle(div).marginLeft, '300px',
'Effect value after updating font-size');
}, 'Effect values reflect changes to font-size on element');
test(function(t) {
var parentDiv = createDiv(t);
var div = createDiv(t);
parentDiv.appendChild(div);
parentDiv.style.fontSize = '10px';
var animation = div.animate([ { marginLeft: '10em' },
{ marginLeft: '20em' } ], 1000);
animation.currentTime = 500;
assert_equals(getComputedStyle(div).marginLeft, '150px',
'Effect value before updating font-size on parent element');
parentDiv.style.fontSize = '20px';
assert_equals(getComputedStyle(div).marginLeft, '300px',
'Effect value after updating font-size on parent element');
}, 'Effect values reflect changes to font-size on parent element');
promise_test(function(t) {
var parentDiv = createDiv(t);
var div = createDiv(t);
parentDiv.appendChild(div);
parentDiv.style.fontSize = '10px';
var animation = div.animate([ { marginLeft: '10em' },
{ marginLeft: '20em' } ], 1000);
animation.pause();
animation.currentTime = 500;
parentDiv.style.fontSize = '20px';
return animation.ready.then(function() {
assert_equals(getComputedStyle(div).marginLeft, '300px',
'Effect value after updating font-size on parent element');
});
}, 'Effect values reflect changes to font-size when computed style is not'
+ ' immediately flushed');
promise_test(function(t) {
var divWith10pxFontSize = createDiv(t);
divWith10pxFontSize.style.fontSize = '10px';
var divWith20pxFontSize = createDiv(t);
divWith20pxFontSize.style.fontSize = '20px';
var div = createDiv(t);
div.remove(); // Detach
var animation = div.animate([ { marginLeft: '10em' },
{ marginLeft: '20em' } ], 1000);
animation.pause();
return animation.ready.then(function() {
animation.currentTime = 500;
divWith10pxFontSize.appendChild(div);
assert_equals(getComputedStyle(div).marginLeft, '150px',
'Effect value after attaching to font-size:10px parent');
divWith20pxFontSize.appendChild(div);
assert_equals(getComputedStyle(div).marginLeft, '300px',
'Effect value after attaching to font-size:20px parent');
});
}, 'Effect values reflect changes to font-size from reparenting');
</script>
</body>

View file

@ -0,0 +1,371 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.finished</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-finished">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
return animation.ready.then(function() {
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise is the same object when playing starts');
animation.pause();
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise does not change when pausing');
animation.play();
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise does not change when play() unpauses');
animation.currentTime = 100 * MS_PER_SEC;
return animation.finished;
}).then(function() {
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise is the same object when playing completes');
});
}, 'Test pausing then playing does not change the finished promise');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
animation.finish();
return animation.finished.then(function() {
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise is the same object when playing completes');
animation.play();
assert_not_equals(animation.finished, previousFinishedPromise,
'Finished promise changes when replaying animation');
previousFinishedPromise = animation.finished;
animation.play();
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise is the same after redundant play() call');
});
}, 'Test restarting a finished animation');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise;
animation.finish();
return animation.finished.then(function() {
previousFinishedPromise = animation.finished;
animation.playbackRate = -1;
assert_not_equals(animation.finished, previousFinishedPromise,
'Finished promise should be replaced when reversing a ' +
'finished promise');
animation.currentTime = 0;
return animation.finished;
}).then(function() {
previousFinishedPromise = animation.finished;
animation.play();
assert_not_equals(animation.finished, previousFinishedPromise,
'Finished promise is replaced after play() call on ' +
'finished, reversed animation');
});
}, 'Test restarting a reversed finished animation');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
animation.finish();
return animation.finished.then(function() {
animation.currentTime = 100 * MS_PER_SEC + 1000;
assert_equals(animation.finished, previousFinishedPromise,
'Finished promise is unchanged jumping past end of ' +
'finished animation');
});
}, 'Test redundant finishing of animation');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
// Setup callback to run if finished promise is resolved
var finishPromiseResolved = false;
animation.finished.then(function() {
finishPromiseResolved = true;
});
return animation.ready.then(function() {
// Jump to mid-way in interval and pause
animation.currentTime = 100 * MS_PER_SEC / 2;
animation.pause();
return animation.ready;
}).then(function() {
// Jump to the end
// (But don't use finish() since that should unpause as well)
animation.currentTime = 100 * MS_PER_SEC;
return waitForAnimationFrames(2);
}).then(function() {
assert_false(finishPromiseResolved,
'Finished promise should not resolve when paused');
});
}, 'Finished promise does not resolve when paused');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
// Setup callback to run if finished promise is resolved
var finishPromiseResolved = false;
animation.finished.then(function() {
finishPromiseResolved = true;
});
return animation.ready.then(function() {
// Jump to mid-way in interval and pause
animation.currentTime = 100 * MS_PER_SEC / 2;
animation.pause();
// Jump to the end
animation.currentTime = 100 * MS_PER_SEC;
return waitForAnimationFrames(2);
}).then(function() {
assert_false(finishPromiseResolved,
'Finished promise should not resolve when pause-pending');
});
}, 'Finished promise does not resolve when pause-pending');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.finish();
return animation.finished.then(function(resolvedAnimation) {
assert_equals(resolvedAnimation, animation,
'Object identity of animation passed to Promise callback'
+ ' matches the animation object owning the Promise');
});
}, 'The finished promise is fulfilled with its Animation');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
// Set up listeners on finished promise
var retPromise = animation.finished.then(function() {
assert_unreached('finished promise was fulfilled');
}).catch(function(err) {
assert_equals(err.name, 'AbortError',
'finished promise is rejected with AbortError');
assert_not_equals(animation.finished, previousFinishedPromise,
'Finished promise should change after the original is ' +
'rejected');
});
animation.cancel();
return retPromise;
}, 'finished promise is rejected when an animation is cancelled by calling ' +
'cancel()');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
animation.finish();
return animation.finished.then(function() {
animation.cancel();
assert_not_equals(animation.finished, previousFinishedPromise,
'A new finished promise should be created when'
+ ' cancelling a finished animation');
});
}, 'cancelling an already-finished animation replaces the finished promise');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.cancel();
// The spec says we still create a new finished promise and reject the old
// one even if we're already idle. That behavior might change, but for now
// test that we do that.
var retPromise = animation.finished.catch(function(err) {
assert_equals(err.name, 'AbortError',
'finished promise is rejected with AbortError');
});
// Redundant call to cancel();
var previousFinishedPromise = animation.finished;
animation.cancel();
assert_not_equals(animation.finished, previousFinishedPromise,
'A redundant call to cancel() should still generate a new'
+ ' finished promise');
return retPromise;
}, 'cancelling an idle animation still replaces the finished promise');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
const HALF_DUR = 100 * MS_PER_SEC / 2;
const QUARTER_DUR = 100 * MS_PER_SEC / 4;
var gotNextFrame = false;
var currentTimeBeforeShortening;
animation.currentTime = HALF_DUR;
return animation.ready.then(function() {
currentTimeBeforeShortening = animation.currentTime;
animation.effect.timing.duration = QUARTER_DUR;
// Below we use gotNextFrame to check that shortening of the animation
// duration causes the finished promise to resolve, rather than it just
// getting resolved on the next animation frame. This relies on the fact
// that the promises are resolved as a micro-task before the next frame
// happens.
waitForAnimationFrames(1).then(function() {
gotNextFrame = true;
});
return animation.finished;
}).then(function() {
assert_false(gotNextFrame, 'shortening of the animation duration should ' +
'resolve the finished promise');
assert_equals(animation.currentTime, currentTimeBeforeShortening,
'currentTime should be unchanged when duration shortened');
var previousFinishedPromise = animation.finished;
animation.effect.timing.duration = 100 * MS_PER_SEC;
assert_not_equals(animation.finished, previousFinishedPromise,
'Finished promise should change after lengthening the ' +
'duration causes the animation to become active');
});
}, 'Test finished promise changes for animation duration changes');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var retPromise = animation.ready.then(function() {
animation.playbackRate = 0;
animation.currentTime = 100 * MS_PER_SEC + 1000;
return waitForAnimationFrames(2);
});
animation.finished.then(t.step_func(function() {
assert_unreached('finished promise should not resolve when playbackRate ' +
'is zero');
}));
return retPromise;
}, 'Test finished promise changes when playbackRate == 0');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
return animation.ready.then(function() {
animation.playbackRate = -1;
return animation.finished;
});
}, 'Test finished promise resolves when reaching to the natural boundary.');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
animation.finish();
return animation.finished.then(function() {
animation.currentTime = 0;
assert_not_equals(animation.finished, previousFinishedPromise,
'Finished promise should change once a prior ' +
'finished promise resolved and the animation ' +
'falls out finished state');
});
}, 'Test finished promise changes when a prior finished promise resolved ' +
'and the animation falls out finished state');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
animation.currentTime = 100 * MS_PER_SEC;
animation.currentTime = 100 * MS_PER_SEC / 2;
assert_equals(animation.finished, previousFinishedPromise,
'No new finished promise generated when finished state ' +
'is checked asynchronously');
}, 'Test no new finished promise generated when finished state ' +
'is checked asynchronously');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var previousFinishedPromise = animation.finished;
animation.finish();
animation.currentTime = 100 * MS_PER_SEC / 2;
assert_not_equals(animation.finished, previousFinishedPromise,
'New finished promise generated when finished state ' +
'is checked synchronously');
}, 'Test new finished promise generated when finished state ' +
'is checked synchronously');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var resolvedFinished = false;
animation.finished.then(function() {
resolvedFinished = true;
});
return animation.ready.then(function() {
animation.finish();
animation.currentTime = 100 * MS_PER_SEC / 2;
}).then(function() {
assert_true(resolvedFinished,
'Animation.finished should be resolved even if ' +
'the finished state is changed soon');
});
}, 'Test synchronous finished promise resolved even if finished state ' +
'is changed soon');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var resolvedFinished = false;
animation.finished.then(function() {
resolvedFinished = true;
});
return animation.ready.then(function() {
animation.currentTime = 100 * MS_PER_SEC;
animation.finish();
}).then(function() {
assert_true(resolvedFinished,
'Animation.finished should be resolved soon after finish() is ' +
'called even if there are other asynchronous promises just before it');
});
}, 'Test synchronous finished promise resolved even if asynchronous ' +
'finished promise happens just before synchronous promise');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.finished.then(t.step_func(function() {
assert_unreached('Animation.finished should not be resolved');
}));
return animation.ready.then(function() {
animation.currentTime = 100 * MS_PER_SEC;
animation.currentTime = 100 * MS_PER_SEC / 2;
});
}, 'Test finished promise is not resolved when the animation ' +
'falls out finished state immediately');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
return animation.ready.then(function() {
animation.currentTime = 100 * MS_PER_SEC;
animation.finished.then(t.step_func(function() {
assert_unreached('Animation.finished should not be resolved');
}));
animation.currentTime = 0;
});
}, 'Test finished promise is not resolved once the animation ' +
'falls out finished state even though the current finished ' +
'promise is generated soon after animation state became finished');
</script>
</body>

View file

@ -0,0 +1,24 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.id</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-id">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
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>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.oncancel</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-oncancel">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
async_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var finishedTimelineTime;
animation.finished.then().catch(function() {
finishedTimelineTime = animation.timeline.currentTime;
});
animation.oncancel = t.step_func_done(function(event) {
assert_equals(event.currentTime, null,
'event.currentTime should be null');
assert_equals(event.timelineTime, finishedTimelineTime,
'event.timelineTime should equal to the animation timeline ' +
'when finished promise is rejected');
});
animation.cancel();
}, 'oncancel event is fired when animation.cancel() is called.');
</script>
</body>

View file

@ -0,0 +1,122 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.onfinish</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-onfinish">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
async_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var finishedTimelineTime;
animation.finished.then(function() {
finishedTimelineTime = animation.timeline.currentTime;
});
animation.onfinish = t.step_func_done(function(event) {
assert_equals(event.currentTime, 0,
'event.currentTime should be zero');
assert_equals(event.timelineTime, finishedTimelineTime,
'event.timelineTime should equal to the animation timeline ' +
'when finished promise is resolved');
});
animation.playbackRate = -1;
}, 'onfinish event is fired when the currentTime < 0 and ' +
'the playbackRate < 0');
async_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var finishedTimelineTime;
animation.finished.then(function() {
finishedTimelineTime = animation.timeline.currentTime;
});
animation.onfinish = t.step_func_done(function(event) {
assert_equals(event.currentTime, 100 * MS_PER_SEC,
'event.currentTime should be the effect end');
assert_equals(event.timelineTime, finishedTimelineTime,
'event.timelineTime should equal to the animation timeline ' +
'when finished promise is resolved');
});
animation.currentTime = 100 * MS_PER_SEC;
}, 'onfinish event is fired when the currentTime > 0 and ' +
'the playbackRate > 0');
async_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var finishedTimelineTime;
animation.finished.then(function() {
finishedTimelineTime = animation.timeline.currentTime;
});
animation.onfinish = t.step_func_done(function(event) {
assert_equals(event.currentTime, 100 * MS_PER_SEC,
'event.currentTime should be the effect end');
assert_equals(event.timelineTime, finishedTimelineTime,
'event.timelineTime should equal to the animation timeline ' +
'when finished promise is resolved');
});
animation.finish();
}, 'onfinish event is fired when animation.finish() is called');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.onfinish = function(event) {
assert_unreached('onfinish event should not be fired');
};
animation.currentTime = 100 * MS_PER_SEC / 2;
animation.pause();
return animation.ready.then(function() {
animation.currentTime = 100 * MS_PER_SEC;
return waitForAnimationFrames(2);
});
}, 'onfinish event is not fired when paused');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.onfinish = function(event) {
assert_unreached('onfinish event should not be fired');
};
return animation.ready.then(function() {
animation.playbackRate = 0;
animation.currentTime = 100 * MS_PER_SEC;
return waitForAnimationFrames(2);
});
}, 'onfinish event is not fired when the playbackRate is zero');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.onfinish = function(event) {
assert_unreached('onfinish event should not be fired');
};
return animation.ready.then(function() {
animation.currentTime = 100 * MS_PER_SEC;
animation.currentTime = 100 * MS_PER_SEC / 2;
return waitForAnimationFrames(2);
});
}, 'onfinish event is not fired when the animation falls out ' +
'finished state immediately');
</script>
</body>

View file

@ -0,0 +1,99 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.pause()</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-pause">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 1000 * MS_PER_SEC);
var previousCurrentTime = animation.currentTime;
return animation.ready.then(waitForAnimationFrames(1)).then(function() {
assert_true(animation.currentTime >= previousCurrentTime,
'currentTime is initially increasing');
animation.pause();
return animation.ready;
}).then(function() {
previousCurrentTime = animation.currentTime;
return waitForAnimationFrames(1);
}).then(function() {
assert_equals(animation.currentTime, previousCurrentTime,
'currentTime does not increase after calling pause()');
});
}, 'pause() a running animation');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 1000 * MS_PER_SEC);
// Go to idle state then pause
animation.cancel();
animation.pause();
assert_equals(animation.currentTime, 0, 'currentTime is set to 0');
assert_equals(animation.startTime, null, 'startTime is not set');
assert_equals(animation.playState, 'pending', 'initially pause-pending');
// Check it still resolves as expected
return animation.ready.then(function() {
assert_equals(animation.playState, 'paused',
'resolves to paused state asynchronously');
assert_equals(animation.currentTime, 0,
'keeps the initially set currentTime');
});
}, 'pause() from idle');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 1000 * MS_PER_SEC);
animation.cancel();
animation.playbackRate = -1;
animation.pause();
assert_equals(animation.currentTime, 1000 * MS_PER_SEC,
'currentTime is set to the effect end');
return animation.ready.then(function() {
assert_equals(animation.currentTime, 1000 * MS_PER_SEC,
'keeps the initially set currentTime');
});
}, 'pause() from idle with a negative playbackRate');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, {duration: 1000 * MS_PER_SEC,
iterations: Infinity});
animation.cancel();
animation.playbackRate = -1;
assert_throws('InvalidStateError',
function () { animation.pause(); },
'Expect InvalidStateError exception on calling pause() ' +
'from idle with a negative playbackRate and ' +
'infinite-duration animation');
}, 'pause() from idle with a negative playbackRate and endless effect');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 1000 * MS_PER_SEC);
return animation.ready
.then(function(animation) {
animation.finish();
animation.pause();
return animation.ready;
}).then(function(animation) {
assert_equals(animation.currentTime, 1000 * MS_PER_SEC,
'currentTime after pausing finished animation');
});
}, 'pause() on a finished animation');
</script>
</body>

View file

@ -0,0 +1,97 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.ready</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-ready">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var originalReadyPromise = animation.ready;
var pauseReadyPromise;
return animation.ready.then(function() {
assert_equals(animation.ready, originalReadyPromise,
'Ready promise is the same object when playing completes');
animation.pause();
assert_not_equals(animation.ready, originalReadyPromise,
'A new ready promise is created when pausing');
pauseReadyPromise = animation.ready;
// Wait for the promise to fulfill since if we abort the pause the ready
// promise object is reused.
return animation.ready;
}).then(function() {
animation.play();
assert_not_equals(animation.ready, pauseReadyPromise,
'A new ready promise is created when playing');
});
}, 'A new ready promise is created when play()/pause() is called');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
return animation.ready.then(function() {
var promiseBeforeCallingPlay = animation.ready;
animation.play();
assert_equals(animation.ready, promiseBeforeCallingPlay,
'Ready promise has same object identity after redundant call'
+ ' to play()');
});
}, 'Redundant calls to play() do not generate new ready promise objects');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
return animation.ready.then(function(resolvedAnimation) {
assert_equals(resolvedAnimation, animation,
'Object identity of Animation passed to Promise callback'
+ ' matches the Animation object owning the Promise');
});
}, 'The ready promise is fulfilled with its Animation');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
var retPromise = animation.ready.then(function() {
assert_unreached('ready promise was fulfilled');
}).catch(function(err) {
assert_equals(err.name, 'AbortError',
'ready promise is rejected with AbortError');
});
animation.cancel();
return retPromise;
}, 'ready promise is rejected when a pause-pending animation is cancelled by'
+ ' calling cancel()');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
return animation.ready.then(function() {
animation.pause();
// Set up listeners on pause-pending ready promise
var retPromise = animation.ready.then(function() {
assert_unreached('ready promise was fulfilled');
}).catch(function(err) {
assert_equals(err.name, 'AbortError',
'ready promise is rejected with AbortError');
});
animation.cancel();
return retPromise;
});
}, 'ready promise is rejected when a pause-pending animation is cancelled by'
+ ' calling cancel()');
</script>
</body>

View file

@ -0,0 +1,150 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Animation.reverse()</title>
<link rel="help" href="https://w3c.github.io/web-animations/#dom-animation-reverse">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<link rel="stylesheet" href="/resources/testharness.css">
<body>
<div id="log"></div>
<script>
"use strict";
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, {duration: 100 * MS_PER_SEC,
iterations: Infinity});
// Wait a frame because if currentTime is still 0 when we call
// reverse(), it will throw (per spec).
return animation.ready.then(waitForAnimationFrames(1)).then(function() {
assert_greater_than_equal(animation.currentTime, 0,
'currentTime expected to be greater than 0, one frame after starting');
animation.currentTime = 50 * MS_PER_SEC;
var previousPlaybackRate = animation.playbackRate;
animation.reverse();
assert_equals(animation.playbackRate, -previousPlaybackRate,
'playbackRate should be inverted');
});
}, 'reverse() inverts playbackRate');
promise_test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, {duration: 100 * MS_PER_SEC,
iterations: Infinity});
animation.currentTime = 50 * MS_PER_SEC;
animation.pause();
return animation.ready.then(function() {
animation.reverse();
return animation.ready;
}).then(function() {
assert_equals(animation.playState, 'running',
'Animation.playState should be "running" after reverse()');
});
}, 'reverse() starts to play when pausing animation');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.currentTime = 50 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.currentTime, 50 * MS_PER_SEC,
'reverse() should not change the currentTime ' +
'if the currentTime is in the middle of animation duration');
}, 'reverse() maintains the same currentTime');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.currentTime = 200 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.currentTime, 100 * MS_PER_SEC,
'reverse() should start playing from the animation effect end ' +
'if the playbackRate > 0 and the currentTime > effect end');
}, 'reverse() when playbackRate > 0 and currentTime > effect end');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.currentTime = -200 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.currentTime, 100 * MS_PER_SEC,
'reverse() should start playing from the animation effect end ' +
'if the playbackRate > 0 and the currentTime < 0');
}, 'reverse() when playbackRate > 0 and currentTime < 0');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.playbackRate = -1;
animation.currentTime = -200 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.currentTime, 0,
'reverse() should start playing from the start of animation time ' +
'if the playbackRate < 0 and the currentTime < 0');
}, 'reverse() when playbackRate < 0 and currentTime < 0');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.playbackRate = -1;
animation.currentTime = 200 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.currentTime, 0,
'reverse() should start playing from the start of animation time ' +
'if the playbackRate < 0 and the currentTime > effect end');
}, 'reverse() when playbackRate < 0 and currentTime > effect end');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, {duration: 100 * MS_PER_SEC,
iterations: Infinity});
animation.currentTime = -200 * MS_PER_SEC;
assert_throws('InvalidStateError',
function () { animation.reverse(); },
'reverse() should throw InvalidStateError ' +
'if the playbackRate > 0 and the currentTime < 0 ' +
'and the target effect is positive infinity');
}, 'reverse() when playbackRate > 0 and currentTime < 0 ' +
'and the target effect end is positive infinity');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, {duration: 100 * MS_PER_SEC,
iterations: Infinity});
animation.playbackRate = -1;
animation.currentTime = -200 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.currentTime, 0,
'reverse() should start playing from the start of animation time ' +
'if the playbackRate < 0 and the currentTime < 0 ' +
'and the target effect is positive infinity');
}, 'reverse() when playbackRate < 0 and currentTime < 0 ' +
'and the target effect end is positive infinity');
test(function(t) {
var div = createDiv(t);
var animation = div.animate({}, 100 * MS_PER_SEC);
animation.playbackRate = 0;
animation.currentTime = 50 * MS_PER_SEC;
animation.reverse();
assert_equals(animation.playbackRate, 0,
'reverse() should preserve playbackRate if the playbackRate == 0');
assert_equals(animation.currentTime, 50 * MS_PER_SEC,
'reverse() should not affect the currentTime if the playbackRate == 0');
t.done();
}, 'reverse() when playbackRate == 0');
</script>
</body>

View file

@ -37,17 +37,6 @@ function assert_frame_lists_equal(a, b) {
var gEmptyKeyframeListTests = [
[],
[{}],
[{ easing: "ease-in" }],
[{ unknown: "unknown" }, { unknown: "unknown" }],
[{ color: "invalid" }, { color: "invalid" }],
{ easing: "ease-in" },
{ unknown: "unknown" },
{ unknown: [] },
{ unknown: ["unknown"] },
{ unknown: ["unknown", "unknown"] },
{ animationName: ["none", "abc"] },
{ color: [] },
null,
undefined,
];
@ -79,7 +68,7 @@ test(function(t) {
"resulting easing for '" + easing + "'");
});
}, "easing values are parsed correctly when passed to the " +
"KeyframeEffectReadOnly constructor in PropertyIndexedKeyframes");
"KeyframeEffectReadOnly constructor in a property-indexed keyframe");
test(function(t) {
gEasingValueTests.forEach(function(subtest) {
@ -93,7 +82,7 @@ test(function(t) {
"resulting easing for '" + easing + "'");
});
}, "easing values are parsed correctly when passed to the " +
"KeyframeEffectReadOnly constructor in Keyframe");
"KeyframeEffectReadOnly constructor in regular keyframes");
test(function(t) {
gEasingValueTests.forEach(function(subtest) {
@ -135,7 +124,7 @@ test(function(t) {
});
});
}, "composite values are parsed correctly when passed to the " +
"KeyframeEffectReadOnly constructor in PropertyIndexedKeyframes");
"KeyframeEffectReadOnly constructor in property-indexed keyframes");
test(function(t) {
var getFrames = function(composite) {
@ -155,7 +144,7 @@ test(function(t) {
});
});
}, "composite values are parsed correctly when passed to the " +
"KeyframeEffectReadOnly constructor in Keyframe");
"KeyframeEffectReadOnly constructor in regular keyframes");
test(function(t) {
gGoodOptionsCompositeValueTests.forEach(function(composite) {
@ -176,79 +165,119 @@ test(function(t) {
"KeyframeEffectReadOnly constructor in KeyframeTimingOptions");
var gPropertyIndexedKeyframesTests = [
{ desc: "a one property two value PropertyIndexedKeyframes specification",
{ desc: "a one property two value property-indexed keyframes specification",
input: { left: ["10px", "20px"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "20px" }] },
{ desc: "a one shorthand property two value PropertyIndexedKeyframes specification",
output: [{ offset: null, computedOffset: 0, easing: "linear",
left: "10px" },
{ offset: null, computedOffset: 1, easing: "linear",
left: "20px" }] },
{ desc: "a one shorthand property two value property-indexed keyframes"
+ " specification",
input: { margin: ["10px", "10px 20px 30px 40px"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear", marginTop: "10px", marginRight: "10px", marginBottom: "10px", marginLeft: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", marginTop: "10px", marginRight: "20px", marginBottom: "30px", marginLeft: "40px" }] },
{ desc: "a two property (one shorthand and one of its longhand components) two value PropertyIndexedKeyframes specification",
output: [{ offset: null, computedOffset: 0, easing: "linear",
margin: "10px" },
{ offset: null, computedOffset: 1, easing: "linear",
margin: "10px 20px 30px 40px" }] },
{ desc: "a two property (one shorthand and one of its longhand components)"
+ " two value property-indexed keyframes specification",
input: { marginTop: ["50px", "60px"],
margin: ["10px", "10px 20px 30px 40px"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear", marginTop: "50px", marginRight: "10px", marginBottom: "10px", marginLeft: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", marginTop: "60px", marginRight: "20px", marginBottom: "30px", marginLeft: "40px" }] },
{ desc: "a two property two value PropertyIndexedKeyframes specification",
output: [{ offset: null, computedOffset: 0, easing: "linear",
marginTop: "50px", margin: "10px" },
{ offset: null, computedOffset: 1, easing: "linear",
marginTop: "60px", margin: "10px 20px 30px 40px" }] },
{ desc: "a two property two value property-indexed keyframes specification",
input: { left: ["10px", "20px"],
top: ["30px", "40px"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px", top: "30px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "20px", top: "40px" }] },
{ desc: "a two property PropertyIndexedKeyframes specification with different numbers of values",
output: [{ offset: null, computedOffset: 0, easing: "linear",
left: "10px", top: "30px" },
{ offset: null, computedOffset: 1, easing: "linear",
left: "20px", top: "40px" }] },
{ desc: "a two property property-indexed keyframes specification with"
+ " different numbers of values",
input: { left: ["10px", "20px", "30px"],
top: ["40px", "50px"] },
output: [{ offset: 0.0, computedOffset: 0.0, easing: "linear", left: "10px", top: "40px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear", left: "20px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear", left: "30px", top: "50px" }] },
{ desc: "a PropertyIndexedKeyframes specification with an invalid value",
output: [{ offset: null, computedOffset: 0.0, easing: "linear",
left: "10px", top: "40px" },
{ offset: null, computedOffset: 0.5, easing: "linear",
left: "20px" },
{ offset: null, computedOffset: 1.0, easing: "linear",
left: "30px", top: "50px" }] },
{ desc: "a property-indexed keyframes specification with an invalid value",
input: { left: ["10px", "20px", "30px", "40px", "50px"],
top: ["15px", "25px", "invalid", "45px", "55px"] },
output: [{ offset: 0.00, computedOffset: 0.00, easing: "linear", left: "10px", top: "15px" },
{ offset: 0.25, computedOffset: 0.25, easing: "linear", left: "20px", top: "25px" },
{ offset: 0.50, computedOffset: 0.50, easing: "linear", left: "30px" },
{ offset: 0.75, computedOffset: 0.75, easing: "linear", left: "40px", top: "45px" },
{ offset: 1.00, computedOffset: 1.00, easing: "linear", left: "50px", top: "55px" }] },
{ desc: "a one property two value PropertyIndexedKeyframes specification that needs to stringify its values",
output: [{ offset: null, computedOffset: 0.00, easing: "linear",
left: "10px", top: "15px" },
{ offset: null, computedOffset: 0.25, easing: "linear",
left: "20px", top: "25px" },
{ offset: null, computedOffset: 0.50, easing: "linear",
left: "30px", top: "invalid" },
{ offset: null, computedOffset: 0.75, easing: "linear",
left: "40px", top: "45px" },
{ offset: null, computedOffset: 1.00, easing: "linear",
left: "50px", top: "55px" }] },
{ desc: "a one property two value property-indexed keyframes specification"
+ " that needs to stringify its values",
input: { opacity: [0, 1] },
output: [{ offset: 0, computedOffset: 0, easing: "linear", opacity: "0" },
{ offset: 1, computedOffset: 1, easing: "linear", opacity: "1" }] },
{ desc: "a one property one value PropertyIndexedKeyframes specification",
output: [{ offset: null, computedOffset: 0, easing: "linear",
opacity: "0" },
{ offset: null, computedOffset: 1, easing: "linear",
opacity: "1" }] },
{ desc: "a one property one value property-indexed keyframes specification",
input: { left: ["10px"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "10px" }] },
{ desc: "a one property one non-array value PropertyIndexedKeyframes specification",
output: [{ offset: null, computedOffset: 1, easing: "linear",
left: "10px" }] },
{ desc: "a one property one non-array value property-indexed keyframes"
+ " specification",
input: { left: "10px" },
output: [{ offset: 0, computedOffset: 0, easing: "linear" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "10px" }] },
{ desc: "a one property two value PropertyIndexedKeyframes specification where the first value is invalid",
output: [{ offset: null, computedOffset: 1, easing: "linear",
left: "10px" }] },
{ desc: "a one property two value property-indexed keyframes specification"
+ " where the first value is invalid",
input: { left: ["invalid", "10px"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "10px" }] },
{ desc: "a one property two value PropertyIndexedKeyframes specification where the second value is invalid",
output: [{ offset: null, computedOffset: 0, easing: "linear",
left: "invalid" },
{ offset: null, computedOffset: 1, easing: "linear",
left: "10px" }] },
{ desc: "a one property two value property-indexed keyframes specification"
+ " where the second value is invalid",
input: { left: ["10px", "invalid"] },
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear" }] },
{ desc: "a two property PropertyIndexedKeyframes specification where one property is missing from the first Keyframe",
output: [{ offset: null, computedOffset: 0, easing: "linear",
left: "10px" },
{ offset: null, computedOffset: 1, easing: "linear",
left: "invalid" }] },
{ desc: "a two property property-indexed keyframes specification where one"
+ " property is missing from the first keyframe",
input: [{ offset: 0, left: "10px" },
{ offset: 1, left: "20px", top: "30px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "20px", top: "30px" }] },
{ desc: "a two property PropertyIndexedKeyframes specification where one property is missing from the last Keyframe",
{ offset: 1, computedOffset: 1, easing: "linear",
left: "20px", top: "30px" }] },
{ desc: "a two property property-indexed keyframes specification where one"
+ " property is missing from the last keyframe",
input: [{ offset: 0, left: "10px", top: "20px" },
{ offset: 1, left: "30px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" , top: "20px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "30px" }] },
{ desc: "a PropertyIndexedKeyframes specification with repeated values at offset 0 with different easings",
output: [{ offset: 0, computedOffset: 0, easing: "linear",
left: "10px" , top: "20px" },
{ offset: 1, computedOffset: 1, easing: "linear",
left: "30px" }] },
{ desc: "a property-indexed keyframes specification with repeated values"
+ " at offset 0 with different easings",
input: [{ offset: 0.0, left: "100px", easing: "ease" },
{ offset: 0.0, left: "200px", easing: "ease" },
{ offset: 0.5, left: "300px", easing: "linear" },
{ offset: 1.0, left: "400px", easing: "ease-out" },
{ offset: 1.0, left: "500px", easing: "step-end" }],
output: [{ offset: 0.0, computedOffset: 0.0, easing: "ease", left: "100px" },
{ offset: 0.0, computedOffset: 0.0, easing: "ease", left: "200px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear", left: "300px" },
{ offset: 1.0, computedOffset: 1.0, easing: "ease-out", left: "400px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear", left: "500px" }] },
output: [{ offset: 0.0, computedOffset: 0.0, easing: "ease",
left: "100px" },
{ offset: 0.0, computedOffset: 0.0, easing: "ease",
left: "200px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear",
left: "300px" },
{ offset: 1.0, computedOffset: 1.0, easing: "ease-out",
left: "400px" },
{ offset: 1.0, computedOffset: 1.0, easing: "step-end",
left: "500px" }] },
];
gPropertyIndexedKeyframesTests.forEach(function(subtest) {
@ -282,41 +311,56 @@ test(function(t) {
});
new KeyframeEffectReadOnly(target, [kf1, kf2]);
assert_array_equals(actualOrder, expectedOrder, "property access order");
}, "the KeyframeEffectReadOnly constructor reads Keyframe properties in the " +
}, "the KeyframeEffectReadOnly constructor reads keyframe properties in the " +
"expected order");
var gKeyframeSequenceTests = [
{ desc: "a one property two Keyframe sequence",
{ desc: "a one property two keyframe sequence",
input: [{ offset: 0, left: "10px" },
{ offset: 1, left: "20px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "20px" }] },
{ desc: "a two property two Keyframe sequence",
{ offset: 1, computedOffset: 1, easing: "linear", left: "20px" }]
},
{ desc: "a two property two keyframe sequence",
input: [{ offset: 0, left: "10px", top: "30px" },
{ offset: 1, left: "20px", top: "40px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px", top: "30px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "20px", top: "40px" }] },
{ desc: "a one shorthand property two Keyframe sequence",
output: [{ offset: 0, computedOffset: 0, easing: "linear",
left: "10px", top: "30px" },
{ offset: 1, computedOffset: 1, easing: "linear",
left: "20px", top: "40px" }] },
{ desc: "a one shorthand property two keyframe sequence",
input: [{ offset: 0, margin: "10px" },
{ offset: 1, margin: "20px 30px 40px 50px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", marginTop: "10px", marginRight: "10px", marginBottom: "10px", marginLeft: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", marginTop: "20px", marginRight: "30px", marginBottom: "40px", marginLeft: "50px" }] },
{ desc: "a two property (a shorthand and one of its component longhands) two Keyframe sequence",
output: [{ offset: 0, computedOffset: 0, easing: "linear",
margin: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear",
margin: "20px 30px 40px 50px" }] },
{ desc: "a two property (a shorthand and one of its component longhands)"
+ " two keyframe sequence",
input: [{ offset: 0, margin: "10px", marginTop: "20px" },
{ offset: 1, marginTop: "70px", margin: "30px 40px 50px 60px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", marginTop: "20px", marginRight: "10px", marginBottom: "10px", marginLeft: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", marginTop: "70px", marginRight: "40px", marginBottom: "50px", marginLeft: "60px" }] },
{ desc: "a Keyframe sequence with duplicate values for a given interior offset",
output: [{ offset: 0, computedOffset: 0, easing: "linear",
margin: "10px", marginTop: "20px" },
{ offset: 1, computedOffset: 1, easing: "linear",
marginTop: "70px", margin: "30px 40px 50px 60px" }] },
{ desc: "a keyframe sequence with duplicate values for a given interior"
+ " offset",
input: [{ offset: 0.0, left: "10px" },
{ offset: 0.5, left: "20px" },
{ offset: 0.5, left: "30px" },
{ offset: 0.5, left: "40px" },
{ offset: 1.0, left: "50px" }],
output: [{ offset: 0.0, computedOffset: 0.0, easing: "linear", left: "10px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear", left: "20px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear", left: "40px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear", left: "50px" }] },
{ desc: "a Keyframe sequence with duplicate values for offsets 0 and 1",
output: [{ offset: 0.0, computedOffset: 0.0, easing: "linear",
left: "10px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear",
left: "20px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear",
left: "30px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear",
left: "40px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear",
left: "50px" }] },
{ desc: "a keyframe sequence with duplicate values for offsets 0 and 1",
input: [{ offset: 0, left: "10px" },
{ offset: 0, left: "20px" },
{ offset: 0, left: "30px" },
@ -324,50 +368,72 @@ var gKeyframeSequenceTests = [
{ offset: 1, left: "50px" },
{ offset: 1, left: "60px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" },
{ offset: 0, computedOffset: 0, easing: "linear", left: "20px" },
{ offset: 0, computedOffset: 0, easing: "linear", left: "30px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "40px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "60px" }] },
{ desc: "a two property four Keyframe sequence",
{ offset: 1, computedOffset: 1, easing: "linear", left: "50px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "60px" }]
},
{ desc: "a two property four keyframe sequence",
input: [{ offset: 0, left: "10px" },
{ offset: 0, top: "20px" },
{ offset: 1, top: "30px" },
{ offset: 1, left: "40px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px", top: "20px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "40px", top: "30px" }] },
{ desc: "a one property Keyframe sequence with some omitted offsets",
output: [{ offset: 0, computedOffset: 0, easing: "linear", left: "10px" },
{ offset: 0, computedOffset: 0, easing: "linear", top: "20px" },
{ offset: 1, computedOffset: 1, easing: "linear", top: "30px" },
{ offset: 1, computedOffset: 1, easing: "linear", left: "40px" }]
},
{ desc: "a one property keyframe sequence with some omitted offsets",
input: [{ offset: 0.00, left: "10px" },
{ offset: 0.25, left: "20px" },
{ left: "30px" },
{ left: "40px" },
{ offset: 1.00, left: "50px" }],
output: [{ offset: 0.00, computedOffset: 0.00, easing: "linear", left: "10px" },
{ offset: 0.25, computedOffset: 0.25, easing: "linear", left: "20px" },
{ offset: 0.50, computedOffset: 0.50, easing: "linear", left: "30px" },
{ offset: 0.75, computedOffset: 0.75, easing: "linear", left: "40px" },
{ offset: 1.00, computedOffset: 1.00, easing: "linear", left: "50px" }] },
{ desc: "a two property Keyframe sequence with some omitted offsets",
output: [{ offset: 0.00, computedOffset: 0.00, easing: "linear",
left: "10px" },
{ offset: 0.25, computedOffset: 0.25, easing: "linear",
left: "20px" },
{ offset: null, computedOffset: 0.50, easing: "linear",
left: "30px" },
{ offset: null, computedOffset: 0.75, easing: "linear",
left: "40px" },
{ offset: 1.00, computedOffset: 1.00, easing: "linear",
left: "50px" }] },
{ desc: "a two property keyframe sequence with some omitted offsets",
input: [{ offset: 0.00, left: "10px", top: "20px" },
{ offset: 0.25, left: "30px" },
{ left: "40px" },
{ left: "50px", top: "60px" },
{ offset: 1.00, left: "70px", top: "80px" }],
output: [{ offset: 0.00, computedOffset: 0.00, easing: "linear", left: "10px", top: "20px" },
{ offset: 0.25, computedOffset: 0.25, easing: "linear", left: "30px" },
{ offset: 0.50, computedOffset: 0.50, easing: "linear", left: "40px" },
{ offset: 0.75, computedOffset: 0.75, easing: "linear", left: "50px", top: "60px" },
{ offset: 1.00, computedOffset: 1.00, easing: "linear", left: "70px", top: "80px" }] },
{ desc: "a one property Keyframe sequence with all omitted offsets",
output: [{ offset: 0.00, computedOffset: 0.00, easing: "linear",
left: "10px", top: "20px" },
{ offset: 0.25, computedOffset: 0.25, easing: "linear",
left: "30px" },
{ offset: null, computedOffset: 0.50, easing: "linear",
left: "40px" },
{ offset: null, computedOffset: 0.75, easing: "linear",
left: "50px", top: "60px" },
{ offset: 1.00, computedOffset: 1.00, easing: "linear",
left: "70px", top: "80px" }] },
{ desc: "a one property keyframe sequence with all omitted offsets",
input: [{ left: "10px" },
{ left: "20px" },
{ left: "30px" },
{ left: "40px" },
{ left: "50px" }],
output: [{ offset: 0.00, computedOffset: 0.00, easing: "linear", left: "10px" },
{ offset: 0.25, computedOffset: 0.25, easing: "linear", left: "20px" },
{ offset: 0.50, computedOffset: 0.50, easing: "linear", left: "30px" },
{ offset: 0.75, computedOffset: 0.75, easing: "linear", left: "40px" },
{ offset: 1.00, computedOffset: 1.00, easing: "linear", left: "50px" }] },
{ desc: "a Keyframe sequence with different easing values, but the same easing value for a given offset",
output: [{ offset: null, computedOffset: 0.00, easing: "linear",
left: "10px" },
{ offset: null, computedOffset: 0.25, easing: "linear",
left: "20px" },
{ offset: null, computedOffset: 0.50, easing: "linear",
left: "30px" },
{ offset: null, computedOffset: 0.75, easing: "linear",
left: "40px" },
{ offset: null, computedOffset: 1.00, easing: "linear",
left: "50px" }] },
{ desc: "a keyframe sequence with different easing values, but the same"
+ " easing value for a given offset",
input: [{ offset: 0.0, easing: "ease", left: "10px"},
{ offset: 0.0, easing: "ease", top: "20px"},
{ offset: 0.5, easing: "linear", left: "30px" },
@ -375,62 +441,79 @@ var gKeyframeSequenceTests = [
{ offset: 1.0, easing: "step-end", left: "50px" },
{ offset: 1.0, easing: "step-end", top: "60px" }],
output: [{ offset: 0.0, computedOffset: 0.0, easing: "ease",
left: "10px", top: "20px" },
left: "10px" },
{ offset: 0.0, computedOffset: 0.0, easing: "ease",
top: "20px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear",
left: "30px", top: "40px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear",
left: "50px", top: "60px" }] },
{ desc: "a Keyframe sequence with different composite values, but the same composite value for a given offset",
left: "30px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear",
top: "40px" },
{ offset: 1.0, computedOffset: 1.0, easing: "step-end",
left: "50px" },
{ offset: 1.0, computedOffset: 1.0, easing: "step-end",
top: "60px" }] },
{ desc: "a keyframe sequence with different composite values, but the"
+ " same composite value for a given offset",
input: [{ offset: 0.0, composite: "replace", left: "10px" },
{ offset: 0.0, composite: "replace", top: "20px" },
{ offset: 0.5, composite: "add", left: "30px" },
{ offset: 0.5, composite: "add", top: "40px" },
{ offset: 1.0, composite: "replace", left: "50px" },
{ offset: 1.0, composite: "replace", top: "60px" }],
output: [{ offset: 0.0, computedOffset: 0.0, easing: "linear", composite: "replace", left: "10px", top: "20px" },
{ offset: 0.5, computedOffset: 0.5, easing: "linear", composite: "add", left: "30px", top: "40px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear", composite: "replace", left: "50px", top: "60px" }] },
{ desc: "a one property two Keyframe sequence that needs to stringify its values",
output: [{ offset: 0.0, computedOffset: 0.0, easing: "linear",
composite: "replace", left: "10px" },
{ offset: 0.0, computedOffset: 0.0, easing: "linear",
composite: "replace", top: "20px" },
{ offset: 0.5, computedOffset: 0.0, easing: "linear",
composite: "add", left: "30px" },
{ offset: 0.5, computedOffset: 0.0, easing: "linear",
composite: "add", top: "40px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear",
composite: "replace", left: "50px" },
{ offset: 1.0, computedOffset: 1.0, easing: "linear",
composite: "replace", top: "60px" }] },
{ desc: "a one property two keyframe sequence that needs to stringify"
+ " its values",
input: [{ offset: 0, opacity: 0 },
{ offset: 1, opacity: 1 }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", opacity: "0" },
{ offset: 1, computedOffset: 1, easing: "linear", opacity: "1" }] },
{ desc: "a Keyframe sequence where shorthand precedes longhand",
{ offset: 1, computedOffset: 1, easing: "linear", opacity: "1" }]
},
{ desc: "a keyframe sequence where shorthand precedes longhand",
input: [{ offset: 0, margin: "10px", marginRight: "20px" },
{ offset: 1, margin: "30px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", marginBottom: "10px", marginLeft: "10px", marginRight: "20px", marginTop: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", marginBottom: "30px", marginLeft: "30px", marginRight: "30px", marginTop: "30px" }] },
{ desc: "a Keyframe sequence where longhand precedes shorthand",
output: [{ offset: 0, computedOffset: 0, easing: "linear",
margin: "10px", marginRight: "20px" },
{ offset: 1, computedOffset: 1, easing: "linear",
margin: "30px" }] },
{ desc: "a keyframe sequence where longhand precedes shorthand",
input: [{ offset: 0, marginRight: "20px", margin: "10px" },
{ offset: 1, margin: "30px" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear", marginBottom: "10px", marginLeft: "10px", marginRight: "20px", marginTop: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear", marginBottom: "30px", marginLeft: "30px", marginRight: "30px", marginTop: "30px" }] },
{ desc: "a Keyframe sequence where lesser shorthand precedes greater shorthand",
input: [{ offset: 0, borderLeft: "1px solid rgb(1, 2, 3)", border: "2px dotted rgb(4, 5, 6)" },
output: [{ offset: 0, computedOffset: 0, easing: "linear",
marginRight: "20px", margin: "10px" },
{ offset: 1, computedOffset: 1, easing: "linear",
margin: "30px" }] },
{ desc: "a keyframe sequence where lesser shorthand precedes greater"
+ " shorthand",
input: [{ offset: 0,
borderLeft: "1px solid rgb(1, 2, 3)",
border: "2px dotted rgb(4, 5, 6)" },
{ offset: 1, border: "3px dashed rgb(7, 8, 9)" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear",
borderBottomColor: "rgb(4, 5, 6)", borderBottomWidth: "2px",
borderLeftColor: "rgb(1, 2, 3)", borderLeftWidth: "1px",
borderRightColor: "rgb(4, 5, 6)", borderRightWidth: "2px",
borderTopColor: "rgb(4, 5, 6)", borderTopWidth: "2px" },
borderLeft: "1px solid rgb(1, 2, 3)",
border: "2px dotted rgb(4, 5, 6)" },
{ offset: 1, computedOffset: 1, easing: "linear",
borderBottomColor: "rgb(7, 8, 9)", borderBottomWidth: "3px",
borderLeftColor: "rgb(7, 8, 9)", borderLeftWidth: "3px",
borderRightColor: "rgb(7, 8, 9)", borderRightWidth: "3px",
borderTopColor: "rgb(7, 8, 9)", borderTopWidth: "3px" }] },
{ desc: "a Keyframe sequence where greater shorthand precedes lesser shorthand",
input: [{ offset: 0, border: "2px dotted rgb(4, 5, 6)", borderLeft: "1px solid rgb(1, 2, 3)" },
border: "3px dashed rgb(7, 8, 9)" }] },
{ desc: "a keyframe sequence where greater shorthand precedes lesser"
+ " shorthand",
input: [{ offset: 0, border: "2px dotted rgb(4, 5, 6)",
borderLeft: "1px solid rgb(1, 2, 3)" },
{ offset: 1, border: "3px dashed rgb(7, 8, 9)" }],
output: [{ offset: 0, computedOffset: 0, easing: "linear",
borderBottomColor: "rgb(4, 5, 6)", borderBottomWidth: "2px",
borderLeftColor: "rgb(1, 2, 3)", borderLeftWidth: "1px",
borderRightColor: "rgb(4, 5, 6)", borderRightWidth: "2px",
borderTopColor: "rgb(4, 5, 6)", borderTopWidth: "2px" },
border: "2px dotted rgb(4, 5, 6)",
borderLeft: "1px solid rgb(1, 2, 3)" },
{ offset: 1, computedOffset: 1, easing: "linear",
borderBottomColor: "rgb(7, 8, 9)", borderBottomWidth: "3px",
borderLeftColor: "rgb(7, 8, 9)", borderLeftWidth: "3px",
borderRightColor: "rgb(7, 8, 9)", borderRightWidth: "3px",
borderTopColor: "rgb(7, 8, 9)", borderTopWidth: "3px" }] },
border: "3px dashed rgb(7, 8, 9)" }] },
];
gKeyframeSequenceTests.forEach(function(subtest) {
@ -467,7 +550,7 @@ gInvalidEasingInKeyframeSequenceTests.forEach(function(subtest) {
assert_throws(new TypeError, function() {
new KeyframeEffectReadOnly(target, subtest.input);
});
}, "Invalid easing [" + subtest.desc + "] in KeyframeSequence " +
}, "Invalid easing [" + subtest.desc + "] in keyframe sequence " +
"should be thrown");
});

View file

@ -6,6 +6,7 @@
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../testcommon.js"></script>
<script src="../resources/effect-easing-tests.js"></script>
<body>
<div id="log"></div>
<div id="target"></div>
@ -22,54 +23,6 @@ function assert_style_left_at(animation, time, easingFunction) {
easingFunction(portion) * 100 + ' at ' + time + 'ms');
}
var gEffectEasingTests = [
{
desc: 'steps(start) function',
easing: 'steps(2, start)',
easingFunction: stepStart(2)
},
{
desc: 'steps(end) function',
easing: 'steps(2, end)',
easingFunction: stepEnd(2)
},
{
desc: 'linear function',
easing: 'linear', // cubic-bezier(0, 0, 1.0, 1.0)
easingFunction: cubicBezier(0, 0, 1.0, 1.0)
},
{
desc: 'ease function',
easing: 'ease', // cubic-bezier(0.25, 0.1, 0.25, 1.0)
easingFunction: cubicBezier(0.25, 0.1, 0.25, 1.0)
},
{
desc: 'ease-in function',
easing: 'ease-in', // cubic-bezier(0.42, 0, 1.0, 1.0)
easingFunction: cubicBezier(0.42, 0, 1.0, 1.0)
},
{
desc: 'ease-in-out function',
easing: 'ease-in-out', // cubic-bezier(0.42, 0, 0.58, 1.0)
easingFunction: cubicBezier(0.42, 0, 0.58, 1.0)
},
{
desc: 'ease-out function',
easing: 'ease-out', // cubic-bezier(0, 0, 0.58, 1.0)
easingFunction: cubicBezier(0, 0, 0.58, 1.0)
},
{
desc: 'easing function which produces values greater than 1',
easing: 'cubic-bezier(0, 1.5, 1, 1.5)',
easingFunction: cubicBezier(0, 1.5, 1, 1.5)
},
{
desc: 'easing function which produces negative values',
easing: 'cubic-bezier(0, -0.5 ,1, -0.5)',
easingFunction: cubicBezier(0, -0.5, 1, -0.5)
},
];
gEffectEasingTests.forEach(function(options) {
test(function(t) {
var target = createDiv(t);
@ -694,7 +647,7 @@ var gStepTimingFunctionTests = [
{ currentTime: 2500, progress: 0.5 },
]
}
]
];
gStepTimingFunctionTests.forEach(function(options) {
test(function(t) {

View file

@ -70,6 +70,46 @@ test(function(t) {
}, 'Overlapping keyframes between 0 and 1 use the appropriate value on each'
+ ' side of the overlap point');
test(function(t) {
var div = createDiv(t);
var anim = div.animate({ visibility: ['hidden','visible'] },
{ duration: 100 * MS_PER_SEC, fill: 'both' });
anim.currentTime = 0;
assert_equals(getComputedStyle(div).visibility, 'hidden',
'Visibility when progress = 0.');
anim.currentTime = 10 * MS_PER_SEC + 1;
assert_equals(getComputedStyle(div).visibility, 'visible',
'Visibility when progress > 0 due to linear easing.');
anim.finish();
assert_equals(getComputedStyle(div).visibility, 'visible',
'Visibility when progress = 1.');
}, "Test visibility clamping behavior.");
test(function(t) {
var div = createDiv(t);
var anim = div.animate({ visibility: ['hidden', 'visible'] },
{ duration: 100 * MS_PER_SEC, fill: 'both',
easing: 'cubic-bezier(0.25, -0.6, 0, 0.5)' });
anim.currentTime = 0;
assert_equals(getComputedStyle(div).visibility, 'hidden',
'Visibility when progress = 0.');
// Timing function is below zero. So we expected visibility is hidden.
anim.currentTime = 10 * MS_PER_SEC + 1;
assert_equals(getComputedStyle(div).visibility, 'hidden',
'Visibility when progress < 0 due to cubic-bezier easing.');
anim.currentTime = 60 * MS_PER_SEC;
assert_equals(getComputedStyle(div).visibility, 'visible',
'Visibility when progress > 0 due to cubic-bezier easing.');
}, "Test visibility clamping behavior with an easing that has a negative component");
done();
</script>
</body>

View file

@ -0,0 +1,42 @@
var gEffectEasingTests = [
{
desc: 'steps(start) function',
easing: 'steps(2, start)',
easingFunction: stepStart(2)
},
{
desc: 'steps(end) function',
easing: 'steps(2, end)',
easingFunction: stepEnd(2)
},
{
desc: 'linear function',
easing: 'linear', // cubic-bezier(0, 0, 1.0, 1.0)
easingFunction: cubicBezier(0, 0, 1.0, 1.0)
},
{
desc: 'ease function',
easing: 'ease', // cubic-bezier(0.25, 0.1, 0.25, 1.0)
easingFunction: cubicBezier(0.25, 0.1, 0.25, 1.0)
},
{
desc: 'ease-in function',
easing: 'ease-in', // cubic-bezier(0.42, 0, 1.0, 1.0)
easingFunction: cubicBezier(0.42, 0, 1.0, 1.0)
},
{
desc: 'ease-in-out function',
easing: 'ease-in-out', // cubic-bezier(0.42, 0, 0.58, 1.0)
easingFunction: cubicBezier(0.42, 0, 0.58, 1.0)
},
{
desc: 'ease-out function',
easing: 'ease-out', // cubic-bezier(0, 0, 0.58, 1.0)
easingFunction: cubicBezier(0, 0, 0.58, 1.0)
},
{
desc: 'easing function which produces values greater than 1',
easing: 'cubic-bezier(0, 1.5, 1, 1.5)',
easingFunction: cubicBezier(0, 1.5, 1, 1.5)
}
];

View file

@ -6,9 +6,9 @@
<div id=log></div>
<script>
async_test(function(t) {
var ws = new WebSocket(SCHEME_DOMAIN_PORT+'/echo-query?x\u0000');
var ws = new WebSocket(SCHEME_DOMAIN_PORT+'/echo-query?x\u0000y\u0000');
ws.onmessage = t.step_func(function(e) {
assert_equals(e.data, 'x%00');
assert_equals(e.data, 'x%00y');
ws.close();
t.done();
})