Update web-platform-tests to revision 51b8e0940e87eda1f843a48d847d653b9a22c8c4

This commit is contained in:
WPT Sync Bot 2020-03-05 08:19:09 +00:00
parent ea8aed1ba9
commit a7b57c1cbf
53 changed files with 1764 additions and 337 deletions

View file

@ -237342,7 +237342,7 @@
[]
],
"support.js": [
"d3b2bb7d1212dd6de0379bf2dce0b23e45f7c7cb",
"8dbfa6f1e37d7631bf5f69b9da9b906876c5d2cf",
[]
]
},
@ -279916,7 +279916,7 @@
[]
],
"testcommon.js": [
"b4fde4b967de2eb67cb3e0819c3a896375e861f9",
"7d63d2c49bce5152d765ac62728f749f622bde2a",
[]
]
}
@ -305255,7 +305255,7 @@
[]
],
"numeric-testcommon.js": [
"56022d06399df48b48d72ef71169578034a5f86c",
"996250d359dc676e001bb1aebfacae98c263e41a",
[]
],
"parsing-testcommon.js": [
@ -315475,10 +315475,6 @@
"6604450991a122e3e241e40b1b9e0516c525389d",
[]
],
"dedicated-worker.js": [
"5d46edcde24c1854c19069d967038c493d7e24f0",
[]
],
"iframe.html": [
"a6b74ad924aa108e15603544f7b0a80a3e18940b",
[]
@ -315538,6 +315534,10 @@
"sw.js": [
"57f0b41ba5b5ff6318e1e4913dfd13bdb1f237a4",
[]
],
"universal-worker.js": [
"5d46edcde24c1854c19069d967038c493d7e24f0",
[]
]
},
"sandbox.https.html.headers": [
@ -324888,7 +324888,7 @@
[]
],
"construct-stylesheets.idl": [
"fca2a3e2887a192f737b4ce05edc52810b2cd7a9",
"35121866f66eaa8b6663480d1a4cb72c1cf8fd1e",
[]
],
"cookie-store.idl": [
@ -328375,7 +328375,7 @@
},
"script-tests": {
"FileSystemBaseHandle-IndexedDB.js": [
"d7403ff5ea6d649d2b36870a5add0730f528a1ea",
"855e52f04ddf2f4f8641524010216c6e8c7cdda7",
[]
],
"FileSystemBaseHandle-postMessage-BroadcastChannel.js": [
@ -337717,7 +337717,7 @@
[]
],
"nfc-mock.js": [
"7832a8231efb01f277960afc91adf01cfe3e3ac6",
"14bb8fdada399999efb67576481efa8881af48f3",
[]
],
"sensor.mojom.js": [
@ -347603,7 +347603,7 @@
[]
],
"revlist.py": [
"c4cbc2943b7ce02ef2a58f65d34ab717bfb0c981",
"1893fdefa8346e8f969e41789df6e3ddd8540629",
[]
],
"run.py": [
@ -349915,7 +349915,7 @@
],
"resources": {
"nfc-helpers.js": [
"5bec071dfa7ad65fe4181a897ebe91912ee94476",
"b1753ddd1b3c5fb70b5ee4d99500623e3fdbdbfd",
[]
],
"support-iframe.html": [
@ -365657,6 +365657,181 @@
{}
]
],
"transaction-scheduling-across-connections.any.js": [
"92d098d29c937c84b7bc9dd1219f126db0b7e585",
[
"IndexedDB/transaction-scheduling-across-connections.any.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
],
[
"IndexedDB/transaction-scheduling-across-connections.any.worker.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
]
],
"transaction-scheduling-across-databases.any.js": [
"064444175867ca528d0d90ea0be40d573561ca88",
[
"IndexedDB/transaction-scheduling-across-databases.any.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
],
[
"IndexedDB/transaction-scheduling-across-databases.any.worker.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
]
],
"transaction-scheduling-mixed-scopes.any.js": [
"5f04a6a288d23c29a7663ccfcdb32c932d573831",
[
"IndexedDB/transaction-scheduling-mixed-scopes.any.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
],
[
"IndexedDB/transaction-scheduling-mixed-scopes.any.worker.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
]
],
"transaction-scheduling-ordering.any.js": [
"9f47e5c58ca3961d83263c7d90da597b83e06ceb",
[
"IndexedDB/transaction-scheduling-ordering.any.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
],
[
"IndexedDB/transaction-scheduling-ordering.any.worker.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
]
],
"transaction-scheduling-ro-waits-for-rw.any.js": [
"dca08b820888f13c00cdb5a8a61badac884fac21",
[
"IndexedDB/transaction-scheduling-ro-waits-for-rw.any.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
],
[
"IndexedDB/transaction-scheduling-ro-waits-for-rw.any.worker.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
]
],
"transaction-scheduling-rw-scopes.any.js": [
"7c6f61614b0689d3090222872379c1269559ec4c",
[
"IndexedDB/transaction-scheduling-rw-scopes.any.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
],
[
"IndexedDB/transaction-scheduling-rw-scopes.any.worker.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
]
],
"transaction-scheduling-within-database.any.js": [
"10dd8b6d7ac5ed9dd4399a04dada2f5e88b827d8",
[
"IndexedDB/transaction-scheduling-within-database.any.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
],
[
"IndexedDB/transaction-scheduling-within-database.any.worker.html",
{
"script_metadata": [
[
"script",
"support.js"
]
]
}
]
],
"transaction_bubble-and-capture.htm": [
"bffa9307ccf3c7738639eb243cbf2d3ae984cb5d",
[
@ -379148,7 +379323,7 @@
},
"nonce-hiding": {
"nonces.html": [
"b023d060323d9d9f366ffdb53159a6f25ab24065",
"7ee10a7b29e5a54918a75037ded0e5bd087f601a",
[
null,
{}
@ -383914,6 +384089,13 @@
{}
]
],
"AnimationEffect-updateTiming.tentative.html": [
"de6953c761facd400a37572419e3c97f42b4cfed",
[
null,
{}
]
],
"CSSAnimation-animationName.tentative.html": [
"370d5ef85e27c2e83deb54522a31da9deb8b556c",
[
@ -383936,7 +384118,7 @@
]
],
"CSSAnimation-effect.tentative.html": [
"bbf35d5113a9c3d1e4dba7318f649494c88feb4c",
"95a904187204286a49a17377d3201f4918128566",
[
null,
{
@ -383966,7 +384148,7 @@
]
],
"CSSAnimation-pausing.tentative.html": [
"2b6e6853b4178f90e820a16a74a5d63524123d32",
"156a1afa964de12e4a983c032f9b526b5cdf625d",
[
null,
{}
@ -384015,7 +384197,14 @@
]
],
"KeyframeEffect-getKeyframes.tentative.html": [
"591cc15a7eda8279ec5e9af529f8e6a06b7caa6c",
"f7d767dea8ca15589a53fae7952bb15e383ccd22",
[
null,
{}
]
],
"KeyframeEffect-setKeyframes.tentative.html": [
"7d8f845413a38daae9bcc9d6ce251e44cda582cb",
[
null,
{}
@ -402871,7 +403060,7 @@
]
],
"round-function.html": [
"236b9a9a8a32e691c5b5b0ea6df66995ec1e4a10",
"b1e950efe7238fcc08b9423dca88e51383f8f7d3",
[
null,
{}
@ -431087,7 +431276,7 @@
]
],
"dedicated-worker-cache-storage.https.html": [
"2559de839a304dce0c2700dc4f8ee0002de04332",
"dced705206fd02ede82ef4c3ed29bbd8f5f1ea2c",
[
null,
{}
@ -431202,6 +431391,13 @@
{}
]
],
"service-worker-cache-storage.https.html": [
"873f06ce4ffbf83bca2ac4dbdc04e5b5bf92abb6",
[
null,
{}
]
],
"srcdoc.https.html": [
"3fbba961b2736ed8c9fb973d61dfac5e54267c40",
[
@ -549877,6 +550073,13 @@
null,
{}
]
],
"suspended-context-messageport.https.html": [
"5f5b10a251f41fd29f694564a717587f8a5a0bad",
[
null,
{}
]
]
},
"the-biquadfilternode-interface": {
@ -550616,7 +550819,7 @@
]
],
"createcredential-badargs-authnrselection.https.html": [
"5da0745734ffbfbcd0d1658e4f4c1914d9940611",
"9497a001f0f4726ac628178983e8ce2625b25e88",
[
null,
{
@ -550636,7 +550839,7 @@
]
],
"createcredential-badargs-rp.https.html": [
"cbd86b8f083e3d1d3b28842b9fa5759047cfac1e",
"8886cc15c92e0ae98f0c65d6d1daa92cb93acfb7",
[
null,
{
@ -550656,7 +550859,7 @@
]
],
"createcredential-excludecredentials.https.html": [
"3a5af481fcac6cd1f9f0e1e95caab7ba7a7facb4",
"2b1eec19b7b3a806150d13c77d8cfd1ea01973a6",
[
null,
{
@ -550666,7 +550869,7 @@
]
],
"createcredential-extensions.https.html": [
"036200dbbf949e7d0b274e44cc88e7aef1366cb8",
"46cab3051b4924a2cb9cf1d7a2df46046b7b2f23",
[
null,
{
@ -550686,7 +550889,7 @@
]
],
"createcredential-pubkeycredparams.https.html": [
"c845a90687576a2d33b7f459c293aa74a15c1fb9",
"d1df7952d6766744280b460a18d2e561a5527e5d",
[
null,
{

View file

@ -0,0 +1,4 @@
[hit-test-floats-002.html]
[Hit test float]
expected: FAIL

View file

@ -0,0 +1,4 @@
[hit-test-floats-004.html]
[Miss float below something else]
expected: FAIL

View file

@ -1,4 +0,0 @@
[hit-test-floats-005.html]
[Miss clipped float]
expected: FAIL

View file

@ -0,0 +1,16 @@
[AnimationEffect-updateTiming.tentative.html]
[AnimationEffect.updateTiming({ delay, fill }) causes changes to the animation-delay and animation-fill-mode to be ignored]
expected: FAIL
[AnimationEffect.updateTiming({ duration }) causes changes to the animation-duration to be ignored]
expected: FAIL
[AnimationEffect.updateTiming({ iterations, direction }) causes changes to the animation-iteration-count and animation-direction to be ignored]
expected: FAIL
[AnimationEffect properties that do not map to animation-* properties should not be changed when animation-* style is updated]
expected: FAIL
[AnimationEffect.updateTiming() does override to changes from animation-* properties if there is an error]
expected: FAIL

View file

@ -17,3 +17,6 @@
[Setting a null effect on a running animation fires an animationend event]
expected: FAIL
[Replacing the effect of a CSSAnimation causes subsequent changes to corresponding animation-* properties to be ignored]
expected: FAIL

View file

@ -17,3 +17,21 @@
[play() is overridden by later setting "animation-play-state: paused"]
expected: FAIL
[reverse() overrides animation-play-state when it starts playing the animation]
expected: FAIL
[Setting the startTime to non-null does NOT override the animation-play-state if the animation is already running]
expected: FAIL
[reverse() does NOT override animation-play-state if the animation is already running]
expected: FAIL
[Setting the startTime to null overrides animation-play-state if the animation is already running]
expected: FAIL
[Setting the startTime to non-null overrides animation-play-state if the animation is paused]
expected: FAIL
[play() does NOT override the animation-play-state if there was an error]
expected: FAIL

View file

@ -71,3 +71,6 @@
[KeyframeEffect.getKeyframes() returns expected values for animations with CSS variables as keyframe values in a shorthand property]
expected: FAIL
[KeyframeEffect.getKeyframes() reflects changes to @keyframes rules]
expected: FAIL

View file

@ -0,0 +1,10 @@
[KeyframeEffect-setKeyframes.tentative.html]
[KeyframeEffect.setKeyframes() causes subsequent changes to animation-timing-function to be ignored]
expected: FAIL
[KeyframeEffect.setKeyframes() causes subsequent changes to @keyframes rules to be ignored]
expected: FAIL
[KeyframeEffect.setKeyframes() should NOT cause subsequent changes to @keyframes rules to be ignored if it threw]
expected: FAIL

View file

@ -1,2 +1,2 @@
[no-transition-from-ua-to-blocking-stylesheet.html]
expected: FAIL
expected: TIMEOUT

View file

@ -335,3 +335,63 @@
[round(to-zero, -18px, 10px) should be used-value-equivalent to -10px]
expected: FAIL
[round(to-zero, 5, infinity) should equal 0⁺.]
expected: FAIL
[round(up, -1, infinity should equal 0⁻.]
expected: FAIL
[round(to-zero, -5, infinity) should equal 0⁻.]
expected: FAIL
[round(up, -1 * 0, infinity should equal 0⁻.]
expected: FAIL
[round(up, 0, infinity) should equal 0⁺.]
expected: FAIL
[round(down, -1, infinity) should equal -Infinity.]
expected: FAIL
[round(down, -1 * 0, infinity) should equal 0⁻.]
expected: FAIL
[round(infinity, 5) should equal +Infinity.]
expected: FAIL
[round(-infinity, 5) should equal -Infinity.]
expected: FAIL
[round(down, 0, infinity) should equal 0⁺.]
expected: FAIL
[round(-infinity, -5) should equal -Infinity.]
expected: FAIL
[round(down, 1, infinity) should equal 0⁺.]
expected: FAIL
[round(5, infinity) should equal 0⁺.]
expected: FAIL
[round(up, 1, infinity) should equal +Infinity.]
expected: FAIL
[round(infinity, -5) should equal +Infinity.]
expected: FAIL
[round(5, -infinity) should equal 0⁺.]
expected: FAIL
[round(to-zero, -5, -infinity) should equal 0⁻.]
expected: FAIL
[round(to-zero, 5, -infinity) should equal 0⁺.]
expected: FAIL
[round(-5, -infinity) should equal 0⁻.]
expected: FAIL
[round(-5, infinity) should equal 0⁻.]
expected: FAIL

View file

@ -0,0 +1,4 @@
[CaretPosition-001.html]
[Element at (400, 100)]
expected: FAIL

View file

@ -2,6 +2,3 @@
[listeners are called when <iframe> is resized]
expected: FAIL
[listeners are called correct number of times]
expected: FAIL

View file

@ -312,18 +312,24 @@
[fetch(): separate response Content-Type: text/plain ]
expected: NOTRUN
[<iframe>: combined response Content-Type: text/html;" text/plain]
expected: FAIL
[<iframe>: separate response Content-Type: text/plain */*;charset=gbk]
expected: FAIL
[<iframe>: combined response Content-Type: text/html */*]
expected: FAIL
[<iframe>: separate response Content-Type: text/html */*;charset=gbk]
expected: FAIL
[<iframe>: combined response Content-Type: text/html;x=" text/plain]
expected: FAIL
[<iframe>: combined response Content-Type: */* text/html]
expected: FAIL
[<iframe>: combined response Content-Type: text/html;charset=gbk text/plain text/html]
expected: FAIL
[<iframe>: separate response Content-Type: text/html;" text/plain]
expected: FAIL
[<iframe>: separate response Content-Type: text/plain */*]
expected: FAIL
[<iframe>: separate response Content-Type: text/html;x=" text/plain]
expected: FAIL

View file

@ -56,6 +56,3 @@
[separate text/javascript x/x]
expected: FAIL
[separate text/javascript;charset=windows-1252 text/javascript]
expected: FAIL

View file

@ -11,9 +11,3 @@
[X-Content-Type-Options%3A%20nosniff%2C%2C%40%23%24%23%25%25%26%5E%26%5E*()()11!]
expected: FAIL
[X-Content-Type-Options%3A%20%40%23%24%23%25%25%26%5E%26%5E*()()11!%2Cnosniff]
expected: FAIL
[Content-Type-Options%3A%20nosniff]
expected: FAIL

View file

@ -0,0 +1,4 @@
[traverse_the_history_1.html]
[Multiple history traversals from the same task]
expected: FAIL

View file

@ -1,4 +0,0 @@
[traverse_the_history_3.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

View file

@ -1,8 +1,4 @@
[skip-document-with-fragment.html]
expected: TIMEOUT
[Autofocus elements in iframed documents with URL fragments should be skipped.]
expected: FAIL
[Autofocus elements in top-level browsing context's documents with URI fragments should be skipped.]
expected: TIMEOUT

View file

@ -32,3 +32,6 @@
[X Rendered audio for channel 5 does not equal [0,0.0626220703125,0.125030517578125,0.18695068359375,0.24810791015625,0.308319091796875,0.3673095703125,0.42486572265625,0.480743408203125,0.53472900390625,0.58660888671875,0.636199951171875,0.68328857421875,0.727691650390625,0.76922607421875,0.8077392578125...\] with an element-wise tolerance of {"absoluteThreshold":0.000030517578125,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[1\]\t3.6732959747314453e-1\t6.2622070312500000e-2\t3.0470752716064453e-1\t4.8658168859649127e+0\t3.0517578125000000e-5\n\t[2\]\t6.8329977989196777e-1\t1.2503051757812500e-1\t5.5826926231384277e-1\t4.4650639949963384e+0\t3.0517578125000000e-5\n\t[3\]\t9.0373212099075317e-1\t1.8695068359375000e-1\t7.1678143739700317e-1\t3.8340669508039502e+0\t3.0517578125000000e-5\n\t[4\]\t9.9780619144439697e-1\t2.4810791015625000e-1\t7.4969828128814697e-1\t3.0216621502152523e+0\t3.0517578125000000e-5\n\t[5\]\t9.5236867666244507e-1\t3.0831909179687500e-1\t6.4404958486557007e-1\t2.0889059484187866e+0\t3.0517578125000000e-5\n\t...and 42281 more errors.\n\tMax AbsError of 1.9986916780471802e+0 at index of 17995.\n\t[17995\]\t9.9994289875030518e-1\t-9.9874877929687500e-1\t1.9986916780471802e+0\t2.0011956154322119e+0\t3.0517578125000000e-5\n\tMax RelError of Infinity at index of 12348.\n\t[12348\]\t9.5105654001235962e-1\t0.0000000000000000e+0\t9.5105654001235962e-1\tInfinity\t3.0517578125000000e-5\n]
expected: FAIL
[X Rendered audio for channel 5 does not equal [0,0.0626220703125,0.125030517578125,0.18695068359375,0.24810791015625,0.308319091796875,0.3673095703125,0.42486572265625,0.480743408203125,0.53472900390625,0.58660888671875,0.636199951171875,0.68328857421875,0.727691650390625,0.76922607421875,0.8077392578125...\] with an element-wise tolerance of {"absoluteThreshold":0.000030517578125,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[1\]\t3.6732959747314453e-1\t6.2622070312500000e-2\t3.0470752716064453e-1\t4.8658168859649127e+0\t3.0517578125000000e-5\n\t[2\]\t6.8329977989196777e-1\t1.2503051757812500e-1\t5.5826926231384277e-1\t4.4650639949963384e+0\t3.0517578125000000e-5\n\t[3\]\t9.0373212099075317e-1\t1.8695068359375000e-1\t7.1678143739700317e-1\t3.8340669508039502e+0\t3.0517578125000000e-5\n\t[4\]\t9.9780619144439697e-1\t2.4810791015625000e-1\t7.4969828128814697e-1\t3.0216621502152523e+0\t3.0517578125000000e-5\n\t[5\]\t9.5236867666244507e-1\t3.0831909179687500e-1\t6.4404958486557007e-1\t2.0889059484187866e+0\t3.0517578125000000e-5\n\t...and 44043 more errors.\n\tMax AbsError of 1.9962286949157715e+0 at index of 32177.\n\t[32177\]\t9.9879217147827148e-1\t-9.9743652343750000e-1\t1.9962286949157715e+0\t2.0013591321441684e+0\t3.0517578125000000e-5\n\tMax RelError of Infinity at index of 14112.\n\t[14112\]\t-9.5105654001235962e-1\t0.0000000000000000e+0\t9.5105654001235962e-1\tInfinity\t3.0517578125000000e-5\n]
expected: FAIL

View file

@ -170,3 +170,9 @@
[X SNR (-254.56109005477157 dB) is not greater than or equal to 65.737. Got -254.56109005477157.]
expected: FAIL
[X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...\] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[19030\]\t-1.5346258564356873e+24\t-7.3546999692916870e-1\t1.5346258564356873e+24\t2.0865920606459265e+24\t3.8985999999999999e-3\n\t[19031\]\t-3.6017334461212158e-1\t-6.9157749414443970e-1\t3.3140414953231812e-1\t4.7920030992665957e-1\t3.8985999999999999e-3\n\t[38059\]\t-3.0465973817666023e+24\t-9.8956179618835449e-1\t3.0465973817666023e+24\t3.0787338329972360e+24\t3.8985999999999999e-3\n\t[38060\]\t-8.8409073650836945e-2\t-9.9664616584777832e-1\t9.0823709219694138e-1\t9.1129341918891205e-1\t3.8985999999999999e-3\n\tMax AbsError of 3.0465973817666023e+24 at index of 38059.\n\tMax RelError of 3.0787338329972360e+24 at index of 38059.\n]
expected: FAIL
[X SNR (-447.224261749694 dB) is not greater than or equal to 65.737. Got -447.224261749694.]
expected: FAIL

View file

@ -0,0 +1,10 @@
[suspended-context-messageport.https.html]
[offline before start]
expected: FAIL
[offline on complete]
expected: FAIL
[realtime suspended]
expected: FAIL

View file

@ -101,6 +101,16 @@ function assert_key_equals(actual, expected, description) {
assert_equals(indexedDB.cmp(actual, expected), 0, description);
}
// Usage:
// indexeddb_test(
// (test_object, db_connection, upgrade_tx, open_request) => {
// // Database creation logic.
// },
// (test_object, db_connection, open_request) => {
// // Test logic.
// test_object.done();
// },
// 'Test case description');
function indexeddb_test(upgrade_func, open_func, description, options) {
async_test(function(t) {
options = Object.assign({upgrade_will_abort: false}, options);
@ -189,3 +199,13 @@ function keep_alive(tx, store_name) {
keepSpinning = false;
};
}
// Returns a new function. After it is called |count| times, |func|
// will be called.
function barrier_func(count, func) {
let n = 0;
return () => {
if (++n === count)
func();
};
}

View file

@ -0,0 +1,72 @@
// META: script=support.js
indexeddb_test(
(t, db) => {
const store = db.createObjectStore('store');
},
(t, db1) => {
// Open a second connection to the same database.
const open_request = indexedDB.open(db1.name);
open_request.onerror = t.unreached_func('open() should succeed');
open_request.onupgradeneeded =
t.unreached_func('second connection should not upgrade');
open_request.onsuccess = t.step_func(() => {
const db2 = open_request.result;
t.add_cleanup(() => { db2.close(); });
const transaction1 = db1.transaction('store', 'readwrite');
transaction1.onabort = t.unreached_func('transaction1 should complete');
const transaction2 = db2.transaction('store', 'readwrite');
transaction2.onabort = t.unreached_func('transaction2 should complete');
let transaction1PutSuccess = false;
let transaction1Complete = false;
let transaction2PutSuccess = false;
// Keep transaction1 alive for a while and ensure transaction2
// doesn't start.
let count = 0;
(function doTransaction1Put() {
const request = transaction1.objectStore('store').put(1, count++);
request.onerror = t.unreached_func('request should succeed');
request.onsuccess = t.step_func(evt => {
transaction1PutSuccess = true;
if (count < 5) {
doTransaction1Put();
}
});
}());
transaction1.oncomplete = t.step_func(evt => {
transaction1Complete = true;
assert_false(
transaction2PutSuccess,
'transaction1 should complete before transaction2 put succeeds');
});
const request = transaction2.objectStore('store').put(2, 0);
request.onerror = t.unreached_func('request should succeed');
request.onsuccess = t.step_func(evt => {
transaction2PutSuccess = true;
assert_true(
transaction1Complete,
'transaction2 put should not succeed before transaction1 completes');
});
transaction2.oncomplete = t.step_func_done(evt => {
assert_true(
transaction1PutSuccess,
'transaction1 put should succeed before transaction2 runs');
assert_true(
transaction1Complete,
'transaction1 should complete before transaction2 runs');
assert_true(
transaction2PutSuccess,
'transaction2 put should succeed before transaction2 completes');
});
});
},
"Check that readwrite transactions with overlapping scopes do not run in parallel.");

View file

@ -0,0 +1,72 @@
// META: script=support.js
indexeddb_test(
(t, db) => {
const store = db.createObjectStore('store');
},
(t, db1) => {
// Open a second database.
const db2name = db1.name + '-2';
const delete_request = indexedDB.deleteDatabase(db2name);
delete_request.onerror = t.unreached_func('deleteDatabase() should succeed');
const open_request = indexedDB.open(db2name, 1);
open_request.onerror = t.unreached_func('open() should succeed');
open_request.onupgradeneeded = t.step_func(() => {
const db2 = open_request.result;
const store = db2.createObjectStore('store');
});
open_request.onsuccess = t.step_func(() => {
const db2 = open_request.result;
t.add_cleanup(() => {
db2.close();
indexedDB.deleteDatabase(db2.name);
});
let transaction1PutSuccess = false;
let transaction2PutSuccess = false;
const onTransactionComplete = barrier_func(2, t.step_func_done(() => {
assert_true(transaction1PutSuccess,
'transaction1 should have executed at least one request');
assert_true(transaction2PutSuccess,
'transaction1 should have executed at least one request');
}));
const transaction1 = db1.transaction('store', 'readwrite');
transaction1.onabort = t.unreached_func('transaction1 should complete');
transaction1.oncomplete = t.step_func(onTransactionComplete);
const transaction2 = db2.transaction('store', 'readwrite');
transaction2.onabort = t.unreached_func('transaction2 should complete');
transaction2.oncomplete = t.step_func(onTransactionComplete);
// Keep both transactions alive until each has reported at least one
// successful operation.
function doTransaction1Put() {
const request = transaction1.objectStore('store').put(0, 0);
request.onerror = t.unreached_func('put request should succeed');
request.onsuccess = t.step_func(() => {
transaction1PutSuccess = true;
if (!transaction2PutSuccess)
doTransaction1Put();
});
}
function doTransaction2Put() {
const request = transaction2.objectStore('store').put(0, 0);
request.onerror = t.unreached_func('put request should succeed');
request.onsuccess = t.step_func(() => {
transaction2PutSuccess = true;
if (!transaction1PutSuccess)
doTransaction2Put();
});
}
doTransaction1Put();
doTransaction2Put();
});
},
"Check that transactions in different databases can run in parallel.");

View file

@ -0,0 +1,63 @@
// META: script=support.js
indexeddb_test(
(t, db) => {
const store = db.createObjectStore('store');
db.createObjectStore('a');
db.createObjectStore('b');
db.createObjectStore('c');
},
(t, db) => {
let transaction1Started = false;
let transaction1Complete = false;
let transaction2Started = false;
let transaction2Complete = false;
let transaction3Started = false;
let transaction3Complete = false;
const transaction1 = db.transaction(['a'], 'readonly');
let request = transaction1.objectStore('a').get(0);
request.onerror = t.unreached_func('request should succeed');
request.onsuccess = t.step_func(() => {
transaction1Started = true;
});
transaction1.onabort = t.unreached_func('transaction1 should complete');
transaction1.oncomplete = t.step_func(() => {
transaction1Complete = true;
assert_false(transaction2Started);
assert_false(transaction3Started);
});
// transaction2 overlaps with transaction1, so must wait until transaction1
// completes.
const transaction2 = db.transaction(['a', 'b'], 'readwrite');
request = transaction2.objectStore('a').get(0);
request.onerror = t.unreached_func('request should succeed');
request.onsuccess = t.step_func(() => {
assert_true(transaction1Complete);
transaction2Started = true;
});
transaction2.onabort = t.unreached_func('transaction2 should complete');
transaction2.oncomplete = t.step_func(() => {
transaction2Complete = true;
assert_false(transaction3Started);
});
// transaction3 overlaps with transaction2, so must wait until transaction2
// completes even though it does not overlap with transaction1.
const transaction3 = db.transaction(['b', 'c'], 'readonly');
request = transaction3.objectStore('b').get(0);
request.onerror = t.unreached_func('request should succeed');
request.onsuccess = t.step_func(() => {
assert_true(transaction1Complete);
assert_true(transaction2Complete);
transaction3Started = true;
});
transaction3.onabort = t.unreached_func('transaction3 should complete');
transaction3.oncomplete = t.step_func_done(() => {
transaction3Complete = true;
});
},
"Check that scope restrictions on mixed transactions are enforced.");

View file

@ -0,0 +1,40 @@
// META: script=support.js
indexeddb_test(
(t, db) => {
const store = db.createObjectStore('store');
},
(t, db) => {
// Create in order tx1, tx2.
const tx1 = db.transaction('store', 'readwrite');
const tx2 = db.transaction('store', 'readwrite');
// Use in order tx2, tx1.
tx2.objectStore('store').get(0);
tx1.objectStore('store').get(0);
const order = [];
const done = barrier_func(2, t.step_func_done(() => {
// IndexedDB Spec:
// https://w3c.github.io/IndexedDB/#transaction-scheduling
//
// If multiple "readwrite" transactions are attempting to
// access the same object store (i.e. if they have overlapping
// scope), the transaction that was created first must be the
// transaction which gets access to the object store first.
//
assert_array_equals(order, [1, 2]);
}));
tx1.oncomplete = t.step_func(e => {
order.push(1);
done();
});
tx2.oncomplete = t.step_func(e => {
order.push(2);
done();
});
},
"Verify Indexed DB transactions are ordered per spec");

View file

@ -0,0 +1,26 @@
// META: script=support.js
indexeddb_test(
(t, db) => {
const store = db.createObjectStore('store');
store.put('value', 'key');
},
(t, db) => {
const transaction1 = db.transaction('store', 'readwrite');
transaction1.onabort = t.unreached_func('transaction1 should not abort');
const transaction2 = db.transaction('store', 'readonly');
transaction2.onabort = t.unreached_func('transaction2 should not abort');
const request = transaction1.objectStore('store').put('new value', 'key');
request.onerror = t.unreached_func('request should not fail');
const request2 = transaction2.objectStore('store').get('key');
request2.onerror = t.unreached_func('request2 should not fail');
request2.onsuccess = t.step_func_done(evt => {
assert_equals(request2.result, 'new value',
'Request should see new value.');
});
},
"readonly transaction should see the result of a previous readwrite transaction");

View file

@ -0,0 +1,63 @@
// META: script=support.js
indexeddb_test(
(t, db) => {
const store = db.createObjectStore('store');
db.createObjectStore('a');
db.createObjectStore('b');
db.createObjectStore('c');
},
(t, db) => {
let transaction1Started = false;
let transaction1Complete = false;
let transaction2Started = false;
let transaction2Complete = false;
let transaction3Started = false;
let transaction3Complete = false;
const transaction1 = db.transaction(['a'], 'readwrite');
let request = transaction1.objectStore('a').get(0);
request.onerror = t.unreached_func('request should succeed');
request.onsuccess = t.step_func(() => {
transaction1Started = true;
});
transaction1.onabort = t.unreached_func('transaction1 should complete');
transaction1.oncomplete = t.step_func(() => {
transaction1Complete = true;
assert_false(transaction2Started);
assert_false(transaction3Started);
});
// transaction2 overlaps with transaction1, so must wait until transaction1
// completes.
const transaction2 = db.transaction(['a', 'b'], 'readwrite');
request = transaction2.objectStore('a').get(0);
request.onerror = t.unreached_func('request should succeed');
request.onsuccess = t.step_func(() => {
assert_true(transaction1Complete);
transaction2Started = true;
});
transaction2.onabort = t.unreached_func('transaction2 should complete');
transaction2.oncomplete = t.step_func(() => {
transaction2Complete = true;
assert_false(transaction3Started);
});
// transaction3 overlaps with transaction2, so must wait until transaction2
// completes even though it does not overlap with transaction1.
const transaction3 = db.transaction(['b', 'c'], 'readwrite');
request = transaction3.objectStore('b').get(0);
request.onerror = t.unreached_func('request should succeed');
request.onsuccess = t.step_func(() => {
assert_true(transaction1Complete);
assert_true(transaction2Complete);
transaction3Started = true;
});
transaction3.onabort = t.unreached_func('transaction3 should complete');
transaction3.oncomplete = t.step_func_done(() => {
transaction3Complete = true;
});
},
"Check that scope restrictions on read-write transactions are enforced.");

View file

@ -0,0 +1,55 @@
// META: script=support.js
indexeddb_test(
(t, db) => {
const store = db.createObjectStore('store');
store.put('value', 'key');
},
(t, db) => {
let transaction1GetSuccess = false;
let transaction2GetSuccess = false;
const onTransactionComplete = barrier_func(2, t.step_func_done(() => {
assert_true(transaction1GetSuccess,
'transaction1 should have executed at least one request');
assert_true(transaction2GetSuccess,
'transaction1 should have executed at least one request');
}));
const transaction1 = db.transaction('store', 'readonly');
transaction1.onabort = t.unreached_func('transaction1 should not abort');
transaction1.oncomplete = t.step_func(onTransactionComplete);
const transaction2 = db.transaction('store', 'readonly');
transaction2.onabort = t.unreached_func('transaction2 should not abort');
transaction2.oncomplete = t.step_func(onTransactionComplete);
// Keep both transactions alive until each has reported at least one
// successful operation
function doTransaction1Get() {
const request = transaction1.objectStore('store').get('key');
request.onerror = t.unreached_func('request should not fail');
request.onsuccess = t.step_func(() => {
transaction1GetSuccess = true;
if (!transaction2GetSuccess)
doTransaction1Get();
});
}
function doTransaction2Get() {
// NOTE: No logging since execution order is not deterministic.
const request = transaction2.objectStore('store').get('key');
request.onerror = t.unreached_func('request should not fail');
request.onsuccess = t.step_func(() => {
transaction2GetSuccess = true;
if (!transaction1GetSuccess)
doTransaction2Get();
});
}
doTransaction1Get();
doTransaction2Get();
},
'Check that read-only transactions within a database can run in parallel.');

View file

@ -3,30 +3,62 @@
<script src="/resources/testharnessreport.js"></script>
<div id=log></div>
<script>
[["meh", ""],
["div", ""],
["script", ""],
["meh", "http://www.w3.org/2000/svg"],
["svg", "http://www.w3.org/2000/svg"],
["script", "http://www.w3.org/2000/svg"]].forEach(([localName, namespace]) => {
test(t => {
const element = namespace === "" ? document.createElement(localName) : document.createElementNS(namespace, localName);
t.add_cleanup(() => element.remove());
assert_equals(element.nonce, "", "Initial IDL attribute value");
element.setAttribute("nonce", "x");
assert_equals(element.nonce, "x", "IDL attribute is modified after content attribute set");
assert_equals(element.getAttribute("nonce"), "x", "Content attribute is modified after content attribute set");
document.body.appendChild(element);
assert_equals(element.nonce, "x", "IDL attribute is unchanged after element insertion");
assert_equals(element.getAttribute("nonce"), "", "Content attribute is changed after element insertion");
}, `Basic nonce tests for ${localName} in ${namespace === "" ? "HTML" : "SVG"} namespace`);
const namespace_url= {
"HTML": "http://www.w3.org/1999/xhtml",
"SVG": "http://www.w3.org/2000/svg",
}
const test_cases = [
["meh" , "HTML"],
["div" , "HTML"],
["script" , "HTML"],
["meh" , "SVG"],
["svg" , "SVG"],
["script" , "SVG"],
];
test(t => {
const element = namespace === "" ? document.createElement(localName) : document.createElementNS(namespace, localName);
element.setAttribute("nonce", "x");
assert_equals(element.nonce, "x", "IDL attribute is modified after content attribute set");
element.removeAttribute("nonce");
assert_equals(element.nonce, "", "IDL attribute is empty after content attribute removal");
}, `Ensure that removal of content attribute does not affect IDL attribute for ${localName} in ${namespace === "" ? "HTML" : "SVG"} namespace`);
});
test_cases.forEach(([localName, namespace]) => {
test(t => {
const element = document.createElementNS(namespace_url[namespace], localName);
t.add_cleanup(() => element.remove());
assert_equals(element.nonce, "", "Initial IDL attribute value");
assert_equals(element.getAttribute("nonce"), null, "Initial content attribute");
element.setAttribute("nonce", "x");
assert_equals(element.nonce, "x", "IDL attribute is modified after content attribute set");
assert_equals(element.getAttribute("nonce"), "x", "Content attribute is modified after content attribute set");
document.body.appendChild(element);
assert_equals(element.nonce, "x", "IDL attribute is unchanged after element insertion");
assert_equals(element.getAttribute("nonce"), "", "Content attribute is changed after element insertion");
}, `Basic nonce tests for ${localName} in ${namespace} namespace`);
test(t => {
const element = document.createElementNS(namespace_url[namespace], localName);
t.add_cleanup(() => element.remove());
element.setAttribute("nonce", "x");
assert_equals(element.nonce, "x", "IDL attribute is modified after content attribute set");
element.removeAttribute("nonce");
assert_equals(element.nonce, "", "IDL attribute is empty after content attribute removal");
}, `Ensure that removal of content attribute does not affect IDL attribute for ${localName} in ${namespace} namespace`);
test(t => {
const element = document.createElementNS(namespace_url[namespace], localName);
t.add_cleanup(() => element.remove());
assert_equals(element.nonce, "");
assert_equals(element.getAttribute("nonce"), null);
element.setAttribute("nonce", "");
assert_equals(element.nonce, "");
assert_equals(element.getAttribute("nonce"), "");
document.body.appendChild(element);
assert_equals(element.nonce, "");
assert_equals(element.getAttribute("nonce"), "");
element.removeAttribute("nonce");
assert_equals(element.nonce, "");
assert_equals(element.getAttribute("nonce"), null);
}, `Test empty nonces for ${localName} in ${namespace} namespace`);
});
</script>

View file

@ -0,0 +1,179 @@
<!doctype html>
<meta charset=utf-8>
<title>AnimationEffect.updateTiming() for CSS animations</title>
<!-- TODO: Add a more specific link for this once it is specified. -->
<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/testcommon.js"></script>
<style>
@keyframes anim-empty { }
</style>
<body>
<div id="log"></div>
<script>
"use strict";
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-empty 100s';
const animation = div.getAnimations()[0];
animation.effect.updateTiming({ duration: 2 * MS_PER_SEC });
div.style.animationDuration = '4s';
div.style.animationDelay = '6s';
getComputedStyle(div).animationDuration;
assert_equals(
animation.effect.getTiming().duration,
2 * MS_PER_SEC,
'Duration should be the value set by the API'
);
assert_equals(
animation.effect.getTiming().delay,
6 * MS_PER_SEC,
'Delay should be the value set by style'
);
}, 'AnimationEffect.updateTiming({ duration }) causes changes to the'
+ ' animation-duration to be ignored');
// The above test covers duration (with delay as a control). The remaining
// properties are:
//
// iterations - animation-iteration-count
// direction - animation-direction
// delay - animation-delay
// fill - animation-fill-mode
//
// Which we test in two batches, overriding two properties each time and using
// the remaining two properties as control values to check they are NOT
// overridden.
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-empty 100s';
const animation = div.getAnimations()[0];
animation.effect.updateTiming({ iterations: 2, direction: 'reverse' });
div.style.animationIterationCount = '4';
div.style.animationDirection = 'alternate';
div.style.animationDelay = '6s';
div.style.animationFillMode = 'both';
getComputedStyle(div).animationIterationCount;
assert_equals(
animation.effect.getTiming().iterations,
2,
'Iterations should be the value set by the API'
);
assert_equals(
animation.effect.getTiming().direction,
'reverse',
'Direction should be the value set by the API'
);
assert_equals(
animation.effect.getTiming().delay,
6 * MS_PER_SEC,
'Delay should be the value set by style'
);
assert_equals(
animation.effect.getTiming().fill,
'both',
'Fill should be the value set by style'
);
}, 'AnimationEffect.updateTiming({ iterations, direction }) causes changes to'
+ ' the animation-iteration-count and animation-direction to be ignored');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-empty 100s';
const animation = div.getAnimations()[0];
animation.effect.updateTiming({ delay: 2 * MS_PER_SEC, fill: 'both' });
div.style.animationDelay = '4s';
div.style.animationFillMode = 'forwards';
div.style.animationIterationCount = '6';
div.style.animationDirection = 'reverse';
getComputedStyle(div).animationDelay;
assert_equals(
animation.effect.getTiming().delay,
2 * MS_PER_SEC,
'Delay should be the value set by the API'
);
assert_equals(
animation.effect.getTiming().fill,
'both',
'Fill should be the value set by the API'
);
assert_equals(
animation.effect.getTiming().iterations,
6,
'Iterations should be the value set by style'
);
assert_equals(
animation.effect.getTiming().direction,
'reverse',
'Direction should be the value set by style'
);
}, 'AnimationEffect.updateTiming({ delay, fill }) causes changes to'
+ ' the animation-delay and animation-fill-mode to be ignored');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-empty 100s';
const animation = div.getAnimations()[0];
assert_throws_js(TypeError, () => {
animation.effect.updateTiming({ duration: 2 * MS_PER_SEC, iterations: -1 });
}, 'Negative iteration count should cause an error to be thrown');
div.style.animationDuration = '4s';
getComputedStyle(div).animationDuration;
assert_equals(
animation.effect.getTiming().duration,
4 * MS_PER_SEC,
'Duration should be the value set by style'
);
}, 'AnimationEffect.updateTiming() does override to changes from animation-*'
+ ' properties if there is an error');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-empty 100s';
const animation = div.getAnimations()[0];
animation.effect.updateTiming({
easing: 'steps(4)',
endDelay: 2 * MS_PER_SEC,
iterationStart: 4,
});
div.style.animationDuration = '6s';
div.style.animationTimingFunction = 'ease-in';
getComputedStyle(div).animationDuration;
assert_equals(
animation.effect.getTiming().easing,
'steps(4)',
'endDelay should be the value set by the API'
);
assert_equals(
animation.effect.getTiming().endDelay,
2 * MS_PER_SEC,
'endDelay should be the value set by the API'
);
assert_equals(
animation.effect.getTiming().iterationStart,
4,
'iterationStart should be the value set by style'
);
}, 'AnimationEffect properties that do not map to animation-* properties'
+ ' should not be changed when animation-* style is updated');
</script>
</body>

View file

@ -128,4 +128,82 @@ promise_test(async t => {
}, 'After replacing a finished animation\'s effect with a longer one ' +
'it fires an animationstart event');
test(t => {
const div = addDiv(t);
// Create custom keyframes so we can tweak them
const stylesheet = document.styleSheets[0];
const keyframes = '@keyframes anim-custom { to { left: 100px } }';
const ruleIndex = stylesheet.insertRule(keyframes, 0);
const keyframesRule = stylesheet.cssRules[ruleIndex];
t.add_cleanup(function() {
stylesheet.deleteRule(ruleIndex);
});
div.style.animation = 'anim-custom 100s';
// Replace the effect
const animation = div.getAnimations()[0];
animation.effect = new KeyframeEffect(
div,
{ left: '200px' },
200 * MS_PER_SEC
);
// Update the timing properties
div.style.animationDuration = '4s';
div.style.animationIterationCount = '6';
div.style.animationDirection = 'reverse';
div.style.animationDelay = '8s';
div.style.animationFillMode = 'both';
div.style.animationPlayState = 'paused';
getComputedStyle(div).animationDuration;
// Update the keyframes
keyframesRule.deleteRule(0);
keyframesRule.appendRule('to { left: 300px }');
// Check nothing (except the play state) changed
assert_equals(
animation.effect.getTiming().duration,
200 * MS_PER_SEC,
'duration should be the value set by the API'
);
assert_equals(
animation.effect.getTiming().iterations,
1,
'iterations should be the value set by the API'
);
assert_equals(
animation.effect.getTiming().direction,
'normal',
'direction should be the value set by the API'
);
assert_equals(
animation.effect.getTiming().delay,
0,
'delay should be the value set by the API'
);
assert_equals(
animation.effect.getTiming().fill,
'auto',
'fill should be the value set by the API'
);
assert_equals(
animation.effect.getKeyframes()[0].left,
'200px',
'keyframes should be the value set by the API'
);
// Unlike the other properties animation-play-state maps to the Animation
// not the KeyframeEffect so it should be overridden.
assert_equals(
animation.playState,
'paused',
'play state should be the value set by style'
);
}, 'Replacing the effect of a CSSAnimation causes subsequent changes to'
+ ' corresponding animation-* properties to be ignored');
</script>

View file

@ -16,129 +16,218 @@
<script>
'use strict';
const getMarginLeft = cs => parseFloat(cs.marginLeft);
promise_test(async t => {
const div = addDiv(t);
const cs = getComputedStyle(div);
div.style.animation = 'anim 1000s paused';
const animation = div.getAnimations()[0];
assert_equals(getMarginLeft(cs), 0,
'Initial value of margin-left is zero');
animation.play();
await animation.ready;
await waitForNextFrame();
assert_greater_than(getMarginLeft(cs), 0,
'Playing value of margin-left is greater than zero');
assert_equals(
animation.playState,
'running',
'Play state is running after calling play()'
);
// Flip the animation-play-state back and forth to check it has no effect
div.style.animationPlayState = 'running';
getComputedStyle(div).animationPlayState;
div.style.animationPlayState = 'paused';
getComputedStyle(div).animationPlayState;
assert_equals(
animation.playState,
'running',
'Should still be running even after flipping the animation-play-state'
);
}, 'play() overrides animation-play-state');
promise_test(async t => {
const div = addDiv(t);
const cs = getComputedStyle(div);
div.style.animation = 'anim 1000s paused';
const animation = div.getAnimations()[0];
assert_equals(getMarginLeft(cs), 0,
'Initial value of margin-left is zero');
div.style.animation = 'anim 100s infinite paused';
animation.pause();
const animation = div.getAnimations()[0];
animation.playbackRate = -1;
animation.currentTime = -1;
assert_throws_dom('InvalidStateError', () => {
animation.play();
}, 'Trying to play a reversed infinite animation should throw');
assert_equals(
animation.playState,
'paused',
'Animation should still be paused'
);
animation.playbackRate = 1;
div.style.animationPlayState = 'running';
assert_equals(
animation.playState,
'running',
'Changing the animation-play-state should play the animation'
);
}, 'play() does NOT override the animation-play-state if there was an error');
promise_test(async t => {
const div = addDiv(t);
div.style.animation = 'anim 1000s paused';
const animation = div.getAnimations()[0];
animation.pause();
div.style.animationPlayState = 'running';
getComputedStyle(div).animationPlayState;
await animation.ready;
await waitForNextFrame();
assert_equals(cs.animationPlayState, 'running',
'animation-play-state is running');
assert_equals(getMarginLeft(cs), 0,
'Paused value of margin-left is zero');
assert_equals(animation.playState, 'paused', 'playState is paused ');
// Flip the animation-play-state back and forth to check it has no effect
div.style.animationPlayState = 'paused';
getComputedStyle(div).animationPlayState;
div.style.animationPlayState = 'running';
getComputedStyle(div).animationPlayState;
assert_equals(
animation.playState,
'paused',
'Should still be paused even after flipping the animation-play-state'
);
}, 'pause() overrides animation-play-state');
promise_test(async t => {
const div = addDiv(t);
const cs = getComputedStyle(div);
div.style.animation = 'anim 1000s paused';
const animation = div.getAnimations()[0];
assert_equals(getMarginLeft(cs), 0,
'Initial value of margin-left is zero');
animation.play();
div.style.animation = 'anim 100s paused';
await animation.ready;
const animation = div.getAnimations()[0];
animation.reverse();
assert_equals(
animation.playState,
'running',
'Play state is running after calling reverse()'
);
// Flip the animation-play-state back and forth to check it has no effect
div.style.animationPlayState = 'running';
cs.animationPlayState; // Trigger style resolution
await waitForNextFrame();
assert_equals(cs.animationPlayState, 'running',
'animation-play-state is running');
getComputedStyle(div).animationPlayState;
div.style.animationPlayState = 'paused';
await animation.ready;
getComputedStyle(div).animationPlayState;
assert_equals(cs.animationPlayState, 'paused',
'animation-play-state is paused');
const previousAnimVal = getMarginLeft(cs);
await waitForNextFrame();
assert_equals(getMarginLeft(cs), previousAnimVal,
'Animated value of margin-left does not change when'
+ ' paused by style');
}, 'play() is overridden by later setting "animation-play-state: paused"');
assert_equals(
animation.playState,
'running',
'Should still be running even after flipping the animation-play-state'
);
}, 'reverse() overrides animation-play-state when it starts playing the'
+ ' animation');
promise_test(async t => {
const div = addDiv(t);
const cs = getComputedStyle(div);
div.style.animation = 'anim 1000s';
div.style.animation = 'anim 100s';
const animation = div.getAnimations()[0];
assert_equals(getMarginLeft(cs), 0,
'Initial value of margin-left is zero');
animation.reverse();
assert_equals(
animation.playState,
'running',
'Play state is running after calling reverse()'
);
// Set the specified style first. If implementations fail to
// apply the style changes first, they will ignore the redundant
// call to play() and fail to correctly override the pause style.
div.style.animationPlayState = 'paused';
animation.play();
const previousAnimVal = getMarginLeft(cs);
getComputedStyle(div).animationPlayState;
await animation.ready;
await waitForNextFrame();
assert_equals(cs.animationPlayState, 'paused',
'animation-play-state is paused');
assert_greater_than(getMarginLeft(cs), previousAnimVal,
'Playing value of margin-left is increasing');
}, 'play() flushes pending changes to animation-play-state first');
assert_equals(
animation.playState,
'paused',
'Should be paused after changing the animation-play-state'
);
}, 'reverse() does NOT override animation-play-state if the animation is'
+ ' already running');
promise_test(async t => {
const div = addDiv(t);
const cs = getComputedStyle(div);
div.style.animation = 'anim 1000s paused';
div.style.animation = 'anim 100s';
const animation = div.getAnimations()[0];
assert_equals(getMarginLeft(cs), 0,
'Initial value of margin-left is zero');
animation.startTime = null;
// Unlike the previous test for play(), since calling pause() is sticky,
// we'll apply it even if the underlying style also says we're paused.
//
// We would like to test that implementations flush styles before running
// pause() but actually there's no style we can currently set that will
// change the behavior of pause(). That may change in the future
// (e.g. if we introduce animation-timeline or animation-playback-rate etc.).
//
// For now this just serves as a sanity check that we do the same thing
// even if we set style before calling the API.
assert_equals(
animation.playState,
'paused',
'Play state is paused after setting the start time to null'
);
// Flip the animation-play-state back and forth to check it has no effect
div.style.animationPlayState = 'paused';
getComputedStyle(div).animationPlayState;
div.style.animationPlayState = 'running';
animation.pause();
const previousAnimVal = getMarginLeft(cs);
getComputedStyle(div).animationPlayState;
await animation.ready;
await waitForNextFrame();
assert_equals(
animation.playState,
'paused',
'Should still be paused even after flipping the animation-play-state'
);
}, 'Setting the startTime to null overrides animation-play-state if the'
+ ' animation is already running');
assert_equals(cs.animationPlayState, 'running',
'animation-play-state is running');
assert_equals(getMarginLeft(cs), previousAnimVal,
'Paused value of margin-left does not change');
}, 'pause() applies pending changes to animation-play-state first');
// (Note that we can't actually test for this; see comment above, in test-body.)
promise_test(async t => {
const div = addDiv(t);
div.style.animation = 'anim 100s paused';
const animation = div.getAnimations()[0];
animation.startTime = document.timeline.currentTime;
assert_equals(
animation.playState,
'running',
'Play state is running after setting the start time to non-null'
);
// Flip the animation-play-state back and forth to check it has no effect
div.style.animationPlayState = 'running';
getComputedStyle(div).animationPlayState;
div.style.animationPlayState = 'paused';
getComputedStyle(div).animationPlayState;
assert_equals(
animation.playState,
'running',
'Should still be running even after flipping the animation-play-state'
);
}, 'Setting the startTime to non-null overrides animation-play-state if the'
+ ' animation is paused');
promise_test(async t => {
const div = addDiv(t);
div.style.animation = 'anim 100s';
const animation = div.getAnimations()[0];
animation.startTime = document.timeline.currentTime;
div.style.animationPlayState = 'paused';
getComputedStyle(div).animationPlayState;
assert_equals(
animation.playState,
'paused',
'Should be paused after changing the animation-play-state'
);
}, 'Setting the startTime to non-null does NOT override the'
+ ' animation-play-state if the animation is already running');
promise_test(async t => {
const div = addDiv(t, { style: 'animation: anim 1000s' });

View file

@ -164,20 +164,6 @@
const getKeyframes = elem => elem.getAnimations()[0].effect.getKeyframes();
const assert_frames_equal = (a, b, name) => {
assert_equals(Object.keys(a).sort().toString(),
Object.keys(b).sort().toString(),
"properties on " + name);
for (const p in a) {
if (p === 'offset' || p === 'computedOffset') {
assert_approx_equals(a[p], b[p], 0.00001,
"value for '" + p + "' on " + name);
} else {
assert_equals(a[p], b[p], "value for '" + p + "' on " + name);
}
}
};
// animation-timing-function values to test with, where the value
// is exactly the same as its serialization, sorted by the order
// getKeyframes() will group frames with the same easing function
@ -221,11 +207,9 @@ test(t => {
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-simple 100s';
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease",
@ -233,10 +217,7 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease",
color: "rgb(255, 255, 255)", composite: "auto" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected frames for a simple'
+ ' animation');
@ -292,11 +273,9 @@ test(t => {
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-simple-shorthand 100s';
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
@ -306,21 +285,16 @@ test(t => {
marginBottom: "16px", marginLeft: "16px",
marginRight: "16px", marginTop: "16px" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected frames for a simple'
+ ' animation that specifies a single shorthand property');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-omit-to 100s';
div.style.color = 'rgb(255, 255, 255)';
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
@ -328,21 +302,16 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
color: "rgb(255, 255, 255)" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
'animation with a 0% keyframe and no 100% keyframe');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-omit-from 100s';
div.style.color = 'rgb(255, 255, 255)';
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
@ -350,21 +319,16 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
color: "rgb(0, 0, 255)" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
'animation with a 100% keyframe and no 0% keyframe');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-omit-from-to 100s';
div.style.color = 'rgb(255, 255, 255)';
const frames = getKeyframes(div);
assert_equals(frames.length, 3, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
@ -374,21 +338,16 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
color: "rgb(255, 255, 255)" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
'animation with no 0% or 100% keyframe but with a 50% keyframe');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-partially-omit-to 100s';
div.style.marginTop = '250px';
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
@ -396,21 +355,16 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
marginTop: '250px', marginBottom: '200px' },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
'animation with a partially complete 100% keyframe (because the ' +
'!important rule is ignored)');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-different-props 100s';
const frames = getKeyframes(div);
assert_equals(frames.length, 4, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
@ -422,21 +376,16 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
color: "rgb(255, 255, 255)", marginTop: "16px" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
'animation with different properties on different keyframes, all ' +
'with the same easing function');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-different-props-and-easing 100s';
const frames = getKeyframes(div);
assert_equals(frames.length, 4, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "linear", composite: "auto",
@ -448,21 +397,16 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
color: "rgb(255, 255, 255)", marginTop: "16px" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
'animation with different properties on different keyframes, with ' +
'a different easing function on each');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-merge-offset 100s';
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
@ -470,21 +414,16 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
color: "rgb(255, 255, 255)", marginTop: "16px" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
'animation with multiple keyframes for the same time, and all with ' +
'the same easing function');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-merge-offset-and-easing 100s';
const frames = getKeyframes(div);
assert_equals(frames.length, 3, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "steps(1)", composite: "auto",
@ -495,21 +434,16 @@ test(t => {
color: "rgb(255, 255, 255)", fontSize: "32px", marginTop: "16px",
paddingLeft: "4px" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
'animation with multiple keyframes for the same time and with ' +
'different easing functions');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-no-merge-equiv-easing 100s';
const frames = getKeyframes(div);
assert_equals(frames.length, 3, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "steps(1)", composite: "auto",
@ -519,21 +453,16 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
marginTop: "20px", marginRight: "20px", marginBottom: "20px" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected frames for an ' +
'animation with multiple keyframes for the same time and with ' +
'different but equivalent easing functions');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-overriding 100s';
const frames = getKeyframes(div);
assert_equals(frames.length, 6, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
@ -549,10 +478,7 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
paddingTop: "70px" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected frames for ' +
'overlapping keyframes');
@ -561,11 +487,9 @@ test(t => {
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-filter 100s';
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
@ -573,20 +497,15 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
filter: "blur(5px) sepia(60%) saturate(30%)" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
'animations with filter properties and missing keyframes');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-filter-drop-shadow 100s';
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
@ -594,10 +513,7 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
filter: "drop-shadow(rgb(255, 0, 0) 50px 30px 10px)" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
'animation with drop-shadow of filter property');
@ -607,14 +523,12 @@ test(t => {
test(t => {
const div = addDiv(t);
div.style.textShadow = '1px 1px 2px rgb(0, 0, 0), ' +
'0 0 16px rgb(0, 0, 255), ' +
'0 0 3.2px rgb(0, 0, 255)';
div.style.animation = 'anim-text-shadow 100s';
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const frames = getKeyframes(div);
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
@ -624,10 +538,7 @@ test(t => {
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
textShadow: "none" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
'animations with text-shadow properties and missing keyframes');
@ -673,17 +584,13 @@ test(t => {
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
transform: "none" },
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
transform: "translate(100px)" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
'animations with CSS variables as keyframe values');
@ -693,8 +600,6 @@ test(t => {
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
marginBottom: "0px",
@ -707,9 +612,7 @@ test(t => {
marginRight: "100px",
marginTop: "100px" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
'animations with CSS variables as keyframe values in a shorthand property');
@ -719,17 +622,13 @@ test(t => {
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
color: "rgb(0, 0, 0)" },
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
color: "rgb(0, 255, 0)" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
'animations with a CSS variable which is overriden by the value in keyframe');
@ -739,19 +638,63 @@ test(t => {
const frames = getKeyframes(div);
assert_equals(frames.length, 2, "number of frames");
const expected = [
{ offset: 0, computedOffset: 0, easing: "ease", composite: "auto",
transform: "translate(100px)" },
{ offset: 1, computedOffset: 1, easing: "ease", composite: "auto",
transform: "none" },
];
for (let i = 0; i < frames.length; i++) {
assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
}
assert_frame_lists_equal(frames, expected);
}, 'KeyframeEffect.getKeyframes() returns expected values for ' +
'animations with only custom property in a keyframe');
test(t => {
const div = addDiv(t);
// Add custom @keyframes rule
const stylesheet = document.styleSheets[0];
const keyframes = '@keyframes anim-custom { to { left: 100px } }';
const ruleIndex = stylesheet.insertRule(keyframes, 0);
const keyframesRule = stylesheet.cssRules[ruleIndex];
t.add_cleanup(function() {
stylesheet.deleteRule(ruleIndex);
});
div.style.animation = 'anim-custom 100s';
// Sanity check the initial result
let frames = getKeyframes(div);
assert_frames_equal(
frames[frames.length - 1],
{
offset: 1,
computedOffset: 1,
easing: 'ease',
composite: 'auto',
left: '100px',
},
'Keyframes reflect the initial @keyframes rule'
);
// Update the @keyframes rule
keyframesRule.deleteRule(0);
keyframesRule.appendRule('to { left: 200px }');
// Check the result from getKeyframes() is updated
frames = getKeyframes(div);
assert_frames_equal(
frames[frames.length - 1],
{
offset: 1,
computedOffset: 1,
easing: 'ease',
composite: 'auto',
left: '200px',
},
'Keyframes reflects the updated @keyframes rule'
);
}, 'KeyframeEffect.getKeyframes() reflects changes to @keyframes rules');
</script>
</body>

View file

@ -0,0 +1,122 @@
<!doctype html>
<meta charset=utf-8>
<title>KeyframeEffect.setKeyframes() for CSS animations</title>
<!-- TODO: Add a more specific link for this once it is specified. -->
<link rel="help" href="https://drafts.csswg.org/css-animations-2/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="support/testcommon.js"></script>
<style>
@keyframes anim-simple {
from { left: 0px }
to { left: 100px }
}
</style>
<body>
<div id="log"></div>
<script>
"use strict";
// Note that the sanity check that getKeyframes() normally DOES return the
// updated keyframes is contained in KeyframeEffect-getKeyframes.html.
test(t => {
const div = addDiv(t);
// Add custom @keyframes rule
const stylesheet = document.styleSheets[0];
const keyframes = '@keyframes anim-custom { to { left: 100px } }';
const ruleIndex = stylesheet.insertRule(keyframes, 0);
const keyframesRule = stylesheet.cssRules[ruleIndex];
t.add_cleanup(function() {
stylesheet.deleteRule(ruleIndex);
});
div.style.animation = 'anim-custom 100s';
// Update the keyframes via the API
const animation = div.getAnimations()[0];
animation.effect.setKeyframes({ left: '200px' });
// Then update them via style
keyframesRule.deleteRule(0);
keyframesRule.appendRule('to { left: 300px }');
// The result should be the keyframes as set by the API, not via style.
const frames = animation.effect.getKeyframes();
assert_frames_equal(
frames[frames.length - 1],
{
offset: null,
computedOffset: 1,
easing: 'linear',
composite: 'auto',
left: '200px',
},
'Keyframes reflect the value set via setKeyframes'
);
}, 'KeyframeEffect.setKeyframes() causes subsequent changes to @keyframes'
+ ' rules to be ignored');
test(t => {
const div = addDiv(t);
div.style.animation = 'anim-simple 100s';
const animation = div.getAnimations()[0];
assert_equals(animation.effect.getKeyframes()[0].easing, 'ease');
animation.effect.setKeyframes({ left: ['200px', '300px'] });
assert_equals(animation.effect.getKeyframes()[0].easing, 'linear');
div.style.animationTimingFunction = 'ease-in';
getComputedStyle(div).animationTimingFunction;
assert_equals(
animation.effect.getKeyframes()[0].easing,
'linear',
'Easing should be the easing set by the API'
);
}, 'KeyframeEffect.setKeyframes() causes subsequent changes to'
+ ' animation-timing-function to be ignored');
test(t => {
const div = addDiv(t);
const stylesheet = document.styleSheets[0];
const keyframes = '@keyframes anim-custom { to { left: 100px } }';
const ruleIndex = stylesheet.insertRule(keyframes, 0);
const keyframesRule = stylesheet.cssRules[ruleIndex];
t.add_cleanup(function() {
stylesheet.deleteRule(ruleIndex);
});
div.style.animation = 'anim-custom 100s';
// Try updating in a way that throws an error
const animation = div.getAnimations()[0];
assert_throws_js(TypeError, () => {
animation.effect.setKeyframes({ left: '200px', offset: 'yer' });
});
keyframesRule.deleteRule(0);
keyframesRule.appendRule('to { left: 300px }');
// The result should be the keyframes as set via style.
const frames = animation.effect.getKeyframes();
assert_frames_equal(
frames[frames.length - 1],
{
offset: 1,
computedOffset: 1,
easing: 'ease',
composite: 'auto',
left: '300px',
},
'Keyframes reflect the value set via style'
);
}, 'KeyframeEffect.setKeyframes() should NOT cause subsequent changes to'
+ ' @keyframes rules to be ignored if it threw');
</script>
</body>

View file

@ -36,6 +36,55 @@ function assert_time_equals_literal(actual, expected, description) {
assert_approx_equals(actual, expected, TIME_PRECISION, description);
}
/*
* Compare two keyframes
*/
function assert_frames_equal(actual, expected, name) {
// TODO: Make this skip the 'composite' member when it is not specified in
// `expected` or when the implementation does not support it.
assert_array_equals(
Object.keys(actual).sort(),
Object.keys(expected).sort(),
`properties on ${name} should match`
);
for (const prop in actual) {
if (
// 'offset' can be null
(prop === 'offset' && typeof actual[prop] === 'number') ||
prop === 'computedOffset'
) {
assert_approx_equals(
actual[prop],
expected[prop],
0.00001,
"value for '" + prop + "' on " + name
);
} else {
assert_equals(
actual[prop],
expected[prop],
`value for '${prop}' on ${name} should match`
);
}
}
}
/*
* Compare two lists of keyframes
*/
function assert_frame_lists_equal(actual, expected) {
assert_equals(
actual.length,
expected.length,
'Number of keyframes should match'
);
for (let i = 0; i < actual.length; i++) {
assert_frames_equal(actual[i], expected[i], `Keyframe #${i}`);
}
}
/**
* Appends a div to the document body.
*

View file

@ -1,5 +1,6 @@
<!doctype html>
<title>round() function</title>
<meta charset=utf-8>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../support/numeric-testcommon.js"></script>

View file

@ -35,16 +35,20 @@ function test_math_used(testString, expectedString, {base="123px", msg, prop="le
All of these expect the testString to evaluate to a <number>.
*/
function test_plus_infinity(testString) {
test_math_used(`calc(1px * ${testString})`, "calc(infinity * 1px)");
test_math_used(`calc(1px * ${testString})`, "calc(infinity * 1px)",
{msg:`${testString} should equal +Infinity.`});
}
function test_minus_infinity(testString) {
test_math_used(`calc(1px * ${testString})`, "calc(-infinity * 1px)");
test_math_used(`calc(1px * ${testString})`, "calc(-infinity * 1px)",
{msg:`${testString} should equal -Infinity.`});
}
function test_plus_zero(testString) {
test_math_used(`calc(1px / ${testString})`, "calc(infinity * 1px)");
test_math_used(`calc(1px / ${testString})`, "calc(infinity * 1px)",
{msg:`${testString} should equal 0⁺.`});
}
function test_minus_zero(testString) {
test_math_used(`calc(1px / ${testString})`, "calc(-infinity * 1px)");
test_math_used(`calc(1px / ${testString})`, "calc(-infinity * 1px)",
{msg:`${testString} should equal 0⁻.`});
}
function test_nan(testString) {
// Make sure that it's NaN, not an infinity,

View file

@ -5,6 +5,7 @@
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script>
// See also: ./shared-worker-cache-storage.https.html
function remote(path) {
const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
@ -12,11 +13,11 @@ function remote(path) {
}
const iframe_path = "./resources/iframe.html?pipe=";
const dedicated_worker_path = "./dedicated-worker.js?pipe=";
const dedicated_worker_path = "./universal-worker.js?pipe=";
const ressource_path = "/images/blue.png?pipe=";
const coep_header= {
"coep-none" : "|header(Cross-Origin-Embedder-Policy,none)",
"coep-none" : "",
"coep-require-corp" : "|header(Cross-Origin-Embedder-Policy,require-corp)",
}
@ -107,7 +108,7 @@ function check(
iframe.contentWindow.postMessage(iframe_eval);
const {data} = await iframe_response;
assert_equals(data == "success", loaded);
assert_equals(data === "success", loaded);
}, `${iframe_coep} ${worker_coep} ${response_corp}`)
}

View file

@ -0,0 +1,117 @@
<!doctype html>
<html>
<title> Check enforcement of COEP in a ServiceWorker using CacheStorage. </title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
<script>
// See also: ./dedicated-worker-cache-storage.https.html
function remote(path) {
const REMOTE_ORIGIN = get_host_info().HTTPS_REMOTE_ORIGIN;
return new URL(path, REMOTE_ORIGIN);
}
const iframe_path = "./resources/iframe.html?pipe=";
const service_worker_path = "./resources/universal-worker.js?pipe=";
const ressource_path = "/images/blue.png?pipe=";
const coep_header= {
"coep-none" : "",
"coep-require-corp" : "|header(Cross-Origin-Embedder-Policy,require-corp)",
}
const corp_header = {
"corp-undefined" : "",
"corp-cross-origin" : "|header(Cross-Origin-Resource-Policy,cross-origin)",
}
// Send a message to the |worker| and wait for its response.
function executeCommandInServiceWorker(worker, command) {
const channel = new MessageChannel();
const response = new Promise(resolve => channel.port1.onmessage = resolve);
worker.postMessage(command, [ channel.port2 ]);
return response;
}
// Check enforcement of COEP in a ServiceWorker using CacheStorage.
//
// 1) Fetch a response from a document with COEP:none. Store it in the
// CacheStorage. The response is cross-origin without any CORS header.
// 2) From a ServiceWorker, retrieve the response from the CacheStorage.
//
// Test parameters:
// - |worker_coep| the COEP header of the ServiceWorker's script response.
// - |response_corp| the CORP header of the response.
//
// Test expectations:
// |loaded| is true whenever the worker is able to fetch the response from
// the CacheStorage. According to the specification:
// https://mikewest.github.io/corpp/#initialize-embedder-policy-for-global
// it must be false when:
// - |worker_coep| is 'coep-require-corp' and
// - |response-corp| is 'corp-undefined'.
function check(
// Test parameters:
worker_coep,
response_corp,
// Test expectations:
loaded) {
promise_test(async (t) => {
// 1) Fetch a response from a document with COEP:none. Store it in the
// CacheStorage. The response is cross-origin without any CORS header.
const resource_path = ressource_path + corp_header[response_corp];
const resource_url = remote(resource_path);
const fetch_request = new Request(resource_url, {mode: 'no-cors'});
const cache = await caches.open('v1');
const fetch_response = await fetch(fetch_request);
await cache.put(fetch_request, fetch_response);
// 2) Start a ServiceWorker.
const SCOPE= new URL(location.href).pathname;
const service_worker_allowed = `|header(service-worker-allowed,${SCOPE})`;
const SCRIPT =
service_worker_path +
coep_header[worker_coep] +
service_worker_allowed;
const reg = await service_worker_unregister_and_register(t, SCRIPT, SCOPE);
add_completion_callback(() => reg.unregister());
// Start talking to the ServiceWorker, no matter its state.
const worker = reg.installing || reg.waiting || reg.active;
// 3) From the service worker, try to retrieve the response from the
// CacheStorage.
const response = executeCommandInServiceWorker(worker, `
(async function() {
const cache = await caches.open('v1');
const request = new Request('${resource_url}', {
mode: 'no-cors'
});
try {
const response = await cache.match(request);
message.ports[0].postMessage('success');
} catch(error) {
message.ports[0].postMessage('error');
}
})()
`);
const {data} = await response;
assert_equals(data === "success", loaded);
}, `A ServiceWorker with ${worker_coep} use CacheStorage to get a ${response_corp} response.`)
}
// ------------------------------------------------------
// worker_coep , response_corp , loaded
// ------------------------------------------------------
check("coep-none" , "corp-undefined" , true);
check("coep-none" , "corp-cross-origin" , true);
check("coep-require-corp" , "corp-undefined" , false);
check("coep-require-corp" , "corp-cross-origin" , true);
</script>
</html>

View file

@ -16,6 +16,6 @@ dictionary CSSStyleSheetInit {
boolean disabled = false;
};
partial interface DocumentOrShadowRoot {
partial interface mixin DocumentOrShadowRoot {
attribute FrozenArray<CSSStyleSheet> adoptedStyleSheets;
};

View file

@ -100,3 +100,27 @@ directory_test(async (t, root_dir) => {
assert_equals(result.length, value.length);
await assert_equals_cloned_handles(result, value);
}, 'Store handle in IndexedDB and read using a cursor.');
directory_test(async (t, root_dir) => {
const handles = await create_file_system_handles(t, root_dir);
const db = await createDatabase(t, db => {
const store = db.createObjectStore('store', {keyPath: 'key'});
});
t.add_cleanup(() => deleteAllDatabases(t));
const value = handles;
let tx = db.transaction('store', 'readwrite');
let store = tx.objectStore('store');
await promiseForRequest(t, store.put({key: 'key', value}));
await promiseForTransaction(t, tx);
tx = db.transaction('store', 'readonly');
store = tx.objectStore('store');
const result = await promiseForRequest(t, store.get('key'));
await promiseForTransaction(t, tx);
assert_true(Array.isArray(result.value), 'Result should be an array');
assert_equals(result.value.length, value.length);
await assert_equals_cloned_handles(result.value, value);
}, 'Store handle in IndexedDB using inline keys.');

View file

@ -26,6 +26,10 @@ function toMojoNDEFRecord(record) {
nfcRecord.recordType = record.recordType;
nfcRecord.mediaType = record.mediaType;
nfcRecord.id = record.id;
if (record.recordType == 'text') {
nfcRecord.encoding = record.encoding == null? 'utf-8': record.encoding;
nfcRecord.lang = record.lang == null? 'en': record.lang;
}
nfcRecord.data = toByteArray(record.data);
if (record.data != null && record.data.records !== undefined) {
// |record.data| may be an NDEFMessageInit, i.e. the payload is a message.
@ -72,6 +76,17 @@ function compareNDEFRecords(providedRecord, receivedRecord) {
assert_not_equals(providedRecord.recordType, 'empty');
if (providedRecord.recordType == 'text') {
assert_equals(
providedRecord.encoding == null? 'utf-8': providedRecord.encoding,
receivedRecord.encoding);
assert_equals(providedRecord.lang == null? 'en': providedRecord.lang,
receivedRecord.lang);
} else {
assert_equals(null, receivedRecord.encoding);
assert_equals(null, receivedRecord.lang);
}
assert_array_equals(toByteArray(providedRecord.data),
new Uint8Array(receivedRecord.data));
}

View file

@ -106,11 +106,14 @@ def get_parser():
type=int,
help="maximum number of revisions to be returned by "
"the command")
parser.add_argument("--verbose", action="store_true", help="debug logging")
return parser
def run_rev_list(**kwargs):
# type: (**Any) -> None
if kwargs.get('verbose'):
logger.setLevel(logging.DEBUG)
# "epoch_threshold" is a safety margin. After this time it is fine to
# assume that any tags are created and pushed.
epoch_threshold = 600

View file

@ -192,7 +192,8 @@ function assertWebNDEFMessagesEqual(message, expectedMessage) {
assert_equals(record.recordType, expectedRecord.recordType);
assert_equals(record.mediaType, expectedRecord.mediaType);
assert_equals(record.id, expectedRecord.id);
assert_equals(record.encoding, expectedRecord.encoding);
assert_equals(record.lang, expectedRecord.lang);
// Compares record data
assert_array_equals(new Uint8Array(record.data),
new Uint8Array(expectedRecord.data));

View file

@ -0,0 +1,51 @@
<!doctype html>
<title>Test MessagePort while AudioContext is not running</title>
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script>
const get_node_and_message = async (context) => {
const node = new AudioWorkletNode(context, 'port-processor');
return new Promise((resolve) => {
node.port.onmessage = (event) => resolve({node: node, event: event});
});
};
const ping_for_message = async (node) => {
return new Promise((resolve) => {
node.port.onmessage = resolve;
node.port.postMessage('ping');
});
};
const modulePath = 'processors/port-processor.js';
promise_test(async () => {
const realtime = new AudioContext();
await realtime.audioWorklet.addModule(modulePath);
await realtime.suspend();
const currentTime = realtime.currentTime;
let {node, event} = await get_node_and_message(realtime);
assert_equals(event.data.timeStamp, currentTime, 'created message time');
event = await ping_for_message(node);
assert_equals(event.data.timeStamp, currentTime, 'pong time');
}, 'realtime suspended');
let offline;
promise_test(async () => {
offline = new OfflineAudioContext({length: 128 + 1, sampleRate: 16384});
await offline.audioWorklet.addModule(modulePath);
assert_equals(offline.currentTime, 0, 'time before start');
let {node, event} = await get_node_and_message(offline);
assert_equals(event.data.timeStamp, 0, 'created time before start');
event = await ping_for_message(node);
assert_equals(event.data.timeStamp, 0, 'pong time before start');
}, 'offline before start');
promise_test(async () => {
await offline.startRendering();
const expected = 2 * 128 / offline.sampleRate;
assert_equals(offline.currentTime, expected, 'time on complete');
let {node, event} = await get_node_and_message(offline);
assert_equals(event.data.timeStamp, expected, "created time on complete");
event = await ping_for_message(node);
assert_equals(event.data.timeStamp, expected, "pong time on complete");
}, 'offline on complete');
</script>

View file

@ -46,8 +46,6 @@ standardSetup(function() {
authnrSelBadUvNull.userVerification = null;
// authenticatorSelection bad values
new CreateCredentialsTest("options.publicKey.authenticatorSelection", []).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection is empty array", TypeError);
new CreateCredentialsTest("options.publicKey.authenticatorSelection", null).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection is null", TypeError);
new CreateCredentialsTest("options.publicKey.authenticatorSelection", "").runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection is empty string", TypeError);
new CreateCredentialsTest("options.publicKey.authenticatorSelection", "none").runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection is string", TypeError);
@ -65,10 +63,6 @@ standardSetup(function() {
new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelRkTrue)
.modify("options.publicKey.timeout", 300)
.runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection residentKey true", "NotAllowedError");
new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelRkBadString)
.modify("options.publicKey.timeout", 300)
.runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection residentKey is string", TypeError);
// TODO: not sure if rk is "boolean" or "truthy"; add test cases if it should only accept boolean values
// authenticatorSelection bad userVerification values
new CreateCredentialsTest("options.publicKey.authenticatorSelection", authnrSelBadUvEmptyStr).runTest("Bad AuthenticatorSelectionCriteria: authenticatorSelection userVerification empty string", TypeError);

View file

@ -21,7 +21,6 @@ standardSetup(function() {
new CreateCredentialsTest("options.publicKey.rp", {}).runTest("Bad rp: rp is empty object", TypeError);
// // rp.id
new CreateCredentialsTest("options.publicKey.rp.id", {}).runTest("Bad rp: id is object", TypeError);
new CreateCredentialsTest("options.publicKey.rp.id", null).runTest("Bad rp: id is null", "SecurityError");
new CreateCredentialsTest("options.publicKey.rp.id", "").runTest("Bad rp: id is empty String", "SecurityError");
new CreateCredentialsTest("options.publicKey.rp.id", "invalid domain.com").runTest("Bad rp: id is invalid domain (has space)", "SecurityError");
@ -30,17 +29,10 @@ standardSetup(function() {
// // rp.name
new CreateCredentialsTest({path: "options.publicKey.rp.name", value: undefined}).runTest("rp missing name", TypeError);
new CreateCredentialsTest("options.publicKey.rp.name", {}).runTest("Bad rp: name is object", TypeError);
new CreateCredentialsTest("options.publicKey.rp.name", null).runTest("Bad rp: name is null", TypeError);
new CreateCredentialsTest("options.publicKey.rp.name", "").runTest("Bad rp: name is empty String", TypeError);
// // rp.icon
new CreateCredentialsTest("options.publicKey.rp.icon", {}).runTest("Bad rp: icon is object", TypeError);
new CreateCredentialsTest("options.publicKey.rp.icon", null).runTest("Bad rp: icon is null", TypeError);
new CreateCredentialsTest("options.publicKey.rp.icon", "").runTest("Bad rp: icon is empty String", TypeError);
// rp.icon
new CreateCredentialsTest("options.publicKey.rp.icon", "http://fidoalliance.co.nz/testimages/catimage.png")
.runTest("Bad rp: icon is insecure", "SecurityError");
// // TODO: unicode tests for icon URL (see also: USVString)
});
/* JSHINT */

View file

@ -45,7 +45,7 @@ standardSetup(function() {
}
};
var p = createCredential(args);
return promise_rejects_dom(t, "NotAllowedError", p, "expected to fail on excluded credenetial");
return promise_rejects_dom(t, "InvalidStateError", p, "expected to fail on excluded credential");
});
}, "exclude existing credential");

View file

@ -21,15 +21,6 @@ standardSetup(function() {
// bad extension values
new CreateCredentialsTest("options.publicKey.extensions", "hi mom").runTest("Bad extensions: extensions is string", TypeError);
new CreateCredentialsTest("options.publicKey.extensions", null).runTest("Bad extensions: extensions is null", TypeError);
new CreateCredentialsTest("options.publicKey.extensions", []).runTest("Bad extensions: extensions is empty Array", TypeError);
new CreateCredentialsTest("options.publicKey.extensions", new ArrayBuffer(0)).runTest("Bad extensions: extensions is empty ArrayBuffer", TypeError);
var badJson = '{"foo": true, "bar: "yup"}'; // missing quote after "bar"
new CreateCredentialsTest("options.publicKey.extensions", {foo: badJson}).runTest("Bad extensions: malformatted JSON", TypeError);
new CreateCredentialsTest("options.publicKey.extensions", {foo: dummyExtension}).runTest("Bad extensions: JavaScript object", TypeError);
var badExtId = {};
badExtId[createRandomString(65)] = dummyExtension;
new CreateCredentialsTest("options.publicKey.extensions", {badExtId: dummyExtension}).runTest("Bad extensions: extension ID too long", TypeError);
// phony extensions
// TODO: not sure if this should pass or fail

View file

@ -42,10 +42,10 @@ standardSetup(function() {
new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [badTypeEmptyObj]).runTest("Bad pubKeyCredParams: first param has bad type (empty object)", TypeError);
new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [badAlg])
.modify("options.publicKey.timeout", 300)
.runTest("Bad pubKeyCredParams: first param has bad alg (42)", "NotSupportedError");
.runTest("Bad pubKeyCredParams: first param has bad alg (42)", "NotAllowedError");
new CreateCredentialsTest("options.publicKey.pubKeyCredParams", [badAlgZero])
.modify("options.publicKey.timeout", 300)
.runTest("Bad pubKeyCredParams: first param has bad alg (0)", "NotSupportedError");
.runTest("Bad pubKeyCredParams: first param has bad alg (0)", "NotAllowedError");
// TODO: come back to this when mock authenticators support multiple cryptos so that we can test the preference ranking
// function verifyEC256(res) {