diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:35:49 +0000 |
commit | d8bbc7858622b6d9c278469aab701ca0b609cddf (patch) | |
tree | eff41dc61d9f714852212739e6b3738b82a2af87 /testing/web-platform/tests | |
parent | Releasing progress-linux version 125.0.3-1~progress7.99u1. (diff) | |
download | firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.tar.xz firefox-d8bbc7858622b6d9c278469aab701ca0b609cddf.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
1683 files changed, 35985 insertions, 10864 deletions
diff --git a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/classic_interop/__init__.py b/dom/quota/scripts/qm-try-analysis/qm_try_analysis/__init__.py index e69de29bb2..e69de29bb2 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/classic_interop/__init__.py +++ b/dom/quota/scripts/qm-try-analysis/qm_try_analysis/__init__.py diff --git a/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/__init__.py b/js/src/tests/test262/prs/3994/built-ins/Uint8Array/browser.js index e69de29bb2..e69de29bb2 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/__init__.py +++ b/js/src/tests/test262/prs/3994/built-ins/Uint8Array/browser.js diff --git a/testing/web-platform/tests/IndexedDB/idbdatabase_deleteObjectStore.any.js b/testing/web-platform/tests/IndexedDB/idbdatabase_deleteObjectStore.any.js new file mode 100644 index 0000000000..5288891bd7 --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/idbdatabase_deleteObjectStore.any.js @@ -0,0 +1,89 @@ +// META: global=window,worker +// META: title=IDBDatabase.deleteObjectStore() +// META: script=resources/support.js +// @author Microsoft <https://www.microsoft.com> +// @author Odin Hørthe Omdal <mailto:odinho@opera.com> + +'use_strict'; + +async_test(t => { + let db; + let add_success = false; + + const open_rq = createdb(t); + open_rq.onupgradeneeded = function(e) { + db = e.target.result; + + const objStore = db.createObjectStore("store", { autoIncrement: true }); + assert_equals(db.objectStoreNames[0], "store", "objectStoreNames"); + + const rq_add = objStore.add(1); + rq_add.onsuccess = function() { add_success = true; }; + rq_add.onerror = fail(t, 'rq_add.error'); + + objStore.createIndex("idx", "a"); + db.deleteObjectStore("store"); + assert_equals(db.objectStoreNames.length, 0, "objectStoreNames.length after delete"); + assert_false(db.objectStoreNames.contains("store")); + + const exc = "InvalidStateError"; + assert_throws_dom(exc, function() { objStore.add(2); }); + assert_throws_dom(exc, function() { objStore.put(3); }); + assert_throws_dom(exc, function() { objStore.get(1); }); + assert_throws_dom(exc, function() { objStore.clear(); }); + assert_throws_dom(exc, function() { objStore.count(); }); + assert_throws_dom(exc, function() { objStore.delete(1); }); + assert_throws_dom(exc, function() { objStore.openCursor(); }); + assert_throws_dom(exc, function() { objStore.index("idx"); }); + assert_throws_dom(exc, function() { objStore.deleteIndex("idx"); }); + assert_throws_dom(exc, function() { objStore.createIndex("idx2", "a"); + }); + }; + + open_rq.onsuccess = function() { + assert_true(add_success, "First add was successful"); + t.done(); + } +}, 'Deleted object store\'s name should be removed from database\'s list. Attempting to use a \ +deleted IDBObjectStore should throw an InvalidStateError'); + +async_test(t => { + const open_rq = createdb(t); + + open_rq.onupgradeneeded = function(e) { + const db = e.target.result; + assert_throws_dom('NotFoundError', function() { db.deleteObjectStore('whatever'); }); + t.done(); + }; +}, 'Attempting to remove an object store that does not exist should throw a NotFoundError'); + +async_test(t => { + const keys = []; + const open_rq = createdb(t); + + open_rq.onupgradeneeded = function(e) { + const db = e.target.result; + + const objStore = db.createObjectStore("resurrected", { autoIncrement: true, keyPath: "k" }); + objStore.add({ k: 5 }).onsuccess = function(e) { keys.push(e.target.result); }; + objStore.add({}).onsuccess = function(e) { keys.push(e.target.result); }; + objStore.createIndex("idx", "i"); + assert_true(objStore.indexNames.contains("idx")); + assert_equals(objStore.keyPath, "k", "keyPath"); + + db.deleteObjectStore("resurrected"); + + const objStore2 = db.createObjectStore("resurrected", { autoIncrement: true }); + objStore2.add("Unicorns'R'us").onsuccess = function(e) { keys.push(e.target.result); }; + assert_false(objStore2.indexNames.contains("idx"), "index exist on new objstore"); + assert_equals(objStore2.keyPath, null, "keyPath"); + + assert_throws_dom("NotFoundError", function() { objStore2.index("idx"); }); + }; + + open_rq.onsuccess = function(e) { + assert_array_equals(keys, [5, 6, 1], "keys"); + t.done(); + }; +}, 'Attempting to access an index that was deleted as part of object store deletion and then \ +recreated using the same object store name should throw a NotFoundError'); diff --git a/testing/web-platform/tests/IndexedDB/idbdatabase_deleteObjectStore.htm b/testing/web-platform/tests/IndexedDB/idbdatabase_deleteObjectStore.htm deleted file mode 100644 index e3f6a775c5..0000000000 --- a/testing/web-platform/tests/IndexedDB/idbdatabase_deleteObjectStore.htm +++ /dev/null @@ -1,25 +0,0 @@ -<!DOCTYPE html> -<title>IDBDatabase.deleteObjectStore() - object store's name is removed from database's list </title> -<link rel="author" title="Microsoft" href="http://www.microsoft.com"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="resources/support.js"></script> - -<script> - -var t = async_test(), - open_rq = createdb(t) - -open_rq.onupgradeneeded = function(e) { - var db = e.target.result - - db.createObjectStore("deleted"); - db.deleteObjectStore("deleted"); - assert_false(db.objectStoreNames.contains("deleted")) - - t.done() -} - -</script> - -<div id="log"></div> diff --git a/testing/web-platform/tests/IndexedDB/idbdatabase_deleteObjectStore3.htm b/testing/web-platform/tests/IndexedDB/idbdatabase_deleteObjectStore3.htm deleted file mode 100644 index 3ddbe8ec62..0000000000 --- a/testing/web-platform/tests/IndexedDB/idbdatabase_deleteObjectStore3.htm +++ /dev/null @@ -1,23 +0,0 @@ -<!DOCTYPE html> -<title>IDBDatabase.deleteObjectStore() - attempt to remove an object store that does not exist </title> -<link rel="author" title="Microsoft" href="http://www.microsoft.com"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="resources/support.js"></script> - -<script> - -var t = async_test(), - open_rq = createdb(t); - -open_rq.onupgradeneeded = function(e) -{ - var db = e.target.result; - assert_throws_dom('NotFoundError', - function() { db.deleteObjectStore('whatever'); }); - t.done(); -} - -</script> - -<div id="log"></div> diff --git a/testing/web-platform/tests/IndexedDB/idbdatabase_deleteObjectStore4-not_reused.htm b/testing/web-platform/tests/IndexedDB/idbdatabase_deleteObjectStore4-not_reused.htm deleted file mode 100644 index 0a5e1b83cf..0000000000 --- a/testing/web-platform/tests/IndexedDB/idbdatabase_deleteObjectStore4-not_reused.htm +++ /dev/null @@ -1,42 +0,0 @@ -<!DOCTYPE html> -<meta charset=utf-8> -<title>IDBDatabase.deleteObjectStore() - the object store is not reused</title> -<link rel="author" href="mailto:odinho@opera.com" title="Odin Hørthe Omdal"> -<script src=/resources/testharness.js></script> -<script src=/resources/testharnessreport.js></script> -<script src=resources/support.js></script> - -<script> - -var t = async_test(), - keys = [], - open_rq = createdb(t) - -open_rq.onupgradeneeded = function(e) { - var db = e.target.result - - var objStore = db.createObjectStore("resurrected", { autoIncrement: true, keyPath: "k" }); - objStore.add({k:5}).onsuccess = function(e) { keys.push(e.target.result); } - objStore.add({}).onsuccess = function(e) { keys.push(e.target.result); } - objStore.createIndex("idx", "i"); - assert_true(objStore.indexNames.contains("idx")); - assert_equals(objStore.keyPath, "k", "keyPath"); - - db.deleteObjectStore("resurrected"); - - var objStore2 = db.createObjectStore("resurrected", { autoIncrement: true }); - objStore2.add("Unicorns'R'us").onsuccess = function(e) { keys.push(e.target.result); }; - assert_false(objStore2.indexNames.contains("idx"), "index exist on new objstore"); - assert_equals(objStore2.keyPath, null, "keyPath"); - - assert_throws_dom("NotFoundError", function() { objStore2.index("idx"); }); -} - -open_rq.onsuccess = function(e) { - assert_array_equals(keys, [5, 6, 1], "keys"); - t.done(); -} - -</script> - -<div id="log"></div> diff --git a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete.any.js b/testing/web-platform/tests/IndexedDB/idbobjectstore_delete.any.js new file mode 100644 index 0000000000..0b731f391d --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/idbobjectstore_delete.any.js @@ -0,0 +1,207 @@ +// META: global=window,worker +// META: title=IDBObjectStore.delete() +// META: script=resources/support.js +// @author Microsoft <https://www.microsoft.com> + +'use_strict'; + +async_test(t => { + let db; + const record = { key: 1, property: "data" }; + + const open_rq = createdb(t); + open_rq.onupgradeneeded = function(e) { + db = e.target.result; + + const objStore = db.createObjectStore("test", { keyPath: "key" }); + objStore.add(record); + }; + + open_rq.onsuccess = function(e) { + const delete_rq = db.transaction("test", "readwrite", + { durability: 'relaxed' }) + .objectStore("test") + .delete(record.key); + + delete_rq.onsuccess = t.step_func(function(e) { + assert_equals(e.target.result, undefined); + + e.target.transaction.oncomplete = t.step_func(VerifyRecordRemoved); + }); + }; + + function VerifyRecordRemoved() { + const rq = db.transaction("test", "readonly", + { durability: 'relaxed' }) + .objectStore("test") + .get(record.key); + + rq.onsuccess = t.step_func(function(e) { + assert_equals(e.target.result, undefined); + t.done(); + }); + } +}, 'delete() removes record (inline keys)'); + +async_test(t => { + const open_rq = createdb(t); + open_rq.onupgradeneeded = function(e) { + const db = e.target.result; + + const delete_rq = db.createObjectStore("test") + .delete(1); + + delete_rq.onsuccess = t.step_func(function(e) { + assert_equals(e.target.result, undefined); + t.done(); + }); + }; +}, 'delete() key doesn\'t match any records'); + +async_test(t => { + let db; + const record = { test: { obj: { key: 1 } }, property: "data" }; + + const open_rq = createdb(t); + open_rq.onupgradeneeded = function(e) { + db = e.target.result; + + const objStore = db.createObjectStore("test", + { keyPath: "test.obj.key" }); + objStore.add(record); + }; + + open_rq.onsuccess = function(e) { + const delete_rq = db.transaction("test", "readwrite", + { durability: 'relaxed' }) + .objectStore("test") + .delete(record.test.obj.key); + + delete_rq.onsuccess = t.step_func(function(e) { + assert_equals(e.target.result, undefined); + + e.target.transaction.oncomplete = t.step_func(VerifyRecordRemoved); + }); + }; + + function VerifyRecordRemoved() { + const rq = db.transaction("test", "readonly", + { durability: 'relaxed' }) + .objectStore("test") + .get(record.test.obj.key); + + rq.onsuccess = t.step_func(function(e) { + assert_equals(e.target.result, undefined); + t.done(); + }); + } +}, 'Object store\'s key path is an object attribute'); + +async_test(t => { + let db; + const key = 1; + const record = { property: "data" }; + + const open_rq = createdb(t); + open_rq.onupgradeneeded = function(e) { + db = e.target.result; + + const objStore = db.createObjectStore("test"); + objStore.add(record, key); + }; + + open_rq.onsuccess = function(e) { + const delete_rq = db.transaction("test", "readwrite", + { durability: 'relaxed' }) + .objectStore("test") + .delete(key); + + delete_rq.onsuccess = t.step_func(function(e) { + assert_equals(e.target.result, undefined); + + e.target.transaction.oncomplete = t.step_func(VerifyRecordRemoved); + }); + }; + + function VerifyRecordRemoved() { + const rq = db.transaction("test", "readonly", + { durability: 'relaxed' }) + .objectStore("test") + .get(key); + + rq.onsuccess = t.step_func(function(e) { + assert_equals(e.target.result, undefined); + t.done(); + }); + } +}, 'delete() removes record (out-of-line keys)'); + +async_test(t => { + let db; + const open_rq = createdb(t); + + open_rq.onupgradeneeded = function(e) { + db = e.target.result; + const os = db.createObjectStore("store"); + + for(let i = 0; i < 10; i++) + os.add("data" + i, i); + }; + + open_rq.onsuccess = function (e) { + const os = db.transaction("store", "readwrite", + { durability: 'relaxed' }) + .objectStore("store"); + + os.delete(IDBKeyRange.bound(3, 6)); + os.count().onsuccess = t.step_func(function(e) { + assert_equals(e.target.result, 6, "Count after deleting \ + 3-6 from 10"); + t.done(); + }); + }; +}, 'delete() removes all of the records in the range'); + +async_test(function(t) { + let db; + const records = [{ pKey: "primaryKey_0" }, { pKey: "primaryKey_1" }]; + + const open_rq = createdb(t); + open_rq.onupgradeneeded = function(event) { + db = event.target.result; + const objStore = db.createObjectStore("store", { keyPath: "pKey" }); + for (let i = 0; i < records.length; i++) { + objStore.add(records[i]); + } + }; + + open_rq.onsuccess = function(event) { + const txn = db.transaction("store", "readonly", + { durability: 'relaxed' }); + const ostore = txn.objectStore("store"); + t.step(function() { + assert_throws_dom("ReadOnlyError", function() { + ostore.delete("primaryKey_0"); + }); + }); + t.done(); + }; +}, 'If the transaction this IDBObjectStore belongs to has its mode set to \ +readonly, throw ReadOnlyError'); + +async_test(t => { + let ostore; + const records = [{ pKey: "primaryKey_0" }, { pKey: "primaryKey_1" }]; + + const open_rq = createdb(t); + open_rq.onupgradeneeded = function(event) { + const db = event.target.result; + ostore = db.createObjectStore("store", { keyPath: "pKey" }); + db.deleteObjectStore("store"); + assert_throws_dom("InvalidStateError", function() { + ostore.delete("primaryKey_0"); + }); + t.done(); + }; +}, 'If the object store has been deleted, the implementation must throw a \ +DOMException of type InvalidStateError'); diff --git a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete.htm b/testing/web-platform/tests/IndexedDB/idbobjectstore_delete.htm deleted file mode 100644 index 880309d01a..0000000000 --- a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete.htm +++ /dev/null @@ -1,46 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>IDBObjectStore.delete() - delete removes record (inline keys)</title> -<link rel="author" title="Microsoft" href="http://www.microsoft.com"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="resources/support.js"></script> - -<script> - var db, - t = async_test(), - record = { key: 1, property: "data" }; - - var open_rq = createdb(t); - open_rq.onupgradeneeded = function(e) { - db = e.target.result; - - var objStore = db.createObjectStore("test", { keyPath: "key" }); - objStore.add(record); - }; - - open_rq.onsuccess = function(e) { - var delete_rq = db.transaction("test", "readwrite", {durability: 'relaxed'}) - .objectStore("test") - .delete(record.key); - - delete_rq.onsuccess = t.step_func(function(e) { - assert_equals(e.target.result, undefined); - - e.target.transaction.oncomplete = t.step_func(VerifyRecordRemoved); - }); - }; - - function VerifyRecordRemoved() { - var rq = db.transaction("test", "readonly", {durability: 'relaxed'}) - .objectStore("test") - .get(record.key); - - rq.onsuccess = t.step_func(function(e) { - assert_equals(e.target.result, undefined); - t.done(); - }); - } -</script> - -<div id="log"></div> diff --git a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete2.htm b/testing/web-platform/tests/IndexedDB/idbobjectstore_delete2.htm deleted file mode 100644 index eb71169905..0000000000 --- a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete2.htm +++ /dev/null @@ -1,27 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>IDBObjectStore.delete() - key doesn't match any records </title> -<link rel="author" title="Microsoft" href="http://www.microsoft.com"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="resources/support.js"></script> - -<script> - var db, - t = async_test(); - - var open_rq = createdb(t); - open_rq.onupgradeneeded = function(e) { - db = e.target.result; - - var delete_rq = db.createObjectStore("test") - .delete(1); - - delete_rq.onsuccess = t.step_func(function(e) { - assert_equals(e.target.result, undefined); - t.done(); - }); - }; -</script> - -<div id="log"></div> diff --git a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete3.htm b/testing/web-platform/tests/IndexedDB/idbobjectstore_delete3.htm deleted file mode 100644 index 1ea9dd9958..0000000000 --- a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete3.htm +++ /dev/null @@ -1,47 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>IDBObjectStore.delete() - object store's key path is an object attribute </title> -<link rel="author" title="Microsoft" href="http://www.microsoft.com"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="resources/support.js"></script> - -<script> - var db, - t = async_test(), - record = { test: { obj: { key: 1 } }, property: "data" }; - - var open_rq = createdb(t); - open_rq.onupgradeneeded = function(e) { - db = e.target.result; - - var objStore = db.createObjectStore("test", { keyPath: "test.obj.key" }); - objStore.add(record); - }; - - open_rq.onsuccess = function(e) { - var delete_rq = db.transaction("test", "readwrite", {durability: 'relaxed'}) - .objectStore("test") - .delete(record.test.obj.key); - - delete_rq.onsuccess = t.step_func(function(e) { - assert_equals(e.target.result, undefined); - - e.target.transaction.oncomplete = t.step_func(VerifyRecordRemoved); - }); - }; - - function VerifyRecordRemoved() { - var rq = db.transaction("test", "readonly", {durability: 'relaxed'}) - .objectStore("test") - .get(record.test.obj.key); - - rq.onsuccess = t.step_func(function(e) { - assert_equals(e.target.result, undefined); - t.done(); - }); - } - -</script> - -<div id="log"></div> diff --git a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete4.htm b/testing/web-platform/tests/IndexedDB/idbobjectstore_delete4.htm deleted file mode 100644 index 9d074bff85..0000000000 --- a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete4.htm +++ /dev/null @@ -1,48 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>IDBObjectStore.delete() - delete removes record (out-of-line keys) </title> -<link rel="author" title="Microsoft" href="http://www.microsoft.com"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="resources/support.js"></script> - -<script> - var db, - t = async_test(), - key = 1, - record = { property: "data" }; - - var open_rq = createdb(t); - open_rq.onupgradeneeded = function(e) { - db = e.target.result; - - var objStore = db.createObjectStore("test"); - objStore.add(record, key); - }; - - open_rq.onsuccess = function(e) { - var delete_rq = db.transaction("test", "readwrite", {durability: 'relaxed'}) - .objectStore("test") - .delete(key); - - delete_rq.onsuccess = t.step_func(function(e) { - assert_equals(e.target.result, undefined); - - e.target.transaction.oncomplete = t.step_func(VerifyRecordRemoved); - }); - }; - - function VerifyRecordRemoved() { - var rq = db.transaction("test", "readonly", {durability: 'relaxed'}) - .objectStore("test") - .get(key); - - rq.onsuccess = t.step_func(function(e) { - assert_equals(e.target.result, undefined); - t.done(); - }); - } - -</script> - -<div id="log"></div> diff --git a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete5.htm b/testing/web-platform/tests/IndexedDB/idbobjectstore_delete5.htm deleted file mode 100644 index f7696e6efa..0000000000 --- a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete5.htm +++ /dev/null @@ -1,32 +0,0 @@ -<!DOCTYPE html> -<title>IDBObjectStore.delete() - removes all of the records in the range</title> -<link rel="author" title="Microsoft" href="http://www.microsoft.com"> -<script src=/resources/testharness.js></script> -<script src=/resources/testharnessreport.js></script> -<script src=resources/support.js></script> -<script> - var db - var open_rq = createdb(async_test()) - - open_rq.onupgradeneeded = function(e) { - db = e.target.result - var os = db.createObjectStore("store") - - for(var i = 0; i < 10; i++) - os.add("data" + i, i) - } - - open_rq.onsuccess = function (e) { - var os = db.transaction("store", "readwrite", {durability: 'relaxed'}) - .objectStore("store") - - os.delete( IDBKeyRange.bound(3, 6) ) - os.count().onsuccess = this.step_func(function(e) - { - assert_equals(e.target.result, 6, "Count after deleting 3-6 from 10"); - this.done(); - }) - } -</script> - -<div id=log></div> diff --git a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete6.htm b/testing/web-platform/tests/IndexedDB/idbobjectstore_delete6.htm deleted file mode 100644 index 70d8af32fe..0000000000 --- a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete6.htm +++ /dev/null @@ -1,36 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>IDBObjectStore.delete() - If the transaction this IDBObjectStore belongs to has its mode set to readonly, throw ReadOnlyError</title> -<link rel="author" title="Intel" href="http://www.intel.com"> -<link rel="help" href="https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#widl-IDBObjectStore-delete-IDBRequest-any-key"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="resources/support.js"></script> -<div id="log"></div> -<script> - var db, - t = async_test(), - records = [{ pKey: "primaryKey_0"}, - { pKey: "primaryKey_1"}]; - - var open_rq = createdb(t); - open_rq.onupgradeneeded = function (event) { - db = event.target.result; - var objStore = db.createObjectStore("store", {keyPath:"pKey"}); - for (var i = 0; i < records.length; i++) { - objStore.add(records[i]); - } - } - - open_rq.onsuccess = function (event) { - var txn = db.transaction("store", "readonly", {durability: 'relaxed'}); - var ostore = txn.objectStore("store"); - t.step(function(){ - assert_throws_dom("ReadOnlyError", function(){ - ostore.delete("primaryKey_0"); - }); - }); - t.done(); - } -</script> - diff --git a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete7.htm b/testing/web-platform/tests/IndexedDB/idbobjectstore_delete7.htm deleted file mode 100644 index a65885cc2b..0000000000 --- a/testing/web-platform/tests/IndexedDB/idbobjectstore_delete7.htm +++ /dev/null @@ -1,27 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>IDBObjectStore.delete() - If the object store has been deleted, the implementation must throw a DOMException of type InvalidStateError</title> -<link rel="author" title="Intel" href="http://www.intel.com"> -<link rel="help" href="https://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#widl-IDBObjectStore-delete-IDBRequest-any-key"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="resources/support.js"></script> -<div id="log"></div> -<script> - var db, - ostore, - t = async_test(), - records = [{ pKey: "primaryKey_0"}, - { pKey: "primaryKey_1"}]; - - var open_rq = createdb(t); - open_rq.onupgradeneeded = function (event) { - db = event.target.result; - ostore = db.createObjectStore("store", {keyPath:"pKey"}); - db.deleteObjectStore("store"); - assert_throws_dom("InvalidStateError", function(){ - ostore.delete("primaryKey_0"); - }); - t.done(); - } -</script> diff --git a/testing/web-platform/tests/IndexedDB/idbobjectstore_deleteIndex.any.js b/testing/web-platform/tests/IndexedDB/idbobjectstore_deleteIndex.any.js new file mode 100644 index 0000000000..ed0246e0e7 --- /dev/null +++ b/testing/web-platform/tests/IndexedDB/idbobjectstore_deleteIndex.any.js @@ -0,0 +1,42 @@ +// META: global=window,worker +// META: title=IDBObjectStore.deleteIndex() +// META: script=resources/support.js +// @author Microsoft <https://www.microsoft.com> + +'use_strict'; + +async_test(t => { + let db; + const key = 1; + const record = { property: "data" }; + + const open_rq = createdb(t); + open_rq.onupgradeneeded = function(e) { + db = e.target.result; + const objStore = db.createObjectStore("test"); + objStore.createIndex("index", "indexedProperty"); + }; + + open_rq.onsuccess = function(e) { + db.close(); + const new_version = createdb(t, db.name, 2); + new_version.onupgradeneeded = function(e) { + db = e.target.result; + const objStore = e.target.transaction.objectStore("test"); + objStore.deleteIndex("index"); + }; + + new_version.onsuccess = function(e) { + let index; + const objStore = db.transaction("test", "readonly", + { durability: 'relaxed' }) + .objectStore("test"); + + assert_throws_dom('NotFoundError', function() + { index = objStore.index("index"); }); + assert_equals(index, undefined); + db.close(); + t.done(); + }; + }; +}, 'IDBObjectStore.deleteIndex() removes the index'); diff --git a/testing/web-platform/tests/IndexedDB/idbobjectstore_deleteIndex.htm b/testing/web-platform/tests/IndexedDB/idbobjectstore_deleteIndex.htm deleted file mode 100644 index f12af6fc53..0000000000 --- a/testing/web-platform/tests/IndexedDB/idbobjectstore_deleteIndex.htm +++ /dev/null @@ -1,44 +0,0 @@ -<!DOCTYPE html> -<meta charset="utf-8"> -<title>IDBObjectStore.deleteIndex() - removes the index </title> -<link rel="author" title="Microsoft" href="http://www.microsoft.com"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="resources/support.js"></script> - -<script> - var db, - t = async_test(), - key = 1, - record = { property: "data" }; - - var open_rq = createdb(t); - open_rq.onupgradeneeded = function(e) { - db = e.target.result; - db.createObjectStore("test") - .createIndex("index", "indexedProperty") - }; - - open_rq.onsuccess = function(e) { - db.close(); - var new_version = createdb(t, db.name, 2); - new_version.onupgradeneeded = function(e) { - db = e.target.result; - var objStore = e.target.transaction.objectStore("test") - objStore.deleteIndex("index"); - } - new_version.onsuccess = function(e) { - var index, - objStore = db.transaction("test", "readonly", {durability: 'relaxed'}) - .objectStore("test"); - - assert_throws_dom('NotFoundError', - function() { index = objStore.index("index") }); - assert_equals(index, undefined); - db.close(); - t.done(); - } - } -</script> - -<div id="log"></div> diff --git a/testing/web-platform/tests/IndexedDB/idbobjectstore_deleted.htm b/testing/web-platform/tests/IndexedDB/idbobjectstore_deleted.htm deleted file mode 100644 index 5ccc8fdb1b..0000000000 --- a/testing/web-platform/tests/IndexedDB/idbobjectstore_deleted.htm +++ /dev/null @@ -1,50 +0,0 @@ -<!DOCTYPE html> -<meta charset=utf-8> -<title>Attempting to use deleted IDBObjectStore</title> -<link rel=help href="http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html#object-store"> -<link rel=assert title="InvalidStateError Occurs if a request is made on a source object that has been deleted or removed."> -<link rel=author href="mailto:odinho@opera.com" title="Odin Hørthe Omdal"> -<script src=/resources/testharness.js></script> -<script src=/resources/testharnessreport.js></script> -<script src=resources/support.js></script> - -<script> - var db, - add_success = false, - t = async_test() - - var open_rq = createdb(t); - open_rq.onupgradeneeded = function(e) { - db = e.target.result; - - var objStore = db.createObjectStore("store", { autoIncrement: true }); - assert_equals(db.objectStoreNames[0], "store", "objectStoreNames"); - - var rq_add = objStore.add(1); - rq_add.onsuccess = function() { add_success = true; }; - rq_add.onerror = fail(t, 'rq_add.error'); - - objStore.createIndex("idx", "a"); - db.deleteObjectStore("store"); - assert_equals(db.objectStoreNames.length, 0, "objectStoreNames.length after delete"); - - const exc = "InvalidStateError" - assert_throws_dom(exc, function() { objStore.add(2); }); - assert_throws_dom(exc, function() { objStore.put(3); }); - assert_throws_dom(exc, function() { objStore.get(1); }); - assert_throws_dom(exc, function() { objStore.clear(); }); - assert_throws_dom(exc, function() { objStore.count(); }); - assert_throws_dom(exc, function() { objStore.delete(1); }); - assert_throws_dom(exc, function() { objStore.openCursor(); }); - assert_throws_dom(exc, function() { objStore.index("idx"); }); - assert_throws_dom(exc, function() { objStore.deleteIndex("idx"); }); - assert_throws_dom(exc, function() { objStore.createIndex("idx2", "a"); }); - } - - open_rq.onsuccess = function() { - assert_true(add_success, "First add was successful"); - t.done(); - } -</script> - -<div id=log></div> diff --git a/testing/web-platform/tests/IndexedDB/ready-state-destroyed-execution-context.html b/testing/web-platform/tests/IndexedDB/ready-state-destroyed-execution-context.html index 8194052391..6b2677fae7 100644 --- a/testing/web-platform/tests/IndexedDB/ready-state-destroyed-execution-context.html +++ b/testing/web-platform/tests/IndexedDB/ready-state-destroyed-execution-context.html @@ -21,6 +21,10 @@ promise_test(async t => { const openRequest = iframe.contentWindow.indexedDB.open(dbname); assert_equals(openRequest.readyState, 'pending'); iframe.remove(); + await new Promise(resolve => { + openRequest.onerror = resolve; + openRequest.onsuccess = resolve; + }); assert_equals(openRequest.readyState, 'done'); }, 'readyState accessor is valid after execution context is destroyed'); diff --git a/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa.js b/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa.js index 7817b78cff..74452d2609 100644 --- a/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa.js +++ b/testing/web-platform/tests/WebCryptoAPI/sign_verify/eddsa.js @@ -1,363 +1,214 @@ function run_test() { - setup({explicit_done: true}); - var subtle = self.crypto.subtle; // Change to test prefixed implementations - // When are all these tests really done? When all the promises they use have resolved. - var all_promises = []; - // Source file [algorithm_name]_vectors.js provides the getTestVectors method // for the algorithm that drives these tests. var testVectors = getTestVectors(); - // Test verification first, because signing tests rely on that working testVectors.forEach(function(vector) { - var promise = importVectorKeys(vector, ["verify"], ["sign"]) - .then(function(vectors) { - var algorithm = {name: vector.algorithmName}; - promise_test(function(test) { - var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, vector.data) - .then(function(is_verified) { - assert_true(is_verified, "Signature verified"); - }, function(err) { - assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); - }); - - return operation; - }, vector.name + " verification"); - - }, function(err) { - // We need a failed test if the importVectorKey operation fails, so - // we know we never tested verification. - promise_test(function(test) { - assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " verification"); - }); - - all_promises.push(promise); - }); + var algorithm = {name: vector.algorithmName}; - // Test verification with an altered buffer after call - testVectors.forEach(function(vector) { - var promise = importVectorKeys(vector, ["verify"], ["sign"]) - .then(function(vectors) { - var algorithm = {name: vector.algorithmName}; - promise_test(function(test) { + // Test verification first, because signing tests rely on that working + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + isVerified = await subtle.verify(algorithm, key, vector.signature, vector.data) + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_true(isVerified, "Signature verified"); + }, vector.name + " verification"); + + // Test verification with an altered buffer after call + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); var signature = copyBuffer(vector.signature); - var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.data) - .then(function(is_verified) { - assert_true(is_verified, "Signature verified"); - }, function(err) { - assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); - }); - - signature[0] = 255 - signature[0]; - return operation; - }, vector.name + " verification with altered signature after call"); - }, function(err) { - promise_test(function(test) { - assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " verification with altered signature after call"); - }); - - all_promises.push(promise); - }); - - // Check for successful verification even if data is altered after call. - testVectors.forEach(function(vector) { - var promise = importVectorKeys(vector, ["verify"], ["sign"]) - .then(function(vectors) { - var algorithm = {name: vector.algorithmName}; - promise_test(function(test) { + [isVerified] = await Promise.all([ + subtle.verify(algorithm, key, signature, vector.data), + signature[0] = 255 - signature[0] + ]); + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_true(isVerified, "Signature verified"); + }, vector.name + " verification with altered signature after call"); + + // Check for successful verification even if data is altered after call. + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); var data = copyBuffer(vector.data); - var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, data) - .then(function(is_verified) { - assert_true(is_verified, "Signature verified"); - }, function(err) { - assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); - }); - - data[0] = 255 - data[0]; - return operation; - }, vector.name + " with altered data after call"); - }, function(err) { - promise_test(function(test) { - assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " with altered data after call"); - }); - - all_promises.push(promise); - }); - - // Check for failures due to using privateKey to verify. - testVectors.forEach(function(vector) { - var promise = importVectorKeys(vector, ["verify"], ["sign"]) - .then(function(vectors) { - var algorithm = {name: vector.algorithmName}; - promise_test(function(test) { - return subtle.verify(algorithm, vector.privateKey, vector.signature, vector.data) - .then(function(data) { - assert_unreached("Should have thrown error for using privateKey to verify in " + vector.name + ": " + err.message + "'"); - }, function(err) { - assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); - }); - }, vector.name + " using privateKey to verify"); - - }, function(err) { - promise_test(function(test) { - assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " using privateKey to verify"); - }); - - all_promises.push(promise); - }); - - // Check for failures due to using publicKey to sign. - testVectors.forEach(function(vector) { - var promise = importVectorKeys(vector, ["verify"], ["sign"]) - .then(function(vectors) { - var algorithm = {name: vector.algorithmName}; - promise_test(function(test) { - return subtle.sign(algorithm, vector.publicKey, vector.data) - .then(function(signature) { - assert_unreached("Should have thrown error for using publicKey to sign in " + vector.name + ": " + err.message + "'"); - }, function(err) { - assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); - }); - }, vector.name + " using publicKey to sign"); - }, function(err) { - promise_test(function(test) { - assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " using publicKey to sign"); - }); - - all_promises.push(promise); - }); - - // Check for failures due to no "verify" usage. - testVectors.forEach(function(originalVector) { - var vector = Object.assign({}, originalVector); - - var promise = importVectorKeys(vector, [], ["sign"]) - .then(function(vectors) { - var algorithm = {name: vector.algorithmName}; - promise_test(function(test) { - return subtle.verify(algorithm, vector.publicKey, vector.signature, vector.data) - .then(function(data) { - assert_unreached("Should have thrown error for no verify usage in " + vector.name + ": " + err.message + "'"); - }, function(err) { - assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); - }); - }, vector.name + " no verify usage"); - }, function(err) { - promise_test(function(test) { - assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " no verify usage"); - }); - - all_promises.push(promise); - }); - - // Check for successful signing and verification. - testVectors.forEach(function(vector) { - var promise = importVectorKeys(vector, ["verify"], ["sign"]) - .then(function(vectors) { - var algorithm = {name: vector.algorithmName}; - promise_test(function(test) { - return subtle.sign(algorithm, vector.privateKey, vector.data) - .then(function(signature) { - assert_true(equalBuffers(signature, vector.signature), "Signing did not give the expected output"); - // Can we verify the signature? - return subtle.verify(algorithm, vector.publicKey, signature, vector.data) - .then(function(is_verified) { - assert_true(is_verified, "Round trip verification works"); - return signature; - }, function(err) { - assert_unreached("verify error for test " + vector.name + ": " + err.message + "'"); - }); - }, function(err) { - assert_unreached("sign error for test " + vector.name + ": '" + err.message + "'"); - }); - }, vector.name + " round trip"); - - }, function(err) { - // We need a failed test if the importVectorKey operation fails, so - // we know we never tested signing or verifying - promise_test(function(test) { - assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " round trip"); - }); - - all_promises.push(promise); - }); - - // Test signing with the wrong algorithm - testVectors.forEach(function(vector) { - // Want to get the key for the wrong algorithm - var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) - .then(function(wrongKey) { - var algorithm = {name: vector.algorithmName}; - return importVectorKeys(vector, ["verify"], ["sign"]) - .then(function(vectors) { - promise_test(function(test) { - var operation = subtle.sign(algorithm, wrongKey, vector.data) - .then(function(signature) { - assert_unreached("Signing should not have succeeded for " + vector.name); - }, function(err) { - assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); - }); - - return operation; - }, vector.name + " signing with wrong algorithm name"); - - }, function(err) { - // We need a failed test if the importVectorKey operation fails, so - // we know we never tested verification. - promise_test(function(test) { - assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " signing with wrong algorithm name"); - }); - }, function(err) { - promise_test(function(test) { - assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); - }, "generate wrong key step: " + vector.name + " signing with wrong algorithm name"); - }); - - all_promises.push(promise); - }); - - // Test verification with the wrong algorithm - testVectors.forEach(function(vector) { - // Want to get the key for the wrong algorithm - var promise = subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) - .then(function(wrongKey) { - return importVectorKeys(vector, ["verify"], ["sign"]) - .then(function(vectors) { - var algorithm = {name: vector.algorithmName}; - promise_test(function(test) { - var operation = subtle.verify(algorithm, wrongKey, vector.signature, vector.data) - .then(function(signature) { - assert_unreached("Verifying should not have succeeded for " + vector.name); - }, function(err) { - assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); - }); - - return operation; - }, vector.name + " verifying with wrong algorithm name"); - - }, function(err) { - // We need a failed test if the importVectorKey operation fails, so - // we know we never tested verification. - promise_test(function(test) { - assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " verifying with wrong algorithm name"); - }); - }, function(err) { - promise_test(function(test) { - assert_unreached("Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); - }, "generate wrong key step: " + vector.name + " verifying with wrong algorithm name"); - }); - - all_promises.push(promise); - }); - - // Test verification fails with wrong signature - testVectors.forEach(function(vector) { - var promise = importVectorKeys(vector, ["verify"], ["sign"]) - .then(function(vectors) { - var algorithm = {name: vector.algorithmName}; + [isVerified] = await Promise.all([ + subtle.verify(algorithm, key, vector.signature, data), + data[0] = 255 - data[0] + ]); + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_true(isVerified, "Signature verified"); + }, vector.name + " with altered data after call"); + + // Check for failures due to using privateKey to verify. + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("pkcs8", vector.privateKeyBuffer, algorithm, false, ["sign"]); + isVerified = await subtle.verify(algorithm, key, vector.signature, vector.data) + assert_unreached("Should have thrown error for using privateKey to verify in " + vector.name); + } catch (err) { + if (err instanceof AssertionError) + throw err; + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }; + assert_false(isVerified, "Signature verified"); + }, vector.name + " using privateKey to verify"); + + // Check for failures due to using publicKey to sign. + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + let signature = await subtle.sign(algorithm, key, vector.data); + assert_unreached("Should have thrown error for using publicKey to sign in " + vector.name); + } catch (err) { + if (err instanceof AssertionError) + throw err; + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }; + }, vector.name + " using publicKey to sign"); + + // Check for failures due to no "verify" usage. + promise_test(async() => { + let isVerified = false; + let key; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, []); + isVerified = await subtle.verify(algorithm, key, vector.signature, vector.data) + assert_unreached("Should have thrown error for no verify usage in " + vector.name); + } catch (err) { + if (err instanceof AssertionError) + throw err; + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_equals(err.name, "InvalidAccessError", "Should throw InvalidAccessError instead of '" + err.message + "'"); + }; + assert_false(isVerified, "Signature verified"); + }, vector.name + " no verify usage"); + + // Check for successful signing and verification. + var algorithm = {name: vector.algorithmName}; + promise_test(async() => { + let isVerified = false; + let privateKey, publicKey; + let signature; + try { + privateKey = await subtle.importKey("pkcs8", vector.privateKeyBuffer, algorithm, false, ["sign"]); + publicKey = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + signature = await subtle.sign(algorithm, privateKey, vector.data); + isVerified = await subtle.verify(algorithm, publicKey, vector.signature, vector.data) + } catch (err) { + assert_false(publicKey === undefined || privateKey === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_false(signature === undefined, "sign error for test " + vector.name + ": '" + err.message + "'"); + assert_unreached("verify error for test " + vector.name + ": " + err.message + "'"); + }; + assert_true(isVerified, "Round trip verification works"); + }, vector.name + " round trip"); + + // Test signing with the wrong algorithm + var algorithm = {name: vector.algorithmName}; + promise_test(async() => { + let wrongKey; + try { + wrongKey = await subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) + let signature = await subtle.sign(algorithm, wrongKey, vector.data); + assert_unreached("Signing should not have succeeded for " + vector.name); + } catch (err) { + if (err instanceof AssertionError) + throw err; + assert_false(wrongKey === undefined, "Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); + assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); + }; + }, vector.name + " signing with wrong algorithm name"); + + // Test verification with the wrong algorithm + var algorithm = {name: vector.algorithmName}; + promise_test(async() => { + let wrongKey; + try { + wrongKey = await subtle.generateKey({name: "HMAC", hash: "SHA-1"}, false, ["sign", "verify"]) + let isVerified = await subtle.verify(algorithm, wrongKey, vector.signature, vector.data) + assert_unreached("Verifying should not have succeeded for " + vector.name); + } catch (err) { + if (err instanceof AssertionError) + throw err; + assert_false(wrongKey === undefined, "Generate wrong key for test " + vector.name + " failed: '" + err.message + "'"); + assert_equals(err.name, "InvalidAccessError", "Should have thrown InvalidAccessError instead of '" + err.message + "'"); + }; + }, vector.name + " verifying with wrong algorithm name"); + + // Test verification fails with wrong signature + var algorithm = {name: vector.algorithmName}; + promise_test(async() => { + let key; + let isVerified = true; var signature = copyBuffer(vector.signature); signature[0] = 255 - signature[0]; - promise_test(function(test) { - var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.data) - .then(function(is_verified) { - assert_false(is_verified, "Signature NOT verified"); - }, function(err) { - assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); - }); - - return operation; - }, vector.name + " verification failure due to altered signature"); - - }, function(err) { - // We need a failed test if the importVectorKey operation fails, so - // we know we never tested verification. - promise_test(function(test) { - assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " verification failure due to altered signature"); - }); - - all_promises.push(promise); - }); - - // Test verification fails with short (odd length) signature - testVectors.forEach(function(vector) { - var promise = importVectorKeys(vector, ["verify"], ["sign"]) - .then(function(vectors) { - var algorithm = {name: vector.algorithmName}; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + isVerified = await subtle.verify(algorithm, key, signature, vector.data) + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_false(isVerified, "Signature verified"); + }, vector.name + " verification failure due to altered signature"); + + // Test verification fails with short (odd length) signature + promise_test(async() => { + let key; + let isVerified = true; var signature = vector.signature.slice(1); // Skip the first byte - promise_test(function(test) { - var operation = subtle.verify(algorithm, vector.publicKey, signature, vector.data) - .then(function(is_verified) { - assert_false(is_verified, "Signature NOT verified"); - }, function(err) { - assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); - }); - - return operation; - }, vector.name + " verification failure due to shortened signature"); - - }, function(err) { - // We need a failed test if the importVectorKey operation fails, so - // we know we never tested verification. - promise_test(function(test) { - assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " verification failure due to shortened signature"); - }); - - all_promises.push(promise); - }); - - // Test verification fails with wrong data - testVectors.forEach(function(vector) { - var promise = importVectorKeys(vector, ["verify"], ["sign"]) - .then(function(vectors) { - var algorithm = {name: vector.algorithmName}; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + isVerified = await subtle.verify(algorithm, key, signature, vector.data) + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_false(isVerified, "Signature verified"); + }, vector.name + " verification failure due to shortened signature"); + + // Test verification fails with wrong data + promise_test(async() => { + let key; + let isVerified = true; var data = copyBuffer(vector.data); data[0] = 255 - data[0]; - promise_test(function(test) { - var operation = subtle.verify(algorithm, vector.publicKey, vector.signature, data) - .then(function(is_verified) { - assert_false(is_verified, "Signature NOT verified"); - }, function(err) { - assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); - }); - - return operation; - }, vector.name + " verification failure due to altered data"); - - }, function(err) { - // We need a failed test if the importVectorKey operation fails, so - // we know we never tested verification. - promise_test(function(test) { - assert_unreached("importVectorKeys failed for " + vector.name + ". Message: ''" + err.message + "''"); - }, "importVectorKeys step: " + vector.name + " verification failure due to altered data"); - }); - - all_promises.push(promise); - }); - - - promise_test(function() { - return Promise.all(all_promises) - .then(function() {done();}) - .catch(function() {done();}) - }, "setup"); - - // Test that generated keys are valid for signing and verifying. - testVectors.forEach(function(vector) { - var algorithm = {name: vector.algorithmName}; + try { + key = await subtle.importKey("spki", vector.publicKeyBuffer, algorithm, false, ["verify"]); + isVerified = await subtle.verify(algorithm, key, vector.signature, data) + } catch (err) { + assert_false(key === undefined, "importKey failed for " + vector.name + ". Message: ''" + err.message + "''"); + assert_unreached("Verification should not throw error " + vector.name + ": " + err.message + "'"); + }; + assert_false(isVerified, "Signature verified"); + }, vector.name + " verification failure due to altered data"); + + // Test that generated keys are valid for signing and verifying. promise_test(async() => { let key = await subtle.generateKey(algorithm, false, ["sign", "verify"]); let signature = await subtle.sign(algorithm, key.privateKey, vector.data); @@ -371,59 +222,21 @@ function run_test() { Object.keys(kSmallOrderTestCases).forEach(function (algorithmName) { var algorithm = {name: algorithmName}; kSmallOrderTestCases[algorithmName].forEach(function(test) { - // Test low-order public keys promise_test(async() => { let isVerified = true; let publicKey; try { - publicKey = await subtle.importKey("raw", test.keyData, - algorithm, - false, ["verify"]) - isVerified = await subtle.verify(algorithmName, publicKey, test.signature, test.message); + publicKey = await subtle.importKey("raw", test.keyData, algorithm, false, ["verify"]) + isVerified = await subtle.verify(algorithm, publicKey, test.signature, test.message); } catch (err) { - assert_equals(isVerified, test.verified, "Signature verification result."); + assert_true(publicKey !== undefined, "Public key should be valid."); assert_unreached("The operation shouldn't fail, but it thown this error: " + err.name + ": " + err.message + "."); } - assert_false(isVerified, "Signature verification result."); + assert_equals(isVerified, test.verified, "Signature verification result."); }, algorithmName + " Verification checks with small-order key of order - Test " + test.id); }); }); - // A test vector has all needed fields for signing and verifying, EXCEPT that the - // key field may be null. This function replaces that null with the Correct - // CryptoKey object. - // - // Returns a Promise that yields an updated vector on success. - function importVectorKeys(vector, publicKeyUsages, privateKeyUsages) { - var publicPromise, privatePromise; - - if (vector.publicKey !== null) { - publicPromise = new Promise(function(resolve, reject) { - resolve(vector); - }); - } else { - publicPromise = subtle.importKey(vector.publicKeyFormat, vector.publicKeyBuffer, {name: vector.algorithmName}, false, publicKeyUsages) - .then(function(key) { - vector.publicKey = key; - return vector; - }); // Returns a copy of the sourceBuffer it is sent. - } - - if (vector.privateKey !== null) { - privatePromise = new Promise(function(resolve, reject) { - resolve(vector); - }); - } else { - privatePromise = subtle.importKey(vector.privateKeyFormat, vector.privateKeyBuffer, {name: vector.algorithmName}, false, privateKeyUsages) - .then(function(key) { - vector.privateKey = key; - return vector; - }); - } - - return Promise.all([publicPromise, privatePromise]); - } - // Returns a copy of the sourceBuffer it is sent. function copyBuffer(sourceBuffer) { var source = new Uint8Array(sourceBuffer); diff --git a/testing/web-platform/tests/accname/name/comp_label.html b/testing/web-platform/tests/accname/name/comp_label.html index 3eda2a5367..373940c04d 100644 --- a/testing/web-platform/tests/accname/name/comp_label.html +++ b/testing/web-platform/tests/accname/name/comp_label.html @@ -154,13 +154,6 @@ <textarea aria-label="label" data-expectedlabel="label" data-testname="label valid on textarea element" class="ex">x</textarea> <ul aria-label="label" data-expectedlabel="label" data-testname="label valid on list (unordered) element" class="ex">x</ul> -<h2>Undefined aria-label tests</h2> -<img alt="alt" aria-label="undefined" data-expectedlabel="alt" data-testname="aria-label undefined on img w/ alt" class="ex" /> -<img aria-label="undefined" data-expectedlabel="" data-testname="aria-label undefined on img w/o alt" class="ex" /> -<img alt="" aria-label="undefined" data-expectedlabel="" data-testname="aria-label undefined on img w/ empty alt" class="ex" /> -<img aria-label="undefined" data-expectedlabel="title" data-testname="aria-label undefined on img w/o alt but w/ title" class="ex" /> -<img alt="" aria-label="undefined" data-expectedlabel="title" data-testname="aria-label undefined on img w/ empty alt but w/ title" class="ex" /> - <h2>Name computation precedence tests</h2> <!-- Name computation: https://w3c.github.io/accname/#computation-steps --> @@ -179,9 +172,9 @@ x x </button> -<button aria-labelledby="span4" aria-label="foo" data-expectedlabel="foo" data-testname="button's hidden referenced name (visibility:hidden) with hidden aria-labelledby traversal falls back to aria-label" class="ex"> - <span id="span4"> - <span id="span5" style="visibility:hidden;">label</span> +<button aria-labelledby="span5" aria-label="foo" data-expectedlabel="foo" data-testname="button's hidden referenced name (visibility:hidden) with hidden aria-labelledby traversal falls back to aria-label" class="ex"> + <span id="span5"> + <span id="span6" style="visibility:hidden;">label</span> </span> x </button> @@ -193,8 +186,8 @@ x </span> <!-- Step 2B: LabelledBy supercedes 2D: AriaLabel, also see wpt/accname/name/comp_labelledby.html --> -<a href="#" aria-labelledby="span6" aria-label="foo" data-expectedlabel="label" data-testname="link's aria-labelledby name supercedes aria-label" class="ex">x</a> -<span id="span6">label</span> +<a href="#" aria-labelledby="span7" aria-label="foo" data-expectedlabel="label" data-testname="link's aria-labelledby name supercedes aria-label" class="ex">x</a> +<span id="span7">label</span> <!-- Step 2C: Embedded Control labelling supercedes 2D: AriaLabel, see wpt/accname/name/comp_embedded_control.html --> diff --git a/testing/web-platform/tests/audio-output/setSinkId-with-selectAudioOutput.https.html b/testing/web-platform/tests/audio-output/setSinkId-with-selectAudioOutput.https.html index dbe32e2606..a78d91dd12 100644 --- a/testing/web-platform/tests/audio-output/setSinkId-with-selectAudioOutput.https.html +++ b/testing/web-platform/tests/audio-output/setSinkId-with-selectAudioOutput.https.html @@ -29,7 +29,7 @@ promise_test(async t => { assert_equals(r, undefined, "resetting sinkid on default audio output should always work"); }, "setSinkId() after selectAudioOutput()"); -const src = "/media/movie_5.ogv"; +const src = "/media/movie_5.webm"; promise_test(async t => { assert_not_equals(deviceId, undefined, "selectAudioOutput() resolved"); diff --git a/testing/web-platform/tests/bluetooth/WEB_FEATURES.yml b/testing/web-platform/tests/bluetooth/WEB_FEATURES.yml new file mode 100644 index 0000000000..9b01ed7dae --- /dev/null +++ b/testing/web-platform/tests/bluetooth/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: web-bluetooth + files: "**" diff --git a/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-fail.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-fail.tentative.https.html index 8b1b42ec78..305222a58f 100644 --- a/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-fail.tentative.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-fail.tentative.https.html @@ -12,7 +12,7 @@ 'use strict'; promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantWritePermission(); const customFormatArray = []; const customFormatMap = {}; @@ -28,7 +28,7 @@ promise_test(async t => { }, 'navigator.clipboard.write() fails for more than 100 custom formats'); promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantWritePermission(); const format1 = 'application/x-custom-format-clipboard-test-format-1'; const format2 = 'application/x-custom-format-clipboard-test-format-2'; @@ -42,7 +42,7 @@ promise_test(async t => { }, 'navigator.clipboard.write() fails for custom formats without web prefix'); promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantWritePermission(); const format1 = 'web '; const format2 = 'web a'; @@ -56,7 +56,7 @@ promise_test(async t => { }, 'navigator.clipboard.write() fails for custom formats with web prefix, but invalid MIME types'); promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantWritePermission(); const format1 = 'web text/plain'; const format2 = 'text/custom'; @@ -69,7 +69,7 @@ promise_test(async t => { }, 'navigator.clipboard.write() fails for custom format with web prefix, but different Blob type'); promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantWritePermission(); const format1 = 'web Text/plain'; const format2 = 'text/plain'; @@ -82,7 +82,7 @@ promise_test(async t => { }, 'navigator.clipboard.write() fails for custom format with different case than the Blob type'); promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantWritePermission(); const format1 = 'web text/plain'; const format2 = 'Text/plain'; @@ -95,7 +95,7 @@ promise_test(async t => { }, 'navigator.clipboard.write() fails for invalid mime type that is different than the Blob type'); promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantWritePermission(); const format1 = 'web Text/plain'; const format2 = 'web text/plain'; @@ -108,7 +108,7 @@ promise_test(async t => { }, 'navigator.clipboard.write() fails for invalid mime type with web prefix and the Blob type'); promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantWritePermission(); const format1 = 'Text/plain'; const format2 = 'text/plain'; diff --git a/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-web-prefix.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-web-prefix.tentative.https.html index 9a6e5da6ff..a5ee7c5bf0 100644 --- a/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-web-prefix.tentative.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-web-prefix.tentative.https.html @@ -12,8 +12,8 @@ 'use strict'; promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const format1 = 'web text/plain'; const format2 = 'web text/plain'; diff --git a/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-without-web-prefix.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-without-web-prefix.tentative.https.html index 8b9d4de0a1..e20b66b551 100644 --- a/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-without-web-prefix.tentative.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read-without-web-prefix.tentative.https.html @@ -12,8 +12,8 @@ 'use strict'; promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const format1 = 'web text/plain'; const format2 = 'text/plain'; diff --git a/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read.tentative.https.html index a005f8c98f..d174df6749 100644 --- a/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read.tentative.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-custom-formats-write-read.tentative.https.html @@ -12,8 +12,8 @@ 'use strict'; promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const format1 = 'web application/x-custom-format-clipboard-test-format-1'; const format2 = 'web application/x-custom-format-clipboard-test-format-2'; const blobInput1 = new Blob(['input data 1'], {type: format1}); diff --git a/testing/web-platform/tests/clipboard-apis/async-html-script-removal.https.html b/testing/web-platform/tests/clipboard-apis/async-html-script-removal.https.html index 44c11add85..9ec9a37356 100644 --- a/testing/web-platform/tests/clipboard-apis/async-html-script-removal.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-html-script-removal.https.html @@ -34,8 +34,8 @@ const html_with_script = const html_without_script = '<title>Title of the document</title> <p>Hello World</p>'; promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const blobInput = new Blob([html_with_script], {type: 'text/html'}); const clipboardItem = new ClipboardItem({'text/html': blobInput}); await waitForUserActivation(); diff --git a/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-basics.https.html b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-basics.https.html index 4a11d5ac66..5d6f701bdb 100644 --- a/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-basics.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-basics.https.html @@ -13,8 +13,8 @@ // Permissions are required in order to invoke navigator.clipboard functions in // an automated test. async function getPermissions() { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); await waitForUserActivation(); } diff --git a/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-resource-load.https.html b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-resource-load.https.html index d1e3019e7f..25a90a2d08 100644 --- a/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-resource-load.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-resource-load.https.html @@ -26,7 +26,7 @@ promise_test(async test => { let loadObserved = false; const observer = new PerformanceObserver(() => loadObserved = true); observer.observe({type: 'resource'}); - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); + await tryGrantReadPermission(); await test_driver.click(button); await waitForUserActivation(); diff --git a/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html index cc18367534..4f8463cd7c 100644 --- a/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-read-sanitize.https.html @@ -28,7 +28,7 @@ document.oncopy = ev => { }; promise_test(async test => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); + await tryGrantReadPermission(); await test_driver.click(button); await waitForUserActivation(); diff --git a/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-write-multiple.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-write-multiple.tentative.https.sub.html index 73cdd2f049..c310203503 100644 --- a/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-write-multiple.tentative.https.sub.html +++ b/testing/web-platform/tests/clipboard-apis/async-navigator-clipboard-write-multiple.tentative.https.sub.html @@ -18,8 +18,8 @@ // Permissions are required in order to invoke navigator.clipboard functions in // an automated test. async function getPermissions() { - await test_driver.set_permission({name: "clipboard-read"}, "granted"); - await test_driver.set_permission({name: "clipboard-write"}, "granted"); + await tryGrantReadPermission(); + await tryGrantWritePermission() await waitForUserActivation(); } diff --git a/testing/web-platform/tests/clipboard-apis/async-promise-write-blobs-read-blobs.https.html b/testing/web-platform/tests/clipboard-apis/async-promise-write-blobs-read-blobs.https.html index 12184c92e0..6b79893160 100644 --- a/testing/web-platform/tests/clipboard-apis/async-promise-write-blobs-read-blobs.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-promise-write-blobs-read-blobs.https.html @@ -21,8 +21,8 @@ promise_test(async t => { const promise1 = new Promise((resolve, reject) => { resolve(loadBlob('resources/greenbox.png')); }); - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const blobText = new Blob(['test text'], {type: 'text/plain'}); diff --git a/testing/web-platform/tests/clipboard-apis/async-unsanitized-html-formats-write-read.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-unsanitized-html-formats-write-read.tentative.https.html index 46e335c804..848900830f 100644 --- a/testing/web-platform/tests/clipboard-apis/async-unsanitized-html-formats-write-read.tentative.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-unsanitized-html-formats-write-read.tentative.https.html @@ -27,8 +27,8 @@ function reformatHtml(html) { // Writes a payload with custom content and checks to ensure the correct data // was written successfully. promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); // Create and write unsanitized version of standard HTML and custom formats. const format1 = 'text/html'; diff --git a/testing/web-platform/tests/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html index 1c5638ca0a..dea91b41d7 100644 --- a/testing/web-platform/tests/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-unsanitized-plaintext-formats-write-read.tentative.https.html @@ -14,8 +14,8 @@ // Writes a payload with custom content and checks to ensure the correct data // was written successfully. promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const dataToWrite = 'Test text.'; const format1 = 'web text/plain'; diff --git a/testing/web-platform/tests/clipboard-apis/async-unsanitized-standard-html-read-fail.tentative.https.html b/testing/web-platform/tests/clipboard-apis/async-unsanitized-standard-html-read-fail.tentative.https.html index e7ddbb0cdf..f0884d6c20 100644 --- a/testing/web-platform/tests/clipboard-apis/async-unsanitized-standard-html-read-fail.tentative.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-unsanitized-standard-html-read-fail.tentative.https.html @@ -12,7 +12,7 @@ 'use strict'; promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); + await tryGrantReadPermission(); await waitForUserActivation(); await promise_rejects_dom(t, 'NotAllowedError', @@ -20,7 +20,7 @@ promise_test(async t => { }, 'navigator.clipboard.read() fails for multiple unsanitized formats requested.'); promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); + await tryGrantReadPermission(); await waitForUserActivation(); await promise_rejects_dom(t, 'NotAllowedError', @@ -28,7 +28,7 @@ promise_test(async t => { }, 'navigator.clipboard.read() fails for unsanitized text/plain requested.'); promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); + await tryGrantReadPermission(); await waitForUserActivation(); await promise_rejects_dom(t, 'NotAllowedError', @@ -36,11 +36,11 @@ promise_test(async t => { }, 'navigator.clipboard.read() fails for unsanitized image/png requested.'); promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); + await tryGrantReadPermission(); await waitForUserActivation(); await promise_rejects_dom(t, 'NotAllowedError', navigator.clipboard.read({unsanitized: ['image/svg+xml']})); }, 'navigator.clipboard.read() fails for unsanitized image/svg+xml requested.'); -</script>
\ No newline at end of file +</script> diff --git a/testing/web-platform/tests/clipboard-apis/async-write-blobs-read-blobs.https.html b/testing/web-platform/tests/clipboard-apis/async-write-blobs-read-blobs.https.html index 8bec558b2b..d89640d58a 100644 --- a/testing/web-platform/tests/clipboard-apis/async-write-blobs-read-blobs.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-write-blobs-read-blobs.https.html @@ -18,8 +18,8 @@ async function loadBlob(fileName) { } promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const blobText = new Blob(['test text'], {type: 'text/plain'}); const blobImage = await loadBlob('resources/greenbox.png'); diff --git a/testing/web-platform/tests/clipboard-apis/async-write-html-read-html.https.html b/testing/web-platform/tests/clipboard-apis/async-write-html-read-html.https.html index ec1817c027..2573916bd6 100644 --- a/testing/web-platform/tests/clipboard-apis/async-write-html-read-html.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-write-html-read-html.https.html @@ -26,8 +26,8 @@ function reformatHtml(html) { } async function readWriteTest(textInput) { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const blobInput = new Blob([textInput], {type: 'text/html'}); const clipboardItem = new ClipboardItem({'text/html': blobInput}); await waitForUserActivation(); diff --git a/testing/web-platform/tests/clipboard-apis/async-write-image-read-image.https.html b/testing/web-platform/tests/clipboard-apis/async-write-image-read-image.https.html index e10b69d824..a8aa7d457c 100644 --- a/testing/web-platform/tests/clipboard-apis/async-write-image-read-image.https.html +++ b/testing/web-platform/tests/clipboard-apis/async-write-image-read-image.https.html @@ -42,8 +42,8 @@ async function loadBlob(fileName) { } promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const blobInput = await loadBlob('resources/greenbox.png'); @@ -71,8 +71,8 @@ promise_test(async t => { }, 'Verify write and read clipboard [image/png Blob]'); promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const invalidPngBlob = new Blob(['this text is not a valid png image'], {type: 'image/png'}); diff --git a/testing/web-platform/tests/clipboard-apis/detached-iframe/read-on-detaching-iframe.https.html b/testing/web-platform/tests/clipboard-apis/detached-iframe/read-on-detaching-iframe.https.html index 8e8e015aa0..827c17a9e3 100644 --- a/testing/web-platform/tests/clipboard-apis/detached-iframe/read-on-detaching-iframe.https.html +++ b/testing/web-platform/tests/clipboard-apis/detached-iframe/read-on-detaching-iframe.https.html @@ -6,6 +6,7 @@ <script src="/resources/testharnessreport.js"></script> <script src="/resources/testdriver.js"></script> <script src="/resources/testdriver-vendor.js"></script> +<script src="../resources/user-activation.js"></script> <iframe id="iframe"></iframe> <script> 'use strict'; @@ -14,8 +15,8 @@ promise_test(async t => { // This tests proper behavior on a detaching iframe. text/plain is chosen for // simplicity, and the test should fail the same way no matter what the input // type is. - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const iframe = document.getElementById('iframe'); const iframeClipboard = iframe.contentWindow.navigator.clipboard; diff --git a/testing/web-platform/tests/clipboard-apis/detached-iframe/write-on-detaching-iframe.https.html b/testing/web-platform/tests/clipboard-apis/detached-iframe/write-on-detaching-iframe.https.html index c6913d9e9f..b3836799df 100644 --- a/testing/web-platform/tests/clipboard-apis/detached-iframe/write-on-detaching-iframe.https.html +++ b/testing/web-platform/tests/clipboard-apis/detached-iframe/write-on-detaching-iframe.https.html @@ -6,6 +6,7 @@ <script src="/resources/testharnessreport.js"></script> <script src="/resources/testdriver.js"></script> <script src="/resources/testdriver-vendor.js"></script> +<script src="../resources/user-activation.js"></script> <iframe id="iframe"></iframe> <script> 'use strict'; @@ -14,8 +15,8 @@ promise_test(async t => { // This tests proper behavior on a detaching iframe. text/plain is chosen for // simplicity, and the test should fail the same way no matter what the input // type is. - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const iframe = document.getElementById('iframe'); const iframeClipboard = iframe.contentWindow.navigator.clipboard; diff --git a/testing/web-platform/tests/clipboard-apis/detached-iframe/write-read-on-detached-iframe.https.html b/testing/web-platform/tests/clipboard-apis/detached-iframe/write-read-on-detached-iframe.https.html index b21e6b20bc..155f9d004a 100644 --- a/testing/web-platform/tests/clipboard-apis/detached-iframe/write-read-on-detached-iframe.https.html +++ b/testing/web-platform/tests/clipboard-apis/detached-iframe/write-read-on-detached-iframe.https.html @@ -15,8 +15,8 @@ promise_test(async t => { // This tests proper behavior on a detaching iframe. text/plain is chosen for // simplicity, and the test should fail the same way no matter what the input // type is. - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const iframe = document.getElementById('iframe'); const iframeClipboard = iframe.contentWindow.navigator.clipboard; diff --git a/testing/web-platform/tests/clipboard-apis/detached-iframe/writeText-readText-on-detached-iframe.https.html b/testing/web-platform/tests/clipboard-apis/detached-iframe/writeText-readText-on-detached-iframe.https.html index 24fa586fc7..8c6448d5cc 100644 --- a/testing/web-platform/tests/clipboard-apis/detached-iframe/writeText-readText-on-detached-iframe.https.html +++ b/testing/web-platform/tests/clipboard-apis/detached-iframe/writeText-readText-on-detached-iframe.https.html @@ -12,8 +12,8 @@ 'use strict'; promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const iframe = document.getElementById('iframe'); await waitForUserActivation(); diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html index 7af2b8944e..8ca1819df8 100644 --- a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html +++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-disabled-by-feature-policy.tentative.https.sub.html @@ -5,6 +5,7 @@ <script src="/resources/testdriver.js"></script> <script src="/resources/testdriver-vendor.js"></script> <script src="/feature-policy/resources/featurepolicy.js"></script> +<script src="../../resources/user-activation.js"></script> <script> 'use strict'; @@ -14,7 +15,7 @@ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src; promise_test(async t => { - await test_driver.set_permission({ name: 'clipboard-read' }, 'granted'); + await tryGrantReadPermission(); return promise_rejects_dom(t, 'NotAllowedError', navigator.clipboard.readText('test text')); }, 'Feature-Policy header clipboard-read "none" disallows the top-level document.'); diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html index 552183cc67..c21d6cf0f6 100644 --- a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html +++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-by-feature-policy.tentative.https.sub.html @@ -15,7 +15,7 @@ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src; promise_test(async t => { - await test_driver.set_permission({ name: 'clipboard-read' }, 'granted'); + await tryGrantReadPermission(); await waitForUserActivation(); await navigator.clipboard.readText('test text'); }, 'Feature-Policy header clipboard-read "*" allows the top-level document.'); diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html index 17dc3628a7..79dd6cf891 100644 --- a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html +++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-read/clipboard-read-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html @@ -15,7 +15,7 @@ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src; promise_test(async t => { - await test_driver.set_permission({ name: 'clipboard-read' }, 'granted'); + await tryGrantReadPermission(); await waitForUserActivation(); await navigator.clipboard.readText('test text'); }, 'Feature-Policy header clipboard-read "self" allows the top-level document.'); diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html index 5d19d8dd6f..b328debde2 100644 --- a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html +++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-disabled-by-feature-policy.tentative.https.sub.html @@ -5,6 +5,7 @@ <script src="/resources/testdriver.js"></script> <script src="/resources/testdriver-vendor.js"></script> <script src="/feature-policy/resources/featurepolicy.js"></script> +<script src="../../resources/user-activation.js"></script> <script> 'use strict'; @@ -14,7 +15,7 @@ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src; promise_test(async t => { - await test_driver.set_permission({ name: 'clipboard-write' }, 'granted'); + await tryGrantWritePermission(); return promise_rejects_dom(t, 'NotAllowedError', navigator.clipboard.writeText('test text')); }, 'Feature-Policy header clipboard-write "none" disallows the top-level document.'); diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html index ca97994c61..cb908df65f 100644 --- a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html +++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-by-feature-policy.tentative.https.sub.html @@ -15,7 +15,7 @@ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src; promise_test(async t => { - await test_driver.set_permission({ name: 'clipboard-write' }, 'granted'); + await tryGrantWritePermission(); await waitForUserActivation(); await navigator.clipboard.writeText('test text'); }, 'Feature-Policy header clipboard-write "*" allows the top-level document.'); diff --git a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html index 5615a68ac5..532c72545c 100644 --- a/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html +++ b/testing/web-platform/tests/clipboard-apis/feature-policy/clipboard-write/clipboard-write-enabled-on-self-origin-by-feature-policy.tentative.https.sub.html @@ -15,7 +15,7 @@ const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' + same_origin_src; promise_test(async t => { - await test_driver.set_permission({ name: 'clipboard-write' }, 'granted'); + await tryGrantWritePermission(); await waitForUserActivation(); await navigator.clipboard.writeText('test text'); }, 'Feature-Policy header clipboard-write "self" allows the top-level document.'); diff --git a/testing/web-platform/tests/clipboard-apis/permissions/readText-denied.https.html b/testing/web-platform/tests/clipboard-apis/permissions/readText-denied.https.html index 010f4ba21b..379b0ff18b 100644 --- a/testing/web-platform/tests/clipboard-apis/permissions/readText-denied.https.html +++ b/testing/web-platform/tests/clipboard-apis/permissions/readText-denied.https.html @@ -12,7 +12,7 @@ 'use strict'; promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'denied'); + await trySetPermission('clipboard-read', 'denied'); await waitForUserActivation(); await promise_rejects_dom(t, 'NotAllowedError', navigator.clipboard.readText()); diff --git a/testing/web-platform/tests/clipboard-apis/permissions/readText-granted.https.html b/testing/web-platform/tests/clipboard-apis/permissions/readText-granted.https.html index e912bd64a8..0b2d7034df 100644 --- a/testing/web-platform/tests/clipboard-apis/permissions/readText-granted.https.html +++ b/testing/web-platform/tests/clipboard-apis/permissions/readText-granted.https.html @@ -12,8 +12,8 @@ 'use strict'; promise_test(async () => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); + await tryGrantReadPermission(); await waitForUserActivation(); await navigator.clipboard.readText(); }, 'navigator.clipboard.readText() succeeds when permission granted'); -</script>
\ No newline at end of file +</script> diff --git a/testing/web-platform/tests/clipboard-apis/permissions/writeText-denied.https.html b/testing/web-platform/tests/clipboard-apis/permissions/writeText-denied.https.html index 5fbcab4117..3d6b4397a0 100644 --- a/testing/web-platform/tests/clipboard-apis/permissions/writeText-denied.https.html +++ b/testing/web-platform/tests/clipboard-apis/permissions/writeText-denied.https.html @@ -12,7 +12,7 @@ 'use strict'; promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-write'}, 'denied'); + await trySetPermission('clipboard-write', 'denied'); await waitForUserActivation(); await promise_rejects_dom(t, 'NotAllowedError', navigator.clipboard.writeText('xyz')); diff --git a/testing/web-platform/tests/clipboard-apis/permissions/writeText-granted.https.html b/testing/web-platform/tests/clipboard-apis/permissions/writeText-granted.https.html index ff347b7add..c0307811dc 100644 --- a/testing/web-platform/tests/clipboard-apis/permissions/writeText-granted.https.html +++ b/testing/web-platform/tests/clipboard-apis/permissions/writeText-granted.https.html @@ -12,8 +12,8 @@ 'use strict'; promise_test(async () => { - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantWritePermission(); await waitForUserActivation(); await navigator.clipboard.writeText('xyz'); }, 'navigator.clipboard.writeText() succeeds when permission granted'); -</script>
\ No newline at end of file +</script> diff --git a/testing/web-platform/tests/clipboard-apis/resources/page.html b/testing/web-platform/tests/clipboard-apis/resources/page.html index 35bde8e501..fc8bd895d1 100644 --- a/testing/web-platform/tests/clipboard-apis/resources/page.html +++ b/testing/web-platform/tests/clipboard-apis/resources/page.html @@ -9,8 +9,8 @@ window.addEventListener("message", async (e) => { if (e.data && e.data[0] == "write") { test_driver.set_test_context(window.parent); - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); await waitForUserActivation(); await navigator.clipboard.write([ new ClipboardItem({ diff --git a/testing/web-platform/tests/clipboard-apis/resources/user-activation.js b/testing/web-platform/tests/clipboard-apis/resources/user-activation.js index ed294bb9cb..4535f8c6d7 100644 --- a/testing/web-platform/tests/clipboard-apis/resources/user-activation.js +++ b/testing/web-platform/tests/clipboard-apis/resources/user-activation.js @@ -23,3 +23,22 @@ async function waitForUserActivation() { test_driver.click(document.body); await clickedPromise; } + +async function trySetPermission(perm, state) { + try { + await test_driver.set_permission({ name: perm }, state) + } catch { + // This is expected, as clipboard permissions are not supported by every engine + // and also the set_permission. The permission is not required by such engines as + // they require user activation instead. + } +} + +async function tryGrantReadPermission() { + await trySetPermission("clipboard-read", "granted"); +} + +async function tryGrantWritePermission() { + await trySetPermission("clipboard-write", "granted"); +} + diff --git a/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-read.https.html b/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-read.https.html index c46e5d4317..516783edae 100644 --- a/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-read.https.html +++ b/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-read.https.html @@ -14,8 +14,8 @@ <script> async function readWriteTest(textInput) { promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const blobInput = new Blob([textInput], {type: 'text/plain'}); const clipboardItemInput = new ClipboardItem({'text/plain': blobInput}); diff --git a/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-readText.https.html b/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-readText.https.html index 66969b1777..ab7b619ac9 100644 --- a/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-readText.https.html +++ b/testing/web-platform/tests/clipboard-apis/text-write-read/async-write-readText.https.html @@ -13,8 +13,8 @@ <script> async function readWriteTest(textInput) { promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); const blobInput = new Blob([textInput], {type: 'text/plain'}); const clipboardItem = new ClipboardItem({'text/plain': blobInput}); diff --git a/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-read.https.html b/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-read.https.html index ddf563269a..40f26d4786 100644 --- a/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-read.https.html +++ b/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-read.https.html @@ -13,8 +13,8 @@ <script> async function readWriteTest(textInput) { promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); await waitForUserActivation(); await navigator.clipboard.writeText(textInput); diff --git a/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-readText.https.html b/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-readText.https.html index 0defdf7a70..647a00e050 100644 --- a/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-readText.https.html +++ b/testing/web-platform/tests/clipboard-apis/text-write-read/async-writeText-readText.https.html @@ -11,8 +11,8 @@ <script> async function readWriteTest(textInput) { promise_test(async t => { - await test_driver.set_permission({name: 'clipboard-read'}, 'granted'); - await test_driver.set_permission({name: 'clipboard-write'}, 'granted'); + await tryGrantReadPermission(); + await tryGrantWritePermission(); await waitForUserActivation(); await navigator.clipboard.writeText(textInput); diff --git a/testing/web-platform/tests/common/media.js b/testing/web-platform/tests/common/media.js index 800593f534..a5a8e957e9 100644 --- a/testing/web-platform/tests/common/media.js +++ b/testing/web-platform/tests/common/media.js @@ -14,9 +14,6 @@ function getVideoURI(base) if (videotag.canPlayType('video/webm; codecs="vp9, opus"') ) { extension = '.webm'; - } else if ( videotag.canPlayType('video/ogg; codecs="theora, vorbis"') ) - { - extension = '.ogv'; } } @@ -52,7 +49,6 @@ function getMediaContentType(url) { var extension = new URL(url, location).pathname.split(".").pop(); var map = { "mp4" : "video/mp4", - "ogv" : "application/ogg", "webm": "video/webm", "mp3" : "audio/mp3", "oga" : "application/ogg", diff --git a/testing/web-platform/tests/compression/WEB_FEATURES.yml b/testing/web-platform/tests/compression/WEB_FEATURES.yml new file mode 100644 index 0000000000..c8b072631e --- /dev/null +++ b/testing/web-platform/tests/compression/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: compression-streams + files: "**" diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_basic.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_basic.https.any.js new file mode 100644 index 0000000000..15d572bd8e --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_basic.https.any.js @@ -0,0 +1,93 @@ +// META: timeout=long +// META: script=/resources/test-only-api.js +// META: script=resources/pressure-helpers.js +// META: global=window,dedicatedworker,sharedworker + +'use strict'; + +pressure_test((t, mockPressureService) => { + const observer = new PressureObserver(() => { + assert_unreached('The observer callback should not be called'); + }); + + mockPressureService.setExpectedFailure( + new DOMException('', 'NotSupportedError')); + return promise_rejects_dom(t, 'NotSupportedError', observer.observe('cpu')); +}, 'Return NotSupportedError when calling observer()'); + +pressure_test(async (t, mockPressureService) => { + const changes = await new Promise(resolve => { + const observer = new PressureObserver(resolve); + t.add_cleanup(() => observer.disconnect()); + observer.observe('cpu'); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + }); + assert_true(changes.length === 1); + assert_equals(changes[0].state, 'critical'); + assert_equals(changes[0].source, 'cpu'); + assert_equals(typeof changes[0].time, 'number'); +}, 'Basic functionality test'); + +pressure_test((t, mockPressureService) => { + const observer = new PressureObserver(() => { + assert_unreached('The observer callback should not be called'); + }); + + const promise = observer.observe('cpu'); + observer.unobserve('cpu'); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + + return promise_rejects_dom(t, 'AbortError', promise); +}, 'Removing observer before observe() resolves works'); + +pressure_test(async (t, mockPressureService) => { + const callbackPromises = []; + const observePromises = []; + + for (let i = 0; i < 2; i++) { + callbackPromises.push(new Promise(resolve => { + const observer = new PressureObserver(resolve); + t.add_cleanup(() => observer.disconnect()); + observePromises.push(observer.observe('cpu')); + })); + } + + await Promise.all(observePromises); + + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + + return Promise.all(callbackPromises); +}, 'Calling observe() multiple times works'); + +pressure_test(async (t, mockPressureService) => { + const observer1_changes = []; + await new Promise(resolve => { + const observer1 = new PressureObserver(changes => { + observer1_changes.push(changes); + resolve(); + }); + t.add_cleanup(() => observer1.disconnect()); + observer1.observe('cpu'); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + }); + assert_true(observer1_changes.length === 1); + assert_equals(observer1_changes[0][0].source, 'cpu'); + assert_equals(observer1_changes[0][0].state, 'critical'); + + const observer2_changes = []; + await new Promise(resolve => { + const observer2 = new PressureObserver(changes => { + observer2_changes.push(changes); + resolve(); + }); + t.add_cleanup(() => observer2.disconnect()); + observer2.observe('cpu'); + }); + assert_true(observer2_changes.length === 1); + assert_equals(observer2_changes[0][0].source, 'cpu'); + assert_equals(observer2_changes[0][0].state, 'critical'); +}, 'Starting a new observer after an observer has started works'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_basic.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_basic.tentative.https.any.js deleted file mode 100644 index 28322ced72..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_basic.tentative.https.any.js +++ /dev/null @@ -1,93 +0,0 @@ -// META: timeout=long -// META: script=/resources/test-only-api.js -// META: script=resources/pressure-helpers.js -// META: global=window,dedicatedworker,sharedworker - -'use strict'; - -pressure_test((t, mockPressureService) => { - const observer = new PressureObserver(() => { - assert_unreached('The observer callback should not be called'); - }); - - mockPressureService.setExpectedFailure( - new DOMException('', 'NotSupportedError')); - return promise_rejects_dom(t, 'NotSupportedError', observer.observe('cpu')); -}, 'Return NotSupportedError when calling observer()'); - -pressure_test(async (t, mockPressureService) => { - const changes = await new Promise(resolve => { - const observer = new PressureObserver(resolve); - t.add_cleanup(() => observer.disconnect()); - observer.observe('cpu'); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - }); - assert_true(changes.length === 1); - assert_equals(changes[0].state, 'critical'); - assert_equals(changes[0].source, 'cpu'); - assert_equals(typeof changes[0].time, 'number'); -}, 'Basic functionality test'); - -pressure_test((t, mockPressureService) => { - const observer = new PressureObserver(() => { - assert_unreached('The observer callback should not be called'); - }); - - const promise = observer.observe('cpu'); - observer.unobserve('cpu'); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - - return promise_rejects_dom(t, 'AbortError', promise); -}, 'Removing observer before observe() resolves works'); - -pressure_test(async (t, mockPressureService) => { - const callbackPromises = []; - const observePromises = []; - - for (let i = 0; i < 2; i++) { - callbackPromises.push(new Promise(resolve => { - const observer = new PressureObserver(resolve); - t.add_cleanup(() => observer.disconnect()); - observePromises.push(observer.observe('cpu')); - })); - } - - await Promise.all(observePromises); - - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - - return Promise.all(callbackPromises); -}, 'Calling observe() multiple times works'); - -pressure_test(async (t, mockPressureService) => { - const observer1_changes = []; - await new Promise(resolve => { - const observer1 = new PressureObserver(changes => { - observer1_changes.push(changes); - resolve(); - }); - t.add_cleanup(() => observer1.disconnect()); - observer1.observe('cpu'); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - }); - assert_true(observer1_changes.length === 1); - assert_equals(observer1_changes[0][0].source, 'cpu'); - assert_equals(observer1_changes[0][0].state, 'critical'); - - const observer2_changes = []; - await new Promise(resolve => { - const observer2 = new PressureObserver(changes => { - observer2_changes.push(changes); - resolve(); - }); - t.add_cleanup(() => observer2.disconnect()); - observer2.observe('cpu'); - }); - assert_true(observer2_changes.length === 1); - assert_equals(observer2_changes[0][0].source, 'cpu'); - assert_equals(observer2_changes[0][0].state, 'critical'); -}, 'Starting a new observer after an observer has started works'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_detached_iframe.https.html b/testing/web-platform/tests/compute-pressure/compute_pressure_detached_iframe.https.html new file mode 100644 index 0000000000..6123521248 --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_detached_iframe.https.html @@ -0,0 +1,95 @@ +<!doctype html> +<meta charset="utf-8"> +<title>PressureObserver on DOMWindow of detached iframe</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/test-only-api.js"></script> +<script src="resources/pressure-helpers.js"></script> +<body> +<script> +'use strict'; + +test(() => { + const iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + const frame_window = iframe.contentWindow; + + iframe.remove(); + assert_equals(undefined, frame_window.PressureObserver); +}, 'PressureObserver constructor does not exist in detached iframes'); + +promise_test(async t => { + const iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + const frame_window = iframe.contentWindow; + + const observer = new frame_window.PressureObserver(() => {}); + const iframe_DOMException = frame_window.DOMException; + + iframe.remove(); + + // Calling observe() from a detached iframe should fail but not crash. + await promise_rejects_dom(t, 'NotSupportedError', iframe_DOMException, + observer.observe('cpu')); +}, 'PressureObserver.observe() on detached frame rejects'); + +promise_test(async t => { + const iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + const frame_window = iframe.contentWindow; + + const observer = new frame_window.PressureObserver(() => {}); + + await observer.observe('cpu'); + + iframe.remove(); + + // Calling disconnect() from a detached iframe should not crash. + observer.disconnect(); +}, 'PressureObserver.disconnect() on detached frame returns'); + +pressure_test(async (t, mockPressureService) => { + const iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + const frame_window = iframe.contentWindow; + + const observer = new frame_window.PressureObserver(() => {}); + const iframe_DOMException = frame_window.DOMException; + + // await is intentionally not used here. We want to remove the iframe while + // the returned Promise settles. + observer.observe('cpu'); + iframe.remove(); + + // Establish an observer and wait for changes in the main frame. This should + // keep the test running long enough to catch any crash from the observe() + // call in the removed iframe's PressureObserver. + const changes = await new Promise((resolve, reject) => { + const observer = new PressureObserver(resolve); + t.add_cleanup(() => observer.disconnect()); + observer.observe('cpu').catch(reject); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + }); + assert_equals(changes[0].state, 'critical'); +}, 'Detaching frame while PressureObserver.observe() settles'); + +pressure_test(async (t, mockPressureService) => { + const iframe = document.createElement('iframe'); + document.body.appendChild(iframe); + const frame_window = iframe.contentWindow; + const observer = new frame_window.PressureObserver(() => { + assert_unreached('The observer callback should not be called'); + }); + + await observer.observe('cpu'); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + + iframe.remove(); + + return new Promise(resolve => t.step_timeout(resolve, 1000)); +}, 'PressureObserver on detached frame returns with no callback'); + +</script> +</body> diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_detached_iframe.tentative.https.html b/testing/web-platform/tests/compute-pressure/compute_pressure_detached_iframe.tentative.https.html deleted file mode 100644 index 5511a14704..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_detached_iframe.tentative.https.html +++ /dev/null @@ -1,95 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>PressureObserver on DOMWindow of detached iframe</title> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/resources/test-only-api.js"></script> -<script src="resources/pressure-helpers.js"></script> -<body> -<script> -'use strict'; - -test(() => { - const iframe = document.createElement('iframe'); - document.body.appendChild(iframe); - const frame_window = iframe.contentWindow; - - iframe.remove(); - assert_equals(undefined, frame_window.PressureObserver); -}, 'PressureObserver constructor does not exist in detached iframes'); - -promise_test(async t => { - const iframe = document.createElement('iframe'); - document.body.appendChild(iframe); - const frame_window = iframe.contentWindow; - - const observer = new frame_window.PressureObserver(() => {}); - const iframe_DOMException = frame_window.DOMException; - - iframe.remove(); - - // Calling observe() from a detached iframe should fail but not crash. - await promise_rejects_dom(t, 'NotSupportedError', iframe_DOMException, - observer.observe('cpu')); -}, 'PressureObserver.observe() on detached frame rejects'); - -promise_test(async t => { - const iframe = document.createElement('iframe'); - document.body.appendChild(iframe); - const frame_window = iframe.contentWindow; - - const observer = new frame_window.PressureObserver(() => {}); - - await observer.observe('cpu'); - - iframe.remove(); - - // Calling disconnect() from a detached iframe should not crash. - observer.disconnect(); -}, 'PressureObserver.disconnect() on detached frame returns'); - -pressure_test(async (t, mockPressureService) => { - const iframe = document.createElement('iframe'); - document.body.appendChild(iframe); - const frame_window = iframe.contentWindow; - - const observer = new frame_window.PressureObserver(() => {}); - const iframe_DOMException = frame_window.DOMException; - - // await is intentionally not used here. We want to remove the iframe while - // the returned Promise settles. - observer.observe('cpu'); - iframe.remove(); - - // Establish an observer and wait for changes in the main frame. This should - // keep the test running long enough to catch any crash from the observe() - // call in the removed iframe's PressureObserver. - const changes = await new Promise((resolve, reject) => { - const observer = new PressureObserver(resolve); - t.add_cleanup(() => observer.disconnect()); - observer.observe('cpu').catch(reject); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - }); - assert_equals(changes[0].state, 'critical'); -}, 'Detaching frame while PressureObserver.observe() settles'); - -pressure_test(async (t, mockPressureService) => { - const iframe = document.createElement('iframe'); - document.body.appendChild(iframe); - const frame_window = iframe.contentWindow; - const observer = new frame_window.PressureObserver(() => { - assert_unreached('The observer callback should not be called'); - }); - - await observer.observe('cpu'); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - - iframe.remove(); - - return new Promise(resolve => t.step_timeout(resolve, 1000)); -}, 'PressureObserver on detached frame returns with no callback'); - -</script> -</body> diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect.https.any.js new file mode 100644 index 0000000000..f8bc3fb357 --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect.https.any.js @@ -0,0 +1,44 @@ +// META: script=/resources/test-only-api.js +// META: script=resources/pressure-helpers.js +// META: global=window,dedicatedworker,sharedworker + +'use strict'; + +test(t => { + const observer = new PressureObserver(() => { + assert_unreached('The observer callback should not be called'); + }); + t.add_cleanup(() => observer.disconnect()); + observer.disconnect(); +}, 'Call disconnect() directly should not crash'); + +pressure_test(async (t, mockPressureService) => { + const observer1_changes = []; + const observer1 = new PressureObserver(change => { + observer1_changes.push(change); + }); + t.add_cleanup(() => observer1.disconnect()); + // Ensure that observer1's schema gets registered before observer2 starts. + await observer1.observe('cpu'); + observer1.disconnect(); + + const observer2_changes = []; + await new Promise((resolve, reject) => { + const observer2 = new PressureObserver(change => { + observer2_changes.push(change); + resolve(); + }); + t.add_cleanup(() => observer2.disconnect()); + observer2.observe('cpu').catch(reject); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + }); + + assert_equals( + observer1_changes.length, 0, + 'disconnected observers should not receive callbacks'); + + assert_equals(observer2_changes.length, 1); + assert_equals(observer2_changes[0].length, 1); + assert_equals(observer2_changes[0][0].state, 'critical'); +}, 'Stopped PressureObserver do not receive changes'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect.tentative.https.any.js deleted file mode 100644 index 1d188fad8b..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect.tentative.https.any.js +++ /dev/null @@ -1,44 +0,0 @@ -// META: script=/resources/test-only-api.js -// META: script=resources/pressure-helpers.js -// META: global=window,dedicatedworker,sharedworker - -'use strict'; - -test(t => { - const observer = new PressureObserver(() => { - assert_unreached('The observer callback should not be called'); - }); - t.add_cleanup(() => observer.disconnect()); - observer.disconnect(); -}, 'Call disconnect() directly should not crash'); - -pressure_test(async (t, mockPressureService) => { - const observer1_changes = []; - const observer1 = new PressureObserver(change => { - observer1_changes.push(change); - }); - t.add_cleanup(() => observer1.disconnect()); - // Ensure that observer1's schema gets registered before observer2 starts. - await observer1.observe('cpu'); - observer1.disconnect(); - - const observer2_changes = []; - await new Promise((resolve, reject) => { - const observer2 = new PressureObserver(change => { - observer2_changes.push(change); - resolve(); - }); - t.add_cleanup(() => observer2.disconnect()); - observer2.observe('cpu').catch(reject); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - }); - - assert_equals( - observer1_changes.length, 0, - 'disconnected observers should not receive callbacks'); - - assert_equals(observer2_changes.length, 1); - assert_equals(observer2_changes[0].length, 1); - assert_equals(observer2_changes[0][0].state, 'critical'); -}, 'Stopped PressureObserver do not receive changes'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect_idempotent.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect_idempotent.https.any.js new file mode 100644 index 0000000000..3c9a6688a4 --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect_idempotent.https.any.js @@ -0,0 +1,37 @@ +// META: script=/resources/test-only-api.js +// META: script=resources/pressure-helpers.js +// META: global=window,dedicatedworker,sharedworker + +'use strict'; + +pressure_test(async (t, mockPressureService) => { + const observer1_changes = []; + const observer1 = new PressureObserver(changes => { + observer1_changes.push(changes); + }); + t.add_cleanup(() => observer1.disconnect()); + // Ensure that observer1's schema gets registered before observer2 starts. + const promise = observer1.observe('cpu'); + observer1.disconnect(); + observer1.disconnect(); + await promise_rejects_dom(t, 'AbortError', promise); + + const observer2_changes = []; + await new Promise((resolve, reject) => { + const observer2 = new PressureObserver(changes => { + observer2_changes.push(changes); + resolve(); + }); + t.add_cleanup(() => observer2.disconnect()); + observer2.observe('cpu').catch(reject); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + }); + + assert_equals( + observer1_changes.length, 0, + 'stopped observers should not receive callbacks'); + + assert_equals(observer2_changes.length, 1); + assert_equals(observer2_changes[0][0].state, 'critical'); +}, 'Stopped PressureObserver do not receive changes'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect_idempotent.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect_idempotent.tentative.https.any.js deleted file mode 100644 index 74d37bd6e5..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect_idempotent.tentative.https.any.js +++ /dev/null @@ -1,37 +0,0 @@ -// META: script=/resources/test-only-api.js -// META: script=resources/pressure-helpers.js -// META: global=window,dedicatedworker,sharedworker - -'use strict'; - -pressure_test(async (t, mockPressureService) => { - const observer1_changes = []; - const observer1 = new PressureObserver(changes => { - observer1_changes.push(changes); - }); - t.add_cleanup(() => observer1.disconnect()); - // Ensure that observer1's schema gets registered before observer2 starts. - const promise = observer1.observe('cpu'); - observer1.disconnect(); - observer1.disconnect(); - await promise_rejects_dom(t, 'AbortError', promise); - - const observer2_changes = []; - await new Promise((resolve, reject) => { - const observer2 = new PressureObserver(changes => { - observer2_changes.push(changes); - resolve(); - }); - t.add_cleanup(() => observer2.disconnect()); - observer2.observe('cpu').catch(reject); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - }); - - assert_equals( - observer1_changes.length, 0, - 'stopped observers should not receive callbacks'); - - assert_equals(observer2_changes.length, 1); - assert_equals(observer2_changes[0][0].state, 'critical'); -}, 'Stopped PressureObserver do not receive changes'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect_immediately.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect_immediately.https.any.js new file mode 100644 index 0000000000..86963e242a --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect_immediately.https.any.js @@ -0,0 +1,67 @@ +// META: script=/resources/test-only-api.js +// META: script=resources/pressure-helpers.js +// META: global=window,dedicatedworker,sharedworker + +'use strict'; + +pressure_test(async (t, mockPressureService) => { + const observer1_changes = []; + const observer1 = new PressureObserver(changes => { + observer1_changes.push(changes); + }); + t.add_cleanup(() => observer1.disconnect()); + // Ensure that observer1's schema gets registered before observer2 starts. + const promise = observer1.observe('cpu'); + observer1.disconnect(); + await promise_rejects_dom(t, 'AbortError', promise); + + const observer2_changes = []; + await new Promise((resolve, reject) => { + const observer2 = new PressureObserver(changes => { + observer2_changes.push(changes); + resolve(); + }); + t.add_cleanup(() => observer2.disconnect()); + observer2.observe('cpu').catch(reject); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + }); + + assert_equals( + observer1_changes.length, 0, + 'stopped observers should not receive callbacks'); + + assert_equals(observer2_changes.length, 1); + assert_equals(observer2_changes[0].length, 1); + assert_equals(observer2_changes[0][0].state, 'critical'); +}, 'Stopped PressureObserver do not receive changes'); + +pressure_test(async (t, mockPressureService) => { + const observer1_changes = []; + const observer1 = new PressureObserver(changes => { + observer1_changes.push(changes); + }); + t.add_cleanup(() => observer1.disconnect()); + + const observer2_changes = []; + await new Promise(async resolve => { + const observer2 = new PressureObserver(changes => { + observer2_changes.push(changes); + resolve(); + }); + t.add_cleanup(() => observer2.disconnect()); + const promise = observer1.observe('cpu'); + observer2.observe('cpu'); + observer1.disconnect(); + await promise_rejects_dom(t, 'AbortError', promise); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + }); + + assert_equals( + observer1_changes.length, 0, + 'stopped observers should not receive callbacks'); + + assert_equals(observer2_changes.length, 1); + assert_equals(observer2_changes[0][0].state, 'critical'); +}, 'Removing observer before observe() resolves does not affect other observers'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect_immediately.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect_immediately.tentative.https.any.js deleted file mode 100644 index 9b545fbe1c..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_disconnect_immediately.tentative.https.any.js +++ /dev/null @@ -1,67 +0,0 @@ -// META: script=/resources/test-only-api.js -// META: script=resources/pressure-helpers.js -// META: global=window,dedicatedworker,sharedworker - -'use strict'; - -pressure_test(async (t, mockPressureService) => { - const observer1_changes = []; - const observer1 = new PressureObserver(changes => { - observer1_changes.push(changes); - }); - t.add_cleanup(() => observer1.disconnect()); - // Ensure that observer1's schema gets registered before observer2 starts. - const promise = observer1.observe('cpu'); - observer1.disconnect(); - await promise_rejects_dom(t, 'AbortError', promise); - - const observer2_changes = []; - await new Promise((resolve, reject) => { - const observer2 = new PressureObserver(changes => { - observer2_changes.push(changes); - resolve(); - }); - t.add_cleanup(() => observer2.disconnect()); - observer2.observe('cpu').catch(reject); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - }); - - assert_equals( - observer1_changes.length, 0, - 'stopped observers should not receive callbacks'); - - assert_equals(observer2_changes.length, 1); - assert_equals(observer2_changes[0].length, 1); - assert_equals(observer2_changes[0][0].state, 'critical'); -}, 'Stopped PressureObserver do not receive changes'); - -pressure_test(async (t, mockPressureService) => { - const observer1_changes = []; - const observer1 = new PressureObserver(changes => { - observer1_changes.push(changes); - }); - t.add_cleanup(() => observer1.disconnect()); - - const observer2_changes = []; - await new Promise(async resolve => { - const observer2 = new PressureObserver(changes => { - observer2_changes.push(changes); - resolve(); - }); - t.add_cleanup(() => observer2.disconnect()); - const promise = observer1.observe('cpu'); - observer2.observe('cpu'); - observer1.disconnect(); - await promise_rejects_dom(t, 'AbortError', promise); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - }); - - assert_equals( - observer1_changes.length, 0, - 'stopped observers should not receive callbacks'); - - assert_equals(observer2_changes.length, 1); - assert_equals(observer2_changes[0][0].state, 'critical'); -}, 'Removing observer before observe() resolves does not affect other observers'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_duplicate_updates.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_duplicate_updates.https.any.js new file mode 100644 index 0000000000..04c5df5e57 --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_duplicate_updates.https.any.js @@ -0,0 +1,32 @@ +// META: script=/resources/test-only-api.js +// META: script=resources/pressure-helpers.js +// META: global=window,dedicatedworker,sharedworker + +'use strict'; + +pressure_test(async (t, mockPressureService) => { + const pressureChanges = await new Promise(async resolve => { + const observer_changes = []; + let n = 0; + const observer = new PressureObserver(changes => { + observer_changes.push(changes); + if (++n === 2) + resolve(observer_changes); + }, {sampleInterval: 200}); + observer.observe('cpu'); + const updatesDelivered = mockPressureService.updatesDelivered(); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval*/ 200); + // Deliver 2 updates. + await t.step_wait( + () => mockPressureService.updatesDelivered() >= (updatesDelivered + 2), + 'Wait for more than one update to be delivered to the observer'); + mockPressureService.setPressureUpdate('cpu', 'nominal'); + // Deliver more updates, |resolve()| will be called when the new pressure + // state reaches PressureObserver and its callback is invoked + // for the second time. + }); + assert_equals(pressureChanges.length, 2); + assert_equals(pressureChanges[0][0].state, 'critical'); + assert_equals(pressureChanges[1][0].state, 'nominal'); +}, 'Changes that fail the "has change in data" test are discarded.'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_duplicate_updates.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_duplicate_updates.tentative.https.any.js deleted file mode 100644 index dde92932dd..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_duplicate_updates.tentative.https.any.js +++ /dev/null @@ -1,32 +0,0 @@ -// META: script=/resources/test-only-api.js -// META: script=resources/pressure-helpers.js -// META: global=window,dedicatedworker,sharedworker - -'use strict'; - -pressure_test(async (t, mockPressureService) => { - const pressureChanges = await new Promise(async resolve => { - const observer_changes = []; - let n = 0; - const observer = new PressureObserver(changes => { - observer_changes.push(changes); - if (++n === 2) - resolve(observer_changes); - }, {sampleRate: 5.0}); - observer.observe('cpu'); - const updatesDelivered = mockPressureService.updatesDelivered(); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate*/ 5.0); - // Deliver 2 updates. - await t.step_wait( - () => mockPressureService.updatesDelivered() >= (updatesDelivered + 2), - 'Wait for more than one update to be delivered to the observer'); - mockPressureService.setPressureUpdate('cpu', 'nominal'); - // Deliver more updates, |resolve()| will be called when the new pressure - // state reaches PressureObserver and its callback is invoked - // for the second time. - }); - assert_equals(pressureChanges.length, 2); - assert_equals(pressureChanges[0][0].state, 'critical'); - assert_equals(pressureChanges[1][0].state, 'nominal'); -}, 'Changes that fail the "has change in data" test are discarded.'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_multiple.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_multiple.https.any.js new file mode 100644 index 0000000000..8c50cc4b3d --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_multiple.https.any.js @@ -0,0 +1,35 @@ +// META: script=/resources/test-only-api.js +// META: script=resources/pressure-helpers.js +// META: global=window,dedicatedworker,sharedworker + +'use strict'; + +pressure_test(async (t, mockPressureService) => { + const changes1_promise = new Promise((resolve, reject) => { + const observer = new PressureObserver(resolve); + t.add_cleanup(() => observer.disconnect()); + observer.observe('cpu').catch(reject); + }); + + const changes2_promise = new Promise((resolve, reject) => { + const observer = new PressureObserver(resolve); + t.add_cleanup(() => observer.disconnect()); + observer.observe('cpu').catch(reject); + }); + + const changes3_promise = new Promise((resolve, reject) => { + const observer = new PressureObserver(resolve); + t.add_cleanup(() => observer.disconnect()); + observer.observe('cpu').catch(reject); + }); + + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + + const [changes1, changes2, changes3] = + await Promise.all([changes1_promise, changes2_promise, changes3_promise]); + + for (const changes of [changes1, changes2, changes3]) { + assert_equals(changes[0].state, 'critical'); + } +}, 'Three PressureObserver instances receive changes'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_multiple.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_multiple.tentative.https.any.js deleted file mode 100644 index c8cef5beca..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_multiple.tentative.https.any.js +++ /dev/null @@ -1,35 +0,0 @@ -// META: script=/resources/test-only-api.js -// META: script=resources/pressure-helpers.js -// META: global=window,dedicatedworker,sharedworker - -'use strict'; - -pressure_test(async (t, mockPressureService) => { - const changes1_promise = new Promise((resolve, reject) => { - const observer = new PressureObserver(resolve); - t.add_cleanup(() => observer.disconnect()); - observer.observe('cpu').catch(reject); - }); - - const changes2_promise = new Promise((resolve, reject) => { - const observer = new PressureObserver(resolve); - t.add_cleanup(() => observer.disconnect()); - observer.observe('cpu').catch(reject); - }); - - const changes3_promise = new Promise((resolve, reject) => { - const observer = new PressureObserver(resolve); - t.add_cleanup(() => observer.disconnect()); - observer.observe('cpu').catch(reject); - }); - - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - - const [changes1, changes2, changes3] = - await Promise.all([changes1_promise, changes2_promise, changes3_promise]); - - for (const changes of [changes1, changes2, changes3]) { - assert_equals(changes[0].state, 'critical'); - } -}, 'Three PressureObserver instances receive changes'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_observe_idempotent.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_observe_idempotent.https.any.js new file mode 100644 index 0000000000..9fcbb49814 --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_observe_idempotent.https.any.js @@ -0,0 +1,19 @@ +// META: script=/resources/test-only-api.js +// META: script=resources/pressure-helpers.js +// META: global=window,dedicatedworker,sharedworker + +'use strict'; + +pressure_test(async (t, mockPressureService) => { + const update = await new Promise((resolve, reject) => { + const observer = new PressureObserver(resolve); + t.add_cleanup(() => observer.disconnect()); + observer.observe('cpu').catch(reject); + observer.observe('cpu').catch(reject); + observer.observe('cpu').catch(reject); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + }); + + assert_equals(update[0].state, 'critical'); +}, 'PressureObserver.observe() is idempotent'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_observe_idempotent.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_observe_idempotent.tentative.https.any.js deleted file mode 100644 index 5dc3804b2f..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_observe_idempotent.tentative.https.any.js +++ /dev/null @@ -1,19 +0,0 @@ -// META: script=/resources/test-only-api.js -// META: script=resources/pressure-helpers.js -// META: global=window,dedicatedworker,sharedworker - -'use strict'; - -pressure_test(async (t, mockPressureService) => { - const update = await new Promise((resolve, reject) => { - const observer = new PressureObserver(resolve); - t.add_cleanup(() => observer.disconnect()); - observer.observe('cpu').catch(reject); - observer.observe('cpu').catch(reject); - observer.observe('cpu').catch(reject); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - }); - - assert_equals(update[0].state, 'critical'); -}, 'PressureObserver.observe() is idempotent'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_observe_unobserve_failure.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_observe_unobserve_failure.https.any.js index 8eafeb356d..8eafeb356d 100644 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_observe_unobserve_failure.tentative.https.any.js +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_observe_unobserve_failure.https.any.js diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_options.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_options.https.any.js new file mode 100644 index 0000000000..d0760ef622 --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_options.https.any.js @@ -0,0 +1,26 @@ +// META: global=window,dedicatedworker,sharedworker + +'use strict'; + +test(t => { + const observer = new PressureObserver(() => {}, {sampleInterval: 0}); + assert_equals(typeof observer, 'object'); +}, 'PressureObserver constructor doesnt throw error for sampleInterval value 0'); + + +test(t => { + assert_throws_js(TypeError, () => { + new PressureObserver(() => {}, {sampleInterval: -2}); + }); +}, 'PressureObserver constructor requires a positive sampleInterval'); + +test(t => { + assert_throws_js(TypeError, () => { + new PressureObserver(() => {}, {sampleInterval: 2 ** 32}); + }); +}, 'PressureObserver constructor requires a sampleInterval in unsigned long range'); + +test(t => { + const observer = new PressureObserver(() => {}, {}); + assert_equals(typeof observer, 'object'); +}, 'PressureObserver constructor succeeds on empty sampleInterval'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_options.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_options.tentative.https.any.js deleted file mode 100644 index 69999819d9..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_options.tentative.https.any.js +++ /dev/null @@ -1,25 +0,0 @@ -// META: global=window,dedicatedworker,sharedworker - -'use strict'; - -test(t => { - assert_throws_js(RangeError, () => { - new PressureObserver(() => {}, {sampleRate: 0}); - }); -}, 'PressureObserver constructor requires a non-zero sampleRate'); - -test(t => { - assert_throws_js(RangeError, () => { - new PressureObserver(() => {}, {sampleRate: -2}); - }); -}, 'PressureObserver constructor requires a positive sampleRate'); - -test(t => { - const observer = new PressureObserver(() => {}, {sampleRate: 0.5}); - assert_equals(typeof observer, 'object'); -}, 'PressureObserver constructor doesnt throw error on positive sampleRate'); - -test(t => { - const observer = new PressureObserver(() => {}, {}); - assert_equals(typeof observer, 'object'); -}, 'PressureObserver constructor succeeds on empty sampleRate'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_rate_obfuscation_mitigation_not_triggered.https.window.js b/testing/web-platform/tests/compute-pressure/compute_pressure_rate_obfuscation_mitigation_not_triggered.https.window.js new file mode 100644 index 0000000000..e348a8ea08 --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_rate_obfuscation_mitigation_not_triggered.https.window.js @@ -0,0 +1,50 @@ +// META: timeout=long +// META: script=/resources/test-only-api.js +// META: script=resources/pressure-helpers.js +// META: global=window,dedicatedworker,sharedworker + +'use strict'; + +pressure_test(async (t, mockPressureService) => { + const sampleIntervalInMs = 100; + const readings = ['nominal', 'fair', 'serious', 'critical']; + // Normative values for rate obfuscation parameters. + // https://w3c.github.io/compute-pressure/#rate-obfuscation-normative-parameters. + const minPenaltyTimeInMs = 5000; + const minChangesThreshold = 50; + + const changes = await new Promise(async resolve => { + const observerChanges = []; + const observer = new PressureObserver(changes => { + observerChanges.push(changes); + }, {sampleInterval: sampleIntervalInMs}); + + observer.observe('cpu'); + mockPressureService.startPlatformCollector(sampleIntervalInMs); + let i = 0; + // mockPressureService.updatesDelivered() does not necessarily match + // pressureChanges.length, as system load and browser optimizations can + // cause the actual timer used by mockPressureService to deliver readings + // to be a bit slower or faster than requested. + while (observerChanges.length < minChangesThreshold) { + mockPressureService.setPressureUpdate( + 'cpu', readings[i++ % readings.length]); + // Allow tasks to run (avoid a micro-task loop). + await new Promise((resolve) => t.step_timeout(resolve, 0)); + await t.step_wait( + () => mockPressureService.updatesDelivered() >= i, + `At least ${i} readings have been delivered`); + } + observer.disconnect(); + resolve(observerChanges); + }); + assert_equals(changes.length, minChangesThreshold); + + for (let i = 0; i < (changes.length - 1); i++) { + // Because no penalty should be triggered, the timestamp difference + // between samples should be less than the minimum penalty. + assert_less_than( + changes[i + 1][0].time - changes[i][0].time, minPenaltyTimeInMs, + 'Not in sample time boundaries'); + } +}, 'No rate obfuscation mitigation should happen, when number of changes is below minimum changes before penalty'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_rate_obfuscation_mitigation_not_triggered.tentative.https.window.js b/testing/web-platform/tests/compute-pressure/compute_pressure_rate_obfuscation_mitigation_not_triggered.tentative.https.window.js deleted file mode 100644 index cb1aa432ce..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_rate_obfuscation_mitigation_not_triggered.tentative.https.window.js +++ /dev/null @@ -1,50 +0,0 @@ -// META: timeout=long -// META: script=/resources/test-only-api.js -// META: script=resources/pressure-helpers.js -// META: global=window,dedicatedworker,sharedworker - -'use strict'; - -pressure_test(async (t, mockPressureService) => { - const sampleRateInHz = 10; - const readings = ['nominal', 'fair', 'serious', 'critical']; - // Normative values for rate obfuscation parameters. - // https://w3c.github.io/compute-pressure/#rate-obfuscation-normative-parameters. - const minPenaltyTimeInMs = 5000; - const minChangesThreshold = 50; - - const changes = await new Promise(async resolve => { - const observerChanges = []; - const observer = new PressureObserver(changes => { - observerChanges.push(changes); - }, {sampleRate: sampleRateInHz}); - - observer.observe('cpu'); - mockPressureService.startPlatformCollector(sampleRateInHz); - let i = 0; - // mockPressureService.updatesDelivered() does not necessarily match - // pressureChanges.length, as system load and browser optimizations can - // cause the actual timer used by mockPressureService to deliver readings - // to be a bit slower or faster than requested. - while (observerChanges.length < minChangesThreshold) { - mockPressureService.setPressureUpdate( - 'cpu', readings[i++ % readings.length]); - // Allow tasks to run (avoid a micro-task loop). - await new Promise((resolve) => t.step_timeout(resolve, 0)); - await t.step_wait( - () => mockPressureService.updatesDelivered() >= i, - `At least ${i} readings have been delivered`); - } - observer.disconnect(); - resolve(observerChanges); - }); - assert_equals(changes.length, minChangesThreshold); - - for (let i = 0; i < (changes.length - 1); i++) { - // Because no penalty should be triggered, the timestamp difference - // between samples should be less than the minimum penalty. - assert_less_than( - changes[i + 1][0].time - changes[i][0].time, minPenaltyTimeInMs, - 'Not in sample time boundaries'); - } -}, 'No rate obfuscation mitigation should happen, when number of changes is below minimum changes before penalty'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_rate_obfuscation_mitigation_triggered.https.window.js b/testing/web-platform/tests/compute-pressure/compute_pressure_rate_obfuscation_mitigation_triggered.https.window.js new file mode 100644 index 0000000000..ebe33bc8bf --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_rate_obfuscation_mitigation_triggered.https.window.js @@ -0,0 +1,56 @@ +// META: timeout=long +// META: script=/resources/test-only-api.js +// META: script=resources/pressure-helpers.js +// META: global=window,dedicatedworker,sharedworker + +'use strict'; + +pressure_test(async (t, mockPressureService) => { + const sampleIntervalInMs = 40; + const readings = ['nominal', 'fair', 'serious', 'critical']; + // Normative values for rate obfuscation parameters. + // https://w3c.github.io/compute-pressure/#rate-obfuscation-normative-parameters. + const minPenaltyTimeInMs = 5000; + const maxChangesThreshold = 100; + const minChangesThreshold = 50; + let gotPenalty = false; + await new Promise(async resolve => { + const observerChanges = []; + const observer = new PressureObserver(changes => { + if (observerChanges.length >= (minChangesThreshold - 1)) { + const lastSample = observerChanges.at(-1); + if ((changes[0].time - lastSample[0].time) >= minPenaltyTimeInMs) { + // The update delivery might still be working even if + // maxChangesThreshold have been reached and before disconnect() is + // processed. + // Therefore we are adding a flag to dismiss any updates after the + // penalty is detected, which is the condition for the test to pass. + gotPenalty = true; + observer.disconnect(); + resolve(); + } + } + observerChanges.push(changes); + }, {sampleInterval: sampleIntervalInMs}); + + observer.observe('cpu'); + mockPressureService.startPlatformCollector(sampleIntervalInMs); + let i = 0; + // mockPressureService.updatesDelivered() does not necessarily match + // pressureChanges.length, as system load and browser optimizations can + // cause the actual timer used by mockPressureService to deliver readings + // to be a bit slower or faster than requested. + while (observerChanges.length <= maxChangesThreshold || !gotPenalty) { + mockPressureService.setPressureUpdate( + 'cpu', readings[i++ % readings.length]); + // Allow tasks to run (avoid a micro-task loop). + await new Promise((resolve) => t.step_timeout(resolve, 0)); + await t.step_wait( + () => mockPressureService.updatesDelivered() >= i, + `At least ${i} readings have been delivered`); + } + + assert_true(gotPenalty, 'Penalty not triggered'); + + }); +}, 'Rate obfuscation mitigation should have been triggered, when changes is higher than minimum changes before penalty'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_rate_obfuscation_mitigation_triggered.tentative.https.window.js b/testing/web-platform/tests/compute-pressure/compute_pressure_rate_obfuscation_mitigation_triggered.tentative.https.window.js deleted file mode 100644 index 11dcc3c70a..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_rate_obfuscation_mitigation_triggered.tentative.https.window.js +++ /dev/null @@ -1,56 +0,0 @@ -// META: timeout=long -// META: script=/resources/test-only-api.js -// META: script=resources/pressure-helpers.js -// META: global=window,dedicatedworker,sharedworker - -'use strict'; - -pressure_test(async (t, mockPressureService) => { - const sampleRateInHz = 25; - const readings = ['nominal', 'fair', 'serious', 'critical']; - // Normative values for rate obfuscation parameters. - // https://w3c.github.io/compute-pressure/#rate-obfuscation-normative-parameters. - const minPenaltyTimeInMs = 5000; - const maxChangesThreshold = 100; - const minChangesThreshold = 50; - let gotPenalty = false; - await new Promise(async resolve => { - const observerChanges = []; - const observer = new PressureObserver(changes => { - if (observerChanges.length >= (minChangesThreshold - 1)) { - const lastSample = observerChanges.at(-1); - if ((changes[0].time - lastSample[0].time) >= minPenaltyTimeInMs) { - // The update delivery might still be working even if - // maxChangesThreshold have been reached and before disconnect() is - // processed. - // Therefore we are adding a flag to dismiss any updates after the - // penalty is detected, which is the condition for the test to pass. - gotPenalty = true; - observer.disconnect(); - resolve(); - } - } - observerChanges.push(changes); - }, {sampleRate: sampleRateInHz}); - - observer.observe('cpu'); - mockPressureService.startPlatformCollector(sampleRateInHz); - let i = 0; - // mockPressureService.updatesDelivered() does not necessarily match - // pressureChanges.length, as system load and browser optimizations can - // cause the actual timer used by mockPressureService to deliver readings - // to be a bit slower or faster than requested. - while (observerChanges.length <= maxChangesThreshold || !gotPenalty) { - mockPressureService.setPressureUpdate( - 'cpu', readings[i++ % readings.length]); - // Allow tasks to run (avoid a micro-task loop). - await new Promise((resolve) => t.step_timeout(resolve, 0)); - await t.step_wait( - () => mockPressureService.updatesDelivered() >= i, - `At least ${i} readings have been delivered`); - } - - assert_true(gotPenalty, 'Penalty not triggered'); - - }); -}, 'Rate obfuscation mitigation should have been triggered, when changes is higher than minimum changes before penalty'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_supported_sources.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_supported_sources.https.any.js index 63f2666cca..63f2666cca 100644 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_supported_sources.tentative.https.any.js +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_supported_sources.https.any.js diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_take_records.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_take_records.https.any.js new file mode 100644 index 0000000000..55660b228b --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_take_records.https.any.js @@ -0,0 +1,29 @@ +// META: script=/resources/test-only-api.js +// META: script=resources/pressure-helpers.js +// META: global=window,dedicatedworker,sharedworker + +'use strict'; + +test(t => { + const observer = new PressureObserver( + t.unreached_func('This callback should not have been called.')); + + const records = observer.takeRecords(); + assert_equals(records.length, 0, 'No record before observe'); +}, 'Calling takeRecords() before observe()'); + +pressure_test(async (t, mockPressureService) => { + let observer; + const changes = await new Promise(resolve => { + observer = new PressureObserver(resolve); + t.add_cleanup(() => observer.disconnect()); + + observer.observe('cpu'); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + }); + assert_equals(changes[0].state, 'critical'); + + const records = observer.takeRecords(); + assert_equals(records.length, 0, 'No record available'); +}, 'takeRecords() returns empty record after callback invoke'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_take_records.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_take_records.tentative.https.any.js deleted file mode 100644 index d93c9b5c88..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_take_records.tentative.https.any.js +++ /dev/null @@ -1,29 +0,0 @@ -// META: script=/resources/test-only-api.js -// META: script=resources/pressure-helpers.js -// META: global=window,dedicatedworker,sharedworker - -'use strict'; - -test(t => { - const observer = new PressureObserver( - t.unreached_func('This callback should not have been called.')); - - const records = observer.takeRecords(); - assert_equals(records.length, 0, 'No record before observe'); -}, 'Calling takeRecords() before observe()'); - -pressure_test(async (t, mockPressureService) => { - let observer; - const changes = await new Promise(resolve => { - observer = new PressureObserver(resolve); - t.add_cleanup(() => observer.disconnect()); - - observer.observe('cpu'); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - }); - assert_equals(changes[0].state, 'critical'); - - const records = observer.takeRecords(); - assert_equals(records.length, 0, 'No record available'); -}, 'takeRecords() returns empty record after callback invoke'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_timestamp.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_timestamp.https.any.js new file mode 100644 index 0000000000..09caeb3478 --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_timestamp.https.any.js @@ -0,0 +1,75 @@ +// META: script=/resources/test-only-api.js +// META: script=resources/pressure-helpers.js +// META: global=window,dedicatedworker,sharedworker + +'use strict'; + +pressure_test(async (t, mockPressureService) => { + const readings = ['nominal', 'fair', 'serious', 'critical']; + + const sampleInterval = 250; + const pressureChanges = await new Promise(async resolve => { + const observerChanges = []; + const observer = new PressureObserver(changes => { + observerChanges.push(changes); + }, {sampleInterval}); + observer.observe('cpu'); + + mockPressureService.startPlatformCollector(sampleInterval / 2); + let i = 0; + // mockPressureService.updatesDelivered() does not necessarily match + // pressureChanges.length, as system load and browser optimizations can + // cause the actual timer used by mockPressureService to deliver readings + // to be a bit slower or faster than requested. + while (observerChanges.length < 4) { + mockPressureService.setPressureUpdate( + 'cpu', readings[i++ % readings.length]); + await t.step_wait( + () => mockPressureService.updatesDelivered() >= i, + `At least ${i} readings have been delivered`); + } + observer.disconnect(); + resolve(observerChanges); + }); + + assert_equals(pressureChanges.length, 4); + assert_greater_than_equal( + pressureChanges[1][0].time - pressureChanges[0][0].time, sampleInterval); + assert_greater_than_equal( + pressureChanges[2][0].time - pressureChanges[1][0].time, sampleInterval); + assert_greater_than_equal( + pressureChanges[3][0].time - pressureChanges[2][0].time, sampleInterval); +}, 'Faster collector: Timestamp difference between two changes should be higher or equal to the observer sample rate'); + +pressure_test(async (t, mockPressureService) => { + const pressureChanges = []; + const sampleInterval = 1000; + const observer = new PressureObserver(changes => { + pressureChanges.push(changes); + }, {sampleInterval}); + + await new Promise(async resolve => { + observer.observe('cpu'); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(sampleInterval); + await t.step_wait(() => pressureChanges.length == 1); + observer.disconnect(); + resolve(); + }); + + await new Promise(async resolve => { + observer.observe('cpu'); + mockPressureService.setPressureUpdate('cpu', 'serious'); + mockPressureService.startPlatformCollector(sampleInterval / 4); + await t.step_wait(() => pressureChanges.length == 2); + observer.disconnect(); + resolve(); + }); + + assert_equals(pressureChanges.length, 2); + // When disconnect() is called, PressureRecord in [[LastRecordMap]] for cpu + // should be deleted. So the second PressureRecord is not discarded even + // though the time interval does not meet the requirement. + assert_less_than( + pressureChanges[1][0].time - pressureChanges[0][0].time, sampleInterval); +}, 'disconnect() should update [[LastRecordMap]]'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_timestamp.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_timestamp.tentative.https.any.js deleted file mode 100644 index f283caa6ba..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_timestamp.tentative.https.any.js +++ /dev/null @@ -1,79 +0,0 @@ -// META: script=/resources/test-only-api.js -// META: script=resources/pressure-helpers.js -// META: global=window,dedicatedworker,sharedworker - -'use strict'; - -pressure_test(async (t, mockPressureService) => { - const readings = ['nominal', 'fair', 'serious', 'critical']; - - const sampleRate = 4.0; - const pressureChanges = await new Promise(async resolve => { - const observerChanges = []; - const observer = new PressureObserver(changes => { - observerChanges.push(changes); - }, {sampleRate}); - observer.observe('cpu'); - - mockPressureService.startPlatformCollector(sampleRate * 2); - let i = 0; - // mockPressureService.updatesDelivered() does not necessarily match - // pressureChanges.length, as system load and browser optimizations can - // cause the actual timer used by mockPressureService to deliver readings - // to be a bit slower or faster than requested. - while (observerChanges.length < 4) { - mockPressureService.setPressureUpdate( - 'cpu', readings[i++ % readings.length]); - await t.step_wait( - () => mockPressureService.updatesDelivered() >= i, - `At least ${i} readings have been delivered`); - } - observer.disconnect(); - resolve(observerChanges); - }); - - assert_equals(pressureChanges.length, 4); - assert_greater_than_equal( - pressureChanges[1][0].time - pressureChanges[0][0].time, - (1 / sampleRate * 1000)); - assert_greater_than_equal( - pressureChanges[2][0].time - pressureChanges[1][0].time, - (1 / sampleRate * 1000)); - assert_greater_than_equal( - pressureChanges[3][0].time - pressureChanges[2][0].time, - (1 / sampleRate * 1000)); -}, 'Faster collector: Timestamp difference between two changes should be higher or equal to the observer sample rate'); - -pressure_test(async (t, mockPressureService) => { - const pressureChanges = []; - const sampleRate = 1.0; - const observer = new PressureObserver(changes => { - pressureChanges.push(changes); - }, {sampleRate}); - - await new Promise(async resolve => { - observer.observe('cpu'); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(sampleRate); - await t.step_wait(() => pressureChanges.length == 1); - observer.disconnect(); - resolve(); - }); - - await new Promise(async resolve => { - observer.observe('cpu'); - mockPressureService.setPressureUpdate('cpu', 'serious'); - mockPressureService.startPlatformCollector(sampleRate * 4); - await t.step_wait(() => pressureChanges.length == 2); - observer.disconnect(); - resolve(); - }); - - assert_equals(pressureChanges.length, 2); - // When disconnect() is called, PressureRecord in [[LastRecordMap]] for cpu - // should be deleted. So the second PressureRecord is not discarded even - // though the time interval does not meet the requirement. - assert_less_than( - pressureChanges[1][0].time - pressureChanges[0][0].time, - (1 / sampleRate * 1000)); -}, 'disconnect() should update [[LastRecordMap]]'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_update_toJSON.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_update_toJSON.https.any.js new file mode 100644 index 0000000000..7f726698d6 --- /dev/null +++ b/testing/web-platform/tests/compute-pressure/compute_pressure_update_toJSON.https.any.js @@ -0,0 +1,17 @@ +// META: script=/resources/test-only-api.js +// META: script=resources/pressure-helpers.js +// META: global=window,dedicatedworker,sharedworker + +pressure_test(async (t, mockPressureService) => { + const changes = await new Promise(resolve => { + const observer = new PressureObserver(resolve); + observer.observe('cpu'); + mockPressureService.setPressureUpdate('cpu', 'critical'); + mockPressureService.startPlatformCollector(/*sampleInterval=*/ 200); + }); + assert_true(changes.length === 1); + const json = changes[0].toJSON(); + assert_equals(json.state, 'critical'); + assert_equals(json.source, 'cpu'); + assert_equals(typeof json.time, 'number'); +}, 'Basic functionality test'); diff --git a/testing/web-platform/tests/compute-pressure/compute_pressure_update_toJSON.tentative.https.any.js b/testing/web-platform/tests/compute-pressure/compute_pressure_update_toJSON.tentative.https.any.js deleted file mode 100644 index 0024d69754..0000000000 --- a/testing/web-platform/tests/compute-pressure/compute_pressure_update_toJSON.tentative.https.any.js +++ /dev/null @@ -1,17 +0,0 @@ -// META: script=/resources/test-only-api.js -// META: script=resources/pressure-helpers.js -// META: global=window,dedicatedworker,sharedworker - -pressure_test(async (t, mockPressureService) => { - const changes = await new Promise(resolve => { - const observer = new PressureObserver(resolve); - observer.observe('cpu'); - mockPressureService.setPressureUpdate('cpu', 'critical'); - mockPressureService.startPlatformCollector(/*sampleRate=*/ 5.0); - }); - assert_true(changes.length === 1); - const json = changes[0].toJSON(); - assert_equals(json.state, 'critical'); - assert_equals(json.source, 'cpu'); - assert_equals(typeof json.time, 'number'); -}, 'Basic functionality test'); diff --git a/testing/web-platform/tests/compute-pressure/idlharness.https.any.js b/testing/web-platform/tests/compute-pressure/idlharness.https.any.js index e828996232..48ab5615b0 100644 --- a/testing/web-platform/tests/compute-pressure/idlharness.https.any.js +++ b/testing/web-platform/tests/compute-pressure/idlharness.https.any.js @@ -11,5 +11,5 @@ idl_test(['compute-pressure'], ['dom', 'html'], async idl_array => { PressureObserver: ['observer'], }); - self.observer = new PressureObserver(() => {}, {sampleRate: 1.0}); + self.observer = new PressureObserver(() => {}, {sampleInterval: 1000}); }); diff --git a/testing/web-platform/tests/content-security-policy/media-src/media-src-7_1.html b/testing/web-platform/tests/content-security-policy/media-src/media-src-7_1.html index 8fd094e955..9cf142185a 100644 --- a/testing/web-platform/tests/content-security-policy/media-src/media-src-7_1.html +++ b/testing/web-platform/tests/content-security-policy/media-src/media-src-7_1.html @@ -35,14 +35,14 @@ <video id="videoObject" width="320" height="240" controls onloadeddata="media_loaded(source_test)"> <source id="videoSourceObject" - type="video/ogg" + type="video/webm" onerror="media_error_handler(source_test)" - src="/media/A4.ogv"> + src="/media/A4.webm"> </video> <video id="videoObject2" width="320" height="240" controls onerror="media_error_handler(src_test)" onloadeddata="media_loaded(src_test)" - src="/media/A4.ogv"> + src="/media/A4.webm"> </body> </html> diff --git a/testing/web-platform/tests/content-security-policy/media-src/media-src-7_1_2.sub.html b/testing/web-platform/tests/content-security-policy/media-src/media-src-7_1_2.sub.html index 8312defb2e..579668c83e 100644 --- a/testing/web-platform/tests/content-security-policy/media-src/media-src-7_1_2.sub.html +++ b/testing/web-platform/tests/content-security-policy/media-src/media-src-7_1_2.sub.html @@ -24,7 +24,7 @@ })); // we assume tests are run from 'hostname' and 'www.hostname' or 'www2.hostname' is a valid alias - var mediaURL = location.protocol + "//{{domains[www2]}}:{{ports[http][0]}}/media/A4.ogv"; + var mediaURL = location.protocol + "//{{domains[www2]}}:{{ports[http][0]}}/media/A4.webm"; function media_loaded(t) { t.step( function () { @@ -41,7 +41,7 @@ <video id="videoObject" width="320" height="240" controls onloadeddata="media_loaded(source_test)"> <source id="videoSourceObject" - type="video/ogg" + type="video/webm" onerror="media_error_handler(source_test)"> </video> <video id="videoObject2" width="320" height="240" controls diff --git a/testing/web-platform/tests/content-security-policy/media-src/media-src-7_2_2.sub.html b/testing/web-platform/tests/content-security-policy/media-src/media-src-7_2_2.sub.html index ce6b8add3c..e1627b138c 100644 --- a/testing/web-platform/tests/content-security-policy/media-src/media-src-7_2_2.sub.html +++ b/testing/web-platform/tests/content-security-policy/media-src/media-src-7_2_2.sub.html @@ -48,7 +48,7 @@ <script> let source = document.createElement("source"); source.src = mediaURL; - source.type = "audio/ogg"; + source.type = "audio/webm"; source.onerror = _ => { media_error_handler(source_test); } document.getElementById("audioObject").appendChild(source); document.getElementById("audioObject2").src = mediaURL; diff --git a/testing/web-platform/tests/content-security-policy/media-src/media-src-7_3.sub.html b/testing/web-platform/tests/content-security-policy/media-src/media-src-7_3.sub.html index 46489e2668..2acae07b7e 100644 --- a/testing/web-platform/tests/content-security-policy/media-src/media-src-7_3.sub.html +++ b/testing/web-platform/tests/content-security-policy/media-src/media-src-7_3.sub.html @@ -37,8 +37,8 @@ <video id="videoObject" width="320" height="240" controls onloadeddata="media_loaded(source_test)" crossorigin> <source id="audioSourceObject" - type="audio/ogg" - src="/media/A4.ogv"> + type="video/webm" + src="/media/A4.webm"> <track id="trackObject" kind="subtitles" srclang="en" diff --git a/testing/web-platform/tests/content-security-policy/media-src/media-src-7_3_2.sub.html b/testing/web-platform/tests/content-security-policy/media-src/media-src-7_3_2.sub.html index 431a58608a..cb3558e6c2 100644 --- a/testing/web-platform/tests/content-security-policy/media-src/media-src-7_3_2.sub.html +++ b/testing/web-platform/tests/content-security-policy/media-src/media-src-7_3_2.sub.html @@ -43,8 +43,8 @@ onerror="media_error_handler(source_test)" crossorigin> <source id="audioSourceObject" - type="audio/ogg" - src="/media/A4.ogv"> + type="video/webm" + src="/media/A4.webm"> <track default id="trackObject" kind="subtitles" diff --git a/testing/web-platform/tests/content-security-policy/media-src/media-src-blocked.sub.html b/testing/web-platform/tests/content-security-policy/media-src/media-src-blocked.sub.html index b2b57dec64..16c36dfb20 100644 --- a/testing/web-platform/tests/content-security-policy/media-src/media-src-blocked.sub.html +++ b/testing/web-platform/tests/content-security-policy/media-src/media-src-blocked.sub.html @@ -14,7 +14,7 @@ <script> const otherOrigin = get_host_info().OTHER_ORIGIN; const audioUrl = otherOrigin + "/media/sound_5.oga"; - const videoUrl = otherOrigin + "/media/A4.ogv"; + const videoUrl = otherOrigin + "/media/A4.webm"; // Asynchronously returns the next `securitypolicyviolation` event. async function nextViolation() { @@ -29,7 +29,7 @@ const violationPromise = nextViolation(); const video = document.createElement("video"); - video.type = "video/ogg"; + video.type = "video/webm"; video.src = videoUrl; video.onloadeddata = reject; video.onerror = () => { resolve(violationPromise); }; @@ -49,7 +49,7 @@ video.onloadeddata = reject; const source = document.createElement("source"); - source.type = "video/ogg"; + source.type = "video/webm"; source.src = videoUrl; source.onerror = () => { resolve(violationPromise); }; @@ -64,7 +64,7 @@ const violationPromise = nextViolation(); const audio = document.createElement("audio"); - audio.type = "audio/ogg"; + audio.type = "audio/webm"; audio.src = audioUrl; audio.oncanplay = reject; audio.onloadedmetadata = reject; @@ -86,7 +86,7 @@ audio.onloadeddata = reject; const source = document.createElement("source"); - source.type = "audio/ogg"; + source.type = "audio/webm"; source.src = audioUrl; source.onerror = () => { resolve(violationPromise); }; diff --git a/testing/web-platform/tests/content-security-policy/media-src/media-src-redir-bug.sub.html b/testing/web-platform/tests/content-security-policy/media-src/media-src-redir-bug.sub.html index a0708bf5ed..ac8c167d5f 100644 --- a/testing/web-platform/tests/content-security-policy/media-src/media-src-redir-bug.sub.html +++ b/testing/web-platform/tests/content-security-policy/media-src/media-src-redir-bug.sub.html @@ -44,28 +44,28 @@ <video id="videoObject" width="320" height="240" controls onloadeddata="media_loaded(source_test)"> <source id="videoSourceObject" - type="video/ogg" + type="video/webm" onerror="media_error_handler(source_test)" - src="http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.ogv"> + src="http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.webm"> </video> <video id="videoObject2" width="320" height="240" controls onerror="media_error_handler(src_test)" onloadeddata="media_loaded(src_test)" - src="http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.ogv"> + src="http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.webm"> <video id="videoObject3" width="320" height="240" controls onloadeddata="media_loaded(source_redir_test)"> <source id="videoSourceObject" - type="video/ogg" + type="video/webm" onerror="media_error_handler(source_test)" - src="/common/redirect.py?location=http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.ogv"> + src="/common/redirect.py?location=http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.webm"> </video> <video id="videoObject2" width="320" height="240" controls onerror="media_error_handler(src_redir_test)" onloadeddata="media_loaded(src_redir_test)" - src="/common/redirect.py?location=http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.ogv"> + src="/common/redirect.py?location=http://{{domains[www2]}}:{{ports[http][0]}}/media/A4.webm"> </body> </html> diff --git a/testing/web-platform/tests/credential-management/digital-identity.https.html b/testing/web-platform/tests/credential-management/digital-identity.https.html index 82630e2a5b..b2f36d21ee 100644 --- a/testing/web-platform/tests/credential-management/digital-identity.https.html +++ b/testing/web-platform/tests/credential-management/digital-identity.https.html @@ -1,14 +1,22 @@ <!DOCTYPE html> <title>Digital Identity Credential tests.</title> <link rel="help" href="https://wicg.github.io/digital-identities/"> +<script src="/common/get-host-info.sub.js"></script> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/resources/testdriver.js"></script> <script src="/resources/testdriver-vendor.js"></script> <body> +<script type="module"> +import { buildValidNavigatorIdentityRequest } from './support/digital-identity-helper.js'; + +// This regex removes the filename from the path so that we just get +// the directory. +const host = get_host_info(); +const basePath = window.location.pathname.replace(/\/[^\/]*$/, '/'); +const remoteBaseURL = host.HTTPS_REMOTE_ORIGIN + basePath; -<script> // Builds valid digital identity request for navigator.credentials.get() API. function buildValidNavigatorCredentialsRequest() { return { @@ -34,28 +42,13 @@ function buildValidNavigatorCredentialsRequest() { }; } -// Builds valid digital identity request for navigator.identity.get() API. -function buildValidNavigatorIdentityRequest() { - return { - digital: { - providers: [{ - protocol: "protocol", - selector: { - format: ['mdoc'], - doctype: 'org.iso.18013.5.1.mDL', - fields: [ - 'org.iso.18013.5.1.family_name', - 'org.iso.18013.5.1.portrait', - ] - }, - params: { - nonce: '1234', - readerPublicKey: 'test_reader_public_key', - extraParamAsNeededByDigitalCredentials: true, - }, - }], - }, - }; +async function createIframeAndWaitForMessage(test, iframeUrl) { + const messageWatcher = new EventWatcher(test, window, "message"); + var iframe = document.createElement("iframe"); + iframe.src = iframeUrl; + document.body.appendChild(iframe); + const message = await messageWatcher.wait_for("message"); + return message.data; } // Requires browser to have mode where OS-presented digital-identity-prompt is @@ -96,7 +89,7 @@ promise_test(async t => { promise_test(async t => { let request = buildValidNavigatorIdentityRequest(); let credential = await navigator.identity.get(request); - assert_equals("protocol", credential.protocol); + assert_equals("urn:openid.net:oid4vp", credential.protocol); assert_equals("fake_test_token", credential.data); }, "navigator.identity.get() API works in toplevel frame."); @@ -109,6 +102,12 @@ promise_test(async t => { promise_test(async t => { let request = buildValidNavigatorIdentityRequest(); + request.digital.providers = []; + await promise_rejects_js(t, TypeError, navigator.identity.get(request)); +}, "navigator.identity.get() API fails if there are no providers."); + +promise_test(async t => { + let request = buildValidNavigatorIdentityRequest(); let providerCopy = structuredClone(request.digital.providers[0]); request.digital.providers.push(providerCopy); await promise_rejects_js(t, TypeError, navigator.identity.get(request)); @@ -122,4 +121,18 @@ promise_test(async t=> { abortController.abort(); await promise_rejects_dom(t, "AbortError", requestPromise); }, "navigator.identity.get() promise is rejected when the page aborts the request."); + +promise_test(async t=> { + const message = await createIframeAndWaitForMessage( + t, basePath + "support/digital-identity-iframe.html"); + assert_equals(message.result, "Pass"); + assert_equals(message.data, "fake_test_token"); +}, "navigator.identity.get() succeeds in same-origin iframe"); + +promise_test(async t=> { + const message = await createIframeAndWaitForMessage( + t, remoteBaseURL + "support/digital-identity-iframe.html"); + assert_equals(message.result, "Fail"); + assert_equals(message.errorType, "NotAllowedError"); +}, "navigator.identity.get() fails in cross-origin iframe"); </script> diff --git a/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-continue-on-disallowed.https.html b/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-continue-on-disallowed.https.html new file mode 100644 index 0000000000..fcda3a3dd5 --- /dev/null +++ b/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-continue-on-disallowed.https.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<title>Federated Credential Management API network request tests.</title> +<link rel="help" href="https://fedidcg.github.io/FedCM"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<body> + +<script type="module"> +import {fedcm_test, + request_options_with_mediation_required, + select_manifest, + fedcm_get_and_select_first_account} from '../support/fedcm-helper.sub.js'; + +fedcm_test(async t => { + // First, do a regular fedcm request so we that we can be considered + // a returning user below. + let options = request_options_with_mediation_required(); + await fedcm_get_and_select_first_account(t, options); + + // Now do a silent mediation request. + options = request_options_with_mediation_required('manifest_with_continue_on.json'); + options.mediation = 'silent'; + await select_manifest(t, options); + const cred_promise = fedcm_get_and_select_first_account(t, options); + return promise_rejects_dom(t, 'NetworkError', cred_promise); +}, "continue_on with mediation:silent should fail"); + +</script> diff --git a/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-continue-on-with-account.https.html b/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-continue-on-with-account.https.html new file mode 100644 index 0000000000..5bd8ef34fe --- /dev/null +++ b/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-continue-on-with-account.https.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<title>Federated Credential Management API network request tests.</title> +<link rel="help" href="https://fedidcg.github.io/FedCM"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<body> + +<script type="module"> +import {fedcm_test, + request_options_with_mediation_required, + select_manifest, + fedcm_get_and_select_first_account} from '../support/fedcm-helper.sub.js'; + +fedcm_test(async t => { + const options = request_options_with_mediation_required('manifest_with_continue_on.json'); + options.identity.providers[0].nonce = "accountId=jane_doe"; + await select_manifest(t, options); + const cred = await fedcm_get_and_select_first_account(t, options); + // This indicates the account that was selected in the dialog, + // not the account that was specified in IdentityProvider.resolve, + // hence we get 1234 instead of jane_doe. + assert_equals(cred.token, "account=1234"); + + // Now, jane_doe should be considered a returning user. Make sure + // auto reauthentication works. We have to use optional instead of + // silent so that we can open the continue_on popup. + options.mediation = "optional"; + return test_driver.bless('initiate FedCM request', async function() { + let cred2 = await navigator.credentials.get(options); + assert_equals(cred2.token, "account=jane_doe"); + }); +}, "continue_on and IdentityProvider.resolve work correctly."); + +</script> diff --git a/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-continue-on.https.html b/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-continue-on.https.html index 3ce1f51e37..c7da5384af 100644 --- a/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-continue-on.https.html +++ b/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-continue-on.https.html @@ -18,7 +18,7 @@ fedcm_test(async t => { const options = request_options_with_mediation_required('manifest_with_continue_on.json'); await select_manifest(t, options); const cred = await fedcm_get_and_select_first_account(t, options); - assert_equals(cred.token, "resolved token"); + assert_equals(cred.token, "account=1234"); }, "continue_on and IdentityProvider.resolve work correctly."); </script> diff --git a/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-userinfo-after-resolve.https.html b/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-userinfo-after-resolve.https.html index ef53ed4ffc..0521f4a2ab 100644 --- a/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-userinfo-after-resolve.https.html +++ b/testing/web-platform/tests/credential-management/fedcm-authz/fedcm-userinfo-after-resolve.https.html @@ -29,7 +29,7 @@ fedcm_test(async t => { const options = alt_request_options_with_mediation_required('manifest_with_continue_on.json'); await select_manifest(t, options); const cred = await fedcm_get_and_select_first_account(t, options); - assert_equals(cred.token, "resolved token"); + assert_equals(cred.token, "account=1234"); const iframe_in_idp_scope = `${alt_manifest_origin}/\ credential-management/support/fedcm/userinfo-iframe.html`; diff --git a/testing/web-platform/tests/credential-management/fedcm-context.https.html b/testing/web-platform/tests/credential-management/fedcm-context.https.html index bc1f96eafa..7b3e1032af 100644 --- a/testing/web-platform/tests/credential-management/fedcm-context.https.html +++ b/testing/web-platform/tests/credential-management/fedcm-context.https.html @@ -16,32 +16,32 @@ import {request_options_with_mediation_required, fedcm_test(async t => { let p = navigator.credentials.get(request_options_with_mediation_required()); - const title = await fedcm_get_title_promise(t); - assert_true(title.toLowerCase().includes('sign in')); + const result = await fedcm_get_title_promise(t); + assert_true(result.title.toLowerCase().includes('sign in')); window.test_driver.select_fedcm_account(0); return p; }, "FedCM call defaults to 'signin' context."); fedcm_test(async t => { let p = navigator.credentials.get(request_options_with_context("manifest.py", "signup")); - const title = await fedcm_get_title_promise(t); - assert_true(title.toLowerCase().includes('sign up')); + const result = await fedcm_get_title_promise(t); + assert_true(result.title.toLowerCase().includes('sign up')); window.test_driver.select_fedcm_account(0); return p; }, "FedCM with 'signup' context."); fedcm_test(async t => { let p = navigator.credentials.get(request_options_with_context("manifest.py", "use")); - const title = await fedcm_get_title_promise(t); - assert_true(title.toLowerCase().includes('use')); + const result = await fedcm_get_title_promise(t); + assert_true(result.title.toLowerCase().includes('use')); window.test_driver.select_fedcm_account(0); return p; }, "FedCM with 'use' context."); fedcm_test(async t => { let p = navigator.credentials.get(request_options_with_context("manifest.py", "continue")); - const title = await fedcm_get_title_promise(t); - assert_true(title.toLowerCase().includes('continue')); + const result = await fedcm_get_title_promise(t); + assert_true(result.title.toLowerCase().includes('continue')); window.test_driver.select_fedcm_account(0); return p; }, "FedCM with 'continue' context."); diff --git a/testing/web-platform/tests/credential-management/fedcm-csp.https.html b/testing/web-platform/tests/credential-management/fedcm-csp.https.html index 5925741438..c9a2456e4d 100644 --- a/testing/web-platform/tests/credential-management/fedcm-csp.https.html +++ b/testing/web-platform/tests/credential-management/fedcm-csp.https.html @@ -3,6 +3,8 @@ <link rel="help" href="https://fedidcg.github.io/FedCM"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> <body> diff --git a/testing/web-platform/tests/credential-management/fedcm-domainhint.https.html b/testing/web-platform/tests/credential-management/fedcm-domainhint.https.html index 3e07491d48..20b4569a05 100644 --- a/testing/web-platform/tests/credential-management/fedcm-domainhint.https.html +++ b/testing/web-platform/tests/credential-management/fedcm-domainhint.https.html @@ -22,7 +22,7 @@ fedcm_test(async t => { let options = request_options_with_domain_hint('manifest.py', 'nomatch'); - const cred = fedcm_get_and_select_first_account(t, options); + const cred = navigator.credentials.get(options); // We expect a mismatch dialog. const type = await fedcm_get_dialog_type_promise(t); assert_equals(type, 'ConfirmIdpLogin'); diff --git a/testing/web-platform/tests/credential-management/fedcm-endpoint-redirects.https.html b/testing/web-platform/tests/credential-management/fedcm-endpoint-redirects.https.html index cff5036f39..36a4de7900 100644 --- a/testing/web-platform/tests/credential-management/fedcm-endpoint-redirects.https.html +++ b/testing/web-platform/tests/credential-management/fedcm-endpoint-redirects.https.html @@ -20,7 +20,7 @@ fedcm_test(async t => { let test_options = request_options_with_mediation_required("manifest_redirect_accounts.json"); await select_manifest(t, test_options); - const cred = fedcm_get_and_select_first_account(t, test_options); + const cred = navigator.credentials.get(test_options); // We expect a mismatch dialog. const type = await fedcm_get_dialog_type_promise(t); assert_equals(type, 'ConfirmIdpLogin'); diff --git a/testing/web-platform/tests/credential-management/fedcm-iframe.https.html b/testing/web-platform/tests/credential-management/fedcm-iframe.https.html index dc0c17dea6..6a9bec677c 100644 --- a/testing/web-platform/tests/credential-management/fedcm-iframe.https.html +++ b/testing/web-platform/tests/credential-management/fedcm-iframe.https.html @@ -2,6 +2,8 @@ <link rel="help" href="https://wicg.github.io/FedCM"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> <script src="/common/get-host-info.sub.js"></script> <div id=log> <script type="module"> diff --git a/testing/web-platform/tests/credential-management/fedcm-loginhint.https.html b/testing/web-platform/tests/credential-management/fedcm-loginhint.https.html index edae955a76..fe35007a87 100644 --- a/testing/web-platform/tests/credential-management/fedcm-loginhint.https.html +++ b/testing/web-platform/tests/credential-management/fedcm-loginhint.https.html @@ -19,7 +19,7 @@ fedcm_test(async t => { await mark_signed_in(); let options = request_options_with_login_hint('manifest.py', 'nomatch'); - const cred = fedcm_get_and_select_first_account(t, options); + const cred = navigator.credentials.get(options); // We expect a mismatch dialog. const type = await fedcm_get_dialog_type_promise(t); assert_equals(type, 'ConfirmIdpLogin'); diff --git a/testing/web-platform/tests/credential-management/fedcm-same-site-none/fedcm-same-site-none.https.html b/testing/web-platform/tests/credential-management/fedcm-same-site-none/fedcm-same-site-none.https.html new file mode 100644 index 0000000000..77ecdaff9f --- /dev/null +++ b/testing/web-platform/tests/credential-management/fedcm-same-site-none/fedcm-same-site-none.https.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<title>Federated Credential Management API SameSite=None tests.</title> +<link rel="help" href="https://fedidcg.github.io/FedCM"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<body> + +<script type="module"> +import {fedcm_test, + alt_request_options_with_mediation_required, + select_manifest, + fedcm_get_and_select_first_account} from '../support/fedcm-helper.sub.js'; + +fedcm_test(async t => { + const options = alt_request_options_with_mediation_required('manifest_check_same_site_strict.json'); + await select_manifest(t, options); + const cred = await fedcm_get_and_select_first_account(t, options); + assert_equals(cred.token, "token"); + assert_equals(cred.isAutoSelected, false); +}, "FedCM requests should be considered cross-origin and therefore not send SameSite=Strict cookies."); + +</script> diff --git a/testing/web-platform/tests/credential-management/support/digital-identity-helper.js b/testing/web-platform/tests/credential-management/support/digital-identity-helper.js new file mode 100644 index 0000000000..2020d6cda7 --- /dev/null +++ b/testing/web-platform/tests/credential-management/support/digital-identity-helper.js @@ -0,0 +1,19 @@ +// Builds valid digital identity request for navigator.identity.get() API. +export function buildValidNavigatorIdentityRequest() { + return { + digital: { + providers: [{ + protocol: "urn:openid.net:oid4vp", + request: JSON.stringify({ + // Based on https://github.com/openid/OpenID4VP/issues/125 + client_id: "client.example.org", + client_id_scheme: "web-origin", + nonce: "n-0S6_WzA2Mj", + presentation_definition: { + // Presentation Exchange request, omitted for brevity + } + }), + }], + }, + }; +} diff --git a/testing/web-platform/tests/credential-management/support/digital-identity-iframe.html b/testing/web-platform/tests/credential-management/support/digital-identity-iframe.html new file mode 100644 index 0000000000..8e193ff09f --- /dev/null +++ b/testing/web-platform/tests/credential-management/support/digital-identity-iframe.html @@ -0,0 +1,27 @@ +<!doctype html> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script type="module"> +import { buildValidNavigatorIdentityRequest } from './digital-identity-helper.js'; + +// Loading digital-identity-iframe.html in the test will make a digital credential call on load, and +// trigger a postMessage upon completion. +// +// message { +// string result: "Pass" | "Fail" +// string data: credential.token +// string errorType: error.data +// } + +window.onload = async () => { + try { + let request = buildValidNavigatorIdentityRequest(); + let credential = await navigator.identity.get(request); + + window.top.postMessage({result: "Pass", data: credential.data}, '*'); + } catch (error) { + window.top.postMessage({result: "Fail", errorType: error.name}, '*'); + } +}; + +</script> diff --git a/testing/web-platform/tests/credential-management/support/fedcm-helper.sub.js b/testing/web-platform/tests/credential-management/support/fedcm-helper.sub.js index 765b3cc48a..f0031fa531 100644 --- a/testing/web-platform/tests/credential-management/support/fedcm-helper.sub.js +++ b/testing/web-platform/tests/credential-management/support/fedcm-helper.sub.js @@ -8,7 +8,9 @@ export function open_and_wait_for_popup(origin, path) { // We rely on the popup page to send us a message when done. const popup_message_handler = (event) => { - if (event.origin == origin) { + // We use new URL() to ensure the two origins are normalized the same + // way (especially so that default ports are handled identically). + if (new URL(event.origin).toString() == new URL(origin).toString()) { popup_window.close(); window.removeEventListener('message', popup_message_handler); resolve(); @@ -22,7 +24,7 @@ export function open_and_wait_for_popup(origin, path) { // Set the identity provider cookie. export function set_fedcm_cookie(host) { if (host == undefined) { - document.cookie = 'cookie=1; SameSite=Strict; Path=/credential-management/support; Secure'; + document.cookie = 'cookie=1; SameSite=None; Path=/credential-management/support; Secure'; return Promise.resolve(); } else { return open_and_wait_for_popup(host, '/credential-management/support/set_cookie'); @@ -102,6 +104,15 @@ credential-management/support/fedcm/${manifest_filename}`; // Test wrapper which does FedCM-specific setup. export function fedcm_test(test_func, test_name) { promise_test(async t => { + // Ensure we start from a clean slate. + await test_driver.delete_all_cookies(); + // Turn off delays that are not useful in tests. + try { + await test_driver.set_fedcm_delay_enabled(false); + } catch (e) { + // Failure is not critical; it just might slow down tests. + } + await set_fedcm_cookie(); await set_alt_fedcm_cookie(); await test_func(t); diff --git a/testing/web-platform/tests/credential-management/support/fedcm/accounts_check_same_site_strict.py b/testing/web-platform/tests/credential-management/support/fedcm/accounts_check_same_site_strict.py new file mode 100644 index 0000000000..a6f385feac --- /dev/null +++ b/testing/web-platform/tests/credential-management/support/fedcm/accounts_check_same_site_strict.py @@ -0,0 +1,28 @@ +import importlib +error_checker = importlib.import_module("credential-management.support.fedcm.request-params-check") + +def main(request, response): + request_error = error_checker.accountsCheck(request) + if (request_error): + return request_error + if request.cookies.get(b"same_site_strict") == b"1": + return (546, [], "Should not send SameSite=Strict cookies") + if request.headers.get(b"Sec-Fetch-Site") != b"cross-site": + return (538, [], "Wrong Sec-Fetch-Site header") + + response.headers.set(b"Content-Type", b"application/json") + + return """ +{ + "accounts": [{ + "id": "1234", + "given_name": "John", + "name": "John Doe", + "email": "john_doe@idp.example", + "picture": "https://idp.example/profile/123", + "approved_clients": ["123", "456", "789"], + "login_hints": ["john_doe"], + "domain_hints": ["idp.example", "example"] + }] +} +""" diff --git a/testing/web-platform/tests/credential-management/support/fedcm/accounts_no_approved_clients.py b/testing/web-platform/tests/credential-management/support/fedcm/accounts_no_approved_clients.py new file mode 100644 index 0000000000..faea06edc3 --- /dev/null +++ b/testing/web-platform/tests/credential-management/support/fedcm/accounts_no_approved_clients.py @@ -0,0 +1,30 @@ +import importlib +error_checker = importlib.import_module("credential-management.support.fedcm.request-params-check") + +def main(request, response): + request_error = error_checker.accountsCheck(request) + if (request_error): + return request_error + + response.headers.set(b"Content-Type", b"application/json") + + return """ +{ + "accounts": [{ + "id": "1234", + "given_name": "John", + "name": "John Doe", + "email": "john_doe@idp.example", + "picture": "https://idp.example/profile/123", + "login_hints": ["john_doe"], + "domain_hints": ["idp.example", "example"] + }, + { + "id": "jane_doe", + "given_name": "Jane", + "name": "Jane Doe", + "email": "jane_doe@idp.example", + "picture": "https://idp.example/profile/5678" + }] +} +""" diff --git a/testing/web-platform/tests/credential-management/support/fedcm/continue_on.py b/testing/web-platform/tests/credential-management/support/fedcm/continue_on.py index 42b4f3f8fd..1b4831b51d 100644 --- a/testing/web-platform/tests/credential-management/support/fedcm/continue_on.py +++ b/testing/web-platform/tests/credential-management/support/fedcm/continue_on.py @@ -8,5 +8,7 @@ def main(request, response): response.headers.set(b"Content-Type", b"application/json") - return "{\"continue_on\": \"resolve.html\"}" + account = request.POST.get(b"account_id").decode("utf-8") + nonce = request.POST.get(b"nonce").decode("utf-8") + return "{\"continue_on\": \"resolve.html?selected=%s&%s\"}" % (account, nonce) diff --git a/testing/web-platform/tests/credential-management/support/fedcm/manifest_check_same_site_strict.json b/testing/web-platform/tests/credential-management/support/fedcm/manifest_check_same_site_strict.json new file mode 100644 index 0000000000..d730415983 --- /dev/null +++ b/testing/web-platform/tests/credential-management/support/fedcm/manifest_check_same_site_strict.json @@ -0,0 +1,7 @@ +{ + "accounts_endpoint": "accounts_check_same_site_strict.py", + "client_metadata_endpoint": "client_metadata.py", + "id_assertion_endpoint": "token_check_same_site_strict.py", + "login_url": "login.html" +} + diff --git a/testing/web-platform/tests/credential-management/support/fedcm/manifest_with_continue_on.json b/testing/web-platform/tests/credential-management/support/fedcm/manifest_with_continue_on.json index 3f5a954b87..d7673c7e1b 100644 --- a/testing/web-platform/tests/credential-management/support/fedcm/manifest_with_continue_on.json +++ b/testing/web-platform/tests/credential-management/support/fedcm/manifest_with_continue_on.json @@ -1,5 +1,5 @@ { - "accounts_endpoint": "accounts.py", + "accounts_endpoint": "accounts_no_approved_clients.py", "client_metadata_endpoint": "client_metadata.py", "id_assertion_endpoint": "continue_on.py", "disconnect_endpoint": "disconnect.py", diff --git a/testing/web-platform/tests/credential-management/support/fedcm/request-params-check.py b/testing/web-platform/tests/credential-management/support/fedcm/request-params-check.py index b774496d5d..6c610e6e20 100644 --- a/testing/web-platform/tests/credential-management/support/fedcm/request-params-check.py +++ b/testing/web-platform/tests/credential-management/support/fedcm/request-params-check.py @@ -17,8 +17,6 @@ def commonUncredentialedRequestCheck(request): def commonCredentialedRequestCheck(request): if request.cookies.get(b"cookie") != b"1": return (537, [], "Missing cookie") - if request.headers.get(b"Sec-Fetch-Site") != b"none": - return (538, [], "Wrong Sec-Fetch-Site header") def commonPostCheck(request): if not request.headers.get(b"Origin"): diff --git a/testing/web-platform/tests/credential-management/support/fedcm/resolve.html b/testing/web-platform/tests/credential-management/support/fedcm/resolve.html index 87f5112cfd..dbdc28c324 100644 --- a/testing/web-platform/tests/credential-management/support/fedcm/resolve.html +++ b/testing/web-platform/tests/credential-management/support/fedcm/resolve.html @@ -1,7 +1,16 @@ <!DOCTYPE html> <script> async function doResolve() { - IdentityProvider.resolve("resolved token"); + let params = new URLSearchParams(document.location.search); + let options = {}; + if (params.get("accountId")) { + options.accountId = params.get("accountId"); + } + let token = "resolved token"; + if (params.get("selected")) { + token = "account=" + params.get("selected"); + } + IdentityProvider.resolve(token, options); } window.onload = doResolve; </script> diff --git a/testing/web-platform/tests/credential-management/support/fedcm/set_accounts_cookie.py b/testing/web-platform/tests/credential-management/support/fedcm/set_accounts_cookie.py index ab34992210..15adf11324 100644 --- a/testing/web-platform/tests/credential-management/support/fedcm/set_accounts_cookie.py +++ b/testing/web-platform/tests/credential-management/support/fedcm/set_accounts_cookie.py @@ -15,6 +15,7 @@ def main(request, response): // If this page was opened as a popup, notify the opener. if (window.opener) { window.opener.postMessage("done_loading", "*"); + window.close(); } </script> Sent header value: {}".format(header_value) diff --git a/testing/web-platform/tests/credential-management/support/fedcm/token_check_same_site_strict.py b/testing/web-platform/tests/credential-management/support/fedcm/token_check_same_site_strict.py new file mode 100644 index 0000000000..8a4b3a234b --- /dev/null +++ b/testing/web-platform/tests/credential-management/support/fedcm/token_check_same_site_strict.py @@ -0,0 +1,15 @@ +import importlib +error_checker = importlib.import_module("credential-management.support.fedcm.request-params-check") + +def main(request, response): + request_error = error_checker.tokenCheck(request) + if (request_error): + return request_error + if request.cookies.get(b"same_site_strict") == b"1": + return (546, [], "Should not send SameSite=Strict cookies") + + response.headers.set(b"Content-Type", b"application/json") + response.headers.set(b"Access-Control-Allow-Origin", request.headers.get(b"Origin")) + response.headers.set(b"Access-Control-Allow-Credentials", "true") + + return "{\"token\": \"token\"}" diff --git a/testing/web-platform/tests/credential-management/support/fencedframe-mark-signedin.html b/testing/web-platform/tests/credential-management/support/fencedframe-mark-signedin.html index 532db7047a..681fcd6787 100644 --- a/testing/web-platform/tests/credential-management/support/fencedframe-mark-signedin.html +++ b/testing/web-platform/tests/credential-management/support/fencedframe-mark-signedin.html @@ -3,13 +3,17 @@ <fencedframe></fencedframe> <script> -const url = new URL("mark_signedin", location.href); -document.querySelector("fencedframe").config = new FencedFrameConfig(url); - // If this page was opened as a popup, notify the opener when we are done loading. if (window.opener) { window.onload = function() { window.opener.postMessage("done_loading", "*"); }; } + +// This code is intentionally after the onload listener registration +// because it can throw if FencedFrameConfig is not defined. In that +// case, we still want to notify the opener to avoid a test timeout. +const url = new URL("mark_signedin", location.href); +document.querySelector("fencedframe").config = new FencedFrameConfig(url); + </script> diff --git a/testing/web-platform/tests/credential-management/support/set_cookie b/testing/web-platform/tests/credential-management/support/set_cookie index 1080b366e4..2c3196058a 100644 --- a/testing/web-platform/tests/credential-management/support/set_cookie +++ b/testing/web-platform/tests/credential-management/support/set_cookie @@ -6,6 +6,7 @@ // If this page was opened as a popup, notify the opener. if (window.opener) { window.opener.postMessage("done_loading", "*"); + window.close(); } </script> </body> diff --git a/testing/web-platform/tests/credential-management/support/set_cookie.headers b/testing/web-platform/tests/credential-management/support/set_cookie.headers index b19ff933a6..4226ff4c99 100644 --- a/testing/web-platform/tests/credential-management/support/set_cookie.headers +++ b/testing/web-platform/tests/credential-management/support/set_cookie.headers @@ -1,2 +1,3 @@ Content-Type: text/html Set-Cookie: cookie=1; SameSite=None; Secure +Set-Cookie: same_site_strict=1; SameSite=Strict; Secure diff --git a/testing/web-platform/tests/css/CSS2/floats/block-in-inline-become-float.html b/testing/web-platform/tests/css/CSS2/floats/block-in-inline-become-float.html new file mode 100644 index 0000000000..2a415e1838 --- /dev/null +++ b/testing/web-platform/tests/css/CSS2/floats/block-in-inline-become-float.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="http://crbug.com/332396356"> +<link rel="match" href="../../reference/ref-filled-green-100px-square.xht"> +<style> + .half { + display: inline-block; + vertical-align: top; + width: 50px; + height: 100px; + background: green; + } +</style> +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> +<div style="width:100px; height:100px; background:red;"> + <span> + <div class="half"></div><div id="trouble"></div><div class="half"></div> + </span> +</div> +<script> + document.body.offsetTop; + trouble.style.cssFloat = "left"; +</script> diff --git a/testing/web-platform/tests/css/CSS2/tables/reference/no_red_3x3_monospace_table-ref.xht b/testing/web-platform/tests/css/CSS2/tables/reference/no_red_3x3_monospace_table-ref.xht index 821ddc77b0..5425f5b175 100644 --- a/testing/web-platform/tests/css/CSS2/tables/reference/no_red_3x3_monospace_table-ref.xht +++ b/testing/web-platform/tests/css/CSS2/tables/reference/no_red_3x3_monospace_table-ref.xht @@ -17,6 +17,7 @@ position: absolute; top: 1px; left: 1px; + right: 1px; } td { padding: 0; diff --git a/testing/web-platform/tests/css/CSS2/tables/table-vertical-align-baseline-008.xht b/testing/web-platform/tests/css/CSS2/tables/table-vertical-align-baseline-008.xht new file mode 100644 index 0000000000..1200ac1ae8 --- /dev/null +++ b/testing/web-platform/tests/css/CSS2/tables/table-vertical-align-baseline-008.xht @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Test for baseline alignment of table cells</title> + <link rel="author" title="Oriol Brufau" href="obrufau@igalia.com" /> + <link rel="help" href="https://github.com/servo/servo/issues/31722" /> + <link rel="help" href="https://drafts.csswg.org/css2/#height-layout" /> + <link rel="match" href="../reference/ref-filled-green-100px-square.xht" /> + <meta name="assert" content="Since the cell is empty, the baseline of the row + is synthesized from the bottom content edge of the cell." /> + <style><![CDATA[ + .wrapper { float: left; font-size: 0; background: red } + .wrapper > * { width: 50px; height: 100px; background: green } + ]]></style> + </head> + <body> + <p>Test passes if there is a filled green square and <strong>no red</strong>.</p> + <div class="wrapper"> + <div style="display: inline-block"></div> + <table style="display: inline-table; border-spacing: 0"> + <td style="vertical-align: baseline; padding: 0"></td> + </table> + </div> + </body> +</html> diff --git a/testing/web-platform/tests/css/CSS2/tables/table-vertical-align-baseline-009.xht b/testing/web-platform/tests/css/CSS2/tables/table-vertical-align-baseline-009.xht new file mode 100644 index 0000000000..4620848ddc --- /dev/null +++ b/testing/web-platform/tests/css/CSS2/tables/table-vertical-align-baseline-009.xht @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>CSS Test: Test for baseline alignment of table cells</title> + <link rel="author" title="Oriol Brufau" href="obrufau@igalia.com" /> + <link rel="help" href="https://github.com/servo/servo/issues/31651" /> + <link rel="help" href="https://drafts.csswg.org/css2/#height-layout" /> + <link rel="match" href="../reference/ref-filled-green-100px-square.xht" /> + <meta name="assert" content="The baseline of the table should be aligned with the baseline of the cell in the first row." /> + <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> + <style><![CDATA[ + span { + font: 50px/1 Ahem; + color: green; + } + ]]></style> + </head> + <body> + <p>Test passes if there is a filled green square and <strong>no red</strong>.</p> + <div style="float: left; position: relative; font-size: 0; background: red"> + <span style="position: absolute; left: 0; bottom: 0">X</span> + <span>X</span> + <span style="display: inline-table"> + <span style="display: table-row">X</span> + <span style="display: table-row">X</span> + </span> + </div> + </body> +</html> diff --git a/testing/web-platform/tests/css/CSS2/text/text-align-justify-with-overflow-ref.html b/testing/web-platform/tests/css/CSS2/text/text-align-justify-with-overflow-ref.html new file mode 100644 index 0000000000..be2ac9dae1 --- /dev/null +++ b/testing/web-platform/tests/css/CSS2/text/text-align-justify-with-overflow-ref.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> + +<html> +<head> + <title>CSS Test: Overflowing content with text-align: justify</title> + <link rel="author" title="Martin Robinson" href="mrobinson@igalila.com"> + <link rel="help" href="https://drafts.csswg.org/css-text-3/#text-indent-property"> + <link rel="help" href="http://www.w3.org/TR/CSS21/text.html#indentation-prop"> + <link ref="help" href="https://drafts.csswg.org/css-text/#text-align-property"> + <style type="text/css"> + div { + width: 0px; + } + </style> +</head> + +<body> + +<!-- These two divs should overflow, one because of the length of the content + and the other because of `text-indent`. In both of these cases, `text-align: justify` + should be treated like `text-align: left` since there is no extra space to + distribute to justification opportunities. --> +<div>lorem ipsum lastline</div> +<div style="text-indent: 50px">lorem ipsum lastline</div> +</body> +</html> diff --git a/testing/web-platform/tests/css/CSS2/text/text-align-justify-with-overflow.html b/testing/web-platform/tests/css/CSS2/text/text-align-justify-with-overflow.html new file mode 100644 index 0000000000..927e9afd5a --- /dev/null +++ b/testing/web-platform/tests/css/CSS2/text/text-align-justify-with-overflow.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> + +<html> +<head> + <title>CSS Test: Overflowing content with text-align: justify</title> + <link rel="author" title="Martin Robinson" href="mrobinson@igalila.com"> + <link rel="help" href="https://drafts.csswg.org/css-text-3/#text-indent-property"> + <link rel="help" href="http://www.w3.org/TR/CSS21/text.html#indentation-prop"> + <link ref="help" href="https://drafts.csswg.org/css-text/#text-align-property"> + <link rel="match" href="text-align-justify-with-overflow-ref.html"/> + <style type="text/css"> + div { + text-align: justify; + width: 0px; + } + </style> +</head> + +<body> + +<!-- These two divs should overflow, one because of the length of the content + and the other because of `text-indent`. In both of these cases, `text-align: justify` + should be treated like `text-align: left` since there is no extra space to + distribute to justification opportunities. --> +<div>lorem ipsum lastline</div> +<div style="text-indent: 50px">lorem ipsum lastline</div> + +</body> +</html> diff --git a/testing/web-platform/tests/css/CSS2/text/text-indent-wrap-002-ref.html b/testing/web-platform/tests/css/CSS2/text/text-indent-wrap-002-ref.html new file mode 100644 index 0000000000..9750ce4c89 --- /dev/null +++ b/testing/web-platform/tests/css/CSS2/text/text-indent-wrap-002-ref.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> + +<html> +<head> + <title>CSS Test: text-indent test (multiple-lines and text-align: justify)</title> + <link rel="author" title="Martin Robinson" href="mrobinson@igalila.com"> + <link rel="help" href="https://drafts.csswg.org/css-text-3/#text-indent-property"> + <link rel="help" href="http://www.w3.org/TR/CSS21/text.html#indentation-prop"> + <link ref="help" href="https://drafts.csswg.org/css-text/#text-align-property"> + <style type="text/css"> + p { + text-align: justify; + } + span { + margin-left: 100px; + background: yellow; + } + </style> +</head> +<body> +<p><span>This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines.</span></p> +</body> +</html> diff --git a/testing/web-platform/tests/css/CSS2/text/text-indent-wrap-002.html b/testing/web-platform/tests/css/CSS2/text/text-indent-wrap-002.html new file mode 100644 index 0000000000..128cba3f12 --- /dev/null +++ b/testing/web-platform/tests/css/CSS2/text/text-indent-wrap-002.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> + +<html> +<head> + <title>CSS Test: text-indent test (multiple-lines and text-align: justify)</title> + <link rel="author" title="Martin Robinson" href="mrobinson@igalila.com"> + <link rel="help" href="https://drafts.csswg.org/css-text-3/#text-indent-property"> + <link rel="help" href="http://www.w3.org/TR/CSS21/text.html#indentation-prop"> + <link ref="help" href="https://drafts.csswg.org/css-text/#text-align-property"> + <link rel="match" href="text-indent-wrap-002-ref.html"/> + <style type="text/css"> + p { + text-indent: 100px; + text-align: justify; + } + span { background: yellow } + </style> +</head> +<body> +<p><span>This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines. This is a long piece of text that will wrap to multiple lines.</span></p> +</body> +</html> diff --git a/testing/web-platform/tests/css/compositing/mix-blend-mode/support/RGB_Circles.oggtheora.ogv b/testing/web-platform/tests/css/compositing/mix-blend-mode/support/RGB_Circles.oggtheora.ogv Binary files differdeleted file mode 100644 index baed6e2f5d..0000000000 --- a/testing/web-platform/tests/css/compositing/mix-blend-mode/support/RGB_Circles.oggtheora.ogv +++ /dev/null diff --git a/testing/web-platform/tests/css/css-align/gaps/gap-parsing-002.html b/testing/web-platform/tests/css/css-align/gaps/gap-parsing-002.html new file mode 100644 index 0000000000..f84abccf3c --- /dev/null +++ b/testing/web-platform/tests/css/css-align/gaps/gap-parsing-002.html @@ -0,0 +1,67 @@ +<!DOCTYPE html> +<html> +<head> +<title>CSS Align Gap Values: longhand and shorthand gap parsing for style attribute</title> +<link rel="help" href="https://drafts.csswg.org/css-align/#gaps"> +<link rel="author" title="Karl Dubost" href="mailto:karlcow@apple.com"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("gap", "normal"); +test_valid_value("gap", "10px"); +test_valid_value("gap", "normal normal", "normal"); +test_valid_value("gap", "10px 10px", "10px"); +test_valid_value("column-gap", "normal"); +test_valid_value("column-gap", "10px"); +test_valid_value("row-gap", "normal"); +test_valid_value("row-gap", "10px"); + +const div = document.createElement("div"); +const style = div.style; + +test(function() { + style.cssText = "row-gap: normal; column-gap: normal;"; + assert_equals(style.cssText, "gap: normal;"); +}, "'row-gap: normal; column-gap: normal;' is serialized to 'gap: normal;'"); + +test(function() { + style.cssText = "row-gap: normal; column-gap: normal;"; + assert_equals(style.getPropertyValue('gap'), "normal"); +}, "getPropertyValue for 'row-gap: normal; column-gap: normal;' returns 'normal'"); + +test(function() { + style.cssText = "row-gap: 10px; column-gap: 10px;"; + assert_equals(style.cssText, "gap: 10px;"); +}, "'row-gap: 10px; column-gap: 10px;' is serialized to 'gap: 10px;'"); + +test(function() { + style.cssText = "row-gap: 10px; column-gap: 10px;"; + assert_equals(style.getPropertyValue('gap'), "10px"); +}, "getPropertyValue for 'row-gap: 10px; column-gap: 10px;' returns '10px'"); + +test(function() { + style.cssText = "row-gap: 10px; column-gap: normal;"; + assert_equals(style.cssText, "gap: 10px normal;"); +}, "'row-gap: 10px; column-gap: normal;' is serialized to 'gap: 10px normal;'"); + +test(function() { + style.cssText = "row-gap: 10px; column-gap: normal;"; + assert_equals(style.getPropertyValue('gap'), "10px normal"); +}, "getPropertyValue for 'row-gap: 10px; column-gap: normal;' returns '10px normal'"); + +test(function() { + style.cssText = "column-gap: normal; row-gap: 10px;"; + assert_equals(style.cssText, "gap: 10px normal;"); +}, "'column-gap: normal; row-gap: 10px;' is serialized to 'gap: 10px normal;'"); + +test(function() { + style.cssText = "column-gap: normal; row-gap: 10px;"; + assert_equals(style.getPropertyValue('gap'), "10px normal"); +}, "getPropertyValue for 'column-gap: normal; row-gap: 10px;' returns '10px normal'"); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-align/parsing/gap-shorthand.html b/testing/web-platform/tests/css/css-align/parsing/gap-shorthand.html index 52397fb744..6095104fa3 100644 --- a/testing/web-platform/tests/css/css-align/parsing/gap-shorthand.html +++ b/testing/web-platform/tests/css/css-align/parsing/gap-shorthand.html @@ -4,7 +4,7 @@ <meta charset="utf-8"> <title>CSS Box Alignment Level 3: gap sets longhands</title> <link rel="help" href="https://drafts.csswg.org/css-align-3/#propdef-gap"> -<meta name="assert" content="row-gap supports the full grammar '<row-gap> <column-gap>?'."> +<meta name="assert" content="gap supports the full grammar '<row-gap> <column-gap>?'."> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/css/support/shorthand-testcommon.js"></script> diff --git a/testing/web-platform/tests/css/css-align/parsing/grid-column-gap-computed.html b/testing/web-platform/tests/css/css-align/parsing/grid-column-gap-computed.html new file mode 100644 index 0000000000..ae43a2a3f5 --- /dev/null +++ b/testing/web-platform/tests/css/css-align/parsing/grid-column-gap-computed.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: getComputedStyle().gridColumnGap</title> +<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy"> +<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com"> +<meta name="assert" content="grid-column-gap computed value is as specified."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +</head> +<body> +<div id="target"></div> +<style> + #target { + font-size: 40px; + } +</style> +<script> +test_computed_value("grid-column-gap", "normal"); + +test_computed_value("grid-column-gap", "calc(10px + 0.5em)", "30px"); +test_computed_value("grid-column-gap", "calc(10px - 0.5em)", "0px"); +test_computed_value("grid-column-gap", "40%"); +test_computed_value("grid-column-gap", "calc(50% + 60px)"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-align/parsing/grid-column-gap-invalid.html b/testing/web-platform/tests/css/css-align/parsing/grid-column-gap-invalid.html new file mode 100644 index 0000000000..a43f05dbe1 --- /dev/null +++ b/testing/web-platform/tests/css/css-align/parsing/grid-column-gap-invalid.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: parsing grid-column-gap with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy"> +<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com"> +<meta name="assert" content="grid-column-gap supports only the grammar '<length-percentage> | normal'."> +<meta name="assert" content="grid-column-gap rejects negative <length-percentage>."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("grid-column-gap", "auto"); + +test_invalid_value("grid-column-gap", "10"); +test_invalid_value("grid-column-gap", "10px 20px"); +test_invalid_value("grid-column-gap", "-1px"); +test_invalid_value("grid-column-gap", "-10%"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-align/parsing/grid-column-gap-valid.html b/testing/web-platform/tests/css/css-align/parsing/grid-column-gap-valid.html new file mode 100644 index 0000000000..b96e36e434 --- /dev/null +++ b/testing/web-platform/tests/css/css-align/parsing/grid-column-gap-valid.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: parsing grid-column-gap with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy"> +<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com"> +<meta name="assert" content="grid-column-gap supports the full grammar '<length-percentage> | normal'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("grid-column-gap", "normal"); + +test_valid_value("grid-column-gap", "0", "0px"); +test_valid_value("grid-column-gap", "1px"); +test_valid_value("grid-column-gap", "calc(2em + 3ex)"); +test_valid_value("grid-column-gap", "4%"); +test_valid_value("grid-column-gap", "5vmin"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-align/parsing/grid-gap-computed.html b/testing/web-platform/tests/css/css-align/parsing/grid-gap-computed.html new file mode 100644 index 0000000000..eee24ad653 --- /dev/null +++ b/testing/web-platform/tests/css/css-align/parsing/grid-gap-computed.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: getComputedStyle().gridGap</title> +<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy"> +<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com"> +<meta name="assert" content="grid-gap computed value is a pair of keyword or <length-percentage> values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +<style> + #target { + font-size: 40px; + } +</style> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("grid-gap", "normal"); +test_computed_value("grid-gap", "10px"); +test_computed_value("grid-gap", "20%"); +test_computed_value("grid-gap", "calc(20% + 10px)"); +test_computed_value("grid-gap", "calc(-0.5em + 10px)", "0px"); +test_computed_value("grid-gap", "calc(0.5em + 10px)", "30px"); + +test_computed_value("grid-gap", "normal 10px"); +test_computed_value("grid-gap", "10px 20%"); +test_computed_value("grid-gap", "20% calc(20% + 10px)"); +test_computed_value("grid-gap", "calc(20% + 10px) normal"); + +test_computed_value("grid-gap", "calc(-0.5em + 10px) calc(0.5em + 10px)", "0px 30px"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-align/parsing/grid-gap-invalid.html b/testing/web-platform/tests/css/css-align/parsing/grid-gap-invalid.html new file mode 100644 index 0000000000..7536b091d8 --- /dev/null +++ b/testing/web-platform/tests/css/css-align/parsing/grid-gap-invalid.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: parsing grid-gap with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy"> +<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com"> +<meta name="assert" content="grid-gap supports only the grammar '<grid-row-gap> <grid-column-gap>?'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("grid-gap", "auto"); +test_invalid_value("grid-gap", "-10px"); + +test_invalid_value("grid-gap", "10px 20% 30px"); +test_invalid_value("grid-gap", "normal 10px normal"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-align/parsing/grid-gap-shorthand.html b/testing/web-platform/tests/css/css-align/parsing/grid-gap-shorthand.html new file mode 100644 index 0000000000..61ded44176 --- /dev/null +++ b/testing/web-platform/tests/css/css-align/parsing/grid-gap-shorthand.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: grid-gap sets longhands</title> +<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy"> +<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com"> +<meta name="assert" content="grid-gap supports the full grammar '<grid-row-gap> <column-gap>?'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/shorthand-testcommon.js"></script> +</head> +<body> +<script> +test_shorthand_value('gap', 'normal', { + 'row-gap': 'normal', + 'column-gap': 'normal' +}); + +test_shorthand_value('gap', '10px 20%', { + 'row-gap': '10px', + 'column-gap': '20%' +}); + +test_shorthand_value('gap', '10px normal', { + 'row-gap': '10px', + 'column-gap': 'normal' +}); + +test_shorthand_value('gap', 'normal calc(20% + 10px)', { + 'grid-row-gap': 'normal', + 'grid-column-gap': 'calc(20% + 10px)' +}); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-align/parsing/grid-gap-valid.html b/testing/web-platform/tests/css/css-align/parsing/grid-gap-valid.html new file mode 100644 index 0000000000..c70b9205fd --- /dev/null +++ b/testing/web-platform/tests/css/css-align/parsing/grid-gap-valid.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: parsing grid-gap with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy"> +<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com"> +<meta name="assert" content="grid-gap supports the full grammar '<grid-row-gap> <grid-column-gap>?'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("grid-gap", "normal normal", "normal"); +test_valid_value("grid-gap", "10px 10px", "10px"); +test_valid_value("grid-gap", "20% 20%", "20%"); +test_valid_value("grid-gap", "calc(20% + 10px) calc(20% + 10px)", "calc(20% + 10px)"); + +test_valid_value("grid-gap", "normal 10px"); +test_valid_value("grid-gap", "10px 20%"); +test_valid_value("grid-gap", "20% calc(20% + 10px)"); +test_valid_value("grid-gap", "calc(20% + 10px) 0px"); +test_valid_value("grid-gap", "0px normal"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-align/parsing/grid-row-gap-computed.html b/testing/web-platform/tests/css/css-align/parsing/grid-row-gap-computed.html new file mode 100644 index 0000000000..d108cb2737 --- /dev/null +++ b/testing/web-platform/tests/css/css-align/parsing/grid-row-gap-computed.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: getComputedStyle().gridRowGap</title> +<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy"> +<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com"> +<meta name="assert" content="grid-row-gap computed value is a specified keyword or a computed <length-percentage>."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +<style> + #target { + font-size: 40px; + } +</style> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("grid-row-gap", "normal"); + +test_computed_value("grid-row-gap", "10px"); +test_computed_value("grid-row-gap", "20%"); +test_computed_value("grid-row-gap", "calc(20% + 10px)"); + +test_computed_value("grid-row-gap", "calc(-0.5em + 10px)", "0px"); +test_computed_value("grid-row-gap", "calc(0.5em + 10px)", "30px"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-align/parsing/grid-row-gap-invalid.html b/testing/web-platform/tests/css/css-align/parsing/grid-row-gap-invalid.html new file mode 100644 index 0000000000..39d0835a48 --- /dev/null +++ b/testing/web-platform/tests/css/css-align/parsing/grid-row-gap-invalid.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: parsing grid-row-gap with invalid values</title> +<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy"> +<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com"> +<meta name="assert" content="grid-row-gap supports only the grammar 'normal | <length-percentage>'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_invalid_value("grid-row-gap", "auto"); +test_invalid_value("grid-row-gap", "-10px"); + +test_invalid_value("grid-row-gap", "10px 20%"); +test_invalid_value("grid-row-gap", "normal 10px"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-align/parsing/grid-row-gap-valid.html b/testing/web-platform/tests/css/css-align/parsing/grid-row-gap-valid.html new file mode 100644 index 0000000000..fe12376395 --- /dev/null +++ b/testing/web-platform/tests/css/css-align/parsing/grid-row-gap-valid.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>CSS Box Alignment Level 3: parsing grid-row-gap with valid values</title> +<link rel="help" href="https://drafts.csswg.org/css-align/#grid-gap-legacy"> +<link rel="author" title="Takuya Kurimoto" href="mailto:takuya004869@gmail.com"> +<meta name="assert" content="grid-row-gap supports the full grammar 'normal | <length-percentage>'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +test_valid_value("grid-row-gap", "normal"); + +test_valid_value("grid-row-gap", "10px"); +test_valid_value("grid-row-gap", "20%"); +test_valid_value("grid-row-gap", "calc(20% + 10px)"); +test_valid_value("grid-row-gap", "0", "0px"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-htb.html b/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-htb.html index 7012208044..20abb2ed09 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-htb.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-htb.html @@ -21,7 +21,7 @@ } .target { - anchor-default: --anchor; + position-anchor: --anchor; position: absolute; background: cyan; justify-self: anchor-center; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-vrl.html b/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-vrl.html index 584424d306..099d9cd15b 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-vrl.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-center-htb-vrl.html @@ -22,7 +22,7 @@ .target { writing-mode: vertical-rl; - anchor-default: --anchor; + position-anchor: --anchor; position: absolute; background: cyan; align-self: anchor-center; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-htb.html b/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-htb.html index c7ee230262..3e4f485cec 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-htb.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-htb.html @@ -23,7 +23,7 @@ .target { writing-mode: horizontal-tb; - anchor-default: --anchor; + position-anchor: --anchor; position: absolute; background: cyan; align-self: anchor-center; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-vrl.html b/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-vrl.html index d314dc7f2f..fe40c73141 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-vrl.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-center-vrl-vrl.html @@ -22,7 +22,7 @@ } .target { - anchor-default: --anchor; + position-anchor: --anchor; position: absolute; background: cyan; justify-self: anchor-center; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-default-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-default-001.html deleted file mode 100644 index 1700a84aa8..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-default-001.html +++ /dev/null @@ -1,55 +0,0 @@ -<!DOCTYPE html> -<title>Tests the 'anchor-default' property</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-default"> -<link rel="author" href="mailto:xiaochengh@chromium.org"> -<link rel="match" href="anchor-default-ref.html"> -<style> -.anchor { - width: 100px; - height: 100px; - background: orange; -} - -.target { - position: fixed; - background: lime; - position-try-options: --pf; - left: 999999px; /* force fallback */ -} - -@position-try --pf { - top: anchor(bottom, 0px); - left: anchor(left, 0px); - width: anchor-size(width, 0px); - height: anchor-size(height, 0px); -} - -body { - margin: 0; -} - -#anchor1 { - anchor-name: --a1; - margin-left: 100px; -} - -#target1 { - anchor-default: --a1; -} - -#anchor2 { - anchor-name: --a2; - margin-left: 300px; - margin-top: 100px; -} - -#target2 { - anchor-default: --a2; -} -</style> - -<div id="anchor1" class="anchor">anchor1</div> -<div id="anchor2" class="anchor">anchor2</div> - -<div id="target1" class="target">target1</div> -<div id="target2" class="target">target2</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-default-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-default-002.html deleted file mode 100644 index c0a962ad36..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-default-002.html +++ /dev/null @@ -1,69 +0,0 @@ -<!DOCTYPE html> -<title>Tests that 'anchor-default' property value is tree-scoped</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-default"> -<link rel="author" href="mailto:xiaochengh@chromium.org"> -<link rel="match" href="anchor-default-ref.html"> -<style> -.anchor { - width: 100px; - height: 100px; - background: orange; -} - -.target { - position: fixed; - background: lime; - position-try-options: --pf; - left: 999999px; /* force fallback */ -} - -@position-try --pf { - top: anchor(bottom, 0px); - left: anchor(left, 0px); - width: anchor-size(width, 0px); - height: anchor-size(height, 0px); -} - -body { - margin: 0; -} - -#fake-anchor { - anchor-name: --a; -} - -#anchor1 { - margin-left: 100px; -} - -#anchor2 { - margin-left: 300px; - margin-top: 100px; -} - -</style> - -<div id="fake-anchor"></div> - -<div id="anchor1" class="anchor"> - anchor1 - <div id="target1" class="target">target1</div> -</div> - -<div id="anchor2" class="anchor"> - anchor2 - <div id="target2" class="target">target2</div> -</div> - -<script> -for (let host of document.querySelectorAll('.anchor')) { - let shadow = host.attachShadow({mode: 'open'}); - shadow.innerHTML = ` - <style> - :host { anchor-name: --a; } - ::slotted(.target) { anchor-default: --a; } - </style> - <slot></slot> - `; -} -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-default-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-default-003.html deleted file mode 100644 index 00c2032434..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-default-003.html +++ /dev/null @@ -1,55 +0,0 @@ -<!DOCTYPE html> -<title>Tests that layout is updated on anchor-default value changes</title> -<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/#anchor-default"> -<link rel="author" href="mailto:xiaochengh@chromium.org"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> - -<style> -#target { - position: fixed; - width: 100px; - height: 100px; - background: lime; - top: anchor(top); - left: anchor(right); - anchor-default: --a; -} - -#target.after { - anchor-default: --b; -} - -#anchor1, #anchor2 { - width: 100px; - height: 100px; - background: orange; -} - -#anchor1 { - anchor-name: --a; -} - -#anchor2 { - margin-left: 100px; - anchor-name: --b; -} - -body { - margin: 0; -} -</style> - -<div id="anchor1"></div> -<div id="anchor2"></div> -<div id="target"></div> - -<script> -test(() => { - document.body.offsetLeft; // Force layout - target.classList.add('after'); - // #target should be anchored to #anchor2 now - assert_equals(target.offsetLeft, 200); - assert_equals(target.offsetTop, 100); -}, 'Layout is updated on `anchor-default` changes'); -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-default-basics.html b/testing/web-platform/tests/css/css-anchor-position/anchor-default-basics.html deleted file mode 100644 index 783cb539cc..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-default-basics.html +++ /dev/null @@ -1,42 +0,0 @@ -<!DOCTYPE html> -<title>Tests basics of the 'anchor-default' property</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-anchor-default"> -<link rel="author" href="mailto:xiaochengh@chromium.org"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<script src="/css/support/computed-testcommon.js"></script> -<script src="/css/support/inheritance-testcommon.js"></script> -<script src="/css/support/interpolation-testcommon.js"></script> - -<div id="container"> - <div id="target"></div> -</div> - -<script> -// anchor-default: <anchor-element> -// <anchor-element> = implicit | <dashed-ident> -test_valid_value('anchor-default', 'implicit'); -test_valid_value('anchor-default', '--foo'); -test_invalid_value('anchor-default', 'none'); -test_invalid_value('anchor-default', 'foo-bar'); -test_invalid_value('anchor-default', '--foo --bar') -test_invalid_value('anchor-default', '--foo, --bar') -test_invalid_value('anchor-default', '100px'); -test_invalid_value('anchor-default', '100%'); - -// Computed value: as specified -test_computed_value('anchor-default', 'implicit'); -test_computed_value('anchor-default', '--foo'); - -// Initial: implicit -// Inherited: no -assert_not_inherited('anchor-default', 'implicit', '--foo'); - -// Animation type: discrete -test_no_interpolation({ - property: 'anchor-default', - from: '--foo', - to: 'implicit', -}); -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-002.html index ae697fcc74..a3e00d5048 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-002.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-002.html @@ -13,7 +13,7 @@ body { margin: 0; } -.cb { +.rel { position: relative; background: lightgray; } @@ -36,7 +36,7 @@ body { <!-- anchor is fragmented in second and third columns --> <div class="multicol" id="test1"> - <div class="cb"> + <div class="rel"> <div class="spacer"></div> <div class="anchor"></div> <div class="target"></div> @@ -51,7 +51,7 @@ body { height: 100px; } -#test1 .cb { +#test1 .rel{ width: 100px; height: 300px; } @@ -79,19 +79,23 @@ test(() => { <div id="test2" style="font: 20px/1 Ahem; width: 11em"> - Lorem - <span class="cb"> - ipsum <span class="anchor">dolor</span> sit - <span class="target"></span> - </span> - amet.<br> + <div> + Lorem + <span class="rel"> + ipsum <span class="anchor">dolor</span> sit + <span class="target"></span> + </span> + amet. + </div> - Lorem - <span class="cb"> - ipsum dolor <span class="anchor">sit</span> - <span class="target"></span> - </span> - amet.<br> + <div> + Lorem + <span class="rel"> + ipsum dolor <span class="anchor">sit</span> + <span class="target"></span> + </span> + amet. + </div> </div> <script> test(() => { diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-003.html index da9ec4a145..fc384ab4cd 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-003.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-getComputedStyle-003.html @@ -52,7 +52,7 @@ body { } #target1 { - anchor-default: --a1; + position-anchor: --a1; } #anchor2 { @@ -62,7 +62,7 @@ body { } #target2 { - anchor-default: --a2; + position-anchor: --a2; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-inline-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-inline-001.html index dba3472f5b..d65936ad86 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-name-inline-001.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-name-inline-001.html @@ -48,7 +48,7 @@ <span class="relpos"> <span class="anchor">12</span> <span class="anchor abspos">123</span> - <span class="target" data-expected-width=20></span> + <span class="target" data-expected-width=30></span> </span> <span class="target" data-expected-width=30></span> </span> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained-dynamic.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained-dynamic.html deleted file mode 100644 index 50cec96f36..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained-dynamic.html +++ /dev/null @@ -1,71 +0,0 @@ -<!DOCTYPE html> -<title>CSS Anchor Positioning: anchor-name is style contained - dynamic containment</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#name"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/resources/check-layout-th.js"></script> -<script src="support/test-common.js"></script> -<style> - .container { - position: relative; - } - .anchor { - width: 100px; - height: 100px; - anchor-name: var(--anchor-name); - } - .contain-style { - contain: style; - } - .target { - position: absolute; - anchor-default: var(--anchor-name); - top: anchor(bottom, 50px); - } - #a1 { --anchor-name: --a1; } - #a2 { --anchor-name: --a2; } - #a3 { --anchor-name: --a3; } -</style> -<div id="a1" class="container"> - <div class="anchor"></div> - <div class="contain-style"> - <div class="anchor"></div> - </div> - <div class="target" data-offset-y="100"></div> -</div> -<div id="a2" class="container"> - <div class="anchor"></div> - <div class="anchor"></div> - <div class="contain-style"> - <div class="target" data-offset-y="50"></div> - </div> -</div> -<div id="a3" class="container"> - <div class="anchor contain-style"> - <div class="target" data-offset-y="50"></div> - </div> -</div> -<script type="module"> - await checkLayoutForAnchorPos('.target'); - - const t1 = document.querySelector("#a1 .target"); - const t2 = document.querySelector("#a2 .target"); - const t3 = document.querySelector("#a3 .target"); - const t4 = document.querySelector("#a4 .target"); - const t5 = document.querySelector("#a5 .target"); - t1.setAttribute("data-offset-y", "200"); - t2.setAttribute("data-offset-y", "200"); - t3.setAttribute("data-offset-y", "100"); - for (let element of document.querySelectorAll(".contain-style")) { - element.style.contain = "none"; - } - await checkLayoutForAnchorPos('.target'); - - t1.setAttribute("data-offset-y", "100"); - t2.setAttribute("data-offset-y", "50"); - t3.setAttribute("data-offset-y", "50"); - for (let element of document.querySelectorAll(".contain-style")) { - element.style.contain = ""; - } - await checkLayoutForAnchorPos('.target'); -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained.html b/testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained.html deleted file mode 100644 index a529575889..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-name-style-contained.html +++ /dev/null @@ -1,60 +0,0 @@ -<!DOCTYPE html> -<title>CSS Anchor Positioning: anchor-name is style contained</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#name"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/resources/check-layout-th.js"></script> -<script src="support/test-common.js"></script> -<style> - .container { - position: relative; - } - .anchor { - width: 100px; - height: 100px; - anchor-name: var(--anchor-name); - } - .contain-style { - contain: style; - } - .target { - position: absolute; - anchor-default: var(--anchor-name); - top: anchor(bottom, 50px); - } - #a1 { --anchor-name: --a1; } - #a2 { --anchor-name: --a2; } - #a3 { --anchor-name: --a3; } - #a4 { --anchor-name: --a4; } - #a5 { --anchor-name: --a5; } -</style> -<body onload="checkLayoutForAnchorPos('.target')"> -<div id="a1" class="container"> - <div class="anchor"></div> - <div class="contain-style"> - <div class="anchor"></div> - </div> - <div class="target" data-offset-y="100"></div> -</div> -<div id="a2" class="container"> - <div class="anchor"></div> - <div class="anchor"></div> - <div class="contain-style"> - <div class="target" data-offset-y="50"></div> - </div> -</div> -<div id="a3" class="container"> - <div class="contain-style"> - <div class="anchor"></div> - <div class="target" data-offset-y="100"></div> - </div> -</div> -<div id="a4" class="container"> - <div class="anchor contain-style"> - <div class="target" data-offset-y="50"></div> - </div> -</div> -<div id="a5" class="container"> - <div class="anchor contain-style"></div> - <div class="target" data-offset-y="100"></div> -</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-circular.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-circular.html index 8efbeb09e2..85fca57421 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-position-circular.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-circular.html @@ -15,7 +15,7 @@ div { #anchored1 { position: absolute; - anchor-default: --a1; + position-anchor: --a1; left: anchor(--a1 left); top: anchor(--a1 bottom); background: orange; @@ -24,7 +24,7 @@ div { #anchored2 { position: absolute; - anchor-default: --a2; + position-anchor: --a2; left: anchor(--a2 left); top: anchor(--a2 bottom); background: green; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-004.html index 146703e628..b7944652a0 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-004.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-004.html @@ -30,7 +30,7 @@ </style> <body> <div class="cb"> - <div style="contain: layout size paint; height: 50px"> + <div style="contain: strict; height: 50px"> <div class="spacer" style="height: 10px"></div> <div id="anchor1"></div> </div> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-005.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-005.html new file mode 100644 index 0000000000..b22a060cbd --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-dynamic-005.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#anchor-pos"> +<link rel="match" href="../reference/ref-filled-green-100px-square-only.html"> +<style> + #cb { + position: relative; + width: 100px; + height: 100px; + overflow: clip; + } + #anchor { + position: absolute; + inset: 0; + background: green; + width: 100px; + height: 100px; + anchor-name: --a; + } + #bug { + position: absolute; + width: 100px; + height: 100px; + background: red; + left: anchor(right); + top: anchor(top); + position-anchor: --a; + } + #target { + position: absolute; + top: anchor(top); + } +</style> +<p>Test passes if there is a filled green square.</p> +<div id="cb"> + <div id="anchor"></div> + <div> + <div id=bug></div> + </div> + <div id=target></div> +</div> +<script> +document.body.offsetTop; +document.getElementById('target').style.top = 'calc(anchor(top) + 10px)'; +</script> + diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-001.html index 055459551b..a8513bb74c 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-001.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-001.html @@ -21,7 +21,7 @@ width: 100px; height: 100px; background: lime; - anchor-default: --a; + position-anchor: --a; outline: none; } diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-002.html index a87a9d7eed..9ce0b8e5f9 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-002.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-002.html @@ -21,7 +21,7 @@ width: 100px; height: 100px; background: lime; - anchor-default: --a; + position-anchor: --a; outline: none; } diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-003.html index 96d5219c5c..3bc815af0c 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-003.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-003.html @@ -21,7 +21,7 @@ width: 100px; height: 100px; background: lime; - anchor-default: --a; + position-anchor: --a; outline: none; } diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-004.html index c986e3f98d..ad0a7b8b32 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-004.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-004.html @@ -21,7 +21,7 @@ width: 100px; height: 100px; background: lime; - anchor-default: --a; + position-anchor: --a; outline: none; } diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-005.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-005.html index cf39c77736..51aa482aee 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-005.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-005.html @@ -22,7 +22,7 @@ width: 100px; height: 100px; background: lime; - anchor-default: --a; + position-anchor: --a; } body { diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-006.html b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-006.html index c13284b854..a3b9e63c06 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-006.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-position-top-layer-006.html @@ -22,7 +22,7 @@ width: 100px; height: 100px; background: lime; - anchor-default: --a; + position-anchor: --a; } body { diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-001.html index 8609795c8a..b9dfc56e2e 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-001.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-001.html @@ -40,7 +40,7 @@ body { position: absolute; left: anchor(--anchor left); bottom: anchor(--anchor top); - anchor-default: --anchor; + position-anchor: --anchor; } #outer-anchored { @@ -48,7 +48,7 @@ body { position: absolute; left: anchor(--anchor left); top: anchor(--anchor bottom); - anchor-default: --anchor; + position-anchor: --anchor; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-002.html index 8ef6f500a1..2c51e6a1a3 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-002.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-002.html @@ -45,7 +45,7 @@ body { height: 50px; top: anchor(--a1 top); left: anchor(--a1 right); - anchor-default: --a1; + position-anchor: --a1; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-003.html index c1b31c0bec..6c0dd08355 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-003.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-003.html @@ -47,7 +47,7 @@ body { position: absolute; left: anchor(--a left); bottom: anchor(--a top); - anchor-default: --a; + position-anchor: --a; width: 50px; height: 50px; background: lime; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-004.html index d08279118d..c0e0afb23f 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-004.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-004.html @@ -32,7 +32,7 @@ body { .target { position: absolute; - anchor-default: --a; + position-anchor: --a; top: anchor(--a bottom); left: anchor(--a left); color: lime; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-005.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-005.html index a9a7d24d2b..f8f26b719f 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-005.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-005.html @@ -33,7 +33,7 @@ body { width: 100px; height: 100px; bottom: anchor(--a top); - anchor-default: --a; + position-anchor: --a; background: lime; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-006.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-006.html index 2ffd026b55..9c8a8c0ac2 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-006.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-006.html @@ -50,21 +50,21 @@ body { /* Needs scroll adjustment in x axis only */ #target1 { - anchor-default: --a1; + position-anchor: --a1; left: anchor(left); top: anchor(--scroller1 bottom); } /* Needs scroll adjustment in y axis only */ #target2 { - anchor-default: --a2; + position-anchor: --a2; top: anchor(top); left: anchor(--scroller2 right); } /* No scroll adjustment needed */ #target3 { - anchor-default: --a3; + position-anchor: --a3; top: anchor(--scroller3 bottom); left: anchor(--scroller3 right); } diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-007.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-007.html index ec51910619..7e288d713f 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-007.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-007.html @@ -56,7 +56,7 @@ body { height: 50px; left: anchor(--a3 left); top: anchor(--a1 top); - anchor-default: --a2; + position-anchor: --a2; background: lime; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-001.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-001.tentative.html index 60ad128022..1235f8fad4 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-001.tentative.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-001.tentative.html @@ -25,7 +25,7 @@ div { #anchored1 { position: absolute; - anchor-default: --a1; + position-anchor: --a1; left: anchor(--a1 left); top: anchor(--a1 bottom); background: green; @@ -34,7 +34,7 @@ div { #anchored2 { position: absolute; - anchor-default: --a2; + position-anchor: --a2; left: anchor(--a2 left); top: anchor(--a2 bottom); background: lime; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-002.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-002.tentative.html index e180c56468..9c60799e0b 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-002.tentative.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-002.tentative.html @@ -31,7 +31,7 @@ div { #anchored1 { position: absolute; - anchor-default: --a1; + position-anchor: --a1; left: anchor(--a1 left); top: anchor(--a1 bottom); background: green; @@ -40,7 +40,7 @@ div { #anchored2 { position: absolute; - anchor-default: --a2; + position-anchor: --a2; left: anchor(--a2 left); top: anchor(--a2 bottom); background: lime; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-003.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-003.tentative.html index 8912fcb699..b441c92bf1 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-003.tentative.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-003.tentative.html @@ -31,7 +31,7 @@ div { #anchored1 { position: absolute; - anchor-default: --a1; + position-anchor: --a1; left: anchor(--a1 left); top: anchor(--a1 bottom); background: green; @@ -41,7 +41,7 @@ div { #anchored2 { position: absolute; - anchor-default: --a2; + position-anchor: --a2; left: anchor(--a2 left); top: anchor(--a2 bottom); background: lime; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-004.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-004.tentative.html index 5834eb1f4c..f1765a9870 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-004.tentative.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-004.tentative.html @@ -37,7 +37,7 @@ div { #anchored1 { position: absolute; - anchor-default: --a1; + position-anchor: --a1; left: anchor(--a1 left); top: anchor(--a1 bottom); background: green; @@ -45,7 +45,7 @@ div { #anchored2 { position: absolute; - anchor-default: --a2; + position-anchor: --a2; left: anchor(--a2 left); top: anchor(--a2 bottom); background: lime; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-fallback.tentative.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-fallback.tentative.html index 4000eb54a5..d2300da818 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-fallback.tentative.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-chained-fallback.tentative.html @@ -27,7 +27,7 @@ div { #anchored1 { position: absolute; - anchor-default: --a1; + position-anchor: --a1; background: green; position-try-options: --fallback; anchor-name: --a2; @@ -37,7 +37,7 @@ div { #anchored2 { position: absolute; - anchor-default: --a2; + position-anchor: --a2; left: anchor(--a2 left); top: anchor(--a2 bottom); background: lime; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-001-crash.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-001-crash.html index 005a27393a..4dd9bad60e 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-001-crash.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-001-crash.html @@ -23,7 +23,7 @@ #anchored { position: absolute; - anchor-default: --a; + position-anchor: --a; left: anchor(--a left); bottom: anchor(--a top); width: 100px; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-002-crash.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-002-crash.html index 83ce146825..80dabbb666 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-002-crash.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-002-crash.html @@ -23,7 +23,7 @@ position: fixed; top: anchor(--a bottom); left: anchor(--a left); - anchor-default: --a; + position-anchor: --a; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-003-crash.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-003-crash.html index 594c844bfb..f46d902ffe 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-003-crash.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-003-crash.html @@ -23,7 +23,7 @@ position: fixed; top: anchor(--a bottom); left: anchor(--a left); - anchor-default: --a; + position-anchor: --a; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-004-crash.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-004-crash.html index 226a1b099c..ee5ad2f41a 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-004-crash.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-004-crash.html @@ -23,7 +23,7 @@ position: fixed; top: anchor(--a bottom); left: anchor(--a left); - anchor-default: --a; + position-anchor: --a; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-005-crash.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-005-crash.html index 639e2e064a..c5e44a79e7 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-005-crash.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-005-crash.html @@ -23,7 +23,7 @@ position: fixed; top: anchor(--a bottom); left: anchor(--a left); - anchor-default: --a; + position-anchor: --a; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-006.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-006.html index 6e57accc45..49c6dc780c 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-006.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-composited-scrolling-006.html @@ -29,7 +29,7 @@ body { background: red; left: 0; bottom: anchor(--a top); - anchor-default: --a; + position-anchor: --a; } #overlap { position: absolute; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos-002.html index 5b2aa2dd50..1a05d8b93a 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos-002.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos-002.html @@ -23,7 +23,7 @@ div { #anchored { position: fixed; - anchor-default: --a1; + position-anchor: --a1; left: anchor(--a1 right); top: anchor(--a1 top); background: green; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos.html index a32ef3f7c4..7923ed789d 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-fixedpos.html @@ -22,7 +22,7 @@ div { #anchored { position: fixed; - anchor-default: --a1; + position-anchor: --a1; left: anchor(--a1 right); top: anchor(--a1 top); background: green; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-js-expose.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-js-expose.html index 1ef44d03c2..3b3f1a0608 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-js-expose.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-js-expose.html @@ -35,7 +35,7 @@ bottom: anchor(--anchor top); width: 100px; height: 100px; - anchor-default: --anchor; + position-anchor: --anchor; background-color: green; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-nested.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-nested.html index 557f748c02..291fe0d710 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-nested.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-nested.html @@ -41,7 +41,7 @@ body { width: 50px; height: 50px; left: anchor(--anchor left); - anchor-default: --anchor; + position-anchor: --anchor; } .above { diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-001.html index b696ae0060..3130018e73 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-001.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-001.html @@ -35,7 +35,7 @@ #anchored { position: absolute; background: green; - anchor-default: --a; + position-anchor: --a; position-try-options: --f1, --f2; width: 100px; height: 100px; /* Above the anchor */ diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-002.html index 3b84124705..52bbcd62fc 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-002.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-002.html @@ -31,7 +31,7 @@ body { width: 100px; height: 100px; background: green; - anchor-default: --a; + position-anchor: --a; top: anchor(--a top); left: anchor(--a right); position-try-options: --pf; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-003.html index dd9fdc92c2..b89a574d76 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-003.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-003.html @@ -39,7 +39,7 @@ width: 100px; height: 100px; background: green; - anchor-default: --a; + position-anchor: --a; position-try-options: --pf; } diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-004.html index 0aab60b7a8..bf0bee972d 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-004.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-004.html @@ -33,7 +33,7 @@ body { width: 100px; height: 100px; background: green; - anchor-default: --a; + position-anchor: --a; position-try-options: --pf1, --pf2; /* Top of the anchor */ bottom: anchor(--a top); diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-005.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-005.html index e2dac13abd..197a9e4f79 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-005.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-005.html @@ -32,7 +32,7 @@ body { width: 100px; height: 100px; background: green; - anchor-default: --a; + position-anchor: --a; top: anchor(--a top); left: anchor(--a right); position-try-options: --pf; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-006.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-006.html index 1f9004de54..132c45c89c 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-006.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-006.html @@ -28,7 +28,7 @@ body { width: 100px; height: 100px; background: green; - anchor-default: --a; + position-anchor: --a; position-try: --pf1, --pf2, --pf3; inset-block-start: anchor(--a end); inset-inline-start: anchor(--a end); diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-007.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-007.html index 32b7f64173..a02bd35a66 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-007.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-007.html @@ -32,7 +32,7 @@ html { width: 100px; height: 100px; background: green; - anchor-default: --a; + position-anchor: --a; position-try-options: --pf1, --pf2, --pf3; inset-block-start: anchor(--a end); inset-inline-start: anchor(--a end); diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-008.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-008.html index 99f180bb46..2deddd587e 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-008.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-008.html @@ -33,7 +33,7 @@ html { width: 100px; height: 100px; background: green; - anchor-default: --a; + position-anchor: --a; position-try-options: --pf1, --pf2, --pf3; inset-block-start: anchor(--a end); inset-inline-start: anchor(--a end); diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-009.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-009.html index 0267d1987b..0d7d6b077f 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-009.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-009.html @@ -32,7 +32,7 @@ html { width: 100px; height: 100px; background: green; - anchor-default: --a; + position-anchor: --a; position-try-options: --pf1, --pf2, --pf3; inset-block-start: anchor(--a end); inset-inline-start: anchor(--a end); diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-010.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-010.html index 133649c720..21f32ad068 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-010.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-010.html @@ -33,7 +33,7 @@ html { width: 100px; height: 100px; background: green; - anchor-default: --a; + position-anchor: --a; position-try-options: --pf1, --pf2, --pf3; inset-block-start: anchor(--a end); inset-inline-start: anchor(--a end); diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-011.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-011.html index 005a4ee728..5de8461010 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-011.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-011.html @@ -45,7 +45,7 @@ width: 100px; height: 100px; background: green; - anchor-default: --a; + position-anchor: --a; position-try-options: --pf1, --pf2, --pf3; bottom: anchor(--a top); left: anchor(--a right); diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-012-ref.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-012-ref.html new file mode 100644 index 0000000000..fbc0b5fc6d --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-012-ref.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<style> +#scroller { + width: 400px; + height: 400px; + overflow-y: scroll; +} + +.box { + min-height: 100px; + width: 100px; +} + +#anchor { + background: orange; +} + +#anchored { + width: 100px; + height: 100px; + background: green; +} +</style> + +<div id="scroller"> + <div class="box"></div> + <div class="box"></div> + <div class="box"></div> + <div class="box" id="anchor"></div> + <div id="anchored"></div> + <div class="box"></div> + <div class="box"></div> + <div class="box"></div> +</div> + +<script> +scroller.scrollTop = 150; +</script> + diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-012.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-012.html new file mode 100644 index 0000000000..7c0b381999 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-position-try-012.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<title>Tests position fallback change on scroll with anchor and anchored under the same scroll container</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#scroll"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback"> +<link rel="match" href="anchor-scroll-position-try-012-ref.html"> +<style> +#scroller { + position: relative; + width: 400px; + height: 400px; + overflow-y: scroll; +} + +.box { + min-height: 100px; + width: 100px; +} + +#anchor { + anchor-name: --a; + background: orange; +} + +#anchored { + position: absolute; + top: anchor(--a bottom); + width: 100px; + height: 100px; + background: green; + position-anchor: --a; + position-try-options: --pf; +} + +@position-try --pf { + top: auto; + bottom: anchor(--a top); +} +</style> + +<div id="scroller"> + <div class="box"></div> + <div class="box"></div> + <div class="box"></div> + <div class="box" id="anchor"></div> + <div class="box"></div> + <div class="box"></div> + <div class="box"></div> + <div class="box"></div> + <div id="anchored"></div> +</div> + +<script> +requestAnimationFrame(() => { + requestAnimationFrame(() => { + scroller.scrollTop = 150; + document.documentElement.classList.remove('reftest-wait'); + }); +}); +</script> +</html> + diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-001.html index abab944751..a6c3b05725 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-001.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-001.html @@ -27,7 +27,7 @@ div { #anchored { position: absolute; - anchor-default: --a1; + position-anchor: --a1; left: anchor(--a1 left); top: anchor(--a1 bottom); background: green; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-002.html index 357421ecf1..e2d91fe4dd 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-002.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-002.html @@ -28,7 +28,7 @@ div { #anchored { position: absolute; - anchor-default: --a1; + position-anchor: --a1; left: anchor(--a1 left); top: anchor(--a1 bottom); background: green; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-003.html index 0e77004491..b40f5cc8d5 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-003.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-003.html @@ -27,7 +27,7 @@ div { #anchored { position: absolute; - anchor-default: --a1; + position-anchor: --a1; left: anchor(--a1 left); top: anchor(--a1 bottom); background: green; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-004.html index f7878ae8df..30325ce1e0 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-004.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-to-sticky-004.html @@ -30,7 +30,7 @@ div { #anchored { position: absolute; - anchor-default: --a1; + position-anchor: --a1; left: anchor(--a1 left); top: anchor(--a1 bottom); background: green; diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-001.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-001.html index f11797edad..aa49fbcc6d 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-001.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-001.html @@ -41,7 +41,7 @@ body { position: absolute; left: anchor(--anchor left); bottom: anchor(--anchor top); - anchor-default: --anchor; + position-anchor: --anchor; } #outer-anchored { @@ -49,7 +49,7 @@ body { position: absolute; left: anchor(--anchor left); top: anchor(--anchor bottom); - anchor-default: --anchor; + position-anchor: --anchor; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-002.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-002.html index 19447952b0..5695db2a1c 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-002.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-002.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <html class="reftest-wait"> -<title>Anchored elements should update location on `anchor-default` property changes</title> +<title>Anchored elements should update location on `position-anchor` property changes</title> <link rel="author" href="mailto:xiaochengh@chromium.org"> <link rel="help" href="https://drafts.csswg.org/css-anchor-1/"> <link rel="match" href="reference/anchor-scroll-ref.html"> @@ -75,8 +75,8 @@ function raf() { async function runTest() { await raf(); await raf(); - document.getElementById('inner-anchored').style.anchorDefault = '--anchor'; - document.getElementById('outer-anchored').style.anchorDefault = '--anchor'; + document.getElementById('inner-anchored').style.positionAnchor = '--anchor'; + document.getElementById('outer-anchored').style.positionAnchor = '--anchor'; document.documentElement.classList.remove('reftest-wait'); } runTest(); diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-003.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-003.html index 57a524c483..2e1532badf 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-003.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-003.html @@ -37,7 +37,7 @@ body { position: absolute; left: anchor(--anchor left); bottom: anchor(--anchor top); - anchor-default: --anchor; + position-anchor: --anchor; } #outer-anchored { @@ -45,7 +45,7 @@ body { position: absolute; left: anchor(--anchor left); top: anchor(--anchor bottom); - anchor-default: --anchor; + position-anchor: --anchor; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-004.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-004.html index d20a7b660a..87138fb2d9 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-004.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-004.html @@ -49,7 +49,7 @@ body { position: absolute; left: anchor(--anchor left); bottom: anchor(--anchor top); - anchor-default: --anchor; + position-anchor: --anchor; } #outer-anchored { @@ -57,7 +57,7 @@ body { position: absolute; left: anchor(--anchor left); top: anchor(--anchor bottom); - anchor-default: --anchor; + position-anchor: --anchor; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-005.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-005.html index c2e7248c80..37874bba55 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-005.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-005.html @@ -24,7 +24,7 @@ background-color: green; top: anchor(--a top); left: 0; - anchor-default: --a; + position-anchor: --a; } </style> <div id="cb"> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-006.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-006.html index 2535c68f78..81defee7cf 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-006.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-006.html @@ -24,7 +24,7 @@ background-color: green; top: anchor(--a top); left: 0; - anchor-default: --a; + position-anchor: --a; } </style> <div id="cb"> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-007.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-007.html index 4859f01d66..33050348c4 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-007.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-update-007.html @@ -37,7 +37,7 @@ body { position: absolute; left: anchor(--anchor left); bottom: anchor(--anchor top); - anchor-default: --anchor; + position-anchor: --anchor; } #outer-anchored { @@ -45,7 +45,7 @@ body { position: fixed; left: anchor(--anchor left); top: anchor(--anchor bottom); - anchor-default: --anchor; + position-anchor: --anchor; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vlr.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vlr.html index 00406c825e..76186f9cad 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vlr.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vlr.html @@ -50,7 +50,7 @@ body { position: absolute; top: anchor(--anchor top); left: anchor(--anchor right); - anchor-default: --anchor; + position-anchor: --anchor; } #outer-anchored { @@ -58,7 +58,7 @@ body { position: absolute; top: anchor(--anchor top); right: anchor(--anchor left); - anchor-default: --anchor; + position-anchor: --anchor; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vrl.html b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vrl.html index 2432d72899..13ea8b37ab 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vrl.html +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-scroll-vrl.html @@ -50,7 +50,7 @@ body { position: absolute; top: anchor(--anchor top); left: anchor(--anchor right); - anchor-default: --anchor; + position-anchor: --anchor; } #outer-anchored { @@ -58,7 +58,7 @@ body { position: absolute; top: anchor(--anchor top); right: anchor(--anchor left); - anchor-default: --anchor; + position-anchor: --anchor; } </style> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-transition-attr.html b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-attr.html new file mode 100644 index 0000000000..50f1154532 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-attr.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: Transition when the anchor attribute changes</title> +<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/9598"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #cb { + display: inline-block; + position: relative; + width: 250px; + height: 250px; + border: 1px solid black; + } + #anchor1, #anchor2 { + width: 100px; + } + #anchor1 { + background: wheat; + height: 50px; + } + #anchor2 { + background: tomato; + height: 90px; + } + #anchored { + position: absolute; + width: anchor-size(width); + height: anchor-size(height); + top: anchor(top); + left: anchor(right); + transition-duration: 1000s; + transition-timing-function: steps(2, start); + transition-property: top, height; + background-color: skyblue; + } +</style> +<div id=cb> + <div id=anchor1>Anchor1</div> + <div id=anchor2>Anchor2</div> + <div id=anchored anchor=anchor1></div> +</div> +<script> + test(() => { + assert_equals(anchored.offsetTop, 0); + assert_equals(anchored.offsetHeight, 50); + anchored.setAttribute('anchor', 'anchor2'); + assert_equals(anchored.offsetTop, 25); + assert_equals(anchored.offsetHeight, 70); + }, 'Transition when the anchor attribute changes'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-transition-default.html b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-default.html new file mode 100644 index 0000000000..1bee0cbbe4 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-default.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: Transition when position-anchor changes</title> +<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/9598"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #cb { + display: inline-block; + position: relative; + width: 250px; + height: 250px; + border: 1px solid black; + } + #anchor1, #anchor2 { + width: 100px; + } + #anchor1 { + background: wheat; + height: 50px; + anchor-name: --a1; + } + #anchor2 { + background: tomato; + height: 90px; + anchor-name: --a2; + } + #anchored { + position: absolute; + width: anchor-size(width); + height: anchor-size(height); + top: anchor(top); + left: anchor(right); + transition-duration: 1000s; + transition-timing-function: steps(2, start); + transition-property: top, height; + background-color: skyblue; + position-anchor: --a1; + } +</style> +<div id=cb> + <div id=anchor1>Anchor1</div> + <div id=anchor2>Anchor2</div> + <div id=anchored></div> +</div> +<script> + test(() => { + assert_equals(anchored.offsetTop, 0); + assert_equals(anchored.offsetHeight, 50); + anchored.style.positionAnchor = '--a2'; + assert_equals(anchored.offsetTop, 25); + assert_equals(anchored.offsetHeight, 70); + }, 'Transition when position-anchor changes'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-transition-eval.html b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-eval.html new file mode 100644 index 0000000000..cf65742b2d --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-eval.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: Transition when the result of anchor()/anchor-size() changes</title> +<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/9598"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + .cb { + display: inline-block; + position: relative; + width: 250px; + height: 250px; + border: 1px solid black; + } + .anchor { + position: absolute; + width: 50px; + height: 40px; + left: 60px; + top: 40px; + background: coral; + anchor-name: --a; + } + .shift { + left: 80px; + } + .grow { + width: 70px; + } + .anchored { + position: absolute; + width: anchor-size(width); + height: 40px; + position-anchor: --a; + top: anchor(bottom); + left: anchor(right); + transition-duration: 1000s; + transition-timing-function: steps(2, start); + background-color: skyblue; + } +</style> + +<div id=anchor_test class=cb> + <div class=anchor></div> + <div class=anchored style="transition-property:left"></div> +</div> +<script> + test(() => { + let anchor = anchor_test.querySelector('.anchor'); + let anchored = anchor_test.querySelector('.anchored'); + assert_equals(anchored.offsetLeft, 110); + anchor.classList.add('shift'); + assert_equals(anchored.offsetLeft, 120); + }, 'Transition when the result of anchor() changes'); +</script> + +<div id=anchor_size_test class=cb> + <div class=anchor></div> + <div class=anchored style="transition-property:width"></div> +</div> +<script> + test(() => { + let anchor = anchor_size_test.querySelector('.anchor'); + let anchored = anchor_size_test.querySelector('.anchored'); + assert_equals(anchored.offsetWidth, 50); + anchor.classList.add('grow'); + assert_equals(anchored.offsetWidth, 60); + }, 'Transition when the result of anchor-size() changes'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-transition-name.html b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-name.html new file mode 100644 index 0000000000..ea7b403e61 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-transition-name.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: Transition when the dereferenced anchor name changes</title> +<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/9598"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #cb { + display: inline-block; + position: relative; + width: 250px; + height: 250px; + border: 1px solid black; + } + #anchor1, #anchor2 { + width: 100px; + } + #anchor1 { + background: wheat; + height: 50px; + } + #anchor2 { + background: tomato; + height: 90px; + } + .anchor-name { + anchor-name: --a; + } + #anchored { + position: absolute; + width: anchor-size(width); + height: anchor-size(height); + position-anchor: --a; + top: anchor(top); + left: anchor(right); + transition-duration: 1000s; + transition-timing-function: steps(2, start); + transition-property: top, height; + background-color: skyblue; + } +</style> +<div id=cb> + <div id=anchor1 class=anchor-name>Anchor1</div> + <div id=anchor2>Anchor2</div> + <div id=anchored></div> +</div> +<script> + test(() => { + assert_equals(anchored.offsetTop, 0); + assert_equals(anchored.offsetHeight, 50); + anchor1.classList.toggle('anchor-name'); + anchor2.classList.toggle('anchor-name'); + assert_equals(anchored.offsetTop, 25); + assert_equals(anchored.offsetHeight, 70); + }, 'Transition when the dereferenced anchor name changes'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-typed-om.html b/testing/web-platform/tests/css/css-anchor-position/anchor-typed-om.html new file mode 100644 index 0000000000..d4fec49dd3 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/anchor-typed-om.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning Test: anchor()/anchor-size() functions in CSS Typed OM</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/9598"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #cb { + position: relative; + width: 100px; + height: 100px; + border: 1px solid black; + } + #anchor { + position: absolute; + left: 10px; + top: 10px; + width: 20px; + height: 20px; + background: coral; + anchor-name: --a; + } + #target { + position: absolute; + background: skyblue; + position-anchor: --a; + left: anchor(right); + top: calc(anchor(bottom) + 5px); + width: anchor-size(--unknown width, 25px); + height: calc(anchor-size(height) * 2); + } +</style> + +<div id=cb> + <div id=anchor></div> + <div id=target></div> +</div> +<script> + function assert_unit_value(actual, expected) { + assert_true(actual instanceof CSSUnitValue); + assert_true(expected instanceof CSSUnitValue); + assert_equals(actual.value, expected.value); + assert_equals(actual.unit, expected.unit); + } + + test(() => { + assert_unit_value(target.computedStyleMap().get('left'), CSS.px(30)); + assert_unit_value(target.computedStyleMap().get('top'), CSS.px(35)); + }, 'anchor() computes to pixels'); + + test(() => { + assert_unit_value(target.computedStyleMap().get('width'), CSS.px(25)); + assert_unit_value(target.computedStyleMap().get('height'), CSS.px(40)); + }, 'anchor-size() computes to pixels'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/at-position-try-allowed-declarations.html b/testing/web-platform/tests/css/css-anchor-position/at-position-try-allowed-declarations.html index 622e9827b4..f6dac753d2 100644 --- a/testing/web-platform/tests/css/css-anchor-position/at-position-try-allowed-declarations.html +++ b/testing/web-platform/tests/css/css-anchor-position/at-position-try-allowed-declarations.html @@ -44,7 +44,7 @@ test_allowed_declaration('inset-inline-end'); test_allowed_declaration('inset-block'); test_allowed_declaration('inset-inline'); test_allowed_declaration('inset'); -test_allowed_declaration('inset-area', 'all'); +test_allowed_declaration('inset-area', 'span-all'); // Margin properties are allowed test_allowed_declaration('margin-top'); @@ -74,12 +74,12 @@ test_allowed_declaration('max-block-size'); test_allowed_declaration('max-inline-size'); // Box alignment properties are allowed -test_allowed_declaration('justify-content', 'normal'); -test_allowed_declaration('align-content', 'normal'); -test_allowed_declaration('justify-items', 'normal'); -test_allowed_declaration('align-items', 'normal'); test_allowed_declaration('justify-self', 'normal'); test_allowed_declaration('align-self', 'normal'); +test_allowed_declaration('place-self', 'normal'); + +// The 'position-anchor' property is allowed +test_allowed_declaration('position-anchor', '--anchor'); // Custom properties are disallowed test_disallowed_declaration('--custom'); @@ -91,12 +91,16 @@ test_disallowed_declaration('padding'); test_disallowed_declaration('display'); test_disallowed_declaration('position'); test_disallowed_declaration('float'); +test_disallowed_declaration('justify-content', 'normal'); +test_disallowed_declaration('align-content', 'normal'); +test_disallowed_declaration('justify-items', 'normal'); +test_disallowed_declaration('align-items', 'normal'); -// 'revert' and 'revert-layer' are disallowed -test_disallowed_declaration('top', 'revert'); -test_disallowed_declaration('top', 'revert-layer'); -test_disallowed_declaration('inset', 'revert'); -test_disallowed_declaration('inset', 'revert-layer'); +// 'revert' and 'revert-layer' are allowed +test_allowed_declaration('top', 'revert'); +test_allowed_declaration('top', 'revert-layer'); +test_allowed_declaration('inset', 'revert'); +test_allowed_declaration('inset', 'revert-layer'); // !important is disallowed test_disallowed_declaration('top', '1px !important'); diff --git a/testing/web-platform/tests/css/css-anchor-position/at-position-try-cssom.html b/testing/web-platform/tests/css/css-anchor-position/at-position-try-cssom.html index d4a1f4fa24..91172c5185 100644 --- a/testing/web-platform/tests/css/css-anchor-position/at-position-try-cssom.html +++ b/testing/web-platform/tests/css/css-anchor-position/at-position-try-cssom.html @@ -6,7 +6,7 @@ <script src="/resources/testharnessreport.js"></script> <div id="anchor"></div> -<div id="not-anchor"></div> +<div id="other-anchor"></div> <div id="target"></div> <script> @@ -32,12 +32,12 @@ test(t => { test(t => { const style = createStyle(t, ` @position-try --pf { top: anchor(top); left: 0; } - #anchor, #not-anchor, #target { + #anchor, #other-anchor, #target { position: absolute; width: 100px; height: 100px; } #anchor { top: 100px; left: 0; anchor-name: --a; } - #not-anchor { top: 200px; left: 0; anchor-name: --b; } - #target { position-try-options: --pf; anchor-default: --a; left: 999999px; } + #other-anchor { top: 200px; left: 0; anchor-name: --b; } + #target { position-try-options: --pf; position-anchor: --a; left: 999999px; } `); const positionTryRule = style.sheet.cssRules[0]; @@ -50,12 +50,17 @@ test(t => { assert_equals(target.getBoundingClientRect().left, 100); assert_equals(target.getBoundingClientRect().top, 100); - // These properties are disallowed in `@position-try` rule, and hence should not affect + // This property are disallowed in `@position-try` rule, and hence should not affect // position fallback. - positionTryRule.style.setProperty('anchor-default', '--b'); positionTryRule.style.setProperty('position', 'static'); assert_equals(target.getBoundingClientRect().left, 100); assert_equals(target.getBoundingClientRect().top, 100); + + // `position-anchor` is an allowed property in `@position-try` and should affect position fallback. + positionTryRule.style.setProperty('position-anchor', '--b'); + assert_equals(target.getBoundingClientRect().left, 100); + assert_equals(target.getBoundingClientRect().top, 200); + }, 'CSSPositionTryRule.style.setProperty setting allowed and disallowed properties'); </script> diff --git a/testing/web-platform/tests/css/css-anchor-position/chrome-40286059-crash.html b/testing/web-platform/tests/css/css-anchor-position/chrome-40286059-crash.html new file mode 100644 index 0000000000..dbbeb5ac4d --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/chrome-40286059-crash.html @@ -0,0 +1,7 @@ +<!DOCTYPE html> +<link rel="help" href="https://crbug.com/40286059"> +<p>Pass if no crash</p> +<div id="pop" style="position-try-options: --foo" popover></div> +<script> + getComputedStyle(pop).left; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-abs-inline-container.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-abs-inline-container.html index 52344614f0..7bc59356e9 100644 --- a/testing/web-platform/tests/css/css-anchor-position/inset-area-abs-inline-container.html +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-abs-inline-container.html @@ -25,13 +25,13 @@ position: absolute; align-self: stretch; justify-self: stretch; - anchor-default: --anchor; + position-anchor: --anchor; background-color: blue; } - #top-left { inset-area: top / left; } - #top-right { inset-area: top / right; } - #bottom-left { inset-area: bottom / left; } - #bottom-right { inset-area: bottom / right; } + #top-left { inset-area: top left; } + #top-right { inset-area: top right; } + #bottom-left { inset-area: bottom left; } + #bottom-right { inset-area: bottom right; } </style> <div id="in-flow"> <br> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-align-justify-wm-dir.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-align-justify-wm-dir.html new file mode 100644 index 0000000000..3e42913ffc --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-align-justify-wm-dir.html @@ -0,0 +1,155 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: inset-area positioning - alignment with writing-mode and direction</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + /* A 300x300 container with a 100x100 centered anchor */ + #container { + position: relative; + width: 300px; + height: 300px; + } + #anchor { + position: absolute; + top: 100px; + left: 100px; + width: 100px; + height: 100px; + anchor-name: --anchor; + } + #anchored { + position: absolute; + width: 10px; + height: 10px; + inset: 10px; + position-anchor: --anchor; + } +</style> +<div id="container"> + <div id="anchor"></div> + <div id="anchored"></div> +</div> +<script> + function test_inset_area(writing_direction, inset_area, expected_offsets) { + anchored.style.insetArea = inset_area; + test(() => { + assert_equals(anchored.offsetLeft, expected_offsets.left, "Checking offsetLeft"); + assert_equals(anchored.offsetTop, expected_offsets.top, "Checking offsetTop"); + assert_equals(anchored.offsetWidth, expected_offsets.width, "Checking offsetWidth"); + assert_equals(anchored.offsetHeight, expected_offsets.height, "Checking offsetHeight"); + }, "Offsets for: " + inset_area + " with writing-mode / direction: " + writing_direction); + } + + const top_left = {left:80, top:80, width:10, height:10}; + const top_right = {left:210, top:80, width:10, height:10}; + const bottom_left = {left:80, top:210, width:10, height:10}; + const bottom_right = {left:210, top:210, width:10, height:10}; + + anchored.style.writingMode = "horizontal-tb"; + anchored.style.direction = "ltr"; + + // Writing-mode and direction on container + let writing_direction = "containing-block: horizontal-tb / rtl"; + container.style.writingMode = "horizontal-tb"; + container.style.direction = "rtl"; + test_inset_area(writing_direction, "start start", top_right); + test_inset_area(writing_direction, "self-start self-start", top_left); + test_inset_area(writing_direction, "x-start y-start", top_right); + test_inset_area(writing_direction, "x-self-start y-self-start", top_left); + + // containing-block: vertical-lr / ltr + // self: horizontal-tb / ltr + writing_direction = "containing-block: vertical-lr / ltr"; + container.style.writingMode = "vertical-lr"; + container.style.direction = "ltr"; + test_inset_area(writing_direction, "start start", top_left); + test_inset_area(writing_direction, "self-start self-start", top_left); + test_inset_area(writing_direction, "x-start y-start", top_left); + test_inset_area(writing_direction, "x-self-start y-self-start", top_left); + + // containing-block: vertical-lr / rtl + // self: horizontal-tb / ltr + writing_direction = "containing-block: vertical-lr / rtl"; + container.style.writingMode = "vertical-lr"; + container.style.direction = "rtl"; + test_inset_area(writing_direction, "start start", bottom_left); + test_inset_area(writing_direction, "self-start self-start", top_left); + test_inset_area(writing_direction, "x-start y-start", bottom_left); + test_inset_area(writing_direction, "x-self-start y-self-start", top_left); + + // containing-block: vertical-rl / ltr + // self: horizontal-tb / ltr + writing_direction = "containing-block: vertical-rl / ltr"; + container.style.writingMode = "vertical-rl"; + container.style.direction = "ltr"; + test_inset_area(writing_direction, "start start", top_right); + test_inset_area(writing_direction, "self-start self-start", top_left); + test_inset_area(writing_direction, "x-start y-start", top_right); + test_inset_area(writing_direction, "x-self-start y-self-start", top_left); + + // containing-block: vertical-rl / rtl + // self: horizontal-tb / ltr + writing_direction = "containing-block: vertical-rl / rtl"; + container.style.writingMode = "vertical-rl"; + container.style.direction = "rtl"; + test_inset_area(writing_direction, "start start", bottom_right); + test_inset_area(writing_direction, "self-start self-start", top_left); + test_inset_area(writing_direction, "x-start y-start", bottom_right); + test_inset_area(writing_direction, "x-self-start y-self-start", top_left); + + // Writing-mode and direction on self + container.style.writingMode = "horizontal-tb"; + container.style.direction = "ltr"; + + // containing-block: horizontal-tb / ltr + // self: horizontal-tb / rtl + writing_direction = "self: horizontal-tb / rtl"; + anchored.style.writingMode = "horizontal-tb"; + anchored.style.direction = "rtl"; + test_inset_area(writing_direction, "start start", top_left); + test_inset_area(writing_direction, "self-start self-start", top_right); + test_inset_area(writing_direction, "x-start y-start", top_left); + test_inset_area(writing_direction, "x-self-start y-self-start", top_right); + + // containing-block: horizontal-tb / ltr + // self: vertical-lr / ltr + writing_direction = "self: vertical-lr / ltr"; + anchored.style.writingMode = "vertical-lr"; + anchored.style.direction = "ltr"; + test_inset_area(writing_direction, "start start", top_left); + test_inset_area(writing_direction, "self-start self-start", top_left); + test_inset_area(writing_direction, "x-start y-start", top_left); + test_inset_area(writing_direction, "x-self-start y-self-start", top_left); + + // containing-block: horizontal-tb / ltr + // self: vertical-lr / rtl + writing_direction = "self: vertical-lr / rtl"; + anchored.style.writingMode = "vertical-lr"; + anchored.style.direction = "rtl"; + test_inset_area(writing_direction, "start start", top_left); + test_inset_area(writing_direction, "self-start self-start", bottom_left); + test_inset_area(writing_direction, "x-start y-start", top_left); + test_inset_area(writing_direction, "x-self-start y-self-start", bottom_left); + + // containing-block: horizontal-tb / ltr + // self: vertical-rl / ltr + writing_direction = "self: vertical-rl / ltr"; + anchored.style.writingMode = "vertical-rl"; + anchored.style.direction = "ltr"; + test_inset_area(writing_direction, "start start", top_left); + test_inset_area(writing_direction, "self-start self-start", top_right); + test_inset_area(writing_direction, "x-start y-start", top_left); + test_inset_area(writing_direction, "x-self-start y-self-start", top_right); + + // containing-block: horizontal-tb / ltr + // self: vertical-rl / rtl + writing_direction = "self: vertical-rl / rtl"; + anchored.style.writingMode = "vertical-rl"; + anchored.style.direction = "rtl"; + test_inset_area(writing_direction, "start start", top_left); + test_inset_area(writing_direction, "self-start self-start", bottom_right); + test_inset_area(writing_direction, "x-start y-start", top_left); + test_inset_area(writing_direction, "x-self-start y-self-start", bottom_right); + +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-align-justify.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-align-justify.html new file mode 100644 index 0000000000..7f7f05ccf3 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-align-justify.html @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: inset-area positioning - alignment</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + /* A 300x300 container with a 100x100 centered anchor */ + #container { + position: relative; + width: 300px; + height: 300px; + } + #anchor { + position: absolute; + top: 100px; + left: 100px; + width: 100px; + height: 100px; + anchor-name: --anchor; + } + #anchored { + position: absolute; + width: 10px; + height: 10px; + inset: 10px; + position-anchor: --anchor; + } +</style> +<div id="container"> + <div id="anchor"></div> + <div id="anchored"></div> +</div> +<script> + function test_inset_area(inset_area, expected_offsets) { + anchored.style.insetArea = inset_area; + + test(() => { + assert_equals(anchored.offsetLeft, expected_offsets.left, "Check expected offsetLeft"); + assert_equals(anchored.offsetTop, expected_offsets.top, "Check expected offsetTop"); + assert_equals(anchored.offsetWidth, expected_offsets.width, "Check expected offsetWidth"); + assert_equals(anchored.offsetHeight, expected_offsets.height, "Check expected offsetHeight"); + }, "Offsets for inset-area: " + inset_area); + } + + // anchor-center in both directions + test_inset_area("span-all", {left:145, top:145, width:10, height:10}); + + // Single region spans + test_inset_area("top left", {left:80, top:80, width:10, height:10}); + test_inset_area("top center", {left:145, top:80, width:10, height:10}); + test_inset_area("top right", {left:210, top:80, width:10, height:10}); + test_inset_area("center left", {left:80, top:145, width:10, height:10}); + test_inset_area("center center", {left:145, top:145, width:10, height:10}); + test_inset_area("center right", {left:210, top:145, width:10, height:10}); + test_inset_area("bottom left", {left:80, top:210, width:10, height:10}); + test_inset_area("bottom center", {left:145, top:210, width:10, height:10}); + test_inset_area("bottom right", {left:210, top:210, width:10, height:10}); + + // Multi-region spans + test_inset_area("top span-left", {left:180, top:80, width:10, height:10}); + test_inset_area("top span-right", {left:110, top:80, width:10, height:10}); + test_inset_area("span-top left", {left:80, top:180, width:10, height:10}); + test_inset_area("span-bottom left", {left:80, top:110, width:10, height:10}); + +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-anchor-outside.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-anchor-outside.html new file mode 100644 index 0000000000..ac7e85bafb --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-anchor-outside.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: inset-area positioning - anchor outside containing block</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #container { + position: relative; + width: 400px; + height: 400px; + } + #anchor { + position: absolute; + left: -200px; + top: 500px; + width: 100px; + height: 100px; + anchor-name: --anchor; + } + #anchored { + position: absolute; + align-self: stretch; + justify-self: stretch; + position-anchor: --anchor; + } +</style> +<div id="container"> + <div id="anchor"></div> + <div id="anchored"></div> +</div> +<script> + function test_inset_area(inset_area, expected_offsets) { + anchored.style.insetArea = inset_area; + + test(() => { + assert_equals(anchored.offsetLeft, expected_offsets.left, "Check expected offsetLeft"); + assert_equals(anchored.offsetTop, expected_offsets.top, "Check expected offsetTop"); + assert_equals(anchored.offsetWidth, expected_offsets.width, "Check expected offsetWidth"); + assert_equals(anchored.offsetHeight, expected_offsets.height, "Check expected offsetHeight"); + }, "Offsets for inset-area: " + inset_area); + } + + test_inset_area("span-all", {left:0, top:0, width:400, height:400}); + + test_inset_area("left span-all", {left:-200, top:0, width:0, height:400}); + test_inset_area("span-left span-all", {left:-100, top:0, width:0, height:400}); + test_inset_area("span-all center", {left:-200, top:0, width:100, height:400}); + test_inset_area("span-right span-all", {left:-200, top:0, width:600, height:400}); + test_inset_area("right span-all", {left:-100, top:0, width:500, height:400}); + + test_inset_area("top span-all", {left:0, top:0, width:400, height:500}); + test_inset_area("span-top span-all", {left:0, top:0, width:400, height:600}); + test_inset_area("center span-all", {left:0, top:500, width:400, height:100}); + test_inset_area("span-bottom span-all", {left:0, top:500, width:400, height:0}); + test_inset_area("bottom span-all", {left:0, top:600, width:400, height:0}); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-anchor-partially-outside.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-anchor-partially-outside.html new file mode 100644 index 0000000000..9d7f5b5f6b --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-anchor-partially-outside.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: inset-area positioning - anchor partially outside containing block</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #container { + position: relative; + width: 400px; + height: 400px; + } + #anchor { + position: absolute; + right: -50px; + top: -50px; + width: 100px; + height: 100px; + anchor-name: --anchor; + } + #anchored { + position: absolute; + align-self: stretch; + justify-self: stretch; + position-anchor: --anchor; + } +</style> +<div id="container"> + <div id="anchor"></div> + <div id="anchored"></div> +</div> +<script> + function test_inset_area(inset_area, expected_offsets) { + anchored.style.insetArea = inset_area; + + test(() => { + assert_equals(anchored.offsetLeft, expected_offsets.left, "Check expected offsetLeft"); + assert_equals(anchored.offsetTop, expected_offsets.top, "Check expected offsetTop"); + assert_equals(anchored.offsetWidth, expected_offsets.width, "Check expected offsetWidth"); + assert_equals(anchored.offsetHeight, expected_offsets.height, "Check expected offsetHeight"); + }, "Offsets for inset-area: " + inset_area); + } + + test_inset_area("span-all", {left:0, top:0, width:400, height:400}); + + test_inset_area("left span-all", {left:0, top:0, width:350, height:400}); + test_inset_area("span-left span-all", {left:0, top:0, width:450, height:400}); + test_inset_area("span-all center", {left:350, top:0, width:100, height:400}); + test_inset_area("span-right span-all", {left:350, top:0, width:50, height:400}); + test_inset_area("right span-all", {left:450, top:0, width:0, height:400}); + + test_inset_area("top span-all", {left:0, top:-50, width:400, height:0}); + test_inset_area("span-top span-all", {left:0, top:0, width:400, height:50}); + test_inset_area("center span-all", {left:0, top:-50, width:400, height:100}); + test_inset_area("span-bottom span-all", {left:0, top:-50, width:400, height:450}); + test_inset_area("bottom span-all", {left:0, top:50, width:400, height:350}); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-basic.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-basic.html index 4a63635558..b89d0e2428 100644 --- a/testing/web-platform/tests/css/css-anchor-position/inset-area-basic.html +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-basic.html @@ -31,7 +31,7 @@ position: absolute; align-self: stretch; justify-self: stretch; - anchor-default: --anchor; + position-anchor: --anchor; } #anchor { margin-top: 150px; @@ -49,73 +49,69 @@ function test_inset_area(inset_area, expected_offsets) { anchored.style.insetArea = inset_area; test(() => { - assert_equals(anchored.offsetLeft, expected_offsets.left); - assert_equals(anchored.offsetTop, expected_offsets.top); - assert_equals(anchored.offsetWidth, expected_offsets.width); - assert_equals(anchored.offsetHeight, expected_offsets.height); + assert_equals(anchored.offsetLeft, expected_offsets.left, "Check expected offsetLeft"); + assert_equals(anchored.offsetTop, expected_offsets.top, "Check expected offsetTop"); + assert_equals(anchored.offsetWidth, expected_offsets.width, "Check expected offsetWidth"); + assert_equals(anchored.offsetHeight, expected_offsets.height, "Check expected offsetHeight"); }, "Offsets for: " + inset_area); } test_inset_area("none", {left:0, top:0, width:0, height:0}); - test_inset_area("all", {left:0, top:0, width:400, height:400}); - test_inset_area("all / all", {left:0, top:0, width:400, height:400}); + test_inset_area("span-all", {left:0, top:0, width:400, height:400}); + test_inset_area("span-all span-all", {left:0, top:0, width:400, height:400}); // Single region spans - test_inset_area("top / left", {left:0, top:0, width:100, height:150}); - test_inset_area("top / center", {left:100, top:0, width:150, height:150}); - test_inset_area("top / right", {left:250, top:0, width:150, height:150}); - test_inset_area("center / left", {left:0, top:150, width:100, height:75}); - test_inset_area("center / center", {left:100, top:150, width:150, height:75}); - test_inset_area("center / right", {left:250, top:150, width:150, height:75}); - test_inset_area("bottom / left", {left:0, top:225, width:100, height:175}); - test_inset_area("bottom / center", {left:100, top:225, width:150, height:175}); - test_inset_area("bottom / right", {left:250, top:225, width:150, height:175}); + test_inset_area("top left", {left:0, top:0, width:100, height:150}); + test_inset_area("top center", {left:100, top:0, width:150, height:150}); + test_inset_area("top right", {left:250, top:0, width:150, height:150}); + test_inset_area("center left", {left:0, top:150, width:100, height:75}); + test_inset_area("center center", {left:100, top:150, width:150, height:75}); + test_inset_area("center right", {left:250, top:150, width:150, height:75}); + test_inset_area("bottom left", {left:0, top:225, width:100, height:175}); + test_inset_area("bottom center", {left:100, top:225, width:150, height:175}); + test_inset_area("bottom right", {left:250, top:225, width:150, height:175}); - test_inset_area("start / start", {left:0, top:0, width:100, height:150}); - test_inset_area("start / center", {left:100, top:0, width:150, height:150}); - test_inset_area("start / end", {left:250, top:0, width:150, height:150}); - test_inset_area("center / start", {left:0, top:150, width:100, height:75}); - test_inset_area("center / end", {left:250, top:150, width:150, height:75}); - test_inset_area("end / start", {left:0, top:225, width:100, height:175}); - test_inset_area("end / center", {left:100, top:225, width:150, height:175}); - test_inset_area("end / end", {left:250, top:225, width:150, height:175}); + test_inset_area("start start", {left:0, top:0, width:100, height:150}); + test_inset_area("start center", {left:100, top:0, width:150, height:150}); + test_inset_area("start end", {left:250, top:0, width:150, height:150}); + test_inset_area("center start", {left:0, top:150, width:100, height:75}); + test_inset_area("center end", {left:250, top:150, width:150, height:75}); + test_inset_area("end start", {left:0, top:225, width:100, height:175}); + test_inset_area("end center", {left:100, top:225, width:150, height:175}); + test_inset_area("end end", {left:250, top:225, width:150, height:175}); - test_inset_area("self-start / self-start", {left:0, top:0, width:100, height:150}); - test_inset_area("self-start / center", {left:100, top:0, width:150, height:150}); - test_inset_area("self-start / self-end", {left:250, top:0, width:150, height:150}); - test_inset_area("center / self-start", {left:0, top:150, width:100, height:75}); - test_inset_area("center / self-end", {left:250, top:150, width:150, height:75}); - test_inset_area("self-end / self-start", {left:0, top:225, width:100, height:175}); - test_inset_area("self-end / center", {left:100, top:225, width:150, height:175}); - test_inset_area("self-end / self-end", {left:250, top:225, width:150, height:175}); + test_inset_area("self-start self-start", {left:0, top:0, width:100, height:150}); + test_inset_area("self-start center", {left:100, top:0, width:150, height:150}); + test_inset_area("self-start self-end", {left:250, top:0, width:150, height:150}); + test_inset_area("center self-start", {left:0, top:150, width:100, height:75}); + test_inset_area("center self-end", {left:250, top:150, width:150, height:75}); + test_inset_area("self-end self-start", {left:0, top:225, width:100, height:175}); + test_inset_area("self-end center", {left:100, top:225, width:150, height:175}); + test_inset_area("self-end self-end", {left:250, top:225, width:150, height:175}); - test_inset_area("y-start / x-start", {left:0, top:0, width:100, height:150}); - test_inset_area("y-start / center", {left:100, top:0, width:150, height:150}); - test_inset_area("y-start / x-end", {left:250, top:0, width:150, height:150}); - test_inset_area("center / x-start", {left:0, top:150, width:100, height:75}); - test_inset_area("center / x-end", {left:250, top:150, width:150, height:75}); - test_inset_area("y-end / x-start", {left:0, top:225, width:100, height:175}); - test_inset_area("y-end / center", {left:100, top:225, width:150, height:175}); - test_inset_area("y-end / x-end", {left:250, top:225, width:150, height:175}); + test_inset_area("y-start x-start", {left:0, top:0, width:100, height:150}); + test_inset_area("y-start center", {left:100, top:0, width:150, height:150}); + test_inset_area("y-start x-end", {left:250, top:0, width:150, height:150}); + test_inset_area("center x-start", {left:0, top:150, width:100, height:75}); + test_inset_area("center x-end", {left:250, top:150, width:150, height:75}); + test_inset_area("y-end x-start", {left:0, top:225, width:100, height:175}); + test_inset_area("y-end center", {left:100, top:225, width:150, height:175}); + test_inset_area("y-end x-end", {left:250, top:225, width:150, height:175}); - test_inset_area("y-self-start / x-self-start", {left:0, top:0, width:100, height:150}); - test_inset_area("y-self-start / center", {left:100, top:0, width:150, height:150}); - test_inset_area("y-self-start / x-self-end", {left:250, top:0, width:150, height:150}); - test_inset_area("center / x-self-start", {left:0, top:150, width:100, height:75}); - test_inset_area("center / x-self-end", {left:250, top:150, width:150, height:75}); - test_inset_area("y-self-end / x-self-start", {left:0, top:225, width:100, height:175}); - test_inset_area("y-self-end / center", {left:100, top:225, width:150, height:175}); - test_inset_area("y-self-end / x-self-end", {left:250, top:225, width:150, height:175}); + test_inset_area("y-self-start x-self-start", {left:0, top:0, width:100, height:150}); + test_inset_area("y-self-start center", {left:100, top:0, width:150, height:150}); + test_inset_area("y-self-start x-self-end", {left:250, top:0, width:150, height:150}); + test_inset_area("center x-self-start", {left:0, top:150, width:100, height:75}); + test_inset_area("center x-self-end", {left:250, top:150, width:150, height:75}); + test_inset_area("y-self-end x-self-start", {left:0, top:225, width:100, height:175}); + test_inset_area("y-self-end center", {left:100, top:225, width:150, height:175}); + test_inset_area("y-self-end x-self-end", {left:250, top:225, width:150, height:175}); // Multi-region spans - test_inset_area("y-self-start center / self-end center", {left:100, top:0, width:300, height:225}); - test_inset_area("bottom center / x-start x-end", {left:0, top:150, width:400, height:250}); - - // Non-orthogonal axes. - test_inset_area("x-start / left", {left:0, top:0, width:0, height:0}); - test_inset_area("y-end / y-self-start", {left:0, top:0, width:0, height:0}); + test_inset_area("span-y-self-start span-x-self-end", {left:100, top:0, width:300, height:225}); + test_inset_area("span-bottom span-all", {left:0, top:150, width:400, height:250}); // No implicit anchor means the inset-area should not apply. - anchored.style.anchorDefault = "implicit"; - test_inset_area("all / top", {left:0, top:0, width:0, height:0}); + anchored.style.positionAnchor = "implicit"; + test_inset_area("span-all top", {left:0, top:0, width:0, height:0}); </script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-computed-insets.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-computed-insets.html new file mode 100644 index 0000000000..b06714b8a7 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-computed-insets.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: inset-area should not affect computed inset values</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/css-typed-om/resources/testhelper.js"></script> +<style> + #abs { + position: absolute; + inset-area: span-all; + } +</style> +<div id="abs"></div> +<script> + test(() => { + let style = abs.computedStyleMap(); + assert_equals(style.get("inset-area").toString(), "span-all", "inset-area is supported"); + assert_style_value_equals(style.get("left"), new CSSKeywordValue("auto")); + assert_style_value_equals(style.get("right"), new CSSKeywordValue("auto")); + assert_style_value_equals(style.get("top"), new CSSKeywordValue("auto")); + assert_style_value_equals(style.get("bottom"), new CSSKeywordValue("auto")); + }, "inset-area does not affect insets at computed value time"); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-computed-insets.tentative.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-computed-insets.tentative.html deleted file mode 100644 index 4e35dd883a..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/inset-area-computed-insets.tentative.html +++ /dev/null @@ -1,24 +0,0 @@ -<!DOCTYPE html> -<title>CSS Anchor Positioning: inset-area should not affect computed inset values</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area"> -<lnik rel="help" href="https://github.com/w3c/csswg-drafts/issues/9598"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/css-typed-om/resources/testhelper.js"></script> -<style> - #abs { - position: absolute; - inset-area: all; - } -</style> -<div id="abs"></div> -<script> - test(() => { - let style = abs.computedStyleMap(); - assert_equals(style.get("inset-area").toString(), "all", "inset-area is supported"); - assert_style_value_equals(style.get("left"), new CSSKeywordValue("auto")); - assert_style_value_equals(style.get("right"), new CSSKeywordValue("auto")); - assert_style_value_equals(style.get("top"), new CSSKeywordValue("auto")); - assert_style_value_equals(style.get("bottom"), new CSSKeywordValue("auto")); - }, "inset-area does not affect insets at computed value time"); -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-computed.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-computed.html index 46e29fda39..a49ff2a452 100644 --- a/testing/web-platform/tests/css/css-anchor-position/inset-area-computed.html +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-computed.html @@ -10,14 +10,13 @@ </div> <script> test_computed_value("inset-area", "none"); - test_computed_value("inset-area", "all"); + test_computed_value("inset-area", "span-all"); test_computed_value("inset-area", "x-start"); test_computed_value("inset-area", "center"); - test_computed_value("inset-area", "all / all", "all"); - test_computed_value("inset-area", "top center"); - test_computed_value("inset-area", "bottom center / all", "center bottom"); - test_computed_value("inset-area", "x-start center x-end", "x-start x-end"); - test_computed_value("inset-area", "x-start / x-end"); + test_computed_value("inset-area", "span-all span-all", "span-all"); + test_computed_value("inset-area", "center center", "center"); + test_computed_value("inset-area", "top center", "center top"); + test_computed_value("inset-area", "span-bottom span-all", "span-bottom"); - assert_not_inherited("inset-area", "none", "all"); + assert_not_inherited("inset-area", "none", "span-all"); </script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-in-grid.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-in-grid.html new file mode 100644 index 0000000000..bbf92e8d1c --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-in-grid.html @@ -0,0 +1,106 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: inset-area positioning inside grid</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<!-- + Grid: + + 100 100 100 100 + +--------+--------+--------+--------+ + | | | | | + 100 | | | | | + | | | | | + +--------+--------+--------+--------+ + | | |XXXXXXXX|XXXXXXXX| + 100 | | |XXXXXXXX|XXXXXXXX| + | | |XXXXXXXX|XXXXXXXX| + +--------+--------+--------+--------+ + | | |XXXXXXXX|XXXXXXXX| + 100 | | |XXXXXXXX|XXXXXXXX| + | | |XXXXXXXX|XXXXXXXX| + +--------+--------+--------+--------+ + | | | | | + 100 | | | | | + | | | | | + +--------+--------+--------+--------+ + + + Inset regions: + + 100 150 150 + +--------+-------------+------------+ + | | | | + | | | | + 150 | | | | + | | | | + | | | | + +--------+-------------+------------+ + | | | | + 75 | | | | + | | | | + +--------+-------------+------------+ + | | | | + | | | | + 175 | | | | + | | | | + | | | | + +--------+-------------+------------+ + + --> + +<style> + #container { + display: grid; + grid: 1fr 1fr 1fr 1fr / 1fr 1fr 1fr 1fr; + position: relative; + width: 400px; + height: 400px; + } + + #anchor { + position: absolute; + left: 100px; + top: 150px; + width: 150px; + height: 75px; + anchor-name: --anchor; + } + + #anchored { + grid-row-start: 2; + grid-row-end: span 2; + grid-column-start: 3; + grid-column-end: auto; + position: absolute; + align-self: stretch; + justify-self: stretch; + position-anchor: --anchor; + } +</style> + +<div id="container"> + <div id="anchor"></div> + <div id="anchored"></div> +</div> +<script> + function test_inset_area(inset_area, insets, expected_offsets) { + anchored.style.insetArea = inset_area; + for (const [prop, value] of Object.entries(insets)) { + anchored.style[prop] = value; + } + + test(() => { + assert_equals(anchored.offsetLeft, expected_offsets.left, "Check expected offsetLeft"); + assert_equals(anchored.offsetTop, expected_offsets.top, "Check expected offsetTop"); + assert_equals(anchored.offsetWidth, expected_offsets.width, "Check expected offsetWidth"); + assert_equals(anchored.offsetHeight, expected_offsets.height, "Check expected offsetHeight"); + }, "Offsets for inset-area: " + inset_area + " and insets: " + JSON.stringify(insets)); + } + + test_inset_area("span-bottom span-left", {left:"auto", right:"auto", top:"auto", bottom:"auto"}, + {left:200, top:150, width:50, height:150}); + + test_inset_area("span-bottom span-left", {left:"10px", right:"10px", top:"10px", bottom:"10px"}, + {left:210, top:160, width:30, height:130}); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-inline-container.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-inline-container.html index 91c9b09735..b25a928b1f 100644 --- a/testing/web-platform/tests/css/css-anchor-position/inset-area-inline-container.html +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-inline-container.html @@ -23,13 +23,15 @@ } .anchored { position: absolute; - anchor-default: --anchor; + align-self: stretch; + justify-self: stretch; + position-anchor: --anchor; background-color: blue; } - #top-left { inset-area: top / left; } - #top-right { inset-area: top / right; } - #bottom-left { inset-area: bottom / left; } - #bottom-right { inset-area: bottom / right; } + #top-left { inset-area: top left; } + #top-right { inset-area: top right; } + #bottom-left { inset-area: bottom left; } + #bottom-right { inset-area: bottom right; } </style> <div id="in-flow"> <br> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-interpolation.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-interpolation.html index 29fe76ca5c..b09b391353 100644 --- a/testing/web-platform/tests/css/css-anchor-position/inset-area-interpolation.html +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-interpolation.html @@ -9,6 +9,6 @@ test_no_interpolation({ property: "inset-area", from: "none", - to: "all / left" + to: "span-all left" }); </script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-parsing.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-parsing.html index 1ee88b00e6..7c7fc82491 100644 --- a/testing/web-platform/tests/css/css-anchor-position/inset-area-parsing.html +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-parsing.html @@ -6,44 +6,157 @@ <script src="/css/support/parsing-testcommon.js"></script> <body> <script> + function test_valid_single_inset_area_values(valid_keywords) { + for (const keyword of valid_keywords) { + test_valid_value("inset-area", keyword); + } + }; + + function test_valid_inset_area_value_pairs(valid_keywords1, + valid_keywords2, + flip_order) { + for (const keyword1 of valid_keywords1) { + for (const keyword2 of valid_keywords2) { + if (keyword1 === keyword2) { + test_valid_value("inset-area", keyword1 + " " + keyword2, keyword1); + } else { + test_valid_value("inset-area", keyword1 + " " + keyword2, + flip_order ? keyword2 + " " + keyword1 : keyword1 + " " + keyword2); + } + } + } + }; + + function test_valid_inset_area_value_pairs_with_span_all_center( + valid_keywords, flip_order) { + for (const keyword of valid_keywords) { + test_valid_value("inset-area", keyword + " center", + flip_order ? "center " + keyword : keyword + " center"); + test_valid_value("inset-area", "center " + keyword, + flip_order ? "center " + keyword : keyword + " center"); + test_valid_value("inset-area", keyword + " span-all", keyword); + test_valid_value("inset-area", "span-all " + keyword, keyword); + } + }; + + function test_valid_inset_area_value_start_end_pairs_with_span_all_center( + valid_keywords) { + for (const keyword of valid_keywords) { + test_valid_value("inset-area", keyword + " center"); + test_valid_value("inset-area", "center " + keyword); + test_valid_value("inset-area", keyword + " span-all"); + test_valid_value("inset-area", "span-all " + keyword); + } + } + + function test_invalid_inset_area_value_pairs(valid_keywords1, + valid_keywords2) { + for (const keyword1 of valid_keywords1) { + for (const keyword2 of valid_keywords2) { + test_invalid_value("inset-area", keyword1 + " " + keyword2); + test_invalid_value("inset-area", keyword2 + " " + keyword1); + } + } + }; + + function test_invalid_inset_area_value_equal_pairs(valid_keywords) { + for (const keyword of valid_keywords) { + test_invalid_value("inset-area", keyword + " " + keyword); + } + }; + + const horizontal = [ "left", "right", "span-left", "span-right", "x-start", + "x-end", "span-x-start", "span-x-end", "x-self-start", + "x-self-end", "span-x-self-start", "span-x-self-end" ]; + const vertical = [ "top", "bottom", "span-top", "span-bottom", "y-start", + "y-end", "span-y-start", "span-y-end", "y-self-start", + "y-self-end", "span-y-self-start", "span-y-self-end" ]; + const inline = [ "inline-start", "inline-end", "span-inline-start", + "span-inline-end" ]; + const block = [ "block-start", "block-end", "span-block-start", + "span-block-end" ]; + const self_inline = [ "self-inline-start", "self-inline-end", + "span-self-inline-start", "span-self-inline-end" ]; + const self_block = [ "self-block-start", "self-block-end", + "span-self-block-start", "span-self-block-end" ]; + const start_end = [ "start", "end", "span-start", "span-end" ]; + const self_start_end = [ "self-start", "self-end", "span-self-start", + "span-self-end" ]; + + // Test initial value 'none' test_valid_value("inset-area", "none"); - test_valid_value("inset-area", "all"); - test_valid_value("inset-area", "start"); - test_valid_value("inset-area", "end"); - test_valid_value("inset-area", "top"); - test_valid_value("inset-area", "left"); - test_valid_value("inset-area", "bottom"); - test_valid_value("inset-area", "right"); - test_valid_value("inset-area", "self-start"); - test_valid_value("inset-area", "self-end"); - test_valid_value("inset-area", "x-start"); - test_valid_value("inset-area", "x-end"); - test_valid_value("inset-area", "x-self-start"); - test_valid_value("inset-area", "x-self-end"); - test_valid_value("inset-area", "y-start"); - test_valid_value("inset-area", "y-end"); - test_valid_value("inset-area", "y-self-start"); - test_valid_value("inset-area", "y-self-end"); - - test_valid_value("inset-area", "all / all", "all"); - test_valid_value("inset-area", "top / all", "top"); - test_valid_value("inset-area", "all / top", "all / top"); - test_valid_value("inset-area", "start end", "all"); - test_valid_value("inset-area", "center end start", "all"); - test_valid_value("inset-area", "center x-end x-start", "x-start x-end"); - test_valid_value("inset-area", "center end start / top center bottom", "all / top bottom"); - test_valid_value("inset-area", "end center / start", "center end / start"); - test_valid_value("inset-area", "bottom / left"); - test_valid_value("inset-area", "center start", "start center"); - // Valid to parse and compute, but resolves to 'none'. - test_valid_value("inset-area", "x-start / x-start"); - - test_invalid_value("inset-area", "/ all"); - test_invalid_value("inset-area", "none / none"); - test_invalid_value("inset-area", "start / none"); - test_invalid_value("inset-area", "none / start"); - test_invalid_value("inset-area", "center center"); + test_invalid_value("inset-area", "none none"); + test_invalid_value("inset-area", "start none"); + test_invalid_value("inset-area", "none start"); test_invalid_value("inset-area", "top left top"); - test_invalid_value("inset-area", "x-start end"); - test_invalid_value("inset-area", "bottom left"); + + // Test keywords allowed in all axes + test_valid_value("inset-area", "center"); + test_valid_value("inset-area", "center center", "center") + test_valid_value("inset-area", "span-all"); + test_valid_value("inset-area", "span-all span-all", "span-all"); + test_valid_value("inset-area", "center span-all"); + test_valid_value("inset-area", "span-all center"); + + test_valid_single_inset_area_values(horizontal); + test_valid_single_inset_area_values(vertical); + test_valid_single_inset_area_values(inline); + test_valid_single_inset_area_values(block); + test_valid_single_inset_area_values(start_end); + test_valid_single_inset_area_values(self_start_end); + + // Used if the sets of valid keywords are serialized in a different order than + // the argument order + const flip_order = true; + + // Test all valid combinations in both orders + test_valid_inset_area_value_pairs(horizontal, vertical); + test_valid_inset_area_value_pairs(vertical, horizontal, flip_order); + test_valid_inset_area_value_pairs(block, inline); + test_valid_inset_area_value_pairs(inline, block, flip_order); + test_valid_inset_area_value_pairs(self_block, self_inline); + test_valid_inset_area_value_pairs(self_inline, self_block, flip_order); + test_valid_inset_area_value_pairs(start_end, start_end); + test_valid_inset_area_value_pairs(self_start_end, self_start_end); + + // Test all valid combinations with 'span-all' and 'center' in both orders + test_valid_inset_area_value_pairs_with_span_all_center(horizontal); + test_valid_inset_area_value_pairs_with_span_all_center(vertical, flip_order); + test_valid_inset_area_value_pairs_with_span_all_center(block); + test_valid_inset_area_value_pairs_with_span_all_center(inline, flip_order); + test_valid_inset_area_value_pairs_with_span_all_center(self_block); + test_valid_inset_area_value_pairs_with_span_all_center(self_inline, flip_order); + test_valid_inset_area_value_start_end_pairs_with_span_all_center(start_end); + test_valid_inset_area_value_start_end_pairs_with_span_all_center(self_start_end); + + // Test all invalid combinations with incompatible axes in both orders + test_invalid_inset_area_value_pairs(horizontal, inline); + test_invalid_inset_area_value_pairs(horizontal, block); + test_invalid_inset_area_value_pairs(horizontal, self_inline); + test_invalid_inset_area_value_pairs(horizontal, self_block); + test_invalid_inset_area_value_pairs(horizontal, start_end); + test_invalid_inset_area_value_pairs(horizontal, self_start_end); + test_invalid_inset_area_value_pairs(vertical, inline); + test_invalid_inset_area_value_pairs(vertical, block); + test_invalid_inset_area_value_pairs(vertical, self_inline); + test_invalid_inset_area_value_pairs(vertical, self_block); + test_invalid_inset_area_value_pairs(vertical, start_end); + test_invalid_inset_area_value_pairs(vertical, self_start_end); + test_invalid_inset_area_value_pairs(inline, self_inline); + test_invalid_inset_area_value_pairs(inline, self_block); + test_invalid_inset_area_value_pairs(inline, start_end); + test_invalid_inset_area_value_pairs(inline, self_start_end); + test_invalid_inset_area_value_pairs(block, self_inline); + test_invalid_inset_area_value_pairs(block, self_block); + test_invalid_inset_area_value_pairs(block, start_end); + test_invalid_inset_area_value_pairs(block, self_start_end); + test_invalid_inset_area_value_pairs(start_end, self_start_end); + + // Test all invalid combinations of same axis + test_invalid_inset_area_value_equal_pairs(horizontal); + test_invalid_inset_area_value_equal_pairs(vertical); + test_invalid_inset_area_value_equal_pairs(inline); + test_invalid_inset_area_value_equal_pairs(block); + test_invalid_inset_area_value_equal_pairs(self_inline); + test_invalid_inset_area_value_equal_pairs(self_block); </script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-scroll-adjust-ref.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-scroll-adjust-ref.html new file mode 100644 index 0000000000..5cd863500a --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-scroll-adjust-ref.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<title>CSS Test Reference</title> +<p>You should see a green square below</p> +<div id="scroller" style="overflow:auto;width:400px;height:200px"> + <div style="height:200px"></div> + <div style="width:200px;height:200px;background:green"></div> + <div style="height:300px"></div> +</div> +<script> + scroller.scrollTop = 200; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-scroll-adjust.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-scroll-adjust.html new file mode 100644 index 0000000000..0d3cef4fc9 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-scroll-adjust.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: inset-area anchored against scrolling anchor</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area"> +<link rel="match" href="inset-area-scroll-adjust-ref.html"> +<style> + #scroller { + width: 400px; + height: 200px; + overflow: auto; + } + #anchor { + background: green; + width: 200px; + height: 100px; + } + #anchored { + inset-area: bottom; + margin: 0; + padding: 0; + border: none; + background: green; + width: 200px; + height: 100px; + } + .filler { height: 200px; } +</style> +<p>You should see a green square below</p> +<div id="scroller"> + <div class="filler"></div> + <div id="anchor" popovertarget="anchored"></div> + <div id="anchored" anchor="anchor" popover></div> + <div class="filler"></div> + <div class="filler"></div> +</div> +<script> + anchored.showPopover(); + scroller.scrollTop = 200; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-with-insets.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-with-insets.html new file mode 100644 index 0000000000..2482b44313 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-with-insets.html @@ -0,0 +1,79 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: inset-area positioning with additional insets</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position/#inset-area"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<!-- The grid: + + 100 150 150 + +--------+--------+--------+ + | | | | + 150 | | | | + | | | | + +--------+--------+--------+ + | | | | + 75 | | | | + | | | | + +--------+--------+--------+ + | | | | + 175 | | | | + | | | | + +--------+--------+--------+ + + --> +<style> + #container { + position: absolute; + width: 400px; + height: 400px; + } + #anchored { + position: absolute; + align-self: stretch; + justify-self: stretch; + position-anchor: --anchor; + } + #anchor { + margin-top: 150px; + margin-left: 100px; + width: 150px; + height: 75px; + anchor-name: --anchor; + } +</style> +<div id="container"> + <div id="anchored"></div> + <div id="anchor"></div> +</div> +<script> + function test_inset_area(inset_area, insets, expected_offsets) { + anchored.style.insetArea = inset_area; + for (const [prop, value] of Object.entries(insets)) { + anchored.style[prop] = value; + } + + test(() => { + assert_equals(anchored.offsetLeft, expected_offsets.left, "Check expected offsetLeft"); + assert_equals(anchored.offsetTop, expected_offsets.top, "Check expected offsetTop"); + assert_equals(anchored.offsetWidth, expected_offsets.width, "Check expected offsetWidth"); + assert_equals(anchored.offsetHeight, expected_offsets.height, "Check expected offsetHeight"); + }, "Offsets for inset-area: " + inset_area + " and insets: " + JSON.stringify(insets)); + } + + test_inset_area("span-all", {top:"5px", bottom:"5px", left:"5px", right:"5px"}, + {left:5, top:5, width:390, height:390}); + + test_inset_area("center center", {top:"10px", bottom:"40px", left:"5px", right:"15px"}, + {left:105, top:160, width:130, height:25}); + + test_inset_area("left bottom", {top:"10px", bottom:"40px", left:"5px", right:"15px"}, + {left:5, top:235, width:80, height:125}); + + test_inset_area("span-right center", {top:"20%", bottom:"auto", left:"auto", right:"25%"}, + {left:100, top:165, width:225, height:60}); + + // No implicit anchor means the inset-area should not apply, but the insets still should. + anchored.style.positionAnchor = "implicit"; + test_inset_area("bottom right", {left:"50px", right:"100px", top:"30px" , bottom:"10px"}, + {left:50, top:30, width:250, height:360}); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/inset-area-wm-dir.html b/testing/web-platform/tests/css/css-anchor-position/inset-area-wm-dir.html index 5268cba7e5..7e740b4ace 100644 --- a/testing/web-platform/tests/css/css-anchor-position/inset-area-wm-dir.html +++ b/testing/web-platform/tests/css/css-anchor-position/inset-area-wm-dir.html @@ -31,7 +31,7 @@ position: absolute; align-self: stretch; justify-self: stretch; - anchor-default: --anchor; + position-anchor: --anchor; } #anchor { margin-top: 150px; @@ -70,50 +70,54 @@ let writing_direction = "containing-block: horizontal-tb / rtl"; container.style.writingMode = "horizontal-tb"; container.style.direction = "rtl"; - test_inset_area(writing_direction, "start / start", top_right); - test_inset_area(writing_direction, "self-start / self-start", top_left); - test_inset_area(writing_direction, "x-start / y-start", top_right); - test_inset_area(writing_direction, "x-self-start / y-self-start", top_left); + test_inset_area(writing_direction, "start start", top_right); + test_inset_area(writing_direction, "self-start self-start", top_left); + test_inset_area(writing_direction, "x-start y-start", top_right); + test_inset_area(writing_direction, "x-self-start y-self-start", top_left); + test_inset_area(writing_direction, "inline-start block-end", bottom_right); + test_inset_area(writing_direction, "self-inline-start self-block-end", bottom_left); // containing-block: vertical-lr / ltr // self: horizontal-tb / ltr writing_direction = "containing-block: vertical-lr / ltr"; container.style.writingMode = "vertical-lr"; container.style.direction = "ltr"; - test_inset_area(writing_direction, "start / start", top_left); - test_inset_area(writing_direction, "self-start / self-start", top_left); - test_inset_area(writing_direction, "x-start / y-start", top_left); - test_inset_area(writing_direction, "x-self-start / y-self-start", top_left); + test_inset_area(writing_direction, "start start", top_left); + test_inset_area(writing_direction, "self-start self-start", top_left); + test_inset_area(writing_direction, "x-start y-start", top_left); + test_inset_area(writing_direction, "x-self-start y-self-start", top_left); + test_inset_area(writing_direction, "inline-start block-end", top_right); + test_inset_area(writing_direction, "self-inline-start self-block-end", bottom_left); // containing-block: vertical-lr / rtl // self: horizontal-tb / ltr writing_direction = "containing-block: vertical-lr / rtl"; container.style.writingMode = "vertical-lr"; container.style.direction = "rtl"; - test_inset_area(writing_direction, "start / start", bottom_left); - test_inset_area(writing_direction, "self-start / self-start", top_left); - test_inset_area(writing_direction, "x-start / y-start", bottom_left); - test_inset_area(writing_direction, "x-self-start / y-self-start", top_left); + test_inset_area(writing_direction, "start start", bottom_left); + test_inset_area(writing_direction, "self-start self-start", top_left); + test_inset_area(writing_direction, "x-start y-start", bottom_left); + test_inset_area(writing_direction, "x-self-start y-self-start", top_left); // containing-block: vertical-rl / ltr // self: horizontal-tb / ltr writing_direction = "containing-block: vertical-rl / ltr"; container.style.writingMode = "vertical-rl"; container.style.direction = "ltr"; - test_inset_area(writing_direction, "start / start", top_right); - test_inset_area(writing_direction, "self-start / self-start", top_left); - test_inset_area(writing_direction, "x-start / y-start", top_right); - test_inset_area(writing_direction, "x-self-start / y-self-start", top_left); + test_inset_area(writing_direction, "start start", top_right); + test_inset_area(writing_direction, "self-start self-start", top_left); + test_inset_area(writing_direction, "x-start y-start", top_right); + test_inset_area(writing_direction, "x-self-start y-self-start", top_left); // containing-block: vertical-rl / rtl // self: horizontal-tb / ltr writing_direction = "containing-block: vertical-rl / rtl"; container.style.writingMode = "vertical-rl"; container.style.direction = "rtl"; - test_inset_area(writing_direction, "start / start", bottom_right); - test_inset_area(writing_direction, "self-start / self-start", top_left); - test_inset_area(writing_direction, "x-start / y-start", bottom_right); - test_inset_area(writing_direction, "x-self-start / y-self-start", top_left); + test_inset_area(writing_direction, "start start", bottom_right); + test_inset_area(writing_direction, "self-start self-start", top_left); + test_inset_area(writing_direction, "x-start y-start", bottom_right); + test_inset_area(writing_direction, "x-self-start y-self-start", top_left); // Writing-mode and direction on self container.style.writingMode = "horizontal-tb"; @@ -124,49 +128,49 @@ writing_direction = "self: horizontal-tb / rtl"; anchored.style.writingMode = "horizontal-tb"; anchored.style.direction = "rtl"; - test_inset_area(writing_direction, "start / start", top_left); - test_inset_area(writing_direction, "self-start / self-start", top_right); - test_inset_area(writing_direction, "x-start / y-start", top_left); - test_inset_area(writing_direction, "x-self-start / y-self-start", top_right); + test_inset_area(writing_direction, "start start", top_left); + test_inset_area(writing_direction, "self-start self-start", top_right); + test_inset_area(writing_direction, "x-start y-start", top_left); + test_inset_area(writing_direction, "x-self-start y-self-start", top_right); // containing-block: horizontal-tb / ltr // self: vertical-lr / ltr writing_direction = "self: vertical-lr / ltr"; anchored.style.writingMode = "vertical-lr"; anchored.style.direction = "ltr"; - test_inset_area(writing_direction, "start / start", top_left); - test_inset_area(writing_direction, "self-start / self-start", top_left); - test_inset_area(writing_direction, "x-start / y-start", top_left); - test_inset_area(writing_direction, "x-self-start / y-self-start", top_left); + test_inset_area(writing_direction, "start start", top_left); + test_inset_area(writing_direction, "self-start self-start", top_left); + test_inset_area(writing_direction, "x-start y-start", top_left); + test_inset_area(writing_direction, "x-self-start y-self-start", top_left); // containing-block: horizontal-tb / ltr // self: vertical-lr / rtl writing_direction = "self: vertical-lr / rtl"; anchored.style.writingMode = "vertical-lr"; anchored.style.direction = "rtl"; - test_inset_area(writing_direction, "start / start", top_left); - test_inset_area(writing_direction, "self-start / self-start", bottom_left); - test_inset_area(writing_direction, "x-start / y-start", top_left); - test_inset_area(writing_direction, "x-self-start / y-self-start", bottom_left); + test_inset_area(writing_direction, "start start", top_left); + test_inset_area(writing_direction, "self-start self-start", bottom_left); + test_inset_area(writing_direction, "x-start y-start", top_left); + test_inset_area(writing_direction, "x-self-start y-self-start", bottom_left); // containing-block: horizontal-tb / ltr // self: vertical-rl / ltr writing_direction = "self: vertical-rl / ltr"; anchored.style.writingMode = "vertical-rl"; anchored.style.direction = "ltr"; - test_inset_area(writing_direction, "start / start", top_left); - test_inset_area(writing_direction, "self-start / self-start", top_right); - test_inset_area(writing_direction, "x-start / y-start", top_left); - test_inset_area(writing_direction, "x-self-start / y-self-start", top_right); + test_inset_area(writing_direction, "start start", top_left); + test_inset_area(writing_direction, "self-start self-start", top_right); + test_inset_area(writing_direction, "x-start y-start", top_left); + test_inset_area(writing_direction, "x-self-start y-self-start", top_right); // containing-block: horizontal-tb / ltr // self: vertical-rl / rtl writing_direction = "self: vertical-rl / rtl"; anchored.style.writingMode = "vertical-rl"; anchored.style.direction = "rtl"; - test_inset_area(writing_direction, "start / start", top_left); - test_inset_area(writing_direction, "self-start / self-start", bottom_right); - test_inset_area(writing_direction, "x-start / y-start", top_left); - test_inset_area(writing_direction, "x-self-start / y-self-start", bottom_right); + test_inset_area(writing_direction, "start start", top_left); + test_inset_area(writing_direction, "self-start self-start", bottom_right); + test_inset_area(writing_direction, "x-start y-start", top_left); + test_inset_area(writing_direction, "x-self-start y-self-start", bottom_right); </script> diff --git a/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-computed.html b/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-computed.html index f0944b7faa..91b27d4f31 100644 --- a/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-computed.html +++ b/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-computed.html @@ -19,6 +19,15 @@ test_computed_value("position-try-options", "flip-start flip-inline flip-block"); test_computed_value("position-try-options", "flip-start --flop", "--flop flip-start"); test_computed_value("position-try-options", "--flop flip-start"); + test_computed_value("position-try-options", "inset-area(left top)"); + test_computed_value("position-try-options", "inset-area(top left)", "inset-area(left top)"); + test_computed_value("position-try-options", "inset-area(start start)", "inset-area(start)"); + test_computed_value("position-try-options", "inset-area(left), inset-area(right)"); + test_computed_value("position-try-options", "--foo, inset-area(left)"); + test_computed_value("position-try-options", "--foo, inset-area(left), --bar"); + test_computed_value("position-try-options", "--foo, flip-start, inset-area(left)"); + test_computed_value("position-try-options", "--foo flip-start, inset-area(left)"); + test_computed_value("position-try-options", "inset-area(left), --bar flip-start"); assert_not_inherited("position-try-options", "none", "flip-inline"); </script> diff --git a/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-parsing.html b/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-parsing.html index 303cc4d0dd..72bc74111b 100644 --- a/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-parsing.html +++ b/testing/web-platform/tests/css/css-anchor-position/parsing/position-try-options-parsing.html @@ -25,6 +25,15 @@ test_valid_value("position-try-options", "--bar flip-inline flip-block"); test_valid_value("position-try-options", "flip-inline --foo", "--foo flip-inline"); test_valid_value("position-try-options", "flip-inline flip-start --foo", "--foo flip-inline flip-start"); + test_valid_value("position-try-options", "inset-area(left top)"); + test_valid_value("position-try-options", "inset-area(top left)", "inset-area(left top)"); + test_valid_value("position-try-options", "inset-area(start start)", "inset-area(start)"); + test_valid_value("position-try-options", "inset-area(left), inset-area(right)"); + test_valid_value("position-try-options", "--foo, inset-area(left)"); + test_valid_value("position-try-options", "--foo, inset-area(left), --bar"); + test_valid_value("position-try-options", "--foo, flip-start, inset-area(left)"); + test_valid_value("position-try-options", "--foo flip-start, inset-area(left)"); + test_valid_value("position-try-options", "inset-area(left), --bar flip-start"); test_invalid_value("position-try-options", "none, flip-start"); test_invalid_value("position-try-options", "flip-block flip-block"); @@ -37,4 +46,10 @@ test_invalid_value("position-try-options", "foo"); test_invalid_value("position-try-options", "flip-start 123"); test_invalid_value("position-try-options", "--foo 123"); + test_invalid_value("position-try-options", "--foo inset-area(left)"); + test_invalid_value("position-try-options", "flip-start inset-area(left)"); + test_invalid_value("position-try-options", "inset-area(left) --foo "); + test_invalid_value("position-try-options", "inset-area(left) flip-start"); + test_invalid_value("position-try-options", "--foo, none"); + test_invalid_value("position-try-options", "--foo, inset-area(none)"); </script> diff --git a/testing/web-platform/tests/css/css-anchor-position/parsing/position-visibility-computed.tentative.html b/testing/web-platform/tests/css/css-anchor-position/parsing/position-visibility-computed.tentative.html new file mode 100644 index 0000000000..ff4ceb73df --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/parsing/position-visibility-computed.tentative.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Anchor Positioning Test: Computed position-visibility</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +<script src="/css/support/inheritance-testcommon.js"></script> +<div id="container"> + <div id="target"></div> +</div> +<script> + test_computed_value("position-visibility", "always"); + test_computed_value("position-visibility", "anchors-valid"); + test_computed_value("position-visibility", "anchors-visible"); + test_computed_value("position-visibility", "no-overflow"); + test_computed_value("position-visibility", "anchors-valid no-overflow"); + test_computed_value("position-visibility", "anchors-visible no-overflow"); + test_computed_value("position-visibility", "no-overflow anchors-valid", "anchors-valid no-overflow"); + test_computed_value("position-visibility", "no-overflow anchors-visible", "anchors-visible no-overflow"); + + assert_not_inherited("position-visibility", "always", "no-overflow"); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/parsing/position-visibility-parsing.tentative.html b/testing/web-platform/tests/css/css-anchor-position/parsing/position-visibility-parsing.tentative.html new file mode 100644 index 0000000000..18dd27eadb --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/parsing/position-visibility-parsing.tentative.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Anchor Positioning Test: Parsing of position-visibility</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<div id="target"></div> +<script> + test_valid_value("position-visibility", "initial"); + test_valid_value("position-visibility", "inherit"); + test_valid_value("position-visibility", "unset"); + test_valid_value("position-visibility", "revert"); + test_valid_value("position-visibility", "always"); + test_valid_value("position-visibility", "anchors-valid"); + test_valid_value("position-visibility", "anchors-visible"); + test_valid_value("position-visibility", "no-overflow"); + test_valid_value("position-visibility", "anchors-valid no-overflow"); + test_valid_value("position-visibility", "anchors-visible no-overflow"); + test_valid_value("position-visibility", "no-overflow anchors-valid", "anchors-valid no-overflow"); + test_valid_value("position-visibility", "no-overflow anchors-visible", "anchors-visible no-overflow"); + + test_invalid_value("position-visibility", "always anchors-valid"); + test_invalid_value("position-visibility", "always anchors-visible"); + test_invalid_value("position-visibility", "always no-overflow"); + test_invalid_value("position-visibility", "anchors-valid anchors-visible"); + test_invalid_value("position-visibility", "no-overflow no-overflow"); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-anchor-001.html b/testing/web-platform/tests/css/css-anchor-position/position-anchor-001.html new file mode 100644 index 0000000000..d927b6b902 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-anchor-001.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<title>Tests the 'position-anchor' property</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#position-anchor"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="match" href="position-anchor-ref.html"> +<style> +.anchor { + width: 100px; + height: 100px; + background: orange; +} + +.target { + position: fixed; + background: lime; + position-try-options: --pf; + left: 999999px; /* force fallback */ +} + +@position-try --pf { + top: anchor(bottom, 0px); + left: anchor(left, 0px); + width: anchor-size(width, 0px); + height: anchor-size(height, 0px); +} + +body { + margin: 0; +} + +#anchor1 { + anchor-name: --a1; + margin-left: 100px; +} + +#target1 { + position-anchor: --a1; +} + +#anchor2 { + anchor-name: --a2; + margin-left: 300px; + margin-top: 100px; +} + +#target2 { + position-anchor: --a2; +} +</style> + +<div id="anchor1" class="anchor">anchor1</div> +<div id="anchor2" class="anchor">anchor2</div> + +<div id="target1" class="target">target1</div> +<div id="target2" class="target">target2</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-anchor-002.html b/testing/web-platform/tests/css/css-anchor-position/position-anchor-002.html new file mode 100644 index 0000000000..d55338e1e9 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-anchor-002.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<title>Tests that 'position-anchor' property value is tree-scoped</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#position-anchor"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<link rel="match" href="position-anchor-ref.html"> +<style> +.anchor { + width: 100px; + height: 100px; + background: orange; +} + +.target { + position: fixed; + background: lime; + position-try-options: --pf; + left: 999999px; /* force fallback */ +} + +@position-try --pf { + top: anchor(bottom, 0px); + left: anchor(left, 0px); + width: anchor-size(width, 0px); + height: anchor-size(height, 0px); +} + +body { + margin: 0; +} + +#fake-anchor { + anchor-name: --a; +} + +#anchor1 { + margin-left: 100px; +} + +#anchor2 { + margin-left: 300px; + margin-top: 100px; +} + +</style> + +<div id="fake-anchor"></div> + +<div id="anchor1" class="anchor"> + anchor1 + <div id="target1" class="target">target1</div> +</div> + +<div id="anchor2" class="anchor"> + anchor2 + <div id="target2" class="target">target2</div> +</div> + +<script> +for (let host of document.querySelectorAll('.anchor')) { + let shadow = host.attachShadow({mode: 'open'}); + shadow.innerHTML = ` + <style> + :host { anchor-name: --a; } + ::slotted(.target) { position-anchor: --a; } + </style> + <slot></slot> + `; +} +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-anchor-003.html b/testing/web-platform/tests/css/css-anchor-position/position-anchor-003.html new file mode 100644 index 0000000000..6117027e22 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-anchor-003.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<title>Tests that layout is updated on position-anchor value changes</title> +<link rel="help" href="https://drafts4.csswg.org/css-anchor-position-1/#position-anchor"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<style> +#target { + position: fixed; + width: 100px; + height: 100px; + background: lime; + top: anchor(top); + left: anchor(right); + position-anchor: --a; +} + +#target.after { + position-anchor: --b; +} + +#anchor1, #anchor2 { + width: 100px; + height: 100px; + background: orange; +} + +#anchor1 { + anchor-name: --a; +} + +#anchor2 { + margin-left: 100px; + anchor-name: --b; +} + +body { + margin: 0; +} +</style> + +<div id="anchor1"></div> +<div id="anchor2"></div> +<div id="target"></div> + +<script> +test(() => { + document.body.offsetLeft; // Force layout + target.classList.add('after'); + // #target should be anchored to #anchor2 now + assert_equals(target.offsetLeft, 200); + assert_equals(target.offsetTop, 100); +}, 'Layout is updated on `position-anchor` changes'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-anchor-basics.html b/testing/web-platform/tests/css/css-anchor-position/position-anchor-basics.html new file mode 100644 index 0000000000..f9fe9dd6f8 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-anchor-basics.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<title>Tests basics of the 'position-anchor' property</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-position-anchor"> +<link rel="author" href="mailto:xiaochengh@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +<script src="/css/support/inheritance-testcommon.js"></script> +<script src="/css/support/interpolation-testcommon.js"></script> + +<div id="container"> + <div id="target"></div> +</div> + +<script> +// position-anchor: <anchor-element> +// <anchor-element> = implicit | <dashed-ident> +test_valid_value('position-anchor', 'implicit'); +test_valid_value('position-anchor', '--foo'); +test_invalid_value('position-anchor', 'none'); +test_invalid_value('position-anchor', 'foo-bar'); +test_invalid_value('position-anchor', '--foo --bar') +test_invalid_value('position-anchor', '--foo, --bar') +test_invalid_value('position-anchor', '100px'); +test_invalid_value('position-anchor', '100%'); + +// Computed value: as specified +test_computed_value('position-anchor', 'implicit'); +test_computed_value('position-anchor', '--foo'); + +// Initial: implicit +// Inherited: no +assert_not_inherited('position-anchor', 'implicit', '--foo'); + +// Animation type: discrete +test_no_interpolation({ + property: 'position-anchor', + from: '--foo', + to: 'implicit', +}); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/anchor-default-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-anchor-ref.html index 4d7de12447..4d7de12447 100644 --- a/testing/web-platform/tests/css/css-anchor-position/anchor-default-ref.html +++ b/testing/web-platform/tests/css/css-anchor-position/position-anchor-ref.html diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-basics.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-basics.html deleted file mode 100644 index bfeb921de1..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/position-fallback-basics.html +++ /dev/null @@ -1,40 +0,0 @@ -<!DOCTYPE html> -<title>Tests basics of the 'position-fallback' property</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#propdef-position-fallback"> -<link rel="author" href="mailto:xiaochengh@chromium.org"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<script src="/css/support/computed-testcommon.js"></script> -<script src="/css/support/inheritance-testcommon.js"></script> -<script src="/css/support/interpolation-testcommon.js"></script> - -<div id="container"> - <div id="target"></div> -</div> - -<script> -// position-fallback: none | <dashed-ident> -test_valid_value('position-fallback', 'none'); -test_valid_value('position-fallback', '--foo'); -test_invalid_value('position-fallback', 'foo-bar'); -test_invalid_value('position-fallback', '--foo --bar') -test_invalid_value('position-fallback', '--foo, --bar') -test_invalid_value('position-fallback', '100px'); -test_invalid_value('position-fallback', '100%'); - -// Computed value: as specified -test_computed_value('position-fallback', 'none'); -test_computed_value('position-fallback', '--foo'); - -// Initial: none -// Inherited: no -assert_not_inherited('position-fallback', 'none', '--foo'); - -// Animation type: discrete -test_no_interpolation({ - property: 'position-fallback', - from: '--foo', - to: 'none', -}); -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-001.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-001.html deleted file mode 100644 index ebd1e78fb9..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-001.html +++ /dev/null @@ -1,84 +0,0 @@ -<!DOCTYPE html> -<title>Tests basic functionalities of 'position-fallback-bounds'</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds"> -<link rel="author" href="mailto:xiaochengh@chromium.org"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/resources/check-layout-th.js"></script> -<script src="support/test-common.js"></script> - -<style> -body { - margin: 0; -} - -.container { - position: relative; - width: 400px; - height: 400px; - top: 100px; - anchor-name: --bounds; - outline: 1px dashed black; -} - -.anchor { - position: absolute; - width: 100px; - height: 100px; - left: 150px; - background-color: orange; -} - -.target { - position: fixed; - width: 100px; - height: 100px; - background-color: lime; - left: anchor(left); - position-fallback-bounds: --bounds; -} - -#anchor1 { - top: 0; - anchor-name: --a1; -} -#anchor2 { - bottom: 0; - anchor-name: --a2; -} - -#target1 { - anchor-default: --a1; - bottom: anchor(top); - position-try-options: --bottom; -} -#target2 { - anchor-default: --a2; - top: anchor(bottom); - position-try-options: --top; -} - -@position-try --bottom { - bottom: auto; - top: anchor(bottom); -} -@position-try --top { - top: auto; - bottom: anchor(top); -} -</style> - -<body onload="checkLayoutForAnchorPos('.target')"> - <div class="container" id="bounds"> - <div class="anchor" id="anchor1"></div> - <div class="anchor" id="anchor2"></div> - </div> - - <!-- Enough space above the anchor in the viewport but not in the additional - bounds rect, which triggers fallback --> - <div class="target" id="target1" data-offset-y=200></div> - - <!-- Enough space below the anchor in the viewport but not in the additional - bounds rect, which triggers fallback --> - <div class="target" id="target2" data-offset-y=300></div> -</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-002.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-002.html deleted file mode 100644 index 159484ad33..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-002.html +++ /dev/null @@ -1,87 +0,0 @@ -<!DOCTYPE html> -<title>Tests 'position-fallback-bounds' with mixed writing modes</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds"> -<link rel="author" href="mailto:xiaochengh@chromium.org"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/resources/check-layout-th.js"></script> -<script src="support/test-common.js"></script> - -<style> -body { - margin: 0; -} - -.container { - position: relative; - width: 400px; - height: 400px; - top: 100px; - anchor-name: --bounds; - outline: 1px dashed black; -} - -.anchor { - position: absolute; - width: 100px; - height: 100px; - left: 150px; - background-color: orange; -} - -.target { - position: fixed; - width: 100px; - height: 100px; - background-color: lime; - left: anchor(left); - position-fallback-bounds: --bounds; -} - -#anchor1 { - top: 0; - anchor-name: --a1; -} -#anchor2 { - bottom: 0; - anchor-name: --a2; -} - -#target1 { - anchor-default: --a1; - bottom: anchor(top); - position-try: --bottom; - writing-mode: vertical-rl; -} -#target2 { - anchor-default: --a2; - top: anchor(bottom); - position-try: --top; - writing-mode: vertical-lr; - direction: rtl; -} - -@position-try --bottom { - bottom: auto; - top: anchor(bottom); -} -@position-try --top { - top: auto; - bottom: anchor(top); -} -</style> - -<body onload="checkLayoutForAnchorPos('.target')"> - <div class="container" id="bounds"> - <div class="anchor" id="anchor1"></div> - <div class="anchor" id="anchor2"></div> - </div> - - <!-- Enough space above the anchor in the viewport but not in the additional - bounds rect, which triggers fallback --> - <div class="target" id="target1" data-offset-y=200></div> - - <!-- Enough space below the anchor in the viewport but not in the additional - bounds rect, which triggers fallback --> - <div class="target" id="target2" data-offset-y=300></div> -</body> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-003.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-003.html deleted file mode 100644 index 9da7bf9ccd..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-003.html +++ /dev/null @@ -1,95 +0,0 @@ -<!DOCTYPE html> -<title>Tests basic interaction between 'position-fallback-bounds' and scrolling</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds"> -<link rel="author" href="mailto:xiaochengh@chromium.org"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="support/test-common.js"></script> - -<style> -body { - position: relative; - width: 200vw; - height: 200vh; -} - -#bounds { - position: fixed; - inset: 50px; - background: gray; - opacity: 0.1; - z-index: -1; - anchor-name: --bounds; -} - -#anchor { - position: absolute; - width: 100px; - height: 100px; - left: 200px; - top: 200px; - background-color: orange; - anchor-name: --a; -} - -#target { - position: fixed; - width: 100px; - height: 100px; - background-color: lime; - anchor-default: --a; - position-fallback-bounds: --bounds; - position-try-options: --corner1, --corner2, --corner3; - bottom: anchor(top); - right: anchor(left); -} - -@position-try --corner1 { - inset: auto; - top: anchor(bottom); - right: anchor(left); -} -@position-try --corner2 { - inset: auto; - bottom: anchor(top); - left: anchor(right); -} -@position-try --corner3 { - inset: auto; - top: anchor(bottom); - left: anchor(right); -} -</style> - -<div id="bounds"></div> -<div id="anchor"></div> -<div id="target"></div> - -<script> -promise_test(async () => { - await waitUntilNextAnimationFrame(); - assert_fallback_position(target, anchor, 'top'); - assert_fallback_position(target, anchor, 'left'); -}, "Target is at anchor's top-left corner at initial scroll position"); - -promise_test(async () => { - document.documentElement.scrollTop = 100; - await waitUntilNextAnimationFrame(); - assert_fallback_position(target, anchor, 'bottom'); - assert_fallback_position(target, anchor, 'left'); -}, "Target falls back to anchor's bottom-left corner after anchor is scrolled upwards"); - -promise_test(async () => { - document.documentElement.scrollLeft = 100; - await waitUntilNextAnimationFrame(); - assert_fallback_position(target, anchor, 'bottom'); - assert_fallback_position(target, anchor, 'right'); -}, "Target falls back to anchor's bottom-right corner after anchor is further scrolled leftwards"); - -promise_test(async () => { - document.documentElement.scrollTop = 0; - await waitUntilNextAnimationFrame(); - assert_fallback_position(target, anchor, 'top'); - assert_fallback_position(target, anchor, 'right'); -}, "Target falls back to anchor's top-left corner after anchor is scrolled back downwards"); -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-004.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-004.html deleted file mode 100644 index eeee710e0f..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-004.html +++ /dev/null @@ -1,99 +0,0 @@ -<!DOCTYPE html> -<title>Tests complex interaction between 'position-fallback-bounds' and scrolling</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds"> -<link rel="author" href="mailto:xiaochengh@chromium.org"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="support/test-common.js"></script> - -<style> -body { - margin: 0; -} - -#anchor-scroller { - position: absolute; - height: 175px; - width: 200px; - left: 200px; - top: 100px; - overflow-y: scroll; - z-index: 100; -} - -#anchor { - position: absolute; - width: 100px; - height: 100px; - left: 0; - top: 150px; - background-color: orange; - anchor-name: --a; -} - -#bounds-scroller { - position: absolute; - width: 400px; - height: 400px; - left: 100px; - top: 0; - overflow-y: scroll; -} - -#bounds { - position: absolute; - width: 400px; - height: 400px; - top: 100px; - background: gray; - anchor-name: --bounds; -} - -#target { - position: fixed; - width: 100px; - height: 100px; - background-color: lime; - left: anchor(left); - anchor-default: --a; - position-fallback-bounds: --bounds; - position-try-options: --bottom; - bottom: anchor(top); -} - -@position-try --bottom { - top: anchor(bottom); - bottom: auto; -} -</style> - -<div id="anchor-scroller"> - <div id="anchor"></div> -</div> - -<div id="bounds-scroller"> - <div id="bounds"></div> -</div> - -<div id="target"></div> - -<script> -promise_test(async () => { - await waitUntilNextAnimationFrame(); - assert_fallback_position(target, anchor, 'top'); -}, 'Target is above anchor at initial scroll position'); - -promise_test(async () => { - const anchorScroller = document.getElementById('anchor-scroller'); - anchorScroller.scrollTop = 100; - await waitUntilNextAnimationFrame(); - assert_fallback_position(target, anchor, 'bottom'); -}, 'Target falls back to below anchor after anchor is scrolled upwards'); - -promise_test(async () => { - const boundsScroller = document.getElementById('bounds-scroller'); - boundsScroller.scrollTop = 100; - await waitUntilNextAnimationFrame(); - assert_fallback_position(target, anchor, 'top'); -}, 'Target returns to above anchor after bounds are scrolled upwards'); -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-005.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-005.html deleted file mode 100644 index fecd4328a5..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-005.html +++ /dev/null @@ -1,76 +0,0 @@ -<!DOCTYPE html> -<title>Tests relayout after 'position-fallback-bounds' change</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds"> -<link rel="author" href="mailto:xiaochengh@chromium.org"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="support/test-common.js"></script> - -<style> -.bounds { - position: absolute; - left: 100px; - width: 200px; - height: 200px; - background: gray; - opacity: 0.1; -} - -#bounds1 { - top: 0; - anchor-name: --bounds1; -} - -#bounds2 { - top: 300px; - anchor-name: --bounds2; -} - -#anchor { - position: absolute; - width: 100px; - height: 100px; - background: orange; - left: 150px; - top: 200px; - anchor-name: --a; -} - -#target { - position: fixed; - width: 100px; - height: 100px; - background: lime; - left: anchor(left); - bottom: anchor(top); - anchor-default: --a; - position-try-options: --bottom; - position-fallback-bounds: --bounds1; -} - -@position-try --bottom { - bottom: auto; - top: anchor(bottom); -} -</style> - -<div class="bounds" id="bounds1"></div> -<div class="bounds" id="bounds2"></div> -<div id="anchor"></div> -<div id="target"></div> - -<script> -test(() => { - assert_fallback_position(target, anchor, 'top'); -}, 'Initial layout'); - -test(() => { - target.style = 'position-fallback-bounds: --bounds2'; - assert_fallback_position(target, anchor, 'bottom'); -}, 'Layout is updated after position-fallback-bounds property changes'); - -test(() => { - bounds2.style = 'top: 0; height: 500px'; - assert_fallback_position(target, anchor, 'top'); -}, 'Layout is updated after additional fallback-bounds rect changes'); -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-006.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-006.html deleted file mode 100644 index 023c650cfc..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-006.html +++ /dev/null @@ -1,65 +0,0 @@ -<!DOCTYPE html> -<title>Tests 'position-fallback-bounds' should work without default anchor</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-bounds"> -<link rel="author" href="mailto:xiaochengh@chromium.org"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="support/test-common.js"></script> - -<style> -body { - margin: 0; -} - -#bounds { - position: absolute; - left: 0; - right: 0; - top: 100px; - height: calc(100vh + 200px); - background: lightgray; - anchor-name: --bounds; -} - -#anchor { - position: fixed; - width: 100px; - height: 100px; - left: 0; - top: 100px; - background: orange; - anchor-name: --a; -} - -#target { - position: fixed; - width: 100px; - height: 100px; - background: lime; - position-try-options: --bottom; - position-fallback-bounds: --bounds; - bottom: anchor(--a top); -} - -@position-try --bottom { - top: anchor(--a bottom); - bottom: auto; -} -</style> - -<div id=bounds></div> -<div id=anchor></div> -<div id=target></div> - -<script> -promise_test(async () => { - await waitUntilNextAnimationFrame(); - assert_fallback_position(target, anchor, 'bottom'); -}, "Target is below anchor at initial scroll position"); - -promise_test(async () => { - document.documentElement.scrollTop = 100; - await waitUntilNextAnimationFrame(); - assert_fallback_position(target, anchor, 'top'); -}, "Target moves to above anchor after the additional fallback-bounds rect is scrolled upwards"); -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-basics.html b/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-basics.html deleted file mode 100644 index 3b8b67bd97..0000000000 --- a/testing/web-platform/tests/css/css-anchor-position/position-fallback-bounds-basics.html +++ /dev/null @@ -1,40 +0,0 @@ -<!DOCTYPE html> -<title>Tests basics of the 'position-fallback' property</title> -<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#propdef-position-fallback-bounds"> -<link rel="author" href="mailto:xiaochengh@chromium.org"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<script src="/css/support/computed-testcommon.js"></script> -<script src="/css/support/inheritance-testcommon.js"></script> -<script src="/css/support/interpolation-testcommon.js"></script> - -<div id="container"> - <div id="target"></div> -</div> - -<script> -// position-fallback-bounds: normal | <dashed-ident> -test_valid_value('position-fallback-bounds', 'normal'); -test_valid_value('position-fallback-bounds', '--foo'); -test_invalid_value('position-fallback-bounds', 'foo-bar'); -test_invalid_value('position-fallback-bounds', '--foo --bar') -test_invalid_value('position-fallback-bounds', '--foo, --bar') -test_invalid_value('position-fallback-bounds', '100px'); -test_invalid_value('position-fallback-bounds', '100%'); - -// Computed value: as specified -test_computed_value('position-fallback-bounds', 'normal'); -test_computed_value('position-fallback-bounds', '--foo'); - -// Initial: normal -// Inherited: no -assert_not_inherited('position-fallback-bounds', 'normal', '--foo'); - -// Animation type: discrete -test_no_interpolation({ - property: 'position-fallback-bounds', - from: '--foo', - to: 'normal', -}); -</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-try-cascade.html b/testing/web-platform/tests/css/css-anchor-position/position-try-cascade.html new file mode 100644 index 0000000000..d73ddd586a --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-try-cascade.html @@ -0,0 +1,141 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning Test: @position-try and cascade interaction</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-apply"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + .cb { + position: relative; + width: 100px; + height: 100px; + background: lightpink; + display: inline-block; + } + .abs { + position: absolute; + background: darkcyan; + left: 0px; + top: 0px; + width: 150px; /* force fallback */ + height: 25px; + position-try-options: --pf; + } + @position-try --pf { + width: 50px; + left: 50px; + top: 50px; + } +</style> + +<!-- Basic @position-try rule --> +<div class=cb> + <div id=abs_try class=abs></div> +</div> +<script> + test(() => { + assert_equals(abs_try.offsetLeft, 50); + assert_equals(abs_try.offsetTop, 50); + }, '@position-try rule applies'); +</script> + +<!-- Inline style --> +<div class=cb> + <div id=abs_inline class=abs style="left:20px"></div> +</div> +<script> + test(() => { + assert_equals(abs_inline.offsetLeft, 50); + assert_equals(abs_inline.offsetTop, 50); + }, '@position-try rule wins over inline style'); +</script> + +<!-- !important --> +<style> + #abs_important { + left: 10px !important; + } +</style> +<div class=cb> + <div id=abs_important class=abs></div> +</div> +<script> + test(() => { + assert_equals(abs_important.offsetLeft, 10); + assert_equals(abs_important.offsetTop, 50); + }, '@position-try rule does not win over !important'); +</script> + +<!-- Animations --> +<style> + @keyframes anim { + from { top: 20px; } + to { top: 20px; } + } + #abs_animation { + animation: anim 1000s steps(2, start) paused; + } +</style> +<div class=cb> + <div id=abs_animation class=abs></div> +</div> +<script> + test(() => { + assert_equals(abs_animation.offsetLeft, 50); + assert_equals(abs_animation.offsetTop, 20); + }, '@position-try rule does not win over animations'); +</script> + +<!-- Transitions --> +<style> + #abs_transition.move { + top: 10px !important; + transition: top 1000s steps(2, start); +</style> +<div class=cb> + <div id=abs_transition class=abs></div> +</div> +<script> + test(() => { + abs_transition.offsetTop; + abs_transition.classList.add('move'); + assert_equals(abs_transition.offsetLeft, 50); + assert_equals(abs_transition.offsetTop, 30); + }, '@position-try rule does not win over transitions'); +</script> + +<!-- revert / revert-layer --> +<style> + #abs_revert { + position-try-options: --pf-revert; + } + @layer author-layer { + #abs_revert { + top: 30px; + left: 30px; + } + } + #abs_revert { + top: 20px; + left: 20px; + /* overflowing .cb to force --pf-revert to be applied */ + width: 200px; + height: 200px; + } + @position-try --pf-revert { + left: revert; + top: revert-layer; + width: 30px; + height: 30px; + } +</style> +<div class=cb class=abs> + <div id=abs_revert class=abs></div> +</div> +<script> + test(() => { + assert_equals(abs_revert.offsetLeft, 0, "left reverted back to user origin"); + assert_equals(abs_revert.offsetTop, 20, "top reverted back to author"); + assert_equals(abs_revert.offsetWidth, 30, "width from --pf-revert"); + assert_equals(abs_revert.offsetHeight, 30, "height from --pf-revert"); + }, '@position-try revert / revert-layer reverts to user / author origin'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-try-initial-transition.html b/testing/web-platform/tests/css/css-anchor-position/position-try-initial-transition.html new file mode 100644 index 0000000000..0e81607ae6 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-try-initial-transition.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning Test: Initial @position-try does not trigger a transition</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-apply"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id=cb> + <div id=abs></div> +</div> +<style> + #cb { + position: relative; + width: 100px; + height: 100px; + background: lightpink; + } + #abs { + position: absolute; + background: darkcyan; + top: 10px; + left: 10px; + width: 150px; /* force fallback */ + height: 25px; + position-try-options: --pf; + transition-property: top, left; + transition-duration: 10s; + transition-timing-function: steps(2, start); + } + @position-try --pf { + width: 50px; + top: 50px; + left: 50px; + } +</style> +<script> + test(() => { + assert_equals(getComputedStyle(abs).top, '50px'); + assert_equals(getComputedStyle(abs).left, '50px'); + }, 'No transition for initial style with @position-try'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-try-options-limit.html b/testing/web-platform/tests/css/css-anchor-position/position-try-options-limit.html new file mode 100644 index 0000000000..32a4f592fa --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-try-options-limit.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning Test: position options list limit</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-apply"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #container { + position: relative; + width: 200px; + height: 200px; + } + .positioned { + width: 200px; + height: 200px; + position: absolute; + top: 0; + left: 10px; /* overflowing #container */ + } + + @position-try --bar { + left: 0; /* not overflowing #container */ + } + #t1 { + /* If --foo is not found, we should still try --bar even if we limit the + number of applied position options to five because the --foo's are not + added to the `position options list` per spec. */ + position-try-options: --foo, --foo, --foo, --foo, --foo, --foo, --foo, --bar; + } + + /* --f1 .. --f4 all overflowing #container */ + @position-try --f1 { left: 10px; } + @position-try --f2 { left: 10px; } + @position-try --f3 { left: 10px; } + @position-try --f4 { left: 10px; } + @position-try --f5 { left: 20px; width: 20px; /* not overflowing #container */ } + #t2 { + position-try-options: --f1, --f2, --f3, --f4, --f5; + } + +</style> +<div id="container"> + <div id="t1" class="positioned"></div> + <div id="t2" class="positioned"></div> +</div> +<script> + test(() => { + assert_equals(t1.offsetLeft, 0, "The --bar try option should be applied"); + }, "Try options which are not found are not part of the limit"); + + test(() => { + assert_equals(t2.offsetLeft, 20, "The --f5 try option should be applied"); + }, "Must support At least five try options"); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-try-order-basic.html b/testing/web-platform/tests/css/css-anchor-position/position-try-order-basic.html new file mode 100644 index 0000000000..d1c74e7b9f --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-try-order-basic.html @@ -0,0 +1,197 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: Basic position-try-order behavior</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#position-try-order-property"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #cb { + position: absolute; + width: 400px; + height: 400px; + border: 1px solid black; + } + #anchor { + position: absolute; + left: 150px; + top: 200px; + width: 150px; + height: 150px; + background-color: coral; + anchor-name: --a; + } + #target, #ref { + position: absolute; + left: 450px; /* force fallback */ + width: 40px; + height: 40px; + background-color: skyblue; + position-anchor: --a; + } + #ref { + background-color: seagreen; + } + +/* + +The IMCB for --right is the whole area to the right of the anchor, and similarly +for --left, etc. + + ┌──────────────┐ + │ xxxx│ + │ xxxx│ + │ ┌────┐xxxx│ + │ │ │xxxx│ + │ └────┘xxxx│ + │ xxxx│ + │ xxxx│ + └──────────────┘ + +**/ + + @position-try --right { + inset: unset; + left: anchor(right); + } + @position-try --left { + inset: unset; + right: anchor(left); + } + @position-try --top { + inset: unset; + bottom: anchor(top); + } + @position-try --bottom { + inset: unset; + top: anchor(bottom); + } + +/* + +The IMCB for --right-sweep is the area that would be "swept" by the anchor if it +moved right, and similarly for --left-sweep, etc. + + ┌──────────────┐ + │ │ + │ │ + │ ┌────┐xxxx│ + │ │ │xxxx│ + │ └────┘xxxx│ + │ │ + │ │ + └──────────────┘ + +*/ + + @position-try --right-sweep { + inset: unset; + top: anchor(top); + bottom: anchor(bottom); + left: anchor(right); + align-self: center; + } + + @position-try --left-sweep { + inset: unset; + top: anchor(top); + bottom: anchor(bottom); + right: anchor(left); + align-self: center; + } + + @position-try --bottom-sweep { + left: anchor(left); + right: anchor(right); + top: anchor(bottom); + justify-self: center; + } + + @position-try --top-sweep { + left: anchor(left); + right: anchor(right); + bottom: anchor(top); + justify-self: center; + } + +</style> +<style id=style> +</style> +<div id=cb> + <div id=anchor></div> + <div id=target></div> + <div id=ref></div> +</div> +<script> + +// Test that an element with the specified `position_try` gets the same +// position as a reference element with `position_try_expected`. +function test_order(position_try, position_try_expected) { + test((t) => { + style.textContent = ` + #target { + position-try: ${position_try}; + } + #ref { + position-try: ${position_try_expected}; + } + `; + assert_true(CSS.supports('position-try', 'normal --x')); + assert_equals(target.offsetLeft, ref.offsetLeft, 'offsetLeft'); + assert_equals(target.offsetTop, ref.offsetTop, 'offsetTop'); + }, `${position_try} | ${position_try_expected}`); +} + +// Note: --right, --left, --top, and --bottom all fit, but have different +// inset-modifed containing blocks. +test_order('--right', '--right'); +test_order('--left', '--left'); +test_order('--top', '--top'); +test_order('--bottom', '--bottom'); + +// position-try-order:normal just picks the first option. +test_order('--right, --left, --bottom, --top', '--right'); +test_order('normal --right, --left, --bottom, --top', '--right'); +test_order('normal --top, --left, --bottom, --right', '--top'); + +// --right and --left have the same IMCB block-size. +test_order('most-block-size --right, --left', '--right'); +test_order('most-height --right, --left', '--right'); +// --left has more inline-size than --right. +test_order('most-inline-size --right, --left', '--left'); +test_order('most-width --right, --left', '--left'); + +// --bottom and --top have the same IMCB inline-size. +test_order('most-inline-size --bottom, --top', '--bottom'); +test_order('most-width --bottom, --top', '--bottom'); +// --top has more block-size than --bottom. +test_order('most-block-size --bottom, --top', '--top'); +test_order('most-height --bottom, --top', '--top'); + +// --bottom/--top has more IMBC inline-size than --right/--left. +test_order('most-inline-size --right, --left, --bottom, --top', '--bottom'); +test_order('most-inline-size --right, --left, --top, --bottom', '--top'); + +// --right/--left has more IMBC block-size than --bottom/--top. +test_order('most-block-size --bottom, --top, --right, --left', '--right'); +test_order('most-block-size --bottom, --top, --left, --right', '--left'); + +// --left-sweep and --bottom-sweep has the same IMBC inline-size ... +test_order('most-inline-size --left-sweep, --bottom-sweep', '--left-sweep'); +test_order('most-inline-size --bottom-sweep, --left-sweep', '--bottom-sweep'); +// ... but not the same block-size. +test_order('most-block-size --left-sweep, --bottom-sweep', '--left-sweep'); +test_order('most-block-size --bottom-sweep, --left-sweep', '--left-sweep'); + +test_order('most-inline-size --right-sweep, --left-sweep, --bottom-sweep, --top-sweep', '--left-sweep'); +test_order('most-block-size --right-sweep, --left-sweep, --bottom-sweep, --top-sweep', '--top-sweep'); + +test_order(`most-inline-size + --right-sweep, --left-sweep, --bottom-sweep, --top-sweep, + --right, --left, --bottom, --top + `, '--left-sweep'); + +test_order(`most-block-size + --right-sweep, --left-sweep, --bottom-sweep, --top-sweep, + --right, --left, --bottom, --top + `, '--right'); + +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-try-position-anchor.html b/testing/web-platform/tests/css/css-anchor-position/position-try-position-anchor.html new file mode 100644 index 0000000000..7355c23f53 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-try-position-anchor.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning Test: @position-try can set position-anchor</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-1/#fallback"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<script src="support/test-common.js"></script> +<style> + #cb { + position: relative; + width: 400px; + height: 400px; + } + .anchor { + width: 100px; + height: 100px; + } + #anchor-a { + anchor-name: --a; + /* Makes #anchored overflow when aligned with right edge */ + margin-left: 100px; + } + #anchor-b { + anchor-name: --b; + } + #anchored { + position: absolute; + left: anchor(right); + width: 300px; + height: 100px; + position-anchor: --a; + position-try-options: --pf; + } + @position-try --pf { + position-anchor: --b; + } +</style> +<body onload="checkLayoutForAnchorPos('#anchored')"> +<div id="cb"> + <div id="anchor-a" class="anchor"></div> + <div id="anchor-b" class="anchor"></div> + <div id="anchored" data-offset-x="100"></div> +</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-try-transition-basic.html b/testing/web-platform/tests/css/css-anchor-position/position-try-transition-basic.html new file mode 100644 index 0000000000..3bb982f073 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-try-transition-basic.html @@ -0,0 +1,58 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: Transition when @position-try is applied</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-apply"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #cb { + display: inline-block; + position: relative; + width: 400px; + height: 250px; + border: 1px solid black; + } + #cb.shrink { + width: 300px; + } + #target { + position: absolute; + left: 300px; + width: 50px; + height: 50px; + background: skyblue; + position-try-options: --200; + } + #target.anim { + transition: left 1000s steps(2, start); + } + @position-try --200 { + left: 200px; + } +</style> +<div id=cb> + <div id=target></div> +</div> +<script> + function cleanup() { + target.className = ''; + cb.className = ''; + } + + test((t) => { + t.add_cleanup(cleanup); + assert_equals(target.offsetLeft, 300); + cb.classList.add('shrink'); + target.classList.add('anim'); + // Now we take the --200 option: + assert_equals(target.offsetLeft, 250); + }, 'Transition when @position-try is applied'); + + test((t) => { + t.add_cleanup(cleanup); + cb.classList.add('shrink'); + assert_equals(target.offsetLeft, 200); + cb.classList.remove('shrink'); + target.classList.add('anim'); + assert_equals(target.offsetLeft, 250); + }, 'Transition when @position-try is unapplied'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-try-transition-flip.html b/testing/web-platform/tests/css/css-anchor-position/position-try-transition-flip.html new file mode 100644 index 0000000000..6adacadfd4 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-try-transition-flip.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: Transition to a flipped state</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-apply"> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#typedef-position-try-options-try-tactic"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #cb { + display: inline-block; + position: relative; + width: 400px; + height: 250px; + border: 1px solid black; + } + #cb.shrink { + width: 325px; + } + #anchor { + position: absolute; + width: 50px; + height: 50px; + background: coral; + left: 250px; + top: 50px; + anchor-name: --a; + } + #target { + position-anchor: --a; + position: absolute; + left: anchor(right); + top: anchor(top); + width: 50px; + height: 50px; + background: skyblue; + position-try-options: flip-start; + } + #target.anim { + transition: left 1000s steps(2, start); + } +</style> +<div id=cb> + <div id=anchor></div> + <div id=target></div> +</div> +<script> + function cleanup() { + target.className = ''; + cb.className = ''; + } + + test((t) => { + t.add_cleanup(cleanup); + assert_equals(target.offsetLeft, 300); + cb.classList.add('shrink'); + target.classList.add('anim'); + assert_equals(target.offsetLeft, 275); + }, 'Transition to a flipped state'); + + test((t) => { + t.add_cleanup(cleanup); + cb.classList.add('shrink'); + assert_equals(target.offsetLeft, 250); + cb.classList.remove('shrink'); + target.classList.add('anim'); + assert_equals(target.offsetLeft, 275); + }, 'Transition to an unflipped state'); + +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-try-typed-om.html b/testing/web-platform/tests/css/css-anchor-position/position-try-typed-om.html new file mode 100644 index 0000000000..76ec9411b8 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-try-typed-om.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning Test: Effects from @position-try in CSS Typed OM</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#fallback-apply"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @position-try --pt { + left: anchor(right); + top: 50px; + width: anchor-size(width); + height: 40px; + } + #cb { + position: relative; + width: 100px; + height: 100px; + border: 1px solid black; + } + #anchor { + position: absolute; + left: 10px; + top: 10px; + width: 20px; + height: 20px; + background: coral; + anchor-name: --a; + } + #target { + position: absolute; + background: skyblue; + position-anchor: --a; + left: 9999px; /* force overflow */ + position-try-options: --pt; + } +</style> + +<div id=cb> + <div id=anchor></div> + <div id=target></div> +</div> +<script> + function assert_unit_value(actual, expected) { + assert_true(actual instanceof CSSUnitValue); + assert_true(expected instanceof CSSUnitValue); + assert_equals(actual.value, expected.value); + assert_equals(actual.unit, expected.unit); + } + + test(() => { + assert_unit_value(target.computedStyleMap().get('left'), CSS.px(30)); + assert_unit_value(target.computedStyleMap().get('top'), CSS.px(50)); + assert_unit_value(target.computedStyleMap().get('width'), CSS.px(20)); + assert_unit_value(target.computedStyleMap().get('height'), CSS.px(40)); + }, 'Effects of position-try-options are visible in the computed values'); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-add-no-overflow.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-add-no-overflow.tentative.html new file mode 100644 index 0000000000..9d87f82b9f --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-add-no-overflow.tentative.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<meta charset="utf-8"> +<title>CSS Anchor Positioning Test: position-visibility: no-overflow</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-no-overflow-ref.html"> +<style> + #scroll-container { + position: relative; + overflow: hidden scroll; + width: 400px; + height: 100px; + } + + .anchor { + width: 100px; + height: 100px; + background: orange; + display: inline-block; + } + + .target { + position: absolute; + inset-area: block-end; + width: 100px; + height: 100px; + background: red; + top: 0; + left: 0; + } +</style> + +<div id="scroll-container"> + <!-- #target1 should not be visible because it overflows the containing block. --> + <div class="anchor" style="anchor-name: --a1;">anchor1</div> + <div id="target" class="target" style="position-anchor: --a1;">target1</div> +</div> + +<script> +requestAnimationFrame(() => { + requestAnimationFrame(() => { + target.style.positionVisibility = 'no-overflow'; + document.documentElement.classList.remove('reftest-wait'); + }); +}); +</script> +</html>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-valid-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-valid-ref.html new file mode 100644 index 0000000000..96b4e86551 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-valid-ref.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + .anchor { + width: 100px; + height: 100px; + background: orange; + } + .target { + width: 100px; + height: 100px; + background: green; + } +</style> + +<div class="anchor">anchor1</div> +<div class="target">target1</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-valid.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-valid.tentative.html new file mode 100644 index 0000000000..bf67921639 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-valid.tentative.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Anchor Positioning Test: position-visibility: anchors-valid</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-anchors-valid-ref.html"> +<style> + .anchor { + width: 100px; + height: 100px; + background: orange; + display: inline-block; + } + + .target { + position: absolute; + position-visibility: anchors-valid; + inset-area: block-end; + width: 100px; + height: 100px; + background: red; + top: 0; + left: 0; + } +</style> + +<!-- #target1 should be visible. --> +<div class="anchor" style="anchor-name: --a1;">anchor1</div> +<div id="target1" class="target" style="position-anchor: --a1; background: green">target1</div> + +<!-- #target2 should not be visible because anchor name does not exist. --> +<div id="target2" class="target" style="top: anchor(--does-not-exist bottom);">target2</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in-ref.html new file mode 100644 index 0000000000..10f74d4fb0 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in-ref.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + #scroll-container { + overflow: hidden scroll; + width: 300px; + height: 100px; + } + + #anchor { + width: 100px; + height: 100px; + background: orange; + margin-bottom: 100px; + } + + #target { + width: 100px; + height: 100px; + background: green; + } +</style> + +<div id="scroll-container"> + <div id="anchor">anchor</div> +</div> +<div id="target">target</div> + +<script> + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 0; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in.tentative.html new file mode 100644 index 0000000000..cea439c55f --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in.tentative.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<meta charset="utf-8"> +<meta name="assert" content="Scrolling an anchor in to view should cause a position-visibility: anchors-visible element to appear." /> +<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-anchors-visible-after-scroll-in-ref.html"> +<script src="/common/reftest-wait.js"></script> +<script src="/common/rendering-utils.js"></script> +<style> + #scroll-container { + overflow: hidden scroll; + width: 300px; + height: 100px; + } + + #anchor { + anchor-name: --a1; + width: 100px; + height: 100px; + background: orange; + } + + #spacer { + height: 100px; + } + + #target { + position-anchor: --a1; + position-visibility: anchors-visible; + inset-area: block-end; + width: 100px; + height: 100px; + background: green; + position: absolute; + top: 0; + left: 0; + } +</style> + +<div id="scroll-container"> + <div id="anchor">anchor</div> + <div id="spacer"></div> + <div id="target">target</div> +</div> + +<script> + // #target should be initially visible because it is anchored to #anchor, + // which is visible. + waitForAtLeastOneFrame().then(() => { + // Scroll #anchor out of view. + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 100; + // #target should now be invisible. + + waitForAtLeastOneFrame().then(() => { + // Scroll #anchor back into view. + scroller.scrollTop = 0; + + // #target should now be visible again. + takeScreenshot(); + }); + }); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out-ref.html new file mode 100644 index 0000000000..bd4fe1f09f --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out-ref.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + #scroll-container { + overflow: hidden scroll; + width: 300px; + height: 100px; + } + + #spacer { + height: 200px; + } +</style> + +<div id="scroll-container"> + <div id="spacer"><div> +</div> + +<script> + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out.tentative.html new file mode 100644 index 0000000000..b2e3643b07 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out.tentative.html @@ -0,0 +1,59 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<meta charset="utf-8"> +<meta name="assert" content="Scrolling an anchor out of view should cause a position-visibility: anchors-visible element to disappear." /> +<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-anchors-visible-after-scroll-out-ref.html"> +<script src="/common/reftest-wait.js"></script> +<script src="/common/rendering-utils.js"></script> +<style> + #scroll-container { + overflow: hidden scroll; + width: 300px; + height: 100px; + } + + #anchor { + anchor-name: --a1; + width: 100px; + height: 100px; + background: orange; + } + + #spacer { + height: 100px; + } + + #target { + position-anchor: --a1; + position-visibility: anchors-visible; + inset-area: bottom; + width: 100px; + height: 100px; + background: red; + position: absolute; + top: 0; + left: 0; + } +</style> + +<div id="scroll-container"> + <div id="anchor">anchor</div> + <div id="spacer"></div> + <div id="target">target</div> +</div> + +<script> + // #target should be initially visible because it is anchored to #anchor, + // which is visible. + + waitForAtLeastOneFrame().then(() => { + // Scroll #anchor so that it is out of view. + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 100; + + // #target should now be invisible. + takeScreenshot(); + }); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor-ref.html new file mode 100644 index 0000000000..cc35e4cd1f --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor-ref.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + #scroll-container { + overflow: hidden scroll; + width: 300px; + height: 100px; + } + #anchor { + width: 100px; + height: 200px; + background: orange; + } + #target { + width: 100px; + height: 100px; + background: green; + } +</style> + +<div id="scroll-container"> + <div id="anchor"></div> +</div> +<div id="target">target</div> + +<script> + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor.tentative.html new file mode 100644 index 0000000000..f8b1cc6d10 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-change-anchor.tentative.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<meta charset="utf-8"> +<meta name="assert" content="Position-visibility should not be affected by the visibility of a previous anchor." /> +<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-anchors-visible-change-anchor-ref.html"> +<script src="/common/reftest-wait.js"></script> +<script src="/common/rendering-utils.js"></script> +<style> + #scroll-container { + overflow: hidden scroll; + width: 300px; + height: 100px; + } + + .anchor { + width: 100px; + height: 100px; + background: orange; + display: inline-block; + } + + #anchor1 { + height: 200px; + anchor-name: --a1; + } + + #anchor2 { + anchor-name: --a2; + } + + #target { + position-anchor: --a2; + position-visibility: anchors-visible; + inset-area: bottom; + width: 100px; + height: 100px; + background: green; + position: absolute; + top: 0; + left: 0; + } +</style> + +<div id="scroll-container"> + <div id="anchor1" class="anchor">anchor1</div> + <div id="anchor2" class="anchor">anchor2</div> + <div id="target">target</div> +</div> + +<script> + // #target should be initially visible because it is anchored to #anchor2, + // which is visible. + waitForAtLeastOneFrame().then(() => { + // Change #target to be anchored to #anchor1. + target.style.positionAnchor = '--a1'; + // #target should be still be visible because #anchor1 is also visible. + waitForAtLeastOneFrame().then(() => { + // Scroll #anchor2 out of view, with #anchor1 still in view. + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 100; + // #target should still be visible because it is anchored to #anchor1, + // which is still visible. + takeScreenshot(); + }); + }); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility-ref.html new file mode 100644 index 0000000000..c4af73bf65 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility-ref.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + #anchor { + width: 100px; + height: 100px; + background: orange; + } + #target { + width: 100px; + height: 100px; + background: green; + } +</style> +<div id="anchor">anchor</div> +<div id="target">target</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility.tentative.html new file mode 100644 index 0000000000..22a30658c8 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility.tentative.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<meta charset="utf-8"> +<meta name="assert" content="Position-visibility: anchors-visible should show an element after an anchor changes from visibility: hidden to visibility: visible." /> +<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-anchors-visible-change-css-visibility-ref.html"> +<script src="/common/reftest-wait.js"></script> +<script src="/common/rendering-utils.js"></script> +<style> + #container { + visibility: hidden; + } + + #anchor { + anchor-name: --a1; + width: 100px; + height: 100px; + background: orange; + } + + #target { + position-anchor: --a1; + position-visibility: anchors-visible; + inset-area: bottom; + width: 100px; + height: 100px; + background: green; + position: absolute; + top: 0; + left: 0; + } +</style> + +<div id="container"> + <div id="anchor">anchor</div> +</div> +<div id="target">target</div> + +<script> + // #target should be initially hidden because it is anchored to #anchor, + // which is hidden with "visibility: hidden". + + waitForAtLeastOneFrame().then(() => { + // Change #container to "visibility: visible". #target should become + // visible again, because #anchor is no longer hidden. + container.style.visibility = 'visible'; + takeScreenshot(); + }); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility-ref.html new file mode 100644 index 0000000000..3ac85888e4 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility-ref.html @@ -0,0 +1,3 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<!-- This test passes if both the anchor and anchored elements are hidden. --> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility.tentative.html new file mode 100644 index 0000000000..31be797798 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-css-visibility.tentative.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="assert" content="Position-visibility: anchors-visible should hide an element with an anchor that has visibility: hidden." /> +<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-anchors-visible-css-visibility-ref.html"> +<style> + #container { + visibility: hidden; + } + + #anchor { + anchor-name: --a1; + width: 100px; + height: 100px; + background: orange; + } + + #target { + position-anchor: --a1; + position-visibility: anchors-visible; + inset-area: bottom right; + width: 100px; + height: 100px; + background: red; + position: absolute; + top: 0; + left: 0; + } +</style> + +<div id="container"> + <div id="anchor">anchor</div> +</div> +<div id="target">target</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container-ref.html new file mode 100644 index 0000000000..3b6532e27b --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container-ref.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + #target { + width: 100px; + height: 100px; + background: green; + } +</style> +<div id="target">target</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container.tentative.html new file mode 100644 index 0000000000..7b84976fd3 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container.tentative.html @@ -0,0 +1,65 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="assert" content="position-visibility: anchors-visible should consider the visibility of the anchor relative the containing scroller, ignoring visibility in other scrollers." /> +<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-anchors-visible-non-intervening-container-ref.html"> +<style> + #non-intervening-scroll-container { + overflow: hidden; + width: 200px; + height: 200px; + position: relative; + } + + #position-container { + position: relative; + } + + #scroll-container { + overflow: hidden scroll; + width: 400px; + height: 100px; + } + + #anchor { + anchor-name: --a1; + width: 100px; + height: 100px; + background: orange; + } + + #spacer { + height: 100px; + } + + #target { + position-anchor: --a1; + position-visibility: anchors-visible; + inset-area: right; + width: 100px; + height: 100px; + background: green; + position: absolute; + top: 0; + left: 0; + } +</style> + +<div id="non-intervening-scroll-container"> + <div id="position-container"> + <div id="scroll-container"> + <!-- The anchor is not visible to the screen, but it is visible in the --> + <!-- containing block of anchor1 and target1, so the target should not --> + <!-- be hidden due to position-visibility: anchors-visible. --> + <div id="anchor">anchor</div> + <div id="spacer"></div> + <div id="target">target</div> + </div> + </div> +</div> + +<script> + const non_intervening_scroller = document.getElementById('non-intervening-scroll-container'); + non_intervening_scroller.scrollLeft = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed-ref.html new file mode 100644 index 0000000000..e24992fd5e --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed-ref.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<meta charset="utf-8"> + +<!-- This test passes if both the anchor and anchored elements are hidden. --> +<div style="height: 200vh;"></div> + +<script> + window.scrollTo(0, 100); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed.tentative.html new file mode 100644 index 0000000000..25665ae466 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-position-fixed.tentative.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="assert" content="position-visibility: anchors-visible should work with a fixed-position anchored element." /> +<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-anchors-visible-position-fixed-ref.html"> +<style> + #anchor { + anchor-name: --a1; + position: absolute; + top: 0; + left: 0; + width: 100px; + height: 100px; + background: orange; + } + + #target { + position-anchor: --a1; + position-visibility: anchors-visible; + inset-area: bottom right; + width: 100px; + height: 100px; + background: red; + position: fixed; + top: 0; + left: 0; + } + + #spacer { + height: 200vh; + } +</style> + +<!-- Test passes if #target is not visible, due to #anchor being off-screen. --> +<div id="anchor">anchor</div> +<div id="target">target</div> +<div id="spacer"></div> + +<script> + window.scrollTo(0, 100); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-ref.html new file mode 100644 index 0000000000..1779817380 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-ref.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + #scroll-container { + overflow: hidden scroll; + width: 300px; + height: 100px; + } + + #spacer { + height: 200px; + } +</style> + +<div id="scroll-container"> + <div id="spacer"></div> +</div> + +<script> + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-stacked-child.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-stacked-child.tentative.html new file mode 100644 index 0000000000..7c0d5dc6aa --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-stacked-child.tentative.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="assert" content="Position-visibility: anchors-visible should hide an element and stacked children with an out-of-view anchor." /> +<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-anchors-visible-ref.html"> +<style> + #scroll-container { + overflow: hidden scroll; + width: 300px; + height: 100px; + } + + #anchor { + anchor-name: --a1; + width: 100px; + height: 100px; + background: orange; + } + + #spacer { + height: 100px; + } + + #target { + position-anchor: --a1; + position-visibility: anchors-visible; + inset-area: bottom right; + width: 100px; + height: 100px; + background: red; + position: absolute; + top: 0; + left: 0; + } + #stacking-child { + /* stacking context */ + z-index: 1; + width: 100px; + height: 100px; + background: maroon; + position: absolute; + top: 25px; + left: 25px; + } +</style> + +<div id="scroll-container"> + <div id="anchor">anchor</div> + <div id="spacer"></div> + <div id="target">target + <div id="stacking-child"></div> + </div> +</div> + +<script> + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 100; + // #target should not be visible because #anchor is scrolled out of view. +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-with-position.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-with-position.tentative.html new file mode 100644 index 0000000000..82eed0beb9 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible-with-position.tentative.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="assert" content="Position-visibility: anchors-visible should hide an element with an out-of-view anchor and a relpos scroller." /> +<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-anchors-visible-ref.html"> +<style> + #scroll-container { + overflow: hidden scroll; + width: 300px; + height: 100px; + /* Same as position-visibility-anchors-visible.html, but with relpos here */ + position: relative; + } + + #anchor { + anchor-name: --a1; + width: 100px; + height: 100px; + background: orange; + } + + #spacer { + height: 100px; + } + + #target { + position-anchor: --a1; + position-visibility: anchors-visible; + inset-area: bottom right; + width: 100px; + height: 100px; + background: red; + position: absolute; + top: 0; + left: 0; + } +</style> + +<div id="scroll-container"> + <div id="anchor">anchor</div> + <div id="spacer"></div> + <div id="target">target</div> +</div> + +<script> + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 100; + // #target should not be visible because #anchor is scrolled out of view. +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible.tentative.html new file mode 100644 index 0000000000..85b8d897db --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-anchors-visible.tentative.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="assert" content="Position-visibility: anchors-visible should hide an element with an out-of-view anchor." /> +<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-anchors-visible-ref.html"> +<style> + #scroll-container { + overflow: hidden scroll; + width: 300px; + height: 100px; + } + + #anchor { + anchor-name: --a1; + width: 100px; + height: 100px; + background: orange; + } + + #spacer { + height: 100px; + } + + #target { + position-anchor: --a1; + position-visibility: anchors-visible; + inset-area: bottom right; + width: 100px; + height: 100px; + background: red; + position: absolute; + top: 0; + left: 0; + } +</style> + +<div id="scroll-container"> + <div id="anchor">anchor</div> + <div id="spacer"></div> + <div id="target">target</div> +</div> + +<script> + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 100; + // #target should not be visible because #anchor is scrolled out of view. +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow-ref.html new file mode 100644 index 0000000000..d6e64d0d10 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow-ref.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + #scroll-container { + overflow: hidden scroll; + width: 400px; + height: 100px; + } + + .anchor { + width: 100px; + height: 100px; + background: orange; + display: inline-block; + } +</style> + +<div id="scroll-container"> + <div class="anchor">anchor1</div> + <div style="height: 100px"></div> +</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow-scroll-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow-scroll-ref.html new file mode 100644 index 0000000000..b62a6d1078 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow-scroll-ref.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<style> + #scroll-container { + overflow: hidden scroll; + width: 400px; + height: 150px; + } + + .anchor { + width: 100px; + height: 100px; + background: orange; + display: inline-block; + } + + .target { + width: 100px; + height: 100px; + } +</style> + +<div id="scroll-container"> + <div class="anchor">anchor1</div> + <div class="anchor" style="position: relative; top: 100px">anchor2</div> + <div id="target1" class="target" style="background: green">target1</div> + <div style="height: 200px"></div> +</div> +<script> +document.getElementById('scroll-container').scrollTop = 50; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow-scroll.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow-scroll.tentative.html new file mode 100644 index 0000000000..4751faeb0d --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow-scroll.tentative.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<title>CSS Anchor Positioning Test: position-visibility: no-overflow</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-no-overflow-scroll-ref.html"> +<style> + #scroll-container { + position: relative; + overflow: hidden scroll; + width: 400px; + height: 150px; + } + + .anchor { + width: 100px; + height: 100px; + background: orange; + display: inline-block; + } + + .target { + position: absolute; + position-visibility: no-overflow; + width: 100px; + height: 100px; + } +</style> + +<div id="scroll-container"> + <div class="anchor" style="anchor-name: --a1;">anchor1</div> + <div class="anchor" style="anchor-name: --a2; position: relative; top: 100px">anchor2</div> + <div id="target1" class="target" style="position-anchor: --a1; top: anchor(bottom); background: green">target1</div> + <div id="target2" class="target" style="position-anchor: --a2; left: anchor(left); bottom: anchor(top); background: red">target2</div> + <div style="height: 300px"></div> +</div> +<script> +requestAnimationFrame(() => { + requestAnimationFrame(() => { + document.getElementById('scroll-container').scrollTop = 50; + document.documentElement.classList.remove('reftest-wait'); + }); +}); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow-stacked-child.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow-stacked-child.tentative.html new file mode 100644 index 0000000000..f748fda33e --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow-stacked-child.tentative.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Anchor Positioning Test: position-visibility: no-overflow</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-no-overflow-ref.html"> +<style> + #scroll-container { + position: relative; + overflow: hidden scroll; + width: 400px; + height: 100px; + } + + .anchor { + width: 100px; + height: 100px; + background: orange; + display: inline-block; + } + + .target { + position: absolute; + position-visibility: no-overflow; + inset-area: block-end; + width: 100px; + height: 100px; + background: red; + top: 0; + left: 0; + } + + #child { + position: absolute; + /* stacking context */ + z-index: 1; + top: -100px; + left: 100px; + width: 100px; + height: 100px; + background: maroon; + } + + #grandchild { + position: absolute; + z-index: 1; + top: 0px; + left: 100px; + width: 50px; + height: 50px; + background: pink; + } +</style> + +<div id="scroll-container"> + <!-- #target1 should not be visible because it overflows the containing block. --> + <div class="anchor" style="anchor-name: --a1;">anchor1</div> + <div id="target1" class="target" style="position-anchor: --a1;">target1 + <div id="child">Child + <div id="grandchild">Grand child</div> + </div> + </div> +</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow.tentative.html new file mode 100644 index 0000000000..39fb55b120 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-no-overflow.tentative.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Anchor Positioning Test: position-visibility: no-overflow</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-no-overflow-ref.html"> +<style> + #scroll-container { + position: relative; + overflow: hidden scroll; + width: 400px; + height: 100px; + } + + .anchor { + width: 100px; + height: 100px; + background: orange; + display: inline-block; + } + + .target { + position: absolute; + position-visibility: no-overflow; + inset-area: block-end; + width: 100px; + height: 100px; + background: red; + top: 0; + left: 0; + } +</style> + +<div id="scroll-container"> + <!-- #target1 should not be visible because it overflows the containing block. --> + <div class="anchor" style="anchor-name: --a1;">anchor1</div> + <div id="target1" class="target" style="position-anchor: --a1;">target1</div> +</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-remove-anchors-visible-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-remove-anchors-visible-ref.html new file mode 100644 index 0000000000..135763bf6b --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-remove-anchors-visible-ref.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + #scroll-container { + overflow: hidden scroll; + width: 300px; + height: 100px; + } + + #target { + width: 100px; + height: 100px; + margin-top: 100px; + background: green; + } +</style> + +<div id="scroll-container"> + <div id="target">target</div> +</div> + +<script> + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 100; +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-remove-anchors-visible.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-remove-anchors-visible.tentative.html new file mode 100644 index 0000000000..c6649e5f93 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-remove-anchors-visible.tentative.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<meta charset="utf-8"> +<meta name="assert" content="Removing position-visibility: anchors-visible from an invisible anchored element should cause it to become visible." /> +<title>CSS Anchor Positioning Test: position-visibility: anchors-visible</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-remove-anchors-visible-ref.html"> +<script src="/common/reftest-wait.js"></script> +<script src="/common/rendering-utils.js"></script> +<style> + #scroll-container { + overflow: hidden scroll; + width: 300px; + height: 100px; + } + + #anchor { + anchor-name: --a1; + width: 100px; + height: 100px; + background: orange; + } + + #spacer { + height: 100px; + } + + #target { + position-anchor: --a1; + position-visibility: anchors-visible; + inset-area: bottom; + width: 100px; + height: 100px; + background: green; + position: absolute; + top: 0; + left: 0; + } +</style> + +<div id="scroll-container"> + <div id="anchor">anchor</div> + <div id="spacer"></div> + <div id="target">target</div> +</div> + +<script> + // #target should be initially visible because it is anchored to #anchor, + // which is visible. + + // Scroll #anchor so that it is no longer visible. + const scroller = document.getElementById('scroll-container'); + scroller.scrollTop = 100; + + waitForAtLeastOneFrame().then(() => { + // Remove position-visibility: anchors-visible. #target should become + // visible again. + target.style.positionVisibility = 'initial'; + takeScreenshot(); + }); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-remove-no-overflow-ref.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-remove-no-overflow-ref.html new file mode 100644 index 0000000000..b41d2110e5 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-remove-no-overflow-ref.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + #scroll-container { + overflow: hidden scroll; + width: 400px; + height: 100px; + } + + .anchor { + width: 100px; + height: 100px; + background: orange; + display: inline-block; + } + + .target { + width: 100px; + height: 100px; + background: green; + } +</style> + +<div id="scroll-container"> + <div class="anchor">anchor1</div> + <div class="target">target1</div> +</div> diff --git a/testing/web-platform/tests/css/css-anchor-position/position-visibility-remove-no-overflow.tentative.html b/testing/web-platform/tests/css/css-anchor-position/position-visibility-remove-no-overflow.tentative.html new file mode 100644 index 0000000000..a043917da6 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/position-visibility-remove-no-overflow.tentative.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<meta charset="utf-8"> +<title>CSS Anchor Positioning Test: position-visibility: no-overflow</title> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7758"> +<link rel="match" href="position-visibility-no-overflow-ref.html"> +<style> + #scroll-container { + position: relative; + overflow: hidden scroll; + width: 400px; + height: 100px; + } + + .anchor { + width: 100px; + height: 100px; + background: orange; + display: inline-block; + } + + .target { + position: absolute; + position-visibility: no-overflow; + inset-area: block-end; + width: 100px; + height: 100px; + background: green; + top: 0; + left: 0; + } +</style> + +<div id="scroll-container"> + <!-- #target1 should not be visible because it overflows the containing block. --> + <div class="anchor" style="anchor-name: --a1;">anchor1</div> + <div id="target" class="target" style="position-anchor: --a1;">target1</div> +</div> + +<script> +requestAnimationFrame(() => { + requestAnimationFrame(() => { + target.style.positionVisibility = 'initial'; + document.documentElement.classList.remove('reftest-wait'); + }); +}); +</script> +</html>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-anchor-position/property-interpolations.html b/testing/web-platform/tests/css/css-anchor-position/property-interpolations.html index 878f46375b..954e5642dd 100644 --- a/testing/web-platform/tests/css/css-anchor-position/property-interpolations.html +++ b/testing/web-platform/tests/css/css-anchor-position/property-interpolations.html @@ -19,12 +19,12 @@ }); test_no_interpolation({ - property: 'anchor-default', + property: 'position-anchor', from: 'implicit', to: '--foo', }); test_no_interpolation({ - property: 'anchor-default', + property: 'position-anchor', from: '--foo', to: '--bar', }); @@ -76,15 +76,4 @@ from: 'most-width', to: 'most-height', }); - - test_no_interpolation({ - property: 'position-fallback-bounds', - from: 'normal', - to: '--foo', - }); - test_no_interpolation({ - property: 'position-fallback-bounds', - from: '--foo', - to: '--bar', - }); </script> diff --git a/testing/web-platform/tests/css/css-anchor-position/try-tactic-alignment.html b/testing/web-platform/tests/css/css-anchor-position/try-tactic-alignment.html new file mode 100644 index 0000000000..39595276d5 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/try-tactic-alignment.html @@ -0,0 +1,195 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: try-tactic, justify/align-self</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#typedef-position-try-options-try-tactic"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #cb { + position: absolute; + width: 400px; + height: 400px; + border: 1px solid black; + } + #anchor { + position: absolute; + left: 150px; + top: 150px; + width: 100px; + height: 50px; + background-color: coral; + } + #target, #ref { + position: absolute; + left: 450px; /* force fallback */ + width: 40px; + height: 40px; + background-color: skyblue; + } + #ref { + background-color: seagreen; + } +</style> +<style id=style> +</style> +<div id=cb> + <div id=target></div> + <div id=ref></div> +</div> +<script> + +// Verify that a given try-tactic + untransformed declaration equals +// a reference element using `transformed` directly. +function test_justify_flip(try_tactic, untransformed, transformed, direction, writing_mode) { + if (direction==undefined) { + direction = 'ltr'; + } + if (writing_mode==undefined) { + writing_mode = 'horizontal-tb'; + } + test((t) => { + t.add_cleanup(() => { + document.documentElement.style = ''; + style.textContent = ''; + }); + document.documentElement.style.direction = direction; + document.documentElement.style.writingMode = writing_mode; + style.textContent = ` + @position-try --pf { + inset: 0; + ${untransformed} + } + #target { + position-try-options: --pf ${try_tactic}; + } + @position-try --ref { + inset: 0; + ${transformed} + } + #ref { + position-try-options: --ref; + } + `; + assert_equals(target.offsetLeft, ref.offsetLeft, 'offsetLeft'); + assert_equals(target.offsetTop, ref.offsetTop, 'offsetTop'); + }, `${try_tactic}, ${untransformed}, ${transformed}, ${direction}, ${writing_mode}`); +} + +test_justify_flip( + '', + 'justify-self:start;align-self:start', + 'justify-self:start;align-self:start'); + +test_justify_flip( + 'flip-inline', + 'justify-self:start;align-self:start', + 'justify-self:end;align-self:start'); + +test_justify_flip( + 'flip-block', + 'justify-self:start;align-self:start', + 'justify-self:start;align-self:end'); + +test_justify_flip( + 'flip-block flip-inline', + 'justify-self:start;align-self:start', + 'justify-self:end;align-self:end'); + +test_justify_flip( + 'flip-start', + 'justify-self:start;align-self:end', + 'justify-self:end;align-self:start'); + +test_justify_flip( + 'flip-block flip-start', + 'justify-self:start;align-self:start', + 'justify-self:end;align-self:start'); + +test_justify_flip( + 'flip-inline flip-start', + 'justify-self:start;align-self:start', + 'justify-self:start;align-self:end'); + +test_justify_flip( + 'flip-block flip-inline flip-start', + 'justify-self:start;align-self:start', + 'justify-self:end;align-self:end'); + +// left/right are special cases, because they're supported by +// justify-self, but not align-self: + +test_justify_flip( + 'flip-inline', + 'justify-self:left;align-self:start', + 'justify-self:right;align-self:start'); + +test_justify_flip( + 'flip-start', + 'justify-self:left;align-self:end', + 'justify-self:end;align-self:self-start'); + +test_justify_flip( + 'flip-start', + 'justify-self:right;align-self:start', + 'justify-self:start;align-self:self-end'); + + +// That that all relevant computed values can be transformed: + +function test_computed_value(try_tactic, property, value, expected) { + test((t) => { + t.add_cleanup(() => { + style.textContent = ''; + }); + style.textContent = ` + @position-try --pf { + inset: 0; + ${property}:${value}; + } + #target { + position-try-options: --pf ${try_tactic}; + } + `; + assert_equals(getComputedStyle(target).getPropertyValue(property), expected); + }, `${try_tactic}, ${property}:${value}`); +} + +// No flip: +for (let property of ['justify-self', 'align-self']) { + test_computed_value('', property, 'start', 'start'); + test_computed_value('', property, 'end', 'end'); + test_computed_value('', property, 'self-start', 'self-start'); + test_computed_value('', property, 'self-end', 'self-end'); + test_computed_value('', property, 'flex-start', 'flex-start'); + test_computed_value('', property, 'flex-end', 'flex-end'); +} + +test_computed_value('flip-inline', 'justify-self', 'start', 'end'); +test_computed_value('flip-inline', 'justify-self', 'end', 'start'); +test_computed_value('flip-inline', 'justify-self', 'self-start', 'self-end'); +test_computed_value('flip-inline', 'justify-self', 'self-end', 'self-start'); +test_computed_value('flip-inline', 'justify-self', 'flex-start', 'flex-end'); +test_computed_value('flip-inline', 'justify-self', 'flex-end', 'flex-start'); +test_computed_value('flip-inline', 'justify-self', 'left', 'right'); +test_computed_value('flip-inline', 'justify-self', 'right', 'left'); + +test_computed_value('flip-block', 'align-self', 'start', 'end'); +test_computed_value('flip-block', 'align-self', 'end', 'start'); +test_computed_value('flip-block', 'align-self', 'self-start', 'self-end'); +test_computed_value('flip-block', 'align-self', 'self-end', 'self-start'); +test_computed_value('flip-block', 'align-self', 'flex-start', 'flex-end'); +test_computed_value('flip-block', 'align-self', 'flex-end', 'flex-start'); + +test_justify_flip( + 'flip-start', + 'justify-self:left;align-self:end', + 'justify-self:end;align-self:self-start', + 'ltr', + 'vertical-rl'); + +test_justify_flip( + 'flip-start', + 'justify-self:left;align-self:start', + 'justify-self:start;align-self:self-end', + 'rtl'); + +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/try-tactic-anchor.html b/testing/web-platform/tests/css/css-anchor-position/try-tactic-anchor.html new file mode 100644 index 0000000000..8dc45dc6e8 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/try-tactic-anchor.html @@ -0,0 +1,260 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: try-tactic, anchor()</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#typedef-position-try-options-try-tactic"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + :root { + --anchor-left:anchor(left); + } + #cb { + position: absolute; + width: 400px; + height: 400px; + border: 1px solid black; + } + #anchor { + position: absolute; + left: 150px; + top: 150px; + width: 60px; + height: 70px; + background-color: coral; + anchor-name: --a; + } + #target, #ref { + position: absolute; + left: 450px; /* force fallback */ + width: 40px; + height: 40px; + background-color: skyblue; + position-anchor: --a; + } + #ref { + background-color: seagreen; + } +</style> +<style id=style> +</style> +<div id=cb> + <div id=anchor></div> + <div id=target></div> + <div id=ref></div> +</div> +<script> + +// Verify that a given try-tactic + untransformed declaration equals +// a reference element using `transformed` directly. +function test_anchor_flip(try_tactic, untransformed, transformed) { + test((t) => { + style.textContent = ` + @position-try --pf { + inset: auto; + ${untransformed} + } + #target { + position-try-options: --pf ${try_tactic}; + } + @position-try --ref { + inset: auto; + ${transformed} + } + #ref { + position-try-options: --ref; + } + `; + assert_equals(target.offsetLeft, ref.offsetLeft, 'offsetLeft'); + assert_equals(target.offsetTop, ref.offsetTop, 'offsetTop'); + }, `${try_tactic}, ${untransformed}, ${transformed}`); +} + +// These are the possible transformations between +// anchor(left/right/top/bottom): +// +// ┌───┬────┬────┬────┬────┐ +// │ - │ L │ R │ T │ B │ +// │ L │ - │ LR │ LT │ LB │ +// │ R │ RL │ - │ RT │ RB │ +// │ T │ TL │ TR │ - │ TB │ +// │ B │ BL │ BR │ BT │ - │ +// └───┴────┴────┴────┴────┘ + +// No flip, no transformation. +test_anchor_flip('', 'right:anchor(left)', 'right:anchor(left)'); + +// flip-block does not affect left-right. +test_anchor_flip('flip-block', 'right:anchor(left)', 'right:anchor(left)'); +// flip-inline does not affect left-right. +test_anchor_flip('flip-inline', 'bottom:anchor(top)', 'bottom:anchor(top)'); + +// Note that the letters represent the arguments to the anchor() functions, +// not the properties. For example, LR: anchor(left) => anchor(right). + +// LR +test_anchor_flip('flip-inline', 'right:anchor(left)', 'left:anchor(right)'); +// RL +test_anchor_flip('flip-inline', 'left:anchor(right)', 'right:anchor(left)'); + +// LT +test_anchor_flip('flip-start', 'right:anchor(left)', 'bottom:anchor(top)'); +// TL +test_anchor_flip('flip-start', 'bottom:anchor(top)', 'right:anchor(left)'); + +// LB +test_anchor_flip('flip-inline flip-start', 'right:anchor(left)', 'top:anchor(bottom)'); +// BL +test_anchor_flip('flip-start flip-inline', 'top:anchor(bottom)', 'right:anchor(left)'); + +// RT +test_anchor_flip('flip-start flip-block', 'left:anchor(right)', 'bottom:anchor(top)'); +// TR +test_anchor_flip('flip-block flip-start', 'bottom:anchor(top)', 'left:anchor(right)'); + +// RB +test_anchor_flip('flip-start', 'left:anchor(right)', 'top:anchor(bottom)'); +// BR +test_anchor_flip('flip-start', 'top:anchor(bottom)', 'left:anchor(right)'); + +// TB +test_anchor_flip('flip-block', 'bottom:anchor(top)', 'top:anchor(bottom)'); +// BT +test_anchor_flip('flip-block', 'top:anchor(bottom)', 'bottom:anchor(top)'); + +// Logical versions. +// +// These tests duplicate the above, but replace the input logical anchor() +// functions. + +// LR +test_anchor_flip('flip-inline', 'right:anchor(start)', 'left:anchor(right)'); +// RL +test_anchor_flip('flip-inline', 'left:anchor(end)', 'right:anchor(left)'); + +// LT +test_anchor_flip('flip-start', 'right:anchor(start)', 'bottom:anchor(top)'); +// TL +test_anchor_flip('flip-start', 'bottom:anchor(start)', 'right:anchor(left)'); + +// LB +test_anchor_flip('flip-inline flip-start', 'right:anchor(start)', 'top:anchor(bottom)'); +// BL +test_anchor_flip('flip-start flip-inline', 'top:anchor(end)', 'right:anchor(left)'); + +// RT +test_anchor_flip('flip-start flip-block', 'left:anchor(end)', 'bottom:anchor(top)'); +// TR +test_anchor_flip('flip-block flip-start', 'bottom:anchor(start)', 'left:anchor(right)'); + +// RB +test_anchor_flip('flip-start', 'left:anchor(end)', 'top:anchor(bottom)'); +// BR +test_anchor_flip('flip-start', 'top:anchor(end)', 'left:anchor(right)'); + +// TB +test_anchor_flip('flip-block', 'bottom:anchor(start)', 'top:anchor(bottom)'); +// BT +test_anchor_flip('flip-block', 'top:anchor(end)', 'bottom:anchor(top)'); + +// The same again, except with self-start/self-end. + +// LR +test_anchor_flip('flip-inline', 'right:anchor(self-start)', 'left:anchor(right)'); +// RL +test_anchor_flip('flip-inline', 'left:anchor(self-end)', 'right:anchor(left)'); + +// LT +test_anchor_flip('flip-start', 'right:anchor(self-start)', 'bottom:anchor(top)'); +// TL +test_anchor_flip('flip-start', 'bottom:anchor(self-start)', 'right:anchor(left)'); + +// LB +test_anchor_flip('flip-inline flip-start', 'right:anchor(self-start)', 'top:anchor(bottom)'); +// BL +test_anchor_flip('flip-start flip-inline', 'top:anchor(self-end)', 'right:anchor(left)'); + +// RT +test_anchor_flip('flip-start flip-block', 'left:anchor(self-end)', 'bottom:anchor(top)'); +// TR +test_anchor_flip('flip-block flip-start', 'bottom:anchor(self-start)', 'left:anchor(right)'); + +// RB +test_anchor_flip('flip-start', 'left:anchor(self-end)', 'top:anchor(bottom)'); +// BR +test_anchor_flip('flip-start', 'top:anchor(self-end)', 'left:anchor(right)'); + +// TB +test_anchor_flip('flip-block', 'bottom:anchor(self-start)', 'top:anchor(bottom)'); +// BT +test_anchor_flip('flip-block', 'top:anchor(self-end)', 'bottom:anchor(top)'); + + +function test_anchor_size_flip(try_tactic, flip_expected) { + test((t) => { + style.textContent = ` + @position-try --pf { + inset: auto; + width: calc(anchor-size(width) + 20px); + height: anchor-size(height); + } + #target { + position-try-options: --pf ${try_tactic}; + } + `; + + let expected_width = anchor.offsetWidth + (flip_expected ? 0 : 20); + let expected_height = anchor.offsetHeight + (flip_expected ? 20 : 0); + + assert_equals(target.offsetWidth, expected_width, 'offsetWidth'); + assert_equals(target.offsetHeight, expected_height, 'offsetHeight'); + }, try_tactic); +} + +// No flip, no transformation. +test_anchor_size_flip('', /* expect_flip */ false); + +// Note: only the cross-axis flips cause width/height to change. +// LR, TB (and their reverse versions) are in-axis, other combinations are +// cross-axis. + +// In-axis: + +// LR, RL +test_anchor_size_flip('flip-inline', /* expect_flip */ false); +// TB, BT +test_anchor_size_flip('flip-block', /* expect_flip */ false); + +// Cross-axis: + +// LT, TL, RB, BR +test_anchor_size_flip('flip-start', /* expect_flip */ true); + +// LB, BL +test_anchor_size_flip('flip-inline flip-start', /* expect_flip */ true); + +// RT, TR +test_anchor_size_flip('flip-start flip-block', /* expect_flip */ true); + + +test((t) => { + style.textContent = ` + @position-try --pf { + inset: auto; + right: var(--anchor-left); + } + #target { + position-try-options: --pf; + } + `; + // Initially positioned to the left of the anchor. + assert_equals(target.offsetLeft, 110, 'offsetLeft'); + + // Now positioned to the right of the anchor. + style.textContent += ` + #target { + position-try-options: --pf flip-inline; + } + `; + assert_equals(target.offsetLeft, 210, 'offsetLeft'); +}, 'Can transform a value post-var-substitution'); + +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/try-tactic-inset-area.html b/testing/web-platform/tests/css/css-anchor-position/try-tactic-inset-area.html new file mode 100644 index 0000000000..32af18a149 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/try-tactic-inset-area.html @@ -0,0 +1,251 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: try-tactic, inset-area</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#typedef-position-try-options-try-tactic"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #cb { + position: absolute; + width: 400px; + height: 400px; + border: 1px solid black; + } + #anchor { + position: absolute; + left: 150px; + top: 150px; + width: 100px; + height: 50px; + background-color: coral; + } + #target { + position: absolute; + left: 450px; /* force fallback */ + width: 40px; + height: 40px; + background-color: skyblue; + } +</style> +<style id=style> +</style> +<div id=cb> + <div id=target></div> +</div> +<script> + +function test_computed_value(try_tactic, property, value, expected, direction, writing_mode) { + if (direction==undefined) { + direction = 'ltr'; + } + if (writing_mode==undefined) { + writing_mode = 'horizontal-tb'; + } + test((t) => { + t.add_cleanup(() => { + style.textContent = ''; + document.documentElement.style = ''; + }); + document.documentElement.style.direction = direction; + document.documentElement.style.writingMode = writing_mode; + style.textContent = ` + @position-try --pf { + inset: initial; + ${property}:${value}; + } + #target { + position-try-options: --pf ${try_tactic}; + } + `; + assert_equals(getComputedStyle(target).getPropertyValue(property), expected); + }, `${try_tactic}, ${property}:${value}, ${direction} ${writing_mode}`); +} + + +// Physical: + +test_computed_value('flip-inline', 'inset-area', 'left top', 'right top'); +test_computed_value('flip-inline', 'inset-area', 'left bottom', 'right bottom'); +test_computed_value('flip-inline', 'inset-area', 'right bottom', 'left bottom'); +test_computed_value('flip-inline', 'inset-area', 'right top', 'left top'); + +test_computed_value('flip-block', 'inset-area', 'left top', 'left bottom'); +test_computed_value('flip-block', 'inset-area', 'left bottom', 'left top'); +test_computed_value('flip-block', 'inset-area', 'right bottom', 'right top'); +test_computed_value('flip-block', 'inset-area', 'right top', 'right bottom'); + +test_computed_value('flip-block flip-inline', 'inset-area', 'left top', 'right bottom'); +test_computed_value('flip-block flip-inline', 'inset-area', 'left bottom', 'right top'); +test_computed_value('flip-block flip-inline', 'inset-area', 'right bottom', 'left top'); +test_computed_value('flip-block flip-inline', 'inset-area', 'right top', 'left bottom'); + +test_computed_value('flip-start', 'inset-area', 'left top', 'left top'); +test_computed_value('flip-start', 'inset-area', 'left bottom', 'right top'); +test_computed_value('flip-start', 'inset-area', 'right bottom', 'right bottom'); +test_computed_value('flip-start', 'inset-area', 'right top', 'left bottom'); + +test_computed_value('flip-block flip-start', 'inset-area', 'left top', 'right top'); +test_computed_value('flip-block flip-start', 'inset-area', 'left bottom', 'left top'); +test_computed_value('flip-block flip-start', 'inset-area', 'right bottom', 'left bottom'); +test_computed_value('flip-block flip-start', 'inset-area', 'right top', 'right bottom'); + +test_computed_value('flip-inline flip-start', 'inset-area', 'left top', 'left bottom'); +test_computed_value('flip-inline flip-start', 'inset-area', 'left bottom', 'right bottom'); +test_computed_value('flip-inline flip-start', 'inset-area', 'right bottom', 'right top'); +test_computed_value('flip-inline flip-start', 'inset-area', 'right top', 'left top'); + +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'left top', 'right bottom'); +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'left bottom', 'left bottom'); +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'right bottom', 'left top'); +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'right top', 'right top'); + +// Variations: +test_computed_value('flip-block flip-inline', 'inset-area', 'span-left span-top', 'span-right span-bottom'); + +// XY: + +test_computed_value('flip-inline', 'inset-area', 'x-start y-start', 'x-end y-start'); +test_computed_value('flip-inline', 'inset-area', 'x-start y-end', 'x-end y-end'); +test_computed_value('flip-inline', 'inset-area', 'x-end y-end', 'x-start y-end'); +test_computed_value('flip-inline', 'inset-area', 'x-end y-start', 'x-start y-start'); + +test_computed_value('flip-block', 'inset-area', 'x-start y-start', 'x-start y-end'); +test_computed_value('flip-block', 'inset-area', 'x-start y-end', 'x-start y-start'); +test_computed_value('flip-block', 'inset-area', 'x-end y-end', 'x-end y-start'); +test_computed_value('flip-block', 'inset-area', 'x-end y-start', 'x-end y-end'); + +test_computed_value('flip-block flip-inline', 'inset-area', 'x-start y-start', 'x-end y-end'); +test_computed_value('flip-block flip-inline', 'inset-area', 'x-start y-end', 'x-end y-start'); +test_computed_value('flip-block flip-inline', 'inset-area', 'x-end y-end', 'x-start y-start'); +test_computed_value('flip-block flip-inline', 'inset-area', 'x-end y-start', 'x-start y-end'); + +test_computed_value('flip-start', 'inset-area', 'x-start y-start', 'x-start y-start'); +test_computed_value('flip-start', 'inset-area', 'x-start y-end', 'x-end y-start'); +test_computed_value('flip-start', 'inset-area', 'x-end y-end', 'x-end y-end'); +test_computed_value('flip-start', 'inset-area', 'x-end y-start', 'x-start y-end'); + +test_computed_value('flip-block flip-start', 'inset-area', 'x-start y-start', 'x-end y-start'); +test_computed_value('flip-block flip-start', 'inset-area', 'x-start y-end', 'x-start y-start'); +test_computed_value('flip-block flip-start', 'inset-area', 'x-end y-end', 'x-start y-end'); +test_computed_value('flip-block flip-start', 'inset-area', 'x-end y-start', 'x-end y-end'); + +test_computed_value('flip-inline flip-start', 'inset-area', 'x-start y-start', 'x-start y-end'); +test_computed_value('flip-inline flip-start', 'inset-area', 'x-start y-end', 'x-end y-end'); +test_computed_value('flip-inline flip-start', 'inset-area', 'x-end y-end', 'x-end y-start'); +test_computed_value('flip-inline flip-start', 'inset-area', 'x-end y-start', 'x-start y-start'); + +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'x-start y-start', 'x-end y-end'); +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'x-start y-end', 'x-start y-end'); +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'x-end y-end', 'x-start y-start'); +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'x-end y-start', 'x-end y-start'); + +// Variations: +test_computed_value('flip-block flip-inline', 'inset-area', 'span-x-start span-y-start', 'span-x-end span-y-end'); +test_computed_value('flip-block flip-inline', 'inset-area', 'x-self-start y-self-start', 'x-self-end y-self-end'); +test_computed_value('flip-block flip-inline', 'inset-area', 'span-x-self-start span-y-self-start', 'span-x-self-end span-y-self-end'); + +// Logical: + +test_computed_value('flip-inline', 'inset-area', 'block-start inline-start', 'block-start inline-end'); +test_computed_value('flip-inline', 'inset-area', 'block-end inline-start', 'block-end inline-end'); +test_computed_value('flip-inline', 'inset-area', 'block-end inline-end', 'block-end inline-start'); +test_computed_value('flip-inline', 'inset-area', 'block-start inline-end', 'block-start inline-start'); + +test_computed_value('flip-block', 'inset-area', 'block-start inline-start', 'block-end inline-start'); +test_computed_value('flip-block', 'inset-area', 'block-end inline-start', 'block-start inline-start'); +test_computed_value('flip-block', 'inset-area', 'block-end inline-end', 'block-start inline-end'); +test_computed_value('flip-block', 'inset-area', 'block-start inline-end', 'block-end inline-end'); + +test_computed_value('flip-block flip-inline', 'inset-area', 'block-start inline-start', 'block-end inline-end'); +test_computed_value('flip-block flip-inline', 'inset-area', 'block-end inline-start', 'block-start inline-end'); +test_computed_value('flip-block flip-inline', 'inset-area', 'block-end inline-end', 'block-start inline-start'); +test_computed_value('flip-block flip-inline', 'inset-area', 'block-start inline-end', 'block-end inline-start'); + +test_computed_value('flip-start', 'inset-area', 'block-start inline-start', 'block-start inline-start'); +test_computed_value('flip-start', 'inset-area', 'block-end inline-start', 'block-start inline-end'); +test_computed_value('flip-start', 'inset-area', 'block-end inline-end', 'block-end inline-end'); +test_computed_value('flip-start', 'inset-area', 'block-start inline-end', 'block-end inline-start'); + +test_computed_value('flip-block flip-start', 'inset-area', 'block-start inline-start', 'block-start inline-end'); +test_computed_value('flip-block flip-start', 'inset-area', 'block-end inline-start', 'block-start inline-start'); +test_computed_value('flip-block flip-start', 'inset-area', 'block-end inline-end', 'block-end inline-start'); +test_computed_value('flip-block flip-start', 'inset-area', 'block-start inline-end', 'block-end inline-end'); + +test_computed_value('flip-inline flip-start', 'inset-area', 'block-start inline-start', 'block-end inline-start'); +test_computed_value('flip-inline flip-start', 'inset-area', 'block-end inline-start', 'block-end inline-end'); +test_computed_value('flip-inline flip-start', 'inset-area', 'block-end inline-end', 'block-start inline-end'); +test_computed_value('flip-inline flip-start', 'inset-area', 'block-start inline-end', 'block-start inline-start'); + +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'block-start inline-start', 'block-end inline-end'); +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'block-end inline-start', 'block-end inline-start'); +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'block-end inline-end', 'block-start inline-start'); +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'block-start inline-end', 'block-start inline-end'); + +// Variations: +test_computed_value('flip-block flip-inline', 'inset-area', 'span-block-start span-inline-start', 'span-block-end span-inline-end'); +test_computed_value('flip-block flip-inline', 'inset-area', 'self-block-start self-inline-start', 'self-block-end self-inline-end'); +test_computed_value('flip-block flip-inline', 'inset-area', 'span-self-block-start span-self-inline-start', 'span-self-block-end span-self-inline-end'); + +// start/end + +test_computed_value('', 'inset-area', 'start end', 'start end'); + +test_computed_value('flip-block', 'inset-area', 'start end', 'end'); + +test_computed_value('flip-inline', 'inset-area', 'start end', 'start'); + +test_computed_value('flip-block flip-inline', 'inset-area', 'start end', 'end start'); + +test_computed_value('flip-start', 'inset-area', 'start', 'start'); +test_computed_value('flip-start', 'inset-area', 'end', 'end'); +test_computed_value('flip-start', 'inset-area', 'start end', 'end start'); + +test_computed_value('flip-block flip-start', 'inset-area', 'start end', 'end'); + +test_computed_value('flip-inline flip-start', 'inset-area', 'start end', 'start'); + +test_computed_value('flip-block flip-inline flip-start', 'inset-area', 'start end', 'start end'); + +// Variations: + +test_computed_value('flip-block flip-inline', 'inset-area', 'span-start span-end', 'span-end span-start'); +test_computed_value('flip-block flip-inline', 'inset-area', 'self-start self-end', 'self-end self-start'); +test_computed_value('flip-block flip-inline', 'inset-area', 'span-self-start span-self-end', 'span-self-end span-self-start'); + +// center +test_computed_value('flip-block', 'inset-area', 'left center', 'left center'); +test_computed_value('flip-block', 'inset-area', 'center top', 'center bottom'); +test_computed_value('flip-block', 'inset-area', 'center', 'center'); +test_computed_value('flip-block', 'inset-area', 'start center', 'end center'); +test_computed_value('flip-block', 'inset-area', 'center start', 'center start'); +test_computed_value('flip-inline', 'inset-area', 'center start', 'center end'); +test_computed_value('flip-start', 'inset-area', 'center start', 'start center'); + +// span-all +test_computed_value('flip-block', 'inset-area', 'left span-all', 'left'); +test_computed_value('flip-block', 'inset-area', 'span-all top', 'bottom'); +test_computed_value('flip-block', 'inset-area', 'span-all', 'span-all'); +test_computed_value('flip-block', 'inset-area', 'start span-all', 'end span-all'); +test_computed_value('flip-block', 'inset-area', 'span-all start', 'span-all start'); +test_computed_value('flip-inline', 'inset-area', 'span-all start', 'span-all end'); +test_computed_value('flip-start', 'inset-area', 'span-all start', 'start span-all'); + +// Span mix: +test_computed_value('flip-block', 'inset-area', 'left span-top', 'left span-bottom'); +test_computed_value('flip-inline', 'inset-area', 'left span-top', 'right span-top'); +test_computed_value('flip-start', 'inset-area', 'span-block-start inline-end', 'block-end span-inline-start'); + +// Writing modes: +test_computed_value('flip-block', 'inset-area', 'left top', 'right top', 'ltr', 'vertical-rl'); + +test_computed_value('', 'inset-area', 'x-start y-start', 'x-start y-start', 'rtl'); +test_computed_value('flip-block', 'inset-area', 'x-start y-start', 'x-start y-end', 'rtl'); +test_computed_value('flip-inline', 'inset-area', 'x-start y-start', 'x-end y-start', 'rtl'); +test_computed_value('flip-block', 'inset-area', 'x-end y-start', 'x-start y-start', 'ltr', 'vertical-rl'); +test_computed_value('flip-inline', 'inset-area', 'x-end y-start', 'x-end y-end', 'ltr', 'vertical-rl'); + +test_computed_value('flip-inline', 'inset-area', 'start end', 'start', 'rtl'); +test_computed_value('flip-inline', 'inset-area', 'start end', 'start', 'ltr', 'vertical-rl'); +test_computed_value('flip-block', 'inset-area', 'start end', 'end', 'rtl'); +test_computed_value('flip-block', 'inset-area', 'start end', 'end', 'ltr', 'vertical-rl'); + +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/try-tactic-margin.html b/testing/web-platform/tests/css/css-anchor-position/try-tactic-margin.html new file mode 100644 index 0000000000..ab627315b8 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/try-tactic-margin.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: try-tactic (margin)</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#typedef-position-try-options-try-tactic"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @position-try --pf { + left:10px; + top:20px; + margin: 5px 15px 25px 35px; + } + #cb { + position: absolute; + width: 400px; + height: 400px; + border: 1px solid black; + } + #target { + position: absolute; + left: 99999px; /* force fallback */ + width: 30px; + height: 40px; + background-color: skyblue; + } +</style> +<div id=cb> + <div id=target></div> +</div> +<script> + function test_try_tactic(try_tactic, expected_offsets) { + target.style.positionTryOptions = `--pf ${try_tactic}`; + test(() => { + assert_equals(target.offsetLeft, expected_offsets.left, 'offsetLeft'); + assert_equals(target.offsetTop, expected_offsets.top, 'offsetTop'); + assert_equals(target.offsetWidth, expected_offsets.width, 'offsetWidth'); + assert_equals(target.offsetHeight, expected_offsets.height, 'offsetHeight'); + }, target.style.positionTryOptions); + } + + let w = 30; + let h = 40; + let cbw = 400; + let cbh = 400; + + test_try_tactic('', {left:10+35, top:20+5, width:w, height:h}); + test_try_tactic('flip-block', {left:10+35, top:cbh-h-20-5, width:w, height:h}); + test_try_tactic('flip-inline', {left:cbw-w-10-35, top:20+5, width:w, height:h}); + test_try_tactic('flip-block flip-inline', {left:cbw-w-10-35, top:cbh-h-20-5, width:w, height:h}); + test_try_tactic('flip-start', {left:20+5, top:10+35, width:h, height:w}); + test_try_tactic('flip-block flip-start', {left:cbh-h-20-5, top:10+35, width:h, height:w}); + test_try_tactic('flip-inline flip-start', {left:20+5, top:cbw-w-10-35, width:h, height:w}); + test_try_tactic('flip-block flip-inline flip-start', {left:cbh-h-20-5, top:cbw-w-10-35, width:h, height:w}); +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/try-tactic-percentage.html b/testing/web-platform/tests/css/css-anchor-position/try-tactic-percentage.html new file mode 100644 index 0000000000..20cf0f87e0 --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/try-tactic-percentage.html @@ -0,0 +1,113 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: try-tactic, percentage</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#typedef-position-try-options-try-tactic"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + #cb { + position: absolute; + width: 400px; + height: 400px; + border: 1px solid black; + } + #anchor { + position: absolute; + left: 150px; + top: 150px; + width: 100px; + height: 50px; + background-color: coral; + anchor-name: --a; + } + #target, #ref { + position: absolute; + left: 450px; /* force fallback */ + width: 40px; + height: 40px; + background-color: skyblue; + position-anchor: --a; + } + #ref { + background-color: seagreen; + } +</style> +<style id=style> +</style> +<div id=cb> + <div id=anchor></div> + <div id=target></div> + <div id=ref></div> +</div> +<script> + +// Verify that a given try-tactic + untransformed declaration equals +// a reference element using `transformed` directly. +function test_anchor_flip(try_tactic, untransformed, transformed) { + test((t) => { + style.textContent = ` + @position-try --pf { + inset: auto; + ${untransformed} + } + #target { + position-try-options: --pf ${try_tactic}; + } + @position-try --ref { + inset: auto; + ${transformed} + } + #ref { + position-try-options: --ref; + } + `; + assert_equals(target.offsetLeft, ref.offsetLeft, 'offsetLeft'); + assert_equals(target.offsetTop, ref.offsetTop, 'offsetTop'); + }, `${try_tactic}, ${untransformed}, ${transformed}`); +} + +test_anchor_flip('', 'left:anchor(0%)', 'left:anchor(left)'); +test_anchor_flip('', 'left:anchor(100%)', 'left:anchor(right)'); +test_anchor_flip('', 'left:anchor(50%)', 'left:anchor(center)'); +test_anchor_flip('', 'top:anchor(0%)', 'top:anchor(top)'); +test_anchor_flip('', 'top:anchor(100%)', 'top:anchor(bottom)'); +test_anchor_flip('', 'top:anchor(50%)', 'top:anchor(center)'); + +test_anchor_flip('flip-inline', 'left:anchor(10%)', 'right:anchor(90%)'); +test_anchor_flip('flip-inline', 'left:anchor(calc(10% + 20%))', 'right:anchor(70%)'); + +test_anchor_flip('flip-block', 'left:anchor(0%)', 'left:anchor(0%)'); +test_anchor_flip('flip-block', 'left:anchor(100%)', 'left:anchor(100%)'); +test_anchor_flip('flip-block', 'top:anchor(0%)', 'bottom:anchor(100%)'); +test_anchor_flip('flip-block', 'top:anchor(100%)', 'bottom:anchor(0%)'); + +test_anchor_flip('flip-inline', 'left:anchor(0%)', 'right:anchor(100%)'); +test_anchor_flip('flip-inline', 'left:anchor(100%)', 'right:anchor(0%)'); +test_anchor_flip('flip-inline', 'top:anchor(0%)', 'top:anchor(0%)'); +test_anchor_flip('flip-inline', 'top:anchor(100%)', 'top:anchor(100%)'); + +test_anchor_flip('flip-block flip-inline', 'left:anchor(0%)', 'right:anchor(100%)'); +test_anchor_flip('flip-block flip-inline', 'left:anchor(100%)', 'right:anchor(0%)'); +test_anchor_flip('flip-block flip-inline', 'top:anchor(0%)', 'bottom:anchor(100%)'); +test_anchor_flip('flip-block flip-inline', 'top:anchor(100%)', 'bottom:anchor(0%)'); + +test_anchor_flip('flip-start', 'left:anchor(0%)', 'top:anchor(0%)'); +test_anchor_flip('flip-start', 'left:anchor(100%)', 'top:anchor(100%)'); +test_anchor_flip('flip-start', 'bottom:anchor(0%)', 'right:anchor(0%)'); +test_anchor_flip('flip-start', 'bottom:anchor(100%)', 'right:anchor(100%)'); + +test_anchor_flip('flip-block flip-start', 'left:anchor(0%)', 'top:anchor(0%)'); +test_anchor_flip('flip-block flip-start', 'left:anchor(100%)', 'top:anchor(100%)'); +test_anchor_flip('flip-block flip-start', 'bottom:anchor(0%)', 'left:anchor(100%)'); +test_anchor_flip('flip-block flip-start', 'bottom:anchor(100%)', 'left:anchor(0%)'); + +test_anchor_flip('flip-inline flip-start', 'left:anchor(0%)', 'bottom:anchor(100%)'); +test_anchor_flip('flip-inline flip-start', 'left:anchor(100%)', 'bottom:anchor(0%)'); +test_anchor_flip('flip-inline flip-start', 'bottom:anchor(0%)', 'right:anchor(0%)'); +test_anchor_flip('flip-inline flip-start', 'bottom:anchor(100%)', 'right:anchor(100%)'); + +test_anchor_flip('flip-block flip-inline flip-start', 'left:anchor(0%)', 'bottom:anchor(100%)'); +test_anchor_flip('flip-block flip-inline flip-start', 'left:anchor(100%)', 'bottom:anchor(0%)'); +test_anchor_flip('flip-block flip-inline flip-start', 'bottom:anchor(0%)', 'left:anchor(100%)'); +test_anchor_flip('flip-block flip-inline flip-start', 'bottom:anchor(100%)', 'left:anchor(0%)'); + +</script> diff --git a/testing/web-platform/tests/css/css-anchor-position/try-tactic-wm.html b/testing/web-platform/tests/css/css-anchor-position/try-tactic-wm.html new file mode 100644 index 0000000000..bc1c82c35d --- /dev/null +++ b/testing/web-platform/tests/css/css-anchor-position/try-tactic-wm.html @@ -0,0 +1,56 @@ +<!DOCTYPE html> +<title>CSS Anchor Positioning: try-tactic under different writing modes</title> +<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#typedef-position-try-options-try-tactic"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + @position-try --pf { + left:10px; + top:20px; + } + #cb { + position: absolute; + width: 400px; + height: 400px; + border: 1px solid black; + } + #target { + position: absolute; + left: 99999px; /* force fallback */ + width: 30px; + height: 40px; + background-color: skyblue; + } +</style> +<div id=cb> + <div id=target></div> +</div> +<script> + + function test_try_tactic_wm(try_tactic, writing_mode, direction, expected_offsets) { + target.style.positionTryOptions = `--pf ${try_tactic}`; + target.style.writingMode = writing_mode; + target.style.direction = direction; + test(() => { + assert_equals(target.offsetLeft, expected_offsets.left, 'offsetLeft'); + assert_equals(target.offsetTop, expected_offsets.top, 'offsetTop'); + assert_equals(target.offsetWidth, expected_offsets.width, 'offsetWidth'); + assert_equals(target.offsetHeight, expected_offsets.height, 'offsetHeight'); + }, `${try_tactic} ${writing_mode} ${direction}`); + } + + test_try_tactic_wm('', 'horizontal-tb', 'ltr', {left:10, top:20, width:30, height:40}); + + // Effectively flips left:10px to right:10px: + test_try_tactic_wm('flip-inline', 'horizontal-tb', 'ltr', {left:360, top:20, width:30, height:40}); + + // Effectively flips top:20px to bottom:20px: + test_try_tactic_wm('flip-inline', 'vertical-lr', 'ltr', {left:10, top:340, width:30, height:40}); + + // Mirror across the [left,top]=>[bottom,right] diagonal: + test_try_tactic_wm('flip-start', 'horizontal-tb', 'ltr', {left:20, top:10, width:40, height:30}); + + // Mirror across the [right,top]=>[bottom,left] diagonal: + test_try_tactic_wm('flip-start', 'horizontal-tb', 'rtl', {left:340, top:360, width:40, height:30}); + +</script> diff --git a/testing/web-platform/tests/css/css-animations/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-animations/WEB_FEATURES.yml new file mode 100644 index 0000000000..2059bd4927 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/WEB_FEATURES.yml @@ -0,0 +1,5 @@ +features: +- name: animation-composition + files: + - animation-composition.html + - animation-composition-* diff --git a/testing/web-platform/tests/css/css-animations/crashtests/cancel-update.html b/testing/web-platform/tests/css/css-animations/crashtests/cancel-update.html new file mode 100644 index 0000000000..1cb094692e --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/crashtests/cancel-update.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<link rel="help" href="https://crbug.com/564354"> +<link rel="help" href="https://issues.chromium.org/issues/40447203"> +<title>Check that cancelling one running animation and updating another doesn't +crash.</title> +<style> + @keyframes anim { + from { background-color: blue; } + to { background-color: red; } + } + + @keyframes anim2 { + from { opacity: 0; } + to { opacity: 1; } + } +</style> +<div id="target"></div> +<script> + window.onload = () => { + target.style.animation = "anim 1s, anim2 1s"; + target.offsetTop; + target.style.animation = "anim2 2s"; + target.offsetTop; + }; +</script> diff --git a/testing/web-platform/tests/css/css-animations/crashtests/pseudo-element-animation-with-marker.html b/testing/web-platform/tests/css/css-animations/crashtests/pseudo-element-animation-with-marker.html new file mode 100644 index 0000000000..ba7fc2371a --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/crashtests/pseudo-element-animation-with-marker.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<link rel="help" href="https://crbug.com/360457"> +<link rel="help" href="https://issues.chromium.org/issues/41099655"> +<title>Test if it doesn't crash when pseudo element has animation with marker</title> +<style> +@keyframes test { 0% { marker: url("crash"); } } +body:before { animation-name: test; } +</style> +<doby> +</doby> +<script> + window.onload = () => { + document.getAnimations(); + } +</script> diff --git a/testing/web-platform/tests/css/css-animations/display-none-dont-cancel-pseudo.tentative.html b/testing/web-platform/tests/css/css-animations/display-none-dont-cancel-pseudo.tentative.html new file mode 100644 index 0000000000..bed4ec2b80 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/display-none-dont-cancel-pseudo.tentative.html @@ -0,0 +1,52 @@ +<!DOCTYPE html> +<link rel=author href="mailto:graouts@apple.com"> +<link rel=help href="https://drafts.csswg.org/css-display-4/#display-animation"> +<link rel=help href="https://github.com/w3c/csswg-drafts/issues/10111"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/web-animations/testcommon.js"></script> +<style> + +@keyframes display-animation { + from { margin-left: 100px; display: block } + to { margin-left: 200px; display: none } +} + +.target::after { + content: ""; + margin-left: 50px; +} + +.target.animated::after { + animation: display-animation 1ms forwards; +} + +</style> +<body> +<script> + +promise_test(async t => { + const target = createDiv(t); + target.className = "target"; + + const cs = getComputedStyle(target, "::after"); + const animations = () => target.getAnimations({ subtree: true }); + + assert_equals(animations().length, 0, "There are no running animations initially"); + assert_equals(cs.marginLeft, "50px"); + assert_equals(cs.display, "inline"); + + target.classList.add("animated"); + const runningAnimations = animations(); + assert_equals(runningAnimations.length, 1, "Setting the 'animated' class started an animation"); + assert_equals(cs.marginLeft, "100px"); + assert_equals(cs.display, "block"); + + await runningAnimations[0].finished; + assert_equals(animations().length, 1, "The animation remains after completion"); + assert_equals(cs.marginLeft, "200px"); + assert_equals(cs.display, "none"); +}, 'A CSS Animation on a pseudo-element animating to "display: none" with "fill: forwards" remains active after animation completion.'); + +</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-animations/parsing/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-animations/parsing/WEB_FEATURES.yml new file mode 100644 index 0000000000..7e3ccd1b3b --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/parsing/WEB_FEATURES.yml @@ -0,0 +1,4 @@ +features: +- name: animation-composition + files: + - animation-composition-* diff --git a/testing/web-platform/tests/css/css-animations/stability/animation-event-destroy-renderer.html b/testing/web-platform/tests/css/css-animations/stability/animation-event-destroy-renderer.html new file mode 100644 index 0000000000..0a1e1d5085 --- /dev/null +++ b/testing/web-platform/tests/css/css-animations/stability/animation-event-destroy-renderer.html @@ -0,0 +1,73 @@ +<!DOCTYPE html> +<html> +<head> + <title>Destroy and Hide Element in Animation Iteration Event</title> + <!-- Note: this is effectively a crashtest, but as crashtests do not + support variants, authoring as a promise test --> + <meta name="variant" content="?animationstart"> + <meta name="variant" content="?animationiteration"> + <link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=22635"> + <style> + .box { + height: 100px; + width: 100px; + margin: 10px; + background-color: blue; + animation-duration: 0.2s; + animation-iteration-count: 2; + } + + @keyframes move { + from { transform: translate(0px, 0px); } + to { transform: translate(100px, 0px); } + } + </style> + <div id="container"> + <div id="box1" class="box"></div> + <div id="box2" class="box"></div> + </div> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/common/gc.js"></script> + <script> + 'use strict'; + + function eventPromise(target, event, callback) { + return new Promise(resolve => { + const listener = () => { + callback(); + resolve(); + }; + target.addEventListener(event, listener, + { once: true }); + }); + } + + promise_test(async t => { + const eventType = location.search.substring(1); + var box1 = document.getElementById('box1'); + var box2 = document.getElementById('box2'); + + const promises = []; + promises.push(eventPromise(box1, eventType, () => { + box1.parentNode.removeChild(box1); + })); + box1.style.animationName = 'move'; + promises.push(eventPromise(box2, eventType, () => { + box2.style.display = 'none'; + })); + box2.style.animationName = 'move'; + await Promise.all(promises); + + // Garbage collection is best effort. + if (window.garbageCollect) { + await garbageCollect(); + } + + assert_equals(document.getAnimations().length, 0); + }, 'Triggering the cancel of an animation during event handling does not ' + + 'crash.'); + + </script> +</head> +</html> diff --git a/testing/web-platform/tests/css/css-backgrounds/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-backgrounds/WEB_FEATURES.yml new file mode 100644 index 0000000000..bee11ae42c --- /dev/null +++ b/testing/web-platform/tests/css/css-backgrounds/WEB_FEATURES.yml @@ -0,0 +1,9 @@ +features: +- name: background-clip + files: + - background-clip* + - css3-background-clip* +- name: border-image + files: + - border-image* + - css3-border-image* diff --git a/testing/web-platform/tests/css/css-backgrounds/animations/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-backgrounds/animations/WEB_FEATURES.yml new file mode 100644 index 0000000000..0d5911d01d --- /dev/null +++ b/testing/web-platform/tests/css/css-backgrounds/animations/WEB_FEATURES.yml @@ -0,0 +1,4 @@ +features: +- name: border-image + files: + - border-image-* diff --git a/testing/web-platform/tests/css/css-backgrounds/animations/box-shadow-interpolation.html b/testing/web-platform/tests/css/css-backgrounds/animations/box-shadow-interpolation.html index f0b7ec083e..be8f9d3572 100644 --- a/testing/web-platform/tests/css/css-backgrounds/animations/box-shadow-interpolation.html +++ b/testing/web-platform/tests/css/css-backgrounds/animations/box-shadow-interpolation.html @@ -211,7 +211,7 @@ test_interpolation({ test_interpolation({ property: 'box-shadow', - from: '10px 10px 10px 10px color(srgb 0, 0, 0)', + from: '10px 10px 10px 10px color(srgb 0 0 0)', to: '10px 10px 10px 10px rgb(255 255 255)', }, [ {at: -0.3, expect: '10px 10px 10px 10px oklab(0 0 0)'}, @@ -224,7 +224,7 @@ test_interpolation({ test_interpolation({ property: 'box-shadow', - from: '10px 10px 10px 10px color(srgb 0, 0, 0)', + from: '10px 10px 10px 10px color(srgb 0 0 0)', to: '10px 10px 10px 10px color(srgb 1 1 1)', }, [ {at: -0.3, expect: '10px 10px 10px 10px oklab(0 0 0)'}, diff --git a/testing/web-platform/tests/css/css-backgrounds/background-clip/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-backgrounds/background-clip/WEB_FEATURES.yml new file mode 100644 index 0000000000..5258340cee --- /dev/null +++ b/testing/web-platform/tests/css/css-backgrounds/background-clip/WEB_FEATURES.yml @@ -0,0 +1,10 @@ +features: +- name: background-clip + files: + - clip-border-box* + - clip-content-box* + - clip-padding-box* + - clip-rounded-corner.html +- name: background-clip-text + files: + - clip-text-* diff --git a/testing/web-platform/tests/css/css-backgrounds/background-clip/list.txt b/testing/web-platform/tests/css/css-backgrounds/background-clip/list.txt deleted file mode 100644 index 52c47ebba9..0000000000 --- a/testing/web-platform/tests/css/css-backgrounds/background-clip/list.txt +++ /dev/null @@ -1,12 +0,0 @@ -background-clip/border-box.html -background-clip/border-box_with_position.html -background-clip/border-box_with_radius.html -background-clip/border-box_with_size.html -background-clip/content-box.html -background-clip/content-box_with_position.html -background-clip/content-box_with_radius.html -background-clip/content-box_with_size.html -background-clip/padding-box.html -background-clip/padding-box_with_position.html -background-clip/padding-box_with_radius.html -background-clip/padding-box_with_size.html diff --git a/testing/web-platform/tests/css/css-backgrounds/background-origin/list.txt b/testing/web-platform/tests/css/css-backgrounds/background-origin/list.txt deleted file mode 100644 index a6fdedde54..0000000000 --- a/testing/web-platform/tests/css/css-backgrounds/background-origin/list.txt +++ /dev/null @@ -1,12 +0,0 @@ -background-origin/border-box.html -background-origin/border-box_with_position.html -background-origin/border-box_with_radius.html -background-origin/border-box_with_size.html -background-origin/content-box.html -background-origin/content-box_with_position.html -background-origin/content-box_with_radius.html -background-origin/content-box_with_size.html -background-origin/padding-box.html -background-origin/padding-box_with_position.html -background-origin/padding-box_with_radius.html -background-origin/padding-box_with_size.html diff --git a/testing/web-platform/tests/css/css-backgrounds/parsing/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-backgrounds/parsing/WEB_FEATURES.yml new file mode 100644 index 0000000000..6a2438077a --- /dev/null +++ b/testing/web-platform/tests/css/css-backgrounds/parsing/WEB_FEATURES.yml @@ -0,0 +1,7 @@ +features: +- name: background-clip + files: + - background-clip-* +- name: border-image + files: + - border-image-* diff --git a/testing/web-platform/tests/css/css-backgrounds/parsing/background-image-valid.html b/testing/web-platform/tests/css/css-backgrounds/parsing/background-image-valid.html index da08a63885..dcffc3dde5 100644 --- a/testing/web-platform/tests/css/css-backgrounds/parsing/background-image-valid.html +++ b/testing/web-platform/tests/css/css-backgrounds/parsing/background-image-valid.html @@ -21,19 +21,19 @@ test_valid_value("background-image", 'none, url("http://www.example.com/")', ['n test_valid_value( "background-image", "cross-fade(50% url(http://www.example.com), 50% url(http://www.example.com))", [ - "cross-fade(50% url(http://www.example.com), 50% url(http://www.example.com))", - 'cross-fade(50% url("http://www.example.com"), 50% url("http://www.example.com"))' + "cross-fade(url(http://www.example.com) 50%, url(http://www.example.com) 50%)", + 'cross-fade(url("http://www.example.com") 50%, url("http://www.example.com") 50%)' ]); test_valid_value( "background-image", - "cross-fade(33% red, 33% white, blue)"); + "cross-fade(red 33%, white 33%, blue)"); test_valid_value( "background-image", "cross-fade(blue, linear-gradient(90deg, rgb(2, 0, 36) 0%, rgb(0, 212, 255) 100%))"); -test_valid_value("background-image", "cross-fade( 1% red, green)", "cross-fade(1% red, green)"); -test_valid_value("background-image", "cross-fade(1% red , green)", "cross-fade(1% red, green)"); -test_valid_value("background-image", "cross-fade(1% red, green )", "cross-fade(1% red, green)"); -test_valid_value("background-image", "cross-fade(1% red, cross-fade(2% red, green))"); +test_valid_value("background-image", "cross-fade( 1% red, green)", "cross-fade(red 1%, green)"); +test_valid_value("background-image", "cross-fade(1% red , green)", "cross-fade(red 1%, green)"); +test_valid_value("background-image", "cross-fade(1% red, green )", "cross-fade(red 1%, green)"); +test_valid_value("background-image", "cross-fade(red 1%, cross-fade(red 2%, green))"); </script> </body> </html> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-end-radius-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-end-radius-computed.html new file mode 100644 index 0000000000..fd096fa17c --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-end-radius-computed.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'border-block-end-radius'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" content="This test checks that the computed value of 'border-block-end-radius' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("border-block-end-radius", "10px"); +test_computed_value("border-block-end-radius", "10px 10px", "10px"); +test_computed_value("border-block-end-radius", "5px 10px"); +test_computed_value("border-block-end-radius", "10px / 5px 10px"); +test_computed_value("border-block-end-radius", "5px 10px / 10px"); +test_computed_value("border-block-end-radius", "10px 10px / 5px 5px", "10px / 5px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-end-radius-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-end-radius-invalid.html new file mode 100644 index 0000000000..a9554bd924 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-end-radius-invalid.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-block-end-radius' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-block-end-radius' supports only the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("border-block-end-radius", "auto"); +test_invalid_value("border-block-end-radius", "none"); +test_invalid_value("border-block-end-radius", "1px 2px 3px"); +test_invalid_value("border-block-end-radius", "-1px"); +test_invalid_value("border-block-end-radius", "1px -2px"); +test_invalid_value("border-block-end-radius", "1em /"); +test_invalid_value("border-block-end-radius", "1px / 2px 3px 4px"); +test_invalid_value("border-block-end-radius", "1px / 2px / 3px"); +test_invalid_value("border-block-end-radius", "4"); +test_invalid_value("border-block-end-radius", "4 / 5"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-end-radius-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-end-radius-valid.html new file mode 100644 index 0000000000..2989b349a1 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-end-radius-valid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-block-end-radius' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-block-end-radius' supports the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("border-block-end-radius", "1px"); +test_valid_value("border-block-end-radius", "10%"); +test_valid_value("border-block-end-radius", "1px 5%"); +test_valid_value("border-block-end-radius", "5% 1px"); +test_valid_value("border-block-end-radius", "1px / 2px"); +test_valid_value("border-block-end-radius", "1px / 1px 2%"); +test_valid_value("border-block-end-radius", "1px 2% / 3%"); +test_valid_value("border-block-end-radius", "1px 2% / 3px 4px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-start-radius-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-start-radius-computed.html new file mode 100644 index 0000000000..292ae1b6dc --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-start-radius-computed.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'border-block-start-radius'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" content="This test checks that the computed value of 'border-block-start-radius' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("border-block-start-radius", "10px"); +test_computed_value("border-block-start-radius", "10px 10px", "10px"); +test_computed_value("border-block-start-radius", "5px 10px"); +test_computed_value("border-block-start-radius", "10px / 5px 10px"); +test_computed_value("border-block-start-radius", "5px 10px / 10px"); +test_computed_value("border-block-start-radius", "10px 10px / 5px 5px", "10px / 5px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-start-radius-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-start-radius-invalid.html new file mode 100644 index 0000000000..0a9a8a9bb7 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-start-radius-invalid.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-block-start-radius' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-block-start-radius' supports only the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("border-block-start-radius", "auto"); +test_invalid_value("border-block-start-radius", "none"); +test_invalid_value("border-block-start-radius", "1px 2px 3px"); +test_invalid_value("border-block-start-radius", "-1px"); +test_invalid_value("border-block-start-radius", "1px -2px"); +test_invalid_value("border-block-start-radius", "1em /"); +test_invalid_value("border-block-start-radius", "1px / 2px 3px 4px"); +test_invalid_value("border-block-start-radius", "1px / 2px / 3px"); +test_invalid_value("border-block-start-radius", "4"); +test_invalid_value("border-block-start-radius", "4 / 5"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-start-radius-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-start-radius-valid.html new file mode 100644 index 0000000000..8c9f0e0bd1 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-block-start-radius-valid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-block-start-radius' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-block-start-radius' supports the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("border-block-start-radius", "1px"); +test_valid_value("border-block-start-radius", "10%"); +test_valid_value("border-block-start-radius", "1px 5%"); +test_valid_value("border-block-start-radius", "5% 1px"); +test_valid_value("border-block-start-radius", "1px / 2px"); +test_valid_value("border-block-start-radius", "1px / 1px 2%"); +test_valid_value("border-block-start-radius", "1px 2% / 3%"); +test_valid_value("border-block-start-radius", "1px 2% / 3px 4px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-bottom-radius-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-bottom-radius-computed.html new file mode 100644 index 0000000000..a83286a910 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-bottom-radius-computed.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'border-bottom-radius'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" content="This test checks that the computed value of 'border-bottom-radius' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("border-bottom-radius", "10px"); +test_computed_value("border-bottom-radius", "10px 10px", "10px"); +test_computed_value("border-bottom-radius", "5px 10px"); +test_computed_value("border-bottom-radius", "10px / 5px 10px"); +test_computed_value("border-bottom-radius", "5px 10px / 10px"); +test_computed_value("border-bottom-radius", "10px 10px / 5px 5px", "10px / 5px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-bottom-radius-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-bottom-radius-invalid.html new file mode 100644 index 0000000000..bbf7555cdc --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-bottom-radius-invalid.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-bottom-radius' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-bottom-radius' supports only the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("border-bottom-radius", "auto"); +test_invalid_value("border-bottom-radius", "none"); +test_invalid_value("border-bottom-radius", "1px 2px 3px"); +test_invalid_value("border-bottom-radius", "-1px"); +test_invalid_value("border-bottom-radius", "1px -2px"); +test_invalid_value("border-bottom-radius", "1em /"); +test_invalid_value("border-bottom-radius", "1px / 2px 3px 4px"); +test_invalid_value("border-bottom-radius", "1px / 2px / 3px"); +test_invalid_value("border-bottom-radius", "4"); +test_invalid_value("border-bottom-radius", "4 / 5"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-bottom-radius-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-bottom-radius-valid.html new file mode 100644 index 0000000000..fe3502beff --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-bottom-radius-valid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-bottom-radius' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-bottom-radius' supports the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("border-bottom-radius", "1px"); +test_valid_value("border-bottom-radius", "10%"); +test_valid_value("border-bottom-radius", "1px 5%"); +test_valid_value("border-bottom-radius", "5% 1px"); +test_valid_value("border-bottom-radius", "1px / 2px"); +test_valid_value("border-bottom-radius", "1px / 1px 2%"); +test_valid_value("border-bottom-radius", "1px 2% / 3%"); +test_valid_value("border-bottom-radius", "1px 2% / 3px 4px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-clip-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-clip-computed.html new file mode 100644 index 0000000000..37f20490bd --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-clip-computed.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'border-clip'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#border-clip"> +<meta name="assert" content="This test checks that the computed value of 'border-clip' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("border-clip", "normal"); +test_computed_value("border-clip", "10px"); +test_computed_value("border-clip", "10%"); +test_computed_value("border-clip", "0"); +test_computed_value("border-clip", "1fr"); +test_computed_value("border-clip", "10px 10%"); +test_computed_value("border-clip", "10px 1em 10% 1fr 2fr", "10px 16px 10% 1fr 2fr"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-clip-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-clip-invalid.html new file mode 100644 index 0000000000..8f6dff3f08 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-clip-invalid.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-clip' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#border-clip"> +<meta name="assert" + content="This test checks that 'border-clip' supports only the keyword 'normal' or a list of <length-percentage> and <flex> values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("border-clip", "auto"); +test_invalid_value("border-clip", "none"); +test_invalid_value("border-clip", "padding-box"); +test_invalid_value("border-clip", "-10px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-clip-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-clip-valid.html new file mode 100644 index 0000000000..819d800b42 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-clip-valid.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-clip' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#border-clip"> +<meta name="assert" + content="This test checks that 'border-clip' supports the 'normal' keyword, <length-percentage> and <flex> values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("border-clip", "normal"); +test_valid_value("border-clip", "10px"); +test_valid_value("border-clip", "10%"); +test_valid_value("border-clip", "0"); +test_valid_value("border-clip", "1fr"); +test_valid_value("border-clip", "10px 10%"); +test_valid_value("border-clip", "10px 1em 10% 1fr 2fr"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-end-radius-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-end-radius-computed.html new file mode 100644 index 0000000000..58f78efa6d --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-end-radius-computed.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'border-inline-end-radius'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" content="This test checks that the computed value of 'border-inline-end-radius' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("border-inline-end-radius", "10px"); +test_computed_value("border-inline-end-radius", "10px 10px", "10px"); +test_computed_value("border-inline-end-radius", "5px 10px"); +test_computed_value("border-inline-end-radius", "10px / 5px 10px"); +test_computed_value("border-inline-end-radius", "5px 10px / 10px"); +test_computed_value("border-inline-end-radius", "10px 10px / 5px 5px", "10px / 5px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-end-radius-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-end-radius-invalid.html new file mode 100644 index 0000000000..3ee5ccde33 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-end-radius-invalid.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-radius-inline-end' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-radius-inline-end' supports only the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("border-radius-inline-end", "auto"); +test_invalid_value("border-radius-inline-end", "none"); +test_invalid_value("border-radius-inline-end", "1px 2px 3px"); +test_invalid_value("border-radius-inline-end", "-1px"); +test_invalid_value("border-radius-inline-end", "1px -2px"); +test_invalid_value("border-radius-inline-end", "1em /"); +test_invalid_value("border-radius-inline-end", "1px / 2px 3px 4px"); +test_invalid_value("border-radius-inline-end", "1px / 2px / 3px"); +test_invalid_value("border-radius-inline-end", "4"); +test_invalid_value("border-radius-inline-end", "4 / 5"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-end-radius-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-end-radius-valid.html new file mode 100644 index 0000000000..72c8ad8c28 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-end-radius-valid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-inline-end-radius' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-inline-end-radius' supports the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("border-inline-end-radius", "1px"); +test_valid_value("border-inline-end-radius", "10%"); +test_valid_value("border-inline-end-radius", "1px 5%"); +test_valid_value("border-inline-end-radius", "5% 1px"); +test_valid_value("border-inline-end-radius", "1px / 2px"); +test_valid_value("border-inline-end-radius", "1px / 1px 2%"); +test_valid_value("border-inline-end-radius", "1px 2% / 3%"); +test_valid_value("border-inline-end-radius", "1px 2% / 3px 4px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-start-radius-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-start-radius-computed.html new file mode 100644 index 0000000000..f08c0b0bfd --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-start-radius-computed.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'border-inline-start-radius'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" content="This test checks that the computed value of 'border-inline-start-radius' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("border-inline-start-radius", "10px"); +test_computed_value("border-inline-start-radius", "10px 10px", "10px"); +test_computed_value("border-inline-start-radius", "5px 10px"); +test_computed_value("border-inline-start-radius", "10px / 5px 10px"); +test_computed_value("border-inline-start-radius", "5px 10px / 10px"); +test_computed_value("border-inline-start-radius", "10px 10px / 5px 5px", "10px / 5px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-start-radius-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-start-radius-invalid.html new file mode 100644 index 0000000000..eaf404cf02 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-start-radius-invalid.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-inline-start-radius' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-inline-start-radius' supports only the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("border-inline-start-radius", "auto"); +test_invalid_value("border-inline-start-radius", "none"); +test_invalid_value("border-inline-start-radius", "1px 2px 3px"); +test_invalid_value("border-inline-start-radius", "-1px"); +test_invalid_value("border-inline-start-radius", "1px -2px"); +test_invalid_value("border-inline-start-radius", "1em /"); +test_invalid_value("border-inline-start-radius", "1px / 2px 3px 4px"); +test_invalid_value("border-inline-start-radius", "1px / 2px / 3px"); +test_invalid_value("border-inline-start-radius", "4"); +test_invalid_value("border-inline-start-radius", "4 / 5"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-start-radius-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-start-radius-valid.html new file mode 100644 index 0000000000..1100664247 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-inline-start-radius-valid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-inline-start-radius' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-inline-start-radius' supports the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("border-inline-start-radius", "1px"); +test_valid_value("border-inline-start-radius", "10%"); +test_valid_value("border-inline-start-radius", "1px 5%"); +test_valid_value("border-inline-start-radius", "5% 1px"); +test_valid_value("border-inline-start-radius", "1px / 2px"); +test_valid_value("border-inline-start-radius", "1px / 1px 2%"); +test_valid_value("border-inline-start-radius", "1px 2% / 3%"); +test_valid_value("border-inline-start-radius", "1px 2% / 3px 4px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-left-radius-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-left-radius-computed.html new file mode 100644 index 0000000000..2db2025575 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-left-radius-computed.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'border-left-radius'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" content="This test checks that the computed value of 'border-left-radius' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("border-left-radius", "10px"); +test_computed_value("border-left-radius", "10px 10px", "10px"); +test_computed_value("border-left-radius", "5px 10px"); +test_computed_value("border-left-radius", "10px / 5px 10px"); +test_computed_value("border-left-radius", "5px 10px / 10px"); +test_computed_value("border-left-radius", "10px 10px / 5px 5px", "10px / 5px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-left-radius-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-left-radius-invalid.html new file mode 100644 index 0000000000..43002f7900 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-left-radius-invalid.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-left-radius' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-left-radius' supports only the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("border-left-radius", "auto"); +test_invalid_value("border-left-radius", "none"); +test_invalid_value("border-left-radius", "1px 2px 3px"); +test_invalid_value("border-left-radius", "-1px"); +test_invalid_value("border-left-radius", "1px -2px"); +test_invalid_value("border-left-radius", "1em /"); +test_invalid_value("border-left-radius", "1px / 2px 3px 4px"); +test_invalid_value("border-left-radius", "1px / 2px / 3px"); +test_invalid_value("border-left-radius", "4"); +test_invalid_value("border-left-radius", "4 / 5"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-left-radius-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-left-radius-valid.html new file mode 100644 index 0000000000..401eff5729 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-left-radius-valid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-left-radius' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-left-radius' supports the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("border-left-radius", "1px"); +test_valid_value("border-left-radius", "10%"); +test_valid_value("border-left-radius", "1px 5%"); +test_valid_value("border-left-radius", "5% 1px"); +test_valid_value("border-left-radius", "1px / 2px"); +test_valid_value("border-left-radius", "1px / 1px 2%"); +test_valid_value("border-left-radius", "1px 2% / 3%"); +test_valid_value("border-left-radius", "1px 2% / 3px 4px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-right-radius-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-right-radius-computed.html new file mode 100644 index 0000000000..b058938afc --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-right-radius-computed.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'border-right-radius'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" content="This test checks that the computed value of 'border-right-radius' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("border-right-radius", "10px"); +test_computed_value("border-right-radius", "10px 10px", "10px"); +test_computed_value("border-right-radius", "5px 10px"); +test_computed_value("border-right-radius", "10px / 5px 10px"); +test_computed_value("border-right-radius", "5px 10px / 10px"); +test_computed_value("border-right-radius", "10px 10px / 5px 5px", "10px / 5px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-right-radius-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-right-radius-invalid.html new file mode 100644 index 0000000000..f89f098376 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-right-radius-invalid.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-right-radius' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-right-radius' supports only the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("border-right-radius", "auto"); +test_invalid_value("border-right-radius", "none"); +test_invalid_value("border-right-radius", "1px 2px 3px"); +test_invalid_value("border-right-radius", "-1px"); +test_invalid_value("border-right-radius", "1px -2px"); +test_invalid_value("border-right-radius", "1em /"); +test_invalid_value("border-right-radius", "1px / 2px 3px 4px"); +test_invalid_value("border-right-radius", "1px / 2px / 3px"); +test_invalid_value("border-right-radius", "4"); +test_invalid_value("border-right-radius", "4 / 5"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-right-radius-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-right-radius-valid.html new file mode 100644 index 0000000000..dc2f598717 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-right-radius-valid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-right-radius' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-right-radius' supports the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("border-right-radius", "1px"); +test_valid_value("border-right-radius", "10%"); +test_valid_value("border-right-radius", "1px 5%"); +test_valid_value("border-right-radius", "5% 1px"); +test_valid_value("border-right-radius", "1px / 2px"); +test_valid_value("border-right-radius", "1px / 1px 2%"); +test_valid_value("border-right-radius", "1px 2% / 3%"); +test_valid_value("border-right-radius", "1px 2% / 3px 4px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-top-radius-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-top-radius-computed.html new file mode 100644 index 0000000000..e0dcc8ae4e --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-top-radius-computed.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'border-top-radius'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" content="This test checks that the computed value of 'border-top-radius' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("border-top-radius", "10px"); +test_computed_value("border-top-radius", "10px 10px", "10px"); +test_computed_value("border-top-radius", "5px 10px"); +test_computed_value("border-top-radius", "10px / 5px 10px"); +test_computed_value("border-top-radius", "5px 10px / 10px"); +test_computed_value("border-top-radius", "10px 10px / 5px 5px", "10px / 5px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-top-radius-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-top-radius-invalid.html new file mode 100644 index 0000000000..67b0fc8087 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-top-radius-invalid.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-top-radius' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-top-radius' supports only the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("border-top-radius", "auto"); +test_invalid_value("border-top-radius", "none"); +test_invalid_value("border-top-radius", "1px 2px 3px"); +test_invalid_value("border-top-radius", "-1px"); +test_invalid_value("border-top-radius", "1px -2px"); +test_invalid_value("border-top-radius", "1em /"); +test_invalid_value("border-top-radius", "1px / 2px 3px 4px"); +test_invalid_value("border-top-radius", "1px / 2px / 3px"); +test_invalid_value("border-top-radius", "4"); +test_invalid_value("border-top-radius", "4 / 5"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/border-top-radius-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-top-radius-valid.html new file mode 100644 index 0000000000..8dbb9a4bb9 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/border-top-radius-valid.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'border-top-radius' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-sizing"> +<meta name="assert" + content="This test checks that 'border-top-radius' supports the syntax <length-percentage [0,∞]>{1,2} [ / <length-percentage [0,∞]>{1,2} ]?."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("border-top-radius", "1px"); +test_valid_value("border-top-radius", "10%"); +test_valid_value("border-top-radius", "1px 5%"); +test_valid_value("border-top-radius", "5% 1px"); +test_valid_value("border-top-radius", "1px / 2px"); +test_valid_value("border-top-radius", "1px / 1px 2%"); +test_valid_value("border-top-radius", "1px 2% / 3%"); +test_valid_value("border-top-radius", "1px 2% / 3px 4px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-blur-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-blur-computed.html new file mode 100644 index 0000000000..1c61f9a8e5 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-blur-computed.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'box-shadow-blur'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-blur"> +<meta name="assert" content="This test checks that the computed value of 'box-shadow-blur' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("box-shadow-blur", "0", "0px"); +test_computed_value("box-shadow-blur", "1px"); +test_computed_value("box-shadow-blur", "1em", "16px"); +test_computed_value("box-shadow-blur", "1px, 2px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-blur-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-blur-invalid.html new file mode 100644 index 0000000000..20b2a50446 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-blur-invalid.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'box-shadow-blur' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-blur"> +<meta name="assert" content="This test checks that 'box-shadow-blur' supports only properly defined length values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("box-shadow-blur", "auto"); +test_invalid_value("box-shadow-blur", "none"); +test_invalid_value("box-shadow-blur", "1"); +test_invalid_value("box-shadow-blur", "1px 2px"); +test_invalid_value("box-shadow-blur", "-1px"); +test_invalid_value("box-shadow-blur", "1%"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-blur-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-blur-valid.html new file mode 100644 index 0000000000..10d65ebbf0 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-blur-valid.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'box-shadow-blur' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-blur"> +<meta name="assert" content="This test checks that 'box-shadow-blur' supports length values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("box-shadow-blur", "0"); +test_valid_value("box-shadow-blur", "1px"); +test_valid_value("box-shadow-blur", "1em"); +test_valid_value("box-shadow-blur", "calc(1em + 2px)"); +test_valid_value("box-shadow-blur", "1px, 2px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-color-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-color-computed.html new file mode 100644 index 0000000000..53f0daffb7 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-color-computed.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'box-shadow-color'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-color"> +<meta name="assert" content="This test checks that the computed value of 'box-shadow-color' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("box-shadow-color", "currentcolor", "rgb(0, 0, 0)"); +test_computed_value("box-shadow-color", "transparent", "rgba(0, 0, 0, 0)"); +test_computed_value("box-shadow-color", "red", "rgb(255, 0, 0)"); +test_computed_value("box-shadow-color", "magenta", "rgb(255, 0, 255)"); +test_computed_value("box-shadow-color", "#234", "rgb(34, 51, 68)"); +test_computed_value("box-shadow-color", "#FEDCBA", "rgb(254, 220, 186)"); +test_computed_value("box-shadow-color", "rgb(2, 3, 4)"); +test_computed_value("box-shadow-color", "rgb(100%, 0%, 0%)", "rgb(255, 0, 0)"); +test_computed_value("box-shadow-color", "rgba(2, 3, 4, 0.5)"); +test_computed_value("box-shadow-color", "rgba(2, 3, 4, 50%)", "rgba(2, 3, 4, 0.5)"); +test_computed_value("box-shadow-color", "hsl(120, 100%, 50%)", "rgb(0, 255, 0)"); +test_computed_value("box-shadow-color", "hsla(120, 100%, 50%, 0.25)", "rgba(0, 255, 0, 0.25)"); +test_computed_value("box-shadow-color", "rgb(-2, 3, 4)", "rgb(0, 3, 4)"); +test_computed_value("box-shadow-color", "rgb(100, 200, 300)", "rgb(100, 200, 255)"); +test_computed_value("box-shadow-color", "rgb(20, 10, 0, -10)", "rgba(20, 10, 0, 0)"); +test_computed_value("box-shadow-color", "rgb(100%, 200%, 300%)", "rgb(255, 255, 255)"); +test_computed_value("box-shadow-color", "red, blue", "rgb(255, 0, 0), rgb(0, 0, 255)"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-color-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-color-invalid.html new file mode 100644 index 0000000000..7d92386de2 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-color-invalid.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'box-shadow-color' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-color"> +<meta name="assert" content="This test checks that 'box-shadow-color' supports only properly defined color values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("box-shadow-color", "auto"); +test_invalid_value("box-shadow-color", "123"); +test_invalid_value("box-shadow-color", "#12"); +test_invalid_value("box-shadow-color", "#123456789"); +test_invalid_value("box-shadow-color", "rgb"); +test_invalid_value("box-shadow-color", "rgb(1)"); +test_invalid_value("box-shadow-color", "rgb(1,2,3,4,5)"); +test_invalid_value("box-shadow-color", "hsla(1,2,3,4,5)"); +test_invalid_value("box-shadow-color", "rgb(10%, 20, 30%)"); +test_invalid_value("box-shadow-color", "rgba(-2, 300, 400%, -0.5)"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-color-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-color-valid.html new file mode 100644 index 0000000000..c278a7d1cc --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-color-valid.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'box-shadow-color' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-color"> +<meta name="assert" content="This test checks that 'box-shadow-color' supports color values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("box-shadow-color", "currentcolor"); +test_valid_value("box-shadow-color", "transparent"); +test_valid_value("box-shadow-color", "red"); +test_valid_value("box-shadow-color", "magenta"); +test_valid_value("box-shadow-color", "#234", "rgb(34, 51, 68)"); +test_valid_value("box-shadow-color", "#FEDCBA", "rgb(254, 220, 186)"); +test_valid_value("box-shadow-color", "rgb(2, 3, 4)"); +test_valid_value("box-shadow-color", "rgb(100%, 0%, 0%)", "rgb(255, 0, 0)"); +test_valid_value("box-shadow-color", "rgba(2, 3, 4, 0.5)"); +test_valid_value("box-shadow-color", "rgba(2, 3, 4, 50%)", "rgba(2, 3, 4, 0.5)"); +test_valid_value("box-shadow-color", "hsl(120, 100%, 50%)", "rgb(0, 255, 0)"); +test_valid_value("box-shadow-color", "hsla(120, 100%, 50%, 0.25)", "rgba(0, 255, 0, 0.25)"); +test_valid_value("box-shadow-color", "rgb(-2, 3, 4)", "rgb(0, 3, 4)"); +test_valid_value("box-shadow-color", "rgb(100, 200, 300)", "rgb(100, 200, 255)"); +test_valid_value("box-shadow-color", "rgb(20, 10, 0, -10)", "rgba(20, 10, 0, 0)"); +test_valid_value("box-shadow-color", "rgb(100%, 200%, 300%)", "rgb(255, 255, 255)"); +test_valid_value("box-shadow-color", "red, blue", "rgb(255, 0, 0), rgb(0, 0, 255)"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-offset-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-offset-computed.html new file mode 100644 index 0000000000..8ed7d7bc9e --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-offset-computed.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'box-shadow-offset'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-offset"> +<meta name="assert" content="This test checks that the computed value of 'box-shadow-offset' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("box-shadow-offset", "0 0", "0px"); +test_computed_value("box-shadow-offset", "10px 0"); +test_computed_value("box-shadow-offset", "0 10px"); +test_computed_value("box-shadow-offset", "10px 10px", "10px"); +test_computed_value("box-shadow-offset", "10px 20px, 30px 40px"); +test_computed_value("box-shadow-offset", "calc(1em + 1px) calc(-1em + 1px)", "17px -15px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-offset-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-offset-invalid.html new file mode 100644 index 0000000000..dd1c0f3374 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-offset-invalid.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'box-shadow-offset' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-offset"> +<meta name="assert" content="This test checks that 'box-shadow-offset' supports only properly defined offset values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("box-shadow-offset", "auto"); +test_invalid_value("box-shadow-offset", "1 2"); +test_invalid_value("box-shadow-offset", "1% 2%"); +test_invalid_value("box-shadow-offset", "1px 2px 3px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-offset-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-offset-valid.html new file mode 100644 index 0000000000..bfbe21f089 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-offset-valid.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'box-shadow-offset' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-offset"> +<meta name="assert" content="This test checks that 'box-shadow-offset' supports offset values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("box-shadow-offset", "0"); +test_valid_value("box-shadow-offset", "0 0"); +test_valid_value("box-shadow-offset", "1px 2px"); +test_valid_value("box-shadow-offset", "1em 2em"); +test_valid_value("box-shadow-offset", "-1px -2px"); +test_valid_value("box-shadow-offset", "calc(1em + 2px) calc(3rem + 4vw)"); +test_valid_value("box-shadow-offset", "1px 2px, 3px 4px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-position-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-position-computed.html new file mode 100644 index 0000000000..9818199b58 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-position-computed.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'box-shadow-position'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-position"> +<meta name="assert" content="This test checks that the computed value of 'box-shadow-position' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("box-shadow-position", "inset"); +test_computed_value("box-shadow-position", "outset"); +test_computed_value("box-shadow-position", "inset, outset"); +test_computed_value("box-shadow-position", "outset, inset"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-position-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-position-invalid.html new file mode 100644 index 0000000000..9738dd6107 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-position-invalid.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'box-shadow-position' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-position"> +<meta name="assert" content="This test checks that 'box-shadow-position' supports only valid keywords."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("box-shadow-position", "auto"); +test_invalid_value("box-shadow-position", "1 2"); +test_invalid_value("box-shadow-position", "1px"); +test_invalid_value("box-shadow-position", "1px 2px"); +test_invalid_value("box-shadow-position", "1%"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-position-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-position-valid.html new file mode 100644 index 0000000000..0d9b56fb01 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-position-valid.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'box-shadow-position' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-position"> +<meta name="assert" content="This test checks that 'box-shadow-position' supports positional keywords."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("box-shadow-position", "inset"); +test_valid_value("box-shadow-position", "outset"); +test_valid_value("box-shadow-position", "inset, outset"); +test_valid_value("box-shadow-position", "outset, inset"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-spread-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-spread-computed.html new file mode 100644 index 0000000000..e7f39d5565 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-spread-computed.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'box-shadow-spread'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-spread"> +<meta name="assert" content="This test checks that the computed value of 'box-shadow-spread' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("box-shadow-spread", "0", "0px"); +test_computed_value("box-shadow-spread", "1px"); +test_computed_value("box-shadow-spread", "1em", "16px"); +test_computed_value("box-shadow-spread", "-1px"); +test_computed_value("box-shadow-spread", "calc(1em + 1px)", "17px"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-spread-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-spread-invalid.html new file mode 100644 index 0000000000..205fd924b9 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-spread-invalid.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'box-shadow-spread' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-spread"> +<meta name="assert" content="This test checks that 'box-shadow-spread' supports only properly defined length values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("box-shadow-spread", "auto"); +test_invalid_value("box-shadow-spread", "none"); +test_invalid_value("box-shadow-spread", "1"); +test_invalid_value("box-shadow-spread", "1px 2px"); +test_invalid_value("box-shadow-spread", "1%"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-spread-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-spread-valid.html new file mode 100644 index 0000000000..a368d13694 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/box-shadow-spread-valid.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'box-shadow-spread' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#box-shadow-spread"> +<meta name="assert" content="This test checks that 'box-shadow-spread' supports length values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("box-shadow-spread", "0"); +test_valid_value("box-shadow-spread", "1px"); +test_valid_value("box-shadow-spread", "1em"); +test_valid_value("box-shadow-spread", "-1px"); +test_valid_value("box-shadow-spread", "calc(1em + 2px)"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-computed.html new file mode 100644 index 0000000000..c238ddfb96 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-computed.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'corner-shape'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-shaping"> +<meta name="assert" content="This test checks that the computed value of 'corner-shape' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("corner-shape", "round"); +test_computed_value("corner-shape", "angle"); +test_computed_value("corner-shape", "round angle"); +test_computed_value("corner-shape", "round angle round"); +test_computed_value("corner-shape", "round angle round angle"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-invalid.html new file mode 100644 index 0000000000..51f8e05700 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-invalid.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'corner-shape' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-shaping"> +<meta name="assert" content="This test checks that 'corner-shape' supports only up to four times the keywords 'round' and 'angle'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("corner-shape", "auto"); +test_invalid_value("corner-shape", "none"); +test_invalid_value("corner-shape", "scoop"); +test_invalid_value("corner-shape", "10px"); +test_invalid_value("corner-shape", "10%"); +test_invalid_value("corner-shape", "round round round round round"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-valid.html new file mode 100644 index 0000000000..62ee2630ac --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/corner-shape-valid.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'corner-shape' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-shaping"> +<meta name="assert" content="This test checks that 'corner-shape' supports the values 'round' and 'angle'."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("corner-shape", "round"); +test_valid_value("corner-shape", "angle"); +test_valid_value("corner-shape", "angle round"); +test_valid_value("corner-shape", "round angle"); +test_valid_value("corner-shape", "angle round angle"); +test_valid_value("corner-shape", "angle round angle round"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/corners-computed.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/corners-computed.html new file mode 100644 index 0000000000..6605f9eeb0 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/corners-computed.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Computed values of 'corners'</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corners-shorthand"> +<meta name="assert" content="This test checks that the computed value of 'corners' is correct."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> + +<div id="target"></div> + +<script> +test_computed_value("corners", "round"); +test_computed_value("corners", "angle"); +test_computed_value("corners", "round angle"); +test_computed_value("corners", "round angle round"); +test_computed_value("corners", "round angle round angle"); +test_computed_value("corners", "4px"); +test_computed_value("corners", "2%"); +test_computed_value("corners", "4px 2%"); +test_computed_value("corners", "4px 2% 1em", "4px 2% 16px"); +test_computed_value("corners", "4px 2% 1em 4%", "4px 2% 16px 4%"); +test_computed_value("corners", "4px / 2px"); +test_computed_value("corners", "2% / 2px"); +test_computed_value("corners", "4px 2% / 2px"); +test_computed_value("corners", "4px 2% 1em / 2px", "4px 2% 16px / 2px"); +test_computed_value("corners", "4px 2% 1em 4% / 2px", "4px 2% 16px 4% / 2px"); +test_computed_value("corners", "4px / 2px 4%"); +test_computed_value("corners", "4px / 2px 4% 1em", "4px / 2px 4% 16px"); +test_computed_value("corners", "4px / 2px 4% 1em 2%", "4px / 2px 4% 16px 2%"); +test_computed_value("corners", "4px 2% / 2px 4%"); +test_computed_value("corners", "4px 2% 1em / 2px 4% 1em", "4px 2% 16px / 2px 4% 16px"); +test_computed_value("corners", "4px 2% 1em 4% / 2px 4% 1em 2%", "4px 2% 16px 4% / 2px 4% 16px 2%"); +test_computed_value("corners", "4px round"); +test_computed_value("corners", "4px angle"); +test_computed_value("corners", "4px round angle"); +test_computed_value("corners", "4px round angle round"); +test_computed_value("corners", "4px round angle round angle"); +test_computed_value("corners", "4px 2% round"); +test_computed_value("corners", "4px 2% / 2px round"); +test_computed_value("corners", "4px / 2px 4% round"); +test_computed_value("corners", "4px / 2px 4% round angle"); +test_computed_value("corners", "round 4px"); +test_computed_value("corners", "round 4px 2%"); +test_computed_value("corners", "round 4px 2% / 2px"); +test_computed_value("corners", "round 4px / 2px 4%"); +test_computed_value("corners", "round angle 4px / 2px 4%"); +test_computed_value("corners", "round angle round angle 4px 2% 1em 4% / 2px 4% 1em 2%", "round angle round angle 4px 2% 16px 4% / 2px 4% 16px 2%"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/corners-invalid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/corners-invalid.html new file mode 100644 index 0000000000..1a762a7572 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/corners-invalid.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'corners' with invalid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-shaping"> +<meta name="assert" content="This test checks that 'corners' supports only a combination of 'border-radius' and 'corner-shape' values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_invalid_value("corners", "auto"); +test_invalid_value("corners", "none"); +test_invalid_value("corners", "scoop"); +test_invalid_value("corners", "round round round round round"); +test_invalid_value("corners", "-1px"); +test_invalid_value("corners", "4px 2% 1em 4% 2px"); +test_invalid_value("corners", "4px / 2px 4% 1em 2% 2px"); +test_invalid_value("corners", "4px / angle"); +test_invalid_value("corners", "angle / 4px"); +test_invalid_value("corners", "4px angle 2% round"); +test_invalid_value("corners", "round 4px angle 2%"); +test_invalid_value("corners", "4px / 2% angle 1em"); +test_invalid_value("corners", "round 4px / 2% angle"); +</script> diff --git a/testing/web-platform/tests/css/css-borders/tentative/parsing/corners-valid.html b/testing/web-platform/tests/css/css-borders/tentative/parsing/corners-valid.html new file mode 100644 index 0000000000..5b1ae7afb3 --- /dev/null +++ b/testing/web-platform/tests/css/css-borders/tentative/parsing/corners-valid.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Borders and Box Decorations 4 Test: Parsing 'corners' with valid values</title> +<link rel="author" title="Sebastian Zartner" href="mailto:sebastianzartner@gmail.com"> +<link rel="help" href="https://drafts.csswg.org/css-borders-4/#corner-shaping"> +<meta name="assert" content="This test checks that 'corners' supports a combination of 'border-radius' and 'corner-shape' values."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> + +<script> +test_valid_value("corners", "round"); +test_valid_value("corners", "angle"); +test_valid_value("corners", "round angle"); +test_valid_value("corners", "round angle round"); +test_valid_value("corners", "round angle round angle"); +test_valid_value("corners", "4px"); +test_valid_value("corners", "2%"); +test_valid_value("corners", "4px 2%"); +test_valid_value("corners", "4px 2% 1em"); +test_valid_value("corners", "4px 2% 1em 4%"); +test_valid_value("corners", "4px / 2px"); +test_valid_value("corners", "2% / 2px"); +test_valid_value("corners", "4px 2% / 2px"); +test_valid_value("corners", "4px 2% 1em / 2px"); +test_valid_value("corners", "4px 2% 1em 4% / 2px"); +test_valid_value("corners", "4px / 2px 4%"); +test_valid_value("corners", "4px / 2px 4% 1em"); +test_valid_value("corners", "4px / 2px 4% 1em 2%"); +test_valid_value("corners", "4px 2% / 2px 4%"); +test_valid_value("corners", "4px 2% 1em / 2px 4% 1em"); +test_valid_value("corners", "4px 2% 1em 4% / 2px 4% 1em 2%"); +test_valid_value("corners", "4px round"); +test_valid_value("corners", "4px angle"); +test_valid_value("corners", "4px round angle"); +test_valid_value("corners", "4px round angle round"); +test_valid_value("corners", "4px round angle round angle"); +test_valid_value("corners", "4px 2% round"); +test_valid_value("corners", "4px 2% / 2px round"); +test_valid_value("corners", "4px / 2px 4% round"); +test_valid_value("corners", "4px / 2px 4% round angle"); +test_valid_value("corners", "round 4px"); +test_valid_value("corners", "round 4px 2%"); +test_valid_value("corners", "round 4px 2% / 2px"); +test_valid_value("corners", "round 4px / 2px 4%"); +test_valid_value("corners", "round angle 4px / 2px 4%"); +test_valid_value("corners", "round angle round angle 4px 2% 1em 4% / 2px 4% 1em 2%"); +</script> diff --git a/testing/web-platform/tests/css/css-break/grid/monolithic-overflow-print-ref.html b/testing/web-platform/tests/css/css-break/grid/monolithic-overflow-print-ref.html index ba0372df06..58cc0828d2 100644 --- a/testing/web-platform/tests/css/css-break/grid/monolithic-overflow-print-ref.html +++ b/testing/web-platform/tests/css/css-break/grid/monolithic-overflow-print-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <link rel="author" title="Psychpsyo" href="mailto:psychpsyo@gmail.com" /> -<p style="height: 50vh">Test passes if there is two purple rectangles at the start of both page 2 and 3 when printing the page (Ctrl+P).</p> +<p style="height: 50vh">Test passes if there is two purple rectangles at the start of both page 2 and 3 when printing the page (Ctrl+P, with "print backgrounds" enabled).</p> <div style="display:grid; grid-template-columns:1fr 1fr; break-before:page;"> <div style="contain:size; height:75vh;"> <div style="height:75vh; width: 100px; background:purple;"></div> diff --git a/testing/web-platform/tests/css/css-break/grid/monolithic-overflow-print.html b/testing/web-platform/tests/css/css-break/grid/monolithic-overflow-print.html index 5a9e55973d..9b174caf96 100644 --- a/testing/web-platform/tests/css/css-break/grid/monolithic-overflow-print.html +++ b/testing/web-platform/tests/css/css-break/grid/monolithic-overflow-print.html @@ -2,7 +2,7 @@ <link rel="author" title="Psychpsyo" href="mailto:psychpsyo@gmail.com"> <link rel="help" href="https://issues.chromium.org/issues/327643792"> <link rel="match" href="./monolithic-overflow-print-ref.html"> -<p style="height: 50vh">Test passes if there is two purple rectangles at the start of both page 2 and 3 when printing the page (Ctrl+P).</p> +<p style="height: 50vh">Test passes if there is two purple rectangles at the start of both page 2 and 3 when printing the page (Ctrl+P, with "print backgrounds" enabled).</p> <div style="display:grid; grid-template-columns:1fr 1fr;"> <div style="contain:size; height:75vh;"> <div style="height:75vh; width: 100px; background:purple;"></div> diff --git a/testing/web-platform/tests/css/printing/transform-001-print-ref.html b/testing/web-platform/tests/css/css-break/transform-022-print-ref.html index 45c3abf181..45c3abf181 100644 --- a/testing/web-platform/tests/css/printing/transform-001-print-ref.html +++ b/testing/web-platform/tests/css/css-break/transform-022-print-ref.html diff --git a/testing/web-platform/tests/css/css-break/transform-022-print.html b/testing/web-platform/tests/css/css-break/transform-022-print.html new file mode 100644 index 0000000000..b8a097f225 --- /dev/null +++ b/testing/web-platform/tests/css/css-break/transform-022-print.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1442522"> +<link rel="match" href="transform-022-print-ref.html"> +<style> + body { margin: 0; } +</style> +<div style="height:0;"> + There should be 5 pages. +</div> +<div style="transform:rotate(180deg); height:500vh;"></div> diff --git a/testing/web-platform/tests/css/printing/transform-002-print-ref.html b/testing/web-platform/tests/css/css-break/transform-023-print-ref.html index 7204e1d657..7204e1d657 100644 --- a/testing/web-platform/tests/css/printing/transform-002-print-ref.html +++ b/testing/web-platform/tests/css/css-break/transform-023-print-ref.html diff --git a/testing/web-platform/tests/css/css-break/transform-023-print.html b/testing/web-platform/tests/css/css-break/transform-023-print.html new file mode 100644 index 0000000000..7650571abe --- /dev/null +++ b/testing/web-platform/tests/css/css-break/transform-023-print.html @@ -0,0 +1,11 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1371426"> +<link rel="match" href="transform-023-print-ref.html"> +<p>There should be a green square on the second page, and no red.</p> +<div style="break-before:page; width:100px; height:100px; background:red;"> + <div style="position:absolute; width:0; height:0; transform:translateX(49px);"> + <div style="position:absolute; width:51px; height:100px; background:green;"></div> + </div> + <div style="position:absolute; width:50px; height:100px; background:green;"></div> +</div> diff --git a/testing/web-platform/tests/css/printing/transform-003-print-ref.html b/testing/web-platform/tests/css/css-break/transform-024-print-ref.html index 766c415a1b..766c415a1b 100644 --- a/testing/web-platform/tests/css/printing/transform-003-print-ref.html +++ b/testing/web-platform/tests/css/css-break/transform-024-print-ref.html diff --git a/testing/web-platform/tests/css/css-break/transform-024-print.html b/testing/web-platform/tests/css/css-break/transform-024-print.html new file mode 100644 index 0000000000..510b4c2e44 --- /dev/null +++ b/testing/web-platform/tests/css/css-break/transform-024-print.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1371426"> +<link rel="match" href="transform-024-print-ref.html"> +<style> + body { margin: 0; } +</style> +<div style="position:relative; z-index:1; height:100vh;"> + There should be five pages. Each page should have a unique background - but no + red should be seen. +</div> +<!-- This tests that the transform on the container is applied to all the + absolutely positioned descendants, and also that the transform origin is + correct. --> +<div style="transform:rotate(180deg); height:250vh; background:red;"> + <div style="position:absolute; width:100%; transform:translateY(-100vh); top:-100vh; height:100vh; background:yellow;"></div> + <div style="position:absolute; width:100%; height:100vh; background:pink;"></div> + <div style="position:absolute; width:100%; top:100vh; height:100vh; background:cyan;"></div> + <div style="position:absolute; width:100%; transform:translateY(-50vh); top:200vh; height:100vh; background:papayawhip;"></div> + <div style="position:absolute; width:100%; transform:translateY(-100vh); top:300vh; height:100vh; background:olive;"></div> +</div> diff --git a/testing/web-platform/tests/css/css-cascade/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-cascade/WEB_FEATURES.yml new file mode 100644 index 0000000000..38aaee3021 --- /dev/null +++ b/testing/web-platform/tests/css/css-cascade/WEB_FEATURES.yml @@ -0,0 +1,4 @@ +features: +- name: cascade-layers + files: + - layer-* diff --git a/testing/web-platform/tests/css/css-cascade/at-scope-parsing.html b/testing/web-platform/tests/css/css-cascade/at-scope-parsing.html index 88e28fe4ff..8390738dd8 100644 --- a/testing/web-platform/tests/css/css-cascade/at-scope-parsing.html +++ b/testing/web-platform/tests/css/css-cascade/at-scope-parsing.html @@ -42,9 +42,9 @@ test_valid('@scope to (.a)'); test_valid('@scope (.a) to (&)'); test_valid('@scope (.a) to (& > &)'); - test_valid('@scope (.a) to (> .b)'); - test_valid('@scope (.a) to (+ .b)'); - test_valid('@scope (.a) to (~ .b)'); + test_valid('@scope (.a) to (> .b)', '@scope (.a) to (:scope > .b)'); + test_valid('@scope (.a) to (+ .b)', '@scope (.a) to (:scope + .b)'); + test_valid('@scope (.a) to (~ .b)', '@scope (.a) to (:scope ~ .b)'); test_valid('@scope ()', '@scope'); test_valid('@scope to ()', '@scope'); test_valid('@scope () to ()', '@scope'); @@ -75,4 +75,5 @@ test_invalid('@scope (.a'); test_invalid('@scope (.a to (.b)'); test_invalid('@scope ( to (.b)'); + test_invalid('@scope (.a) from (.c)'); </script> diff --git a/testing/web-platform/tests/css/css-cascade/at-scope-relative-syntax.html b/testing/web-platform/tests/css/css-cascade/at-scope-relative-syntax.html new file mode 100644 index 0000000000..274d9afbeb --- /dev/null +++ b/testing/web-platform/tests/css/css-cascade/at-scope-relative-syntax.html @@ -0,0 +1,68 @@ +<!doctype html> +<title>@scope and Nesting: Parsing inner style rules with relative selector syntax</title> +<link rel="help" href="https://drafts.csswg.org/css-cascade-6/#scoped-rules"> +<link rel="help" href="https://drafts.csswg.org/css-nesting/#nesting"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<main id=main></main> +<script> + function create_rule_string(prelude, inner) { + if (prelude.length === 0) { + return `${inner} {}`; + } + let outermost = prelude[0]; + let rest = create_rule_string(prelude.slice(1), inner); + return `${outermost} { ${rest} }`; + } + + function create_rule_by_string(style, prelude, inner) { + style.textContent = create_rule_string(prelude, inner); + } + + function create_rule_by_insertion(style, prelude, inner) { + let current = style.sheet; + for (const p of prelude) { + let idx = current.insertRule(`${p} {}`); + current = current.cssRules[idx]; + } + current.insertRule(`${inner} {}`); + } + + function innermost_selector(depth, rules) { + let r = rules; + let d = depth + 1; + while (d != 0) { + assert_equals(r.cssRules.length, 1); + r = r.cssRules[0]; + d--; + } + return r.selectorText; + } + + const create_method = { + "string": create_rule_by_string, + "insertRule": create_rule_by_insertion, + }; + + function test_inner(prelude, method, actual, expected) { + if (expected === undefined) { + expected = actual; + } + test(t => { + t.add_cleanup(() => main.replaceChildren()); + const style = document.createElement('style'); + main.append(style); + create_method[method](style, prelude, actual); + const innerSelector = innermost_selector(prelude.length, style.sheet); + assert_equals(innerSelector, expected); + }, `${actual} in ${prelude} created by ${method} valid`); + } + + for (const method of Object.keys(create_method)) { + test_inner(['@scope' , '.nest'], method, '> .foo', '& > .foo'); + test_inner(['.nest', '@scope'], method, '> .foo', ':scope > .foo'); + + test_inner(['@scope' , '.nest', '@media screen'], method, '> .foo', '& > .foo'); + test_inner(['.nest', '@scope', '@media screen'], method, '> .foo', ':scope > .foo'); + } +</script> diff --git a/testing/web-platform/tests/css/css-cascade/parsing/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-cascade/parsing/WEB_FEATURES.yml new file mode 100644 index 0000000000..56f7e89b85 --- /dev/null +++ b/testing/web-platform/tests/css/css-cascade/parsing/WEB_FEATURES.yml @@ -0,0 +1,4 @@ +features: +- name: cascade-layers + files: + - layer.html diff --git a/testing/web-platform/tests/css/css-cascade/scope-evaluation.html b/testing/web-platform/tests/css/css-cascade/scope-evaluation.html index f181048115..c6ea13c862 100644 --- a/testing/web-platform/tests/css/css-cascade/scope-evaluation.html +++ b/testing/web-platform/tests/css/css-cascade/scope-evaluation.html @@ -512,7 +512,6 @@ test_scope(document.currentScript, () => { }, ':scope in two different compounds'); </script> - <template> <style> @scope (.a:has(.c)) { @@ -545,3 +544,24 @@ test_scope(document.currentScript, () => { assert_not_green('.second .d'); }, 'Scope root with :has()'); </script> + +<template> + <style> + @scope (.a) to (.b, .c) { + * { background-color:green; } + } + </style> + <div class=a> + <span id="in"></span> + <div class=b> + <span id="out"</span> + <div class=c></div> + </div> + </div> +</template> +<script> +test_scope(document.currentScript, () => { + assert_green('#in'); + assert_not_green('#out'); +}, 'Any scope limit makes the element out of scope'); +</script> diff --git a/testing/web-platform/tests/css/css-cascade/scope-implicit.html b/testing/web-platform/tests/css/css-cascade/scope-implicit.html index 9add25fc9a..a1c6d1c626 100644 --- a/testing/web-platform/tests/css/css-cascade/scope-implicit.html +++ b/testing/web-platform/tests/css/css-cascade/scope-implicit.html @@ -196,4 +196,36 @@ test((t) => { assert_equals(getComputedStyle(outer).zIndex, 'auto'); assert_equals(getComputedStyle(outside_limit).zIndex, 'auto'); }, 'Implicit @scope with limit'); -</script>
\ No newline at end of file +</script> + +<template id=test_concurrent_scope_proximity> +<style> +@scope { + * { z-index: 1;} +} +</style> + <div> + <style> + @scope { + * { z-index:2; } + } + </style> + <div id=inner> + </div> + </div> + <div id=outer></div> +<style> +@scope { + * { z-index: 3;} +} +</style> +</template> +<script> +test((t) => { + t.add_cleanup(() => main.replaceChildren()); + main.append(test_concurrent_scope_proximity.content.cloneNode(true)); + + assert_equals(getComputedStyle(inner).zIndex, '2'); + assert_equals(getComputedStyle(outer).zIndex, '3'); +}, 'Proximity calculation of multiple implicit @scope'); +</script> diff --git a/testing/web-platform/tests/css/css-color/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-color/WEB_FEATURES.yml new file mode 100644 index 0000000000..8910deab28 --- /dev/null +++ b/testing/web-platform/tests/css/css-color/WEB_FEATURES.yml @@ -0,0 +1,22 @@ +features: +- name: color-function + files: + - a98rgb-* + - display-p3-* + - predefined-* + - prophoto-rgb-* + - rec2020-* + - srgb-* + - xyz-* +- name: color-mix + files: + - color-mix-* + - nested-color-mix-with-currentcolor.html +- name: lab + files: + - lab-* + - lch-* +- name: oklab + files: + - oklab-* + - oklch-* diff --git a/testing/web-platform/tests/css/css-color/clip-opacity-out-of-flow-ref.html b/testing/web-platform/tests/css/css-color/clip-opacity-out-of-flow-ref.html new file mode 100644 index 0000000000..7610a6f191 --- /dev/null +++ b/testing/web-platform/tests/css/css-color/clip-opacity-out-of-flow-ref.html @@ -0,0 +1,5 @@ +<!DOCTYPE html> +<div style="position: absolute; width: 200px; height: 200px; background: rgb(255 255 128)"></div> +<div style="position: absolute; width: 250px; height: 100px; background: rgb(128 128 255)"></div> +<div style="position: absolute; width: 100px; height: 250px; background: rgb(128 192 128)"></div> +</div> diff --git a/testing/web-platform/tests/css/css-color/clip-opacity-out-of-flow.html b/testing/web-platform/tests/css/css-color/clip-opacity-out-of-flow.html new file mode 100644 index 0000000000..2ad0193806 --- /dev/null +++ b/testing/web-platform/tests/css/css-color/clip-opacity-out-of-flow.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-color/#transparency"> +<link rel="help" href="https://drafts.csswg.org/css-position/#def-cb"> +<link rel="match" href="clip-opacity-out-of-flow-ref.html"> +<meta name="fuzzy" content="maxDifference=0-1;totalPixels=0-60000"> +<div style="overflow: hidden; width: 250px; height: 250px; will-change:transform"> + <div style="overflow: hidden; width: 300px; height: 300px; transform: translateX(0)"> + <div style="overflow: hidden; width: 200px; height: 200px"> + <div style="width: 300px; height: 300px; background: red; opacity: 0.5"> + <div style="width: 400px; height: 400px; background: yellow"> + <!-- These out-of-flow positioned elements are not contained by the inner clip, + while the outer clip and the opacity still apply. The opacity should also + apply to all descendants as a single group. --> + <div style="position: absolute; width: 400px; height: 100px; background: blue"></div> + <div style="position: fixed; width: 100px; height: 400px; background: green"></div> + </div> + </div> + </div> +</div> diff --git a/testing/web-platform/tests/css/css-color/lab-l-over-100-1.html b/testing/web-platform/tests/css/css-color/lab-l-over-100-1.html index 287eba9824..be8833593d 100644 --- a/testing/web-platform/tests/css/css-color/lab-l-over-100-1.html +++ b/testing/web-platform/tests/css/css-color/lab-l-over-100-1.html @@ -5,13 +5,16 @@ <link rel="match" href="lab-l-over-100-ref.html"> <meta name="assert" content="lab() with lightness greater than 100"> <style> - .ref { background-color: lab(100 150 20); width: 100px; height: 100px} + .square { border: 1px solid black; width: 200px; height: 200px} + .ref { background-color: lab(100 150 20); height: 100px} /* l = 150 should clamp back to 100 */ - .test { background-color: lab(150 150 20); width: 100px; height: 100px} + .test { background-color: lab(150 150 20); height: 100px} </style> <body> - <p>Test passes if you see a single color.</p> - <div class="ref"></div> - <div class="test"></div> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="square"> + <div class="ref"></div> + <div class="test"></div> + </div> </body> diff --git a/testing/web-platform/tests/css/css-color/lab-l-over-100-2.html b/testing/web-platform/tests/css/css-color/lab-l-over-100-2.html index e85d289d21..20a9568094 100644 --- a/testing/web-platform/tests/css/css-color/lab-l-over-100-2.html +++ b/testing/web-platform/tests/css/css-color/lab-l-over-100-2.html @@ -5,13 +5,16 @@ <link rel="match" href="lab-l-over-100-ref.html"> <meta name="assert" content="lab() with lightness greater than 100%"> <style> - .ref { background-color: lab(100% 150 20); width: 100px; height: 100px} + .square { border: 1px solid black; width: 200px; height: 200px} + .ref { background-color: lab(100% 150 20); height: 100px} /* l = 150 should clamp back to 100 */ - .test { background-color: lab(150% 150 20); width: 100px; height: 100px} + .test { background-color: lab(150% 150 20); height: 100px} </style> <body> - <p>Test passes if you see a single color.</p> - <div class="ref"></div> - <div class="test"></div> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="square"> + <div class="ref"></div> + <div class="test"></div> + </div> </body> diff --git a/testing/web-platform/tests/css/css-color/lab-l-over-100-ref.html b/testing/web-platform/tests/css/css-color/lab-l-over-100-ref.html index 57328cfa7c..eccc492079 100644 --- a/testing/web-platform/tests/css/css-color/lab-l-over-100-ref.html +++ b/testing/web-platform/tests/css/css-color/lab-l-over-100-ref.html @@ -2,10 +2,10 @@ <meta charset="utf-8"> <title>CSS Color 4: Verify lightness in Lab is always clamped to a value between 0 to 100</title> <style> - .ref { background-color: lab(100 150 20); width: 100px; height: 200px} + .ref { background-color: lab(100 150 20); border: 1px solid black; width: 200px; height: 200px} </style> <body> - <p>Test passes if you see a single color.</p> + <p>Test passes if you see a square border with a single color inside.</p> <div class="ref"></div> </body> diff --git a/testing/web-platform/tests/css/css-color/lch-l-over-100-1.html b/testing/web-platform/tests/css/css-color/lch-l-over-100-1.html index 6e0c62330c..12d594d1a3 100644 --- a/testing/web-platform/tests/css/css-color/lch-l-over-100-1.html +++ b/testing/web-platform/tests/css/css-color/lch-l-over-100-1.html @@ -5,13 +5,16 @@ <link rel="match" href="lch-l-over-100-ref.html"> <meta name="assert" content="lch() with lightness greater than 100"> <style> - .ref { background-color: lch(100 150 20); width: 100px; height: 100px} + .square { border: 1px solid black; width: 200px; height: 200px} + .ref { background-color: lch(100 150 20); height: 100px} /* l = 150 should clamp back to 100 */ - .test { background-color: lch(150 150 20); width: 100px; height: 100px} + .test { background-color: lch(150 150 20); height: 100px} </style> <body> - <p>Test passes if you see a single color.</p> - <div class="ref"></div> - <div class="test"></div> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="square"> + <div class="ref"></div> + <div class="test"></div> + </div> </body> diff --git a/testing/web-platform/tests/css/css-color/lch-l-over-100-2.html b/testing/web-platform/tests/css/css-color/lch-l-over-100-2.html index 7752d0dc0e..68789b9c85 100644 --- a/testing/web-platform/tests/css/css-color/lch-l-over-100-2.html +++ b/testing/web-platform/tests/css/css-color/lch-l-over-100-2.html @@ -5,13 +5,16 @@ <link rel="match" href="lch-l-over-100-ref.html"> <meta name="assert" content="lch() with lightness graeter than 100%"> <style> - .ref { background-color: lch(100% 150 20); width: 100px; height: 100px} + .square { border: 1px solid black; width: 200px; height: 200px} + .ref { background-color: lch(100% 150 20); height: 100px} /* l = 150% should clamp back to 100% */ - .test { background-color: lch(150% 150 20); width: 100px; height: 100px} + .test { background-color: lch(150% 150 20); height: 100px} </style> <body> - <p>Test passes if you see a single color.</p> - <div class="ref"></div> - <div class="test"></div> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="square"> + <div class="ref"></div> + <div class="test"></div> + </div> </body> diff --git a/testing/web-platform/tests/css/css-color/lch-l-over-100-ref.html b/testing/web-platform/tests/css/css-color/lch-l-over-100-ref.html index 67766274e5..291772e166 100644 --- a/testing/web-platform/tests/css/css-color/lch-l-over-100-ref.html +++ b/testing/web-platform/tests/css/css-color/lch-l-over-100-ref.html @@ -2,11 +2,10 @@ <meta charset="utf-8"> <title>CSS Color 4: Specifying LCH with lightness over 100</title> <style> - .ref { background-color: lch(100 150 20); width: 100px; height: 200px} + .ref { background-color: lch(100 150 20); border: 1px solid black; width: 200px; height: 200px} </style> <body> - <p>Test passes if you see a single color.</p> + <p>Test passes if you see a square border with a single color inside.</p> <div class="ref"></div> - <div class="test"></div> </body> diff --git a/testing/web-platform/tests/css/css-color/oklab-l-almost-0-ref.html b/testing/web-platform/tests/css/css-color/oklab-l-almost-0-ref.html new file mode 100644 index 0000000000..244321ea69 --- /dev/null +++ b/testing/web-platform/tests/css/css-color/oklab-l-almost-0-ref.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + .ref { background-color: oklab(0 0.15 0.15); border: 1px solid black; width: 200px; height: 200px; } +</style> +<body> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="ref"></div> +</body> diff --git a/testing/web-platform/tests/css/css-color/oklab-l-almost-0.html b/testing/web-platform/tests/css/css-color/oklab-l-almost-0.html new file mode 100644 index 0000000000..e8cc1b9042 --- /dev/null +++ b/testing/web-platform/tests/css/css-color/oklab-l-almost-0.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Color 4: OKLab and OKLCH</title> +<link rel="help" href="https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch"> +<link rel="match" href="oklab-l-almost-0-ref.html"> +<meta name="assert" content="oklab() with lightness very close to 0 should render the same as 0"> +<style> + .square { border: 1px solid black; width: 200px; height: 200px; } + .test { background-color: red; height: 100px; } + /* Non-zero a/b is used to show if the result is black or not, but the + * test passes as long as it's the same color. */ + .ref { background-color: oklab(0 0.15 0.15); height: 100px; } + .test { background-color: oklab(0.0001% 0.15 0.15); } +</style> +<body> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="square"> + <div class="ref"></div> + <div class="test"></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-color/oklab-l-almost-1-ref.html b/testing/web-platform/tests/css/css-color/oklab-l-almost-1-ref.html new file mode 100644 index 0000000000..72cdf35447 --- /dev/null +++ b/testing/web-platform/tests/css/css-color/oklab-l-almost-1-ref.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + .ref { background-color: oklab(1 0.15 0.15); border: 1px solid black; width: 200px; height: 200px; } +</style> +<body> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="ref"></div> +</body> diff --git a/testing/web-platform/tests/css/css-color/oklab-l-almost-1.html b/testing/web-platform/tests/css/css-color/oklab-l-almost-1.html new file mode 100644 index 0000000000..352e7b58aa --- /dev/null +++ b/testing/web-platform/tests/css/css-color/oklab-l-almost-1.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Color 4: OKLab and OKLCH</title> +<link rel="help" href="https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch"> +<link rel="match" href="oklab-l-almost-1-ref.html"> +<meta name="assert" content="oklab() with lightness very close to 1 should render the same as 1"> +<style> + .square { border: 1px solid black; width: 200px; height: 200px; } + .test { background-color: red; height: 100px; } + /* Non-zero a/b is used to show if the result is white or not, but the + * test passes as long as it's the same color. */ + .ref { background-color: oklab(1 0.15 0.15); height: 100px; } + .test { background-color: oklab(99.9999% 0.15 0.15); } +</style> +<body> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="square"> + <div class="ref"></div> + <div class="test"></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-color/oklab-l-over-1-1.html b/testing/web-platform/tests/css/css-color/oklab-l-over-1-1.html index 612fe32acb..a16fe7886f 100644 --- a/testing/web-platform/tests/css/css-color/oklab-l-over-1-1.html +++ b/testing/web-platform/tests/css/css-color/oklab-l-over-1-1.html @@ -5,13 +5,16 @@ <link rel="match" href="oklab-l-over-1-ref.html"> <meta name="assert" content="oklab() with lightness greater than 1"> <style> - .ref { background-color: oklab(1 0.5 0.2); width: 100px; height: 100px} + .square { border: 1px solid black; width: 200px; height: 200px} + .ref { background-color: oklab(1 0.5 0.2); height: 100px} /* l = 1.5 should clamp back to 1 */ - .test { background-color: oklab(1.5 0.5 0.2); width: 100px; height: 100px} + .test { background-color: oklab(1.5 0.5 0.2); height: 100px} </style> <body> - <p>Test passes if you see a single color.</p> - <div class="ref"></div> - <div class="test"></div> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="square"> + <div class="ref"></div> + <div class="test"></div> + </div> </body> diff --git a/testing/web-platform/tests/css/css-color/oklab-l-over-1-2.html b/testing/web-platform/tests/css/css-color/oklab-l-over-1-2.html index 11948f014b..6300cb8af3 100644 --- a/testing/web-platform/tests/css/css-color/oklab-l-over-1-2.html +++ b/testing/web-platform/tests/css/css-color/oklab-l-over-1-2.html @@ -5,13 +5,16 @@ <link rel="match" href="oklab-l-over-1-ref.html"> <meta name="assert" content="oklab() with lightness greater than 100%"> <style> - .ref { background-color: oklab(100% 0.5 0.2); width: 100px; height: 100px} + .square { border: 1px solid black; width: 200px; height: 200px} + .ref { background-color: oklab(100% 0.5 0.2); height: 100px} /* l = 150 should clamp back to 100 */ - .test { background-color: oklab(150% 0.5 0.2); width: 100px; height: 100px} + .test { background-color: oklab(150% 0.5 0.2); height: 100px} </style> <body> - <p>Test passes if you see a single color.</p> - <div class="ref"></div> - <div class="test"></div> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="square"> + <div class="ref"></div> + <div class="test"></div> + </div> </body> diff --git a/testing/web-platform/tests/css/css-color/oklab-l-over-1-ref.html b/testing/web-platform/tests/css/css-color/oklab-l-over-1-ref.html index eb380dcb75..f050bbc643 100644 --- a/testing/web-platform/tests/css/css-color/oklab-l-over-1-ref.html +++ b/testing/web-platform/tests/css/css-color/oklab-l-over-1-ref.html @@ -2,10 +2,10 @@ <meta charset="utf-8"> <title>CSS Color 4: Verify lightness in Lab is always clamped to a value between 0 to 100</title> <style> - .ref { background-color: oklab(1 0.5 0.2); width: 100px; height: 200px} + .ref { background-color: oklab(1 0.5 0.2); border: 1px solid black; width: 200px; height: 200px} </style> <body> - <p>Test passes if you see a single color.</p> + <p>Test passes if you see a square border with a single color inside.</p> <div class="ref"></div> </body> diff --git a/testing/web-platform/tests/css/css-color/oklch-l-almost-0-ref.html b/testing/web-platform/tests/css/css-color/oklch-l-almost-0-ref.html new file mode 100644 index 0000000000..e019136f96 --- /dev/null +++ b/testing/web-platform/tests/css/css-color/oklch-l-almost-0-ref.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + .ref { background-color: oklch(0 0.2 45); border: 1px solid black; width: 200px; height: 200px; } +</style> +<body> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="ref"></div> +</body> diff --git a/testing/web-platform/tests/css/css-color/oklch-l-almost-0.html b/testing/web-platform/tests/css/css-color/oklch-l-almost-0.html new file mode 100644 index 0000000000..c171befe64 --- /dev/null +++ b/testing/web-platform/tests/css/css-color/oklch-l-almost-0.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Color 4: OKLab and OKLCH</title> +<link rel="help" href="https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch"> +<link rel="match" href="oklch-l-almost-0-ref.html"> +<meta name="assert" content="oklch() with lightness very close to 0 should render the same as 0"> +<style> + .square { border: 1px solid black; width: 200px; height: 200px; } + .test { background-color: red; height: 100px; } + /* Non-zero chroma is used to show if the result is black or not, but the + * test passes as long as it's the same color. */ + .ref { background-color: oklch(0 0.2 45); height: 100px; } + .test { background-color: oklch(0.0001% 0.2 45); } +</style> +<body> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="square"> + <div class="ref"></div> + <div class="test"></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-color/oklch-l-almost-1-ref.html b/testing/web-platform/tests/css/css-color/oklch-l-almost-1-ref.html new file mode 100644 index 0000000000..e1f29eaa18 --- /dev/null +++ b/testing/web-platform/tests/css/css-color/oklch-l-almost-1-ref.html @@ -0,0 +1,9 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<style> + .ref { background-color: oklch(1 0.2 45); border: 1px solid black; width: 200px; height: 200px; } +</style> +<body> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="ref"></div> +</body> diff --git a/testing/web-platform/tests/css/css-color/oklch-l-almost-1.html b/testing/web-platform/tests/css/css-color/oklch-l-almost-1.html new file mode 100644 index 0000000000..c98cea292f --- /dev/null +++ b/testing/web-platform/tests/css/css-color/oklch-l-almost-1.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Color 4: OKLab and OKLCH</title> +<link rel="help" href="https://drafts.csswg.org/css-color-4/#specifying-oklab-oklch"> +<link rel="match" href="oklch-l-almost-1-ref.html"> +<meta name="assert" content="oklch() with lightness very close to 1 should render the same as 1"> +<style> + .square { border: 1px solid black; width: 200px; height: 200px; } + .test { background-color: red; height: 100px; } + /* Non-zero chroma is used to show if the result is white or not, but the + * test passes as long as it's the same color. */ + .ref { background-color: oklch(1 0.2 45); height: 100px; } + .test { background-color: oklch(99.9999% 0.2 45); } +</style> +<body> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="square"> + <div class="ref"></div> + <div class="test"></div> + </div> +</body> diff --git a/testing/web-platform/tests/css/css-color/oklch-l-over-1-1.html b/testing/web-platform/tests/css/css-color/oklch-l-over-1-1.html index 4eb3cda846..c734723541 100644 --- a/testing/web-platform/tests/css/css-color/oklch-l-over-1-1.html +++ b/testing/web-platform/tests/css/css-color/oklch-l-over-1-1.html @@ -5,13 +5,16 @@ <link rel="match" href="oklch-l-over-1-ref.html"> <meta name="assert" content="oklch() with lightness greater than 1"> <style> - .ref { background-color: oklch(1 0.5 50); width: 100px; height: 100px} + .square { border: 1px solid black; width: 200px; height: 200px} + .ref { background-color: oklch(1 0.5 50); height: 100px} /* l = 1.5 should clamp back to 1 */ - .test { background-color: oklch(1.5 0.5 50); width: 100px; height: 100px} + .test { background-color: oklch(1.5 0.5 50); height: 100px} </style> <body> - <p>Test passes if you see a single color.</p> - <div class="ref"></div> - <div class="test"></div> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="square"> + <div class="ref"></div> + <div class="test"></div> + </div> </body> diff --git a/testing/web-platform/tests/css/css-color/oklch-l-over-1-2.html b/testing/web-platform/tests/css/css-color/oklch-l-over-1-2.html index de8b1a6cdd..59eac2b367 100644 --- a/testing/web-platform/tests/css/css-color/oklch-l-over-1-2.html +++ b/testing/web-platform/tests/css/css-color/oklch-l-over-1-2.html @@ -5,13 +5,16 @@ <link rel="match" href="oklch-l-over-1-ref.html"> <meta name="assert" content="oklch() with lightness greater than 100%"> <style> - .ref { background-color: oklch(100% 0.5 50); width: 100px; height: 100px} + .square { border: 1px solid black; width: 200px; height: 200px} + .ref { background-color: oklch(100% 0.5 50); height: 100px} /* l = 150% should clamp back to 100% */ - .test { background-color: oklch(150% 0.5 50); width: 100px; height: 100px} + .test { background-color: oklch(150% 0.5 50); height: 100px} </style> <body> - <p>Test passes if you see a single color.</p> - <div class="ref"></div> - <div class="test"></div> + <p>Test passes if you see a square border with a single color inside.</p> + <div class="square"> + <div class="ref"></div> + <div class="test"></div> + </div> </body> diff --git a/testing/web-platform/tests/css/css-color/oklch-l-over-1-ref.html b/testing/web-platform/tests/css/css-color/oklch-l-over-1-ref.html index 2c7815c5f0..ae1b5e00e5 100644 --- a/testing/web-platform/tests/css/css-color/oklch-l-over-1-ref.html +++ b/testing/web-platform/tests/css/css-color/oklch-l-over-1-ref.html @@ -2,10 +2,10 @@ <meta charset="utf-8"> <title>CSS Color 4: Verify lightness in Oklch is always clamped to a value between 0 to 1</title> <style> - .ref { background-color: oklch(1 0.5 50); width: 100px; height: 200px} + .ref { background-color: oklch(1 0.5 50); border: 1px solid black; width: 200px; height: 200px} </style> <body> - <p>Test passes if you see a single color.</p> + <p>Test passes if you see a square border with a single color inside.</p> <div class="ref"></div> </body> diff --git a/testing/web-platform/tests/css/css-color/parsing/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-color/parsing/WEB_FEATURES.yml new file mode 100644 index 0000000000..cc8273c2ad --- /dev/null +++ b/testing/web-platform/tests/css/css-color/parsing/WEB_FEATURES.yml @@ -0,0 +1,5 @@ +features: +- name: color-mix + files: + - "*-color-mix-*" + - color-mix-out-of-gamut.html diff --git a/testing/web-platform/tests/css/css-color/parsing/color-computed-color-mix-function.html b/testing/web-platform/tests/css/css-color/parsing/color-computed-color-mix-function.html index b54aa0da12..88cb20e6eb 100644 --- a/testing/web-platform/tests/css/css-color/parsing/color-computed-color-mix-function.html +++ b/testing/web-platform/tests/css/css-color/parsing/color-computed-color-mix-function.html @@ -42,8 +42,8 @@ fuzzy_test_computed_color(`color-mix(in hsl, hsl(120deg 10% 20% / 0), hsl(30deg 30% 40%))`, `color(srgb 0.46 0.52 0.28 / 0.5)`); fuzzy_test_computed_color(`color-mix(in hsl, hsl(120deg 10% 20% / 0) 10%, hsl(30deg 30% 40%))`, `color(srgb 0.52 0.436 0.28 / 0.9)`); - fuzzy_test_computed_color(`color-mix(in hsl, white, blue)`, `color(srgb 0.62 0.62 0.87)`); - fuzzy_test_computed_color(`color-mix(in hsl, white 10%, blue)`, `color(srgb 0.15 0.15 0.96)`); + fuzzy_test_computed_color(`color-mix(in hsl, white, blue)`, `color(srgb 0.625 0.625 0.875)`); + fuzzy_test_computed_color(`color-mix(in hsl, white 10%, blue)`, `color(srgb 0.145 0.145 0.955)`); fuzzy_test_computed_color(`color-mix(in hsl, hsl(40deg 50% 50%), hsl(60deg 50% 50%))`, `color(srgb 0.75 0.666667 0.25)`); fuzzy_test_computed_color(`color-mix(in hsl, hsl(60deg 50% 50%), hsl(40deg 50% 50%))`, `color(srgb 0.75 0.666667 0.25)`); @@ -421,8 +421,8 @@ fuzzy_test_computed_color(`color-mix(in oklab, oklab(0.1 0.2 0.3 / 0), oklab(0.3 0.4 0.5))`, 'oklab(0.3 0.4 0.5 / 0.5)'); fuzzy_test_computed_color(`color-mix(in oklab, oklab(0.1 0.2 0.3 / 0) 10%, oklab(0.3 0.4 0.5))`, 'oklab(0.3 0.4 0.5 / 0.9)'); - fuzzy_test_computed_color(`color-mix(in oklab, white, blue)`, `oklab(0.73 -0.02 -0.16)`); - fuzzy_test_computed_color(`color-mix(in oklab, white 10%, blue)`, `oklab(0.51 -0.03 -0.28)`); + fuzzy_test_computed_color(`color-mix(in oklab, white, blue)`, `oklab(0.726 -0.016 -0.156)`); + fuzzy_test_computed_color(`color-mix(in oklab, white 10%, blue)`, `oklab(0.507 -0.029 -0.28)`); fuzzy_test_computed_color(`color-mix(in oklab, oklab(none none none), oklab(none none none))`, `oklab(none none none)`); fuzzy_test_computed_color(`color-mix(in oklab, oklab(none none none), oklab(0.5 0.6 0.7))`, `oklab(0.5 0.6 0.7)`); @@ -441,7 +441,7 @@ fuzzy_test_computed_color(`color-mix(in oklab, oklab(0.1 0.2 0.3 / 25%) 0%, oklab(none none none / 0.5))`, `oklab(0.1 0.2 0.3 / 0.5)`); fuzzy_test_computed_color(`color-mix(in oklab, oklab(0.1 0.2 0.3 / 25%) 0%, oklab(0.5 none none / 0.5))`, `oklab(0.5 0.2 0.3 / 0.5)`); - for (const colorSpace of [ "srgb", "srgb-linear", "xyz", "xyz-d50", "xyz-d65" ]) { + for (const colorSpace of [ "srgb", "srgb-linear", "display-p3", "a98-rgb", "prophoto-rgb", "rec2020", "xyz", "xyz-d50", "xyz-d65" ]) { const resultColorSpace = colorSpace == "xyz" ? "xyz-d65" : colorSpace; fuzzy_test_computed_color(`color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3), color(${colorSpace} .5 .6 .7))`, `color(${resultColorSpace} 0.3 0.4 0.5)`); diff --git a/testing/web-platform/tests/css/css-color/parsing/color-computed-relative-color.html b/testing/web-platform/tests/css/css-color/parsing/color-computed-relative-color.html index 95c8eee226..ac2f9c87aa 100644 --- a/testing/web-platform/tests/css/css-color/parsing/color-computed-relative-color.html +++ b/testing/web-platform/tests/css/css-color/parsing/color-computed-relative-color.html @@ -106,6 +106,10 @@ fuzzy_test_computed_color(`rgb(from rgb(100 110 120 / 0.8) calc(r + 1) calc(g + 1) calc(b + 1) / calc(alpha + 0.01))`, `color(srgb 0.396 0.435 0.474 / 0.81)`); // rgb(101 111 121) fuzzy_test_computed_color(`rgb(from rebeccapurple calc((r / 255) * 100%) calc((g / 255) * 100%) calc((b / 255) * 100%) / calc(alpha * 100%))`, `color(srgb 0.4 0.2 0.6)`); + // Alpha is clamped to [0,1] + fuzzy_test_computed_color(`rgb(from rgb(from rebeccapurple r g b / calc(alpha + 0.5)) r g b / calc(alpha - 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`); + fuzzy_test_computed_color(`rgb(from rgb(from rebeccapurple r g b / calc(alpha - 1.5)) r g b / calc(alpha + 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`); + // Testing with 'none'. Missing components are resolved to zero during color space conversion. // https://drafts.csswg.org/css-color-4/#missing fuzzy_test_computed_color(`rgb(from rebeccapurple none none none)`, `color(srgb 0 0 0)`); @@ -119,6 +123,7 @@ fuzzy_test_computed_color(`rgb(from rgb(none none none / none) r g b / alpha)`, `color(srgb 0 0 0 / 0)`); fuzzy_test_computed_color(`rgb(from rgb(20% none 60%) r g b)`, `color(srgb 0.2 0 0.6)`); fuzzy_test_computed_color(`rgb(from rgb(20% 40% 60% / none) r g b / alpha)`, `color(srgb 0.2 0.4 0.6 / 0)`); + fuzzy_test_computed_color(`color-mix(in srgb, rgb(from rebeccapurple none g b), rebeccapurple)`, `color(srgb 0.4 0.2 0.6)`); // color-mix fuzzy_test_computed_color(`rgb(from color-mix(in srgb, red, red) r g b / alpha)`, `color(srgb 1 0 0)`); @@ -165,20 +170,24 @@ // Testing valid permutation (types match). fuzzy_test_computed_color(`hsl(from rebeccapurple h l s)`, `color(srgb 0.5 0.3 0.7)`); - fuzzy_test_computed_color(`hsl(from rebeccapurple h alpha l / s)`, `color(srgb 0.4 0 0.8 / 0.5)`); - fuzzy_test_computed_color(`hsl(from rebeccapurple h l l / l)`, `color(srgb 0.4 0.24 0.56 / 0.4)`); - fuzzy_test_computed_color(`hsl(from rebeccapurple h alpha alpha / alpha)`, `color(srgb 1 1 1)`); + fuzzy_test_computed_color(`hsl(from rebeccapurple h alpha l / s)`, `color(srgb 0.4 0.396 0.404)`); + fuzzy_test_computed_color(`hsl(from rebeccapurple h l l / l)`, `color(srgb 0.4 0.24 0.56)`); + fuzzy_test_computed_color(`hsl(from rebeccapurple h alpha alpha / alpha)`, `color(srgb 0.01 0.01 0.01)`); fuzzy_test_computed_color(`hsl(from rgb(20%, 40%, 60%, 80%) h l s)`, `color(srgb 0.3 0.5 0.7 / 0.8)`); - fuzzy_test_computed_color(`hsl(from rgb(20%, 40%, 60%, 80%) h alpha l / s)`, `color(srgb 0.08 0.4 0.72 / 0.5)`); - fuzzy_test_computed_color(`hsl(from rgb(20%, 40%, 60%, 80%) h l l / l)`, `color(srgb 0.24 0.4 0.56 / 0.4)`); - fuzzy_test_computed_color(`hsl(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)`, `color(srgb 0.64 0.8 0.96 / 0.8)`); + fuzzy_test_computed_color(`hsl(from rgb(20%, 40%, 60%, 80%) h alpha l / s)`, `color(srgb 0.397 0.4 0.403)`); + fuzzy_test_computed_color(`hsl(from rgb(20%, 40%, 60%, 80%) h l l / l)`, `color(srgb 0.24 0.4 0.56)`); + fuzzy_test_computed_color(`hsl(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)`, `color(srgb 0.01 0.01 0.01 / 0.8)`); // Testing with calc(). fuzzy_test_computed_color(`hsl(from rebeccapurple calc(h) calc(s) calc(l))`, `color(srgb 0.4 0.2 0.6)`); fuzzy_test_computed_color(`hsl(from rgb(20%, 40%, 60%, 80%) calc(h) calc(s) calc(l) / calc(alpha))`, `color(srgb 0.2 0.4 0.6 / 0.8)`); - fuzzy_test_computed_color(`hsl(from hsl(20 30 40 / 0.8) calc(h + 1) calc(s + 1) calc(l + 1) / calc(alpha + 0.01))`, `color(srgb 0.54 0.37 0.28 / 0.81)`); // hsl(21 31 41) + fuzzy_test_computed_color(`hsl(from hsl(20 30 40 / 0.8) calc(h + 1) calc(s + 1) calc(l + 1) / calc(alpha + 0.01))`, `color(srgb 0.537 0.372 0.283 / 0.81)`); // hsl(21 31 41) fuzzy_test_computed_color(`hsl(from rebeccapurple calc((h / 360) * 360deg) calc((s / 100) * 100%) calc((l / 100) * 100%) / calc(alpha * 100%))`, `color(srgb 0.4 0.2 0.6)`); + // Alpha is clamped to [0,1] + fuzzy_test_computed_color(`hsl(from hsl(from rebeccapurple h s l / calc(alpha + 0.5)) h s l / calc(alpha - 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`); + fuzzy_test_computed_color(`hsl(from hsl(from rebeccapurple h s l / calc(alpha - 1.5)) h s l / calc(alpha + 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`); + // Testing with 'none'. Missing components are resolved to zero during color space conversion. // https://drafts.csswg.org/css-color-4/#missing fuzzy_test_computed_color(`hsl(from rebeccapurple none none none)`, `color(srgb 0 0 0)`); @@ -195,6 +204,7 @@ fuzzy_test_computed_color(`hsl(from hsl(120deg none 50% / .5) h s l)`, `color(srgb 0.5 0.5 0.5 / 0.5)`); fuzzy_test_computed_color(`hsl(from hsl(120deg 20% 50% / none) h s l / alpha)`, `color(srgb 0.4 0.6 0.4 / 0)`); fuzzy_test_computed_color(`hsl(from hsl(none 20% 50% / .5) h s l / alpha)`, `color(srgb 0.6 0.4 0.4 / 0.5)`); + fuzzy_test_computed_color(`color-mix(in hsl, hsl(from rebeccapurple none s l), rebeccapurple)`, `color(srgb 0.4 0.2 0.6)`); // color-mix fuzzy_test_computed_color(`hsl(from color-mix(in srgb, red, red) h s l / alpha)`, `color(srgb 1 0 0)`); @@ -240,13 +250,13 @@ // Testing valid permutation (types match). fuzzy_test_computed_color(`hwb(from rebeccapurple h b w)`, `color(srgb 0.6 0.4 0.8)`); - fuzzy_test_computed_color(`hwb(from rebeccapurple h alpha w / b)`, `color(srgb 0.8333 0.8333 0.8333 / 0.4)`); - fuzzy_test_computed_color(`hwb(from rebeccapurple h w w / w)`, `color(srgb 0.5 0.2 0.8 / 0.2)`); - fuzzy_test_computed_color(`hwb(from rebeccapurple h alpha alpha / alpha)`, `color(srgb 0.5 0.5 0.5)`); + fuzzy_test_computed_color(`hwb(from rebeccapurple h alpha w / b)`, `color(srgb 0.405 0.01 0.8)`); + fuzzy_test_computed_color(`hwb(from rebeccapurple h w w / w)`, `color(srgb 0.5 0.2 0.8)`); + fuzzy_test_computed_color(`hwb(from rebeccapurple h alpha alpha / alpha)`, `color(srgb 0.5 0.01 0.99)`); fuzzy_test_computed_color(`hwb(from rgb(20%, 40%, 60%, 80%) h b w)`, `color(srgb 0.4 0.6 0.8 / 0.8)`); - fuzzy_test_computed_color(`hwb(from rgb(20%, 40%, 60%, 80%) h alpha w / b)`, `color(srgb 0.8 0.8 0.8 / 0.4)`); - fuzzy_test_computed_color(`hwb(from rgb(20%, 40%, 60%, 80%) h w w / w)`, `color(srgb 0.2 0.5 0.8 / 0.2)`); - fuzzy_test_computed_color(`hwb(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)`, `color(srgb 0.5 0.5 0.5 / 0.8)`); + fuzzy_test_computed_color(`hwb(from rgb(20%, 40%, 60%, 80%) h alpha w / b)`, `color(srgb 0.01 0.404 0.8)`); + fuzzy_test_computed_color(`hwb(from rgb(20%, 40%, 60%, 80%) h w w / w)`, `color(srgb 0.2 0.5 0.8)`); + fuzzy_test_computed_color(`hwb(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)`, `color(srgb 0.01 0.5 0.992 / 0.8)`); // Testing with calc(). fuzzy_test_computed_color(`hwb(from rebeccapurple calc(h) calc(w) calc(b))`, `color(srgb 0.4 0.2 0.6)`); @@ -254,6 +264,10 @@ fuzzy_test_computed_color(`hwb(from hwb(20 30 40 / 0.8) calc(h + 1) calc(w + 1) calc(b + 1) / calc(alpha + 0.01))`, `color(srgb 0.59 0.41 0.31 / 0.81)`); // hwb(21 31 41) fuzzy_test_computed_color(`hwb(from rebeccapurple calc((h / 360) * 360deg) calc((w / 100) * 100%) calc((b / 100) * 100%) / calc(alpha * 100%))`, `color(srgb 0.4 0.2 0.6)`); + // Alpha is clamped to [0,1] + fuzzy_test_computed_color(`hwb(from hwb(from rebeccapurple h w b / calc(alpha + 0.5)) h w b / calc(alpha - 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`); + fuzzy_test_computed_color(`hwb(from hwb(from rebeccapurple h w b / calc(alpha - 1.5)) h w b / calc(alpha + 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`); + // Testing with 'none'. Missing components are resolved to zero during color space conversion. // https://drafts.csswg.org/css-color-4/#missing fuzzy_test_computed_color(`hwb(from rebeccapurple none none none)`, `color(srgb 1 0 0)`); @@ -270,6 +284,7 @@ fuzzy_test_computed_color(`hwb(from hwb(120deg none 50% / .5) h w b)`, `color(srgb 0 0.5 0 / 0.5)`); fuzzy_test_computed_color(`hwb(from hwb(120deg 20% 50% / none) h w b / alpha)`, `color(srgb 0.2 0.5 0.2 / 0)`); fuzzy_test_computed_color(`hwb(from hwb(none 20% 50% / .5) h w b / alpha)`, `color(srgb 0.5 0.2 0.2 / 0.5)`); + fuzzy_test_computed_color(`color-mix(in hwb, hwb(from rebeccapurple none w b), rebeccapurple)`, `color(srgb 0.4 0.2 0.6)`); // color-mix fuzzy_test_computed_color(`hwb(from color-mix(in srgb, red, red) h w b / alpha)`, `color(srgb 1 0 0)`); @@ -325,6 +340,10 @@ fuzzy_test_computed_color(`lab(from lab(50 5 10 / 0.8) calc(l + 1) calc(a + 1) calc(b + 1) / calc(alpha + 0.01))`, `lab(51 6 11 / 0.81)`); fuzzy_test_computed_color(`lab(from lab(25 20 50) calc((l / 100) * 100%) calc((a / 125) * 100%) calc((b / 125) * 100%) / calc(alpha * 100%))`, `lab(25 20 50)`); + // Alpha is clamped to [0,1] + fuzzy_test_computed_color(`lab(from lab(from lab(25 20 50) l a b / calc(alpha + 0.5)) l a b / calc(alpha - 0.5))`, `lab(25 20 50 / 0.5)`); + fuzzy_test_computed_color(`lab(from lab(from lab(25 20 50) l a b / calc(alpha - 1.5)) l a b / calc(alpha + 0.5))`, `lab(25 20 50 / 0.5)`); + // Testing with 'none'. fuzzy_test_computed_color(`lab(from lab(25 20 50) none none none)`, `lab(none none none)`); fuzzy_test_computed_color(`lab(from lab(25 20 50) none none none / none)`, `lab(none none none / none)`); @@ -338,6 +357,7 @@ fuzzy_test_computed_color(`lab(from lab(none none none / none) l a b / alpha)`, `lab(0 0 0 / 0)`); fuzzy_test_computed_color(`lab(from lab(25 none 50) l a b)`, `lab(25 0 50)`); fuzzy_test_computed_color(`lab(from lab(25 20 50 / none) l a b / alpha)`, `lab(25 20 50 / 0)`); + fuzzy_test_computed_color(`color-mix(in lab, lab(from lab(25 20 50) none a b), lab(25 20 50))`, `lab(25 20 50)`); // color-mix fuzzy_test_computed_color(`lab(from color-mix(in lab, lab(25 20 50), lab(25 20 50)) l a b / alpha)`, `lab(25 20 50)`); @@ -393,6 +413,10 @@ fuzzy_test_computed_color(`oklab(from oklab(0.5 .05 0.1 / 0.8) calc(l + 0.01) calc(a + 0.01) calc(b + 0.01) / calc(alpha + 0.01))`, `oklab(0.51 .06 0.11 / 0.81)`); fuzzy_test_computed_color(`oklab(from oklab(0.25 0.2 0.5) calc(l * 100%) calc((a / 0.4) * 100%) calc((b / 0.4) * 100%) / calc(alpha * 100%))`, `oklab(0.25 0.2 0.5)`); + // Alpha is clamped to [0,1] + fuzzy_test_computed_color(`oklab(from oklab(from oklab(0.25 0.2 0.5) l a b / calc(alpha + 0.5)) l a b / calc(alpha - 0.5))`, `oklab(0.25 0.2 0.5 / 0.5)`); + fuzzy_test_computed_color(`oklab(from oklab(from oklab(0.25 0.2 0.5) l a b / calc(alpha - 1.5)) l a b / calc(alpha + 0.5))`, `oklab(0.25 0.2 0.5 / 0.5)`); + // Testing with 'none'. fuzzy_test_computed_color(`oklab(from oklab(0.25 0.2 0.5) none none none)`, `oklab(none none none)`); fuzzy_test_computed_color(`oklab(from oklab(0.25 0.2 0.5) none none none / none)`, `oklab(none none none / none)`); @@ -406,6 +430,7 @@ fuzzy_test_computed_color(`oklab(from oklab(none none none / none) l a b / alpha)`, `oklab(0 0 0 / 0)`); fuzzy_test_computed_color(`oklab(from oklab(0.25 none 0.5) l a b)`, `oklab(0.25 0 0.5)`); fuzzy_test_computed_color(`oklab(from oklab(0.25 0.2 0.5 / none) l a b / alpha)`, `oklab(0.25 0.2 0.5 / 0)`); + fuzzy_test_computed_color(`color-mix(in oklab, oklab(from oklab(0.25 0.2 0.5) none a b), oklab(0.25 0.2 0.5))`, `oklab(0.25 0.2 0.5)`); // color-mix fuzzy_test_computed_color(`oklab(from color-mix(in oklab, oklab(0.25 0.2 0.5), oklab(0.25 0.2 0.5)) l a b / alpha)`, `oklab(0.25 0.2 0.5)`); @@ -469,6 +494,10 @@ fuzzy_test_computed_color(`lch(from lch(50 5 10 / 0.8) calc(l + 1) calc(c + 1) calc(h + 1) / calc(alpha + 0.01))`, `lch(51 6 11 / 0.81)`); fuzzy_test_computed_color(`lch(from lch(0.7 45 30) calc((l / 100) * 100%) calc((c / 150) * 100%) calc((h / 360) * 360deg) / calc(alpha * 100%))`, `lch(0.7 45 30)`); + // Alpha is clamped to [0,1] + fuzzy_test_computed_color(`lch(from lch(from lch(0.7 45 30) l c h / calc(alpha + 0.5)) l c h / calc(alpha - 0.5))`, `lch(0.7 45 30 / 0.5)`); + fuzzy_test_computed_color(`lch(from lch(from lch(0.7 45 30) l c h / calc(alpha - 1.5)) l c h / calc(alpha + 0.5))`, `lch(0.7 45 30 / 0.5)`); + // Testing with 'none'. fuzzy_test_computed_color(`lch(from lch(0.7 45 30) none none none)`, `lch(none none none)`); fuzzy_test_computed_color(`lch(from lch(0.7 45 30) none none none / none)`, `lch(none none none / none)`); @@ -482,6 +511,7 @@ fuzzy_test_computed_color(`lch(from lch(none none none / none) l c h / alpha)`, `lch(0 0 0 / 0)`); fuzzy_test_computed_color(`lch(from lch(0.7 none 30) l c h)`, `lch(0.7 0 30)`); fuzzy_test_computed_color(`lch(from lch(0.7 45 30 / none) l c h / alpha)`, `lch(0.7 45 30 / 0)`); + fuzzy_test_computed_color(`color-mix(in lch, lch(from lch(0.7 45 30) l c none), lch(0.7 45 30))`, `lch(0.7 45 30)`); // color-mix fuzzy_test_computed_color(`lch(from color-mix(in lch, lch(70 45 30), lch(70 45 30)) l c h / alpha)`, `lch(70 45 30)`); @@ -546,6 +576,10 @@ fuzzy_test_computed_color(`oklch(from oklch(0.5 .05 0.1 / 0.8) calc(l + 0.01) calc(c + 0.01) calc(h + 0.01) / calc(alpha + 0.01))`, `oklch(0.51 .06 0.11 / 0.81)`); fuzzy_test_computed_color(`oklch(from oklch(0.7 0.45 30) calc(l * 100%) calc((c / 0.4) * 100%) calc((h / 360) * 360deg) / calc(alpha * 100%))`, `oklch(0.7 0.45 30)`); + // Alpha is clamped to [0,1] + fuzzy_test_computed_color(`oklch(from oklch(from oklch(0.7 0.45 30) l c h / calc(alpha + 0.5)) l c h / calc(alpha - 0.5))`, `oklch(0.7 0.45 30 / 0.5)`); + fuzzy_test_computed_color(`oklch(from oklch(from oklch(0.7 0.45 30) l c h / calc(alpha - 1.5)) l c h / calc(alpha + 0.5))`, `oklch(0.7 0.45 30 / 0.5)`); + // Testing with 'none'. fuzzy_test_computed_color(`oklch(from oklch(0.7 0.45 30) none none none)`, `oklch(none none none)`); fuzzy_test_computed_color(`oklch(from oklch(0.7 0.45 30) none none none / none)`, `oklch(none none none / none)`); @@ -559,6 +593,7 @@ fuzzy_test_computed_color(`oklch(from oklch(none none none / none) l c h / alpha)`, `oklch(0 0 0 / 0)`); fuzzy_test_computed_color(`oklch(from oklch(0.7 none 30) l c h)`, `oklch(0.7 0 30)`); fuzzy_test_computed_color(`oklch(from oklch(0.7 0.45 30 / none) l c h / alpha)`, `oklch(0.7 0.45 30 / 0)`); + fuzzy_test_computed_color(`color-mix(in oklch, oklch(from oklch(0.7 0.45 30) l c none), oklch(0.7 0.45 30))`, `oklch(0.7 0.45 30)`); // color-mix fuzzy_test_computed_color(`oklch(from color-mix(in oklch, oklch(0.7 0.45 30), oklch(0.7 0.45 30)) l c h / alpha)`, `oklch(0.7 0.45 30)`); @@ -637,6 +672,10 @@ fuzzy_test_computed_color(`color(from color(${colorSpace} 0.7 0.5 0.3 / 0.8) ${colorSpace} calc(r + 0.01) calc(g + 0.01) calc(b + 0.01) / calc(alpha + 0.01))`, `color(${colorSpace} 0.71 0.51 0.31 / 0.81)`); fuzzy_test_computed_color(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} calc(r * 100%) calc(g * 100%) calc(b * 100%) / calc(alpha * 100%))`, `color(${colorSpace} 0.7 0.5 0.3)`); + // Alpha is clamped to [0,1] + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g b / calc(alpha + 0.5)) ${colorSpace} r g b / calc(alpha - 0.5))`, `color(${colorSpace} 0.7 0.5 0.3 / 0.5)`); + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} r g b / calc(alpha - 1.5)) ${colorSpace} r g b / calc(alpha + 0.5))`, `color(${colorSpace} 0.7 0.5 0.3 / 0.5)`); + // Testing with 'none'. fuzzy_test_computed_color(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} none none none)`, `color(${colorSpace} none none none)`); fuzzy_test_computed_color(`color(from color(${colorSpace} 0.7 0.5 0.3) ${colorSpace} none none none / none)`, `color(${colorSpace} none none none / none)`); @@ -702,6 +741,10 @@ fuzzy_test_computed_color(`color(from color(${colorSpace} 7 -20.5 100 / 0.8) ${colorSpace} calc(x + 1) calc(y + 1) calc(z + 1) / calc(alpha + 0.01))`, `color(${resultColorSpace} 8 -19.5 101 / 0.81)`); fuzzy_test_computed_color(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} calc(x * 100%) calc(y * 100%) calc(z * 100%) / calc(alpha * 100%))`, `color(${resultColorSpace} 7 -20.5 100)`); + // Alpha is clamped to [0,1] + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y z / calc(alpha + 0.5)) ${colorSpace} x y z / calc(alpha - 0.5))`, `color(${resultColorSpace} 7 -20.5 100 / 0.5)`); + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} x y z / calc(alpha - 1.5)) ${colorSpace} x y z / calc(alpha + 0.5))`, `color(${resultColorSpace} 7 -20.5 100 / 0.5)`); + // Testing with 'none'. fuzzy_test_computed_color(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} none none none)`, `color(${resultColorSpace} none none none)`); fuzzy_test_computed_color(`color(from color(${colorSpace} 7 -20.5 100) ${colorSpace} none none none / none)`, `color(${resultColorSpace} none none none / none)`); @@ -744,6 +787,26 @@ fuzzy_test_computed_color(`oklch(from color(srgb 0.25 0.5 0.75) l c h)`, `oklch(0.585502 0.118254 250.546)`, 0.02); // Larger values means larger epsilon. fuzzy_test_computed_color(`color(from oklch(72.322% 0.12403 247.996) srgb r g b)`, `color(srgb 0.382631 0.672756 0.938904)`, 0.001); + // Test that conversion are relatively lossless. + for (const colorSpace of ["xyz-d50", "xyz-d65"]) { + fuzzy_test_computed_color(`color(from rgb(from color(${colorSpace} 0.99 0.88 0.77) r g b) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from hsl(from color(${colorSpace} 0.99 0.88 0.77) h s l) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from hwb(from color(${colorSpace} 0.99 0.88 0.77) h w b) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from lab(from color(${colorSpace} 0.99 0.88 0.77) l a b) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from lch(from color(${colorSpace} 0.99 0.88 0.77) l c h) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from oklab(from color(${colorSpace} 0.99 0.88 0.77) l a b) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from oklch(from color(${colorSpace} 0.99 0.88 0.77) l c h) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.99 0.88 0.77) srgb r g b) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.99 0.88 0.77) srgb-linear r g b) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.99 0.88 0.77) display-p3 r g b) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.99 0.88 0.77) a98-rgb r g b) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.99 0.88 0.77) prophoto-rgb r g b) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.99 0.88 0.77) rec2020 r g b) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.99 0.88 0.77) xyz x y z) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.99 0.88 0.77) xyz-d50 x y z) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + fuzzy_test_computed_color(`color(from color(from color(${colorSpace} 0.99 0.88 0.77) xyz-d65 x y z) ${colorSpace} x y z)`, `color(${colorSpace} 0.99 0.88 0.77)`, 0.0001); + } + // Spec Examples: https://www.w3.org/TR/css-color-5/#relative-colors // All examples here have multiple stages of calculations so minor disagreements in the values of keyword colors and other constants can compound. // These tests require a wider epsilon of 0.02. diff --git a/testing/web-platform/tests/css/css-color/parsing/color-invalid-color-function.html b/testing/web-platform/tests/css/css-color/parsing/color-invalid-color-function.html index 5a39d42c2d..e02fb44fcb 100644 --- a/testing/web-platform/tests/css/css-color/parsing/color-invalid-color-function.html +++ b/testing/web-platform/tests/css/css-color/parsing/color-invalid-color-function.html @@ -12,7 +12,10 @@ </head> <body> <script> -for (const colorSpace of [ "srgb", "srgb-linear", "a98-rgb", "rec2020", "prophoto-rgb" ]) { +const RGB_SPACES = ["srgb", "srgb-linear", "a98-rgb", "rec2020", "prophoto-rgb"]; +const XYZ_SPACES = ["xyz", "xyz-d50", "xyz-d65"]; + +for (const colorSpace of RGB_SPACES) { test_invalid_value("color", `color(${colorSpace} 0 0 0 0)`); test_invalid_value("color", `color(${colorSpace} 0deg 0% 0)`); test_invalid_value("color", `color(${colorSpace} 0% 0 0 1)`); @@ -29,7 +32,7 @@ for (const colorSpace of [ "srgb", "srgb-linear", "a98-rgb", "rec2020", "prophot test_invalid_value("color", `color(${colorSpace} / 0.5)`); } -for (const colorSpace of [ "xyz", "xyz-d50", "xyz-d65" ]) { +for (const colorSpace of XYZ_SPACES) { test_invalid_value("color", `color(${colorSpace} 0 0 0 0)`); test_invalid_value("color", `color(${colorSpace} 0deg 0% 0)`); test_invalid_value("color", `color(${colorSpace} 0% 0 0 1)`); @@ -52,12 +55,17 @@ test_invalid_value("color", "color(displayp3 1 1 1)"); // Bad Display P3 color test_invalid_value("color", "color(1 1 1)"); // No color space test_invalid_value("color", "color(srgb 1 1)"); // One missing component test_invalid_value("color", "color(srgb 1)"); // Two missing components +test_invalid_value("color", "color(srgb 0, 0, 0)"); // Commas as separators test_invalid_value("color", "color(srgb 1 1 1 1)"); // Too many parameters test_invalid_value("color", "color(srgb 1 1 1 1 1)"); // Way too many parameters test_invalid_value("color", "color(srgb 1 eggs 1)"); // Bad parameters test_invalid_value("color", "color(srgb 1 1 1 / bacon)"); // Bad alpha test_invalid_value("color", "color(srgb 1 1 1 / 1 cucumber)"); // Junk after alpha + +for (const colorSpace of [...RGB_SPACES, ...XYZ_SPACES]) { + test_invalid_value("color", `${colorSpace}(0 0 0)`); +} </script> </body> </html> diff --git a/testing/web-platform/tests/css/css-color/parsing/color-invalid-color-mix-function.html b/testing/web-platform/tests/css/css-color/parsing/color-invalid-color-mix-function.html index 40299644bf..2f815f8140 100644 --- a/testing/web-platform/tests/css/css-color/parsing/color-invalid-color-mix-function.html +++ b/testing/web-platform/tests/css/css-color/parsing/color-invalid-color-mix-function.html @@ -74,7 +74,7 @@ test_invalid_value(`color`, `color-mix(${colorSpace}(10% 20 30), ${colorSpace}(50% 60 70))`); // Missing interpolation method. } - for (const colorSpace of [ "srgb", "srgb-linear", "xyz", "xyz-d50", "xyz-d65" ]) { + for (const colorSpace of [ "srgb", "srgb-linear", "display-p3", "a98-rgb", "prophoto-rgb", "rec2020", "xyz", "xyz-d50", "xyz-d65" ]) { test_invalid_value(`color`, `color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3) -10%, color(${colorSpace} .5 .6 .7))`); // Percentages less than 0 are not valid. test_invalid_value(`color`, `color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3) 150%, color(${colorSpace} .5 .6 .7))`); // Percentages greater than 100 are not valid. test_invalid_value(`color`, `color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3) 0%, color(${colorSpace} .5 .6 .7) 0%)`); // Sum of percengates cannot be 0%. diff --git a/testing/web-platform/tests/css/css-color/parsing/color-invalid-relative-color.html b/testing/web-platform/tests/css/css-color/parsing/color-invalid-relative-color.html index 2cb3a25291..c6f1370abd 100644 --- a/testing/web-platform/tests/css/css-color/parsing/color-invalid-relative-color.html +++ b/testing/web-platform/tests/css/css-color/parsing/color-invalid-relative-color.html @@ -28,13 +28,15 @@ test_invalid_value(`color`, `rgb(from rebeccapurple l g b)`); test_invalid_value(`color`, `rgb(from rebeccapurple h g b)`); - // Testing invalid function name variation (only rgb() is valid, rgba() is invalid) - test_invalid_value(`color`, `rgba(from rebeccapurple r g b)`); - test_invalid_value(`color`, `rgba(from rgb(10%, 20%, 30%, 40%) r g b / alpha)`); + // Testing invalid separator + test_invalid_value(`color`, `rgb(from rebeccapurple, r, g, b)`); + test_invalid_value(`color`, `rgba(from rgb(10%, 20%, 30%, 40%), r, g, b, alpha)`); // Testing with calc(). test_invalid_value(`color`, `rgb(from rebeccapurple calc(r + 1%) g b)`); + // Using a channel keyword without being a relative color. + test_invalid_value(`color`, `rgb(0 0 0 / alpha)`); // hsl(from ...) @@ -47,9 +49,9 @@ test_invalid_value(`color`, `hsl(from rebeccapurple x s l)`); test_invalid_value(`color`, `hsl(from rebeccapurple h g b)`); - // Testing invalid function name variation (only hsl() is valid, hsla() is invalid) - test_invalid_value(`color`, `hsla(from rebeccapurple h s l)`); - test_invalid_value(`color`, `hsla(from rgb(10%, 20%, 30%, 40%) h s l / alpha)`); + // Testing invalid separator + test_invalid_value(`color`, `hsl(from rebeccapurple, h, s, l)`); + test_invalid_value(`color`, `hsla(from rgb(10%, 20%, 30%, 40%), h, s, l, alpha)`); // Testing with calc(). test_invalid_value(`color`, `hsl(from rebeccapurple calc(h + 1deg) s l)`); diff --git a/testing/web-platform/tests/css/css-color/parsing/color-invalid-rgb.html b/testing/web-platform/tests/css/css-color/parsing/color-invalid-rgb.html index 90dd082f2a..08d4c6735a 100644 --- a/testing/web-platform/tests/css/css-color/parsing/color-invalid-rgb.html +++ b/testing/web-platform/tests/css/css-color/parsing/color-invalid-rgb.html @@ -22,6 +22,8 @@ tests = [ ["rgb(10%, 50%, 0)", "Values must be all numbers or all percentages"], ["rgb(255, 50%, 0%)", "Values must be all numbers or all percentages"], ["rgb(0, 0 0)", "Comma optional syntax requires no commas at all"], + ["rgb(,0, 0, 0)", "Leading commas are invalid"], + ["rgb(0, 0,, 0)", "Double commas are invalid"], ["rgb(0, 0, 0deg)", "Angles are not accepted in the rgb function"], ["rgb(0, 0, light)", "Keywords are not accepted in the rgb function"], ["rgb()", "The rgb function requires 3 or 4 arguments"], diff --git a/testing/web-platform/tests/css/css-color/parsing/color-valid-color-mix-function.html b/testing/web-platform/tests/css/css-color/parsing/color-valid-color-mix-function.html index f11ecc8e47..cd1f381a1a 100644 --- a/testing/web-platform/tests/css/css-color/parsing/color-valid-color-mix-function.html +++ b/testing/web-platform/tests/css/css-color/parsing/color-valid-color-mix-function.html @@ -380,7 +380,7 @@ test_valid_value(`color`, `color-mix(in oklab, oklab(0.1 0.2 0.3 / none), oklab(0.5 0.6 0.7 / 0.5))`, `color-mix(in oklab, oklab(0.1 0.2 0.3 / none), oklab(0.5 0.6 0.7 / 0.5))`); test_valid_value(`color`, `color-mix(in oklab, oklab(0.1 0.2 0.3 / none), oklab(0.5 0.6 0.7 / none))`, `color-mix(in oklab, oklab(0.1 0.2 0.3 / none), oklab(0.5 0.6 0.7 / none))`); - for (const colorSpace of [ "srgb", "srgb-linear", "xyz", "xyz-d50", "xyz-d65" ]) { + for (const colorSpace of [ "srgb", "srgb-linear", "display-p3", "a98-rgb", "prophoto-rgb", "rec2020", "xyz", "xyz-d50", "xyz-d65" ]) { const resultColorSpace = colorSpace == "xyz" ? "xyz-d65" : colorSpace; test_valid_value(`color`, `color-mix(in ${colorSpace}, color(${colorSpace} .1 .2 .3), color(${colorSpace} .5 .6 .7))`, `color-mix(in ${resultColorSpace}, color(${resultColorSpace} 0.1 0.2 0.3), color(${resultColorSpace} 0.5 0.6 0.7))`); diff --git a/testing/web-platform/tests/css/css-color/parsing/color-valid-relative-color.html b/testing/web-platform/tests/css/css-color/parsing/color-valid-relative-color.html index 5f83f0f074..eb730b51c5 100644 --- a/testing/web-platform/tests/css/css-color/parsing/color-valid-relative-color.html +++ b/testing/web-platform/tests/css/css-color/parsing/color-valid-relative-color.html @@ -24,185 +24,189 @@ <body> <script> // rgb(from ...) + for (const rgbFunction of ["rgb", "rgba"]) { - // Testing no modifications. - fuzzy_test_valid_color(`rgb(from rebeccapurple r g b)`, `color(srgb 0.4 0.2 0.6)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r g b / alpha)`, `color(srgb 0.4 0.2 0.6)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r g b / alpha)`, `color(srgb 0.2 0.4 0.6 / 0.8)`); - fuzzy_test_valid_color(`rgb(from hsl(120deg 20% 50% / .5) r g b / alpha)`, `color(srgb 0.4 0.6 0.4 / 0.5)`); + // Testing no modifications. + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r g b)`, `color(srgb 0.4 0.2 0.6)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r g b / alpha)`, `color(srgb 0.4 0.2 0.6)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r g b / alpha)`, `color(srgb 0.2 0.4 0.6 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from hsl(120deg 20% 50% / .5) r g b / alpha)`, `color(srgb 0.4 0.6 0.4 / 0.5)`); - // Test nesting relative colors. - fuzzy_test_valid_color(`rgb(from rgb(from rebeccapurple r g b) r g b)`, `color(srgb 0.4 0.2 0.6)`); + // Test nesting relative colors. + fuzzy_test_valid_color(`${rgbFunction}(from rgb(from rebeccapurple r g b) r g b)`, `color(srgb 0.4 0.2 0.6)`); - // Testing replacement with 0. - fuzzy_test_valid_color(`rgb(from rebeccapurple 0 0 0)`, `color(srgb 0 0 0)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple 0 0 0 / 0)`, `color(srgb 0 0 0 / 0)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple 0 g b / alpha)`, `color(srgb 0 0.2 0.6)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r 0 b / alpha)`, `color(srgb 0.4 0 0.6)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r g 0 / alpha)`, `color(srgb 0.4 0.2 0)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r g b / 0)`, `color(srgb 0.4 0.2 0.6 / 0)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) 0 g b / alpha)`, `color(srgb 0 0.4 0.6 / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r 0 b / alpha)`, `color(srgb 0.2 0 0.6 / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r g 0 / alpha)`, `color(srgb 0.2 0.4 0 / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r g b / 0)`, `color(srgb 0.2 0.4 0.6 / 0)`); - - // Testing replacement with a number. - fuzzy_test_valid_color(`rgb(from rebeccapurple 25 g b / alpha)`, `color(srgb 0.098 0.2 0.6)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r 25 b / alpha)`, `color(srgb 0.4 0.098 0.6)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r g 25 / alpha)`, `color(srgb 0.4 0.2 0.098)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r g b / .25)`, `color(srgb 0.4 0.2 0.6 / 0.25)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) 25 g b / alpha)`, `color(srgb 0.098 0.4 0.6 / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r 25 b / alpha)`, `color(srgb 0.2 0.098 0.6 / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r g 25 / alpha)`, `color(srgb 0.2 0.4 0.098 / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r g b / .20)`, `color(srgb 0.2 0.4 0.6 / 0.2)`); - - // Testing replacement with a percentage. - fuzzy_test_valid_color(`rgb(from rebeccapurple 20% g b / alpha)`, `color(srgb 0.2 0.2 0.6)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r 20% b / alpha)`, `color(srgb 0.4 0.2 0.6)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r g 20% / alpha)`, `color(srgb 0.4 0.2 0.2)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r g b / 20%)`, `color(srgb 0.4 0.2 0.6 / 0.2)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) 20% g b / alpha)`, `color(srgb 0.2 0.4 0.6 / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r 20% b / alpha)`, `color(srgb 0.2 0.2 0.6 / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r g 20% / alpha)`, `color(srgb 0.2 0.4 0.2 / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r g b / 20%)`, `color(srgb 0.2 0.4 0.6 / 0.2)`); - - // Testing replacement with a number for r, g, b but percent for alpha. - fuzzy_test_valid_color(`rgb(from rebeccapurple 25 g b / 25%)`, `color(srgb 0.098 0.2 0.6 / 0.25)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r 25 b / 25%)`, `color(srgb 0.4 0.098 0.6 / 0.25)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r g 25 / 25%)`, `color(srgb 0.4 0.2 0.098 / 0.25)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) 25 g b / 25%)`, `color(srgb 0.098 0.4 0.6 / 0.25)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r 25 b / 25%)`, `color(srgb 0.2 0.098 0.6 / 0.25)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r g 25 / 25%)`, `color(srgb 0.2 0.4 0.098 / 0.25)`); - - // Testing permutation. - fuzzy_test_valid_color(`rgb(from rebeccapurple g b r)`, `color(srgb 0.2 0.6 0.4)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple b alpha r / g)`, `color(srgb 0.6 1 0.4 / 0.2)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r r r / r)`, `color(srgb 0.4 0.4 0.4 / 0.4)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple alpha alpha alpha / alpha)`, `color(srgb 1 1 1)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) g b r)`, `color(srgb 0.4 0.6 0.2 / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) b alpha r / g)`, `color(srgb 0.6 0.8 0.2 / 0.4)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r r r / r)`, `color(srgb 0.2 0.2 0.2 / 0.2)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) alpha alpha alpha / alpha)`, `color(srgb 0.8 0.8 0.8 / 0.8)`); - - // Testing mixes of number and percentage. (These would not be allowed in the non-relative syntax). - fuzzy_test_valid_color(`rgb(from rebeccapurple r 20% 10)`, `color(srgb 0.4 0.2 0.0392)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r 10 20%)`, `color(srgb 0.4 0.0392 0.2)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple 0% 10 10)`, `color(srgb 0 0.0392 0.0392)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r 20% 10)`, `color(srgb 0.2 0.2 0.0392 / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) r 10 20%)`, `color(srgb 0.2 0.0392 0.2 / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) 0% 10 10)`, `color(srgb 0 0.0392 0.0392 / 0.8)`); - - // r g b - // 102 51 153 - // 40% 20% 60% - // Testing with calc(). - fuzzy_test_valid_color(`rgb(from rebeccapurple calc(r) calc(g) calc(b))`, `color(srgb 0.4 0.2 0.6)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r calc(g * 2) 10)`, `color(srgb 0.4 0.4 0.0392)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple b calc(r * .5) 10)`, `color(srgb 0.6 0.2 0.0392)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r calc(g * .5 + g * .5) 10)`, `color(srgb 0.4 0.2 0.0392)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r calc(b * .5 - g * .5) 10)`, `color(srgb 0.4 0.2 0.0392)`); - fuzzy_test_valid_color(`rgb(from rgb(20%, 40%, 60%, 80%) calc(r) calc(g) calc(b) / calc(alpha))`, `color(srgb 0.2 0.4 0.6 / 0.8)`); + // Testing replacement with 0. + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple 0 0 0)`, `color(srgb 0 0 0)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple 0 0 0 / 0)`, `color(srgb 0 0 0 / 0)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple 0 g b / alpha)`, `color(srgb 0 0.2 0.6)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r 0 b / alpha)`, `color(srgb 0.4 0 0.6)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r g 0 / alpha)`, `color(srgb 0.4 0.2 0)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r g b / 0)`, `color(srgb 0.4 0.2 0.6 / 0)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) 0 g b / alpha)`, `color(srgb 0 0.4 0.6 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r 0 b / alpha)`, `color(srgb 0.2 0 0.6 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r g 0 / alpha)`, `color(srgb 0.2 0.4 0 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r g b / 0)`, `color(srgb 0.2 0.4 0.6 / 0)`); + + // Testing replacement with a number. + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple 25 g b / alpha)`, `color(srgb 0.098 0.2 0.6)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r 25 b / alpha)`, `color(srgb 0.4 0.098 0.6)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r g 25 / alpha)`, `color(srgb 0.4 0.2 0.098)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r g b / .25)`, `color(srgb 0.4 0.2 0.6 / 0.25)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) 25 g b / alpha)`, `color(srgb 0.098 0.4 0.6 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r 25 b / alpha)`, `color(srgb 0.2 0.098 0.6 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r g 25 / alpha)`, `color(srgb 0.2 0.4 0.098 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r g b / .20)`, `color(srgb 0.2 0.4 0.6 / 0.2)`); + + // Testing replacement with a percentage. + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple 20% g b / alpha)`, `color(srgb 0.2 0.2 0.6)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r 20% b / alpha)`, `color(srgb 0.4 0.2 0.6)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r g 20% / alpha)`, `color(srgb 0.4 0.2 0.2)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r g b / 20%)`, `color(srgb 0.4 0.2 0.6 / 0.2)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) 20% g b / alpha)`, `color(srgb 0.2 0.4 0.6 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r 20% b / alpha)`, `color(srgb 0.2 0.2 0.6 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r g 20% / alpha)`, `color(srgb 0.2 0.4 0.2 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r g b / 20%)`, `color(srgb 0.2 0.4 0.6 / 0.2)`); + + // Testing replacement with a number for r, g, b but percent for alpha. + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple 25 g b / 25%)`, `color(srgb 0.098 0.2 0.6 / 0.25)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r 25 b / 25%)`, `color(srgb 0.4 0.098 0.6 / 0.25)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r g 25 / 25%)`, `color(srgb 0.4 0.2 0.098 / 0.25)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) 25 g b / 25%)`, `color(srgb 0.098 0.4 0.6 / 0.25)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r 25 b / 25%)`, `color(srgb 0.2 0.098 0.6 / 0.25)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r g 25 / 25%)`, `color(srgb 0.2 0.4 0.098 / 0.25)`); + + // Testing permutation. + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple g b r)`, `color(srgb 0.2 0.6 0.4)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple b alpha r / g)`, `color(srgb 0.6 0.004 0.4)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r r r / r)`, `color(srgb 0.4 0.4 0.4)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple alpha alpha alpha / alpha)`, `color(srgb 0.004 0.004 0.004)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) g b r)`, `color(srgb 0.4 0.6 0.2 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) b alpha r / g)`, `color(srgb 0.6 0.003 0.2)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r r r / r)`, `color(srgb 0.2 0.2 0.2)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) alpha alpha alpha / alpha)`, `color(srgb 0.003 0.003 0.003 / 0.8)`); + + // Testing mixes of number and percentage. (These would not be allowed in the non-relative syntax). + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r 20% 10)`, `color(srgb 0.4 0.2 0.0392)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r 10 20%)`, `color(srgb 0.4 0.0392 0.2)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple 0% 10 10)`, `color(srgb 0 0.0392 0.0392)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r 20% 10)`, `color(srgb 0.2 0.2 0.0392 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) r 10 20%)`, `color(srgb 0.2 0.0392 0.2 / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) 0% 10 10)`, `color(srgb 0 0.0392 0.0392 / 0.8)`); + + // r g b + // 102 51 153 + // 40% 20% 60% + // Testing with calc(). + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple calc(r) calc(g) calc(b))`, `color(srgb 0.4 0.2 0.6)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r calc(g * 2) 10)`, `color(srgb 0.4 0.4 0.0392)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple b calc(r * .5) 10)`, `color(srgb 0.6 0.2 0.0392)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r calc(g * .5 + g * .5) 10)`, `color(srgb 0.4 0.2 0.0392)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r calc(b * .5 - g * .5) 10)`, `color(srgb 0.4 0.2 0.0392)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20%, 40%, 60%, 80%) calc(r) calc(g) calc(b) / calc(alpha))`, `color(srgb 0.2 0.4 0.6 / 0.8)`); - // Testing with 'none'. - fuzzy_test_valid_color(`rgb(from rebeccapurple none none none)`, `color(srgb none none none)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple none none none / none)`, `color(srgb none none none / none)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r g none)`, `color(srgb 0.4 0.2 none)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r g none / alpha)`, `color(srgb 0.4 0.2 none)`); - fuzzy_test_valid_color(`rgb(from rebeccapurple r g b / none)`, `color(srgb 0.4 0.2 0.6 / none)`); - fuzzy_test_valid_color(`rgb(from rgb(20% 40% 60% / 80%) r g none / alpha)`, `color(srgb 0.2 0.4 none / 0.8)`); - fuzzy_test_valid_color(`rgb(from rgb(20% 40% 60% / 80%) r g b / none)`, `color(srgb 0.2 0.4 0.6 / none)`); - // FIXME: Clarify with spec editors if 'none' should pass through to the constants. - fuzzy_test_valid_color(`rgb(from rgb(none none none) r g b)`, `color(srgb 0 0 0)`); - fuzzy_test_valid_color(`rgb(from rgb(none none none / none) r g b / alpha)`, `color(srgb 0 0 0 / 0)`); - fuzzy_test_valid_color(`rgb(from rgb(20% none 60%) r g b)`, `color(srgb 0.2 0 0.6)`); - fuzzy_test_valid_color(`rgb(from rgb(20% 40% 60% / none) r g b / alpha)`, `color(srgb 0.2 0.4 0.6 / 0)`); + // Testing with 'none'. + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple none none none)`, `color(srgb none none none)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple none none none / none)`, `color(srgb none none none / none)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r g none)`, `color(srgb 0.4 0.2 none)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r g none / alpha)`, `color(srgb 0.4 0.2 none)`); + fuzzy_test_valid_color(`${rgbFunction}(from rebeccapurple r g b / none)`, `color(srgb 0.4 0.2 0.6 / none)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20% 40% 60% / 80%) r g none / alpha)`, `color(srgb 0.2 0.4 none / 0.8)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20% 40% 60% / 80%) r g b / none)`, `color(srgb 0.2 0.4 0.6 / none)`); + // FIXME: Clarify with spec editors if 'none' should pass through to the constants. + fuzzy_test_valid_color(`${rgbFunction}(from rgb(none none none) r g b)`, `color(srgb 0 0 0)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(none none none / none) r g b / alpha)`, `color(srgb 0 0 0 / 0)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20% none 60%) r g b)`, `color(srgb 0.2 0 0.6)`); + fuzzy_test_valid_color(`${rgbFunction}(from rgb(20% 40% 60% / none) r g b / alpha)`, `color(srgb 0.2 0.4 0.6 / 0)`); - // Testing with 'currentColor' - fuzzy_test_valid_color(`rgb(from currentColor r g b)`, `rgb(from currentColor r g b)`); + // Testing with 'currentColor' + fuzzy_test_valid_color(`${rgbFunction}(from currentColor r g b)`, `rgb(from currentColor r g b)`); - // color-mix - fuzzy_test_valid_color(`rgb(from color-mix(in srgb, red, red) r g b / alpha)`, `color(srgb 1 0 0)`); + // color-mix + fuzzy_test_valid_color(`${rgbFunction}(from color-mix(in srgb, red, red) r g b / alpha)`, `color(srgb 1 0 0)`); + } // hsl(from ...) + for (const hslFunction of ["hsl", "hsla"]) { - // Testing no modifications. - fuzzy_test_valid_color(`hsl(from rebeccapurple h s l)`, `color(srgb 0.4 0.2 0.6)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple h s l / alpha)`, `color(srgb 0.4 0.2 0.6)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) h s l / alpha)`, `color(srgb 0.2 0.4 0.6 / 0.8)`); - fuzzy_test_valid_color(`hsl(from hsl(120deg 20% 50% / .5) h s l / alpha)`, `color(srgb 0.4 0.6 0.4 / 0.5)`); + // Testing no modifications. + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h s l)`, `color(srgb 0.4 0.2 0.6)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h s l / alpha)`, `color(srgb 0.4 0.2 0.6)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) h s l / alpha)`, `color(srgb 0.2 0.4 0.6 / 0.8)`); + fuzzy_test_valid_color(`${hslFunction}(from hsl(120deg 20% 50% / .5) h s l / alpha)`, `color(srgb 0.4 0.6 0.4 / 0.5)`); - // Test nesting relative colors. - fuzzy_test_valid_color(`hsl(from hsl(from rebeccapurple h s l) h s l)`, `color(srgb 0.4 0.2 0.6)`); + // Test nesting relative colors. + fuzzy_test_valid_color(`${hslFunction}(from hsl(from rebeccapurple h s l) h s l)`, `color(srgb 0.4 0.2 0.6)`); - // Testing replacement with 0. - fuzzy_test_valid_color(`hsl(from rebeccapurple 0 0% 0%)`, `color(srgb 0 0 0)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple 0deg 0% 0%)`, `color(srgb 0 0 0)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple 0 0% 0% / 0)`, `color(srgb 0 0 0 / 0)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple 0deg 0% 0% / 0)`, `color(srgb 0 0 0 / 0)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple 0 s l / alpha)`, `color(srgb 0.6 0.2 0.2)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple 0deg s l / alpha)`, `color(srgb 0.6 0.2 0.2)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple h 0% l / alpha)`, `color(srgb 0.4 0.4 0.4)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple h s 0% / alpha)`, `color(srgb 0 0 0)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple h s l / 0)`, `color(srgb 0.4 0.2 0.6 / 0)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) 0 s l / alpha)`, `color(srgb 0.6 0.2 0.2 / 0.8)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) 0deg s l / alpha)`, `color(srgb 0.6 0.2 0.2 / 0.8)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) h 0% l / alpha)`, `color(srgb 0.4 0.4 0.4 / 0.8)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) h s 0% / alpha)`, `color(srgb 0 0 0 / 0.8)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) h s l / 0)`, `color(srgb 0.2 0.4 0.6 / 0)`); - - fuzzy_test_valid_color(`hsl(from rebeccapurple 25 s l / alpha)`, `color(srgb 0.6 0.3667 0.2)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple 25deg s l / alpha)`, `color(srgb 0.6 0.3667 0.2)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple h 20% l / alpha)`, `color(srgb 0.4 0.32 0.48)`); - // hsl(from rebeccapurple h s 20% / alpha) is equivalent to color(srgb 0.2 0.1 0.3). - // For the green channel: 0.1 * 255 = 25.5. This should get rounded towards infinity to 26. - // https://www.w3.org/TR/css-color-4/#rgb-functions - fuzzy_test_valid_color(`hsl(from rebeccapurple h s 20% / alpha)`, `color(srgb 0.2 0.1 0.3)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple h s l / .25)`, `color(srgb 0.4 0.2 0.6 / 0.25)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) 25 s l / alpha)`, `color(srgb 0.6 0.3667 0.2 / 0.8)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) 25deg s l / alpha)`, `color(srgb 0.6 0.3667 0.2 / 0.8)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) h 20% l / alpha)`, `color(srgb 0.32 0.4 0.48 / 0.8)`); - // hsl(from rgb(20%, 40%, 60%, 80%) h s 20% / alpha) is equivalent to color(srgb 0.1 0.2 0.3). - // For the red channel: 0.1 * 255 = 25.5. This should get rounded towards infinity to 26. - // https://www.w3.org/TR/css-color-4/#rgb-functions - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) h s 20% / alpha)`, `color(srgb 0.1 0.2 0.3 / 0.8)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) h s l / .2)`, `color(srgb 0.2 0.4 0.6 / 0.2)`); + // Testing replacement with 0. + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple 0 0% 0%)`, `color(srgb 0 0 0)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple 0deg 0% 0%)`, `color(srgb 0 0 0)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple 0 0% 0% / 0)`, `color(srgb 0 0 0 / 0)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple 0deg 0% 0% / 0)`, `color(srgb 0 0 0 / 0)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple 0 s l / alpha)`, `color(srgb 0.6 0.2 0.2)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple 0deg s l / alpha)`, `color(srgb 0.6 0.2 0.2)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h 0% l / alpha)`, `color(srgb 0.4 0.4 0.4)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h s 0% / alpha)`, `color(srgb 0 0 0)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h s l / 0)`, `color(srgb 0.4 0.2 0.6 / 0)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) 0 s l / alpha)`, `color(srgb 0.6 0.2 0.2 / 0.8)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) 0deg s l / alpha)`, `color(srgb 0.6 0.2 0.2 / 0.8)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) h 0% l / alpha)`, `color(srgb 0.4 0.4 0.4 / 0.8)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) h s 0% / alpha)`, `color(srgb 0 0 0 / 0.8)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) h s l / 0)`, `color(srgb 0.2 0.4 0.6 / 0)`); + + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple 25 s l / alpha)`, `color(srgb 0.6 0.3667 0.2)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple 25deg s l / alpha)`, `color(srgb 0.6 0.3667 0.2)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h 20% l / alpha)`, `color(srgb 0.4 0.32 0.48)`); + // hsl(from rebeccapurple h s 20% / alpha) is equivalent to color(srgb 0.2 0.1 0.3). + // For the green channel: 0.1 * 255 = 25.5. This should get rounded towards infinity to 26. + // https://www.w3.org/TR/css-color-4/#rgb-functions + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h s 20% / alpha)`, `color(srgb 0.2 0.1 0.3)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h s l / .25)`, `color(srgb 0.4 0.2 0.6 / 0.25)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) 25 s l / alpha)`, `color(srgb 0.6 0.3667 0.2 / 0.8)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) 25deg s l / alpha)`, `color(srgb 0.6 0.3667 0.2 / 0.8)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) h 20% l / alpha)`, `color(srgb 0.32 0.4 0.48 / 0.8)`); + // hsl(from rgb(20%, 40%, 60%, 80%) h s 20% / alpha) is equivalent to color(srgb 0.1 0.2 0.3). + // For the red channel: 0.1 * 255 = 25.5. This should get rounded towards infinity to 26. + // https://www.w3.org/TR/css-color-4/#rgb-functions + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) h s 20% / alpha)`, `color(srgb 0.1 0.2 0.3 / 0.8)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) h s l / .2)`, `color(srgb 0.2 0.4 0.6 / 0.2)`); - // Testing valid permutation (types match). - fuzzy_test_valid_color(`hsl(from rebeccapurple h l s)`, `color(srgb 0.5 0.3 0.7)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple h alpha l / s)`, `color(srgb 0.4 0 0.8 / 0.5)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple h l l / l)`, `color(srgb 0.4 0.24 0.56 / 0.4)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple h alpha alpha / alpha)`, `color(srgb 1 1 1)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) h l s)`, `color(srgb 0.3 0.5 0.7 / 0.8)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) h alpha l / s)`, `color(srgb 0.08 0.4 0.72 / 0.5)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) h l l / l)`, `color(srgb 0.24 0.4 0.56 / 0.4)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)`, `color(srgb 0.64 0.8 0.96 / 0.8)`); + // Testing valid permutation (types match). + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h l s)`, `color(srgb 0.5 0.3 0.7)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h alpha l / s)`, `color(srgb 0.4 0.396 0.404)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h l l / l)`, `color(srgb 0.4 0.24 0.56)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h alpha alpha / alpha)`, `color(srgb 0.01 0.01 0.01)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) h l s)`, `color(srgb 0.3 0.5 0.7 / 0.8)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) h alpha l / s)`, `color(srgb 0.4 0.4 0.4)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) h l l / l)`, `color(srgb 0.24 0.4 0.56)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)`, `color(srgb 0.01 0.01 0.01 / 0.8)`); - // Testing with calc(). - fuzzy_test_valid_color(`hsl(from rebeccapurple calc(h) calc(s) calc(l))`, `color(srgb 0.4 0.2 0.6)`); - fuzzy_test_valid_color(`hsl(from rgb(20%, 40%, 60%, 80%) calc(h) calc(s) calc(l) / calc(alpha))`, `color(srgb 0.2 0.4 0.6 / 0.8)`); + // Testing with calc(). + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple calc(h) calc(s) calc(l))`, `color(srgb 0.4 0.2 0.6)`); + fuzzy_test_valid_color(`${hslFunction}(from rgb(20%, 40%, 60%, 80%) calc(h) calc(s) calc(l) / calc(alpha))`, `color(srgb 0.2 0.4 0.6 / 0.8)`); - // Testing with 'none'. - fuzzy_test_valid_color(`hsl(from rebeccapurple none none none)`, `color(srgb none none none)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple none none none / none)`, `color(srgb none none none / none)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple h s none)`, `color(srgb 0 0 none)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple h s none / alpha)`, `color(srgb 0 0 none)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple h s l / none)`, `color(srgb 0.4 0.2 0.6 / none)`); - fuzzy_test_valid_color(`hsl(from rebeccapurple none s l / alpha)`, `color(srgb none 0.2 0.2)`); - fuzzy_test_valid_color(`hsl(from hsl(120deg 20% 50% / .5) h s none / alpha)`, `color(srgb 0 0 none / 0.5)`); - fuzzy_test_valid_color(`hsl(from hsl(120deg 20% 50% / .5) h s l / none)`, `color(srgb 0.4 0.6 0.4 / none)`); - fuzzy_test_valid_color(`hsl(from hsl(120deg 20% 50% / .5) none s l / alpha)`, `color(srgb none 0.4 0.4 / 0.5)`); - // FIXME: Clarify with spec editors if 'none' should pass through to the constants. - fuzzy_test_valid_color(`hsl(from hsl(none none none) h s l)`, `color(srgb 0 0 0)`); - fuzzy_test_valid_color(`hsl(from hsl(none none none / none) h s l / alpha)`, `color(srgb 0 0 0 / 0)`); - fuzzy_test_valid_color(`hsl(from hsl(120deg none 50% / .5) h s l)`, `color(srgb 0.5 0.5 0.5 / 0.5)`); - fuzzy_test_valid_color(`hsl(from hsl(120deg 20% 50% / none) h s l / alpha)`, `color(srgb 0.4 0.6 0.4 / 0)`); - fuzzy_test_valid_color(`hsl(from hsl(none 20% 50% / .5) h s l / alpha)`, `color(srgb 0.6 0.4 0.4 / 0.5)`); + // Testing with 'none'. + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple none none none)`, `color(srgb 0 0 0)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple none none none / none)`, `color(srgb 0 0 0 / 0)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h s none)`, `color(srgb 0 0 0)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h s none / alpha)`, `color(srgb 0 0 0)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple h s l / none)`, `color(srgb 0.4 0.2 0.6 / none)`); + fuzzy_test_valid_color(`${hslFunction}(from rebeccapurple none s l / alpha)`, `color(srgb 0.6 0.2 0.2)`); + fuzzy_test_valid_color(`${hslFunction}(from hsl(120deg 20% 50% / .5) h s none / alpha)`, `color(srgb 0 0 0 / 0.5)`); + fuzzy_test_valid_color(`${hslFunction}(from hsl(120deg 20% 50% / .5) h s l / none)`, `color(srgb 0.4 0.6 0.4 / none)`); + fuzzy_test_valid_color(`${hslFunction}(from hsl(120deg 20% 50% / .5) none s l / alpha)`, `color(srgb 0.6 0.4 0.4 / 0.5)`); + // FIXME: Clarify with spec editors if 'none' should pass through to the constants. + fuzzy_test_valid_color(`${hslFunction}(from hsl(none none none) h s l)`, `color(srgb 0 0 0)`); + fuzzy_test_valid_color(`${hslFunction}(from hsl(none none none / none) h s l / alpha)`, `color(srgb 0 0 0 / 0)`); + fuzzy_test_valid_color(`${hslFunction}(from hsl(120deg none 50% / .5) h s l)`, `color(srgb 0.5 0.5 0.5 / 0.5)`); + fuzzy_test_valid_color(`${hslFunction}(from hsl(120deg 20% 50% / none) h s l / alpha)`, `color(srgb 0.4 0.6 0.4 / 0)`); + fuzzy_test_valid_color(`${hslFunction}(from hsl(none 20% 50% / .5) h s l / alpha)`, `color(srgb 0.6 0.4 0.4 / 0.5)`); - // Testing with 'currentColor' - fuzzy_test_valid_color(`hsl(from currentColor h s l)`, `hsl(from currentColor h s l)`); + // Testing with 'currentColor' + fuzzy_test_valid_color(`${hslFunction}(from currentColor h s l)`, `hsl(from currentColor h s l)`); - // color-mix - fuzzy_test_valid_color(`hsl(from color-mix(in srgb, red, red) h s l / alpha)`, `color(srgb 1 0 0)`); + // color-mix + fuzzy_test_valid_color(`${hslFunction}(from color-mix(in srgb, red, red) h s l / alpha)`, `color(srgb 1 0 0)`); + } // hwb(from ...) @@ -245,28 +249,28 @@ // Testing valid permutation (types match). fuzzy_test_valid_color(`hwb(from rebeccapurple h b w)`, `color(srgb 0.6 0.4 0.8)`); - fuzzy_test_valid_color(`hwb(from rebeccapurple h alpha w / b)`, `color(srgb 0.8333 0.8333 0.8333 / 0.4)`); - fuzzy_test_valid_color(`hwb(from rebeccapurple h w w / w)`, `color(srgb 0.5 0.2 0.8 / 0.2)`); - fuzzy_test_valid_color(`hwb(from rebeccapurple h alpha alpha / alpha)`, `color(srgb 0.5 0.5 0.5)`); + fuzzy_test_valid_color(`hwb(from rebeccapurple h alpha w / b)`, `color(srgb 0.405 0.01 0.8)`); + fuzzy_test_valid_color(`hwb(from rebeccapurple h w w / w)`, `color(srgb 0.5 0.2 0.8)`); + fuzzy_test_valid_color(`hwb(from rebeccapurple h alpha alpha / alpha)`, `color(srgb 0.5 0.01 0.99)`); fuzzy_test_valid_color(`hwb(from rgb(20%, 40%, 60%, 80%) h b w)`, `color(srgb 0.4 0.6 0.8 / 0.8)`); - fuzzy_test_valid_color(`hwb(from rgb(20%, 40%, 60%, 80%) h alpha w / b)`, `color(srgb 0.8 0.8 0.8 / 0.4)`); - fuzzy_test_valid_color(`hwb(from rgb(20%, 40%, 60%, 80%) h w w / w)`, `color(srgb 0.2 0.5 0.8 / 0.2)`); - fuzzy_test_valid_color(`hwb(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)`, `color(srgb 0.5 0.5 0.5 / 0.8)`); + fuzzy_test_valid_color(`hwb(from rgb(20%, 40%, 60%, 80%) h alpha w / b)`, `color(srgb 0.01 0.404 0.8)`); + fuzzy_test_valid_color(`hwb(from rgb(20%, 40%, 60%, 80%) h w w / w)`, `color(srgb 0.2 0.5 0.8)`); + fuzzy_test_valid_color(`hwb(from rgb(20%, 40%, 60%, 80%) h alpha alpha / alpha)`, `color(srgb 0.01 0.5 0.99 / 0.8)`); // Testing with calc(). fuzzy_test_valid_color(`hwb(from rebeccapurple calc(h) calc(w) calc(b))`, `color(srgb 0.4 0.2 0.6)`); fuzzy_test_valid_color(`hwb(from rgb(20%, 40%, 60%, 80%) calc(h) calc(w) calc(b) / calc(alpha))`, `color(srgb 0.2 0.4 0.6 / 0.8)`); // Testing with 'none'. - fuzzy_test_valid_color(`hwb(from rebeccapurple none none none)`, `color(srgb none none none)`); - fuzzy_test_valid_color(`hwb(from rebeccapurple none none none / none)`, `color(srgb none none none / none)`); - fuzzy_test_valid_color(`hwb(from rebeccapurple h w none)`, `color(srgb 0.6 0.2 none)`); - fuzzy_test_valid_color(`hwb(from rebeccapurple h w none / alpha)`, `color(srgb 0.6 0.2 none)`); + fuzzy_test_valid_color(`hwb(from rebeccapurple none none none)`, `color(srgb 1 0 0)`); + fuzzy_test_valid_color(`hwb(from rebeccapurple none none none / none)`, `color(srgb 1 0 0 / none)`); + fuzzy_test_valid_color(`hwb(from rebeccapurple h w none)`, `color(srgb 0.6 0.2 1)`); + fuzzy_test_valid_color(`hwb(from rebeccapurple h w none / alpha)`, `color(srgb 0.6 0.2 1)`); fuzzy_test_valid_color(`hwb(from rebeccapurple h w b / none)`, `color(srgb 0.4 0.2 0.6 / none)`); - fuzzy_test_valid_color(`hwb(from rebeccapurple none w b / alpha)`, `color(srgb none 0.2 0.2)`); - fuzzy_test_valid_color(`hwb(from hwb(120deg 20% 50% / .5) h w none / alpha)`, `color(srgb 0.2 1 none / 0.5)`); + fuzzy_test_valid_color(`hwb(from rebeccapurple none w b / alpha)`, `color(srgb 0.6 0.2 0.2)`); + fuzzy_test_valid_color(`hwb(from hwb(120deg 20% 50% / .5) h w none / alpha)`, `color(srgb 0.2 1 0.2 / 0.5)`); fuzzy_test_valid_color(`hwb(from hwb(120deg 20% 50% / .5) h w b / none)`, `color(srgb 0.2 0.5 0.2 / none)`); - fuzzy_test_valid_color(`hwb(from hwb(120deg 20% 50% / .5) none w b / alpha)`, `color(srgb none 0.2 0.2 / 0.5)`); + fuzzy_test_valid_color(`hwb(from hwb(120deg 20% 50% / .5) none w b / alpha)`, `color(srgb 0.5 0.2 0.2 / 0.5)`); // FIXME: Clarify with spec editors if 'none' should pass through to the constants. fuzzy_test_valid_color(`hwb(from hwb(none none none) h w b)`, `color(srgb 1 0 0)`); fuzzy_test_valid_color(`hwb(from hwb(none none none / none) h w b / alpha)`, `color(srgb 1 0 0 / 0)`); @@ -400,7 +404,7 @@ fuzzy_test_valid_color(`oklab(from oklab(0.25 0.2 0.5) calc(l) calc(a) calc(b))`, `oklab(0.25 0.2 0.5)`); fuzzy_test_valid_color(`oklab(from oklab(0.25 0.2 0.5 / 40%) calc(l) calc(a) calc(b) / calc(alpha))`, `oklab(0.25 0.2 0.5 / 0.4)`); fuzzy_test_valid_color(`oklab(from oklab(0.7 0.25 -0.15) calc(l - 0.2) a b)`, `oklab(0.5 0.25 -0.15)`); - fuzzy_test_valid_color(`oklab(from oklab(0.7 0.25 -0.15) l calc(a / 2) calc(b / 3))`, `oklab(0.7 0.125 -0.075)`); + fuzzy_test_valid_color(`oklab(from oklab(0.7 0.25 -0.15) l calc(a / 2) calc(b / 3))`, `oklab(0.7 0.125 -0.05)`); // Testing with 'none'. fuzzy_test_valid_color(`oklab(from oklab(0.25 0.2 0.5) none none none)`, `oklab(none none none)`); diff --git a/testing/web-platform/tests/css/css-contain/quote-scoping-shadow-dom-crash.html b/testing/web-platform/tests/css/css-contain/quote-scoping-shadow-dom-crash.html new file mode 100644 index 0000000000..dd67e61952 --- /dev/null +++ b/testing/web-platform/tests/css/css-contain/quote-scoping-shadow-dom-crash.html @@ -0,0 +1,20 @@ +<!doctype html> +<link rel="help" href="http://crbug.com/329231572"> +<style> + #test { contain: style; } +</style> +<body> +<q id="test"></q> +<div> + <template shadowrootmode="open"> + <slot></slot> + </template> + <q id="test2"></q> +</div> +<script> + test.offsetTop; + test2.slot = "1"; + test2.appendChild(test); + test.offsetTop; +</script> +</body> diff --git a/testing/web-platform/tests/css/css-counter-styles/hebrew/counter-hebrew-nested-ref.html b/testing/web-platform/tests/css/css-counter-styles/hebrew/counter-hebrew-nested-ref.html index 7e725760e8..9ae6b5e011 100644 --- a/testing/web-platform/tests/css/css-counter-styles/hebrew/counter-hebrew-nested-ref.html +++ b/testing/web-platform/tests/css/css-counter-styles/hebrew/counter-hebrew-nested-ref.html @@ -12,54 +12,51 @@ <p></p> <div> <span>א.א</span> - <span>ב</span> - <span>ג</span> - <span>ד</span> - <span>ה</span> - <span>ו</span> - <span>ז</span> - <span>ח</span> - <span>ט</span> - <span>י</span> - <span>יא</span> - <span>יב</span> - <span>יג</span> - <span>יד</span> - <span>טו</span> - <span>טז</span> - <span>יז</span> - <span>יז.כ</span> - <span>יז.ל</span> - <span>יז.מ</span> - <span>יז.נ</span> - <span>יז.ס</span> - <span>יז.ע</span> - <span>יז.פ</span> - <span>יז.צ</span> - <span>יז.ק</span> - <span>יז.ר</span> - <span>יז.ש</span> - <span>יז.ת</span> - <span>יז.תק</span> - <span>יז.תר</span> - <span>יז.תש</span> - <span>יז.תת</span> - <span>יז.תתק</span> - <span>יז.א׳</span> - <span>יז.ב׳</span> - <span>יז.ג׳</span> - <span>יז.ד׳</span> - <span>יז.ה׳</span> - <span>יז.ו׳</span> - <span>יז.ז׳</span> - <span>יז.ח׳</span> - <span>יז.ט׳</span> - <span>יז.ט׳תתקצט</span> - <span>יז.תתקצט׳תתקצט</span> - <style> - #c18:before { counter-set: c 18; content: counter(c, hebrew); } - </style> - <span id="c18"></span> + <span>א.ב</span> + <span>א.ג</span> + <span>א.ד</span> + <span>א.ה</span> + <span>א.ו</span> + <span>א.ז</span> + <span>א.ח</span> + <span>א.ט</span> + <span>א.י</span> + <span>א.יא</span> + <span>א.יב</span> + <span>א.יג</span> + <span>א.יד</span> + <span>א.טו</span> + <span>א.טז</span> + <span>א.יז</span> + <span>א.כ</span> + <span>א.ל</span> + <span>א.מ</span> + <span>א.נ</span> + <span>א.ס</span> + <span>א.ע</span> + <span>א.פ</span> + <span>א.צ</span> + <span>א.ק</span> + <span>א.ר</span> + <span>א.ש</span> + <span>א.ת</span> + <span>א.תק</span> + <span>א.תר</span> + <span>א.תש</span> + <span>א.תת</span> + <span>א.תתק</span> + <span>א.א׳</span> + <span>א.ב׳</span> + <span>א.ג׳</span> + <span>א.ד׳</span> + <span>א.ה׳</span> + <span>א.ו׳</span> + <span>א.ז׳</span> + <span>א.ח׳</span> + <span>א.ט׳</span> + <span>א.ט׳תתקצט</span> + <span>א.תתקצט׳תתקצט</span> + <span>א.1000000</span> </div> </body> diff --git a/testing/web-platform/tests/css/css-flexbox/intrinsic-size/col-wrap-crash.html b/testing/web-platform/tests/css/css-flexbox/intrinsic-size/col-wrap-crash.html new file mode 100644 index 0000000000..09222296c9 --- /dev/null +++ b/testing/web-platform/tests/css/css-flexbox/intrinsic-size/col-wrap-crash.html @@ -0,0 +1,5 @@ +<!DOCTYPE html> +<link rel="help" href="https://crbug.com/331646698"> +<div style="display: flex; flex-direction: column; flex-wrap: wrap; width: min-content; align-items: baseline;"> + <div style="writing-mode: vertical-rl;">crash</div> +</div> diff --git a/testing/web-platform/tests/css/css-fonts/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-fonts/WEB_FEATURES.yml new file mode 100644 index 0000000000..5e69c923ab --- /dev/null +++ b/testing/web-platform/tests/css/css-fonts/WEB_FEATURES.yml @@ -0,0 +1,13 @@ +features: +- name: font-palette + files: + - font-palette.html + - font-palette-* + - palette-values-rule-* +- name: font-synthesis + files: + - font-synthesis-* +- name: font-variant-alternates + files: + - alternates-order.html + - font-variant-alternates-* diff --git a/testing/web-platform/tests/css/css-fonts/animations/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-fonts/animations/WEB_FEATURES.yml new file mode 100644 index 0000000000..f99e4bb4e0 --- /dev/null +++ b/testing/web-platform/tests/css/css-fonts/animations/WEB_FEATURES.yml @@ -0,0 +1,6 @@ +features: +- name: font-palette-animation + files: + - font-palette-animation-not-specified-endpoints.html + - font-palette-interpolation.html + - multiple-elements-font-palette-animation.html diff --git a/testing/web-platform/tests/css/css-fonts/parsing/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-fonts/parsing/WEB_FEATURES.yml new file mode 100644 index 0000000000..6a24d269b4 --- /dev/null +++ b/testing/web-platform/tests/css/css-fonts/parsing/WEB_FEATURES.yml @@ -0,0 +1,14 @@ +features: +- name: font-optical-sizing + files: + - font-optical-sizing-* +- name: font-palette + files: + - font-palette-* + - font-palette-values-* +- name: font-synthesis + files: + - font-synthesis-* +- name: font-variant-alternates + files: + - font-variant-alternates-* diff --git a/testing/web-platform/tests/css/css-fonts/variations/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-fonts/variations/WEB_FEATURES.yml new file mode 100644 index 0000000000..c035a0ee11 --- /dev/null +++ b/testing/web-platform/tests/css/css-fonts/variations/WEB_FEATURES.yml @@ -0,0 +1,4 @@ +features: +- name: font-optical-sizing + files: + - variable-opsz* diff --git a/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print-ref.tentative.html b/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print-ref.tentative.html new file mode 100644 index 0000000000..78464712c5 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print-ref.tentative.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<div style="height:40vh"> + Test passes if there is two purple boxes on both page 1 and page 2 in print mode. (Ctrl+P, with "print backgrounds" enabled) +</div> +<div style="display:grid; grid-template-columns:1fr 1fr;"> + <div style="contain:size; height:40vh;"> + <div style="height:40vh; width: 20vw; background:purple;"></div> + </div> + <div style="contain:size; height:40vh;"> + <div style="height:40vh; width: 20vw; background:purple;"></div> + </div> +</div> +<div style="display:grid; grid-template-columns:1fr 1fr; break-before:page;"> + <div style="contain:size; height:40vh;"> + <div style="height:40vh; width: 20vw; background:purple;"></div> + </div> + <div style="contain:size; height:40vh;"> + <div style="height:40vh; width: 20vw; background:purple;"></div> + </div> +</div> diff --git a/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print.tentative.html b/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print.tentative.html new file mode 100644 index 0000000000..01fd97528f --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/grid-fragmentation-between-rows-001-print.tentative.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<link rel="author" title="Psychpsyo" href="mailto:psychpsyo@gmail.com"> +<link rel="help" href="https://www.w3.org/TR/css-grid-1/#pagination"> +<link rel="match" href="./grid-fragmentation-between-rows-001-print-ref.tentative.html"> +<div style="height: 40vh"> + Test passes if there is two purple boxes on both page 1 and page 2 in print mode. (Ctrl+P, with "print backgrounds" enabled) +</div> +<div style="display: grid; grid-template-columns: 1fr 1fr;"> + <div style="contain:size; height:40vh;"> + <div style="height:40vh; width: 20vw; background:purple;"></div> + </div> + <div style="contain:size; height:40vh;"> + <div style="height:40vh; width: 20vw; background:purple;"></div> + </div> + <div style="contain:size; height:40vh;"> + <div style="height:40vh; width: 20vw; background:purple;"></div> + </div> + <div style="contain:size; height:40vh;"> + <div style="height:40vh; width: 20vw; background:purple;"></div> + </div> +</div> diff --git a/testing/web-platform/tests/css/css-grid/layout-algorithm/grid-fit-content-percentage.html b/testing/web-platform/tests/css/css-grid/layout-algorithm/grid-fit-content-percentage.html index ab55502487..892dbe40b4 100644 --- a/testing/web-platform/tests/css/css-grid/layout-algorithm/grid-fit-content-percentage.html +++ b/testing/web-platform/tests/css/css-grid/layout-algorithm/grid-fit-content-percentage.html @@ -36,26 +36,30 @@ function clamp(value, min, max) { } const minContent = 50; const maxContent = 100; -for (const percentage of [0, 50, 75, 100, 150]) { - const container = document.createElement("div"); - container.className = "container"; - document.body.appendChild(container); - const grid = document.createElement("div"); - grid.className = "grid"; - grid.style.gridTemplateColumns = `fit-content(${percentage}%)`; - container.appendChild(grid); - const item = document.createElement("div"); - item.className = "item"; - grid.appendChild(item); - test(function() { - const colSize = clamp(percentage * maxContent / 100, minContent, maxContent); - const height = colSize < maxContent ? maxContent : minContent; - assert_equals(item.offsetWidth, colSize, "Grid item width"); - assert_equals(item.offsetHeight, height, "Grid item height"); - assert_equals(grid.offsetWidth, maxContent, "Grid container width"); - assert_equals(grid.offsetHeight, height, "Grid container height"); - assert_equals(getComputedStyle(grid).gridTemplateColumns, colSize + "px", - "Grid column size"); - }, `fit-content(${percentage}%)`); +for (const use_calc of [false, true]) { + for (const percentage of [0, 50, 75, 100, 150]) { + const value = use_calc ? `fit-content(calc(0px + ${percentage}%))` + : `fit-content(${percentage}%)`; + const container = document.createElement("div"); + container.className = "container"; + document.body.appendChild(container); + const grid = document.createElement("div"); + grid.className = "grid"; + grid.style.gridTemplateColumns = value; + container.appendChild(grid); + const item = document.createElement("div"); + item.className = "item"; + grid.appendChild(item); + test(function() { + const colSize = clamp(percentage * maxContent / 100, minContent, maxContent); + const height = colSize < maxContent ? maxContent : minContent; + assert_equals(item.offsetWidth, colSize, "Grid item width"); + assert_equals(item.offsetHeight, height, "Grid item height"); + assert_equals(grid.offsetWidth, maxContent, "Grid container width"); + assert_equals(grid.offsetHeight, height, "Grid container height"); + assert_equals(getComputedStyle(grid).gridTemplateColumns, colSize + "px", + "Grid column size"); + }, value); + } } </script> diff --git a/testing/web-platform/tests/css/css-grid/subgrid/placement-invalidation-001.html b/testing/web-platform/tests/css/css-grid/subgrid/placement-invalidation-001.html new file mode 100644 index 0000000000..954880e996 --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/subgrid/placement-invalidation-001.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Grid Test: Subgridded item placement invalidation</title> +<link rel="author" title="Ethan Jimenez" href="mailto:ethavar@microsoft.com"> +<link rel="help" href="https://drafts.csswg.org/css-grid-2/#track-sizing"> +<style> +html, body { + margin: 0; + padding: 0; +} +#grid { + width: 200px; + display: grid; + background: lightgray; + grid-template-columns: [start] auto [end] 1fr; + grid-template-rows: 100px; +} +#subgrid { + display: grid; + grid-column: 1 / -1; + grid-template-columns: subgrid; +} +#item { + width: 50px; + background: lightblue; + border: 5px solid gray; + grid-column: start / end; +} +</style> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<div id="grid"> + <div id="subgrid"> + <div id="item" data-offset-x="140"></div> + </div> +</div> +<script> +"use strict"; + +let grid = document.getElementById("grid"); +let item = document.getElementById("item"); + +// Computing an offset forces layout. +let item_offset = item.offsetLeft; + +grid.style.gridTemplateColumns = "1fr [start] auto [end]"; +item_offset = item.offsetLeft; +checkLayout("#item"); +</script> diff --git a/testing/web-platform/tests/css/css-grid/subgrid/subgrid-button-ref.html b/testing/web-platform/tests/css/css-grid/subgrid/subgrid-button-ref.html new file mode 100644 index 0000000000..b6b5e6115c --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/subgrid/subgrid-button-ref.html @@ -0,0 +1,33 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS test reference</title> +<style> +.grid { + display: grid; + width: 400px; + grid-template-columns: auto auto 1fr; + background-color: #ccc; +} +.button { + display: grid; + grid-template-columns: subgrid; + grid-column: span 3; + text-align: initial; + + background: transparent; + padding: 5px 10px; + margin: 0; + border: 2px solid; + font: inherit; +} +.right { + text-align: right; +} +</style> +<div class="grid"> + <div class="button"> + <span>hello</span> + <span>,</span> + <span class="right">world</span> + </div> +</div> diff --git a/testing/web-platform/tests/css/css-grid/subgrid/subgrid-button.html b/testing/web-platform/tests/css/css-grid/subgrid/subgrid-button.html new file mode 100644 index 0000000000..774702238f --- /dev/null +++ b/testing/web-platform/tests/css/css-grid/subgrid/subgrid-button.html @@ -0,0 +1,36 @@ +<!doctype html> +<meta charset="utf-8"> +<link rel="help" href="https://drafts.csswg.org/css-grid-2/#subgrids"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1887867"> +<link rel="match" href="subgrid-button-ref.html"> +<title>Button subgrid</title> +<style> +.grid { + display: grid; + width: 400px; + grid-template-columns: auto auto 1fr; + background-color: #ccc; +} +.button { + display: grid; + grid-template-columns: subgrid; + grid-column: span 3; + text-align: initial; + + background: transparent; + padding: 5px 10px; + margin: 0; + border: 2px solid; + font: inherit; +} +.right { + text-align: right; +} +</style> +<div class="grid"> + <button class="button"> + <span>hello</span> + <span>,</span> + <span class="right">world</span> + </button> +</div> diff --git a/testing/web-platform/tests/css/css-highlight-api/painting/custom-highlight-painting-019-ref.html b/testing/web-platform/tests/css/css-highlight-api/painting/custom-highlight-painting-019-ref.html new file mode 100644 index 0000000000..2266233684 --- /dev/null +++ b/testing/web-platform/tests/css/css-highlight-api/painting/custom-highlight-painting-019-ref.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>CSS Highlight API Reference: Non-overlapping highlight colors</title> +<style> + body { + text-decoration: 2px green underline; + } + #highlight { + color:blue; + text-decoration: 2px blue underline; + } +</style> +<body><span id="highlight">This part should be blue</span> and this part should be black diff --git a/testing/web-platform/tests/css/css-highlight-api/painting/custom-highlight-painting-019.html b/testing/web-platform/tests/css/css-highlight-api/painting/custom-highlight-painting-019.html new file mode 100644 index 0000000000..8c5ccbf020 --- /dev/null +++ b/testing/web-platform/tests/css/css-highlight-api/painting/custom-highlight-painting-019.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>CSS Highlight API Test: Non-overlapping highlight colors</title> +<link rel="author" title="Stephen Chenney" href="mailto:schenney@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-pseudo-4/#highlight-text"> +<link rel="match" href="custom-highlight-painting-019-ref.html"> +<meta name="assert" value="When painting non-overlapping highlights the current color should be resolved against the next layer beneath the highlight at the current location within the span."> +<meta name="fuzzy" content="0-130;0-4"> +<style> + body { + text-decoration: 2px green underline; + } + ::highlight(foo) { + color:blue; + text-decoration: 2px blue underline; + } + ::highlight(bar) { + text-decoration-line: underline; + text-decoration-thickness: 2px; + } +</style> +<body>This part should be blue and this part should be black +<script> + let textNode = document.body.firstChild; + + let r1 = new Range(); + r1.setStart(textNode, 0); + r1.setEnd(textNode, 24); + + let r2 = new Range(); + r2.setStart(textNode, 29); + r2.setEnd(textNode, 54); + + let h1 = new Highlight(r1); + let h2 = new Highlight(r2); + + CSS.highlights.set("foo", h1); + CSS.highlights.set("bar", h2); +</script>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-images/cross-fade-computed-value.html b/testing/web-platform/tests/css/css-images/cross-fade-computed-value.html index efb3b58d61..dbecb4a654 100644 --- a/testing/web-platform/tests/css/css-images/cross-fade-computed-value.html +++ b/testing/web-platform/tests/css/css-images/cross-fade-computed-value.html @@ -24,29 +24,29 @@ test_computed_value( 'background-image', 'cross-fade(30% color-mix(in srgb, currentcolor, blue), white)', - 'cross-fade(30% color(srgb 0.5 0 0.5), rgb(255, 255, 255))'); + 'cross-fade(color(srgb 0.5 0 0.5) 30%, rgb(255, 255, 255))'); // Unneccessary percentages should be kept. test_computed_value('background-image', 'cross-fade(50% red, 50% green)', - 'cross-fade(50% rgb(255, 0, 0), 50% rgb(0, 128, 0))'); + 'cross-fade(rgb(255, 0, 0) 50%, rgb(0, 128, 0) 50%)'); // Percentage normalization should not be visible computed-value time. test_computed_value('background-image', 'cross-fade(20% red, 20% green)', - 'cross-fade(20% rgb(255, 0, 0), 20% rgb(0, 128, 0))'); + 'cross-fade(rgb(255, 0, 0) 20%, rgb(0, 128, 0) 20%)'); // More than two values. test_computed_value('background-image', 'cross-fade(50% red, 50% green, 50% blue)', - 'cross-fade(50% rgb(255, 0, 0), 50% rgb(0, 128, 0), 50% rgb(0, 0, 255))'); + 'cross-fade(rgb(255, 0, 0) 50%, rgb(0, 128, 0) 50%, rgb(0, 0, 255) 50%)'); // More-than-100% should be invalid, but in calc() we can't reject it parse-time; // it will be clamped on serialization. test_computed_value('background-image', 'cross-fade(calc(101%) red, green)', - 'cross-fade(100% rgb(255, 0, 0), rgb(0, 128, 0))'); + 'cross-fade(rgb(255, 0, 0) 100%, rgb(0, 128, 0))'); test_computed_value('background-image', 'cross-fade(calc(-200%) red, green)', - 'cross-fade(0% rgb(255, 0, 0), rgb(0, 128, 0))'); + 'cross-fade(rgb(255, 0, 0) 0%, rgb(0, 128, 0))'); </script> </body> </html> diff --git a/testing/web-platform/tests/css/css-images/gradient/color-scheme-dependent-color-stops-ref.html b/testing/web-platform/tests/css/css-images/gradient/color-scheme-dependent-color-stops-ref.html new file mode 100644 index 0000000000..28d57bc19c --- /dev/null +++ b/testing/web-platform/tests/css/css-images/gradient/color-scheme-dependent-color-stops-ref.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<html> +<head> +<link rel="author" title="Aditya Keerthi" href="https://github.com/pxlcoder"> +<link rel="help" href="https://www.w3.org/TR/css-color-adjust-1/#color-scheme-prop"> +<link rel="help" href="https://www.w3.org/TR/css-color-4/#css-system-colors"> +<link rel="help" href="https://www.w3.org/TR/css-color-5/#light-dark"> +<link rel="help" href="https://www.w3.org/TR/css-color-5/#color-mix"> +<title>Reference: Test changing used color-scheme updates gradient with color-scheme dependent color stops.</title> +<style> + +.box { + color-scheme: dark; + + width: 100px; + height: 100px; +} + +#system-color { + background-image: linear-gradient(CanvasText, CanvasText); +} + +#system-color-in-color-mix { + background-image: linear-gradient(color-mix(in lch, Canvas, pink), color-mix(in lch, Canvas, pink)); +} + +#light-dark { + background-image: linear-gradient(light-dark(red, green), light-dark(red, green)); +} + +#light-dark-in-color-mix { + background-image: linear-gradient(color-mix(in lch, light-dark(red, green), pink), color-mix(in lch, light-dark(red, green), pink)); +} + +</style> +</head> +<body> +<p>Test system color</p> +<div id="system-color" class="box"></div> +<p>Test system color in color-mix()</p> +<div id="system-color-in-color-mix" class="box"></div> +<p>Test light-dark()</p> +<div id="light-dark" class="box"></div> +<p>Test light-dark() in color-mix()</p> +<div id="light-dark-in-color-mix" class="box"></div> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-images/gradient/color-scheme-dependent-color-stops.html b/testing/web-platform/tests/css/css-images/gradient/color-scheme-dependent-color-stops.html new file mode 100644 index 0000000000..f95a557703 --- /dev/null +++ b/testing/web-platform/tests/css/css-images/gradient/color-scheme-dependent-color-stops.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> +<link rel="author" title="Aditya Keerthi" href="https://github.com/pxlcoder"> +<link rel="help" href="https://www.w3.org/TR/css-color-adjust-1/#color-scheme-prop"> +<link rel="help" href="https://www.w3.org/TR/css-color-4/#css-system-colors"> +<link rel="help" href="https://www.w3.org/TR/css-color-5/#light-dark"> +<link rel="help" href="https://www.w3.org/TR/css-color-5/#color-mix"> +<title>Test changing used color-scheme updates gradient with color-scheme dependent color stops.</title> +<link rel="match" href="color-scheme-dependent-color-stops-ref.html"> +<style> + +.dark { + color-scheme: dark; +} + +.box { + width: 100px; + height: 100px; +} + +#system-color { + background-image: linear-gradient(CanvasText, CanvasText); +} + +#system-color-in-color-mix { + background-image: linear-gradient(color-mix(in lch, Canvas, pink), color-mix(in lch, Canvas, pink)); +} + +#light-dark { + background-image: linear-gradient(light-dark(red, green), light-dark(red, green)); +} + +#light-dark-in-color-mix { + background-image: linear-gradient(color-mix(in lch, light-dark(red, green), pink), color-mix(in lch, light-dark(red, green), pink)); +} + +</style> +</head> +<body> +<p>Test system color</p> +<div id="system-color" class="box"></div> +<p>Test system color in color-mix()</p> +<div id="system-color-in-color-mix" class="box"></div> +<p>Test light-dark()</p> +<div id="light-dark" class="box"></div> +<p>Test light-dark() in color-mix()</p> +<div id="light-dark-in-color-mix" class="box"></div> +<script> + +requestAnimationFrame(() => { + document.querySelectorAll(".box").forEach((box) => { + box.classList.add("dark"); + }); + + document.documentElement.className = ''; +}); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-images/gradient/gradient-single-stop-longer-hue-hsl-ref.html b/testing/web-platform/tests/css/css-images/gradient/gradient-single-stop-longer-hue-hsl-ref.html new file mode 100644 index 0000000000..cf47c75834 --- /dev/null +++ b/testing/web-platform/tests/css/css-images/gradient/gradient-single-stop-longer-hue-hsl-ref.html @@ -0,0 +1,23 @@ +<!doctype html> +<html lang="en"> + +<head> + <meta charset="utf-8"> + <title>Gradient in HSL space</title> + <style> + body { + background: #fff; + } + + div { + width: 300px; + height: 300px; + background-image: linear-gradient(in hsl shorter hue 0deg, hsl(0, 100%, 50%) 0%, hsl(120, 100%, 50%) 33.3333%, hsl(240, 100%, 50%) 66.6667%, hsl(0, 100%, 50%) 100%); + } + </style> +</head> + +<body> + <div></div> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-images/gradient/gradient-single-stop-longer-hue-hsl.html b/testing/web-platform/tests/css/css-images/gradient/gradient-single-stop-longer-hue-hsl.html new file mode 100644 index 0000000000..bbadccb53e --- /dev/null +++ b/testing/web-platform/tests/css/css-images/gradient/gradient-single-stop-longer-hue-hsl.html @@ -0,0 +1,28 @@ +<!doctype html> +<html lang="en"> + +<head> + <meta charset="utf-8"> + <title>Gradient in HSL space</title> + <meta name="fuzzy" content="maxDifference=0-15;totalPixels=0-90000"> + <link rel="author" title="Ashley Hale" href="mailto:ahale@mozilla.com"> + <link rel="help" href="https://www.w3.org/TR/css-color-4/#interpolation"> + <meta name="assert" content="Tests that a multi-stop shorter hue gradient and a single-stop longer hue (wrapping) gradient match in appearance"> + <link rel="match" href="gradient-single-stop-longer-hue-hsl-ref.html"> + <style> + body { + background: #fff; + } + + div { + width: 300px; + height: 300px; + background-image: linear-gradient(in hsl longer hue 0deg, hsl(0, 100%, 50%) 0 0); + } + </style> +</head> + +<body> + <div></div> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-images/gradient/gradient-single-stop-longer-hue-oklch-ref.html b/testing/web-platform/tests/css/css-images/gradient/gradient-single-stop-longer-hue-oklch-ref.html new file mode 100644 index 0000000000..c3f51781c0 --- /dev/null +++ b/testing/web-platform/tests/css/css-images/gradient/gradient-single-stop-longer-hue-oklch-ref.html @@ -0,0 +1,23 @@ +<!doctype html> +<html lang="en"> + +<head> + <meta charset="utf-8"> + <title>Gradient in OKLCH space</title> + <style> + body { + background: #fff; + } + + div { + width: 300px; + height: 300px; + background-image: linear-gradient(in oklch shorter hue 0deg, oklch(0.62796 0.25768 29.23388) 0%, oklch(0.62796 0.25768 149.23388) 33.3333%, oklch(0.62796 0.25768 269.23388) 66.6667%, oklch(0.62796 0.25768 29.23388) 100%); + } + </style> +</head> + +<body> + <div></div> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-images/gradient/gradient-single-stop-longer-hue-oklch.html b/testing/web-platform/tests/css/css-images/gradient/gradient-single-stop-longer-hue-oklch.html new file mode 100644 index 0000000000..abe6604fc4 --- /dev/null +++ b/testing/web-platform/tests/css/css-images/gradient/gradient-single-stop-longer-hue-oklch.html @@ -0,0 +1,28 @@ +<!doctype html> +<html lang="en"> + +<head> + <meta charset="utf-8"> + <title>Gradient in OKLCH space</title> + <meta name="fuzzy" content="maxDifference=0-15;totalPixels=0-90000"> + <link rel="author" title="Ashley Hale" href="mailto:ahale@mozilla.com"> + <link rel="help" href="https://www.w3.org/TR/css-color-4/#interpolation"> + <meta name="assert" content="Tests that a multi-stop shorter hue gradient and a single-stop longer hue (wrapping) gradient match in appearance"> + <link rel="match" href="gradient-single-stop-longer-hue-oklch-ref.html"> + <style> + body { + background: #fff; + } + + div { + width: 300px; + height: 300px; + background-image: linear-gradient(in oklch longer hue 0deg, oklch(0.62796 0.25768 29.23388) 0 0); + } + </style> +</head> + +<body> + <div></div> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-images/gradient/repeating-gradient-hsl-and-oklch-ref.html b/testing/web-platform/tests/css/css-images/gradient/repeating-gradient-hsl-and-oklch-ref.html new file mode 100644 index 0000000000..a5e1554be1 --- /dev/null +++ b/testing/web-platform/tests/css/css-images/gradient/repeating-gradient-hsl-and-oklch-ref.html @@ -0,0 +1,47 @@ +<!doctype html> +<html lang="en"> + +<head> + <meta charset="utf-8"> + <title>Repeating linear gradients in HSL and OKLCH space</title> + <link rel="author" title="Ashley Hale" href="mailto:ahale@mozilla.com"> + <style> + body { + background: #fff; + } + + .a { + width: 100px; + height: 100px; + background: repeating-linear-gradient( + to bottom, + hsl(180 50% 70%) 0px 20px, + hsl(0 50% 50%) 20px 40px, + hsl(180 50% 70%) 40px 60px, + hsl(0 50% 50%) 60px 80px, + hsl(180 50% 70%) 80px 100px + ); + } + + .b { + width: 100px; + height: 100px; + background: repeating-linear-gradient( + to bottom, + oklch(70% 50% 180) 0px 20px, + oklch(50% 50% 0) 20px 40px, + oklch(70% 50% 180) 40px 60px, + oklch(50% 50% 0) 60px 80px, + oklch(70% 50% 180) 80px 100px + ); + } + </style> +</head> + +<body> + <p>repeating-linear-gradient with HSL</p> + <div class="a"></div> + <p>repeating-linear-gradient with OKLCH</p> + <div class="b"></div> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-images/gradient/repeating-gradient-hsl-and-oklch.html b/testing/web-platform/tests/css/css-images/gradient/repeating-gradient-hsl-and-oklch.html new file mode 100644 index 0000000000..71e615d919 --- /dev/null +++ b/testing/web-platform/tests/css/css-images/gradient/repeating-gradient-hsl-and-oklch.html @@ -0,0 +1,44 @@ +<!doctype html> +<html lang="en"> + +<head> + <meta charset="utf-8"> + <title>Repeating linear gradients in HSL and OKLCH space</title> + <link rel="author" title="Ashley Hale" href="mailto:ahale@mozilla.com"> + <link rel="help" href="https://www.w3.org/TR/css-color-4/#interpolation"> + <meta name="assert" content="Tests that a multi-stop shorter hue gradient and a single-stop longer hue (wrapping) gradient match in appearance"> + <link rel="match" href="repeating-gradient-hsl-and-oklch-ref.html"> + <style> + body { + background: #fff; + } + + .a { + width: 100px; + height: 100px; + background: repeating-linear-gradient( + to bottom, + hsl(180 50% 70%) 0px 20px, + hsl(0 50% 50%) 20px 40px + ); + } + + .b { + width: 100px; + height: 100px; + background: repeating-linear-gradient( + to bottom, + oklch(70% 50% 180) 0px 20px, + oklch(50% 50% 0) 20px 40px + ); + } + </style> +</head> + +<body> + <p>repeating-linear-gradient with HSL</p> + <div class="a"></div> + <p>repeating-linear-gradient with OKLCH</p> + <div class="b"></div> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-block-box-001-ref.html b/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-block-box-001-ref.html index 4fc498a27b..2dfe344ce3 100644 --- a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-block-box-001-ref.html +++ b/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-block-box-001-ref.html @@ -2,7 +2,6 @@ <title>Reference for trimming block-boxes at their first/last formatted lines</title> <link rel="help" href="https://drafts.csswg.org/css-inline-3/#leading-trim"> <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> -<link rel="match" href="text-box-trim-half-leading-inline-box-001-ref.html"> <style> .div-parent { diff --git a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-block-box-001.html b/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-block-box-001.html index 9ad9ffc3fc..2cbf1c26db 100644 --- a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-block-box-001.html +++ b/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-block-box-001.html @@ -2,7 +2,7 @@ <title>Tests block boxes's edges are trimmed at text-over/text-under baselines of their first/last formatted lines</title> <link rel="help" href="https://drafts.csswg.org/css-inline-3/#leading-trim"> <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> -<link rel="match" href="text-box-trim-half-leading-inline-box-001-ref.html"> +<link rel="match" href="text-box-trim-half-leading-block-box-001-ref.html"> <style> .div-parent { diff --git a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-001-ref.html b/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-001-ref.html deleted file mode 100644 index 54df108fb9..0000000000 --- a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-001-ref.html +++ /dev/null @@ -1,31 +0,0 @@ -<!DOCTYPE html> -<title>Reference for trimming inline boxes</title> -<link rel="help" href="https://drafts.csswg.org/css-inline-3/#leading-trim"> -<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> - -<style> -div { - border: 1px solid orange; - font-size: 20px; - line-height: 1; -} - -span { - border: 1px solid blue; - border-right: 0; - border-left: 0; - font-family: Ahem; - font-size: 20px; - line-height: 1; -} -</style> - -<div> - <span>Test</span> -</div> -<div> - <span>Test<br><br></span> -</div> -<div> - <span><br>Test</span> -</div> diff --git a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-001.html b/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-001.html deleted file mode 100644 index 130b68c6eb..0000000000 --- a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-001.html +++ /dev/null @@ -1,32 +0,0 @@ -<!DOCTYPE html> -<title>Tests inline boxes are trimmed at text-over/text-under baselines</title> -<link rel="help" href="https://drafts.csswg.org/css-inline-3/#leading-trim"> -<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> -<link rel="match" href="text-box-trim-half-leading-inline-box-001-ref.html"> - -<style> -div { - border: 1px solid orange; - font-size: 20px; - line-height: 1; -} - -span { - border: 1px solid blue; - border-right: 0; - border-left: 0; - font-family: Ahem; - font-size: 20px; - line-height: 3; -} -</style> - -<div> - <span style="text-box-trim:both">Test</span> -</div> -<div> - <span style="text-box-trim:start">Test</span> -</div> -<div> - <span style="text-box-trim:end">Test</span> -</div> diff --git a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-002-ref.html b/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-002-ref.html deleted file mode 100644 index 0a615e6222..0000000000 --- a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-002-ref.html +++ /dev/null @@ -1,31 +0,0 @@ -<!DOCTYPE html> -<title>Reference for trimming multi-line text in inline boxes</title> -<link rel="help" href="https://drafts.csswg.org/css-inline-3/#leading-trim"> -<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> - -<style> -div { - border: 1px solid orange; - font-size: 20px; - line-height: 1; -} - -span { - border: 1px solid blue; - border-right: 0; - border-left: 0; - font-family: Ahem; - font-size: 20px; - line-height: 1; -} -</style> - -<div> - <span>Testline1<br>TestLine2<br>TestLine3</span> -</div> -<div> - <span>Testline1<br><br>TestLine2<br><br>TestLine3<br><br></span> -</div> -<div> - <span><br>Testline1<br><br>TestLine2<br><br>TestLine3</span> -</div> diff --git a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-002.html b/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-002.html deleted file mode 100644 index 631b53697c..0000000000 --- a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-002.html +++ /dev/null @@ -1,33 +0,0 @@ -<!DOCTYPE html> -<title>Tests inline boxes with multi-line text are trimmed at text-over/text-under baselines</title> -<link rel="help" href="https://drafts.csswg.org/css-inline-3/#leading-trim"> -<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> -<link rel="match" href="text-box-trim-half-leading-inline-box-002-ref.html"> - -<style> -div { - border: 1px solid orange; - font-size: 20px; - line-height: 1; -} - -span { - border: 1px solid blue; - border-right: 0; - border-left: 0; - font-family: Ahem; - font-size: 20px; - line-height: 3; -} - -</style> - -<div> - <span style="text-box-trim: both">Testline1<br>TestLine2<br>TestLine3</span> -</div> -<div> - <span style="text-box-trim: start">Testline1<br>TestLine2<br>TestLine3</span> -</div> -<div> - <span style="text-box-trim: end">Testline1<br>TestLine2<br>TestLine3</span> -</div> diff --git a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-003-ref.html b/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-003-ref.html deleted file mode 100644 index bf0fb3283d..0000000000 --- a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-003-ref.html +++ /dev/null @@ -1,32 +0,0 @@ -<!DOCTYPE html> -<title>Reference for trimming inline boxes</title> -<link rel="help" href="https://drafts.csswg.org/css-inline-3/#leading-trim"> -<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> - -<style> -div { - border: 1px solid orange; - font-size: 20px; - line-height: 1; - writing-mode:vertical-lr; -} - -span { - border: 1px solid blue; - border-top: 0; - border-bottom: 0; - font-family: Ahem; - font-size: 20px; - line-height: 1; -} -</style> - -<div> - <span>Test</span> -</div> -<div> - <span>Test<br><br></span> -</div> -<div> - <span><br>Test</span> -</div> diff --git a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-003.html b/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-003.html deleted file mode 100644 index 4c7e33663b..0000000000 --- a/testing/web-platform/tests/css/css-inline/text-box-trim/text-box-trim-half-leading-inline-box-003.html +++ /dev/null @@ -1,33 +0,0 @@ -<!DOCTYPE html> -<title>Tests inline boxes are trimmed at text-over/text-under baselines</title> -<link rel="help" href="https://drafts.csswg.org/css-inline-3/#leading-trim"> -<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> -<link rel="match" href="text-box-trim-half-leading-inline-box-003-ref.html"> - -<style> -div { - border: 1px solid orange; - font-size: 20px; - line-height: 1; - writing-mode:vertical-lr; -} - -span { - border: 1px solid blue; - border-top: 0; - border-bottom: 0; - font-family: Ahem; - font-size: 20px; - line-height: 3; -} -</style> - -<div> - <span style="text-box-trim:both">Test</span> -</div> -<div> - <span style="text-box-trim:start">Test</span> -</div> -<div> - <span style="text-box-trim:end">Test</span> -</div> diff --git a/testing/web-platform/tests/css/css-logical/animations/margin-block-interpolation.html b/testing/web-platform/tests/css/css-logical/animations/margin-block-interpolation.html new file mode 100644 index 0000000000..be4b6fdd8e --- /dev/null +++ b/testing/web-platform/tests/css/css-logical/animations/margin-block-interpolation.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>margin-block interpolation</title> +<link rel="help" href="https://drafts.csswg.org/css-logical/#propdef-margin-block"> +<meta name="assert" content="margin-block supports animation"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/interpolation-testcommon.js"></script> + +<body> +<script> +test_interpolation({ + property: 'margin-block', + from: '10px', + to: '20px', +}, [ + {at: -0.3, expect: '7px'}, + {at: 0, expect: '10px'}, + {at: 0.3, expect: '13px'}, + {at: 0.6, expect: '16px'}, + {at: 1, expect: '20px'}, + {at: 1.5, expect: '25px'}, +]); +</script> +</body> diff --git a/testing/web-platform/tests/css/css-logical/animations/margin-inline-interpolation.html b/testing/web-platform/tests/css/css-logical/animations/margin-inline-interpolation.html new file mode 100644 index 0000000000..86408ea6e9 --- /dev/null +++ b/testing/web-platform/tests/css/css-logical/animations/margin-inline-interpolation.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="UTF-8"> +<title>margin-inline interpolation</title> +<link rel="help" href="https://drafts.csswg.org/css-logical/#propdef-margin-inline"> +<meta name="assert" content="margin-inline supports animation"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/interpolation-testcommon.js"></script> + +<body> +<script> +test_interpolation({ + property: 'margin-inline', + from: '10px', + to: '20px', +}, [ + {at: -0.3, expect: '7px'}, + {at: 0, expect: '10px'}, + {at: 0.3, expect: '13px'}, + {at: 0.6, expect: '16px'}, + {at: 1, expect: '20px'}, + {at: 1.5, expect: '25px'}, +]); +</script> +</body> diff --git a/testing/web-platform/tests/css/css-masking/animations/clip-path-interpolation-shape.html b/testing/web-platform/tests/css/css-masking/animations/clip-path-interpolation-shape.html index ad3e2a0bdb..638e133c26 100644 --- a/testing/web-platform/tests/css/css-masking/animations/clip-path-interpolation-shape.html +++ b/testing/web-platform/tests/css/css-masking/animations/clip-path-interpolation-shape.html @@ -1,10 +1,10 @@ <!DOCTYPE html> <meta charset="UTF-8"> -<meta charset="UTF-8"> <title>clip-path-interpolation</title> <link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> <link rel="help" href="https://drafts.fxtf.org/css-masking-1/#the-clip-path"> -<meta name="assert" content="clip-path supports animation"> +<link rel="help" href="https://drafts.csswg.org/css-shapes-2/#interpolating-shape"> +<meta name="assert" content="clip-path supports animation for shape()"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> @@ -83,7 +83,6 @@ test_no_interpolation({ to: 'shape(from 10px 10px, close)', }); - test_interpolation({ property: 'clip-path', from: 'shape(from 5% 5px, hline to 5%, vline to -5px, close)', @@ -116,6 +115,7 @@ test_interpolation({ {at: -0.3, expect: 'shape(from 2% 2px, curve by 7% 13px via -3% 86px, curve by 33% 17px via 17% 53px 34% 61px)'}, {at: 0, expect: 'shape(from 5% 5px, curve by 10% 10px via 0% 80px, curve by 30% 20px via 20% 50px 25% 70px)'}, {at: 0.5, expect: 'shape(from 10% 10px, curve by 15% 5px via 5% 70px, curve by 25% 25px via 25% 45px 10% 85px)'}, + {at: 1, expect: 'shape(from 15% 15px, curve by 20% 0px via 10% 60px, curve by 20% 30px via 30% 40px -5% 100px)'}, {at: 1.5, expect: 'shape(from 20% 20px, curve by 25% -5px via 15% 50px, curve by 15% 35px via 35% 35px -20% 115px)'}, ]); @@ -127,6 +127,7 @@ test_interpolation({ {at: -0.3, expect: 'shape(from 2% 2px, smooth to 7% 13px via -3% 86px, smooth to 33% 17px)'}, {at: 0, expect: 'shape(from 5% 5px, smooth to 10% 10px via 0% 80px, smooth to 30% 20px)'}, {at: 0.5, expect: 'shape(from 10% 10px, smooth to 15% 5px via 5% 70px, smooth to 25% 25px)'}, + {at: 1, expect: 'shape(from 15% 15px, smooth to 20% 0px via 10% 60px, smooth to 20% 30px)'}, {at: 1.5, expect: 'shape(from 20% 20px, smooth to 25% -5px via 15% 50px, smooth to 15% 35px)'}, ]); @@ -138,6 +139,7 @@ test_interpolation({ {at: -0.3, expect: 'shape(from 2% 2px, smooth by 7% 13px via -3% 86px, smooth by 33% 17px)'}, {at: 0, expect: 'shape(from 5% 5px, smooth by 10% 10px via 0% 80px, smooth by 30% 20px)'}, {at: 0.5, expect: 'shape(from 10% 10px, smooth by 15% 5px via 5% 70px, smooth by 25% 25px)'}, + {at: 1, expect: 'shape(from 15% 15px, smooth by 20% 0px via 10% 60px, smooth by 20% 30px)'}, {at: 1.5, expect: 'shape(from 20% 20px, smooth by 25% -5px via 15% 50px, smooth by 15% 35px)'}, ]); @@ -149,14 +151,14 @@ test_interpolation({ {at: -0.3, expect: 'shape(from 2% 2px, arc to 18% -12px of 7px 17px ccw small, arc by 12% -2px of 33px 33px rotate -42deg cw large , arc to 25% 20px of 10px 5px ccw small)'}, {at: 0, expect: 'shape(from 5% 5px, arc to 15% -15px of 10px 20px, arc by 15% -5px of 30px cw rotate 30deg large, arc to 25% 20px of 10px 5px small)'}, {at: 0.3, expect: 'shape(from 8% 8px, arc to 12% -18px of 13px 23px ccw small, arc by 18% -8px of 27px 27px rotate 102deg cw large, arc to 25% 20px of 10px 5px ccw small )'}, - {at: 0.5, expect: 'shape(from 10% 10px, arc to 10% -20px of 15px 25px ccw small, arc by 20% -10px of 25px rotate 150deg cw small, arc to 25% 20px of 10px 5px cw small)'}, + {at: 0.5, expect: 'shape(from 10% 10px, arc to 10% -20px of 15px 25px ccw small, arc by 20% -10px of 25px rotate 150deg cw large, arc to 25% 20px of 10px 5px cw small)'}, {at: 1, expect: 'shape(from 15% 15px, arc to 5% -25px of 20px 30px, arc by 25% -15px of 20px rotate 270deg cw small, arc to 25% 20px of 10px 5px cw small)'}, {at: 1.5, expect: 'shape(from 20% 20px, arc to 0% -30px of 25px 35px ccw small, arc by 30% -20px of 15px rotate 390deg cw small, arc to 25% 20px of 10px 5px cw small)'}, ]); test_interpolation({ property: 'clip-path', - from: 'shape(from 5px -5%, hline to 10px, vline by 10rem, hline by 1vh, close, vline by 3pt)', + from: 'shape(from 5px -5%, hline to 10px, vline by 10rem, hline by 8.25px, close, vline by 3pt)', to: 'shape(from -5px 5px, hline to 20px, vline by 10%, hline by 1em, close, vline by 6pt)', }, [ {at: -0.3, expect: 'shape(from 8px calc(-6.5% - 1.5px), hline to 7px, vline by calc(-3% + 208px), hline by 5.92px, close, vline by 2.8px)'}, @@ -166,5 +168,115 @@ test_interpolation({ {at: 1.5, expect: 'shape(from -10px calc(2.5% + 7.5px), hline to 25px, vline by calc(15% - 80px), hline by 19.88px, close, vline by 10px)'}, ]); +test_no_interpolation({ + property: 'clip-path', + from: 'shape(from 10px 10px, move to 10% 10%)', + to: 'path("M10 10 z")', +}); + +test_no_interpolation({ + property: 'clip-path', + from: 'path("M10 10 M10 10")', + to: 'shape(from 10px 10px, close)', +}); + +test_no_interpolation({ + property: 'clip-path', + from: 'path("M10 10 h 5")', + to: 'shape(from 10px 10px, hline to 5px)', +}); + +test_no_interpolation({ + property: 'clip-path', + from: 'shape(nonzero from 10px 10px, move to 10% 10%)', + to: 'path(evenodd, "M0 0 M20 20")', +}); + +test_interpolation({ + property: 'clip-path', + from: 'shape(from 5px 5px, hline to 5px, vline to -5px, close)', + to: 'path("M 15 15 H 25 V -15 Z")', +}, [ + {at: -0.3, expect: 'shape(from 2px 2px, hline to -1px, vline to -2px, close)'}, + {at: 0, expect: 'shape(from 5px 5px, hline to 5px, vline to -5px, close)'}, + {at: 0.5, expect: 'shape(from 10px 10px, hline to 15px, vline to -10px, close)'}, + {at: 1, expect: 'shape(from 15px 15px, hline to 25px, vline to -15px, close)'}, + {at: 1.5, expect: 'shape(from 20px 20px, hline to 35px, vline to -20px, close)'}, +]); + +test_interpolation({ + property: 'clip-path', + from: 'shape(from 5% 5px, curve to 10% 10px via 0% 80px, curve to 30% 20px via 20% 50px 25% 70px)', + to: 'path("M 15 15 Q 10 60 20 0 C 30 40 -5 100 20 30")', +}, [ + {at: -0.3, expect: 'shape(from calc(6.5% - 4.5px) 2px, curve to calc(13% - 6px) 13px via calc(0% - 3px) 86px, curve to calc(39% - 6px) 17px via calc(26% - 9px) 53px calc(32.5% + 1.5px) 61px)'}, + {at: 0, expect: 'shape(from 5% 5px, curve to 10% 10px via 0% 80px, curve to 30% 20px via 20% 50px 25% 70px)'}, + {at: 0.5, expect: 'shape(from calc(2.5% + 7.5px) 10px, curve to calc(5% + 10px) 5px via calc(0% + 5px) 70px, curve to calc(15% + 10px) 25px via calc(10% + 15px) 45px calc(12.5% - 2.5px) 85px)'}, + {at: 1, expect: 'shape(from calc(0% + 15px) 15px, curve to calc(0% + 20px) 0px via calc(0% + 10px) 60px, curve to calc(0% + 20px) 30px via calc(0% + 30px) 40px calc(0% - 5px) 100px)'}, + {at: 1.5, expect: 'shape(from calc(-2.5% + 22.5px) 20px, curve to calc(-5% + 30px) -5px via calc(0% + 15px) 50px, curve to calc(-15% + 30px) 35px via calc(-10% + 45px) 35px calc(-12.5% - 7.5px) 115px)'}, +]); + +test_interpolation({ + property: 'clip-path', + from: 'path("M 5 5 q 0 80 10 10 c 20 50 25 70 30 20")', + to: 'shape(from 15% 15px, curve by 20% 0px via 10% 60px, curve by 20% 30px via 30% 40px -5% 100px)', +}, [ + {at: -0.3, expect: 'shape(from calc(-4.5% + 6.5px) 2px, curve by calc(-6% + 13px) 13px via -3% 86px, curve by calc(-6% + 39px) 17px via calc(-9% + 26px) 53px calc(1.5% + 32.5px) 61px)'}, + {at: 0, expect: 'shape(from calc(0% + 5px) 5px, curve by calc(0% + 10px) 10px via 0% 80px, curve by calc(0% + 30px) 20px via calc(0% + 20px) 50px calc(0% + 25px) 70px)'}, + {at: 0.5, expect: 'shape(from calc(7.5% + 2.5px) 10px, curve by calc(10% + 5px) 5px via 5% 70px, curve by calc(10% + 15px) 25px via calc(15% + 10px) 45px calc(-2.5% + 12.5px) 85px)'}, + {at: 1, expect: 'shape(from 15% 15px, curve by 20% 0px via 10% 60px, curve by 20% 30px via 30% 40px -5% 100px)'}, + {at: 1.5, expect: 'shape(from calc(22.5% - 2.5px) 20px, curve by calc(30% - 5px) -5px via 15% 50px, curve by calc(30% - 15px) 35px via calc(45% - 10px) 35px calc(-7.5% - 12.5px) 115px)'}, +]); + +test_interpolation({ + property: 'clip-path', + from: 'shape(from 5% 5px, smooth to 10% 10px via 0% 80px, smooth to 30% 20px)', + to: 'path("M 15 15 S 10 60 20 0 T 20 30")', +}, [ + {at: -0.3, expect: 'shape(from calc(6.5% - 4.5px) 2px, smooth to calc(13% - 6px) 13px via calc(0% - 3px) 86px, smooth to calc(39% - 6px) 17px)'}, + {at: 0, expect: 'shape(from 5% 5px, smooth to 10% 10px via 0% 80px, smooth to 30% 20px)'}, + {at: 0.5, expect: 'shape(from calc(2.5% + 7.5px) 10px, smooth to calc(5% + 10px) 5px via calc(0% + 5px) 70px, smooth to calc(15% + 10px) 25px)'}, + {at: 1, expect: 'shape(from calc(0% + 15px) 15px, smooth to calc(0% + 20px) 0px via calc(0% + 10px) 60px, smooth to calc(0% + 20px) 30px)'}, + {at: 1.5, expect: 'shape(from calc(-2.5% + 22.5px) 20px, smooth to calc(-5% + 30px) -5px via calc(0% + 15px) 50px, smooth to calc(-15% + 30px) 35px)'}, +]); + +test_interpolation({ + property: 'clip-path', + from: 'path("M 5 5 s 0 80 10 10 t 30 20")', + to: 'shape(from 15px 15px, smooth by 20px 0px via 10px 60px, smooth by 20px 30px)', +}, [ + {at: -0.3, expect: 'shape(from 2px 2px, smooth by 7px 13px via -3px 86px, smooth by 33px 17px)'}, + {at: 0, expect: 'shape(from 5px 5px, smooth by 10px 10px via 0px 80px, smooth by 30px 20px)'}, + {at: 0.5, expect: 'shape(from 10px 10px, smooth by 15px 5px via 5px 70px, smooth by 25px 25px)'}, + {at: 1, expect: 'shape(from 15px 15px, smooth by 20px 0px via 10px 60px, smooth by 20px 30px)'}, + {at: 1.5, expect: 'shape(from 20px 20px, smooth by 25px -5px via 15px 50px, smooth by 15px 35px)'}, +]); + +test_interpolation({ + property: 'clip-path', + from: 'shape(from 5% 5px, arc to 15% -15px of 10px 20px, arc by 15% -5px of 30px cw rotate 30deg large, arc to 25% 20px of 10px 5px small)', + to: 'path("M 15 15 A 20,30 0 0,0 5,-25 a 20,20 270 0,1 25,-15 A 10,5 0 0,0 25 20")', +}, [ + {at: -0.3, expect: 'shape(from calc(6.5% - 4.5px) 2px, arc to calc(19.5% - 1.5px) -12px of 7px 17px, arc by calc(19.5% - 7.5px) -2px of 33px cw large rotate -42deg, arc to calc(32.5% - 7.5px) 20px of 10px 5px)'}, + {at: 0, expect: 'shape(from 5% 5px, arc to 15% -15px of 10px 20px, arc by 15% -5px of 30px cw rotate 30deg large, arc to 25% 20px of 10px 5px small)'}, + {at: 0.3, expect: 'shape(from calc(3.5% + 4.5px) 8px, arc to calc(10.5% + 1.5px) -18px of 13px 23px, arc by calc(10.5% + 7.5px) -8px of 27px cw large rotate 102deg, arc to calc(17.5% + 7.5px) 20px of 10px 5px)'}, + {at: 0.5, expect: 'shape(from calc(2.5% + 7.5px) 10px, arc to calc(7.5% + 2.5px) -20px of 15px 25px, arc by calc(7.5% + 12.5px) -10px of 25px cw large rotate 150deg, arc to calc(12.5% + 12.5px) 20px of 10px 5px)'}, + {at: 1, expect: 'shape(from calc(0% + 15px) 15px, arc to calc(0% + 5px) -25px of 20px 30px, arc by calc(0% + 25px) -15px of 20px cw rotate 270deg, arc to calc(0% + 25px) 20px of 10px 5px)'}, + {at: 1.5, expect: 'shape(from calc(-2.5% + 22.5px) 20px, arc to calc(-7.5% + 7.5px) -30px of 25px 35px, arc by calc(-7.5% + 37.5px) -20px of 15px cw rotate 390deg, arc to calc(-12.5% + 37.5px) 20px of 10px 5px)'}, +]); + +test_interpolation({ + property: 'clip-path', + from: 'path("M 5 5 A 10,20 0 0,0 15,-15 a 30,30 30 1,1 15,-5 A 10,5 0 0,0 25 20")', + to: 'shape(from 15px 15px, arc to 5px -25px of 20px 30px, arc by 25px -15px of 20px cw rotate 270deg small, arc to 25px 20px of 10px 5px small cw)' +}, [ + {at: -0.3, expect: 'shape(from 2px 2px, arc to 18px -12px of 7px 17px ccw small, arc by 12px -2px of 33px 33px rotate -42deg cw large , arc to 25px 20px of 10px 5px ccw small)'}, + {at: 0, expect: 'shape(from 5px 5px, arc to 15px -15px of 10px 20px, arc by 15px -5px of 30px cw rotate 30deg large, arc to 25px 20px of 10px 5px small)'}, + {at: 0.3, expect: 'shape(from 8px 8px, arc to 12px -18px of 13px 23px ccw small, arc by 18px -8px of 27px 27px rotate 102deg cw large, arc to 25px 20px of 10px 5px ccw small )'}, + {at: 0.5, expect: 'shape(from 10px 10px, arc to 10px -20px of 15px 25px ccw small, arc by 20px -10px of 25px rotate 150deg cw large, arc to 25px 20px of 10px 5px cw small)'}, + {at: 1, expect: 'shape(from 15px 15px, arc to 5px -25px of 20px 30px, arc by 25px -15px of 20px rotate 270deg cw small, arc to 25px 20px of 10px 5px cw small)'}, + {at: 1.5, expect: 'shape(from 20px 20px, arc to 0px -30px of 25px 35px ccw small, arc by 30px -20px of 15px rotate 390deg cw small, arc to 25px 20px of 10px 5px cw small)'}, +]); + </script> </body> diff --git a/testing/web-platform/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-003.html b/testing/web-platform/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-003.html new file mode 100644 index 0000000000..1324aad97f --- /dev/null +++ b/testing/web-platform/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-003.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> + <title>CSS Masking: Test clip-path interpolation from shape() to path()</title> + <link rel="help" href="https://drafts.csswg.org/css-shapes-2/#funcdef-shape"> + <link rel="match" href="clip-path-path-interpolation-001-ref.html"> + <meta name="assert" content="The clip-path property takes the basic shape + 'shape()' for clipping. Test the interpolation of nonzero + shape and path function."> + <style> + @keyframes anim { + from { + clip-path: shape(nonzero from 20px 20px, + hline by 60px, vline by 60px, hline by -60%, close, + move to 30% 30px, hline by 40px, vline by 40px, hline by -40px, close); + } + to { + clip-path: path(nonzero, "M50 50 h50 v50 h-50 z M20 20 h50 v50 h-50 z"); + } + } + #rect { + width: 100px; + height: 100px; + background-color: green; + animation: anim 10s -5s paused linear; + } + </style> +</head> +<body> + <div id="rect"></div> +</body> +<script> + requestAnimationFrame(() => { + document.documentElement.classList.remove('reftest-wait'); + }); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-004.html b/testing/web-platform/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-004.html new file mode 100644 index 0000000000..69bec3c097 --- /dev/null +++ b/testing/web-platform/tests/css/css-masking/clip-path/animations/clip-path-shape-interpolation-004.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> + <title>CSS Masking: Test clip-path interpolation from path() to shape()</title> + <link rel="help" href="https://drafts.csswg.org/css-shapes-2/#funcdef-shape"> + <link rel="match" href="clip-path-path-interpolation-002-ref.html"> + <meta name="assert" content="The clip-path property takes the basic shape + 'shape()' for clipping. Test the interpolation of evenodd + path and shape function."> + <style> + @keyframes anim { + from { + clip-path: path(evenodd, "M20 20 h60 v60 h-60 z M30 30 h40 v40 h-40 z"); + } + to { + clip-path: shape(evenodd from 50px 50px, + hline by 50px, vline by 50px, hline by -50%, close, + move to 20px 20%, hline by 50px, vline by 50px, hline by -50px, close); + } + } + #rect { + width: 100px; + height: 100px; + background-color: green; + animation: anim 10s -5s paused linear; + } + </style> +</head> +<body> + <div id="rect"></div> +</body> +<script> + requestAnimationFrame(() => { + document.documentElement.classList.remove('reftest-wait'); + }); +</script> +</html> diff --git a/testing/web-platform/tests/css/css-masking/clip-path/clip-path-path-with-zoom-hittest.html b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-path-with-zoom-hittest.html index 30ceefcbc0..ff85e8ff80 100644 --- a/testing/web-platform/tests/css/css-masking/clip-path/clip-path-path-with-zoom-hittest.html +++ b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-path-with-zoom-hittest.html @@ -10,7 +10,7 @@ width: 100px; height: 100px; background-color: green; - clip-path: path(nonzero, 'M0 0, L100 0, L0 100, L 0 0'); + clip-path: path(nonzero, 'M0,0 L100,0 L0,100 L0,0'); zoom: 2; } </style> diff --git a/testing/web-platform/tests/css/css-masking/clip-path/clip-path-path-with-zoom.html b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-path-with-zoom.html index 5879917f36..981519d22b 100644 --- a/testing/web-platform/tests/css/css-masking/clip-path/clip-path-path-with-zoom.html +++ b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-path-with-zoom.html @@ -15,7 +15,7 @@ width: 100px; height: 100px; background-color: green; - clip-path: path(nonzero, 'M0 0, L100 0, L0 100, L 0 0'); + clip-path: path(nonzero, 'M0,0 L100,0 L0,100 L0,0'); zoom: 2; } </style> diff --git a/testing/web-platform/tests/css/css-masking/clip-path/clip-path-scaled-video.html b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-scaled-video.html index c92702d8e7..03e1b21d17 100644 --- a/testing/web-platform/tests/css/css-masking/clip-path/clip-path-scaled-video.html +++ b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-scaled-video.html @@ -14,32 +14,32 @@ </clipPath> <g clip-path="url(#clip)" transform="scale(0.112)"> <foreignObject width="320" height="240"> - <video src="/media/test.ogv" autoplay loop></video> + <video src="/media/test.webm" autoplay loop></video> </foreignObject> </g> <g clip-path="url(#clip)" transform="scale(0.345)"> <foreignObject width="320" height="240"> - <video src="/media/test.ogv" autoplay loop></video> + <video src="/media/test.webm" autoplay loop></video> </foreignObject> </g> <g clip-path="url(#clip)" transform="scale(0.778)"> <foreignObject width="320" height="240"> - <video src="/media/test.ogv" autoplay loop></video> + <video src="/media/test.webm" autoplay loop></video> </foreignObject> </g> <g clip-path="url(#clip)" transform="scale(0.912)"> <foreignObject width="320" height="240"> - <video src="/media/test.ogv" autoplay loop></video> + <video src="/media/test.webm" autoplay loop></video> </foreignObject> </g> <g clip-path="url(#clip)" transform="scale(1.678)"> <foreignObject width="320" height="240"> - <video src="/media/test.ogv" autoplay loop></video> + <video src="/media/test.webm" autoplay loop></video> </foreignObject> </g> <g clip-path="url(#clip)" transform="scale(3.333)"> <foreignObject width="320" height="240"> - <video src="/media/test.ogv" oncanplaythrough="takeScreenshot()" autoplay loop></video> + <video src="/media/test.webm" oncanplaythrough="takeScreenshot()" autoplay loop></video> </foreignObject> </g> </svg> diff --git a/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-003.html b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-003.html index 22e7d9aaf5..ef03f85b28 100644 --- a/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-003.html +++ b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-003.html @@ -8,15 +8,6 @@ 'shape()' for clipping. Test curves."> </head> <style> - div { - width: 100px; - height: 100px; - } - #ref { - clip-path: path(nonzero, "M 10 10, Q 40 0 60 20, T 90 0, c 10 40 20 20 -20 60, s -10 70 -40 -10"); - background-color: red; - position: absolute; - } #rect { width: 100px; height: 100px; @@ -29,8 +20,6 @@ } </style> <body> - <p>You should see no red.</p> - <div id="ref"></div> <div id="rect"></div> </body> </html> diff --git a/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-004.html b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-004.html index 14e3ba6329..1da9177de5 100644 --- a/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-004.html +++ b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-004.html @@ -8,15 +8,6 @@ 'shape()' for clipping. Test arcs."> </head> <style> - div { - width: 100px; - height: 100px; - } - #ref { - clip-path: path(nonzero, "M 20 20 A 25 12 0 0 1 80 20, a 33 33 120 1 1 -40 50, A 20 25 0 0 0 20 20"); - background-color: red; - position: absolute; - } #rect { width: 100px; height: 100px; @@ -28,8 +19,6 @@ } </style> <body> - <p>You should see no red.</p> - <div id="ref"></div> <div id="rect"></div> </body> </html> diff --git a/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-005.html b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-005.html new file mode 100644 index 0000000000..44e358bb59 --- /dev/null +++ b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-005.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSS Masking: Test clip-path property and shape function with padding-box</title> + <link rel="help" href="https://drafts.csswg.org/css-shapes-2/#funcdef-shape"> + <link rel="match" href="reference/clip-path-path-001-ref.html"> + <meta name="assert" content="The clip-path property takes the basic shape + 'shape()' for clipping. Test the usage of the reference box. On pass you + should see a green square."> +</head> +<style> + #rect { + /* The size of the padding-box is 100x100. */ + width: 120px; + height: 120px; + padding: 10px; + border: 10px solid red; + box-sizing: border-box; + background-color: green; + clip-path: shape(from 0px 0px, + hline by 80px, vline by 80%, hline by -80%, close) + padding-box; + } +</style> +<body> + <p>The test passes if there are a green filled rect.</p> + <div id="rect"></div> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-006.html b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-006.html new file mode 100644 index 0000000000..7f6db73ebd --- /dev/null +++ b/testing/web-platform/tests/css/css-masking/clip-path/clip-path-shape-006.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> +<head> + <title>CSS Masking: Test clip-path property and shape function with content-box</title> + <link rel="help" href="https://drafts.csswg.org/css-shapes-2/#funcdef-shape"> + <link rel="match" href="reference/clip-path-path-001-ref.html"> + <meta name="assert" content="The clip-path property takes the basic shape + 'shape()' for clipping. Test the usage of the reference box. On pass you + should see a green square."> +</head> +<style> + #rect { + width: 140px; + height: 140px; + padding: 10px; + border: 10px solid red; + box-sizing: border-box; + background-color: green; + /* The size of the content-box is 100x100. */ + clip-path: shape(from -10px -10%, + hline by 80px, vline by 80%, hline by -80%, close) + content-box; + } +</style> +<body> + <p>The test passes if there are a green filled rect.</p> + <div id="rect"></div> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-masking/clip-path/reference/clip-path-path-with-zoom-ref.html b/testing/web-platform/tests/css/css-masking/clip-path/reference/clip-path-path-with-zoom-ref.html index ef91c619c4..76b6e473f0 100644 --- a/testing/web-platform/tests/css/css-masking/clip-path/reference/clip-path-path-with-zoom-ref.html +++ b/testing/web-platform/tests/css/css-masking/clip-path/reference/clip-path-path-with-zoom-ref.html @@ -6,7 +6,7 @@ width: 200px; height: 200px; background: green; - clip-path: path(nonzero, 'M0 0, L200 0, L0 200'); + clip-path: path(nonzero, 'M0,0 L200,0 L0,200'); } </style> <div id="rect"></div> diff --git a/testing/web-platform/tests/css/css-masking/clip-path/reference/clip-path-shape-003-ref.html b/testing/web-platform/tests/css/css-masking/clip-path/reference/clip-path-shape-003-ref.html index 46e098c4eb..8d6173464c 100644 --- a/testing/web-platform/tests/css/css-masking/clip-path/reference/clip-path-shape-003-ref.html +++ b/testing/web-platform/tests/css/css-masking/clip-path/reference/clip-path-shape-003-ref.html @@ -5,18 +5,14 @@ <link rel="help" href="https://drafts.csswg.org/css-shapes-2/#funcdef-shape"> </head> <style> - div { + #ref { width: 100px; height: 100px; - } - #ref { background-color: green; - clip-path: path(nonzero, "M 10 10, Q 40 0 60 20, T 90 0, c 10 40 20 20 -20 60, s -10 70 -40 -10"); - position: absolute; + clip-path: path(nonzero, "M 10 10 Q 40 0 60 20 T 90 0 c 10 40 20 20 -20 60 s -10 70 -40 -10"); } </style> <body> - <p>You should see no red.</p> <div id="ref"></div> </body> </html> diff --git a/testing/web-platform/tests/css/css-masking/clip-path/reference/clip-path-shape-004-ref.html b/testing/web-platform/tests/css/css-masking/clip-path/reference/clip-path-shape-004-ref.html index ec8f941079..b74e6abdd9 100644 --- a/testing/web-platform/tests/css/css-masking/clip-path/reference/clip-path-shape-004-ref.html +++ b/testing/web-platform/tests/css/css-masking/clip-path/reference/clip-path-shape-004-ref.html @@ -5,18 +5,14 @@ <link rel="help" href="https://drafts.csswg.org/css-shapes-2/#funcdef-shape"> </head> <style> - div { + #ref { width: 100px; height: 100px; - } - #ref { background-color: green; - clip-path: path(nonzero, "M 20 20 A 25 12 0 0 1 80 20, a 33 33 120 1 1 -40 50, A 20 25 0 0 0 20 20"); - position: absolute; + clip-path: path(nonzero, "M 20 20 A 25 12 0 0 1 80 20 a 33 33 120 1 1 -40 50 A 20 25 0 0 0 20 20"); } </style> <body> - <p>You should see no red.</p> <div id="ref"></div> </body> </html> diff --git a/testing/web-platform/tests/css/css-masking/parsing/clip-path-invalid.html b/testing/web-platform/tests/css/css-masking/parsing/clip-path-invalid.html index 40020d91d4..e9a84e8e9c 100644 --- a/testing/web-platform/tests/css/css-masking/parsing/clip-path-invalid.html +++ b/testing/web-platform/tests/css/css-masking/parsing/clip-path-invalid.html @@ -51,6 +51,7 @@ test_invalid_value("clip-path", 'path(evenodd, "")'); test_invalid_value("clip-path", 'path(abc, "m 20 0 h -100 z")'); test_invalid_value("clip-path", 'path(nonzero)'); test_invalid_value("clip-path", 'path("m 20 0 h -100", nonzero)'); +test_invalid_value("clip-path", "path(nonzero, 'M0 0, L100 0, L0 100, L 0 0');"); test_invalid_value("clip-path", "xywh(0px)"); test_invalid_value("clip-path", "xywh(0px 1%)"); diff --git a/testing/web-platform/tests/css/css-multicol/crashtests/block-in-inline-become-float.html b/testing/web-platform/tests/css/css-multicol/crashtests/block-in-inline-become-float.html new file mode 100644 index 0000000000..6d557b7c3a --- /dev/null +++ b/testing/web-platform/tests/css/css-multicol/crashtests/block-in-inline-become-float.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="http://crbug.com/329674902"> +<div style="width:10px; line-height:20px; columns:3; gap:0; height:40px; column-fill:auto; orphans:1; widows:1;"> + <br> + <span> + <div id="trouble" style="contain:size; width:100%; height:100px;"></div> + xxxxxxxxxxxxxxxxxxxxxxxx + xxxxxxxxxxxxxxxxxxxxxxxx + </span> + <script> + document.body.offsetTop; + trouble.style.cssFloat = "left"; + </script> +</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-001.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-001.tentative.html new file mode 100644 index 0000000000..c8cfcb1066 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-001.tentative.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp basic test</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-005-ref.html"> +<meta name="assert" content="line-clamp should clamp to the specified number of lines, without needing display: -webkit-box, -webkit-box-orient, or overflow: hidden."> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + white-space: pre; + background-color: yellow; + padding: 0 4px; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-002.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-002.tentative.html new file mode 100644 index 0000000000..5f21b545fb --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-002.tentative.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp with fewer lines than specified</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-001-ref.html"> +<meta name="assert" content="line-clamp should not have an effect on an element with fewer lines than specified."> +<style> +.clamp { + line-clamp: 6; + font: 16px / 32px serif; + white-space: pre; + background-color: yellow; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-003.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-003.tentative.html new file mode 100644 index 0000000000..fa3b7472e5 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-003.tentative.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp with exactly as many lines as specified</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-001-ref.html"> +<meta name="assert" content="line-clamp should not have an effect on an element with exactly as many lines as specified."> +<style> +.clamp { + line-clamp: 5; + font: 16px / 32px serif; + white-space: pre; + background-color: yellow; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-004.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-004.tentative.html new file mode 100644 index 0000000000..c766d195b7 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-004.tentative.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: sizing of line-clamp affected elements</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-006-ref.html"> +<meta name="assert" content="line-clamp should size the element to the clamped number of lines."> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + white-space: pre; + padding: 0 4px; + background-color: yellow; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> +<p>Following content.</p> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-005.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-005.tentative.html new file mode 100644 index 0000000000..143aa65d89 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-005.tentative.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp with same-BFC block children</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-009-ref.html"> +<meta name="assert" content="line-clamp should count lines in same-BFC block children"> +<style> +.clamp { + line-clamp: 3; + font: 16px / 32px serif; + white-space: pre; + padding: 0 4px; + background-color: yellow; +} +.child { + font: 24px / 48px serif; + color: blue; +} +</style> +<div class="clamp">Line 1 +Line 2<div class="child">Line 3 +Line 4</div></div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-006.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-006.tentative.html new file mode 100644 index 0000000000..f06d94161b --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-006.tentative.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp with same-BFC block children</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-010-ref.html"> +<meta name="assert" content="line-clamp should count lines in same-BFC block children"> +<style> +.clamp { + line-clamp: 5; + font: 16px / 32px serif; + white-space: pre; + padding: 0 4px; + background-color: yellow; +} +.child { + font: 24px / 48px serif; + color: blue; +} +</style> +<div class="clamp">Line 1 +Line 2<div class="child">Line 3 +Line 4</div>Line 5 +Line 6</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-007.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-007.tentative.html new file mode 100644 index 0000000000..c71068641b --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-007.tentative.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp with independent BFC children</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-011-ref.html"> +<meta name="assert" content="line-clamp should skip lines in children with independent BFCs"> +<style> +.clamp { + line-clamp: 3; + font: 16px / 32px serif; + white-space: pre; + padding: 0 4px; + background-color: yellow; +} +.child { + overflow: auto; + font: 24px / 48px serif; + color: blue; + padding: 0 4px; +} +</style> +<div class="clamp">Line 1 +Line 2<div class="child">Line 3 +Line 4</div>Line 5 +Line 6</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-008.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-008.tentative.html new file mode 100644 index 0000000000..0d91b3612d --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-008.tentative.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp hides lines and in-flow boxes after the clamp point</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-005-ref.html"> +<meta name="assert" content="line-clamp should hide lines and in-flow boxes after the clamp point"> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + padding: 0 4px; + background-color: yellow; +} +.pre { + white-space: pre; +} +.red { + background-color: red; +} +</style> +<div class="clamp"> +<div class="pre">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> + +<div class="red">Test</div> + +<table class="red"> + <tr> + <td>A</td> + <td>B</td> + </tr> + <tr> + <td>C</td> + <td>D</td> + </tr> +</table> + +</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-009.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-009.tentative.html new file mode 100644 index 0000000000..4dfd3d6194 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-009.tentative.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: sizing of line-clamp affected elements with clamped block boxes</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-006-ref.html"> +<meta name="assert" content="line-clamp should size the element to the clamped number of lines, regardless of whether there are hidden block boxes after the clamp point"> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + padding: 0 4px; + background-color: yellow; +} +.pre { + white-space: pre; +} +.red { + background-color: red; +} +</style> +<div class="clamp"> +<div class="pre">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> + +<div class="red">Test</div> + +<table class="red"> + <tr> + <td>A</td> + <td>B</td> + </tr> + <tr> + <td>C</td> + <td>D</td> + </tr> +</table> + +</div> + +<p>Following content.</p> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-010.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-010.tentative.html new file mode 100644 index 0000000000..1386b147ce --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-010.tentative.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: when clamping by lines, lines after clamp are hidden even when they don't overflow</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/line-clamp-010-ref.html"> +<meta name="assert" content="When line-clamp is used with a number of lines, it should hide all lines after clamp, regardless of whether the box's height is set so they don't overflow"> +<style> +.clamp { + line-clamp: 2; + font: 16px / 32px serif; + height: 4lh; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-011.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-011.tentative.html new file mode 100644 index 0000000000..953f0c4faa --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-011.tentative.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: when clamping by lines, lines before clamp are not hidden even when they overflow</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/line-clamp-011-ref.html"> +<meta name="assert" content="When line-clamp is used with a number of lines, it should not hide any lines before clamp, regardless of whether the box's height is set so they overflow"> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + height: 3lh; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> +<p>Following content.</p> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-012.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-012.tentative.html new file mode 100644 index 0000000000..be39074037 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-012.tentative.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: when clamping by lines, borders and padding are respected</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/line-clamp-012-ref.html"> +<meta name="assert" content="when line-clamp is used with a number of lines, the box and its clamped children should be sized respecting borders and padding"> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + padding: 4px; + white-space: pre; + background-color: yellow; + border: 2px solid black; +} +.inner { + background-color: skyblue; + padding: 4px; + border: 2px solid purple; +} +</style> +<div class="clamp">Line 1 +Line 2<div class="inner">Line 3 +Line 4 +Line 5 +Line 6</div></div> +<p>Following content.</p> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-013.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-013.tentative.html new file mode 100644 index 0000000000..1bda501f02 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-013.tentative.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: when clamping by lines, clamped block descendent heights are respected</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/line-clamp-013-ref.html"> +<meta name="assert" content="when line-clamp is used with a number of lines, if the clamp happens inside a block descendent with a set height, that height will be respected"> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + padding: 4px; + white-space: pre; + background-color: yellow; + border: 2px solid black; +} +.inner { + background-color: skyblue; + height: 3lh; + padding: 4px; + border: 2px solid purple; +} +</style> +<div class="clamp">Line 1 +Line 2<div class="inner">Line 3 +Line 4 +Line 5 +Line 6</div></div> +<p>Following content.</p> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-014.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-014.tentative.html new file mode 100644 index 0000000000..9ca7c89372 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-014.tentative.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp doesn't apply to inline boxes</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-001-ref.html"> +<meta name="assert" content="line-clamp only affects block containers, not inline boxes"> +<style> +.block { + font: 16px / 32px serif; + background-color: yellow; +} +.clamp { + line-clamp: 4; + white-space: pre; +} +</style> +<div class="block"><span class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</span></div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-015.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-015.tentative.html new file mode 100644 index 0000000000..8203007322 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-015.tentative.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp applies to inline blocks</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/line-clamp-015-ref.html"> +<meta name="assert" content="line-clamp affects block containers, which includes inline blocks"> +<style> +.clamp { + display: inline-block; + line-clamp: 3; + font: 16px / 32px monospace; + white-space: pre; + padding: 0 4px; + width: 7.01ch; + background-color: yellow; +} +</style> +Before <div class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> After</div> +<p>Following content.</p> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-016.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-016.tentative.html new file mode 100644 index 0000000000..09714c499d --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-016.tentative.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp with -webkit-box and -webkit-box-orient</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-005-ref.html"> +<meta name="assert" content="If display: -webkit-box and -webkit-box-orient: vertical are present on the same box as line-clamp, it becomes a block container, and so line-clamp applies to that box."> +<style> +.clamp { + display: -webkit-box; + -webkit-box-orient: vertical; + line-clamp: 4; + font: 16px / 32px serif; + white-space: pre; + padding: 0 4px; + background-color: yellow; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-017.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-017.tentative.html new file mode 100644 index 0000000000..11d6ceeb55 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-017.tentative.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp with -webkit-box and -webkit-box-orient</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-005-ref.html"> +<meta name="assert" content="If display: -webkit-box and -webkit-box-orient: vertical are present on the same box as line-clamp, it becomes a block container, and other flexbox properties don't apply."> +<style> +.clamp { + display: -webkit-box; + -webkit-box-orient: vertical; + line-clamp: 4; + font: 16px / 32px serif; + white-space: pre; + padding: 0 4px; + background-color: yellow; + + /* These properties horizontally center the child, if this box is either a + * -webkit-box or a regular flexbox. */ + -webkit-box-align: center; + align-items: center; +} +</style> +<div class="clamp"><div>Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div></div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-018.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-018.tentative.html new file mode 100644 index 0000000000..af75f7dfb0 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-018.tentative.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp with -webkit-box and -webkit-box-orient</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-001-ref.html"> +<meta name="assert" content="If display: -webkit-box and -webkit-box-orient: vertical are present on the same box as line-clamp, it becomes a block container, and other flexbox properties don't apply. This happens even if there is no clamping."> +<style> +.clamp { + display: -webkit-box; + -webkit-box-orient: vertical; + line-clamp: 6; + font: 16px / 32px serif; + white-space: pre; + background-color: yellow; + + /* These properties horizontally center the child, if this box is either a + * -webkit-box or a regular flexbox. */ + -webkit-box-align: center; + align-items: center; +} +</style> +<div class="clamp"><div>Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div></div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-019.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-019.tentative.html new file mode 100644 index 0000000000..b39376d395 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-019.tentative.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp takes priority over -webkit-line-clamp</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-005-ref.html"> +<meta name="assert" content="If both line-clamp and -webkit-line-clamp are present, line-clamp takes priority"> +<style> +.clamp { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 4; + font: 16px / 32px serif; + white-space: pre; + padding: 0 4px; + background-color: yellow; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-020.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-020.tentative.html new file mode 100644 index 0000000000..9d8a2b4d06 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-020.tentative.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp: none has no effect</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-001-ref.html"> +<meta name="assert" content="line-clamp: none should have no effect."> +<style> +.clamp { + line-clamp: none; + font: 16px / 32px serif; + white-space: pre; + background-color: yellow; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-001.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-001.tentative.html new file mode 100644 index 0000000000..79667f23fb --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-001.tentative.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: abspos at the start of a line-clamp</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/line-clamp-with-abspos-001-ref.html"> +<meta name="assert" content="Absolute positioned boxes in an inline formatting context inside a line-clamp container are not hidden if they are in the box tree before the clamp point."> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +.abspos { + position: absolute; + right: 0; + width: 50px; + height: 50px; + margin: 4px; + background-color: skyblue; +} +</style> +<div class="clamp"><div class="abspos"></div>Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-002.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-002.tentative.html new file mode 100644 index 0000000000..cecb9d52bc --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-002.tentative.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: abspos at the start of a line-clamp</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/line-clamp-with-abspos-001-ref.html"> +<meta name="assert" content="Absolute positioned boxes in a block formatting context inside a line-clamp container are not hidden if they are in the box tree before the clamp point."> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + padding: 0 4px; + background-color: yellow; +} +.abspos { + position: absolute; + right: 0; + width: 50px; + height: 50px; + margin: 4px; + background-color: skyblue; +} +.pre { + white-space: pre; +} +</style> +<div class="clamp"> +<div class="abspos"></div> +<div class="pre">Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> +</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-003.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-003.tentative.html new file mode 100644 index 0000000000..e4bd1de222 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-003.tentative.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: abspos in line-clamp after clamp point</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-005-ref.html"> +<meta name="assert" content="Absolute positioned boxes in an inline formatting context inside a line-clamp container are always hidden if they are in the box tree after the clamp point."> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +.abspos { + position: absolute; + right: 0; + width: 50px; + height: 50px; + margin: 4px; + background-color: skyblue; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +<div class="abspos"></div>Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-004.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-004.tentative.html new file mode 100644 index 0000000000..483e6d1da6 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-004.tentative.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: abspos in line-clamp after clamp point</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-005-ref.html"> +<meta name="assert" content="Absolute positioned boxes in a block formatting context inside a line-clamp container are always hidden if they are in the box tree after the clamp point."> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + padding: 0 4px; + background-color: yellow; +} +.abspos { + position: absolute; + right: 0; + width: 50px; + height: 50px; + margin: 4px; + background-color: skyblue; +} +.pre { + white-space: pre; +} +</style> +<div class="clamp"> +<div class="pre">Line 1 +Line 2 +Line 3 +Line 4</div> +<div class="abspos"></div> +<div>Line 5</div> +</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-005.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-005.tentative.html new file mode 100644 index 0000000000..3dc77831a0 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-005.tentative.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: abspos in line-clamp before clamp point which overflows</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/line-clamp-with-abspos-005-ref.html"> +<meta name="assert" content="Absolute positioned boxes in an inline formatting context inside a line-clamp container are not hidden if they are in the box tree before the clamp point, even if they visually extend beyond that point"> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +.abspos { + position: absolute; + right: 0; + width: 50px; + height: 50px; + margin: 4px; + background-color: skyblue; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4<div class="abspos"></div> +Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-006.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-006.tentative.html new file mode 100644 index 0000000000..f18fed6c2d --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-006.tentative.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: abspos in line-clamp before clamp point which overflows</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/line-clamp-with-abspos-006-ref.html"> +<meta name="assert" content="Absolute positioned boxes in a block formatting context inside a line-clamp container are not hidden if they are in the box tree before the clamp point, even if they visually extend beyond that point"> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + padding: 0 4px; + background-color: yellow; +} +.abspos { + position: absolute; + right: 0; + width: 50px; + height: 75px; + margin: 4px; + background-color: skyblue; +} +.pre { + white-space: pre; +} +</style> +<div class="clamp"> +<div class="pre">Line 1 +Line 2 +Line 3</div> +<div class="abspos"></div> +<div class="pre">Line 4 +Line 5</div> +</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-007.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-007.tentative.html new file mode 100644 index 0000000000..f0a1f58c8d --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-007.tentative.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: abspos in line-clamp before clamp point positioned after it</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/line-clamp-with-abspos-007-ref.html"> +<meta name="assert" content="Absolute positioned boxes inside a line-clamp container are not hidden if they are in the box tree before the clamp point, even if they are positioned after that point"> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +.abspos { + position: absolute; + top: 148px; + right: 0; + width: 50px; + height: 50px; + margin: 4px; + background-color: skyblue; +} +</style> +<div class="clamp"><div class="abspos"></div>Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-008.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-008.tentative.html new file mode 100644 index 0000000000..9c62e44f38 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-008.tentative.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: bottom: 0 abspos in line-clamp before clamp point</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/line-clamp-with-abspos-008-ref.html"> +<meta name="assert" content="Absolute positioned boxes inside a line-clamp container are not hidden if they are in the box tree before the clamp point."> +<style> +.clamp { + line-clamp: 4; + position: relative; + font: 16px / 32px serif; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +.abspos { + position: absolute; + bottom: 0; + right: 0; + width: 50px; + height: 50px; + margin: 4px; + background-color: skyblue; +} +</style> +<div class="clamp"><div class="abspos"></div>Line 1 +Line 2 +Line 3 +Line 4 +Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-009.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-009.tentative.html new file mode 100644 index 0000000000..dce04d720c --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-009.tentative.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: abspos in line-clamp after clamp point positioned before it</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/webkit-line-clamp-005-ref.html"> +<meta name="assert" content="Absolute positioned boxes inside a line-clamp container are hidden if they are in the box tree after the clamp point, even if they are positioned before that point"> +<style> +.clamp { + line-clamp: 4; + position: relative; + font: 16px / 32px serif; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +.abspos { + position: absolute; + top: 0; + right: 0; + width: 50px; + height: 50px; + margin: 4px; + background-color: skyblue; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4 +Line 5<div class="abspos"></div></div> diff --git a/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-010.tentative.html b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-010.tentative.html new file mode 100644 index 0000000000..325278b3a0 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/line-clamp-with-abspos-010.tentative.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Overflow: line-clamp doesn't propagate to abspos</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-overflow-4/#line-clamp"> +<link rel="match" href="reference/line-clamp-with-abspos-010-ref.html"> +<meta name="assert" content="Absolute positioned boxes create a new BFC, and line-clamp does not propagate into independent BFCs"> +<style> +.clamp { + line-clamp: 4; + font: 16px / 32px serif; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +.abspos { + position: absolute; + right: 0; + margin: 4px; + white-space: pre; + background-color: skyblue; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4<div class="abspos">Line A +Line B +Line C +Line D +Line E</div> +Line 5</div> diff --git a/testing/web-platform/tests/css/css-overflow/reference/line-clamp-010-ref.html b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-010-ref.html new file mode 100644 index 0000000000..46ca731c54 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-010-ref.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + font: 16px / 32px serif; + white-space: pre; + height: 4lh; + padding: 0 4px; + background-color: yellow; +} +</style> +<div class="clamp">Line 1 +Line 2…</div> diff --git a/testing/web-platform/tests/css/css-overflow/reference/line-clamp-011-ref.html b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-011-ref.html new file mode 100644 index 0000000000..04297fff2b --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-011-ref.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + font: 16px / 32px serif; + white-space: pre; + height: 3lh; + padding: 0 4px; + background-color: yellow; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4…</div> +<p>Following content.</p> diff --git a/testing/web-platform/tests/css/css-overflow/reference/line-clamp-012-ref.html b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-012-ref.html new file mode 100644 index 0000000000..f412e0110d --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-012-ref.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + font: 16px / 32px serif; + padding: 4px; + white-space: pre; + background-color: yellow; + border: 2px solid black; +} +.inner { + background-color: skyblue; + padding: 4px; + border: 2px solid purple; +} +</style> +<div class="clamp">Line 1 +Line 2<div class="inner">Line 3 +Line 4…</div></div> +<p>Following content.</p> diff --git a/testing/web-platform/tests/css/css-overflow/reference/line-clamp-013-ref.html b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-013-ref.html new file mode 100644 index 0000000000..b2eb05e884 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-013-ref.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + font: 16px / 32px serif; + padding: 4px; + white-space: pre; + background-color: yellow; + border: 2px solid black; +} +.inner { + background-color: skyblue; + height: 3lh; + padding: 4px; + border: 2px solid purple; +} +</style> +<div class="clamp">Line 1 +Line 2<div class="inner">Line 3 +Line 4…</div></div> +<p>Following content.</p> diff --git a/testing/web-platform/tests/css/css-overflow/reference/line-clamp-015-ref.html b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-015-ref.html new file mode 100644 index 0000000000..1af45c1225 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-015-ref.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + display: inline-block; + font: 16px / 32px monospace; + white-space: pre; + padding: 0 4px; + width: 7.01ch; + background-color: yellow; +} +</style> +Before <div class="clamp">Line 1 +Line 2 +Line 3…</div> After</div> +<p>Following content.</p> diff --git a/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-001-ref.html b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-001-ref.html new file mode 100644 index 0000000000..d756162dde --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-001-ref.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + font: 16px / 32px serif; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +.abspos { + position: absolute; + right: 0; + width: 50px; + height: 50px; + margin: 4px; + background-color: skyblue; +} +</style> +<div class="clamp"><div class="abspos"></div>Line 1 +Line 2 +Line 3 +Line 4…</div> diff --git a/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-005-ref.html b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-005-ref.html new file mode 100644 index 0000000000..3b1f9218e8 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-005-ref.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + font: 16px / 32px serif; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +.abspos { + position: absolute; + right: 0; + width: 50px; + height: 50px; + margin: 4px; + background-color: skyblue; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4…<div class="abspos"></div></div> diff --git a/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-006-ref.html b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-006-ref.html new file mode 100644 index 0000000000..4b55c37a03 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-006-ref.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + font: 16px / 32px serif; + padding: 0 4px; + background-color: yellow; +} +.abspos { + position: absolute; + right: 0; + width: 50px; + height: 75px; + margin: 4px; + background-color: skyblue; +} +.pre { + white-space: pre; +} +</style> +<div class="clamp"> +<div class="pre">Line 1 +Line 2 +Line 3</div> +<div class="abspos"></div> +<div class="pre">Line 4…</div> +</div> diff --git a/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-007-ref.html b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-007-ref.html new file mode 100644 index 0000000000..e3dcc696e3 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-007-ref.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + font: 16px / 32px serif; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +.abspos { + position: absolute; + top: 148px; + right: 0; + width: 50px; + height: 50px; + margin: 4px; + background-color: skyblue; +} +</style> +<div class="clamp"><div class="abspos"></div>Line 1 +Line 2 +Line 3 +Line 4…</div> diff --git a/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-008-ref.html b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-008-ref.html new file mode 100644 index 0000000000..373b2755c1 --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-008-ref.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + position: relative; + font: 16px / 32px serif; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +.abspos { + position: absolute; + bottom: 0; + right: 0; + width: 50px; + height: 50px; + margin: 4px; + background-color: skyblue; +} +</style> +<div class="clamp"><div class="abspos"></div>Line 1 +Line 2 +Line 3 +Line 4…</div> diff --git a/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-010-ref.html b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-010-ref.html new file mode 100644 index 0000000000..ecc2fcee1b --- /dev/null +++ b/testing/web-platform/tests/css/css-overflow/reference/line-clamp-with-abspos-010-ref.html @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Reference</title> +<style> +.clamp { + font: 16px / 32px serif; + padding: 0 4px; + white-space: pre; + background-color: yellow; +} +.abspos { + position: absolute; + right: 0; + margin: 4px; + white-space: pre; + background-color: skyblue; +} +</style> +<div class="clamp">Line 1 +Line 2 +Line 3 +Line 4…<div class="abspos">Line A +Line B +Line C +Line D +Line E</div></div> diff --git a/testing/web-platform/tests/css/printing/crashtests/root-element-remove-print.html b/testing/web-platform/tests/css/css-page/crashtests/root-element-remove-print.html index 8497e8c4fd..8497e8c4fd 100644 --- a/testing/web-platform/tests/css/printing/crashtests/root-element-remove-print.html +++ b/testing/web-platform/tests/css/css-page/crashtests/root-element-remove-print.html diff --git a/testing/web-platform/tests/css/printing/crashtests/tall-inline-block-in-float-in-table-cell-print.html b/testing/web-platform/tests/css/css-page/crashtests/tall-inline-block-in-float-in-table-cell-print.html index c70dce2160..c70dce2160 100644 --- a/testing/web-platform/tests/css/printing/crashtests/tall-inline-block-in-float-in-table-cell-print.html +++ b/testing/web-platform/tests/css/css-page/crashtests/tall-inline-block-in-float-in-table-cell-print.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-001-print-ref.html b/testing/web-platform/tests/css/css-page/fixedpos-001-print-ref.html index 3d66305db0..3d66305db0 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-001-print-ref.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-001-print-ref.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-001-print.html b/testing/web-platform/tests/css/css-page/fixedpos-001-print.html index 04feb96e84..04feb96e84 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-001-print.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-001-print.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-002-print-ref.html b/testing/web-platform/tests/css/css-page/fixedpos-002-print-ref.html index 3d66305db0..3d66305db0 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-002-print-ref.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-002-print-ref.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-002-print.html b/testing/web-platform/tests/css/css-page/fixedpos-002-print.html index c23c6be7d2..c23c6be7d2 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-002-print.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-002-print.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-003-print-ref.html b/testing/web-platform/tests/css/css-page/fixedpos-003-print-ref.html index 3d66305db0..3d66305db0 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-003-print-ref.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-003-print-ref.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-003-print.html b/testing/web-platform/tests/css/css-page/fixedpos-003-print.html index 1b06257175..1b06257175 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-003-print.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-003-print.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-004-print-ref.html b/testing/web-platform/tests/css/css-page/fixedpos-004-print-ref.html index 3e3473bcb8..3e3473bcb8 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-004-print-ref.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-004-print-ref.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-004-print.html b/testing/web-platform/tests/css/css-page/fixedpos-004-print.html index c138e9cd6a..c138e9cd6a 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-004-print.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-004-print.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-005-print-ref.html b/testing/web-platform/tests/css/css-page/fixedpos-005-print-ref.html index e692ff8db3..e692ff8db3 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-005-print-ref.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-005-print-ref.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-005-print.html b/testing/web-platform/tests/css/css-page/fixedpos-005-print.html index 0a2edc7178..0a2edc7178 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-005-print.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-005-print.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-006-print-ref.html b/testing/web-platform/tests/css/css-page/fixedpos-006-print-ref.html index b03e1d78d0..b03e1d78d0 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-006-print-ref.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-006-print-ref.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-006-print.html b/testing/web-platform/tests/css/css-page/fixedpos-006-print.html index 2386c166c8..2386c166c8 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-006-print.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-006-print.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-007-print-ref.html b/testing/web-platform/tests/css/css-page/fixedpos-007-print-ref.html index f576c93771..f576c93771 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-007-print-ref.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-007-print-ref.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-007-print.html b/testing/web-platform/tests/css/css-page/fixedpos-007-print.html index 8dcb700b96..8dcb700b96 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-007-print.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-007-print.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-008-print-ref.html b/testing/web-platform/tests/css/css-page/fixedpos-008-print-ref.html index 6ed2528115..6ed2528115 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-008-print-ref.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-008-print-ref.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-008-print.html b/testing/web-platform/tests/css/css-page/fixedpos-008-print.html index 02b5d63cc7..02b5d63cc7 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-008-print.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-008-print.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-with-abspos-with-link-print-ref.html b/testing/web-platform/tests/css/css-page/fixedpos-with-abspos-with-link-print-ref.html index 000c05350b..000c05350b 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-with-abspos-with-link-print-ref.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-with-abspos-with-link-print-ref.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-with-abspos-with-link-print.html b/testing/web-platform/tests/css/css-page/fixedpos-with-abspos-with-link-print.html index 057ddc9146..057ddc9146 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-with-abspos-with-link-print.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-with-abspos-with-link-print.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-with-iframe-print-ref.html b/testing/web-platform/tests/css/css-page/fixedpos-with-iframe-print-ref.html index 5c17140450..5c17140450 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-with-iframe-print-ref.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-with-iframe-print-ref.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-with-iframe-print.html b/testing/web-platform/tests/css/css-page/fixedpos-with-iframe-print.html index 5102d045c4..5102d045c4 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-with-iframe-print.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-with-iframe-print.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-with-link-with-inline-child-print-ref.html b/testing/web-platform/tests/css/css-page/fixedpos-with-link-with-inline-child-print-ref.html index f12d31acd9..f12d31acd9 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-with-link-with-inline-child-print-ref.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-with-link-with-inline-child-print-ref.html diff --git a/testing/web-platform/tests/css/printing/fixedpos-with-link-with-inline-child-print.html b/testing/web-platform/tests/css/css-page/fixedpos-with-link-with-inline-child-print.html index 694e5376cb..694e5376cb 100644 --- a/testing/web-platform/tests/css/printing/fixedpos-with-link-with-inline-child-print.html +++ b/testing/web-platform/tests/css/css-page/fixedpos-with-link-with-inline-child-print.html diff --git a/testing/web-platform/tests/css/printing/media-queries-001-print-ref.html b/testing/web-platform/tests/css/css-page/media-queries-001-print-ref.html index 35af04dc3c..35af04dc3c 100644 --- a/testing/web-platform/tests/css/printing/media-queries-001-print-ref.html +++ b/testing/web-platform/tests/css/css-page/media-queries-001-print-ref.html diff --git a/testing/web-platform/tests/css/printing/media-queries-001-print.html b/testing/web-platform/tests/css/css-page/media-queries-001-print.html index 01b2a00e47..01b2a00e47 100644 --- a/testing/web-platform/tests/css/printing/media-queries-001-print.html +++ b/testing/web-platform/tests/css/css-page/media-queries-001-print.html diff --git a/testing/web-platform/tests/css/printing/media-queries-002-print.html b/testing/web-platform/tests/css/css-page/media-queries-002-print.html index 5f71f3d17a..5f71f3d17a 100644 --- a/testing/web-platform/tests/css/printing/media-queries-002-print.html +++ b/testing/web-platform/tests/css/css-page/media-queries-002-print.html diff --git a/testing/web-platform/tests/css/printing/media-queries-003-print-ref.html b/testing/web-platform/tests/css/css-page/media-queries-003-print-ref.html index 99642ca567..99642ca567 100644 --- a/testing/web-platform/tests/css/printing/media-queries-003-print-ref.html +++ b/testing/web-platform/tests/css/css-page/media-queries-003-print-ref.html diff --git a/testing/web-platform/tests/css/printing/media-queries-003-print.html b/testing/web-platform/tests/css/css-page/media-queries-003-print.html index 2c125296ad..2c125296ad 100644 --- a/testing/web-platform/tests/css/printing/media-queries-003-print.html +++ b/testing/web-platform/tests/css/css-page/media-queries-003-print.html diff --git a/testing/web-platform/tests/css/printing/page-margin-001-print-ref.html b/testing/web-platform/tests/css/css-page/page-margin-001-print-ref.html index 12ebd76dc9..12ebd76dc9 100644 --- a/testing/web-platform/tests/css/printing/page-margin-001-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-margin-001-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-margin-001-print.html b/testing/web-platform/tests/css/css-page/page-margin-001-print.html index c59b3c6427..c59b3c6427 100644 --- a/testing/web-platform/tests/css/printing/page-margin-001-print.html +++ b/testing/web-platform/tests/css/css-page/page-margin-001-print.html diff --git a/testing/web-platform/tests/css/printing/page-margin-002-print-ref.html b/testing/web-platform/tests/css/css-page/page-margin-002-print-ref.html index 2b56504855..2b56504855 100644 --- a/testing/web-platform/tests/css/printing/page-margin-002-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-margin-002-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-margin-002-print.html b/testing/web-platform/tests/css/css-page/page-margin-002-print.html index 944d7efcfd..944d7efcfd 100644 --- a/testing/web-platform/tests/css/printing/page-margin-002-print.html +++ b/testing/web-platform/tests/css/css-page/page-margin-002-print.html diff --git a/testing/web-platform/tests/css/printing/page-margin-003-print-ref.html b/testing/web-platform/tests/css/css-page/page-margin-003-print-ref.html index 52bf736090..52bf736090 100644 --- a/testing/web-platform/tests/css/printing/page-margin-003-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-margin-003-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-margin-003-print.html b/testing/web-platform/tests/css/css-page/page-margin-003-print.html index e7410c02e0..e7410c02e0 100644 --- a/testing/web-platform/tests/css/printing/page-margin-003-print.html +++ b/testing/web-platform/tests/css/css-page/page-margin-003-print.html diff --git a/testing/web-platform/tests/css/printing/page-margin-004-print-ref.html b/testing/web-platform/tests/css/css-page/page-margin-004-print-ref.html index 24549d4954..24549d4954 100644 --- a/testing/web-platform/tests/css/printing/page-margin-004-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-margin-004-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-margin-004-print.html b/testing/web-platform/tests/css/css-page/page-margin-004-print.html index cb9343dcf1..cb9343dcf1 100644 --- a/testing/web-platform/tests/css/printing/page-margin-004-print.html +++ b/testing/web-platform/tests/css/css-page/page-margin-004-print.html diff --git a/testing/web-platform/tests/css/printing/page-margin-005-print-ref.html b/testing/web-platform/tests/css/css-page/page-margin-005-print-ref.html index bd1829be38..bd1829be38 100644 --- a/testing/web-platform/tests/css/printing/page-margin-005-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-margin-005-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-margin-005-print.html b/testing/web-platform/tests/css/css-page/page-margin-005-print.html index 8ca80bc697..8ca80bc697 100644 --- a/testing/web-platform/tests/css/printing/page-margin-005-print.html +++ b/testing/web-platform/tests/css/css-page/page-margin-005-print.html diff --git a/testing/web-platform/tests/css/printing/page-margin-006-print-ref.html b/testing/web-platform/tests/css/css-page/page-margin-006-print-ref.html index fe7a2c66db..fe7a2c66db 100644 --- a/testing/web-platform/tests/css/printing/page-margin-006-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-margin-006-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-margin-006-print.html b/testing/web-platform/tests/css/css-page/page-margin-006-print.html index 43621acf80..43621acf80 100644 --- a/testing/web-platform/tests/css/printing/page-margin-006-print.html +++ b/testing/web-platform/tests/css/css-page/page-margin-006-print.html diff --git a/testing/web-platform/tests/css/css-page/page-margin-negative-print-ref.tentative.html b/testing/web-platform/tests/css/css-page/page-margin-negative-print-ref.tentative.html new file mode 100644 index 0000000000..9e816c12fb --- /dev/null +++ b/testing/web-platform/tests/css/css-page/page-margin-negative-print-ref.tentative.html @@ -0,0 +1,15 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<style> + @page { + size: 300px; + margin: 0; + } + body { + margin: 0; + background: green; + } +</style> +<div> + Green background, no red / yellow. +</div> diff --git a/testing/web-platform/tests/css/css-page/page-margin-negative-print.tentative.html b/testing/web-platform/tests/css/css-page/page-margin-negative-print.tentative.html new file mode 100644 index 0000000000..205a13f7a5 --- /dev/null +++ b/testing/web-platform/tests/css/css-page/page-margin-negative-print.tentative.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-page-3/#page-properties"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/8508"> +<link rel="match" href="page-margin-negative-print-ref.tentative.html"> +<style> + @page { + size: 300px; + margin: -20px; + } + body { + margin: 0; + background: yellow; + } + .fullpager { + width: 300px; + height: 300px; + border: 20px solid red; + background: green; + } +</style> +<div class="fullpager"> + Green background, no red / yellow. +</div> diff --git a/testing/web-platform/tests/css/printing/page-name-001-print-ref.html b/testing/web-platform/tests/css/css-page/page-name-000-print-ref.html index 69c5c37b5a..69c5c37b5a 100644 --- a/testing/web-platform/tests/css/printing/page-name-001-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-name-000-print-ref.html diff --git a/testing/web-platform/tests/css/css-page/page-name-000-print.html b/testing/web-platform/tests/css/css-page/page-name-000-print.html new file mode 100644 index 0000000000..f01d5cac48 --- /dev/null +++ b/testing/web-platform/tests/css/css-page/page-name-000-print.html @@ -0,0 +1,10 @@ +<!DOCTYPE html> +<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> +<link rel="help" href="https://drafts.csswg.org/css-page-3/#using-named-pages"> +<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1414718"> +<link rel="match" href="page-name-000-print-ref.html"> +<div style="page:foo;"> + <div style="float:left;">First page</div> + <div style="clear:both;">Also first page</div> + <div style="page:bar;">Second page</div> +</div> diff --git a/testing/web-platform/tests/css/printing/page-name-002-print-ref.html b/testing/web-platform/tests/css/css-page/page-name-002-print-ref.html index 17ecc93e57..17ecc93e57 100644 --- a/testing/web-platform/tests/css/printing/page-name-002-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-name-002-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-name-002-print.html b/testing/web-platform/tests/css/css-page/page-name-002-print.html index 060b93b0bf..060b93b0bf 100644 --- a/testing/web-platform/tests/css/printing/page-name-002-print.html +++ b/testing/web-platform/tests/css/css-page/page-name-002-print.html diff --git a/testing/web-platform/tests/css/printing/page-name-003-print-ref.html b/testing/web-platform/tests/css/css-page/page-name-003-print-ref.html index 52ea6bfffc..52ea6bfffc 100644 --- a/testing/web-platform/tests/css/printing/page-name-003-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-name-003-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-name-003-print.html b/testing/web-platform/tests/css/css-page/page-name-003-print.html index 9ef4db8c75..9ef4db8c75 100644 --- a/testing/web-platform/tests/css/printing/page-name-003-print.html +++ b/testing/web-platform/tests/css/css-page/page-name-003-print.html diff --git a/testing/web-platform/tests/css/printing/page-name-and-break-001-print.html b/testing/web-platform/tests/css/css-page/page-name-and-break-001-print.html index 04de5f7073..04de5f7073 100644 --- a/testing/web-platform/tests/css/printing/page-name-and-break-001-print.html +++ b/testing/web-platform/tests/css/css-page/page-name-and-break-001-print.html diff --git a/testing/web-platform/tests/css/printing/page-name-and-break-002-print.html b/testing/web-platform/tests/css/css-page/page-name-and-break-002-print.html index 3dd755b027..3dd755b027 100644 --- a/testing/web-platform/tests/css/printing/page-name-and-break-002-print.html +++ b/testing/web-platform/tests/css/css-page/page-name-and-break-002-print.html diff --git a/testing/web-platform/tests/css/printing/page-name-and-break-003-print.html b/testing/web-platform/tests/css/css-page/page-name-and-break-003-print.html index b7e8a20e1d..b7e8a20e1d 100644 --- a/testing/web-platform/tests/css/printing/page-name-and-break-003-print.html +++ b/testing/web-platform/tests/css/css-page/page-name-and-break-003-print.html diff --git a/testing/web-platform/tests/css/printing/page-name-and-break-004-print.html b/testing/web-platform/tests/css/css-page/page-name-and-break-004-print.html index 2e252ea4eb..2e252ea4eb 100644 --- a/testing/web-platform/tests/css/printing/page-name-and-break-004-print.html +++ b/testing/web-platform/tests/css/css-page/page-name-and-break-004-print.html diff --git a/testing/web-platform/tests/css/printing/page-name-and-break-print-ref.html b/testing/web-platform/tests/css/css-page/page-name-and-break-print-ref.html index e3f73e41e2..e3f73e41e2 100644 --- a/testing/web-platform/tests/css/printing/page-name-and-break-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-name-and-break-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-size-001-print-ref.html b/testing/web-platform/tests/css/css-page/page-size-001-print-ref.html index b016d8949f..b016d8949f 100644 --- a/testing/web-platform/tests/css/printing/page-size-001-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-size-001-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-size-001-print.html b/testing/web-platform/tests/css/css-page/page-size-001-print.html index 19d72b0231..19d72b0231 100644 --- a/testing/web-platform/tests/css/printing/page-size-001-print.html +++ b/testing/web-platform/tests/css/css-page/page-size-001-print.html diff --git a/testing/web-platform/tests/css/printing/page-size-002-print-ref.html b/testing/web-platform/tests/css/css-page/page-size-002-print-ref.html index 78a1646943..78a1646943 100644 --- a/testing/web-platform/tests/css/printing/page-size-002-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-size-002-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-size-002-print.html b/testing/web-platform/tests/css/css-page/page-size-002-print.html index 243c382ca2..243c382ca2 100644 --- a/testing/web-platform/tests/css/printing/page-size-002-print.html +++ b/testing/web-platform/tests/css/css-page/page-size-002-print.html diff --git a/testing/web-platform/tests/css/printing/page-size-003-print-ref.html b/testing/web-platform/tests/css/css-page/page-size-003-print-ref.html index 38ea150878..38ea150878 100644 --- a/testing/web-platform/tests/css/printing/page-size-003-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-size-003-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-size-003-print.html b/testing/web-platform/tests/css/css-page/page-size-003-print.html index 805ff0e568..805ff0e568 100644 --- a/testing/web-platform/tests/css/printing/page-size-003-print.html +++ b/testing/web-platform/tests/css/css-page/page-size-003-print.html diff --git a/testing/web-platform/tests/css/printing/page-size-004-print-ref.html b/testing/web-platform/tests/css/css-page/page-size-004-print-ref.html index 0468a745b2..0468a745b2 100644 --- a/testing/web-platform/tests/css/printing/page-size-004-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-size-004-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-size-004-print.html b/testing/web-platform/tests/css/css-page/page-size-004-print.html index 125d7636d2..125d7636d2 100644 --- a/testing/web-platform/tests/css/printing/page-size-004-print.html +++ b/testing/web-platform/tests/css/css-page/page-size-004-print.html diff --git a/testing/web-platform/tests/css/printing/page-size-005-print-ref.html b/testing/web-platform/tests/css/css-page/page-size-005-print-ref.html index c2e07daa29..c2e07daa29 100644 --- a/testing/web-platform/tests/css/printing/page-size-005-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-size-005-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-size-005-print.html b/testing/web-platform/tests/css/css-page/page-size-005-print.html index 37876ae230..37876ae230 100644 --- a/testing/web-platform/tests/css/printing/page-size-005-print.html +++ b/testing/web-platform/tests/css/css-page/page-size-005-print.html diff --git a/testing/web-platform/tests/css/printing/page-size-006-print-ref.html b/testing/web-platform/tests/css/css-page/page-size-006-print-ref.html index 7167da2ac2..7167da2ac2 100644 --- a/testing/web-platform/tests/css/printing/page-size-006-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-size-006-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-size-006-print.html b/testing/web-platform/tests/css/css-page/page-size-006-print.html index 1da29d334d..1da29d334d 100644 --- a/testing/web-platform/tests/css/printing/page-size-006-print.html +++ b/testing/web-platform/tests/css/css-page/page-size-006-print.html diff --git a/testing/web-platform/tests/css/printing/page-size-007-print-ref.html b/testing/web-platform/tests/css/css-page/page-size-007-print-ref.html index 9498e2c17b..9498e2c17b 100644 --- a/testing/web-platform/tests/css/printing/page-size-007-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-size-007-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-size-007-print.html b/testing/web-platform/tests/css/css-page/page-size-007-print.html index 50ab2f4d10..50ab2f4d10 100644 --- a/testing/web-platform/tests/css/printing/page-size-007-print.html +++ b/testing/web-platform/tests/css/css-page/page-size-007-print.html diff --git a/testing/web-platform/tests/css/printing/page-size-008-print-ref.html b/testing/web-platform/tests/css/css-page/page-size-008-print-ref.html index f50ad3531e..f50ad3531e 100644 --- a/testing/web-platform/tests/css/printing/page-size-008-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-size-008-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-size-008-print.html b/testing/web-platform/tests/css/css-page/page-size-008-print.html index 683bdc3a50..683bdc3a50 100644 --- a/testing/web-platform/tests/css/printing/page-size-008-print.html +++ b/testing/web-platform/tests/css/css-page/page-size-008-print.html diff --git a/testing/web-platform/tests/css/printing/page-size-009-print-ref.html b/testing/web-platform/tests/css/css-page/page-size-009-print-ref.html index 86ffa278c9..86ffa278c9 100644 --- a/testing/web-platform/tests/css/printing/page-size-009-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-size-009-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-size-009-print.html b/testing/web-platform/tests/css/css-page/page-size-009-print.html index 48ead4eab3..48ead4eab3 100644 --- a/testing/web-platform/tests/css/printing/page-size-009-print.html +++ b/testing/web-platform/tests/css/css-page/page-size-009-print.html diff --git a/testing/web-platform/tests/css/printing/page-size-010-print-ref.html b/testing/web-platform/tests/css/css-page/page-size-010-print-ref.html index 27cbe4435c..27cbe4435c 100644 --- a/testing/web-platform/tests/css/printing/page-size-010-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-size-010-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-size-010-print.html b/testing/web-platform/tests/css/css-page/page-size-010-print.html index 21205e06a0..21205e06a0 100644 --- a/testing/web-platform/tests/css/printing/page-size-010-print.html +++ b/testing/web-platform/tests/css/css-page/page-size-010-print.html diff --git a/testing/web-platform/tests/css/printing/page-size-011-print-ref.html b/testing/web-platform/tests/css/css-page/page-size-011-print-ref.html index 945f105ef4..945f105ef4 100644 --- a/testing/web-platform/tests/css/printing/page-size-011-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-size-011-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-size-011-print.html b/testing/web-platform/tests/css/css-page/page-size-011-print.html index 2747605806..2747605806 100644 --- a/testing/web-platform/tests/css/printing/page-size-011-print.html +++ b/testing/web-platform/tests/css/css-page/page-size-011-print.html diff --git a/testing/web-platform/tests/css/printing/page-visibility-hidden-001-print-ref.html b/testing/web-platform/tests/css/css-page/page-visibility-hidden-001-print-ref.html index 24d3806eb9..24d3806eb9 100644 --- a/testing/web-platform/tests/css/printing/page-visibility-hidden-001-print-ref.html +++ b/testing/web-platform/tests/css/css-page/page-visibility-hidden-001-print-ref.html diff --git a/testing/web-platform/tests/css/printing/page-visibility-hidden-001-print.html b/testing/web-platform/tests/css/css-page/page-visibility-hidden-001-print.html index 0de5c5ae82..0de5c5ae82 100644 --- a/testing/web-platform/tests/css/printing/page-visibility-hidden-001-print.html +++ b/testing/web-platform/tests/css/css-page/page-visibility-hidden-001-print.html diff --git a/testing/web-platform/tests/css/printing/reference/blank-print-ref.html b/testing/web-platform/tests/css/css-page/reference/blank-print-ref.html index ad73420cdb..ad73420cdb 100644 --- a/testing/web-platform/tests/css/printing/reference/blank-print-ref.html +++ b/testing/web-platform/tests/css/css-page/reference/blank-print-ref.html diff --git a/testing/web-platform/tests/css/printing/reference/filled-green-100px-square-print-ref.html b/testing/web-platform/tests/css/css-page/reference/filled-green-100px-square-print-ref.html index d4834d1358..d4834d1358 100644 --- a/testing/web-platform/tests/css/printing/reference/filled-green-100px-square-print-ref.html +++ b/testing/web-platform/tests/css/css-page/reference/filled-green-100px-square-print-ref.html diff --git a/testing/web-platform/tests/css/printing/remote-origin-iframe-print-ref.html b/testing/web-platform/tests/css/css-page/remote-origin-iframe-print-ref.html index f632519cd6..f632519cd6 100644 --- a/testing/web-platform/tests/css/printing/remote-origin-iframe-print-ref.html +++ b/testing/web-platform/tests/css/css-page/remote-origin-iframe-print-ref.html diff --git a/testing/web-platform/tests/css/printing/remote-origin-iframe-print.html b/testing/web-platform/tests/css/css-page/remote-origin-iframe-print.html index aba10a2a39..73b3878386 100644 --- a/testing/web-platform/tests/css/printing/remote-origin-iframe-print.html +++ b/testing/web-platform/tests/css/css-page/remote-origin-iframe-print.html @@ -14,6 +14,6 @@ <p>The word "PASS" should be seen below.</p> <script> const iframe = document.createElement("iframe"); - iframe.src = get_host_info().REMOTE_ORIGIN + "/css/printing/resources/iframe-with-abspos.html"; + iframe.src = get_host_info().REMOTE_ORIGIN + "/css/css-page/resources/iframe-with-abspos.html"; document.body.appendChild(iframe); </script> diff --git a/testing/web-platform/tests/css/printing/resources/iframe-with-abspos.html b/testing/web-platform/tests/css/css-page/resources/iframe-with-abspos.html index 27de3aad5e..27de3aad5e 100644 --- a/testing/web-platform/tests/css/printing/resources/iframe-with-abspos.html +++ b/testing/web-platform/tests/css/css-page/resources/iframe-with-abspos.html diff --git a/testing/web-platform/tests/css/printing/resources/mq-frame-100px.html b/testing/web-platform/tests/css/css-page/resources/mq-frame-100px.html index ed823c09e8..ed823c09e8 100644 --- a/testing/web-platform/tests/css/printing/resources/mq-frame-100px.html +++ b/testing/web-platform/tests/css/css-page/resources/mq-frame-100px.html diff --git a/testing/web-platform/tests/css/printing/root-element-display-none-print.html b/testing/web-platform/tests/css/css-page/root-element-display-none-print.html index d6802f6225..d6802f6225 100644 --- a/testing/web-platform/tests/css/printing/root-element-display-none-print.html +++ b/testing/web-platform/tests/css/css-page/root-element-display-none-print.html diff --git a/testing/web-platform/tests/css/printing/subpixel-page-size-001-print-ref.html b/testing/web-platform/tests/css/css-page/subpixel-page-size-001-print-ref.html index cf59f2be37..cf59f2be37 100644 --- a/testing/web-platform/tests/css/printing/subpixel-page-size-001-print-ref.html +++ b/testing/web-platform/tests/css/css-page/subpixel-page-size-001-print-ref.html diff --git a/testing/web-platform/tests/css/printing/subpixel-page-size-001-print.html b/testing/web-platform/tests/css/css-page/subpixel-page-size-001-print.html index 9054cd4513..9054cd4513 100644 --- a/testing/web-platform/tests/css/printing/subpixel-page-size-001-print.html +++ b/testing/web-platform/tests/css/css-page/subpixel-page-size-001-print.html diff --git a/testing/web-platform/tests/css/printing/subpixel-page-size-002-print-ref.html b/testing/web-platform/tests/css/css-page/subpixel-page-size-002-print-ref.html index 4ed4bdb5ff..4ed4bdb5ff 100644 --- a/testing/web-platform/tests/css/printing/subpixel-page-size-002-print-ref.html +++ b/testing/web-platform/tests/css/css-page/subpixel-page-size-002-print-ref.html diff --git a/testing/web-platform/tests/css/printing/subpixel-page-size-002-print.html b/testing/web-platform/tests/css/css-page/subpixel-page-size-002-print.html index 463cb21f25..463cb21f25 100644 --- a/testing/web-platform/tests/css/printing/subpixel-page-size-002-print.html +++ b/testing/web-platform/tests/css/css-page/subpixel-page-size-002-print.html diff --git a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-018.https.html b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-018.https.html index 1554cc6445..a07bb2d619 100644 --- a/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-018.https.html +++ b/testing/web-platform/tests/css/css-paint-api/parse-input-arguments-018.https.html @@ -22,11 +22,15 @@ should never be called. In other words, there should be no red painted in the re <div id="canvas-geometry" class="container"></div> <script id="code" type="text/worklet"> -function generateRandString(length) { - var text = ""; - var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - for (var i = 0; i < length; i++) - text += possible.charAt(Math.floor(Math.random() * possible.length)); +function generateRandomIdentifier(length) { + const firstChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + const nthChars = firstChars + "0123456789"; + // Pick a letter for the first character so that the string is a valid + // identifier. + var text = firstChars.charAt(Math.floor(Math.random() * firstChars.length)); + for (var i = 0; i < length - 1; i++) { + text += nthChars.charAt(Math.floor(Math.random() * nthChars.length)); + } return text; } @@ -39,7 +43,7 @@ try { // the input properties here. We make the string length 100 to make sure // that it is veryyyyyyyyyyyy unlikely that two strings will be the same // when running this test. - var current_str = generateRandString(100); + var current_str = generateRandomIdentifier(100); return [current_str]; } // The paint function here should never be called because the inputArguments diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/registered-neutral-keyframe.html b/testing/web-platform/tests/css/css-properties-values-api/animation/registered-neutral-keyframe.html new file mode 100644 index 0000000000..ac3ad0c2c2 --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/registered-neutral-keyframe.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +@keyframes test { + to { --x: to; } +} +#target { + --x: underlying; + animation: test 1s; +} +</style> +<div id="target"></div> +<script> +CSS.registerProperty({ + name: '--x', + syntax: '*', + inherits: false, +}); + +test(() => { + target.style.animationDelay = '-0.25s'; + assert_equals(getComputedStyle(target).getPropertyValue('--x'), 'underlying', 'at 25%'); + + target.style.animationDelay = '-0.75s'; + assert_equals(getComputedStyle(target).getPropertyValue('--x'), 'to', 'at 25%'); +}, 'CSS Animations neutral keyframes on registered custom properties should produce the underlying value'); +</script> diff --git a/testing/web-platform/tests/css/css-properties-values-api/animation/registered-var-to-registered-animating.html b/testing/web-platform/tests/css/css-properties-values-api/animation/registered-var-to-registered-animating.html new file mode 100644 index 0000000000..477e30b47b --- /dev/null +++ b/testing/web-platform/tests/css/css-properties-values-api/animation/registered-var-to-registered-animating.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.css-houdini.org/css-properties-values-api-1"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +#target { + --static: 100; +} +</style> +<div id="target"></div> +<script> +setup(() => { + for (let name of ['--test', '--static', '--animated']) { + CSS.registerProperty({ + name, + syntax: '<number>', + initialValue: '0', + inherits: false, + }); + } +}); + +test(() => { + // --test is animating from a static value to an animated value resulting in it changing quadratically. + let animation = target.animate({'--test': ['var(--static)', 'var(--animated)']}, 100); + let referencedAnimation = target.animate({'--animated': ['200', '300']}, 100); + + referencedAnimation.currentTime = 25; + { + animation.currentTime = 25; + // lerp(100, lerp(200, 300, 25%), 25%) == 0.75*100 + 0.25*(0.75*200 + 0.25*300) == 131.25 + assert_equals(getComputedStyle(target).getPropertyValue('--test'), '131.25', 'target at 25%, to at 25%'); + + animation.currentTime = 75; + // lerp(100, lerp(200, 300, 25%), 75%) == 0.25*100 + 0.75*(0.75*200 + 0.25*300) == 193.75 + assert_equals(getComputedStyle(target).getPropertyValue('--test'), '193.75', 'target at 75%, to at 25%'); + } + + referencedAnimation.currentTime = 75; + { + animation.currentTime = 25; + // lerp(100, lerp(200, 300, 75%), 25%) == 0.75*100 + 0.25*(0.25*200 + 0.75*300) == 143.75 + assert_equals(getComputedStyle(target).getPropertyValue('--test'), '143.75', 'target at 25%, to at 25%'); + + animation.currentTime = 75; + // lerp(100, lerp(200, 300, 75%), 75%) == 0.25*100 + 0.75*(0.25*200 + 0.75*300) == 231.25 + assert_equals(getComputedStyle(target).getPropertyValue('--test'), '231.25', 'target at 75%, to at 25%'); + } +}, 'Animated registered custom properties can var() reference other animated registered custom properties across separate Animations.'); +</script> diff --git a/testing/web-platform/tests/css/css-scoping/font-face-006.html b/testing/web-platform/tests/css/css-scoping/font-face-006.html index 5eabe6ab76..8c6e341713 100644 --- a/testing/web-platform/tests/css/css-scoping/font-face-006.html +++ b/testing/web-platform/tests/css/css-scoping/font-face-006.html @@ -16,7 +16,7 @@ </style> <div id="host"></div> <script> -test(function() { +promise_test(async function() { host.attachShadow({ mode: "open" }).innerHTML = ` <style> :host::before, :host::after { @@ -27,6 +27,8 @@ test(function() { <slot></slot> `; + await document.fonts.ready; + //shrinkwrapped size for a default font will be a bit more than 80-90 //if the font is applied, it will be a bit more than 160 assert_greater_than(document.getElementById('host').offsetWidth, 160); diff --git a/testing/web-platform/tests/css/css-scoping/host-defined.html b/testing/web-platform/tests/css/css-scoping/host-defined.html new file mode 100644 index 0000000000..9e9776754a --- /dev/null +++ b/testing/web-platform/tests/css/css-scoping/host-defined.html @@ -0,0 +1,24 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Shadow host is considered, but does not match :defined</title> +<link rel="author" title="Steinar H. Gunderson" href="mailto:sesse@chromium.org"> +<link rel="help" href="https://crbug.com/332587538"> +<link rel="help" href="https://drafts.csswg.org/selectors/#featureless"> +<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html"> +<p>Test passes if there is a filled green square.</p> +<div id="host"></div> +<script> + host.attachShadow({mode: "open"}).innerHTML = ` + <style> + :host div { + width: 100px; + height: 100px; + background-color: red; + } + :not(:defined) div { + background-color: green; + } + </style> + <div></div> + `; +</script> diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/resources/common.js b/testing/web-platform/tests/css/css-scroll-snap-2/resources/common.js index a641553bea..a3591d48ed 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/resources/common.js +++ b/testing/web-platform/tests/css/css-scroll-snap-2/resources/common.js @@ -8,32 +8,26 @@ function checkSnapEventSupport(event_type) { } } -// This function is deprecated. It tests a deprecated SnapEvent interface. -function assertSnapEventDeprecated(evt, expected_ids) { +function assertSnapEvent(evt, expected_ids) { assert_equals(evt.bubbles, false, "snap events don't bubble"); assert_false(evt.cancelable, "snap events are not cancelable."); - const actual = Array.from(evt.snapTargets, el => el.id).join(","); - const expected = expected_ids.join(","); - assert_equals(actual, expected, "snap event supplied expected targets"); + assert_equals(evt.snapTargetBlock, expected_ids.block, + "snap event supplied expected target in block axis"); + assert_equals(evt.snapTargetInline, expected_ids.inline, + "snap event supplied expected target in inline axis"); } -// This function is deprecated. It tests a deprecated SnapEvent interface. -// This function holds logic intended to be used by tests for scroll snap -// events. -// |test_data| should contain: -// - |scroller|: the snap container being scrolled (or -// document.scrollingElement) -// - |scrolling_function|: this function should trigger the desired snap event -// when executed. -// - |expected_snap_targets|: a list of element ids which the triggered snap -// event should supply in SnapEvent.snapTargets. -// - |expected_scroll_offsets|: the scroll offsets at which the snap container -// should be after scrolling function has been -// executed. -// |event_type|: should be "snapchanged" or "snapchanging". -async function test_snap_event_deprecated(test, test_data, event_type) { +async function snap_test_setup(test, scroller, event_type) { checkSnapEventSupport(event_type); - await waitForScrollReset(test, test_data.scroller); + await waitForScrollReset(test, scroller); + await waitForCompositorCommit(); + test.add_cleanup(async () => { + await waitForScrollReset(test, scroller); + }); +} + +async function test_snap_event(test, test_data, event_type) { + await snap_test_setup(test, test_data.scroller, event_type); let listener = test_data.scroller == document.scrollingElement ? document : test_data.scroller; @@ -42,7 +36,7 @@ async function test_snap_event_deprecated(test, test_data, event_type) { await test_data.scrolling_function(); let evt = await event_promise; - assertSnapEventDeprecated(evt, test_data.expected_snap_targets); + assertSnapEvent(evt, test_data.expected_snap_targets); assert_approx_equals(test_data.scroller.scrollTop, test_data.expected_scroll_offsets.y, 1, "vertical scroll offset mismatch."); @@ -52,7 +46,7 @@ async function test_snap_event_deprecated(test, test_data, event_type) { } async function test_snapchanged(test, test_data) { - await test_snap_event_deprecated(test, test_data, "snapchanged"); + await test_snap_event(test, test_data, "snapchanged"); } function waitForEventUntil(event_target, event_type, wait_until) { @@ -83,6 +77,20 @@ function waitForEventsUntil(event_target, event_type, wait_until) { }); } +function waitForOnSnapchanging(event_target) { + return new Promise(resolve => { + let result = null; + const listener = (evt) => { + result = evt; + }; + event_target.onsnapchanging = listener; + waitForScrollendEventNoTimeout(event_target).then(() => { + event_target.onsnapchanging = null; + resolve(result); + }); + }); +} + // Proxy a wait for a snap event. We want to avoid having a test // timeout in the event of an expected snap event not firing in a particular // test case as that would cause the entire file to fail. diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/resources/programmatic-scroll-common.js b/testing/web-platform/tests/css/css-scroll-snap-2/resources/programmatic-scroll-common.js new file mode 100644 index 0000000000..8257b98fe3 --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap-2/resources/programmatic-scroll-common.js @@ -0,0 +1,26 @@ +// Helper functions for snapchanged-on-programmatic-* tests. + +// Utility function to test that onsnapchanging is triggered for +// snapchanging-on-programmatic-* tests which set up a similar layout in which +// the |scroller| has 3 snap targets that form a vertical column along +// |scroller|'s middle. onsnapchanging should be triggered by conducting a +// programmatic scroll to the top of snap_target. +async function test_programmatic_scroll_onsnapchanging(test, + scroller, + event_target, + snap_target) { + await snap_test_setup(test, scroller, "snapchanging"); + const expected_snap_targets = { block: snap_target, inline: null }; + + // Scroll and wait for a snapchanging event. + const snapchanging_promise = waitForOnSnapchanging(event_target); + scroller.scrollTo(0, snap_target.offsetTop); + const snapchanging_event = await snapchanging_promise; + + // Assert that snapchanging fired and indicated that snap_target would + // be snapped to. + assertSnapEvent(snapchanging_event, expected_snap_targets); + assert_equals(scroller.scrollLeft, 0, "scrollLeft is zero"); + assert_equals(scroller.scrollTop, snap_target.offsetTop, + "snapped to snap_target"); +} diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/resources/user-scroll-common.js b/testing/web-platform/tests/css/css-scroll-snap-2/resources/user-scroll-common.js index 6587aebd92..820f143816 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/resources/user-scroll-common.js +++ b/testing/web-platform/tests/css/css-scroll-snap-2/resources/user-scroll-common.js @@ -68,4 +68,33 @@ async function test_no_snapchanged(t, scroller, delta) { async function test_no_snapchanging(t, scroller, delta) { await test_no_snap_event(t, scroller, delta, "snapchanging"); -}
\ No newline at end of file +} + +// Utility function to test that onsnapchanging is triggered for +// snapchanging-on-user-* tests which set up a similar layout in which +// the |scroller| has 3 snap targets that form a vertical column along +// |scroller|'s middle. onsnapchanging should be triggered by touch-dragging +// |scroller|'s content so that |snap_target|'s top aligns to |snap_target|. +async function test_user_scroll_onsnapchanging(test, scroller, event_target, + snap_target) { + await snap_test_setup(test, scroller, "snapchanging"); + + // Compute touch positions to drag the top of snap_target to the top of + // the scroller. + const scroller_middle = Math.round(scroller.clientWidth / 2); + const start_pos = { x: scroller_middle, y: snap_target.offsetTop }; + const end_pos = { x: scroller_middle, y: 0 }; + const expected_snap_targets = { block: snap_target, inline: null }; + + // Scroll and wait for a snapchanging event. + const snapchanging_promise = waitForOnSnapchanging(event_target); + await snap_event_touch_scroll_helper(start_pos, end_pos); + const snapchanging_event = await snapchanging_promise; + + // Assert that snapchanging fired and indicated that snap_target would + // be snapped to. + assertSnapEvent(snapchanging_event, expected_snap_targets); + assert_equals(scroller.scrollLeft, 0, "scrollLeft is zero"); + assert_equals(scroller.scrollTop, snap_target.offsetTop, + "snapped to snap_target"); +} diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-after-layout-change.tentative.html b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-after-layout-change.tentative.html index 4272412073..a3ba05fdf5 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-after-layout-change.tentative.html +++ b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-after-layout-change.tentative.html @@ -81,7 +81,7 @@ inner_snap_area.style.height = `${scroller.clientHeight + inner_snap_area.clientHeight - 10}px`; const evt = await snapchanged_promise; - assertSnapEventDeprecated(evt, [outer_snap_area.id, inner_snap_area.id]); + assertSnapEvent(evt, { block: inner_snap_area, inline: null }); target_snap_position = inner_snap_area.offsetTop + inner_snap_area.offsetHeight - scroller.clientHeight; assert_equals(scroller.scrollTop, target_snap_position, @@ -107,7 +107,7 @@ inner_snap_area.style.height = `${scroller.clientHeight + inner_snap_area.clientHeight + 10}px`; const evt = await snapchanged_promise; - assertSnapEventDeprecated(evt, [outer_snap_area.id, inner_snap_area.id]); + assertSnapEvent(evt, { block: inner_snap_area, inline: null }); assert_equals(scroller.scrollTop, target_snap_position, "scroller maintains offset which is now covering within inner area"); }, "snapchanged fires after snap area is snapped to upon layout change " + @@ -119,11 +119,11 @@ let snapchanged_promise = waitForSnapChangedEvent(scroller, false); scroller.style.scrollSnapType = "none"; let evt = await snapchanged_promise; - assertSnapEventDeprecated(evt, []); + assertSnapEvent(evt, { block: null, inline: null }); snapchanged_promise = waitForSnapChangedEvent(scroller, false); scroller.style.scrollSnapType = "y mandatory"; evt = await snapchanged_promise; - assertSnapEventDeprecated(evt, [outer_snap_area.id]); + assertSnapEvent(evt, { block: outer_snap_area, inline: null }); }, "snapchanged fires when container stops snapping"); promise_test(async(t) => { @@ -133,12 +133,12 @@ inner_snap_area.style.scrollSnapAlign = "none"; outer_snap_area.style.scrollSnapAlign = "none"; let evt = await snapchanged_promise; - assertSnapEventDeprecated(evt, []); + assertSnapEvent(evt, { block: null, inline: null }); snapchanged_promise = waitForSnapChangedEvent(scroller, false); inner_snap_area.style.scrollSnapAlign = "start"; outer_snap_area.style.scrollSnapAlign = "start"; evt = await snapchanged_promise; - assertSnapEventDeprecated(evt, [outer_snap_area.id]); + assertSnapEvent(evt, { block: outer_snap_area, inline: null }); }, "snapchanged fires when snap container no longer has snap areas"); </script> </body> diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-ensures-dom-order.html b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-ensures-dom-order.html deleted file mode 100644 index f87b6137f1..0000000000 --- a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-ensures-dom-order.html +++ /dev/null @@ -1,95 +0,0 @@ -<!DOCTYPE html> -<html> - -<head> - <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-2/#snap-events" /> - <script src="/resources/testharness.js"></script> - <script src="/resources/testharnessreport.js"></script> - <script src="/dom/events/scrolling/scroll_support.js"></script> - <script src="/css/css-scroll-snap-2/resources/common.js"></script> - <script src="/web-animations/testcommon.js"></script> - <style> - #scroller { - overflow-y: scroll; - scroll-snap-type: y mandatory; - width: 500px; - height: 500px; - background-color: white; - position: relative; - } - .space_filler { - display: inline-block; - width: 40%; - height: 30%; - background-color: green; - } - .snap_area { - scroll-snap-align: start; - background-color: yellow; - position: absolute; - width: 40%; - height: 30%; - } - - #snap_point_1 { - left: 1px; - } - #snap_point_2 { - left: 80%; - } - #snap_point_3 { - left: 40%; - scroll-snap-align: start; - background-color: yellow; - position: absolute; - width: 40%; - height: 30%; - } - </style> -</head> -<body> - <div id="scroller"> - <div class="space_filler"></div> - <div class="space_filler"></div> - <div class="space_filler"></div> - <div class="space_filler"></div> - <div class="space_filler"></div> - <div class="space_filler"></div> - <div class="space_filler"></div> - <div class="space_filler"></div> - <div id="snap_point_1" class="snap_area"><h1>1</h1></div> - <div id="snap_point_2" class="snap_area"><h1>2</h1></div> - </div> - <script> - promise_test(async (t) => { - checkSnapEventSupport("snapchanged"); - await waitForCompositorCommit(); - const snapchanged_promise = waitForSnapChangedEvent(scroller, false); - const snap_point_3 = document.createElement("div"); - snap_point_3.id = "snap_point_3"; - t.add_cleanup(() => { - snap_point_3.remove(); - }); - scroller.insertBefore(snap_point_3, snap_point_2); - const evt_seen = await snapchanged_promise; - assertSnapEventDeprecated(evt_seen, - [snap_point_1.id, snap_point_3.id, snap_point_2.id]); - }, "snapchanged lists snapTargets in DOM order."); - - promise_test(async (t) => { - checkSnapEventSupport("snapchanged"); - await waitForCompositorCommit(); - const unreached_func = t.unreached_func("snapchanged shouldn't fire " + - "since the scroller is snapped to the same elements despite the " + - "dom order change."); - t.add_cleanup(() => { - scroller.removeEventListener("snapchanged", unreached_func); - }) - scroller.addEventListener("snapchanged", unreached_func); - scroller.insertBefore(snap_point_2, snap_point_1); - await waitForCompositorCommit(); - }, "DOM order change doesn't trigger snapchanged if snapped targets " + - "don't change."); - </script> -</body> -</html> diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-root-scroll.tentative.html b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-root-scroll.tentative.html index 7c7b48152e..98ec2e5d75 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-root-scroll.tentative.html +++ b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-root-scroll.tentative.html @@ -69,7 +69,7 @@ scrolling_function: () => { scroller.scrollTo(snap_point_2.offsetLeft, snap_point_2.offsetTop); }, - expected_snap_targets: [snap_point_2.id], + expected_snap_targets: { block: snap_point_2, inline: snap_point_2 }, expected_scroll_offsets: { x: snap_point_2.offsetLeft, y: snap_point_2.offsetTop, @@ -110,7 +110,7 @@ scroller.scrollTo(scroll_left_target, scroll_top_target); evt = await snapchanged_promise; - assertSnapEventDeprecated(evt, [snap_point_2.id]); + assertSnapEvent(evt, { block: snap_point_2, inline: snap_point_2 }); assert_approx_equals(scroller.scrollTop, snap_point_2.offsetTop, 1, "scroller snaps to the top of snap_point_2"); assert_approx_equals(scroller.scrollLeft, snap_point_2.offsetLeft, 1, diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-scroll.tentative.html b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-scroll.tentative.html index 1fb56212a3..9dff856f34 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-scroll.tentative.html +++ b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-programmatic-scroll.tentative.html @@ -76,7 +76,7 @@ scrolling_function: () => { scroller.scrollTo(snap_point_2.offsetLeft, snap_point_2.offsetTop); }, - expected_snap_targets: [snap_point_2.id], + expected_snap_targets: { block: snap_point_2, inline: snap_point_2 }, expected_scroll_offsets: { x: snap_point_2.offsetLeft, y: snap_point_2.offsetTop, @@ -115,7 +115,7 @@ scroller.scrollTo(scroll_left_target, scroll_top_target); evt = await snapchanged_promise; - assertSnapEventDeprecated(evt, [snap_point_2.id]); + assertSnapEvent(evt, { block: snap_point_2, inline: snap_point_2 }) assert_approx_equals(scroller.scrollTop, snap_point_2.offsetTop, 1, "scroller snaps to the top of snap_point_2"); assert_approx_equals(scroller.scrollLeft, snap_point_2.offsetLeft, 1, diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-root-scroll.tentative.html b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-root-scroll.tentative.html index 5405d778bf..127376caa2 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-root-scroll.tentative.html +++ b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-root-scroll.tentative.html @@ -64,6 +64,8 @@ <div id="snap_point_3" class="snap_point"></div> <script> const scroller = document.scrollingElement; + const snap_point_2 = document.getElementById("snap_point_2"); + const snap_point_1 = document.getElementById("snap_point_1"); const offset_to_snap_point_2 = { x: snap_point_2.offsetLeft, y: snap_point_2.offsetTop @@ -82,7 +84,7 @@ scrolling_function: async () => { await snap_event_touch_scroll_helper(start_pos, end_pos); }, - expected_snap_targets: [snap_point_2.id], + expected_snap_targets: { block: snap_point_2, inline: snap_point_2 }, expected_scroll_offsets: { x: offset_to_snap_point_2.x, y: offset_to_snap_point_2.y, @@ -102,7 +104,7 @@ offset_to_snap_point_2.y, { origin: scroller }).send(); }, - expected_snap_targets: [snap_point_2.id], + expected_snap_targets: { block: snap_point_2, inline: snap_point_2 }, expected_scroll_offsets: { x: offset_to_snap_point_2.x, y: offset_to_snap_point_2.y, @@ -132,7 +134,7 @@ await snap_event_scrollbar_drag_helper(scroller, scrollbar_width, drag_amt); }, - expected_snap_targets: [snap_point_1.id, snap_point_2.id], + expected_snap_targets: { block: snap_point_2, inline: snap_point_1 }, expected_scroll_offsets: { x: 0, y: offset_to_snap_point_2.y, @@ -150,7 +152,7 @@ window.test_driver.send_keys(document.documentElement, '\ue015'/*ArrowDown*/); }, - expected_snap_targets: [snap_point_1.id, snap_point_2.id], + expected_snap_targets: { block: snap_point_2, inline: snap_point_1 }, expected_scroll_offsets: { x: 0, y: offset_to_snap_point_2.y, diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-scroll.tentative.html b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-scroll.tentative.html index 4f36200722..91194642b5 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-scroll.tentative.html +++ b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-on-user-scroll.tentative.html @@ -71,6 +71,8 @@ </div> <script> const scroller = document.getElementById("scroller"); + const snap_point_1 = document.getElementById("snap_point_1"); + const snap_point_2 = document.getElementById("snap_point_2"); const offset_to_snap_point_2 = { x: snap_point_2.offsetLeft, y: snap_point_2.offsetTop @@ -89,7 +91,7 @@ scrolling_function: async () => { await snap_event_touch_scroll_helper(start_pos, end_pos); }, - expected_snap_targets: [snap_point_2.id], + expected_snap_targets: { block: snap_point_2, inline: snap_point_2 }, expected_scroll_offsets: { x: offset_to_snap_point_2.x, y: offset_to_snap_point_2.y, @@ -109,7 +111,7 @@ offset_to_snap_point_2.y, { origin: scroller }).send(); }, - expected_snap_targets: [snap_point_2.id], + expected_snap_targets: { block: snap_point_2, inline: snap_point_2 }, expected_scroll_offsets: { x: offset_to_snap_point_2.x, y: offset_to_snap_point_2.y, @@ -136,7 +138,7 @@ scrollbar_to_scroller_ratio; await snap_event_scrollbar_drag_helper(scroller, scrollbar_width, drag_amt); }, - expected_snap_targets: [snap_point_1.id, snap_point_2.id], + expected_snap_targets: { block: snap_point_2, inline: snap_point_1 }, expected_scroll_offsets: { x: 0, y: offset_to_snap_point_2.y, @@ -154,7 +156,7 @@ scroller.focus(); window.test_driver.send_keys(scroller, '\ue015'/*ArrowDown*/); }, - expected_snap_targets: [snap_point_1.id, snap_point_2.id], + expected_snap_targets: { block: snap_point_2, inline: snap_point_1 }, expected_scroll_offsets: { x: 0, y: offset_to_snap_point_2.y, diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-with-proximity-strictness.tentative.html b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-with-proximity-strictness.tentative.html index 6a874e82f2..96cab33739 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-with-proximity-strictness.tentative.html +++ b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanged/snapchanged-with-proximity-strictness.tentative.html @@ -61,7 +61,7 @@ // to outside the proximity range and are no longer snapped. let evt = await snapchanged_promise; assert_equals(scroller.scrollTop, 250); - assertSnapEventDeprecated(evt, []); + assertSnapEvent(evt, { block: null, inline: null }); evt = null; snapchanged_promise = waitForSnapChangedEvent(scroller); @@ -73,7 +73,7 @@ assert_equals(scroller.scrollTop, 0); // snapchanged should fire as we've moved from outside the proximity range // to inside the proximity range and are once again snapped. - assertSnapEventDeprecated(evt, [target.id]); + assertSnapEvent(evt, { block: target, inline: null }); }, "Snapchanged fires when scrolling outside proximity range."); </script> </body> diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-after-layout-change.tentative.html b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-after-layout-change.tentative.html index ff307981e8..0c0bfb623e 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-after-layout-change.tentative.html +++ b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-after-layout-change.tentative.html @@ -102,7 +102,7 @@ // assert snapchanging that should have already happened. await scroll_promise; - assertSnapEventDeprecated(snap_evt, [box2.id]); + assertSnapEvent(snap_evt, { block: null, inline: box2 }); evt_promise = waitForSnapEvent(scroller, "snapchanging", false); // Change layout while pointer is still down. @@ -111,7 +111,7 @@ box2.style.left = box3_prev_left; box3.style.left = box2_prev_left; snap_evt = await evt_promise; - assertSnapEventDeprecated(snap_evt, [box3.id]); + assertSnapEvent(snap_evt, { block: null, inline: box3 }); }, "snapchanging fires after layout change"); </script> </body> diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-root-scroll.tentative.html b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-root-scroll.tentative.html index 7044cfac96..b714a6cfb5 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-root-scroll.tentative.html +++ b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-root-scroll.tentative.html @@ -9,6 +9,7 @@ <script src="/resources/testdriver-vendor.js"></script> <script src="/dom/events/scrolling/scroll_support.js"></script> <script src="/css/css-scroll-snap-2/resources/common.js"></script> + <script src="/css/css-scroll-snap-2/resources/programmatic-scroll-common.js"></script> ß <script src="/web-animations/testcommon.js"></script> </head> @@ -66,6 +67,7 @@ <div id="snap_area_3" class="yellow snap box"></div> <script> const scroller = document.scrollingElement; + const snap_area_2 = document.getElementById("snap_area_2"); promise_test(async (t) => { await waitForCompositorCommit(); @@ -74,17 +76,23 @@ scrolling_function: async () => { scroller.scrollTo(0, snap_area_2.offsetTop); }, - expected_snap_targets: [snap_area_2.id], + expected_snap_targets: { block: snap_area_2, inline: null }, expected_scroll_offsets: { x: 0, y: snap_area_2.offsetTop } }; - await test_snap_event_deprecated(t, test_data, "snapchanging"); + await test_snap_event(t, test_data, "snapchanging"); }, "snapchanging fires on programmatic scrolls that changes a scroller's" + " snap targets."); promise_test(async (t) => { + await test_programmatic_scroll_onsnapchanging(t, scroller, document, + snap_area_2); + }, "programmatic scroll triggers Document.snapchanging when scrolling a " + + "snap container"); + + promise_test(async (t) => { checkSnapEventSupport("snapchanging"); await waitForScrollReset(t, scroller); await waitForCompositorCommit(); diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-scroll.tentative.html b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-scroll.tentative.html index 86946b84e9..6e7b0126f7 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-scroll.tentative.html +++ b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-programmatic-scroll.tentative.html @@ -9,6 +9,7 @@ <script src="/resources/testdriver-vendor.js"></script> <script src="/dom/events/scrolling/scroll_support.js"></script> <script src="/css/css-scroll-snap-2/resources/common.js"></script> + <script src="/css/css-scroll-snap-2/resources/programmatic-scroll-common.js"></script> <script src="/web-animations/testcommon.js"></script> </head> @@ -73,6 +74,7 @@ </div> <script> const scroller = document.getElementById("scroller"); + const snap_area_2 = document.getElementById("snap_area_2"); promise_test(async (t) => { await waitForCompositorCommit(); @@ -81,17 +83,23 @@ scrolling_function: async () => { scroller.scrollTo(0, snap_area_2.offsetTop); }, - expected_snap_targets: [snap_area_2.id], + expected_snap_targets: { block: snap_area_2, inline: null }, expected_scroll_offsets: { x: 0, y: snap_area_2.offsetTop } }; - await test_snap_event_deprecated(t, test_data, "snapchanging"); + await test_snap_event(t, test_data, "snapchanging"); }, "snapchanging fires on programmatic scrolls that changes a scroller's" + " snap targets."); promise_test(async (t) => { + await test_programmatic_scroll_onsnapchanging(t, scroller, scroller, + snap_area_2); + }, "programmatic scroll triggers Element.onsnapchanging when scrolling a " + + "snap container"); + + promise_test(async (t) => { checkSnapEventSupport("snapchanging"); await waitForScrollReset(t, scroller); await waitForCompositorCommit(); @@ -106,7 +114,7 @@ assert_equals(evt, null, "no snap event since scroller is back to top"); assert_equals(scroller.scrollTop, 0, "scroller snaps back to the top"); assert_equals(scroller.scrollLeft, 0, "scroller snaps back to the left"); - }); + }, "snapchanging should not fire since the snap target doesn't change."); </script> </body> diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-root-scroll.tentative.html b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-root-scroll.tentative.html index a9b003e6c5..815c3c0922 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-root-scroll.tentative.html +++ b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-root-scroll.tentative.html @@ -74,6 +74,8 @@ <div id="snap_area_3" class="yellow snap box"></div> <script> const scroller = document.scrollingElement; + const snap_area_2 = document.getElementById("snap_area_2"); + const snap_area_1 = document.getElementById("snap_area_1"); // Touch scroll test. promise_test(async (t) => { @@ -86,13 +88,13 @@ const end_pos = { x: scroller_middle, y: 0 }; await snap_event_touch_scroll_helper(start_pos, end_pos); }, - expected_snap_targets: [snap_area_2.id], + expected_snap_targets: { block: snap_area_2, inline: null }, expected_scroll_offsets: { x: 0, y: snap_area_2.offsetTop } }; - await test_snap_event_deprecated(t, test_data, "snapchanging"); + await test_snap_event(t, test_data, "snapchanging"); }, "touch scrolling fires snapchanging."); // Wheel scroll test. @@ -104,13 +106,13 @@ await new test_driver.Actions().scroll(0, 0, 0, Math.round(snap_area_2.offsetTop / 2) + 1).send(); }, - expected_snap_targets: [snap_area_2.id], + expected_snap_targets: { block: snap_area_2, inline: null }, expected_scroll_offsets: { x: 0, y: snap_area_2.offsetTop } }; - await test_snap_event_deprecated(t, test_data, "snapchanging"); + await test_snap_event(t, test_data, "snapchanging"); }, "mouse wheel scroll triggers snapchanging."); // Scrollbar drag test. @@ -130,13 +132,13 @@ scrollbar_to_scroller_ratio; await snap_event_scrollbar_drag_helper(scroller, scrollbar_width, drag_amt); }, - expected_snap_targets: [snap_area_2.id], + expected_snap_targets: { block: snap_area_2, inline: null }, expected_scroll_offsets: { x: 0, y: snap_area_2.offsetTop } }; - await test_snap_event_deprecated(t, test_data, "snapchanging"); + await test_snap_event(t, test_data, "snapchanging"); }, "scrollbar dragging fires snapchanging."); // Keyboard test. @@ -148,15 +150,20 @@ scroller.focus(); window.test_driver.send_keys(document.documentElement, '\ue015'/*ArrowDown*/); }, - expected_snap_targets: [snap_area_2.id], + expected_snap_targets: { block: snap_area_2, inline: null }, expected_scroll_offsets: { x: 0, y: snap_area_2.offsetTop } }; - await test_snap_event_deprecated(t, test_data, "snapchanging"); + await test_snap_event(t, test_data, "snapchanging"); }, "keyboard scroll triggers snapchanging."); + promise_test(async (t) => { + await test_user_scroll_onsnapchanging(t, scroller, document, + snap_area_2); + }, "Document.onsnapchanging fires when scrolling a snap container."); + // Touch scroll test: peek at snap_area_2 and then drag back to // snap_area_1. promise_test(async (t) => { @@ -183,8 +190,8 @@ .send(); let evts = await evts_promise; assert_equals(evts.length, 2, "2 snapchanging events are seens"); - assertSnapEventDeprecated(evts[0], [snap_area_2.id]); - assertSnapEventDeprecated(evts[1], [snap_area_1.id]); + assertSnapEvent(evts[0], { block: snap_area_2, inline: null }); + assertSnapEvent(evts[1], { block: snap_area_1, inline: null }); }, "snapchanging fires as scroll moves through different snap targets."); // snapchanging doesn't fire test. diff --git a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-scroll.tentative.html b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-scroll.tentative.html index 28ca96ec93..27f52efc71 100644 --- a/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-scroll.tentative.html +++ b/testing/web-platform/tests/css/css-scroll-snap-2/snapchanging/snapchanging-on-user-scroll.tentative.html @@ -9,6 +9,7 @@ <script src="/dom/events/scrolling/scroll_support.js"></script> <script src="/css/css-scroll-snap-2/resources/common.js"></script> <script src="/css/css-scroll-snap-2/resources/user-scroll-common.js"></script> + <script src="/web-animations/testcommon.js"></script> </head> <body> @@ -63,6 +64,8 @@ </div> <script> const scroller = document.getElementById("scroller"); + const snap_area_2 = document.getElementById("snap_area_2"); + const snap_area_1 = document.getElementById("snap_area_1"); // Touch scroll test. promise_test(async (t) => { @@ -75,13 +78,13 @@ const end_pos = { x: scroller_middle, y: 0 }; await snap_event_touch_scroll_helper(start_pos, end_pos); }, - expected_snap_targets: [snap_area_2.id], + expected_snap_targets: { block: snap_area_2, inline: null }, expected_scroll_offsets: { x: 0, y: snap_area_2.offsetTop } }; - await test_snap_event_deprecated(t, test_data, "snapchanging"); + await test_snap_event(t, test_data, "snapchanging"); }, "touch scrolling fires snapchanging."); // Wheel scroll test. @@ -94,13 +97,13 @@ Math.round(snap_area_2.offsetTop / 2) + 1, { origin: scroller }).send(); }, - expected_snap_targets: [snap_area_2.id], + expected_snap_targets: { block: snap_area_2, inline: null }, expected_scroll_offsets: { x: 0, y: snap_area_2.offsetTop } }; - await test_snap_event_deprecated(t, test_data, "snapchanging"); + await test_snap_event(t, test_data, "snapchanging"); }, "mouse wheel scroll triggers snapchanging."); // Scrollbar drag test. @@ -121,13 +124,13 @@ scrollbar_to_scroller_ratio; await snap_event_scrollbar_drag_helper(scroller, scrollbar_width, drag_amt); }, - expected_snap_targets: [snap_area_2.id], + expected_snap_targets: { block: snap_area_2, inline: null }, expected_scroll_offsets: { x: 0, y: snap_area_2.offsetTop } }; - await test_snap_event_deprecated(t, test_data, "snapchanging"); + await test_snap_event(t, test_data, "snapchanging"); }, "scrollbar dragging fires snapchanging."); // Keyboard test. @@ -139,13 +142,13 @@ scroller.focus(); window.test_driver.send_keys(scroller, '\ue015'/*ArrowDown*/); }, - expected_snap_targets: [snap_area_2.id], + expected_snap_targets: { block: snap_area_2, inline: null }, expected_scroll_offsets: { x: 0, y: snap_area_2.offsetTop } }; - await test_snap_event_deprecated(t, test_data, "snapchanging"); + await test_snap_event(t, test_data, "snapchanging"); }, "keyboard scroll triggers snapchanging."); // Touch scroll test: peek at snap_area_2 and then drag back to @@ -174,10 +177,15 @@ .send(); let evts = await evts_promise; assert_equals(evts.length, 2, "2 snapchanging events are seens"); - assertSnapEventDeprecated(evts[0], [snap_area_2.id]); - assertSnapEventDeprecated(evts[1], [snap_area_1.id]); + assertSnapEvent(evts[0], { block: snap_area_2, inline: null }); + assertSnapEvent(evts[1], { block: snap_area_1, inline: null }); }, "snapchanging fires as scroll moves through different snap targets."); + promise_test(async (t) => { + await test_user_scroll_onsnapchanging(t, scroller, scroller, + snap_area_2); + }, "Element.onsnapchanging fires when scrolling a snap container."); + // snapchanging doesn't fire test. promise_test(async (t) => { test_no_snapchanging(t, scroller, 10); diff --git a/testing/web-platform/tests/css/css-scroll-snap/scroll-margin-editable.html b/testing/web-platform/tests/css/css-scroll-snap/scroll-margin-editable.html new file mode 100644 index 0000000000..713502820d --- /dev/null +++ b/testing/web-platform/tests/css/css-scroll-snap/scroll-margin-editable.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<html> + <link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-margin" /> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/dom/events/scrolling/scroll_support.js"></script> +<body> + <style> + html { + padding: 10rem; + border-bottom: solid silver 150vh; + } + .target { + scroll-margin-top: 8rem; + font-size: 2rem; + border: solid grey 1px; + } + </style> + <div contenteditable class="target" id="target"></div> + <script> + promise_test(async (t) => { + document.addEventListener("scroll", t.unreached_func( + "document scrolled for already-in-view target.")); + document.getElementById("target").focus(); + await test_driver.send_keys(target, "a"); + await waitForCompositorCommit(); + }, "already-in-view div should not cause a scroll upon focus."); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-common-to-both-axes.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-common-to-both-axes.html index 09b81e7c0d..a44c8d7c48 100644 --- a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-common-to-both-axes.html +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-common-to-both-axes.html @@ -33,31 +33,48 @@ width: 100px; height: 100px; background-color: green; - display: inline-block; - position: relative; - } - .grid { position: absolute; - width: 350px; - height: 350px; } - .snap:focus { - background-color: blue; + .leftcol { + left: 110px; + } + .midcol { + left: 220px; + } + .rightcol { + left: 330px; + } + .toprow { + top: 110px; + } + .midrow { + top: 220px; + } + .bottomrow { + top: 330px; + } + .placeholder { + position: absolute; + top: 0px; + left: 0px; + width: 10px; + height: 10px; + background-color: black; + scroll-snap-align: start; } </style> <div class="scroller" id="scroller"> <div class="large-space"><div> - <div class="grid" id="grid"> - <div id="box1" tabindex="1" class="snap box">Box 1</div> - <div id="box2" tabindex="1" class="snap box">Box 2</div> - <div id="box3" tabindex="1" class="snap box">Box 3</div> - <div id="box4" tabindex="1" class="snap box">Box 4</div> - <div id="box5" tabindex="1" class="snap box">Box 5</div> - <div id="box6" tabindex="1" class="snap box">Box 6</div> - <div id="box7" tabindex="1" class="snap box">Box 7</div> - <div id="box8" tabindex="1" class="snap box">Box 8</div> - <div id="box9" tabindex="1" class="snap box">Box 9</div> - </div> + <div class="placeholder"></div> + <div id="box1" class="leftcol toprow snap box">Box 1</div> + <div id="box2" class="midcol toprow snap box">Box 2</div> + <div id="box3" class="rightcol toprow snap box">Box 3</div> + <div id="box4" class="leftcol midrow snap box">Box 4</div> + <div id="box5" class="midcol midrow snap box">Box 5</div> + <div id="box6" class="rightcol midrow snap box">Box 6</div> + <div id="box7" class="leftcol bottomrow snap box">Box 7</div> + <div id="box8" class="midcol bottomrow snap box">Box 8</div> + <div id="box9" class="rightcol bottomrow snap box">Box 9</div> </div> <script> window.onload = () => { @@ -79,50 +96,60 @@ await waitForCompositorCommit(); await runScrollSnapSelectionVerificationTest(t, scroller, - /*aligned_elements_x=*/[], + /*aligned_elements_x=*/[box(1), box(4), box(7)], /*aligned_elements_y=*/[box(1), box(2), box(3)], - /*axis=*/ "y", - /*expected_target_x=*/null, + /*axis=*/ "both", + /*expected_target_x=*/box(1), /*expected_target_y=*/box(1)); await runScrollSnapSelectionVerificationTest(t, scroller, - /*aligned_elements_x=*/[], + /*aligned_elements_x=*/[box(1), box(4), box(7)], /*aligned_elements_y=*/[box(4), box(5), box(6)], - /*axis=*/ "y", - /*expected_target_x=*/null, + /*axis=*/"both", + /*expected_target_x=*/box(4), /*expected_target_y=*/box(4)); await runScrollSnapSelectionVerificationTest(t, scroller, - /*aligned_elements_x=*/[], + /*aligned_elements_x=*/[box(1), box(4), box(7)], /*aligned_elements_y=*/[box(7), box(8), box(9)], - /*axis=*/"y", - /*expected_target_x=*/null, + /*axis=*/"both", + /*expected_target_x=*/box(7), /*expected_target_y=*/box(7)); await runScrollSnapSelectionVerificationTest(t, scroller, - /*aligned_elements_x=*/[box(1), box(4), box(7)], - /*aligned_elements_y=*/[], - /*axis=*/"x", - /*expected_target_x=*/box(1)); + /*aligned_elements_x=*/[box(2), box(5), box(8)], + /*aligned_elements_y=*/[box(1), box(2), box(3)], + /*axis=*/ "both", + /*expected_target_x=*/box(2), + /*expected_target_y=*/box(2)); await runScrollSnapSelectionVerificationTest(t, scroller, /*aligned_elements_x=*/[box(2), box(5), box(8)], - /*aligned_elements_y=*/[], - /*axis=*/"x", - /*expected_target_x=*/box(2)); + /*aligned_elements_y=*/[box(4), box(5), box(6)], + /*axis=*/"both", + /*expected_target_x=*/box(5), + /*expected_target_y=*/box(5)); + + await runScrollSnapSelectionVerificationTest(t, scroller, + /*aligned_elements_x=*/[box(2), box(5), box(8)], + /*aligned_elements_y=*/[box(7), box(8), box(9)], + /*axis=*/"both", + /*expected_target_x=*/box(8), + /*expected_target_y=*/box(8)); await runScrollSnapSelectionVerificationTest(t, scroller, /*aligned_elements_x=*/[box(3), box(6), box(9)], - /*aligned_elements_y=*/[], - /*axis=*/"x", - /*expected_target_x=*/box(3)); + /*aligned_elements_y=*/[box(1), box(2), box(3)], + /*axis=*/ "both", + /*expected_target_x=*/box(3), + /*expected_target_y=*/box(3)); await runScrollSnapSelectionVerificationTest(t, scroller, - /*aligned_elements_x=*/[box(2), box(5), box(8)], + /*aligned_elements_x=*/[box(3), box(6), box(9)], /*aligned_elements_y=*/[box(4), box(5), box(6)], /*axis=*/"both", - /*expected_target_x=*/box(5), - /*expected_target_y=*/box(5)); + /*expected_target_x=*/box(6), + /*expected_target_y=*/box(6)); }, "scroller prefers target aligned in both axes."); promise_test(async (t) => { @@ -132,13 +159,16 @@ box8.style.top = `${initial_box8_top}px`; }); + // Snap to box7's row and column. + scroller.scrollTo(box7.offsetLeft, box7.offsetTop); + // Move box 8 below box7 and box9. - box8.style.top = `${2 * box8.offsetTop}px`; + box8.style.top = `${box8.offsetTop + 50}px`; // Snap to box8. scroller.scrollTop = box8.offsetTop; - // Test that if box7 and box9 are also shifted to align with box7, + // Test that if box7 and box9 are also shifted to align with box8, // box8 is still treated as the selected snap target despite box7 being // aligned on both axes. runLayoutSnapSeletionVerificationTest(t, scroller, [box7, box9], diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-targeted-element-main-frame-target.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-targeted-element-main-frame-target.html index 6bc47d15ef..ae445d2861 100644 --- a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-targeted-element-main-frame-target.html +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-targeted-element-main-frame-target.html @@ -53,83 +53,122 @@ // ------------------------- // | Box 7 | Box 8 | Box 9 | // ------------------------- - // This function just gets the numbers beside |box_number| on each row. - // E.g. 4: 4%3 = 1; so the numbers we want are 5 (4+1) and 6 (4+2). - function getAlignedNumbers(n) { + // This function just gets the boxes beside boxn on each row. + // E.g. box4: 4%3 = 1; so the boxes we want are box5 (4+1) and box6 (4+2). + function getAlignedBoxes(n) { n = parseInt(n); const mod_3 = n % 3; + let n1 = n - 1, n2 = n - 2; if (mod_3 == 1) { - return [n + 1, n + 2]; + n1 = n + 1; + n2 = n + 2; } else if (mod_3 == 2) { - return [n - 1, n + 1]; + n1 = n - 1; + n2 = n + 1; } - return [n - 1, n - 2]; + return [document.getElementById(`box${n1}`), + document.getElementById(`box${n2}`)]; } + function stashResult(key, result) { fetch(`/css/css-scroll-snap/snap-after-relayout` + `/multiple-aligned-targets/stash.py?key=${key}`, { method: "POST", - body: result + body: JSON.stringify(result) }).then(() => { window.close(); }); } - function assert_equals(v1, v2) { + + function assert_equals(test_number, v1, v2, description) { if (v1 != v2) { - throw new Error(`Expected equality of v1(${v1}) and v2(${v2}).`); + throw new Error( + `Test ${n} expected equality of ${v1} and ${v2}, ` + + `Description: ${description}`); } } - async function record() { - let key = (new URL(document.location)).searchParams.get("key"); - try { - // Get the id of that targeted element. - const target_id = location.hash.substring(1); - const box_number = target_id.substring(3); - // Get the elements aligned with the targeted element. - const target = document.getElementById(target_id); - if (target == null) { - throw new Error("Null hash fragment target."); + async function waitForScrollReset(scroller, x = 0, y = 0) { + return new Promise((resolve) => { + if (scroller.scrollLeft == x && scroller.scrollTop == y) { + resolve(); + } else { + scroller.addEventListener("scrollend", resolve); + scroller.scrollTo(x, y); } - let [aligned_number_1, aligned_number_2] = - getAlignedNumbers(box_number); - const aligned_box_1 = document.getElementById(`box${aligned_number_1}`); - const aligned_box_2 = document.getElementById(`box${aligned_number_2}`); + }); + } - // Make sure all the boxes are equally aligned. - assert_equals(aligned_box_1.offsetTop, target.offsetTop); - assert_equals(aligned_box_1.offsetTop, aligned_box_2.offsetTop); - - // Scroll to the aligned boxes if necessary. - if (scroller.scrollTop != target.offsetTop) { - const scrollend_promise = new Promise((res) => { - scroller.addEventListener(res); - }); - scroller.scrollTop = target.offsetTop; - await scrollend_promise; + async function setLocationHash(id) { + return new Promise((resolve) => { + if (location.hash === `#${id}`) { + resolve(); + } else { + window.addEventListener("hashchange", resolve); + location.hash = `#${id}`; } + }); + } + + let result = { + passed: 0, + errors: "", + }; + + async function test(n) { + try { + const target_id = `box${n}`; + const target = document.getElementById(target_id); + + // Make boxn the targeted element. + await setLocationHash(target_id); + + // Reset the scroll position. + await waitForScrollReset(scroller); + + const aligned_boxes = getAlignedBoxes(n); + // Make sure all the boxes are equally aligned. + assert_equals(n, aligned_boxes[0].offsetTop, target.offsetTop, + `${aligned_boxes[0].id} is at offset ${target.offsetTop}`); + assert_equals(n, aligned_boxes[1].offsetTop, target.offsetTop, + `${aligned_boxes[1].id} is at offset ${target.offsetTop}`); - // Save target's original top and move it down by 100px; + // Scroll to the aligned boxes. + await waitForScrollReset(scroller, 0, target.offsetTop); + assert_equals(n, scroller.scrollTop, target.offsetTop, + `scrolled to ${target.id} at offset ${target.offsetTop}`); + + // Save target's original top. const original_top = getComputedStyle(target).top; + const original_offset_top = target.offsetTop; + + // Move target along the y axis. target.style.top = `${target.offsetTop + 100}px`; // Assert that scroller followed target as it moved down. - assert_equals(scroller.scrollTop, target.offsetTop); + assert_equals(n, scroller.scrollTop, target.offsetTop, + `scrolled followed ${target.id} to offset ${target.offsetTop}`); // Cleanup: undo style change. - target.style.top = `${original_top}px`; + target.style.top = original_top; + assert_equals(n, target.offsetTop, original_offset_top, + `${target.id} is put back to offset ${original_offset_top}`); - // Stash result. - stashResult(key, "PASS"); + // Record the result. + result.passed += 1; } catch (error) { - stashResult(key, error.message); + result.errors = [result.errors, error.message].join(); } } - window.onload = () => { - window.requestAnimationFrame(function () { - window.requestAnimationFrame(record); - }) + window.onload = async () => { + let key = (new URL(document.location)).searchParams.get("key"); + + for (const n of [1, 2, 3, 4, 5, 6, 7, 8, 9]) { + await test(n); + } + + stashResult(key, result); } </script> </body> diff --git a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-targeted-element-main-frame.html b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-targeted-element-main-frame.html index 6221b0e4b5..ff81dc78c5 100644 --- a/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-targeted-element-main-frame.html +++ b/testing/web-platform/tests/css/css-scroll-snap/snap-after-relayout/multiple-aligned-targets/prefer-targeted-element-main-frame.html @@ -25,7 +25,7 @@ const response = await fetch(url); const text = await response.text(); if (text) { - resolve(text); + resolve(JSON.parse(text)); } else { requestAnimationFrame(fetchResultInternal.bind(this, url)); } @@ -35,28 +35,17 @@ } function runTest() { - function test(n) { - return promise_test(async (t) => { - let key = token(); + promise_test(async (t) => { + let key = token(); - test_driver.bless("Open a URL with a text fragment directive", () => { - window.open(`prefer-targeted-element-main-frame-target.html` + - `?key=${key}#box${n}`, "_blank", "noopener"); - }); + test_driver.bless("Open a URL", () => { + window.open(`prefer-targeted-element-main-frame-target.html` + + `?key=${key}`, "_blank", "noopener"); + }); - assert_equals(await fetchResult(key), "PASS"); - }, `targeted box${n} is selected snap target.`); - } - - test(1); - test(2); - test(3); - test(4); - test(5); - test(6); - test(7); - test(8); - test(9); + const result = await fetchResult(key); + assert_equals([result.passed, result.errors].join(), "9,"); + }, "targeted box is selected snap target."); } </script> </body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-001.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-001.html new file mode 100644 index 0000000000..eff8667531 --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-001.html @@ -0,0 +1,62 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Scrollbars: scrollbar-color auto on the root defers to ::-webkit-scrollbar</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<style> + :root { + scrollbar-color: auto; + } + + :root::-webkit-scrollbar { + display: none; + } + + /* This is so that browsers that don't implement the WebKit prefix still pass the test */ + @supports not selector(::-webkit-scrollbar) { + :root { + overflow: hidden; + } + } + + :root, + body { + margin: 0; + padding: 0; + } + + #content { + height: 10vh; + width: 100%; + background: lightsalmon; + } + + #expander { + /* force vertical scroll */ + height: 200vh; + width: 300px; + background: gray; + } +</style> + +<body> + + <div id="content"></div> + + <div id="expander"></div> + + <script> + test(function () { + let root = document.documentElement; + let body = document.body; + let content = document.getElementById('content'); + + assert_equals(root.offsetWidth, window.innerWidth, "viewport does not show a scrollbar"); + assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); + assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); + }, "scrollbar-color auto on the root defers to ::-webkit-scrollbar"); + </script> +</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-001.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-001.tentative.html deleted file mode 100644 index c056990934..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-001.tentative.html +++ /dev/null @@ -1,62 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>CSS Scrollbars: scrollbar-color auto on the root defers to ::-webkit-scrollbar</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<style> - :root { - scrollbar-color: auto; - } - - :root::-webkit-scrollbar { - display: none; - } - - /* This is so that browsers that don't implement the WebKit prefix still pass the test */ - @supports not selector(::-webkit-scrollbar) { - :root { - overflow: hidden; - } - } - - :root, - body { - margin: 0; - padding: 0; - } - - #content { - height: 10vh; - width: 100%; - background: lightsalmon; - } - - #expander { - /* force vertical scroll */ - height: 200vh; - width: 300px; - background: gray; - } -</style> - -<body> - - <div id="content"></div> - - <div id="expander"></div> - - <script> - test(function () { - let root = document.documentElement; - let body = document.body; - let content = document.getElementById('content'); - - assert_equals(root.offsetWidth, window.innerWidth, "viewport does not show a scrollbar"); - assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); - assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); - }, "scrollbar-color auto on the root defers to ::-webkit-scrollbar"); - </script> -</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-002.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-002.html new file mode 100644 index 0000000000..1dfc63d68e --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-002.html @@ -0,0 +1,55 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Scrollbars: scrollbar-color non-auto on the root overrides ::-webkit-scrollbar</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<style> + :root { + scrollbar-color: yellow blue; + } + + :root::-webkit-scrollbar { + display: none; + } + + :root, + body { + margin: 0; + padding: 0; + } + + #content { + height: 10vh; + width: 100%; + background: lightsalmon; + } + + #expander { + /* force vertical scroll */ + height: 200vh; + width: 300px; + background: gray; + } +</style> + +<body> + + <div id="content"></div> + + <div id="expander"></div> + + <script> + test(function () { + let root = document.documentElement; + let body = document.body; + let content = document.getElementById('content'); + + assert_less_than(root.offsetWidth, window.innerWidth, "viewport has a scrollbar"); + assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); + assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); + }, "scrollbar-color non-auto on the root overrides ::-webkit-scrollbar"); + </script> +</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-002.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-002.tentative.html deleted file mode 100644 index 70bfa5586d..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-002.tentative.html +++ /dev/null @@ -1,55 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>CSS Scrollbars: scrollbar-color non-auto on the root overrides ::-webkit-scrollbar</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<style> - :root { - scrollbar-color: yellow blue; - } - - :root::-webkit-scrollbar { - display: none; - } - - :root, - body { - margin: 0; - padding: 0; - } - - #content { - height: 10vh; - width: 100%; - background: lightsalmon; - } - - #expander { - /* force vertical scroll */ - height: 200vh; - width: 300px; - background: gray; - } -</style> - -<body> - - <div id="content"></div> - - <div id="expander"></div> - - <script> - test(function () { - let root = document.documentElement; - let body = document.body; - let content = document.getElementById('content'); - - assert_less_than(root.offsetWidth, window.innerWidth, "viewport has a scrollbar"); - assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); - assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); - }, "scrollbar-color non-auto on the root overrides ::-webkit-scrollbar"); - </script> -</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-003.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-003.html new file mode 100644 index 0000000000..1dc3c64f79 --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-003.html @@ -0,0 +1,89 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Scrollbars: scrollbar-color on scrollable areas correctly interacts with ::-webkit-scrollbar on container</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<style> + .container { + overflow: auto; + height: 200px; + width: 200px; + margin: 1px; + padding: 0px; + border: none; + background: deepskyblue; + } + + .content { + height: 300px; + width: 100%; + background: lightsalmon; + } + + .container.auto { + scrollbar-color: auto; + } + + .container.auto::-webkit-scrollbar { + display: none; + } + + /* This is so that browsers that don't implement the WebKit prefix still pass the test */ + @supports not selector(::-webkit-scrollbar) { + .container.auto { + overflow: hidden; + } + } + + .container.themed { + scrollbar-color: yellow blue; + } + + .container.themed::-webkit-scrollbar { + display: none; + } +</style> +<script> + function performTest() { + setup({ explicit_done: true }); + + test(function () { + let container = document.getElementById('container_auto'); + let content = document.getElementById('content_auto'); + assert_equals(container.scrollWidth, 200, "auto scrollWidth"); + assert_equals(container.clientWidth, 200, "auto clientWidth"); + assert_equals(container.offsetLeft, content.offsetLeft, "auto offsetLeft"); + assert_equals(container.clientWidth, content.clientWidth, "auto clientWidth"); + assert_equals(container.offsetWidth, content.offsetWidth, "auto offsetWidth"); + }, "scrollbar-color auto defers to ::-webkit-scrollbar"); + + test(function () { + let container = document.getElementById('container_themed'); + let content = document.getElementById('content_themed'); + assert_less_than(container.scrollWidth, container.offsetWidth, "themed scrollWidth"); + assert_less_than(container.clientWidth, container.offsetWidth, "themed clientWidth"); + assert_equals(container.offsetLeft, content.offsetLeft, "themed offsetLeft"); + assert_equals(container.clientWidth, content.clientWidth, "themed clientWidth"); + assert_not_equals(container.offsetWidth, content.offsetWidth, "themed offsetWidth"); + }, "scrollbar-color yellow blue overrides ::-webkit-scrollbar"); + + done(); + } +</script> + +<body onload="performTest()"> + + Test scrollbar-color: vertical scrollbar + + <div class="container auto" id="container_auto"> + <div class="content" id="content_auto">auto</div> + </div> + + <div class="container themed" id="container_themed"> + <div class="content" id="content_themed">themed</div> + </div> + +</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-003.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-003.tentative.html deleted file mode 100644 index 3fb42bedea..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-003.tentative.html +++ /dev/null @@ -1,89 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>CSS Scrollbars: scrollbar-color on scrollable areas correctly interacts with ::-webkit-scrollbar on container</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<style> - .container { - overflow: auto; - height: 200px; - width: 200px; - margin: 1px; - padding: 0px; - border: none; - background: deepskyblue; - } - - .content { - height: 300px; - width: 100%; - background: lightsalmon; - } - - .container.auto { - scrollbar-color: auto; - } - - .container.auto::-webkit-scrollbar { - display: none; - } - - /* This is so that browsers that don't implement the WebKit prefix still pass the test */ - @supports not selector(::-webkit-scrollbar) { - .container.auto { - overflow: hidden; - } - } - - .container.themed { - scrollbar-color: yellow blue; - } - - .container.themed::-webkit-scrollbar { - display: none; - } -</style> -<script> - function performTest() { - setup({ explicit_done: true }); - - test(function () { - let container = document.getElementById('container_auto'); - let content = document.getElementById('content_auto'); - assert_equals(container.scrollWidth, 200, "auto scrollWidth"); - assert_equals(container.clientWidth, 200, "auto clientWidth"); - assert_equals(container.offsetLeft, content.offsetLeft, "auto offsetLeft"); - assert_equals(container.clientWidth, content.clientWidth, "auto clientWidth"); - assert_equals(container.offsetWidth, content.offsetWidth, "auto offsetWidth"); - }, "scrollbar-color auto defers to ::-webkit-scrollbar"); - - test(function () { - let container = document.getElementById('container_themed'); - let content = document.getElementById('content_themed'); - assert_less_than(container.scrollWidth, container.offsetWidth, "themed scrollWidth"); - assert_less_than(container.clientWidth, container.offsetWidth, "themed clientWidth"); - assert_equals(container.offsetLeft, content.offsetLeft, "themed offsetLeft"); - assert_equals(container.clientWidth, content.clientWidth, "themed clientWidth"); - assert_not_equals(container.offsetWidth, content.offsetWidth, "themed offsetWidth"); - }, "scrollbar-color yellow blue overrides ::-webkit-scrollbar"); - - done(); - } -</script> - -<body onload="performTest()"> - - Test scrollbar-color: vertical scrollbar - - <div class="container auto" id="container_auto"> - <div class="content" id="content_auto">auto</div> - </div> - - <div class="container themed" id="container_themed"> - <div class="content" id="content_themed">themed</div> - </div> - -</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-004.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-004.html new file mode 100644 index 0000000000..cff00c634f --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-004.html @@ -0,0 +1,60 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Scrollbars: scrollbar-color on body correctly interacts with ::-webkit-scrollbar</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<style> + .container { + overflow: auto; + height: 200px; + width: 200px; + margin: 1px; + padding: 0px; + border: none; + background: deepskyblue; + } + + .content { + height: 300px; + width: 100%; + background: lightsalmon; + } + + body { + scrollbar-color: yellow blue; + } + + ::-webkit-scrollbar { + display: none; + } +</style> +<script> + function performTest() { + setup({ explicit_done: true }); + + test(function () { + let container = document.getElementById('container_themed'); + let content = document.getElementById('content_themed'); + assert_less_than(container.scrollWidth, container.offsetWidth, "themed scrollWidth"); + assert_less_than(container.clientWidth, container.offsetWidth, "themed clientWidth"); + assert_equals(container.offsetLeft, content.offsetLeft, "themed offsetLeft"); + assert_equals(container.clientWidth, content.clientWidth, "themed clientWidth"); + assert_not_equals(container.offsetWidth, content.offsetWidth, "themed offsetWidth"); + }, "scrollbar-color yellow blue on body overrides ::-webkit-scrollbar"); + + done(); + } +</script> + +<body onload="performTest()"> + + Test scrollbar-color: vertical scrollbar + + <div class="container themed" id="container_themed"> + <div class="content" id="content_themed">themed</div> + </div> + +</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-004.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-004.tentative.html deleted file mode 100644 index 5932cc5d4e..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-004.tentative.html +++ /dev/null @@ -1,60 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>CSS Scrollbars: scrollbar-color on body correctly interacts with ::-webkit-scrollbar</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<style> - .container { - overflow: auto; - height: 200px; - width: 200px; - margin: 1px; - padding: 0px; - border: none; - background: deepskyblue; - } - - .content { - height: 300px; - width: 100%; - background: lightsalmon; - } - - body { - scrollbar-color: yellow blue; - } - - ::-webkit-scrollbar { - display: none; - } -</style> -<script> - function performTest() { - setup({ explicit_done: true }); - - test(function () { - let container = document.getElementById('container_themed'); - let content = document.getElementById('content_themed'); - assert_less_than(container.scrollWidth, container.offsetWidth, "themed scrollWidth"); - assert_less_than(container.clientWidth, container.offsetWidth, "themed clientWidth"); - assert_equals(container.offsetLeft, content.offsetLeft, "themed offsetLeft"); - assert_equals(container.clientWidth, content.clientWidth, "themed clientWidth"); - assert_not_equals(container.offsetWidth, content.offsetWidth, "themed offsetWidth"); - }, "scrollbar-color yellow blue on body overrides ::-webkit-scrollbar"); - - done(); - } -</script> - -<body onload="performTest()"> - - Test scrollbar-color: vertical scrollbar - - <div class="container themed" id="container_themed"> - <div class="content" id="content_themed">themed</div> - </div> - -</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-005.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-005.html new file mode 100644 index 0000000000..d8ddc43c1c --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-005.html @@ -0,0 +1,60 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Scrollbars: scrollbar-color on body correctly interacts with ::-webkit-scrollbar on scrollable area</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<style> + .container { + overflow: auto; + height: 200px; + width: 200px; + margin: 1px; + padding: 0px; + border: none; + background: deepskyblue; + } + + .content { + height: 300px; + width: 100%; + background: lightsalmon; + } + + body { + scrollbar-color: yellow blue; + } + + .container::-webkit-scrollbar { + display: none; + } +</style> +<script> + function performTest() { + setup({ explicit_done: true }); + + test(function () { + let container = document.getElementById('container_themed'); + let content = document.getElementById('content_themed'); + assert_less_than(container.scrollWidth, container.offsetWidth, "themed scrollWidth"); + assert_less_than(container.clientWidth, container.offsetWidth, "themed clientWidth"); + assert_equals(container.offsetLeft, content.offsetLeft, "themed offsetLeft"); + assert_equals(container.clientWidth, content.clientWidth, "themed clientWidth"); + assert_not_equals(container.offsetWidth, content.offsetWidth, "themed offsetWidth"); + }, "scrollbar-color yellow blue on body overrides ::-webkit-scrollbar on scrollable area"); + + done(); + } +</script> + +<body onload="performTest()"> + + Test scrollbar-color: vertical scrollbar + + <div class="container themed" id="container_themed"> + <div class="content" id="content_themed">themed</div> + </div> + +</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-005.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-005.tentative.html deleted file mode 100644 index 52d0027fba..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-005.tentative.html +++ /dev/null @@ -1,60 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>CSS Scrollbars: scrollbar-color on body correctly interacts with ::-webkit-scrollbar on scrollable area</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<style> - .container { - overflow: auto; - height: 200px; - width: 200px; - margin: 1px; - padding: 0px; - border: none; - background: deepskyblue; - } - - .content { - height: 300px; - width: 100%; - background: lightsalmon; - } - - body { - scrollbar-color: yellow blue; - } - - .container::-webkit-scrollbar { - display: none; - } -</style> -<script> - function performTest() { - setup({ explicit_done: true }); - - test(function () { - let container = document.getElementById('container_themed'); - let content = document.getElementById('content_themed'); - assert_less_than(container.scrollWidth, container.offsetWidth, "themed scrollWidth"); - assert_less_than(container.clientWidth, container.offsetWidth, "themed clientWidth"); - assert_equals(container.offsetLeft, content.offsetLeft, "themed offsetLeft"); - assert_equals(container.clientWidth, content.clientWidth, "themed clientWidth"); - assert_not_equals(container.offsetWidth, content.offsetWidth, "themed offsetWidth"); - }, "scrollbar-color yellow blue on body overrides ::-webkit-scrollbar on scrollable area"); - - done(); - } -</script> - -<body onload="performTest()"> - - Test scrollbar-color: vertical scrollbar - - <div class="container themed" id="container_themed"> - <div class="content" id="content_themed">themed</div> - </div> - -</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-006.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-006.html new file mode 100644 index 0000000000..55947c984c --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-006.html @@ -0,0 +1,33 @@ +<!doctype html> +<html> +<title>CSS Scrollbars: scrollbar-color on scrollable areas correctly interacts with ::-webkit-scrollbar-corner</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="match" href="scrollbar-color-006-ref.html" /> +<link rel="mismatch" href="scrollbar-color-006-mis-ref.html" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" /> +<style> + .container { + scrollbar-gutter: stable; + overflow: auto; + height: 200px; + min-width: 200px; + margin: 1px; + padding: 0px; + border: none; + background: deepskyblue; + scrollbar-color: yellow blue; + } + + ::-webkit-scrollbar-corner { + background-color: purple; + } + + .content { + height: 300px; + width: 300px; + background: red; + } +</style> +<div id="one" class="container"> + <div class="content"></div> +</div> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-006.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-006.tentative.html deleted file mode 100644 index 11f0de750b..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-006.tentative.html +++ /dev/null @@ -1,33 +0,0 @@ -<!doctype html> -<html> -<title>CSS Scrollbars: scrollbar-color on scrollable areas correctly interacts with ::-webkit-scrollbar-corner</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="match" href="scrollbar-color-006-ref.html" /> -<link rel="mismatch" href="scrollbar-color-006-mis-ref.html" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<style> - .container { - scrollbar-gutter: stable; - overflow: auto; - height: 200px; - min-width: 200px; - margin: 1px; - padding: 0px; - border: none; - background: deepskyblue; - scrollbar-color: yellow blue; - } - - ::-webkit-scrollbar-corner { - background-color: purple; - } - - .content { - height: 300px; - width: 300px; - background: red; - } -</style> -<div id="one" class="container"> - <div class="content"></div> -</div> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-007.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-007.html new file mode 100644 index 0000000000..0ef07089de --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-007.html @@ -0,0 +1,33 @@ +<!doctype html> +<html> +<title>CSS Scrollbars: scrollbar-color on scrollable areas correctly interacts with ::-webkit-scrollbar-corner on container</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="match" href="scrollbar-color-007-ref.html" /> +<link rel="mismatch" href="scrollbar-color-007-mis-ref.html" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" /> +<style> + .container { + scrollbar-gutter: stable; + overflow: auto; + height: 200px; + min-width: 200px; + margin: 1px; + padding: 0px; + border: none; + background: deepskyblue; + scrollbar-color: yellow blue; + } + + .container::-webkit-scrollbar-corner { + background-color: purple; + } + + .content { + height: 300px; + width: 300px; + background: red; + } +</style> +<div id="one" class="container"> + <div class="content"></div> +</div> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-007.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-007.tentative.html deleted file mode 100644 index 75358fdf68..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-007.tentative.html +++ /dev/null @@ -1,33 +0,0 @@ -<!doctype html> -<html> -<title>CSS Scrollbars: scrollbar-color on scrollable areas correctly interacts with ::-webkit-scrollbar-corner on container</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="match" href="scrollbar-color-007-ref.html" /> -<link rel="mismatch" href="scrollbar-color-007-mis-ref.html" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<style> - .container { - scrollbar-gutter: stable; - overflow: auto; - height: 200px; - min-width: 200px; - margin: 1px; - padding: 0px; - border: none; - background: deepskyblue; - scrollbar-color: yellow blue; - } - - .container::-webkit-scrollbar-corner { - background-color: purple; - } - - .content { - height: 300px; - width: 300px; - background: red; - } -</style> -<div id="one" class="container"> - <div class="content"></div> -</div> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-008.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-008.html new file mode 100644 index 0000000000..01e3848603 --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-008.html @@ -0,0 +1,36 @@ +<!doctype html> +<html> +<title>CSS Scrollbars: scrollbar-color on body correctly interacts with ::-webkit-scrollbar-corner on container</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="match" href="scrollbar-color-008-ref.html" /> +<link rel="mismatch" href="scrollbar-color-008-mis-ref.html" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" /> +<style> + body { + scrollbar-color: yellow blue; + } + + .container { + scrollbar-gutter: stable; + overflow: auto; + height: 200px; + min-width: 200px; + margin: 1px; + padding: 0px; + border: none; + background: deepskyblue; + } + + .container::-webkit-scrollbar-corner { + background-color: purple; + } + + .content { + height: 300px; + width: 300px; + background: red; + } +</style> +<div id="one" class="container"> + <div class="content"></div> +</div> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-008.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-008.tentative.html deleted file mode 100644 index 576193814c..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-008.tentative.html +++ /dev/null @@ -1,36 +0,0 @@ -<!doctype html> -<html> -<title>CSS Scrollbars: scrollbar-color on body correctly interacts with ::-webkit-scrollbar-corner on container</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="match" href="scrollbar-color-008-ref.html" /> -<link rel="mismatch" href="scrollbar-color-008-mis-ref.html" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<style> - body { - scrollbar-color: yellow blue; - } - - .container { - scrollbar-gutter: stable; - overflow: auto; - height: 200px; - min-width: 200px; - margin: 1px; - padding: 0px; - border: none; - background: deepskyblue; - } - - .container::-webkit-scrollbar-corner { - background-color: purple; - } - - .content { - height: 300px; - width: 300px; - background: red; - } -</style> -<div id="one" class="container"> - <div class="content"></div> -</div> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-009.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-009.html new file mode 100644 index 0000000000..6b17c19c15 --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-009.html @@ -0,0 +1,20 @@ +<!doctype html> +<html> +<title>CSS Scrollbars: scrollbar-color on root correctly interacts with ::-webkit-scrollbar-corner</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="match" href="scrollbar-color-009-ref.html" /> +<link rel="mismatch" href="scrollbar-color-009-mis-ref.html" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" /> +<style> + :root { + scrollbar-color: yellow blue; + } + + body { + overflow: scroll; + } + + ::-webkit-scrollbar-corner { + background-color: purple; + } +</style> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-009.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-009.tentative.html deleted file mode 100644 index 312bbc731b..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-009.tentative.html +++ /dev/null @@ -1,20 +0,0 @@ -<!doctype html> -<html> -<title>CSS Scrollbars: scrollbar-color on root correctly interacts with ::-webkit-scrollbar-corner</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="match" href="scrollbar-color-009-ref.html" /> -<link rel="mismatch" href="scrollbar-color-009-mis-ref.html" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<style> - :root { - scrollbar-color: yellow blue; - } - - body { - overflow: scroll; - } - - ::-webkit-scrollbar-corner { - background-color: purple; - } -</style> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-010.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-010.html new file mode 100644 index 0000000000..cc78504ba7 --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-010.html @@ -0,0 +1,20 @@ +<!doctype html> +<html> +<title>CSS Scrollbars: scrollbar-color on root correctly interacts with ::-webkit-scrollbar-corner on body</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="match" href="scrollbar-color-010-ref.html" /> +<link rel="mismatch" href="scrollbar-color-010-mis-ref.html" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" /> +<style> + :root { + scrollbar-color: yellow blue; + } + + body { + overflow: scroll; + } + + body::-webkit-scrollbar-corner { + background-color: purple; + } +</style> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-010.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-010.tentative.html deleted file mode 100644 index 9f560613e0..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-010.tentative.html +++ /dev/null @@ -1,20 +0,0 @@ -<!doctype html> -<html> -<title>CSS Scrollbars: scrollbar-color on root correctly interacts with ::-webkit-scrollbar-corner on body</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="match" href="scrollbar-color-010-ref.html" /> -<link rel="mismatch" href="scrollbar-color-010-mis-ref.html" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<style> - :root { - scrollbar-color: yellow blue; - } - - body { - overflow: scroll; - } - - body::-webkit-scrollbar-corner { - background-color: purple; - } -</style> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-dynamic-8.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-dynamic-8.html new file mode 100644 index 0000000000..8f1cd13a2f --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-dynamic-8.html @@ -0,0 +1,29 @@ +<!doctype html> +<html class="reftest-wait"> +<title>Dynamically set scrollbar-colors when starts ::-webkit-scrollbar and ensure scrollbars update</title> +<link rel="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#color-compat" /> +<link rel="match" href="scrollbar-color-dynamic-8-ref.html" /> +<script src="/common/reftest-wait.js"></script> +<style> + :root::-webkit-scrollbar { + background: pink; + } + :root::-webkit-scrollbar-thumb { + background: orange; + } + + body { + display: flex; + flex-wrap: wrap; + width: 200vw; + height: 200vh; + } +</style> +<script> + requestAnimationFrame(() => requestAnimationFrame(() => { + document.documentElement.style.scrollbarColor = 'blue green'; + + takeScreenshot(); + })); +</script> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-dynamic-8.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-dynamic-8.tentative.html deleted file mode 100644 index 4926fadaca..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-color-dynamic-8.tentative.html +++ /dev/null @@ -1,29 +0,0 @@ -<!doctype html> -<html class="reftest-wait"> -<title>Dynamically set scrollbar-colors when starts ::-webkit-scrollbar and ensure scrollbars update</title> -<link rel="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars" /> -<link rel="match" href="scrollbar-color-dynamic-8-ref.html" /> -<script src="/common/reftest-wait.js"></script> -<style> - :root::-webkit-scrollbar { - background: pink; - } - :root::-webkit-scrollbar-thumb { - background: orange; - } - - body { - display: flex; - flex-wrap: wrap; - width: 200vw; - height: 200vh; - } -</style> -<script> - requestAnimationFrame(() => requestAnimationFrame(() => { - document.documentElement.style.scrollbarColor = 'blue green'; - - takeScreenshot(); - })); -</script> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-010.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-010.html new file mode 100644 index 0000000000..3c28dff498 --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-010.html @@ -0,0 +1,62 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Scrollbars: scrollbar-width auto on the root defers to ::-webkit-scrollbar on the root</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<style> + :root { + scrollbar-width: auto; + } + + :root::-webkit-scrollbar { + display: none; + } + + /* This is so that browsers that don't implement the WebKit prefix still pass the test */ + @supports not selector(::-webkit-scrollbar) { + :root { + overflow: hidden; + } + } + + :root, + body { + margin: 0; + padding: 0; + } + + #content { + height: 10vh; + width: 100%; + background: lightsalmon; + } + + #expander { + /* force vertical scroll */ + height: 200vh; + width: 300px; + background: gray; + } +</style> + +<body> + + <div id="content"></div> + + <div id="expander"></div> + + <script> + test(function () { + let root = document.documentElement; + let body = document.body; + let content = document.getElementById('content'); + + assert_equals(root.offsetWidth, window.innerWidth, "viewport does not show a scrollbar"); + assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); + assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); + }, "scrollbar-width auto on the root defers to ::-webkit-scrollbar"); + </script> +</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-010.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-010.tentative.html deleted file mode 100644 index ea11703067..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-010.tentative.html +++ /dev/null @@ -1,62 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>CSS Scrollbars: scrollbar-width auto on the root defers to ::-webkit-scrollbar on the root</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<style> - :root { - scrollbar-width: auto; - } - - :root::-webkit-scrollbar { - display: none; - } - - /* This is so that browsers that don't implement the WebKit prefix still pass the test */ - @supports not selector(::-webkit-scrollbar) { - :root { - overflow: hidden; - } - } - - :root, - body { - margin: 0; - padding: 0; - } - - #content { - height: 10vh; - width: 100%; - background: lightsalmon; - } - - #expander { - /* force vertical scroll */ - height: 200vh; - width: 300px; - background: gray; - } -</style> - -<body> - - <div id="content"></div> - - <div id="expander"></div> - - <script> - test(function () { - let root = document.documentElement; - let body = document.body; - let content = document.getElementById('content'); - - assert_equals(root.offsetWidth, window.innerWidth, "viewport does not show a scrollbar"); - assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); - assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); - }, "scrollbar-width auto on the root defers to ::-webkit-scrollbar"); - </script> -</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-011.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-011.html new file mode 100644 index 0000000000..e9c22c83a9 --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-011.html @@ -0,0 +1,55 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Scrollbars: scrollbar-width thin on the root overrides ::-webkit-scrollbar on the root</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<style> + :root { + scrollbar-width: thin; + } + + :root::-webkit-scrollbar { + display: none; + } + + :root, + body { + margin: 0; + padding: 0; + } + + #content { + height: 10vh; + width: 100%; + background: lightsalmon; + } + + #expander { + /* force vertical scroll */ + height: 200vh; + width: 300px; + background: gray; + } +</style> + +<body> + + <div id="content"></div> + + <div id="expander"></div> + + <script> + test(function () { + let root = document.documentElement; + let body = document.body; + let content = document.getElementById('content'); + + assert_less_than(root.offsetWidth, window.innerWidth, "viewport has a scrollbar"); + assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); + assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); + }, "scrollbar-width thin on the root overrides ::-webkit-scrollbar"); + </script> +</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-011.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-011.tentative.html deleted file mode 100644 index a5f60dc28b..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-011.tentative.html +++ /dev/null @@ -1,55 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>CSS Scrollbars: scrollbar-width thin on the root overrides ::-webkit-scrollbar on the root</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<style> - :root { - scrollbar-width: thin; - } - - :root::-webkit-scrollbar { - display: none; - } - - :root, - body { - margin: 0; - padding: 0; - } - - #content { - height: 10vh; - width: 100%; - background: lightsalmon; - } - - #expander { - /* force vertical scroll */ - height: 200vh; - width: 300px; - background: gray; - } -</style> - -<body> - - <div id="content"></div> - - <div id="expander"></div> - - <script> - test(function () { - let root = document.documentElement; - let body = document.body; - let content = document.getElementById('content'); - - assert_less_than(root.offsetWidth, window.innerWidth, "viewport has a scrollbar"); - assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); - assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); - }, "scrollbar-width thin on the root overrides ::-webkit-scrollbar"); - </script> -</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-012.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-012.html new file mode 100644 index 0000000000..eefd5538b8 --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-012.html @@ -0,0 +1,56 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Scrollbars: scrollbar-width none on the root overrides ::-webkit-scrollbar on the root</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<style> + :root { + scrollbar-width: none; + } + + :root::-webkit-scrollbar { + width: 20px; + background-color: lightgray; + } + + :root, + body { + margin: 0; + padding: 0; + } + + #content { + height: 10vh; + width: 100%; + background: lightsalmon; + } + + #expander { + /* force vertical scroll */ + height: 200vh; + width: 300px; + background: gray; + } +</style> + +<body> + + <div id="content"></div> + + <div id="expander"></div> + + <script> + test(function () { + let root = document.documentElement; + let body = document.body; + let content = document.getElementById('content'); + + assert_equals(root.offsetWidth, window.innerWidth, "viewport does not show a scrollbar"); + assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); + assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); + }, "scrollbar-width none on the root overrides ::-webkit-scrollbar"); + </script> +</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-012.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-012.tentative.html deleted file mode 100644 index a685d6c05b..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-012.tentative.html +++ /dev/null @@ -1,56 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>CSS Scrollbars: scrollbar-width none on the root overrides ::-webkit-scrollbar on the root</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<style> - :root { - scrollbar-width: none; - } - - :root::-webkit-scrollbar { - width: 20px; - background-color: lightgray; - } - - :root, - body { - margin: 0; - padding: 0; - } - - #content { - height: 10vh; - width: 100%; - background: lightsalmon; - } - - #expander { - /* force vertical scroll */ - height: 200vh; - width: 300px; - background: gray; - } -</style> - -<body> - - <div id="content"></div> - - <div id="expander"></div> - - <script> - test(function () { - let root = document.documentElement; - let body = document.body; - let content = document.getElementById('content'); - - assert_equals(root.offsetWidth, window.innerWidth, "viewport does not show a scrollbar"); - assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); - assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); - }, "scrollbar-width none on the root overrides ::-webkit-scrollbar"); - </script> -</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-013.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-013.html new file mode 100644 index 0000000000..4a8a9182e5 --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-013.html @@ -0,0 +1,55 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Scrollbars: scrollbar-width thin on the root overridess ::-webkit-scrollbar</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<style> + :root { + scrollbar-width: thin; + } + + ::-webkit-scrollbar { + display: none; + } + + :root, + body { + margin: 0; + padding: 0; + } + + #content { + height: 10vh; + width: 100%; + background: lightsalmon; + } + + #expander { + /* force vertical scroll */ + height: 200vh; + width: 300px; + background: gray; + } +</style> + +<body> + + <div id="content"></div> + + <div id="expander"></div> + + <script> + test(function () { + let root = document.documentElement; + let body = document.body; + let content = document.getElementById('content'); + + assert_less_than(root.offsetWidth, window.innerWidth, "viewport has a scrollbar"); + assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); + assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); + }, "scrollbar-width thin on the root overridess ::-webkit-scrollbar"); + </script> +</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-013.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-013.tentative.html deleted file mode 100644 index f6460a4797..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-013.tentative.html +++ /dev/null @@ -1,55 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>CSS Scrollbars: scrollbar-width thin on the root overridess ::-webkit-scrollbar</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<style> - :root { - scrollbar-width: thin; - } - - ::-webkit-scrollbar { - display: none; - } - - :root, - body { - margin: 0; - padding: 0; - } - - #content { - height: 10vh; - width: 100%; - background: lightsalmon; - } - - #expander { - /* force vertical scroll */ - height: 200vh; - width: 300px; - background: gray; - } -</style> - -<body> - - <div id="content"></div> - - <div id="expander"></div> - - <script> - test(function () { - let root = document.documentElement; - let body = document.body; - let content = document.getElementById('content'); - - assert_less_than(root.offsetWidth, window.innerWidth, "viewport has a scrollbar"); - assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); - assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); - }, "scrollbar-width thin on the root overridess ::-webkit-scrollbar"); - </script> -</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-014.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-014.html new file mode 100644 index 0000000000..b8ca3cbb94 --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-014.html @@ -0,0 +1,62 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Scrollbars: scrollbar-width thin on the body doesn't override ::-webkit-scrollbar on root</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<style> + body { + scrollbar-width: thin; + } + + :root::-webkit-scrollbar { + display: none; + } + + /* This is so that browsers that don't implement the WebKit prefix still pass the test */ + @supports not selector(::-webkit-scrollbar) { + :root { + overflow: hidden; + } + } + + :root, + body { + margin: 0; + padding: 0; + } + + #content { + height: 10vh; + width: 100%; + background: lightsalmon; + } + + #expander { + /* force vertical scroll */ + height: 200vh; + width: 300px; + background: gray; + } +</style> + +<body> + + <div id="content"></div> + + <div id="expander"></div> + + <script> + test(function () { + let root = document.documentElement; + let body = document.body; + let content = document.getElementById('content'); + + assert_equals(root.offsetWidth, window.innerWidth, "viewport does not show a scrollbar"); + assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); + assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); + }, "scrollbar-width thin on the body doesn't override ::-webkit-scrollbar on root"); + </script> +</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-014.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-014.tentative.html deleted file mode 100644 index b3702015d5..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-014.tentative.html +++ /dev/null @@ -1,62 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>CSS Scrollbars: scrollbar-width thin on the body doesn't override ::-webkit-scrollbar on root</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<style> - body { - scrollbar-width: thin; - } - - :root::-webkit-scrollbar { - display: none; - } - - /* This is so that browsers that don't implement the WebKit prefix still pass the test */ - @supports not selector(::-webkit-scrollbar) { - :root { - overflow: hidden; - } - } - - :root, - body { - margin: 0; - padding: 0; - } - - #content { - height: 10vh; - width: 100%; - background: lightsalmon; - } - - #expander { - /* force vertical scroll */ - height: 200vh; - width: 300px; - background: gray; - } -</style> - -<body> - - <div id="content"></div> - - <div id="expander"></div> - - <script> - test(function () { - let root = document.documentElement; - let body = document.body; - let content = document.getElementById('content'); - - assert_equals(root.offsetWidth, window.innerWidth, "viewport does not show a scrollbar"); - assert_equals(body.offsetWidth, root.offsetWidth, "body matches root"); - assert_equals(content.offsetWidth, body.offsetWidth, "content matches body"); - }, "scrollbar-width thin on the body doesn't override ::-webkit-scrollbar on root"); - </script> -</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-015.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-015.html new file mode 100644 index 0000000000..7fc1ae120a --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-015.html @@ -0,0 +1,87 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Scrollbars: scrollbar-width on scrollable areas correctly interacts with ::-webkit-scrollbar</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<style> + .container { + overflow: auto; + height: 200px; + width: 200px; + margin: 1px; + padding: 0px; + border: none; + background: deepskyblue; + } + + .content { + height: 300px; + width: 100%; + background: lightsalmon; + } + + .container.auto { + scrollbar-width: auto; + } + + /* This is so that browsers that don't implement the WebKit prefix still pass the test */ + @supports not selector(::-webkit-scrollbar) { + .container.auto { + overflow: hidden; + } + } + + ::-webkit-scrollbar { + display: none; + } + + .container.thin { + scrollbar-width: thin; + } +</style> +<script> + function performTest() { + setup({ explicit_done: true }); + + // ltr + + test(function () { + let container = document.getElementById('container_auto'); + let content = document.getElementById('content_auto'); + assert_equals(container.scrollWidth, 200, "auto scrollWidth"); + assert_equals(container.clientWidth, 200, "auto clientWidth"); + assert_equals(container.offsetLeft, content.offsetLeft, "auto offsetLeft"); + assert_equals(container.clientWidth, content.clientWidth, "auto clientWidth"); + assert_equals(container.offsetWidth, content.offsetWidth, "auto offsetWidth"); + }, "scrollbar-width auto defers to ::-webkit-scrollbar"); + + test(function () { + let container = document.getElementById('container_thin'); + let content = document.getElementById('content_thin'); + assert_less_than(container.scrollWidth, container.offsetWidth, "thin scrollWidth"); + assert_less_than(container.clientWidth, container.offsetWidth, "thin clientWidth"); + assert_equals(container.offsetLeft, content.offsetLeft, "thin offsetLeft"); + assert_equals(container.clientWidth, content.clientWidth, "thin clientWidth"); + assert_not_equals(container.offsetWidth, content.offsetWidth, "thin offsetWidth"); + }, "scrollbar-width thin overrides ::-webkit-scrollbar"); + + done(); + } +</script> + +<body onload="performTest()"> + + Test scrollbar-width: vertical scrollbar + + <div class="container auto" id="container_auto"> + <div class="content" id="content_auto">auto</div> + </div> + + <div class="container thin" id="container_thin"> + <div class="content" id="content_thin">thin</div> + </div> + +</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-015.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-015.tentative.html deleted file mode 100644 index 88a6af27ba..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-015.tentative.html +++ /dev/null @@ -1,87 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>CSS Scrollbars: scrollbar-width on scrollable areas correctly interacts with ::-webkit-scrollbar</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<style> - .container { - overflow: auto; - height: 200px; - width: 200px; - margin: 1px; - padding: 0px; - border: none; - background: deepskyblue; - } - - .content { - height: 300px; - width: 100%; - background: lightsalmon; - } - - .container.auto { - scrollbar-width: auto; - } - - /* This is so that browsers that don't implement the WebKit prefix still pass the test */ - @supports not selector(::-webkit-scrollbar) { - .container.auto { - overflow: hidden; - } - } - - ::-webkit-scrollbar { - display: none; - } - - .container.thin { - scrollbar-width: thin; - } -</style> -<script> - function performTest() { - setup({ explicit_done: true }); - - // ltr - - test(function () { - let container = document.getElementById('container_auto'); - let content = document.getElementById('content_auto'); - assert_equals(container.scrollWidth, 200, "auto scrollWidth"); - assert_equals(container.clientWidth, 200, "auto clientWidth"); - assert_equals(container.offsetLeft, content.offsetLeft, "auto offsetLeft"); - assert_equals(container.clientWidth, content.clientWidth, "auto clientWidth"); - assert_equals(container.offsetWidth, content.offsetWidth, "auto offsetWidth"); - }, "scrollbar-width auto defers to ::-webkit-scrollbar"); - - test(function () { - let container = document.getElementById('container_thin'); - let content = document.getElementById('content_thin'); - assert_less_than(container.scrollWidth, container.offsetWidth, "thin scrollWidth"); - assert_less_than(container.clientWidth, container.offsetWidth, "thin clientWidth"); - assert_equals(container.offsetLeft, content.offsetLeft, "thin offsetLeft"); - assert_equals(container.clientWidth, content.clientWidth, "thin clientWidth"); - assert_not_equals(container.offsetWidth, content.offsetWidth, "thin offsetWidth"); - }, "scrollbar-width thin overrides ::-webkit-scrollbar"); - - done(); - } -</script> - -<body onload="performTest()"> - - Test scrollbar-width: vertical scrollbar - - <div class="container auto" id="container_auto"> - <div class="content" id="content_auto">auto</div> - </div> - - <div class="container thin" id="container_thin"> - <div class="content" id="content_thin">thin</div> - </div> - -</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-016.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-016.html new file mode 100644 index 0000000000..29b4e47628 --- /dev/null +++ b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-016.html @@ -0,0 +1,114 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Scrollbars: scrollbar-width on scrollable areas correctly interacts with ::-webkit-scrollbar on container</title> +<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/#width-compat" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +<style> + .container { + overflow: auto; + height: 200px; + width: 200px; + margin: 1px; + padding: 0px; + border: none; + background: deepskyblue; + } + + .content { + height: 300px; + width: 100%; + background: lightsalmon; + } + + .container.auto { + scrollbar-width: auto; + } + + .container.auto::-webkit-scrollbar { + display: none; + } + + /* This is so that browsers that don't implement the WebKit prefix still pass the test */ + @supports not selector(::-webkit-scrollbar) { + .container.auto { + overflow: hidden; + } + } + + .container.thin { + scrollbar-width: thin; + } + + .container.thin::-webkit-scrollbar { + display: none; + } + + .container.none { + scrollbar-width: none; + } + + .container.none::-webkit-scrollbar { + width: 20px; + background-color: lightgray; + } +</style> +<script> + function performTest() { + setup({ explicit_done: true }); + + // ltr + + test(function () { + let container = document.getElementById('container_auto'); + let content = document.getElementById('content_auto'); + assert_equals(container.scrollWidth, 200, "auto scrollWidth"); + assert_equals(container.clientWidth, 200, "auto clientWidth"); + assert_equals(container.offsetLeft, content.offsetLeft, "auto offsetLeft"); + assert_equals(container.clientWidth, content.clientWidth, "auto clientWidth"); + assert_equals(container.offsetWidth, content.offsetWidth, "auto offsetWidth"); + }, "scrollbar-width auto defers to ::-webkit-scrollbar"); + + test(function () { + let container = document.getElementById('container_thin'); + let content = document.getElementById('content_thin'); + assert_less_than(container.scrollWidth, container.offsetWidth, "thin scrollWidth"); + assert_less_than(container.clientWidth, container.offsetWidth, "thin clientWidth"); + assert_equals(container.offsetLeft, content.offsetLeft, "thin offsetLeft"); + assert_equals(container.clientWidth, content.clientWidth, "thin clientWidth"); + assert_not_equals(container.offsetWidth, content.offsetWidth, "thin offsetWidth"); + }, "scrollbar-width thin overrides ::-webkit-scrollbar"); + + test(function () { + let container = document.getElementById('container_none'); + let content = document.getElementById('content_none'); + assert_equals(container.scrollWidth, 200, "none scrollWidth"); + assert_equals(container.clientWidth, 200, "none clientWidth"); + assert_equals(container.offsetLeft, content.offsetLeft, "none offsetLeft"); + assert_equals(container.clientWidth, content.clientWidth, "none clientWidth"); + assert_equals(container.offsetWidth, content.offsetWidth, "none offsetWidth"); + }, "scrollbar-width none overrides ::-webkit-scrollbar"); + + done(); + } +</script> + +<body onload="performTest()"> + + Test scrollbar-width: vertical scrollbar + + <div class="container auto" id="container_auto"> + <div class="content" id="content_auto">auto</div> + </div> + + <div class="container thin" id="container_thin"> + <div class="content" id="content_thin">thin</div> + </div> + + <div class="container none" id="container_none"> + <div class="content" id="content_none">none</div> + </div> + +</body> diff --git a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-016.tentative.html b/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-016.tentative.html deleted file mode 100644 index 7d64131743..0000000000 --- a/testing/web-platform/tests/css/css-scrollbars/scrollbar-width-016.tentative.html +++ /dev/null @@ -1,114 +0,0 @@ -<!doctype html> -<meta charset="utf-8"> -<title>CSS Scrollbars: scrollbar-width on scrollable areas correctly interacts with ::-webkit-scrollbar on container</title> -<link rel="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> -<link rel="help" href="https://drafts.csswg.org/css-scrollbars-1/" /> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/css/support/parsing-testcommon.js"></script> -<style> - .container { - overflow: auto; - height: 200px; - width: 200px; - margin: 1px; - padding: 0px; - border: none; - background: deepskyblue; - } - - .content { - height: 300px; - width: 100%; - background: lightsalmon; - } - - .container.auto { - scrollbar-width: auto; - } - - .container.auto::-webkit-scrollbar { - display: none; - } - - /* This is so that browsers that don't implement the WebKit prefix still pass the test */ - @supports not selector(::-webkit-scrollbar) { - .container.auto { - overflow: hidden; - } - } - - .container.thin { - scrollbar-width: thin; - } - - .container.thin::-webkit-scrollbar { - display: none; - } - - .container.none { - scrollbar-width: none; - } - - .container.none::-webkit-scrollbar { - width: 20px; - background-color: lightgray; - } -</style> -<script> - function performTest() { - setup({ explicit_done: true }); - - // ltr - - test(function () { - let container = document.getElementById('container_auto'); - let content = document.getElementById('content_auto'); - assert_equals(container.scrollWidth, 200, "auto scrollWidth"); - assert_equals(container.clientWidth, 200, "auto clientWidth"); - assert_equals(container.offsetLeft, content.offsetLeft, "auto offsetLeft"); - assert_equals(container.clientWidth, content.clientWidth, "auto clientWidth"); - assert_equals(container.offsetWidth, content.offsetWidth, "auto offsetWidth"); - }, "scrollbar-width auto defers to ::-webkit-scrollbar"); - - test(function () { - let container = document.getElementById('container_thin'); - let content = document.getElementById('content_thin'); - assert_less_than(container.scrollWidth, container.offsetWidth, "thin scrollWidth"); - assert_less_than(container.clientWidth, container.offsetWidth, "thin clientWidth"); - assert_equals(container.offsetLeft, content.offsetLeft, "thin offsetLeft"); - assert_equals(container.clientWidth, content.clientWidth, "thin clientWidth"); - assert_not_equals(container.offsetWidth, content.offsetWidth, "thin offsetWidth"); - }, "scrollbar-width thin overrides ::-webkit-scrollbar"); - - test(function () { - let container = document.getElementById('container_none'); - let content = document.getElementById('content_none'); - assert_equals(container.scrollWidth, 200, "none scrollWidth"); - assert_equals(container.clientWidth, 200, "none clientWidth"); - assert_equals(container.offsetLeft, content.offsetLeft, "none offsetLeft"); - assert_equals(container.clientWidth, content.clientWidth, "none clientWidth"); - assert_equals(container.offsetWidth, content.offsetWidth, "none offsetWidth"); - }, "scrollbar-width none overrides ::-webkit-scrollbar"); - - done(); - } -</script> - -<body onload="performTest()"> - - Test scrollbar-width: vertical scrollbar - - <div class="container auto" id="container_auto"> - <div class="content" id="content_auto">auto</div> - </div> - - <div class="container thin" id="container_thin"> - <div class="content" id="content_thin">thin</div> - </div> - - <div class="container none" id="container_none"> - <div class="content" id="content_none">none</div> - </div> - -</body> diff --git a/testing/web-platform/tests/css/css-sizing/animation/height-interpolation.html b/testing/web-platform/tests/css/css-sizing/animation/height-interpolation.html index 10ceed5b2c..75e0977fa1 100644 --- a/testing/web-platform/tests/css/css-sizing/animation/height-interpolation.html +++ b/testing/web-platform/tests/css/css-sizing/animation/height-interpolation.html @@ -66,6 +66,12 @@ test_no_interpolation({ to: '20px', }); +test_no_interpolation({ + property: 'height', + from: 'auto', + to: '20px', +}); + test_interpolation({ property: 'height', from: '0px', @@ -78,4 +84,41 @@ test_interpolation({ {at: 1, expect: '100px'}, {at: 1.5, expect: '150px'} ]); + +test_no_interpolation({ + property: 'height', + from: 'auto', + to: 'min-content', +}); + +test_no_interpolation({ + property: 'height', + from: 'fit-content', + to: '20px', +}); + +test_no_interpolation({ + property: 'height', + from: 'max-content', + to: 'fit-content', +}); + +test_no_interpolation({ + property: 'height', + from: 'max-content', + to: 'stretch', +}); + +test_no_interpolation({ + property: 'height', + from: 'max-content', + to: neutralKeyframe, +}); + +test_no_interpolation({ + property: 'height', + from: neutralKeyframe, + to: 'fit-content', +}); + </script> diff --git a/testing/web-platform/tests/css/css-sizing/animation/max-height-interpolation.html b/testing/web-platform/tests/css/css-sizing/animation/max-height-interpolation.html index c4cab0e1cf..f2af3629bc 100644 --- a/testing/web-platform/tests/css/css-sizing/animation/max-height-interpolation.html +++ b/testing/web-platform/tests/css/css-sizing/animation/max-height-interpolation.html @@ -72,6 +72,12 @@ test_no_interpolation({ to: '20px', }); +test_no_interpolation({ + property: 'max-height', + from: 'none', + to: '20px', +}); + test_interpolation({ property: 'max-height', from: '0px', @@ -84,4 +90,35 @@ test_interpolation({ {at: 1, expect: '100px'}, {at: 1.5, expect: '150px'} ]); + +test_no_interpolation({ + property: 'max-height', + from: 'none', + to: 'max-content', +}); + +test_no_interpolation({ + property: 'max-height', + from: 'stretch', + to: 'fit-content', +}); + +test_no_interpolation({ + property: 'max-height', + from: '20px', + to: 'min-content', +}); + +test_no_interpolation({ + property: 'max-height', + from: 'min-content', + to: neutralKeyframe, +}); + +test_no_interpolation({ + property: 'max-height', + from: neutralKeyframe, + to: 'max-content', +}); + </script> diff --git a/testing/web-platform/tests/css/css-sizing/animation/max-width-interpolation.html b/testing/web-platform/tests/css/css-sizing/animation/max-width-interpolation.html index 111199baa7..4d1614db6d 100644 --- a/testing/web-platform/tests/css/css-sizing/animation/max-width-interpolation.html +++ b/testing/web-platform/tests/css/css-sizing/animation/max-width-interpolation.html @@ -65,6 +65,12 @@ test_no_interpolation({ to: '20px', }); +test_no_interpolation({ + property: 'max-width', + from: 'none', + to: '20px', +}); + test_interpolation({ property: 'max-width', from: '0px', @@ -77,4 +83,35 @@ test_interpolation({ {at: 1, expect: '100px'}, {at: 1.5, expect: '150px'} ]); + +test_no_interpolation({ + property: 'max-width', + from: 'stretch', + to: 'none', +}); + +test_no_interpolation({ + property: 'max-width', + from: 'fit-content', + to: '20px', +}); + +test_no_interpolation({ + property: 'max-width', + from: 'max-content', + to: 'min-content', +}); + +test_no_interpolation({ + property: 'max-width', + from: 'min-content', + to: neutralKeyframe, +}); + +test_no_interpolation({ + property: 'max-width', + from: neutralKeyframe, + to: 'fit-content', +}); + </script> diff --git a/testing/web-platform/tests/css/css-sizing/animation/min-height-interpolation.html b/testing/web-platform/tests/css/css-sizing/animation/min-height-interpolation.html index 6fd5b4e2f5..33f3fd1d7f 100644 --- a/testing/web-platform/tests/css/css-sizing/animation/min-height-interpolation.html +++ b/testing/web-platform/tests/css/css-sizing/animation/min-height-interpolation.html @@ -65,6 +65,12 @@ test_no_interpolation({ to: '20px', }); +test_no_interpolation({ + property: 'min-height', + from: 'auto', + to: '20px', +}); + test_interpolation({ property: 'min-height', from: '0px', @@ -77,4 +83,35 @@ test_interpolation({ {at: 1, expect: '100px'}, {at: 1.5, expect: '150px'} ]); + +test_no_interpolation({ + property: 'min-height', + from: 'auto', + to: '0px', +}); + +test_no_interpolation({ + property: 'min-height', + from: 'fit-content', + to: 'min-content', +}); + +test_no_interpolation({ + property: 'min-height', + from: 'max-content', + to: 'stretch', +}); + +test_no_interpolation({ + property: 'min-height', + from: 'fit-content', + to: neutralKeyframe, +}); + +test_no_interpolation({ + property: 'min-height', + from: neutralKeyframe, + to: 'min-content', +}); + </script> diff --git a/testing/web-platform/tests/css/css-sizing/animation/min-width-interpolation.html b/testing/web-platform/tests/css/css-sizing/animation/min-width-interpolation.html index d11fb3d5cb..e9edf0c6ec 100644 --- a/testing/web-platform/tests/css/css-sizing/animation/min-width-interpolation.html +++ b/testing/web-platform/tests/css/css-sizing/animation/min-width-interpolation.html @@ -64,6 +64,12 @@ test_no_interpolation({ to: '20px', }); +test_no_interpolation({ + property: 'min-width', + from: 'auto', + to: '20px', +}); + test_interpolation({ property: 'min-width', from: '0px', @@ -76,4 +82,35 @@ test_interpolation({ {at: 1, expect: '100px'}, {at: 1.5, expect: '150px'} ]); + +test_no_interpolation({ + property: 'min-width', + from: '0px', + to: 'stretch', +}); + +test_no_interpolation({ + property: 'min-width', + from: 'min-content', + to: 'fit-content', +}); + +test_no_interpolation({ + property: 'min-width', + from: 'auto', + to: 'max-content', +}); + +test_no_interpolation({ + property: 'min-width', + from: 'fit-content', + to: neutralKeyframe, +}); + +test_no_interpolation({ + property: 'min-width', + from: neutralKeyframe, + to: 'max-content', +}); + </script> diff --git a/testing/web-platform/tests/css/css-sizing/animation/width-interpolation.html b/testing/web-platform/tests/css/css-sizing/animation/width-interpolation.html index d165c994b5..b229b5bc5e 100644 --- a/testing/web-platform/tests/css/css-sizing/animation/width-interpolation.html +++ b/testing/web-platform/tests/css/css-sizing/animation/width-interpolation.html @@ -97,6 +97,12 @@ test_interpolation({ {at: 1.5, expect: '145px'} ]); +test_no_interpolation({ + property: 'width', + from: 'auto', + to: '40px', +}); + // The "vw" unit equals to 1% of the width of the viewport's initial containing // block: // https://developer.mozilla.org/en-US/docs/Web/CSS/length @@ -123,5 +129,42 @@ test_interpolation({ {at: 1, expect: calc(1)}, {at: 1.5, expect: calc(1.5)} ]); + +test_no_interpolation({ + property: 'width', + from: 'auto', + to: 'fit-content', +}); + +test_no_interpolation({ + property: 'width', + from: 'stretch', + to: 'auto', +}); + +test_no_interpolation({ + property: 'width', + from: '30px', + to: 'fit-content', +}); + +test_no_interpolation({ + property: 'width', + from: 'max-content', + to: 'min-content', +}); + +test_no_interpolation({ + property: 'width', + from: 'max-content', + to: neutralKeyframe, +}); + +test_no_interpolation({ + property: 'width', + from: neutralKeyframe, + to: 'min-content', +}); + </script> </body> diff --git a/testing/web-platform/tests/css/css-sizing/aspect-ratio/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-sizing/aspect-ratio/WEB_FEATURES.yml new file mode 100644 index 0000000000..65e2142a16 --- /dev/null +++ b/testing/web-platform/tests/css/css-sizing/aspect-ratio/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: aspect-ratio + files: "**" diff --git a/testing/web-platform/tests/css/css-sizing/aspect-ratio/support/2x2-green.ogv b/testing/web-platform/tests/css/css-sizing/aspect-ratio/support/2x2-green.ogv Binary files differdeleted file mode 100644 index 29903c0a81..0000000000 --- a/testing/web-platform/tests/css/css-sizing/aspect-ratio/support/2x2-green.ogv +++ /dev/null diff --git a/testing/web-platform/tests/css/css-sizing/aspect-ratio/support/2x2-green.webm b/testing/web-platform/tests/css/css-sizing/aspect-ratio/support/2x2-green.webm Binary files differnew file mode 100644 index 0000000000..74af43afeb --- /dev/null +++ b/testing/web-platform/tests/css/css-sizing/aspect-ratio/support/2x2-green.webm diff --git a/testing/web-platform/tests/css/css-tables/crashtests/caption-repaint-crash.html b/testing/web-platform/tests/css/css-tables/crashtests/caption-repaint-crash.html new file mode 100644 index 0000000000..6a024d0c1d --- /dev/null +++ b/testing/web-platform/tests/css/css-tables/crashtests/caption-repaint-crash.html @@ -0,0 +1,19 @@ +<style> +*:defined { + outline: currentColor dashed; +} +*:read-write { + animation: kf 200ms ease-out 16384 alternate-reverse backwards +} +@keyframes kf { + 20% { + clip-path: polygon(1px 0.75em, 128px 100%) + } +} +</style> +<script> +document.addEventListener("DOMContentLoaded", () => { + a.createCaption() +}) +</script> +<table id="a" contenteditable="true" align="left"> diff --git a/testing/web-platform/tests/css/css-tables/table-cell-inline-size-box-sizing-quirks-ref.html b/testing/web-platform/tests/css/css-tables/table-cell-inline-size-box-sizing-quirks-ref.html new file mode 100644 index 0000000000..4b97b4499b --- /dev/null +++ b/testing/web-platform/tests/css/css-tables/table-cell-inline-size-box-sizing-quirks-ref.html @@ -0,0 +1,15 @@ +<!doctype html> +<title>CSS Test Reference</title> +<style> + td { + width: 50px; + padding: 10px; + border: 1px solid black; + box-sizing: border-box; + } +</style> +<table> + <tr> + <td>A</td> + </tr> +</table> diff --git a/testing/web-platform/tests/css/css-tables/table-cell-inline-size-box-sizing-quirks.html b/testing/web-platform/tests/css/css-tables/table-cell-inline-size-box-sizing-quirks.html new file mode 100644 index 0000000000..a59a0672e9 --- /dev/null +++ b/testing/web-platform/tests/css/css-tables/table-cell-inline-size-box-sizing-quirks.html @@ -0,0 +1,19 @@ +<!~quirks> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://quirks.spec.whatwg.org/#the-table-cell-height-box-sizing-quirk"> +<link rel="match" href="table-cell-inline-size-box-sizing-quirks-ref.html"> +<title>Table cell box-sizing quirk doesn't force inline-axis to be content-box</title> +<style> + td { + width: 50px; + padding: 10px; + border: 1px solid black; + box-sizing: border-box; + } +</style> +<table> + <tr> + <td>A</td> + </tr> +</table> diff --git a/testing/web-platform/tests/css/css-tables/tentative/table-rows-with-zero-columns.html b/testing/web-platform/tests/css/css-tables/tentative/table-rows-with-zero-columns.html new file mode 100644 index 0000000000..da9e0098a7 --- /dev/null +++ b/testing/web-platform/tests/css/css-tables/tentative/table-rows-with-zero-columns.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Test: size of table rows when the table has no columns</title> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-tables-3/"> +<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/10132"> +<meta name="assert" content="If a table has rows but no columns, the rows are as wide as the inner width of the table."> + +<style> +#tests table { + box-sizing: border-box; + width: 60px; + height: 60px; +} +</style> + +<div id="log"></div> + +<main id="tests"> + <table cellspacing="0"> + <tr data-expected-width="60" data-expected-height="60"></tr> + </table> + + <table cellspacing="0"> + <tr data-expected-width="60" data-expected-height="30"></tr> + <tr data-expected-width="60" data-expected-height="30"></tr> + </table> + + <table cellspacing="10"> + <tr data-expected-width="60" data-expected-height="40"></tr> + </table> + + <table cellspacing="10"> + <tr data-expected-width="60" data-expected-height="15"></tr> + <tr data-expected-width="60" data-expected-height="15"></tr> + </table> + + <table cellspacing="0" border="5"> + <tr data-expected-width="50" data-expected-height="50"></tr> + </table> + + <table cellspacing="0" border="5"> + <tr data-expected-width="50" data-expected-height="25"></tr> + <tr data-expected-width="50" data-expected-height="25"></tr> + </table> + + <table cellspacing="10" border="5"> + <tr data-expected-width="50" data-expected-height="30"></tr> + </table> + + <table cellspacing="10" border="5"> + <tr data-expected-width="50" data-expected-height="10"></tr> + <tr data-expected-width="50" data-expected-height="10"></tr> + </table> +</main> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/check-layout-th.js"></script> +<script> +checkLayout("tr"); +</script> diff --git a/testing/web-platform/tests/css/css-text/line-breaking/line-breaking-029.html b/testing/web-platform/tests/css/css-text/line-breaking/line-breaking-029.html new file mode 100644 index 0000000000..c390d2272d --- /dev/null +++ b/testing/web-platform/tests/css/css-text/line-breaking/line-breaking-029.html @@ -0,0 +1,46 @@ +<!doctype html> +<html> +<meta charset="utf-8"> +<title>CSS Text — line breaking around Break After and Exclamation</title> +<meta name=assert content="When ‘white-space’ allows wrapping, line breaking behavior defined for IS and QU line-breaking classes in [UAX14] must be honored."> +<link rel=help href="https://www.w3.org/TR/css-text-3/#line-breaking"> +<link rel=help href="https://bugzilla.mozilla.org/show_bug.cgi?id=1880362"> +<link rel=author title="Makoto Kato" href="mailto:m_kato@ga2.so-net.ne.jp"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.test > div { + font-family: monospace; + font-size: 25px; + width: 1ch; + line-height: 30px; +} +</style> +<body> + +<div class="test"> + <div id="nonbreakable1">1.”</div> + <div id="nonbreakable2">a.”</div> + <div id="nonbreakable3">1."</div> + <div id="nonbreakable4">a."</div> +</div> + +<script> +test(function() { + assert_true(document.getElementById('nonbreakable1').offsetHeight <= 35); +}, "U+0x0031 (NU), U+0x002E (IS) and U+0x201D (QU)"); +test(function() { + assert_true(document.getElementById('nonbreakable2').offsetHeight <= 35); +}, "U+0x0041 (AL), U+0x002E (IS) and U+0x201D (QU)"); +test(function() { + assert_true(document.getElementById('nonbreakable3').offsetHeight <= 35); +}, "U+0x0031 (NU), U+0x002E (IS) and U+0x0022 (QU)"); +test(function() { + assert_true(document.getElementById('nonbreakable4').offsetHeight <= 35); +}, "U+0x0041 (AL), U+0x002E (IS) and U+0x0022 (QU)"); +</script> + +<div id='log'></div> + +</body> +</html> diff --git a/testing/web-platform/tests/css/css-text/parsing/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-text/parsing/WEB_FEATURES.yml new file mode 100644 index 0000000000..e06782e8f5 --- /dev/null +++ b/testing/web-platform/tests/css/css-text/parsing/WEB_FEATURES.yml @@ -0,0 +1,4 @@ +features: +- name: text-spacing-trim + files: + - text-spacing-trim-* diff --git a/testing/web-platform/tests/css/css-text/text-align/text-align-justify-bidi-control-ref.html b/testing/web-platform/tests/css/css-text/text-align/text-align-justify-bidi-control-ref.html new file mode 100644 index 0000000000..c30c5a0178 --- /dev/null +++ b/testing/web-platform/tests/css/css-text/text-align/text-align-justify-bidi-control-ref.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<html lang="ja"> + <head> + <meta charset="utf-8"> + <title>text-align: justify for a bidi control + a CJK ideograph</title> + <style> + p { text-align: justify; width: 3.9em; } + </style> + </head> + <body> + <p>東京都東京都東京都</p> + </body> +</html> diff --git a/testing/web-platform/tests/css/css-text/text-align/text-align-justify-bidi-control.html b/testing/web-platform/tests/css/css-text/text-align/text-align-justify-bidi-control.html new file mode 100644 index 0000000000..934e947b75 --- /dev/null +++ b/testing/web-platform/tests/css/css-text/text-align/text-align-justify-bidi-control.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html lang="ja"> + <head> + <meta charset="utf-8"> + <title>text-align: justify for a bidi control + a CJK ideograph</title> + <link rel="help" href="https://drafts.csswg.org/css-text-3/#text-align-last-property"> + <link rel="help" href="https://crbug.com/331680200"> + <link rel="match" href="text-align-justify-bidi-control-ref.html"> + <style> + p { text-align: justify; width: 3.9em; } + </style> + </head> + <body> + <p>東⁦京都東京⁩都東京都</p> + </body> +</html> diff --git a/testing/web-platform/tests/css/css-text/text-align/text-align-last-justify-br-ref.html b/testing/web-platform/tests/css/css-text/text-align/text-align-last-justify-br-ref.html new file mode 100644 index 0000000000..efe5481e82 --- /dev/null +++ b/testing/web-platform/tests/css/css-text/text-align/text-align-last-justify-br-ref.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html lang="ja"> + <head> + <title>text-align-last: justify for <br></title> + <meta charset="utf-8"> + <style> + .left { float: left; display: inline-block; } + .right { float: right; display: inline-block; } + </style> + </head> + <body> + <p><span class="left">東<br>京</span><span class="right">京<br>城</span></p> + </body> +</html> diff --git a/testing/web-platform/tests/css/css-text/text-align/text-align-last-justify-br.html b/testing/web-platform/tests/css/css-text/text-align/text-align-last-justify-br.html new file mode 100644 index 0000000000..5685da088b --- /dev/null +++ b/testing/web-platform/tests/css/css-text/text-align/text-align-last-justify-br.html @@ -0,0 +1,18 @@ +<!DOCTYPE html> +<html lang="ja"> + <head> + <meta charset="utf-8"> + <title>text-align-last: justify for <br></title> + <link rel="help" href="https://drafts.csswg.org/css-text-3/#text-align-last-property"> + <link rel="help" href="https://crbug.com/331729346"> + <link rel="match" href="text-align-last-justify-br-ref.html"> + <style> + p { + text-align-last: justify; + } + </style> + </head> + <body> + <p>東京<br>京城</p> + </body> +</html> diff --git a/testing/web-platform/tests/css/css-text/text-spacing-trim/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-text/text-spacing-trim/WEB_FEATURES.yml new file mode 100644 index 0000000000..329fa18e2a --- /dev/null +++ b/testing/web-platform/tests/css/css-text/text-spacing-trim/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: text-spacing-trim + files: "**" diff --git a/testing/web-platform/tests/css/css-text/white-space/hanging-whitespace-001.tentative.html b/testing/web-platform/tests/css/css-text/white-space/hanging-whitespace-001.tentative.html new file mode 100644 index 0000000000..17e778165d --- /dev/null +++ b/testing/web-platform/tests/css/css-text/white-space/hanging-whitespace-001.tentative.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Text Level 3: glyphs can only hang at the edge of a line</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-text-3/#hanging"> +<link rel="help" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2"> +<link rel="match" href="/css/reference/ref-filled-green-100px-square.xht"> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<meta name="assert" content="Glyphs can't hang at the end of the line if there are non-hanging glyphs after them in the line"> + +<style> + div { + font: 25px/1 Ahem; + width: 5ch; + height: 4ch; + margin-left: -1ch; + background: + linear-gradient(red, red) 1ch 0 / 4ch 1ch no-repeat, + linear-gradient(green, green) 1ch 0 / 4ch 4ch no-repeat; + + text-align: right; + white-space: normal; + } + span { + color: transparent; + background: green; + } + .break-spaces { + white-space: break-spaces; + } +</style> + +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> + +<div><span>X  </span><span class="break-spaces"> </span></div> + +<!-- + With white-space: normal, ideographic spaces (U+3000) are preserved but do + hang. But here they are followed by spaces with white-space: break-spaces, + which are preserved and never hang. Since only glyphs at the edge of a line + can hang, this prevents the ideographic spaces from hanging. +--> diff --git a/testing/web-platform/tests/css/css-text/white-space/hanging-whitespace-002.tentative.html b/testing/web-platform/tests/css/css-text/white-space/hanging-whitespace-002.tentative.html new file mode 100644 index 0000000000..6c486ac69c --- /dev/null +++ b/testing/web-platform/tests/css/css-text/white-space/hanging-whitespace-002.tentative.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Text Level 3: pre-wrap conditionality when not at line end</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-text-3/#hanging"> +<link rel="help" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2"> +<link rel="match" href="/css/reference/ref-filled-green-100px-square.xht"> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<meta name="assert" content="Spaces with white-space: pre-wrap always hang unconditionally when followed by other unconditionally hanging glyphs"> + +<style> + div { + font: 25px/1 Ahem; + width: 5ch; + height: 4ch; + margin-left: -1ch; + background: + linear-gradient(red, red) 4ch 0 / 1ch 4ch no-repeat, + linear-gradient(green, green) 1ch 0 / 3ch 4ch no-repeat; + + white-space: normal; + text-align: right; + color: green; + } + .pre-wrap { + white-space: pre-wrap; + } +</style> + +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> + +<div><span class="pre-wrap">X </span> <br>X<br>X<br>X</div> + +<!-- + With white-space: pre-wrap, spaces hang conditionally when they are followed + by a forced line break. If instead they are followed by a glyph that hangs + unconditionally, such as the ideographic space (U+3000) with + white-space: normal, then they are not followed by a forced line break, even + if that hanging glyph might be, and thus hang unconditionally as well. +--> diff --git a/testing/web-platform/tests/css/css-text/white-space/hanging-whitespace-003.tentative.html b/testing/web-platform/tests/css/css-text/white-space/hanging-whitespace-003.tentative.html new file mode 100644 index 0000000000..b8f4df5967 --- /dev/null +++ b/testing/web-platform/tests/css/css-text/white-space/hanging-whitespace-003.tentative.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Text Level 3: unconditional hanging spaces don't hang before non-overflowing conditionals</title> +<link rel="author" title="Andreu Botella" href="mailto:abotella@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-text-3/#hanging"> +<link rel="help" href="https://drafts.csswg.org/css-text-3/#white-space-phase-2"> +<link rel="match" href="/css/reference/ref-filled-green-100px-square.xht"> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<meta name="assert" content="Hanging can only happen at the end of a line, so unconditionally hanging spaces can only hang before conditionally hanging spaces if the latter would fully overflow"> + +<style> + div { + font: 25px/1 Ahem; + width: 4ch; + height: 4ch; + background: + linear-gradient(green, green) 0 1ch / 1ch 1ch no-repeat, + linear-gradient(green, green) 0 2ch / 2ch 1ch no-repeat, + linear-gradient(green, green) 1ch 3ch / 3ch 1ch no-repeat, + red; + + text-align: right; + white-space: normal; + color: green; + } + .pre-wrap { + white-space: pre-wrap; + } +</style> + +<p>Test passes if there is a filled green square and <strong>no red</strong>.</p> + +<div>XXXX  <span class="pre-wrap"> </span><br> + XXX  <span class="pre-wrap"> </span><br> + XX  <span class="pre-wrap"> </span><br> + X  <span class="pre-wrap"> </span></div> + +<!-- + With white-space: normal, an ideographic space (U+3000) hangs unconditionally, + as long as every glyph after it until the end of the line also hangs. + With white-space: pre-wrap, a sequence of spaces that is followed by a forced + line break (where the end of the block counts as a forced line break) hangs + conditionally, meaning that the part of it that doesn't fit in the line's + available width is the only one that hangs. (This is independent to whether it + would overflow the block's content area.) + + Therefore, unconditional hanging glyphs before such a sequence of conditional + hanging glyphs can only hang if the conditional sequence overflows the line's + available width. +--> diff --git a/testing/web-platform/tests/css/css-text/white-space/reference/text-wrap-balance-004-misref.html b/testing/web-platform/tests/css/css-text/white-space/reference/text-wrap-balance-004-misref.html new file mode 100644 index 0000000000..d3574427b2 --- /dev/null +++ b/testing/web-platform/tests/css/css-text/white-space/reference/text-wrap-balance-004-misref.html @@ -0,0 +1,63 @@ +<!DOCTYPE html> +<html lang="en" > +<meta charset="utf-8"> +<title>CSS test reference</title> +<link rel='author' title='Florian Rivoal' href='https://florian.rivoal.net/'> + +<style> +section { + width: 50ch; + font-family: monospace; +} +.test { color: blue; } +.ref { color: orange; } +.mis { color: magenta; } + +</style> + +<p>This test passes if the line breaks in the blue text and the orange text occur at the same points, unless they are identical to the magenta text. + +<section class=test> +Lorem ipsum dolor sit amet, +consectetur adipiscing elit, +sed do eiusmod tempor incididunt +ut labore et dolore magna aliqua. +Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. +<br> +Duis aute irure dolor in reprehenderit +in voluptate velit +esse cillum dolore eu fugiat nulla pariatur. +</section> + +<section class=ref> +<div> +Lorem ipsum dolor sit amet, +consectetur adipiscing elit, +sed do eiusmod tempor incididunt +ut labore et dolore magna aliqua. +Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. +</div> +<div> +Duis aute irure dolor in reprehenderit +in voluptate velit +esse cillum dolore eu fugiat nulla pariatur. +</div> +</section> + +<section class=mis> +Lorem ipsum dolor sit amet, +consectetur adipiscing elit, +sed do eiusmod tempor incididunt +ut labore et dolore magna aliqua. +Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. +<br> +Duis aute irure dolor in reprehenderit +in voluptate velit +esse cillum dolore eu fugiat nulla pariatur. +</section> diff --git a/testing/web-platform/tests/css/css-text/white-space/reference/text-wrap-balance-004-ref.html b/testing/web-platform/tests/css/css-text/white-space/reference/text-wrap-balance-004-ref.html new file mode 100644 index 0000000000..74d6b392f9 --- /dev/null +++ b/testing/web-platform/tests/css/css-text/white-space/reference/text-wrap-balance-004-ref.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<html lang="en" > +<meta charset="utf-8"> +<title>CSS test reference</title> +<link rel='author' title='Florian Rivoal' href='https://florian.rivoal.net/'> + +<style> +section { + width: 50ch; + font-family: monospace; +} +.test, .ref { + text-wrap: balance; +} +.test { color: blue; } +.ref { color: orange; } +.mis { color: magenta; } + +</style> + +<p>This test passes if the line breaks in the blue text and the orange text occur at the same points, unless they are identical to the magenta text. + +<section class=test> +<div> +Lorem ipsum dolor sit amet, +consectetur adipiscing elit, +sed do eiusmod tempor incididunt +ut labore et dolore magna aliqua. +Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. +</div> +<div> +Duis aute irure dolor in reprehenderit +in voluptate velit +esse cillum dolore eu fugiat nulla pariatur. +</div> +</section> + +<section class=ref> +<div> +Lorem ipsum dolor sit amet, +consectetur adipiscing elit, +sed do eiusmod tempor incididunt +ut labore et dolore magna aliqua. +Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. +</div> +<div> +Duis aute irure dolor in reprehenderit +in voluptate velit +esse cillum dolore eu fugiat nulla pariatur. +</div> +</section> + +<section class=mis> +Lorem ipsum dolor sit amet, +consectetur adipiscing elit, +sed do eiusmod tempor incididunt +ut labore et dolore magna aliqua. +Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. +<br> +Duis aute irure dolor in reprehenderit +in voluptate velit +esse cillum dolore eu fugiat nulla pariatur. +</section> diff --git a/testing/web-platform/tests/css/css-text/white-space/reference/text-wrap-balance-float-006-ref.html b/testing/web-platform/tests/css/css-text/white-space/reference/text-wrap-balance-float-006-ref.html new file mode 100644 index 0000000000..6e01737173 --- /dev/null +++ b/testing/web-platform/tests/css/css-text/white-space/reference/text-wrap-balance-float-006-ref.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset=utf-8> +<style> +body { + font: 20px/1 monospace; +} +.float { + float: left; + width: 15ch; + height: 150px; + background: silver; +} +.mask { + width: 15ch; + height: 150px; + background: green; + position: absolute; + left: 15ch; +} +.test { + width: 28ch; + background: red; +} +</style> +</head> + +<body> +<p>There should be no red:</p> +<div class="float"> + <div class="mask"></div> +</div> +<p class="test"> +Lorem ipsum dolor <span>sit</span> amet consectetur adipisicing elit. +</p> +</body> + diff --git a/testing/web-platform/tests/css/css-text/white-space/text-wrap-balance-004.html b/testing/web-platform/tests/css/css-text/white-space/text-wrap-balance-004.html new file mode 100644 index 0000000000..f5494708ea --- /dev/null +++ b/testing/web-platform/tests/css/css-text/white-space/text-wrap-balance-004.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<html lang="en" > +<meta charset="utf-8"> +<title>CSS test: balancing and forced breaks</title> +<link rel='author' title='Florian Rivoal' href='https://florian.rivoal.net/'> +<link rel='help' href='https://drafts.csswg.org/css-text-4/#text-wrap-style'> +<meta name="assert" content="Groups of lines separated by a forced line break are processed separately."> +<link rel="match" href="reference/text-wrap-balance-004-ref.html"> +<link rel="mismatch" href="reference/text-wrap-balance-004-misref.html"> + +<style> +section { + width: 50ch; + font-family: monospace; +} +.test, .ref { + text-wrap: balance; +} +.test { color: blue; } +.ref { color: orange; } +.mis { color: magenta; } + +</style> + +<p>This test passes if the line breaks in the blue text and the orange text occur at the same points, unless they are identical to the magenta text. + +<section class=test> +Lorem ipsum dolor sit amet, +consectetur adipiscing elit, +sed do eiusmod tempor incididunt +ut labore et dolore magna aliqua. +Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. +<br> +Duis aute irure dolor in reprehenderit +in voluptate velit +esse cillum dolore eu fugiat nulla pariatur. +</section> + +<section class=ref> +<div> +Lorem ipsum dolor sit amet, +consectetur adipiscing elit, +sed do eiusmod tempor incididunt +ut labore et dolore magna aliqua. +Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. +</div> +<div> +Duis aute irure dolor in reprehenderit +in voluptate velit +esse cillum dolore eu fugiat nulla pariatur. +</div> +</section> + +<section class=mis> +Lorem ipsum dolor sit amet, +consectetur adipiscing elit, +sed do eiusmod tempor incididunt +ut labore et dolore magna aliqua. +Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris nisi +ut aliquip ex ea commodo consequat. +<br> +Duis aute irure dolor in reprehenderit +in voluptate velit +esse cillum dolore eu fugiat nulla pariatur. +</section> diff --git a/testing/web-platform/tests/css/css-text/white-space/text-wrap-balance-float-006.html b/testing/web-platform/tests/css/css-text/white-space/text-wrap-balance-float-006.html new file mode 100644 index 0000000000..5343e84598 --- /dev/null +++ b/testing/web-platform/tests/css/css-text/white-space/text-wrap-balance-float-006.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset=utf-8> +<link rel="match" href="reference/text-wrap-balance-float-006-ref.html"> +<link rel="help" href="https://drafts.csswg.org/css-text-4/#valdef-text-wrap-balance"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1888449"> +<style> +body { + font: 20px/1 monospace; +} +.float { + float: left; + width: 15ch; + height: 150px; + background: silver; +} +.mask { + width: 15ch; + height: 150px; + background: green; + position: absolute; + left: 15ch; +} +.test { + width: 28ch; + text-wrap: balance; + background: red; +} +</style> +</head> + +<body> +<p>There should be no red:</p> +<div class="float"> + <div class="mask"></div> +</div> +<p class="test"> +Lorem ipsum dolor <span>sit</span> amet consectetur adipisicing elit. +</p> +</body> + diff --git a/testing/web-platform/tests/css/css-text/white-space/text-wrap-balance-right-to-left.html b/testing/web-platform/tests/css/css-text/white-space/text-wrap-balance-right-to-left.html new file mode 100644 index 0000000000..b9d50684d1 --- /dev/null +++ b/testing/web-platform/tests/css/css-text/white-space/text-wrap-balance-right-to-left.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-text-4/#text-wrap"> +<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +#container { + width: 20ch; +} +.balance { + text-wrap: balance; +} +</style> +<div id="container" dir="rtl" lang="AR"></div> +<script> +const container = document.getElementById('container'); +for (const text of [ + 'ينبغي تحقيق التوازن', + 'يجب ألا تؤدي موازنة التفاف النص إلى تغيير عدد الأسطر', + 'يجب ألا تؤدي موازنة التفاف النص إلى تغيير عدد الأسطر لهذه الفقرة النصية', + ]) { + const normal = document.createElement('div'); + const balance = document.createElement('div'); + normal.textContent = text; + balance.textContent = text; + balance.classList.add('balance'); + container.appendChild(normal); + container.appendChild(balance); + test(() => { + assert_equals(normal.offsetHeight, balance.offsetHeight); + }); +} +</script> diff --git a/testing/web-platform/tests/css/css-text/white-space/text-wrap-balance-top-to-bottom.html b/testing/web-platform/tests/css/css-text/white-space/text-wrap-balance-top-to-bottom.html new file mode 100644 index 0000000000..0c0f6b55ca --- /dev/null +++ b/testing/web-platform/tests/css/css-text/white-space/text-wrap-balance-top-to-bottom.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-text-4/#text-wrap"> +<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +#container { + height: 15ch; + writing-mode: vertical-rl; +} +.balance { + text-wrap: balance; +} +</style> +<div id="container"></div> +<script> +const container = document.getElementById('container'); +for (const text of [ + '平衡应该', + '平衡文本换行不应改变行数', + '平衡文本换行不应改变该文本段落的行数', + ]) { + const normal = document.createElement('div'); + const balance = document.createElement('div'); + normal.textContent = text; + balance.textContent = text; + balance.classList.add('balance'); + container.appendChild(normal); + container.appendChild(balance); + test(() => { + assert_equals(normal.offsetWidth, balance.offsetWidth); + }); +} +</script> diff --git a/testing/web-platform/tests/css/css-transforms/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-transforms/WEB_FEATURES.yml new file mode 100644 index 0000000000..ca13ab5ae1 --- /dev/null +++ b/testing/web-platform/tests/css/css-transforms/WEB_FEATURES.yml @@ -0,0 +1,6 @@ +features: +- name: transforms3d + files: + - "*3d*" + - backface-visibility-* + - perspective-* diff --git a/testing/web-platform/tests/css/css-transforms/animation/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-transforms/animation/WEB_FEATURES.yml new file mode 100644 index 0000000000..831086f99e --- /dev/null +++ b/testing/web-platform/tests/css/css-transforms/animation/WEB_FEATURES.yml @@ -0,0 +1,5 @@ +features: +- name: transforms3d + files: + - backface-visibility-* + - perspective-* diff --git a/testing/web-platform/tests/css/css-transforms/parsing/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-transforms/parsing/WEB_FEATURES.yml new file mode 100644 index 0000000000..831086f99e --- /dev/null +++ b/testing/web-platform/tests/css/css-transforms/parsing/WEB_FEATURES.yml @@ -0,0 +1,5 @@ +features: +- name: transforms3d + files: + - backface-visibility-* + - perspective-* diff --git a/testing/web-platform/tests/css/css-transforms/support/transform-iframe-002-contents.html b/testing/web-platform/tests/css/css-transforms/support/transform-iframe-002-contents.html new file mode 100644 index 0000000000..84f079c90b --- /dev/null +++ b/testing/web-platform/tests/css/css-transforms/support/transform-iframe-002-contents.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test (Transforms): Iframe (contents)</title> + <link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com"> + <style> + body { + background: green; + } + </style> + </head> + <body> + </body> +</html> diff --git a/testing/web-platform/tests/css/css-transforms/support/transform-iframe-scroll-position-contents.html b/testing/web-platform/tests/css/css-transforms/support/transform-iframe-scroll-position-contents.html new file mode 100644 index 0000000000..8efcdafc83 --- /dev/null +++ b/testing/web-platform/tests/css/css-transforms/support/transform-iframe-scroll-position-contents.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> + +<html> + <head> + <title>CSS Test (Transforms): iframe scroll position</title> + <link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com"> + <style> + html { background: red; } + </style> + </head> + + <body> + <!-- Make a large red page with a small green and blue square that is scrolled to immediately. --> + <div style="position: absolute; width: 50px; height: 25px; top: 3000px; left: 3000px; background: green;"></div> + <div style="position: absolute; width: 50px; height: 25px; top: 3025px; left: 3000px; background: blue;"></div> + <div style="width: 10000px; height: 10000px;"></div> + <script> + window.scrollTo(3000, 3000); + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/css-transforms/transform-iframe-002.html b/testing/web-platform/tests/css/css-transforms/transform-iframe-002.html new file mode 100644 index 0000000000..b9b10ea368 --- /dev/null +++ b/testing/web-platform/tests/css/css-transforms/transform-iframe-002.html @@ -0,0 +1,20 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test (Transforms): Iframe</title> + <link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com"> + <link rel="help" href="http://www.w3.org/TR/css-transforms-1/#transform-rendering"> + <meta name="assert" content="This test ensures that an iframe element can be transformed."> + <link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html"> + <style> + iframe { + height: 50px; + width: 50px; + transform: translate(25px, 25px) scale(2, 2); + border: none; + } + </style> + <p>Test passes if there is a filled green square.</p> + <iframe src="support/transform-iframe-002-contents.html"></iframe> + </body> +</html> diff --git a/testing/web-platform/tests/css/css-transforms/transform-iframe-scroll-position-ref.html b/testing/web-platform/tests/css/css-transforms/transform-iframe-scroll-position-ref.html new file mode 100644 index 0000000000..e4d5da75d7 --- /dev/null +++ b/testing/web-platform/tests/css/css-transforms/transform-iframe-scroll-position-ref.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test (Transforms): iframe scroll position</title> + <link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com"> + <style> + #iframe { + border: 0; + width: 50px; + height: 50px; + border: solid; + } + + #iframe div { + width: 25px; + height: 50px; + float: left; + } + + .rotate { + transform: rotate(90deg); + } + </style> + <body onload="onLoad();"> + <div id="iframe"> + <div style="background: blue;"></div> + <div style="background: green;"></div> + </div> + </body> +</html> diff --git a/testing/web-platform/tests/css/css-transforms/transform-iframe-scroll-position.html b/testing/web-platform/tests/css/css-transforms/transform-iframe-scroll-position.html new file mode 100644 index 0000000000..efb7bab532 --- /dev/null +++ b/testing/web-platform/tests/css/css-transforms/transform-iframe-scroll-position.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html> + <head> + <title>CSS Test (Transforms): iframe scroll position</title> + <link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com"> + <link rel="help" href="http://www.w3.org/TR/css-transforms-1/#transform-rendering"> + <meta name="assert" content="This test ensures that when an iframe element is transformed, the scroll position of the iframe content is preserved."> + <link rel="match" href="transform-iframe-scroll-position-ref.html"> + <style> + iframe { + border: 0; + width: 50px; + height: 50px; + border: solid; + } + + .rotate { + transform: rotate(90deg); + } + </style> + <body onload="onLoad();"> + <iframe id="iframe" src="support/transform-iframe-scroll-position-contents.html"></iframe> + <script> + function onLoad() { + iframe.classList.toggle("rotate"); + } + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/css-transitions/crashtests/delete-image-set.html b/testing/web-platform/tests/css/css-transitions/crashtests/delete-image-set.html new file mode 100644 index 0000000000..b6ba763858 --- /dev/null +++ b/testing/web-platform/tests/css/css-transitions/crashtests/delete-image-set.html @@ -0,0 +1,17 @@ +<!DOCTYPE html> +<link rel="help" href="https://crbug.com/363567"> +<link rel="help" href="https://issues.chromium.org/issues/40360947"> +<style> + div { background: url(#); } + div { background: -webkit-image-set(url(#) 1x); } + div { background: image-set(url(#) 1x); } +</style> +<body> + <div style="transition: 1s">This test passes if it does not crash.</div> +</body> +<script> +window.onload = () => { + document.styleSheets[0].deleteRule(2); + document.styleSheets[0].deleteRule(1); +}; +</script> diff --git a/testing/web-platform/tests/css/css-transitions/parsing/starting-style-parsing.html b/testing/web-platform/tests/css/css-transitions/parsing/starting-style-parsing.html new file mode 100644 index 0000000000..bd147a630a --- /dev/null +++ b/testing/web-platform/tests/css/css-transitions/parsing/starting-style-parsing.html @@ -0,0 +1,38 @@ +<!doctype html> +<title>@starting-style: parsing</title> +<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#at-ruledef-starting-style"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<main id=main></main> +<script> + function test_valid(actual, expected) { + if (expected === undefined) + expected = actual; + test(t => { + t.add_cleanup(() => main.replaceChildren()); + let style = document.createElement('style'); + style.textContent = `${actual}{}`; + main.append(style); + assert_equals(style.sheet.rules.length, 1); + let rule = style.sheet.rules[0]; + assert_equals(rule.cssText, `${expected} {\n}`); + }, `${actual} is valid`); + } + + function test_invalid(actual) { + test(t => { + t.add_cleanup(() => main.replaceChildren()); + let style = document.createElement('style'); + style.textContent = `${actual}{}`; + main.append(style); + assert_equals(style.sheet.rules.length, 0); + }, `${actual} is not valid`); + } + + test_valid('@starting-style'); + + test_invalid('@starting-style div'); + test_invalid('@starting-style ()'); + test_invalid('@starting-style ( {}'); + test_invalid('@starting-style }'); +</script> diff --git a/testing/web-platform/tests/css/css-transitions/starting-style-adjustment.html b/testing/web-platform/tests/css/css-transitions/starting-style-adjustment.html new file mode 100644 index 0000000000..addc795e72 --- /dev/null +++ b/testing/web-platform/tests/css/css-transitions/starting-style-adjustment.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<title>CSS Transitions Test: Style adjustments for @starting-style</title> +<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#defining-before-change-style-the-starting-style-rule"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/css-transitions/support/helper.js"></script> +<style> +legend { + transition: display 1s step-end allow-discrete; +} +@starting-style { + legend { display:inline; } +} +</style> +<body> +<legend></legend> +<script> +promise_test(async t => { + await waitForAnimationFrames(1); + assert_equals(document.getAnimations().length, 0, "No transitions"); +}, "The display property in <legend> @starting-style should be blockified so no transition should start"); +</script> +</body> diff --git a/testing/web-platform/tests/css/css-ui/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-ui/WEB_FEATURES.yml new file mode 100644 index 0000000000..0033402111 --- /dev/null +++ b/testing/web-platform/tests/css/css-ui/WEB_FEATURES.yml @@ -0,0 +1,7 @@ +features: +- name: accent-color + files: + - accent-color-* +- name: appearance + files: + - appearance-* diff --git a/testing/web-platform/tests/css/css-ui/animation/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-ui/animation/WEB_FEATURES.yml new file mode 100644 index 0000000000..07cdf85b20 --- /dev/null +++ b/testing/web-platform/tests/css/css-ui/animation/WEB_FEATURES.yml @@ -0,0 +1,4 @@ +features: +- name: accent-color + files: + - accent-color-* diff --git a/testing/web-platform/tests/css/css-ui/resize-014.html b/testing/web-platform/tests/css/css-ui/resize-014.html index fdbd77e38a..c3e292438b 100644 --- a/testing/web-platform/tests/css/css-ui/resize-014.html +++ b/testing/web-platform/tests/css/css-ui/resize-014.html @@ -18,8 +18,5 @@ video { resize: both; } <source src="support/test.mp4" type="video/mp4"> - <source - src="support/test.ogv" - type="video/ogg"> <video> is not supported. This test is non conclusive. </video> diff --git a/testing/web-platform/tests/css/css-ui/support/test.ogv b/testing/web-platform/tests/css/css-ui/support/test.ogv Binary files differdeleted file mode 100644 index 50d59dfb38..0000000000 --- a/testing/web-platform/tests/css/css-ui/support/test.ogv +++ /dev/null diff --git a/testing/web-platform/tests/css/css-values/WEB_FEATURES.yml b/testing/web-platform/tests/css/css-values/WEB_FEATURES.yml new file mode 100644 index 0000000000..ca3c0ff91b --- /dev/null +++ b/testing/web-platform/tests/css/css-values/WEB_FEATURES.yml @@ -0,0 +1,17 @@ +features: +- name: abs-sign + files: + - signs-abs-* +- name: cap + files: + - cap-* +- name: ic + files: + - ic-* +- name: lh + files: + - lh-* +- name: round-mod-rem + files: + - round-function.html + - round-mod-rem-* diff --git a/testing/web-platform/tests/css/css-values/calc-rounding-003-ref.html b/testing/web-platform/tests/css/css-values/calc-rounding-003-ref.html new file mode 100644 index 0000000000..e80dd7b95c --- /dev/null +++ b/testing/web-platform/tests/css/css-values/calc-rounding-003-ref.html @@ -0,0 +1,10 @@ +<!doctype html> +<meta charset="utf-8"> +<style> + .outer { + width: 100px; + height: 40px; + border: 1px solid; + } +</style> +<div class="outer"></div> diff --git a/testing/web-platform/tests/css/css-values/calc-rounding-003.html b/testing/web-platform/tests/css/css-values/calc-rounding-003.html new file mode 100644 index 0000000000..bf609e1c1d --- /dev/null +++ b/testing/web-platform/tests/css/css-values/calc-rounding-003.html @@ -0,0 +1,25 @@ +<!doctype html> +<meta charset="utf-8"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1893127"> +<link rel="help" href="https://drafts.csswg.org/css-values/#funcdef-calc"> +<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> +<link rel="author" href="https://mozilla.org" title="Mozilla"> +<link rel="match" href="calc-rounding-003-ref.html"> +<style> + .outer { + width: 100px; + border: 1px solid; + } + .inner { + height: 40px; + vertical-align: top; + display: inline-block; + --margin: 4.009px; + width: calc(50% - 2 * var(--margin)); + margin-inline: var(--margin); + } +</style> +<div class="outer" + ><div class="inner"></div + ><div class="inner"></div +></div> diff --git a/testing/web-platform/tests/css/css-values/calc-size/animation/calc-size-height-interpolation.tentative.html b/testing/web-platform/tests/css/css-values/calc-size/animation/calc-size-height-interpolation.tentative.html index 04c44d0904..06277376e9 100644 --- a/testing/web-platform/tests/css/css-values/calc-size/animation/calc-size-height-interpolation.tentative.html +++ b/testing/web-platform/tests/css/css-values/calc-size/animation/calc-size-height-interpolation.tentative.html @@ -2,12 +2,17 @@ <meta charset="UTF-8"> <title>height: calc-size() animations</title> <link rel="help" href="https://drafts.csswg.org/css-values-5/#calc-size"> - +<meta name="timeout" content="long"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="../../../support/interpolation-testcommon.js"></script> <style> +.parent { + display: block; + width: 600px; + height: 300px; +} .target { display: block; } @@ -92,4 +97,96 @@ { at: 1.25, expect: '125px' }, ]); + const KEYWORDS = { + "auto": 100, + "min-content": 100, + "fit-content": 100, + "max-content": 100, + "stretch": 300, + }; + + for (const keyword in KEYWORDS) { + let expected = KEYWORDS[keyword]; + test_interpolation({ + property: 'height', + from: keyword, + to: `calc-size(${keyword}, size * 2)`, + }, [ + { at: -0.25, expect: `${expected * 0.75}px` }, + { at: 0, expect: `${expected}px` }, + { at: 0.75, expect: `${expected * 1.75}px` }, + { at: 1, expect: `${expected * 2}px` }, + { at: 1.25, expect: `${expected * 2.25}px` }, + ]); + + test_interpolation({ + property: 'height', + from: keyword, + to: 'calc-size(any, 50px)', + }, [ + { at: -0.25, expect: `${expected * 1.25 - 50 * 0.25}px` }, + { at: 0, expect: `${expected}px` }, + { at: 0.75, expect: `${expected * 0.25 + 50 * 0.75}px` }, + { at: 1, expect: `50px` }, + { at: 1.25, expect: `${50 * 1.25 - expected * 0.25}px` }, + ]); + + test_interpolation({ + property: 'height', + from: 'calc-size(any, 50px)', + to: `calc-size(${keyword}, size * 2)`, + }, [ + { at: -0.1, expect: `${50 * 1.1 - expected * 0.2}px` }, + { at: 0, expect: "50px" }, + { at: 0.75, expect: `${50 * 0.25 + expected * 1.5}px` }, + { at: 1, expect: `${expected * 2}px` }, + { at: 1.25, expect: `${expected * 2.5 - 50 * 0.25}px` }, + ]); + + test_no_interpolation({ + property: 'height', + from: keyword, + to: 'calc-size(50px, size)', + }); + } + + const KEYWORD_PAIRS = [ + [ "auto", "fit-content" ], + [ "fit-content", "min-content" ], + [ "stretch", "auto" ], + [ "max-content", "stretch" ], + ]; + + for (const pair of KEYWORD_PAIRS) { + test_no_interpolation({ + property: 'height', + from: pair[0], + to: `calc-size(${pair[1]}, size)`, + }); + } + + test_no_interpolation({ + property: 'height', + from: 'calc-size(20px, size)', + to: 'calc-size(50px, size)', + }); + + test_no_interpolation({ + property: 'height', + from: 'calc-size(50%, size)', + to: 'calc-size(50px, size)', + }); + + test_interpolation({ + property: 'height', + from: 'calc-size(37px, 200px)', + to: `calc-size(37px, size * 2 + 3% + 17px)`, /* adds to 100px */ + }, [ + { at: -0.25, expect: '225px' }, + { at: 0, expect: '200px' }, + { at: 0.75, expect: '125px' }, + { at: 1, expect: '100px' }, + { at: 1.25, expect: '75px' }, + ]); + </script> diff --git a/testing/web-platform/tests/css/css-values/calc-size/animation/calc-size-width-interpolation.tentative.html b/testing/web-platform/tests/css/css-values/calc-size/animation/calc-size-width-interpolation.tentative.html index b8b24935c3..88373306c4 100644 --- a/testing/web-platform/tests/css/css-values/calc-size/animation/calc-size-width-interpolation.tentative.html +++ b/testing/web-platform/tests/css/css-values/calc-size/animation/calc-size-width-interpolation.tentative.html @@ -2,7 +2,7 @@ <meta charset="UTF-8"> <title>width: calc-size() animations</title> <link rel="help" href="https://drafts.csswg.org/css-values-5/#calc-size"> - +<meta name="timeout" content="long"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="../../../support/interpolation-testcommon.js"></script> @@ -67,4 +67,118 @@ { at: 1.25, expect: '125px' }, ]); + const KEYWORDS = { + "auto": 200, + "min-content": 100, + "fit-content": 100, + "max-content": 100, + "stretch": 200, + }; + + for (const keyword in KEYWORDS) { + let expected = KEYWORDS[keyword]; + test_interpolation({ + property: 'width', + from: keyword, + to: `calc-size(${keyword}, size * 2)`, + }, [ + { at: -0.25, expect: `${expected * 0.75}px` }, + { at: 0, expect: `${expected}px` }, + { at: 0.75, expect: `${expected * 1.75}px` }, + { at: 1, expect: `${expected * 2}px` }, + { at: 1.25, expect: `${expected * 2.25}px` }, + ]); + + test_interpolation({ + property: 'width', + from: keyword, + to: 'calc-size(any, 50px)', + }, [ + { at: -0.25, expect: `${expected * 1.25 - 50 * 0.25}px` }, + { at: 0, expect: `${expected}px` }, + { at: 0.75, expect: `${expected * 0.25 + 50 * 0.75}px` }, + { at: 1, expect: `50px` }, + { at: 1.25, expect: `${50 * 1.25 - expected * 0.25}px` }, + ]); + + test_interpolation({ + property: 'width', + from: 'calc-size(any, 50px)', + to: `calc-size(${keyword}, size * 2)`, + }, [ + { at: -0.1, expect: `${50 * 1.1 - expected * 0.2}px` }, + { at: 0, expect: "50px" }, + { at: 0.75, expect: `${50 * 0.25 + expected * 1.5}px` }, + { at: 1, expect: `${expected * 2}px` }, + { at: 1.25, expect: `${expected * 2.5 - 50 * 0.25}px` }, + ]); + + test_no_interpolation({ + property: 'width', + from: keyword, + to: 'calc-size(50px, size)', + }); + } + + const KEYWORD_PAIRS = [ + [ "auto", "fit-content" ], + [ "fit-content", "min-content" ], + [ "stretch", "auto" ], + [ "max-content", "stretch" ], + ]; + + for (const pair of KEYWORD_PAIRS) { + test_no_interpolation({ + property: 'width', + from: pair[0], + to: `calc-size(${pair[1]}, size)`, + }); + } + + test_no_interpolation({ + property: 'width', + from: 'calc-size(20px, size)', + to: 'calc-size(50px, size)', + }); + + test_no_interpolation({ + property: 'width', + from: 'calc-size(50%, size)', + to: 'calc-size(50px, size)', + }); + + test_interpolation({ + property: 'width', + from: 'calc-size(37px, 200px)', + to: `calc-size(37px, size * 2 + 7% + 12px)`, /* adds to 100px */ + }, [ + { at: -0.25, expect: '225px' }, + { at: 0, expect: '200px' }, + { at: 0.75, expect: '125px' }, + { at: 1, expect: '100px' }, + { at: 1.25, expect: '75px' }, + ]); + + let parent_auto_style = document.createElement("style"); + parent_auto_style.innerText = ` + body { width: 300px; } + .parent { width: auto; } + `; + document.head.append(parent_auto_style); + + test_interpolation({ + property: 'width', + from: 'inherit', + to: `calc-size(auto, size * 0.5)`, + }, [ + { at: -0.25, expect: '337.5px' }, + { at: 0, expect: '300px' }, + { at: 0.75, expect: '187.5px' }, + { at: 1, expect: '150px' }, + { at: 1.25, expect: '112.5px' }, + ]); + + parent_auto_style.remove(); + + </script> diff --git a/testing/web-platform/tests/css/css-values/calc-size/calc-size-height.tentative.html b/testing/web-platform/tests/css/css-values/calc-size/calc-size-height.tentative.html index 61e59f83db..c3d58931b8 100644 --- a/testing/web-platform/tests/css/css-values/calc-size/calc-size-height.tentative.html +++ b/testing/web-platform/tests/css/css-values/calc-size/calc-size-height.tentative.html @@ -37,7 +37,6 @@ let basic_tests = [ { value: "calc-size(calc-size(2in, 30px), 15em)", expected: "300px" }, { value: "calc-size(calc-size(min-content, 30px), 15em)", expected: "300px" }, { value: "calc-size(calc-size(min-content, size), size)", expected: "10px" }, - { value: "calc(12% + calc-size(any, 31%))", expected_auto: "10px", expected_definite: "43px" }, { value: "calc-size(any, 31% + 12px)", expected_auto: "12px", expected_definite: "43px" }, { value: "calc-size(auto, size * 1.5)", expected: "15px" }, ]; diff --git a/testing/web-platform/tests/css/css-values/calc-size/calc-size-parsing.tentative.html b/testing/web-platform/tests/css/css-values/calc-size/calc-size-parsing.tentative.html index e51247c872..afcb200424 100644 --- a/testing/web-platform/tests/css/css-values/calc-size/calc-size-parsing.tentative.html +++ b/testing/web-platform/tests/css/css-values/calc-size/calc-size-parsing.tentative.html @@ -34,8 +34,33 @@ test_invalid_value("width", "calc-size(any, size)"); test_invalid_value("width", "calc-size(any, fit-content)"); test_invalid_value("width", "calc-size(any, max-content)"); test_invalid_value("width", "calc-size(any, min-content)"); -test_valid_value("width", "calc-size(any, calc-size(10px, sign(size) * size))"); -test_invalid_value("width", "calc-size(any, calc-size(10px, sign(size) * size) * sign(size))"); +test_valid_value("width", "calc-size(10px, sign(size) * size)"); +test_invalid_value("width", "size"); +test_invalid_value("width", "sign(size)"); +test_invalid_value("width", "calc(50px * sign(size))"); +test_invalid_value("width", "calc-size(any, calc-size(10px, sign(size) * size))"); +test_invalid_value("width", "calc-size(any, calc-size(any, 10px))"); +test_invalid_value("width", "calc(calc-size(auto, size))"); +test_invalid_value("width", "calc(50px * sign(calc-size(auto, size)))"); +test_invalid_value("width", "calc(calc-size(auto, size) + calc-size(auto, size))"); +test_invalid_value("width", "calc(abs(calc-size(auto, size)))"); +test_invalid_value("width", "calc(100px * progress(calc-size(auto, size) from calc-size(auto, 0px) to calc-size(auto, size)))"); +test_invalid_value("width", "calc(100px * progress(calc-size(auto, size) from 0px to 100px))"); +test_invalid_value("width", "calc(100px * progress(50px from calc-size(auto, 0px) to calc-size(auto, size)))"); +test_invalid_value("width", "min(calc-size(auto, 0px), calc-size(auto, size))"); +test_invalid_value("width", "calc(min(calc-size(auto, 0px), calc-size(auto, size)))"); +test_invalid_value("width", "max(calc-size(auto, 0px), calc-size(auto, size))"); +test_invalid_value("width", "calc(max(calc-size(auto, 0px), calc-size(auto, size)))"); +test_invalid_value("width", "clamp(calc-size(auto, 0px), calc-size(auto, 30px), calc-size(auto, size))"); +test_invalid_value("width", "calc(clamp(calc-size(auto, 0px), calc-size(auto, 30px), calc-size(auto, size)))"); +test_invalid_value("width", "calc(cos(calc-size(auto, 0px)))"); +test_invalid_value("width", "calc(atan2(calc-size(auto, size), calc-size(auto, 50px)))"); +test_invalid_value("width", "calc(sqrt(calc-size(auto, 0px)))"); +test_invalid_value("width", "calc(hypot(calc-size(auto, size * 0.5), calc-size(auto, size)))"); +test_invalid_value("width", "calc(round(calc-size(auto, size * 0.5), calc-size(auto, size)))"); +test_invalid_value("width", "calc(round(calc-size(auto, size * 0.5)))"); +test_invalid_value("width", "calc(mod(calc-size(auto, size * 0.5), calc-size(auto, size)))"); +test_invalid_value("width", "calc(rem(calc-size(auto, size * 0.5), calc-size(auto, size)))"); test_valid_value("width", "calc-size(30px, 25em)"); test_valid_value("width", "calc-size(calc-size(any, 30px), 25em)"); @@ -43,4 +68,6 @@ test_valid_value("width", "calc-size(calc-size(2in, 30px), 25em)", "calc-size(ca test_valid_value("width", "calc-size(calc-size(min-content, 30px), 25em)"); test_valid_value("width", "calc-size(calc-size(min-content, size), size)"); +test_invalid_value("height", "calc(12% + calc-size(any, 31%))"); + </script> diff --git a/testing/web-platform/tests/css/css-values/container-progress-computed.tentative.html b/testing/web-platform/tests/css/css-values/container-progress-computed.tentative.html new file mode 100644 index 0000000000..9ab537cad6 --- /dev/null +++ b/testing/web-platform/tests/css/css-values/container-progress-computed.tentative.html @@ -0,0 +1,80 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#container-progress-func"> +<link rel="author" title="sakhapov@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../support/numeric-testcommon.js"></script> +<div id="out-of-scope-container"></div> +<div id="extra-container"> + <div id="outer-container"> + <div id="inner-container"> + <div id=target></div> + </div> + </div> +</div> +<style> +:root { + font-size: 10px; +} +#out-of-scope-container { + container: my-container-3 / size; + width: 1px; + height: 1px; +} +#extra-container { + container: my-container-2 / size; + width: 5051px; + height: 1337px; +} +#outer-container { + container: my-container / size; + width: 322px; + height: 228px; +} +#inner-container { + container-type: size; + width: 228px; + height: 322px; +} +#target { + font-size: 10px; +} +</style> +<script> + +let width = window.innerWidth; +let height = window.innerHeight; + +let extraWidth = 5051; +let extraHeight = 1337; +let innerWidth = 228; +let innerHeight = 322; +let outerWidth = 322; +let outerHeight = 228; + +// Identity tests +test_math_used('container-progress(height from 0px to 1px)', innerHeight, {type:'number'}); +test_math_used('container-progress(width of my-container from 0px to 1px)', outerWidth, {type:'number'}); + +// Nestings +test_math_used('container-progress(height from container-progress(height from 0px to 1px) * 1px to container-progress(height from 0px to 1px) * 1px)', '0', {type:'number'}); +test_math_used('container-progress(height from container-progress(height from 0px to 1px) * 0.5px to container-progress(height from 0px to 1px) * 1px)', '1', {type:'number'}); +test_math_used('container-progress(height from container-progress(width of my-container from 0px to 1px) * 1px to container-progress(height of my-container-2 from 0px to 1px) * 1px)', (innerHeight - outerWidth) / (extraHeight - outerWidth), {type:'number'}); + +// General calculations +test_math_used('calc(container-progress(width from 0px to 50px) * 10px + 100px)', (innerWidth / 50 * 10 + 100) + 'px'); +test_math_used('calc(container-progress(height from 10px to sign(50px - 500em) * 10px))', (innerHeight - 10) / (-10 - 10), {type:'number'}); +test_math_used('calc(container-progress(width of my-container from 0px to 50px) * 10px + 100px)', (outerWidth / 50 * 10 + 100) + 'px'); +test_math_used('calc(container-progress(height of my-container from 10px to sign(50px - 500em) * 10px))', (outerHeight - 10) / (-10 - 10), {type:'number'}); + +// Fallback +test_math_used('container-progress(width of non-existing-container from 0px to 1px)', width, {type:'number'}); +test_math_used('container-progress(height of non-existing-container from 0px to 1px)', height, {type:'number'}); +test_math_used('container-progress(width of out-of-scope-container from 0px to 1px)', width, {type:'number'}); +test_math_used('container-progress(height of out-of-scope-container from 0px to 1px)', height, {type:'number'}); + +// Type checking +test_math_used('calc(container-progress(width from 0px to 1px) * 1px)', innerWidth + 'px'); +test_math_used('calc(container-progress(height of my-container from 0px to 1px) * 1s)', outerHeight + 's', {type:'time'}); +test_math_used('calc(container-progress(width of my-container-2 from 0px to 1px) * 1deg)', extraWidth + 'deg', {type:'angle', approx:0.001}); +</script> diff --git a/testing/web-platform/tests/css/css-values/container-progress-invalid.tentative.html b/testing/web-platform/tests/css/css-values/container-progress-invalid.tentative.html new file mode 100644 index 0000000000..a78fd34426 --- /dev/null +++ b/testing/web-platform/tests/css/css-values/container-progress-invalid.tentative.html @@ -0,0 +1,55 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#container-progress-func"> +<link rel="author" title="sakhapov@chromuim.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../support/parsing-testcommon.js"></script> +<script> +function test_invalid_number(value) { + test_invalid_value('opacity', value); +} +function test_invalid_length(value) { + // 'letter-spacing' accepts <length> only, not <percentage> or any mixes. + test_invalid_value('letter-spacing', value); +} + +// Syntax checking +test_invalid_number('container-progress()'); +test_invalid_number('container-progress( )'); +test_invalid_number('container-progress(,)'); +test_invalid_number('container-progress(1 from )'); +test_invalid_number('container-progress(1)'); +test_invalid_number('container-progress(50% to 0)'); +test_invalid_number('container-progress(0 from 1 to)'); +test_invalid_number('container-progress(from to)'); +test_invalid_number('container-progress(from 1 to 0)'); +test_invalid_number('container-progress(3 of 2 from 1 to 0)'); +test_invalid_number('container-progress(width of 2 from 1 to 0)'); +test_invalid_number('container-progress(from 1 to 0 1)'); +test_invalid_number('container-progress(from 1 0)'); +test_invalid_number('container-progress(0 from to 0)'); +test_invalid_number('container-progress(to to to to to)'); +test_invalid_number('container-progress(0, from, 10, to 200)'); +test_invalid_number('container-progress(0, from, 10, to, 200)'); +test_invalid_number('container-progress(0, from 10, to 200)'); +test_invalid_number('container-progress(0, 10, 200)'); + +// General tests +test_invalid_number('container-progress(height from 0 to 8'); +test_invalid_number('container-progress(height container from 0 to 8'); +test_invalid_number('container-progress(height of from 0 to 8'); +test_invalid_number('container-progress(depth from 0px to 8px'); +test_invalid_number('container-progress(width of 10 from 0px to 8px'); +test_invalid_number('container-progress(height of 10 from 0px to 8px'); +test_invalid_number('container-progress(height of name from 0deg to 8deg'); +test_invalid_number('container-progress(height of name from 0 to 8px'); +test_invalid_number('container-progress(10px from 0px to 8px'); +test_invalid_number('container-progress(depth of name from 0px to 8px'); +test_invalid_number('container-progress(width from 0deg to 8deg'); +test_invalid_number('container-progress(5 from 0deg to 8deg'); +test_invalid_number('container-progress(5 from 0% to 8deg'); +test_invalid_number('container-progress(height from 0% to sign(10px)'); +test_invalid_number('container-progress(5% from 0px to 10px'); +test_invalid_length('calc(1px * container-progress(10deg from 0 to 10))'); +test_invalid_length('calc(1px * container-progress(10 from 0px to 10))'); +</script> diff --git a/testing/web-platform/tests/css/css-values/container-progress-serialize.tentative.html b/testing/web-platform/tests/css/css-values/container-progress-serialize.tentative.html new file mode 100644 index 0000000000..181054c653 --- /dev/null +++ b/testing/web-platform/tests/css/css-values/container-progress-serialize.tentative.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#container-progress-func"> +<link rel="author" title="sakhapov@chromuim.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../support/serialize-testcommon.js"></script> +<div id="outer-container"> + <div id="inner-container"> + <div id=target></div> + </div> +</div> +<style> +:root { + font-size: 10px; +} +#outer-container { + container: my-container / size; + width: 322px; + height: 228px; +} +#inner-container { + container-type: size; + width: 228px; + height: 322px; +} +#target { + font-size: 10px; +} +</style> +<script> +function test_serialization(t,s,c) { + test_specified_serialization('opacity', t, s); + test_specified_serialization('transform', `scale(${t})`, `scale(${s})`); + test_computed_serialization('opacity', t, c); + test_computed_serialization('transform', `scale(${t})`, `matrix(${c}, 0, 0, ${c}, 0, 0)`); +} + +test_serialization( + 'calc(container-progress(width from 0px to 1px) / 1000)', + 'calc(container-progress(width from 0px to 1px) / 1000)', + '0.228', +); +test_serialization( + 'calc(0.1 * container-progress(height of my-container from 0px to 10em))', + 'calc(0.1 * container-progress(height of my-container from 0px to 10em))', + '0.228', +); +</script> diff --git a/testing/web-platform/tests/css/css-values/media-progress-computed.tentative.html b/testing/web-platform/tests/css/css-values/media-progress-computed.tentative.html new file mode 100644 index 0000000000..b2b9c6662a --- /dev/null +++ b/testing/web-platform/tests/css/css-values/media-progress-computed.tentative.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<meta name="viewport" content="width=device-width, initial-scale=1"> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#media-progress-func"> +<link rel="author" title="sakhapov@chromium.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../support/numeric-testcommon.js"></script> +<div id="target"></div> +<style> +#target { + font-size: 10px; +} +:root { + width: 100vw; + height: 100vh; +} +</style> +<script> +// innerWidth and innerHeight have lossy precision, see +// https://github.com/w3c/csswg-drafts/issues/5260. +let { width, height } = document.documentElement.getBoundingClientRect(); + +// Identity tests +// NOTE(emilio): We provide custom messages so that the test name doesn't +// depend on the viewport size (since in testharness.js files the viewport size is +// not guaranteed to be fixed, unlike in reftests). +test_math_used('media-progress(height from 0px to 1px)', height, { type:'number', 'msg': 'media-progress() identity check' }); + +// Nestings +test_math_used('media-progress(height from media-progress(height from 0px to 1px) * 1px to media-progress(height from 0px to 1px) * 1px)', '0', {type:'number'}); + +test_math_used('media-progress(height from media-progress(height from 0px to 1px) * 0.5px to media-progress(height from 0px to 1px) * 1px)', '1', {type:'number'}); + +// General calculations. +test_math_used('calc(media-progress(width from 0px to 50px) * 10px + 100px)', (width / 50 * 10 + 100) + 'px', { msg: 'media-progress() with length product' }); +test_math_used('calc(media-progress(height from 10px to sign(50px - 500em) * 10px))', (height - 10) / (-10 - 10), { type:'number', msg: 'media-progress with complex to calculation' }); + +// Type checking +test_math_used('calc(media-progress(width from 0px to 1px) * 1px)', width + 'px', { msg: 'media-progress() as length' }); +test_math_used('calc(media-progress(height from 0px to 1px) * 1s)', height + 's', { type:'time', msg: 'media-progress() as time' }); +test_math_used('calc(media-progress(width from 0px to 1px) * 1deg)', width + 'deg', { type:'angle', approx:0.001, msg: 'media-progress() as angle' }); +</script> diff --git a/testing/web-platform/tests/css/css-values/media-progress-invalid.tentative.html b/testing/web-platform/tests/css/css-values/media-progress-invalid.tentative.html new file mode 100644 index 0000000000..10d40bcbfb --- /dev/null +++ b/testing/web-platform/tests/css/css-values/media-progress-invalid.tentative.html @@ -0,0 +1,45 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#media-progress"> +<link rel="author" title="sakhapov@chromuim.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../support/parsing-testcommon.js"></script> +<script> +function test_invalid_number(value) { + test_invalid_value('opacity', value); +} +function test_invalid_length(value) { + // 'letter-spacing' accepts <length> only, not <percentage> or any mixes. + test_invalid_value('letter-spacing', value); +} + +// Syntax checking +test_invalid_number('media-progress()'); +test_invalid_number('media-progress( )'); +test_invalid_number('media-progress(,)'); +test_invalid_number('media-progress(1 from )'); +test_invalid_number('media-progress(1)'); +test_invalid_number('media-progress(50% to 0)'); +test_invalid_number('media-progress(0 from 1 to)'); +test_invalid_number('media-progress(from to)'); +test_invalid_number('media-progress(from 1 to 0)'); +test_invalid_number('media-progress(from 1 to 0 1)'); +test_invalid_number('media-progress(from 1 0)'); +test_invalid_number('media-progress(0 from to 0)'); +test_invalid_number('media-progress(to to to to to)'); +test_invalid_number('media-progress(0, from, 10, to 200)'); +test_invalid_number('media-progress(0, from, 10, to, 200)'); +test_invalid_number('media-progress(0, from 10, to 200)'); +test_invalid_number('media-progress(0, 10, 200)'); + +// General tests +test_invalid_number('media-progress(height from 0 to 8'); +test_invalid_number('media-progress(depth from 0px to 8px'); +test_invalid_number('media-progress(width from 0deg to 8deg'); +test_invalid_number('media-progress(5 from 0deg to 8deg'); +test_invalid_number('media-progress(5 from 0% to 8deg'); +test_invalid_number('media-progress(height from 0% to sign(10px)'); +test_invalid_number('media-progress(5% from 0px to 10px'); +test_invalid_length('calc(1px * media-progress(10deg from 0 to 10))'); +test_invalid_length('calc(1px * media-progress(10 from 0px to 10))'); +</script> diff --git a/testing/web-platform/tests/css/css-values/media-progress-serialize.tentative.html b/testing/web-platform/tests/css/css-values/media-progress-serialize.tentative.html new file mode 100644 index 0000000000..354dda25b7 --- /dev/null +++ b/testing/web-platform/tests/css/css-values/media-progress-serialize.tentative.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<link rel="help" href="https://drafts.csswg.org/css-values-5/#progress"> +<link rel="author" title="sakhapov@chromuim.org"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../support/serialize-testcommon.js"></script> +<div id=target></div> +<style> +:root { + font-size: 10px; +} +#target { + font-size: 10px; +} +</style> +<script> +function test_serialization(t,s,c) { + test_specified_serialization('opacity', t, s); + test_specified_serialization('transform', `scale(${t})`, `scale(${s})`); + test_computed_serialization('opacity', t, c); + test_computed_serialization('transform', `scale(${t})`, `matrix(${c}, 0, 0, ${c}, 0, 0)`); +} + +test_serialization( + 'media-progress(width from 0px to 1000px)', + 'media-progress(width from 0px to 1000px)', + '0.8', +); +test_serialization( + 'calc(0.1 * media-progress(height from 0px to 10em))', + 'calc(0.1 * media-progress(height from 0px to 10em))', + '0.6', +); +test_serialization( + 'calc(media-progress(width from 0px to 100px) / 20)', + 'calc(media-progress(width from 0px to 100px) / 20)', + '0.4' +); +test_computed_serialization( + 'width', + 'calc(1px * media-progress(height from abs(11em - 10rem) to 110px / 10))', + '590px', +); +</script> diff --git a/testing/web-platform/tests/css/css-values/progress-serialize.tentative.html b/testing/web-platform/tests/css/css-values/progress-serialize.tentative.html index 6ea12f2b75..d2090cb7f8 100644 --- a/testing/web-platform/tests/css/css-values/progress-serialize.tentative.html +++ b/testing/web-platform/tests/css/css-values/progress-serialize.tentative.html @@ -20,12 +20,12 @@ test_serialization( ); test_serialization( 'progress(10em from 0px to 10em)', - 'calc(progress(10em from 0px to 10em))', + 'progress(10em from 0px to 10em)', '1', ); test_serialization( 'progress(10em from 0px to 10rem)', - 'calc(progress(10em from 0px to 10rem))', + 'progress(10em from 0px to 10rem)', '1', ); test_serialization( diff --git a/testing/web-platform/tests/css/css-view-transitions/3d-transform-incoming-ref.html b/testing/web-platform/tests/css/css-view-transitions/3d-transform-incoming-ref.html index c690ee2b9a..18c47557c9 100644 --- a/testing/web-platform/tests/css/css-view-transitions/3d-transform-incoming-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/3d-transform-incoming-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: 3d transform ref</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> div { box-sizing: border-box; will-change: transform } diff --git a/testing/web-platform/tests/css/css-view-transitions/3d-transform-incoming.html b/testing/web-platform/tests/css/css-view-transitions/3d-transform-incoming.html index 24ab886025..983ba1d861 100644 --- a/testing/web-platform/tests/css/css-view-transitions/3d-transform-incoming.html +++ b/testing/web-platform/tests/css/css-view-transitions/3d-transform-incoming.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: 3d transform</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="3d-transform-incoming-ref.html"> <meta name=fuzzy content="3d-transform-incoming-ref.html:0-255;0-515"> diff --git a/testing/web-platform/tests/css/css-view-transitions/3d-transform-outgoing-ref.html b/testing/web-platform/tests/css/css-view-transitions/3d-transform-outgoing-ref.html index 393943e396..0e09cba92b 100644 --- a/testing/web-platform/tests/css/css-view-transitions/3d-transform-outgoing-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/3d-transform-outgoing-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: 3d transform ref</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> div { box-sizing: border-box; will-change: transform } diff --git a/testing/web-platform/tests/css/css-view-transitions/3d-transform-outgoing.html b/testing/web-platform/tests/css/css-view-transitions/3d-transform-outgoing.html index 746b93afd4..398fa97ca9 100644 --- a/testing/web-platform/tests/css/css-view-transitions/3d-transform-outgoing.html +++ b/testing/web-platform/tests/css/css-view-transitions/3d-transform-outgoing.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: 3d transform</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="3d-transform-outgoing-ref.html"> <meta name=fuzzy content="3d-transform-outgoing-ref.html:0-255;0-1200"> diff --git a/testing/web-platform/tests/css/css-view-transitions/content-object-fit-fill-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-object-fit-fill-ref.html index b86e0a95a1..10ef0d5931 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-object-fit-fill-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-object-fit-fill-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: display content in a pseudo with object-fit: none (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> #container { diff --git a/testing/web-platform/tests/css/css-view-transitions/content-object-fit-none-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-object-fit-none-ref.html index 8bd63e9a88..10338231b0 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-object-fit-none-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-object-fit-none-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: display content in a pseudo with object-fit: none (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> #container { diff --git a/testing/web-platform/tests/css/css-view-transitions/content-smaller-than-box-size-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-smaller-than-box-size-ref.html index 381a311cc4..19cd63d142 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-smaller-than-box-size-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-smaller-than-box-size-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: element with content less than box size (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/content-smaller-than-box-size.html b/testing/web-platform/tests/css/css-view-transitions/content-smaller-than-box-size.html index d2b8f63ca0..86c6f9c7ee 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-smaller-than-box-size.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-smaller-than-box-size.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: element with content less than box size</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="content-smaller-than-box-size-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/content-visibility-auto-shared-element-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-visibility-auto-shared-element-ref.html index f4a34967ec..1fa42c5f38 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-visibility-auto-shared-element-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-visibility-auto-shared-element-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: offscreen content</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> body { background: pink } diff --git a/testing/web-platform/tests/css/css-view-transitions/content-visibility-auto-shared-element.html b/testing/web-platform/tests/css/css-view-transitions/content-visibility-auto-shared-element.html index 11b4957fbc..1c74897000 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-visibility-auto-shared-element.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-visibility-auto-shared-element.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: offscreen content with content-visibility auto</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="content-visibility-auto-shared-element-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-child-with-transparent-background-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-with-child-with-transparent-background-ref.html index 7b8c15b87f..250564b41c 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-child-with-transparent-background-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-child-with-transparent-background-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: element with child with transparent background (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> .shared { diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-child-with-transparent-background.html b/testing/web-platform/tests/css/css-view-transitions/content-with-child-with-transparent-background.html index 6c447aa1a8..81a53494d0 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-child-with-transparent-background.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-child-with-transparent-background.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: element with child with transparent background</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="content-with-child-with-transparent-background-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-clip-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-with-clip-ref.html index a322301686..81fbabe3c5 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-clip-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-clip-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: element with clip (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-clip-root-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-with-clip-root-ref.html index f81a96370e..deea590892 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-clip-root-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-clip-root-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: element with clip (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .container { diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-clip-root.html b/testing/web-platform/tests/css/css-view-transitions/content-with-clip-root.html index 5acd847734..e0aef0fc37 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-clip-root.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-clip-root.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: root element with clip</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="content-with-clip-root-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-clip.html b/testing/web-platform/tests/css/css-view-transitions/content-with-clip.html index 28bfa86875..9c9f2385c1 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-clip.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-clip.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: element with clip</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="content-with-clip-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-inline-child-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-with-inline-child-ref.html index 4bb87f316f..fada94f146 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-inline-child-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-inline-child-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: element with inline child (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="stylesheet" href="/fonts/ahem.css"> <link rel="author" href="mailto:bokan@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-inline-child.html b/testing/web-platform/tests/css/css-view-transitions/content-with-inline-child.html index f1a5ca79af..bc72443b39 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-inline-child.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-inline-child.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: element with inline child</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="stylesheet" href="/fonts/ahem.css"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="content-with-inline-child-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-object-view-box-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-with-object-view-box-ref.html index 7aa2014397..c1b316ea6b 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-object-view-box-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-object-view-box-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture elements with object view box on the pseudo (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> body { background: pink } diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-overflow-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-with-overflow-ref.html index a24e30ede9..f6e7c4e6f4 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-overflow-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-overflow-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: shared element with overflow (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-overflow-zoomed-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-with-overflow-zoomed-ref.html index a9d873957a..ae076d5578 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-overflow-zoomed-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-overflow-zoomed-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: shared element with overflow (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-transform-new-image.html b/testing/web-platform/tests/css/css-view-transitions/content-with-transform-new-image.html index 9baf9563d1..f86f64843c 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-transform-new-image.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-transform-new-image.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: object-view-box</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="content-with-transform-ref.html"> <meta name="fuzzy" content="content-with-transform-ref.html:0-1;0-500"> diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-transform-old-image.html b/testing/web-platform/tests/css/css-view-transitions/content-with-transform-old-image.html index e3bd7fff1d..3755910a1b 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-transform-old-image.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-transform-old-image.html @@ -2,7 +2,7 @@ <meta name="timeout" content="long"> <html class=reftest-wait> <title>View transitions: object-view-box</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="content-with-transform-ref.html"> <meta name="fuzzy" content="content-with-transform-ref.html:0-1;0-400"> diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-transform-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-with-transform-ref.html index 568e040c1e..8c2b65e63e 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-transform-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-transform-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: object-view-box (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-transparent-background-ref.html b/testing/web-platform/tests/css/css-view-transitions/content-with-transparent-background-ref.html index 7349d131d7..5ec3d5c64c 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-transparent-background-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-transparent-background-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: element with transparent background (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/content-with-transparent-background.html b/testing/web-platform/tests/css/css-view-transitions/content-with-transparent-background.html index b961a095e7..416cc4eca5 100644 --- a/testing/web-platform/tests/css/css-view-transitions/content-with-transparent-background.html +++ b/testing/web-platform/tests/css/css-view-transitions/content-with-transparent-background.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: element with transparent background</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="content-with-transparent-background-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order-ref.html b/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order-ref.html index 780d5fbd45..808195a557 100644 --- a/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: css tags generate pseudo elements in paint order (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order-with-entry-ref.html b/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order-with-entry-ref.html index e7f62c2346..2c91853187 100644 --- a/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order-with-entry-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order-with-entry-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: css tags generate pseudo elements in paint order (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order-with-entry.html b/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order-with-entry.html index 703b64f5c6..2ba73758e2 100644 --- a/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order-with-entry.html +++ b/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order-with-entry.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: css tags generate pseudo elements in paint order</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="css-tags-paint-order-with-entry-ref.html"> <meta name="fuzzy" content="css-tags-paint-order-with-entry-ref.html:0-120;0-300"> diff --git a/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order.html b/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order.html index c0e8769b47..9f1c6ad961 100644 --- a/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order.html +++ b/testing/web-platform/tests/css/css-view-transitions/css-tags-paint-order.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: css tags generate pseudo elements in paint order</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="css-tags-paint-order-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/css-tags-shared-element-ref.html b/testing/web-platform/tests/css/css-view-transitions/css-tags-shared-element-ref.html index 23fa74dd5e..c2c9a1dc50 100644 --- a/testing/web-platform/tests/css/css-view-transitions/css-tags-shared-element-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/css-tags-shared-element-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: use css tags for shared elements (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/css-tags-shared-element.html b/testing/web-platform/tests/css/css-view-transitions/css-tags-shared-element.html index 33bbae70fc..29ec7350d0 100644 --- a/testing/web-platform/tests/css/css-view-transitions/css-tags-shared-element.html +++ b/testing/web-platform/tests/css/css-view-transitions/css-tags-shared-element.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: use css tags for shared elements</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="css-tags-shared-element-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/dialog-in-rtl-iframe-ref.html b/testing/web-platform/tests/css/css-view-transitions/dialog-in-rtl-iframe-ref.html index 44ed0947ab..fc6b4892a5 100644 --- a/testing/web-platform/tests/css/css-view-transitions/dialog-in-rtl-iframe-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/dialog-in-rtl-iframe-ref.html @@ -2,7 +2,7 @@ <html> <head> <title>View transitions: Dialog element in RTL scrollable iframe</title> - <link rel="help" href="https://github.com/WICG/view-transitions"> + <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <style> iframe { diff --git a/testing/web-platform/tests/css/css-view-transitions/dialog-in-rtl-iframe.html b/testing/web-platform/tests/css/css-view-transitions/dialog-in-rtl-iframe.html index b9c0f7e25c..f5959bf434 100644 --- a/testing/web-platform/tests/css/css-view-transitions/dialog-in-rtl-iframe.html +++ b/testing/web-platform/tests/css/css-view-transitions/dialog-in-rtl-iframe.html @@ -2,7 +2,7 @@ <html class=reftest-wait> <head> <title>View transitions: Dialog element in RTL scrollable iframe</title> - <link rel="help" href="https://github.com/WICG/view-transitions"> + <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="dialog-in-rtl-iframe-ref.html"> <meta name=fuzzy content="dialog-in-rtl-iframe-ref.html:0-80;0-500"> diff --git a/testing/web-platform/tests/css/css-view-transitions/dialog-in-top-layer-during-transition-new.html b/testing/web-platform/tests/css/css-view-transitions/dialog-in-top-layer-during-transition-new.html index 91c2277366..487a828b14 100644 --- a/testing/web-platform/tests/css/css-view-transitions/dialog-in-top-layer-during-transition-new.html +++ b/testing/web-platform/tests/css/css-view-transitions/dialog-in-top-layer-during-transition-new.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: element in top layer during transition</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="dialog-in-top-layer-during-transition-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/dialog-in-top-layer-during-transition-old.html b/testing/web-platform/tests/css/css-view-transitions/dialog-in-top-layer-during-transition-old.html index 406bdac32b..5e9867e8cb 100644 --- a/testing/web-platform/tests/css/css-view-transitions/dialog-in-top-layer-during-transition-old.html +++ b/testing/web-platform/tests/css/css-view-transitions/dialog-in-top-layer-during-transition-old.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: element in top layer during transition</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="dialog-in-top-layer-during-transition-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/dialog-in-top-layer-during-transition-ref.html b/testing/web-platform/tests/css/css-view-transitions/dialog-in-top-layer-during-transition-ref.html index 41467678a3..029b0e9f86 100644 --- a/testing/web-platform/tests/css/css-view-transitions/dialog-in-top-layer-during-transition-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/dialog-in-top-layer-during-transition-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: element in top layer during transition (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/document-element-detached-crash.html b/testing/web-platform/tests/css/css-view-transitions/document-element-detached-crash.html index cfdf769695..66a8db6b67 100644 --- a/testing/web-platform/tests/css/css-view-transitions/document-element-detached-crash.html +++ b/testing/web-platform/tests/css/css-view-transitions/document-element-detached-crash.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="test-wait"> <title>View transitions: documentElement.remove</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/element-with-overflow-ref.html b/testing/web-platform/tests/css/css-view-transitions/element-with-overflow-ref.html index 523c8616a6..698b32956b 100644 --- a/testing/web-platform/tests/css/css-view-transitions/element-with-overflow-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/element-with-overflow-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: element with overflow ref</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> .old_target { diff --git a/testing/web-platform/tests/css/css-view-transitions/element-with-overflow.html b/testing/web-platform/tests/css/css-view-transitions/element-with-overflow.html index 678e0a062e..a4dcf5707b 100644 --- a/testing/web-platform/tests/css/css-view-transitions/element-with-overflow.html +++ b/testing/web-platform/tests/css/css-view-transitions/element-with-overflow.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: element with overflow</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="element-with-overflow-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/event-pseudo-name.html b/testing/web-platform/tests/css/css-view-transitions/event-pseudo-name.html index bccf64915d..a57c1d5108 100644 --- a/testing/web-platform/tests/css/css-view-transitions/event-pseudo-name.html +++ b/testing/web-platform/tests/css/css-view-transitions/event-pseudo-name.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: event pseudo name</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="web-animations-api-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/far-away-capture-ref.html b/testing/web-platform/tests/css/css-view-transitions/far-away-capture-ref.html index 9e10b9b44e..5fadcc5f9c 100644 --- a/testing/web-platform/tests/css/css-view-transitions/far-away-capture-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/far-away-capture-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: offscreen content</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> body { background: pink } diff --git a/testing/web-platform/tests/css/css-view-transitions/far-away-capture.html b/testing/web-platform/tests/css/css-view-transitions/far-away-capture.html index bfe0b9fb94..97ad9dfb44 100644 --- a/testing/web-platform/tests/css/css-view-transitions/far-away-capture.html +++ b/testing/web-platform/tests/css/css-view-transitions/far-away-capture.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: offscreen content</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="far-away-capture-ref.html"> <meta name="fuzzy" content="far-away-capture-ref.html:0-1;0-5"> diff --git a/testing/web-platform/tests/css/css-view-transitions/hit-test-unpainted-element-from-point.html b/testing/web-platform/tests/css/css-view-transitions/hit-test-unpainted-element-from-point.html index 7c9ed911cb..c9650d97b9 100644 --- a/testing/web-platform/tests/css/css-view-transitions/hit-test-unpainted-element-from-point.html +++ b/testing/web-platform/tests/css/css-view-transitions/hit-test-unpainted-element-from-point.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: hit test shared element at the real dom location</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="hit-test-unpainted-element-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/hit-test-unpainted-element-ref.html b/testing/web-platform/tests/css/css-view-transitions/hit-test-unpainted-element-ref.html index dce9f27d90..9d50894d14 100644 --- a/testing/web-platform/tests/css/css-view-transitions/hit-test-unpainted-element-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/hit-test-unpainted-element-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: hit test shared element at the real dom location (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/hit-test-unpainted-element.html b/testing/web-platform/tests/css/css-view-transitions/hit-test-unpainted-element.html index 68026edfb1..e8bec197f6 100644 --- a/testing/web-platform/tests/css/css-view-transitions/hit-test-unpainted-element.html +++ b/testing/web-platform/tests/css/css-view-transitions/hit-test-unpainted-element.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: hit test shared element at the real dom location</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="hit-test-unpainted-element-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/hit-test-unrelated-element-ref.html b/testing/web-platform/tests/css/css-view-transitions/hit-test-unrelated-element-ref.html index bca532b22c..99cde99149 100644 --- a/testing/web-platform/tests/css/css-view-transitions/hit-test-unrelated-element-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/hit-test-unrelated-element-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: hit test shared element at the real dom location (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/hit-test-unrelated-element.html b/testing/web-platform/tests/css/css-view-transitions/hit-test-unrelated-element.html index 1b8868280b..a427aee8d1 100644 --- a/testing/web-platform/tests/css/css-view-transitions/hit-test-unrelated-element.html +++ b/testing/web-platform/tests/css/css-view-transitions/hit-test-unrelated-element.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: hit test shared element at the real dom location</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="hit-test-unrelated-element-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/iframe-new-has-scrollbar-ref.html b/testing/web-platform/tests/css/css-view-transitions/iframe-new-has-scrollbar-ref.html index ea895e8484..338f8a6f23 100644 --- a/testing/web-platform/tests/css/css-view-transitions/iframe-new-has-scrollbar-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/iframe-new-has-scrollbar-ref.html @@ -2,7 +2,7 @@ <html> <head> <title>View transitions: iframe transition to scrollbar (ref)</title> - <link rel="help" href="https://github.com/WICG/view-transitions"> + <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <style> iframe { diff --git a/testing/web-platform/tests/css/css-view-transitions/iframe-new-has-scrollbar.html b/testing/web-platform/tests/css/css-view-transitions/iframe-new-has-scrollbar.html index 37a300902a..67a57bb885 100644 --- a/testing/web-platform/tests/css/css-view-transitions/iframe-new-has-scrollbar.html +++ b/testing/web-platform/tests/css/css-view-transitions/iframe-new-has-scrollbar.html @@ -2,7 +2,7 @@ <html class=reftest-wait> <head> <title>View transitions: iframe transition to scrollbar</title> - <link rel="help" href="https://github.com/WICG/view-transitions"> + <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="iframe-new-has-scrollbar-ref.html"> <meta name=fuzzy content="iframe-new-has-scrollbar-ref.html:0-80;0-500"> diff --git a/testing/web-platform/tests/css/css-view-transitions/iframe-old-has-scrollbar-ref.html b/testing/web-platform/tests/css/css-view-transitions/iframe-old-has-scrollbar-ref.html index 3bb9bdb88d..6d6556d51c 100644 --- a/testing/web-platform/tests/css/css-view-transitions/iframe-old-has-scrollbar-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/iframe-old-has-scrollbar-ref.html @@ -2,7 +2,7 @@ <html> <head> <title>View transitions: iframe transition from scrollbar (ref)</title> - <link rel="help" href="https://github.com/WICG/view-transitions"> + <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <style> iframe { diff --git a/testing/web-platform/tests/css/css-view-transitions/iframe-old-has-scrollbar.html b/testing/web-platform/tests/css/css-view-transitions/iframe-old-has-scrollbar.html index 014a429994..96dd75a3df 100644 --- a/testing/web-platform/tests/css/css-view-transitions/iframe-old-has-scrollbar.html +++ b/testing/web-platform/tests/css/css-view-transitions/iframe-old-has-scrollbar.html @@ -2,7 +2,7 @@ <html class=reftest-wait> <head> <title>View transitions: iframe transition from scrollbar</title> - <link rel="help" href="https://github.com/WICG/view-transitions"> + <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="iframe-old-has-scrollbar-ref.html"> <meta name=fuzzy content="iframe-old-has-scrollbar-ref.html:0-80;0-500"> diff --git a/testing/web-platform/tests/css/css-view-transitions/iframe-transition-ref.html b/testing/web-platform/tests/css/css-view-transitions/iframe-transition-ref.html index 49394f35f6..bf99b25f76 100644 --- a/testing/web-platform/tests/css/css-view-transitions/iframe-transition-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/iframe-transition-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: iframe</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> iframe { width: 500px; height: 500px } diff --git a/testing/web-platform/tests/css/css-view-transitions/iframe-transition.sub.html b/testing/web-platform/tests/css/css-view-transitions/iframe-transition.sub.html index 93a5cbed66..8fa361b0fc 100644 --- a/testing/web-platform/tests/css/css-view-transitions/iframe-transition.sub.html +++ b/testing/web-platform/tests/css/css-view-transitions/iframe-transition.sub.html @@ -2,7 +2,7 @@ <html class=reftest-wait> <title>View transitions: iframe</title> <meta name="timeout" content="long"> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="iframe-transition-ref.html"> <meta name="assert" content="Ensure that iframe root capture is sized and displayed correctly"> diff --git a/testing/web-platform/tests/css/css-view-transitions/inline-element-size-ref.html b/testing/web-platform/tests/css/css-view-transitions/inline-element-size-ref.html index 177594c870..14b76fb07e 100644 --- a/testing/web-platform/tests/css/css-view-transitions/inline-element-size-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/inline-element-size-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: capture elements with display inline (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/inline-element-size.html b/testing/web-platform/tests/css/css-view-transitions/inline-element-size.html index c9f0d48942..4f94c92550 100644 --- a/testing/web-platform/tests/css/css-view-transitions/inline-element-size.html +++ b/testing/web-platform/tests/css/css-view-transitions/inline-element-size.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>View transitions: capture elements with display inline</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="inline-element-size-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/input-targets-root-while-render-blocked.html b/testing/web-platform/tests/css/css-view-transitions/input-targets-root-while-render-blocked.html index dbeb39da54..b26f8a45d6 100644 --- a/testing/web-platform/tests/css/css-view-transitions/input-targets-root-while-render-blocked.html +++ b/testing/web-platform/tests/css/css-view-transitions/input-targets-root-while-render-blocked.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: ensure input targets document root while rendering is suppressed</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7797"> <link rel="author" href="mailto:bokan@chromium.org"> diff --git a/testing/web-platform/tests/css/css-view-transitions/intrinsic-aspect-ratio-ref.html b/testing/web-platform/tests/css/css-view-transitions/intrinsic-aspect-ratio-ref.html index 4455ad6172..02d9cfeb8e 100644 --- a/testing/web-platform/tests/css/css-view-transitions/intrinsic-aspect-ratio-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/intrinsic-aspect-ratio-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: different width container should keep aspect ratio (by default)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .spacer { diff --git a/testing/web-platform/tests/css/css-view-transitions/japanese-tag-ref.html b/testing/web-platform/tests/css/css-view-transitions/japanese-tag-ref.html index 8c57dba658..ac05756f05 100644 --- a/testing/web-platform/tests/css/css-view-transitions/japanese-tag-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/japanese-tag-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: shared element writing-modes (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/japanese-tag.html b/testing/web-platform/tests/css/css-view-transitions/japanese-tag.html index 976dcab4fc..8940608f95 100644 --- a/testing/web-platform/tests/css/css-view-transitions/japanese-tag.html +++ b/testing/web-platform/tests/css/css-view-transitions/japanese-tag.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: shared element writing-modes</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="japanese-tag-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/list-style-position-style-change-crash.html b/testing/web-platform/tests/css/css-view-transitions/list-style-position-style-change-crash.html index 5910d5d95a..2a033ddc7e 100644 --- a/testing/web-platform/tests/css/css-view-transitions/list-style-position-style-change-crash.html +++ b/testing/web-platform/tests/css/css-view-transitions/list-style-position-style-change-crash.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="test-wait"> <title>View transitions: list-style-position crash</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <script> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-new.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-new.html index 9a00a62b7a..23f5fc22cf 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-new.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-new.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element below and on top of viewport partially onscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-below-and-on-top-of-viewport-partially-onscreen-ref.html"> <meta name="fuzzy" content="massive-element-below-and-on-top-of-viewport-partially-onscreen-ref.html:maxDifference=0-2;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-old.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-old.html index ed16ac5662..fbc8edadc0 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-old.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-old.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element below and on top of viewport partially onscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-below-and-on-top-of-viewport-partially-onscreen-ref.html"> <meta name="fuzzy" content="massive-element-below-and-on-top-of-viewport-partially-onscreen-ref.html:maxDifference=0-2;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-ref.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-ref.html index 507f9b8c0a..e251196cbf 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: massive element below viewport partially onscreen (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-offscreen-new.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-offscreen-new.html index b5c0d4e9ae..611d4da21a 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-offscreen-new.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-offscreen-new.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element below viewport and completely offscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-below-viewport-offscreen-ref.html"> <meta name="fuzzy" content="massive-element-below-viewport-offscreen-ref.html:maxDifference=0-3;totalPixels=0-950"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-offscreen-old.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-offscreen-old.html index 6eeb85af3e..bda3ebf1b1 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-offscreen-old.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-offscreen-old.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element below viewport and completely offscreen (old content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-below-viewport-offscreen-ref.html"> <meta name="fuzzy" content="massive-element-below-viewport-offscreen-ref.html:maxDifference=0-2;totalPixels=0-445"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-offscreen-ref.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-offscreen-ref.html index 05827eb196..ef26c46f0d 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-offscreen-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-offscreen-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: massive element below viewport and completely offscreen (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-new.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-new.html index 54232ead6f..e881e19622 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-new.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-new.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element below viewport partially onscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-below-viewport-partially-onscreen-ref.html"> <meta name="fuzzy" content="massive-element-below-viewport-partially-onscreen-ref.html:maxDifference=0-2;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-old.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-old.html index 772720def1..c8c3c53082 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-old.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-old.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element below viewport partially onscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-below-viewport-partially-onscreen-ref.html"> <meta name="fuzzy" content="massive-element-below-viewport-partially-onscreen-ref.html:maxDifference=0-2;totalPixels=0-445"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-ref.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-ref.html index 001c135f0a..3c2ae6a6f5 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-below-viewport-partially-onscreen-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: massive element below viewport partially onscreen (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-offscreen-new.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-offscreen-new.html index d9d03d1e91..c8471032a4 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-offscreen-new.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-offscreen-new.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element on top of viewport and completely offscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-left-of-viewport-offscreen-ref.html"> <meta name="fuzzy" content="massive-element-left-of-viewport-offscreen-ref.html:maxDifference=0-2;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-offscreen-old.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-offscreen-old.html index 7861e5c3b8..04ab58f3aa 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-offscreen-old.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-offscreen-old.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element on top of viewport and completely offscreen (old content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-left-of-viewport-offscreen-ref.html"> <meta name="fuzzy" content="massive-element-left-of-viewport-offscreen-ref.html:maxDifference=0-3;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-offscreen-ref.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-offscreen-ref.html index 32e688bbdb..3fb7a7a530 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-offscreen-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-offscreen-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: massive element on top of viewport and completely offscreen (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> :root { diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-new.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-new.html index 7c14cef2d0..15cc94ffe7 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-new.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-new.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element on top of viewport partially onscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-left-of-viewport-partially-onscreen-ref.html"> <meta name="fuzzy" content="massive-element-left-of-viewport-partially-onscreen-ref.html:maxDifference=0-2;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-old.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-old.html index b586f96de9..0d2aeec59d 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-old.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-old.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element on top of viewport partially onscreen (old content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-left-of-viewport-partially-onscreen-ref.html"> <meta name="fuzzy" content="massive-element-left-of-viewport-partially-onscreen-ref.html:maxDifference=0-3;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-ref.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-ref.html index 8fa3050535..436783b42e 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: massive element on top of viewport partially onscreen (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> :root { diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-new.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-new.html index f4a9f833df..6ef8edd3b0 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-new.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-new.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element on top of viewport and completely offscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-on-top-of-viewport-offscreen-ref.html"> <meta name="fuzzy" content="massive-element-on-top-of-viewport-offscreen-ref.html:maxDifference=0-6;totalPixels=0-920"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-old.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-old.html index a2eb0447bd..5e303e8286 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-old.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-old.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element on top of viewport and completely offscreen (old content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-on-top-of-viewport-offscreen-ref.html"> <meta name="fuzzy" content="massive-element-on-top-of-viewport-offscreen-ref.html:maxDifference=0-3;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-ref.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-ref.html index e835907738..697719507f 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: massive element on top of viewport and completely offscreen (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> .target_at_bottom_edge { diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-new.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-new.html index 764a311fc9..a9e5f5842a 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-new.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-new.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element on top of viewport partially onscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-on-top-of-viewport-partially-onscreen-ref.html"> <meta name="fuzzy" content="massive-element-on-top-of-viewport-partially-onscreen-ref.html:maxDifference=0-2;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-old.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-old.html index cecefd8f58..41dc622914 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-old.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-old.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element on top of viewport partially onscreen (old content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-on-top-of-viewport-partially-onscreen-ref.html"> <meta name="fuzzy" content="massive-element-on-top-of-viewport-partially-onscreen-ref.html:maxDifference=0-3;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-ref.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-ref.html index 9c60e8bf1d..6377c72616 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: massive element on top of viewport partially onscreen (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-new.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-new.html index b1bae1c09b..719701fe88 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-new.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-new.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element below and on top of viewport partially onscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-right-and-left-of-viewport-partially-onscreen-ref.html"> <meta name="fuzzy" content="massive-element-right-and-left-of-viewport-partially-onscreen-ref.html:maxDifference=0-2;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-old.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-old.html index c878ce881e..e8eeec3f26 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-old.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-old.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element below and on top of viewport partially onscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-right-and-left-of-viewport-partially-onscreen-ref.html"> <meta name="fuzzy" content="massive-element-right-and-left-of-viewport-partially-onscreen-ref.html:maxDifference=0-2;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-ref.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-ref.html index 3516741da0..c7bdc7afee 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: massive element below viewport partially onscreen (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> :root { diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-offscreen-new.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-offscreen-new.html index 91b133f2cf..89d00a53a8 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-offscreen-new.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-offscreen-new.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element below viewport and completely offscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-right-of-viewport-offscreen-ref.html"> <meta name="fuzzy" content="massive-element-right-of-viewport-offscreen-ref.html:maxDifference=0-2;totalPixels=0-445"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-offscreen-old.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-offscreen-old.html index 164ff05f93..04247af18e 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-offscreen-old.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-offscreen-old.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element below viewport and completely offscreen (old content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-right-of-viewport-offscreen-ref.html"> <meta name="fuzzy" content="massive-element-right-of-viewport-offscreen-ref.html:maxDifference=0-3;totalPixels=0-445"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-offscreen-ref.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-offscreen-ref.html index f29d3ac609..ed554d4dfe 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-offscreen-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-offscreen-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: massive element below viewport and completely offscreen (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-new.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-new.html index b63ed07225..a7b599e5eb 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-new.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-new.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element below viewport partially onscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-right-of-viewport-partially-onscreen-ref.html"> <meta name="fuzzy" content="massive-element-right-of-viewport-partially-onscreen-ref.html:maxDifference=0-2;totalPixels=0-330"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-old.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-old.html index cf090e0ab4..2498f2e1f1 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-old.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-old.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: massive element below viewport partially onscreen (new content)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="massive-element-right-of-viewport-partially-onscreen-ref.html"> <meta name="fuzzy" content="massive-element-right-of-viewport-partially-onscreen-ref.html:maxDifference=0-3;totalPixels=0-445"> diff --git a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-ref.html b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-ref.html index 1df26bb375..b134c15858 100644 --- a/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: massive element below viewport partially onscreen (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> :root { diff --git a/testing/web-platform/tests/css/css-view-transitions/modify-style-via-cssom-ref.html b/testing/web-platform/tests/css/css-view-transitions/modify-style-via-cssom-ref.html index dd93e0b498..503418c3dc 100644 --- a/testing/web-platform/tests/css/css-view-transitions/modify-style-via-cssom-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/modify-style-via-cssom-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: Modify style via CSSOM (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> #box { diff --git a/testing/web-platform/tests/css/css-view-transitions/modify-style-via-cssom.html b/testing/web-platform/tests/css/css-view-transitions/modify-style-via-cssom.html index 6f026607bc..5e96a68465 100644 --- a/testing/web-platform/tests/css/css-view-transitions/modify-style-via-cssom.html +++ b/testing/web-platform/tests/css/css-view-transitions/modify-style-via-cssom.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait"> <title>View transitions: Modify style via CSSOM</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="modify-style-via-cssom-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/named-element-with-fix-pos-child-new.html b/testing/web-platform/tests/css/css-view-transitions/named-element-with-fix-pos-child-new.html index 8a552b03a5..402c2a4131 100644 --- a/testing/web-platform/tests/css/css-view-transitions/named-element-with-fix-pos-child-new.html +++ b/testing/web-platform/tests/css/css-view-transitions/named-element-with-fix-pos-child-new.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: element with fixed position descendant</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="named-element-with-fix-pos-child-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/named-element-with-fix-pos-child-old.html b/testing/web-platform/tests/css/css-view-transitions/named-element-with-fix-pos-child-old.html index a8211f4200..b011291bfc 100644 --- a/testing/web-platform/tests/css/css-view-transitions/named-element-with-fix-pos-child-old.html +++ b/testing/web-platform/tests/css/css-view-transitions/named-element-with-fix-pos-child-old.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: element with fixed position descendant</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="named-element-with-fix-pos-child-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/named-element-with-fix-pos-child-ref.html b/testing/web-platform/tests/css/css-view-transitions/named-element-with-fix-pos-child-ref.html index a161cd8fc9..7d513e282d 100644 --- a/testing/web-platform/tests/css/css-view-transitions/named-element-with-fix-pos-child-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/named-element-with-fix-pos-child-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: element with fixed position descendant (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-and-old-sizes-match-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-and-old-sizes-match-ref.html index b8c3b34c03..79e8980139 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-and-old-sizes-match-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-and-old-sizes-match-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: capture elements with different size capture</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <script src="/common/reftest-wait.js"></script> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-and-old-sizes-match.html b/testing/web-platform/tests/css/css-view-transitions/new-and-old-sizes-match.html index 78efa9d82f..094d6963bf 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-and-old-sizes-match.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-and-old-sizes-match.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: capture elements with different size capture</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-and-old-sizes-match-ref.html"> <meta name="fuzzy" content="new-and-old-sizes-match-ref.html:0-1;0-300"> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-clip-path-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-clip-path-ref.html index 1b28bbf29b..a216bff8b0 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-clip-path-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-clip-path-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture opacity elements (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .box { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-clip-path.html b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-clip-path.html index 4a2dfc78c2..702bb09bc3 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-clip-path.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-clip-path.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: capture clip-path elements</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-captures-clip-path-ref.html"> <meta name="fuzzy" content="new-content-captures-clip-path-ref.html:0-1;0-500"> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-different-size-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-different-size-ref.html index 2a80cf5568..62de5018a8 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-different-size-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-different-size-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture elements with different size capture (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .box { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-different-size.html b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-different-size.html index a891dec555..740199675d 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-different-size.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-different-size.html @@ -2,7 +2,7 @@ <meta name="timeout" content="long"> <html class=reftest-wait> <title>View transitions: capture elements with different size capture</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-captures-different-size-ref.html"> <meta name=fuzzy content="new-content-captures-different-size-ref.html:0-40;0-30000"> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-opacity-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-opacity-ref.html index deea6d139b..615d414594 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-opacity-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-opacity-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture opacity elements (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .box { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-opacity.html b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-opacity.html index ab4efd1f42..03d2cc858e 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-opacity.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-opacity.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: capture opacity elements</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-captures-opacity-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-positioned-spans-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-positioned-spans-ref.html index 6d906fc6f1..9d9cfa06be 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-positioned-spans-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-positioned-spans-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture opacity elements (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> span { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-positioned-spans.html b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-positioned-spans.html index b88654cd37..7ea6b1583c 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-positioned-spans.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-positioned-spans.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: capture span elements</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-captures-positioned-spans-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-root-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-root-ref.html index 2f2e5e7694..4b0cae3f8e 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-root-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-root-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture opacity elements (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .box { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-root.html b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-root.html index 84e55de58a..90b13c338c 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-root.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-root.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: capture root elements</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-captures-root-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-spans-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-spans-ref.html index b95233d712..f7bba2d683 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-spans-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-spans-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture opacity elements (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> span { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-spans.html b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-spans.html index 843f6752d7..94bef1d6dd 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-captures-spans.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-captures-spans.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: capture span elements</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-captures-spans-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-changes-overflow-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-changes-overflow-ref.html index 5f75909e82..3f69ac695d 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-changes-overflow-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-changes-overflow-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture elements and then change overflow (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> body { background: pink } diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-changes-overflow.html b/testing/web-platform/tests/css/css-view-transitions/new-content-changes-overflow.html index a8e2fa8c6b..9dbd258833 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-changes-overflow.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-changes-overflow.html @@ -2,7 +2,7 @@ <meta name="timeout" content="long"> <html class=reftest-wait> <title>View transitions: capture elements and then change overflow</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-changes-overflow-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-container-writing-modes-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-container-writing-modes-ref.html index 34e36786f8..161e3876ac 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-container-writing-modes-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-container-writing-modes-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: container of shared element writing-modes (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-container-writing-modes.html b/testing/web-platform/tests/css/css-view-transitions/new-content-container-writing-modes.html index e7ac768e17..c6fceff6c1 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-container-writing-modes.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-container-writing-modes.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: container of shared element writing-modes</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-container-writing-modes-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-element-writing-modes-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-element-writing-modes-ref.html index 94280ae3af..9ed3f5f681 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-element-writing-modes-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-element-writing-modes-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: shared element writing-modes (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-element-writing-modes.html b/testing/web-platform/tests/css/css-view-transitions/new-content-element-writing-modes.html index d7740d78f5..6a975c6d9c 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-element-writing-modes.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-element-writing-modes.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: shared element writing-modes</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-element-writing-modes-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-from-root-display-none-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-from-root-display-none-ref.html index e936a779c7..6c165d1b24 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-from-root-display-none-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-from-root-display-none-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: capture starts with root being display none (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .tb { writing-mode: horizontal-tb; } diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-from-root-display-none.html b/testing/web-platform/tests/css/css-view-transitions/new-content-from-root-display-none.html index 4d2ad28b17..e59c029ccc 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-from-root-display-none.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-from-root-display-none.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait style="display: none"> <title>View transitions: capture starts with root being display none</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-from-root-display-none-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-has-scrollbars-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-has-scrollbars-ref.html index eddd733014..6ff0b8cbcf 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-has-scrollbars-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-has-scrollbars-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: incoming viewport has scrollbars (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7859"> <link rel="author" href="mailto:bokan@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-has-scrollbars.html b/testing/web-platform/tests/css/css-view-transitions/new-content-has-scrollbars.html index 4024952b13..3246a7e76f 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-has-scrollbars.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-has-scrollbars.html @@ -2,7 +2,7 @@ <meta name="timeout" content="long"> <html class=reftest-wait> <title>View transitions: incoming viewport has scrollbars</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7859"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="new-content-has-scrollbars-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-intrinsic-aspect-ratio.html b/testing/web-platform/tests/css/css-view-transitions/new-content-intrinsic-aspect-ratio.html index 53ec94122f..60f2b92036 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-intrinsic-aspect-ratio.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-intrinsic-aspect-ratio.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: different width container should keep aspect ratio (by default)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="intrinsic-aspect-ratio-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-is-empty-div-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-is-empty-div-ref.html index c85028d3bd..7492467a12 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-is-empty-div-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-is-empty-div-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: new content captures an empty div (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> div { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-is-empty-div.html b/testing/web-platform/tests/css/css-view-transitions/new-content-is-empty-div.html index bacc63f194..a61a6f06ef 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-is-empty-div.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-is-empty-div.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: old content captures an empty div</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-is-empty-div-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-object-fit-fill.html b/testing/web-platform/tests/css/css-view-transitions/new-content-object-fit-fill.html index b891eee16d..04a80409c5 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-object-fit-fill.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-object-fit-fill.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: display content in a pseudo with object-fit: fill</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="content-object-fit-fill-ref.html"> <meta name="fuzzy" content="content-object-fit-fill-ref.html:0-60;0-20"> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-object-fit-none.html b/testing/web-platform/tests/css/css-view-transitions/new-content-object-fit-none.html index d4b81b1a0e..ccdbc6edc3 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-object-fit-none.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-object-fit-none.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: display content in a pseudo with object-fit: none</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="content-object-fit-none-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path-ref.html index 28e22b8ca2..1a17f0b8e0 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: object-view-box with larger overflow (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path-reference-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path-reference-ref.html index b1871141c2..41b5c41db2 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path-reference-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path-reference-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: object-view-box with larger overflow (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path-reference.html b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path-reference.html index 01a3ed3204..73346e66df 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path-reference.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path-reference.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: object-view-box with larger clip-path</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-object-view-box-clip-path-reference-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path.html b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path.html index dcd5fec70a..47b1e91c9d 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-clip-path.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: object-view-box with larger clip-path</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-object-view-box-clip-path-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow-clipped-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow-clipped-ref.html index d6016c950e..89d2331971 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow-clipped-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow-clipped-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: object-view-box with larger overflow (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow-clipped.html b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow-clipped.html index 593901529f..1c4761f1f3 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow-clipped.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow-clipped.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: object-view-box with larger overflow</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-object-view-box-overflow-clipped-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow-ref.html index d6016c950e..89d2331971 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: object-view-box with larger overflow (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow.html b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow.html index 2bbea1e1c4..89e83beb7f 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-object-view-box-overflow.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: object-view-box with larger overflow</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-object-view-box-overflow-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-scaling-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-content-scaling-ref.html index 1ca35f5a42..729be2fcbf 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-scaling-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-scaling-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: display content in a pseudo with proper scaling (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .inner { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-scaling.html b/testing/web-platform/tests/css/css-view-transitions/new-content-scaling.html index 4ea3c2e04a..bccb760fb5 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-scaling.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-scaling.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: display content in a pseudo with proper scaling</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-content-scaling-ref.html"> <meta name="fuzzy" content="new-content-scaling-ref.html:maxDifference=0-16;totalPixels=0-400"> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-with-object-view-box.html b/testing/web-platform/tests/css/css-view-transitions/new-content-with-object-view-box.html index 47917e90f3..40139bbaa9 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-with-object-view-box.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-with-object-view-box.html @@ -2,7 +2,7 @@ <meta name="timeout" content="long"> <html class=reftest-wait> <title>View transitions: capture elements with object view box on the pseudo</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="content-with-object-view-box-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-with-overflow-zoomed.html b/testing/web-platform/tests/css/css-view-transitions/new-content-with-overflow-zoomed.html index 59170ebf00..4ab9fa5a57 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-with-overflow-zoomed.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-with-overflow-zoomed.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: shared element with overflow</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="content-with-overflow-zoomed-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-content-with-overflow.html b/testing/web-platform/tests/css/css-view-transitions/new-content-with-overflow.html index e2dc045089..165b4710ca 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-content-with-overflow.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-content-with-overflow.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: shared element with overflow</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="content-with-overflow-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-element-on-start-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-element-on-start-ref.html index b3db1f2e19..8c096d1b10 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-element-on-start-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-element-on-start-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: one element captured for two tags (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> div { diff --git a/testing/web-platform/tests/css/css-view-transitions/new-element-on-start.html b/testing/web-platform/tests/css/css-view-transitions/new-element-on-start.html index 7e870e9d89..d57bdd6d42 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-element-on-start.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-element-on-start.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: new element tag specified for start phase</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-element-on-start-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-root-vertical-writing-mode-ref.html b/testing/web-platform/tests/css/css-view-transitions/new-root-vertical-writing-mode-ref.html index f6b817d92b..99ca705d00 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-root-vertical-writing-mode-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-root-vertical-writing-mode-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: container of shared element writing-modes (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/new-root-vertical-writing-mode.html b/testing/web-platform/tests/css/css-view-transitions/new-root-vertical-writing-mode.html index 61dc5aca17..e956fbc6cd 100644 --- a/testing/web-platform/tests/css/css-view-transitions/new-root-vertical-writing-mode.html +++ b/testing/web-platform/tests/css/css-view-transitions/new-root-vertical-writing-mode.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: container of shared element writing-modes</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-root-vertical-writing-mode-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/no-crash-set-exception.html b/testing/web-platform/tests/css/css-view-transitions/no-crash-set-exception.html index e1596cf76e..bc0d764a59 100644 --- a/testing/web-platform/tests/css/css-view-transitions/no-crash-set-exception.html +++ b/testing/web-platform/tests/css/css-view-transitions/no-crash-set-exception.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: author styles ignored during prepare</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <script src="/resources/testharness.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/no-crash-view-transition-in-massive-iframe.html b/testing/web-platform/tests/css/css-view-transitions/no-crash-view-transition-in-massive-iframe.html index c004a94dcb..ae9113cb1f 100644 --- a/testing/web-platform/tests/css/css-view-transitions/no-crash-view-transition-in-massive-iframe.html +++ b/testing/web-platform/tests/css/css-view-transitions/no-crash-view-transition-in-massive-iframe.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: no crash with view-transitions in massive iframe</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:roger.johannesson@xperi.com"> <script src="/resources/testharness.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/no-css-animation-while-render-blocked.html b/testing/web-platform/tests/css/css-view-transitions/no-css-animation-while-render-blocked.html index ae9cfda9bb..85af434a14 100644 --- a/testing/web-platform/tests/css/css-view-transitions/no-css-animation-while-render-blocked.html +++ b/testing/web-platform/tests/css/css-view-transitions/no-css-animation-while-render-blocked.html @@ -2,7 +2,7 @@ <meta name="timeout" content="long"> <html> <title>View transitions: CSS Animations are paused while render-blocked</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <script src="/resources/testharness.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/no-raf-while-render-blocked.html b/testing/web-platform/tests/css/css-view-transitions/no-raf-while-render-blocked.html index 52058b7a04..a86fd14536 100644 --- a/testing/web-platform/tests/css/css-view-transitions/no-raf-while-render-blocked.html +++ b/testing/web-platform/tests/css/css-view-transitions/no-raf-while-render-blocked.html @@ -2,7 +2,7 @@ <meta name="timeout" content="long"> <html> <title>View transitions: rAF is not issued while render-blocked</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <script src="/resources/testharness.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/no-root-capture-ref.html b/testing/web-platform/tests/css/css-view-transitions/no-root-capture-ref.html index 98ac7c9de9..1fe68ad941 100644 --- a/testing/web-platform/tests/css/css-view-transitions/no-root-capture-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/no-root-capture-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: shared element with overflow (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/no-root-capture.html b/testing/web-platform/tests/css/css-view-transitions/no-root-capture.html index 9e16d1e447..87e0326600 100644 --- a/testing/web-platform/tests/css/css-view-transitions/no-root-capture.html +++ b/testing/web-platform/tests/css/css-view-transitions/no-root-capture.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: shared element with overflow</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="no-root-capture-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/no-white-flash-before-activation-ref.html b/testing/web-platform/tests/css/css-view-transitions/no-white-flash-before-activation-ref.html new file mode 100644 index 0000000000..f661c07439 --- /dev/null +++ b/testing/web-platform/tests/css/css-view-transitions/no-white-flash-before-activation-ref.html @@ -0,0 +1,4 @@ +<!DOCTYPE html> +<html> +<body style="background-color: #000;"></body> +</html> diff --git a/testing/web-platform/tests/css/css-view-transitions/no-white-flash-before-activation.html b/testing/web-platform/tests/css/css-view-transitions/no-white-flash-before-activation.html new file mode 100644 index 0000000000..1b4bb7cfae --- /dev/null +++ b/testing/web-platform/tests/css/css-view-transitions/no-white-flash-before-activation.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> + <title>View Transitions: No white flash should be visible during the duration of the update callback</title> + <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/#ref-for-captured-in-a-view-transition"> + <link rel="match" href="no-white-flash-before-activation-ref.html"> + <style> + body { + margin: 0; + } + div { + width: 100vw; + height: 100vh; + background-color: #000; + } + </style> +</head> +<body> + <div></div> + <script src="/common/reftest-wait.js"></script> + <script> + failIfNot(document.startViewTransition, "Missing document.startViewTransition"); + addEventListener("load", () => { + document.startViewTransition(() => { + requestAnimationFrame(takeScreenshot); + return new Promise(() => {}); + }); + }); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-view-transitions/nothing-captured-ref.html b/testing/web-platform/tests/css/css-view-transitions/nothing-captured-ref.html index 98ac7c9de9..1fe68ad941 100644 --- a/testing/web-platform/tests/css/css-view-transitions/nothing-captured-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/nothing-captured-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: shared element with overflow (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/nothing-captured.html b/testing/web-platform/tests/css/css-view-transitions/nothing-captured.html index 468cb6e199..ac5635ec7c 100644 --- a/testing/web-platform/tests/css/css-view-transitions/nothing-captured.html +++ b/testing/web-platform/tests/css/css-view-transitions/nothing-captured.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: shared element with overflow</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="nothing-captured-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/object-view-box-new-image.html b/testing/web-platform/tests/css/css-view-transitions/object-view-box-new-image.html index 3ea7eb96b6..8ef19ed358 100644 --- a/testing/web-platform/tests/css/css-view-transitions/object-view-box-new-image.html +++ b/testing/web-platform/tests/css/css-view-transitions/object-view-box-new-image.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: object-view-box</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="object-view-box-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/object-view-box-old-image.html b/testing/web-platform/tests/css/css-view-transitions/object-view-box-old-image.html index 7f6f79ba44..335e48fa70 100644 --- a/testing/web-platform/tests/css/css-view-transitions/object-view-box-old-image.html +++ b/testing/web-platform/tests/css/css-view-transitions/object-view-box-old-image.html @@ -2,7 +2,7 @@ <meta name="timeout" content="long"> <html class=reftest-wait> <title>View transitions: object-view-box</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="object-view-box-ref.html"> <meta name="fuzzy" content="object-view-box-ref.html:0-1;0-300"> diff --git a/testing/web-platform/tests/css/css-view-transitions/object-view-box-ref.html b/testing/web-platform/tests/css/css-view-transitions/object-view-box-ref.html index 9ed1e50309..744f77f81e 100644 --- a/testing/web-platform/tests/css/css-view-transitions/object-view-box-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/object-view-box-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: object-view-box (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/offscreen-element-modified-before-coming-onscreen-ref.html b/testing/web-platform/tests/css/css-view-transitions/offscreen-element-modified-before-coming-onscreen-ref.html index 171f999554..e1142fa5f6 100644 --- a/testing/web-platform/tests/css/css-view-transitions/offscreen-element-modified-before-coming-onscreen-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/offscreen-element-modified-before-coming-onscreen-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: new element is modified while offscren and brought onscreen using pseudo (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/offscreen-element-modified-before-coming-onscreen.html b/testing/web-platform/tests/css/css-view-transitions/offscreen-element-modified-before-coming-onscreen.html index 77c35ab583..2a47a05972 100644 --- a/testing/web-platform/tests/css/css-view-transitions/offscreen-element-modified-before-coming-onscreen.html +++ b/testing/web-platform/tests/css/css-view-transitions/offscreen-element-modified-before-coming-onscreen.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: new element is modified while offscren and brought onscreen using pseudo</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="offscreen-element-modified-before-coming-onscreen-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-clip-path-ref.html b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-clip-path-ref.html index 0d9d498b6b..2f02cf13d0 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-clip-path-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-clip-path-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture clip-path elements (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .box { diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-clip-path.html b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-clip-path.html index 467b19d928..7ed5e1ca15 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-clip-path.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-clip-path.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: capture clip-path elements</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="old-content-captures-clip-path-ref.html"> <meta name="fuzzy" content="old-content-captures-clip-path-ref.html:0-1;0-500"> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-different-size-ref.html b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-different-size-ref.html index 4fbabd48f7..52e9941416 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-different-size-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-different-size-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture elements with different size capture (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .box { diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-different-size.html b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-different-size.html index a8d375f064..392247bc95 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-different-size.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-different-size.html @@ -2,7 +2,7 @@ <meta name="timeout" content="long"> <html class=reftest-wait> <title>View transitions: capture elements with different size capture</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="old-content-captures-different-size-ref.html"> <meta name=fuzzy content="old-content-captures-different-size-ref.html:0-40;0-30000"> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-opacity-ref.html b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-opacity-ref.html index 33f7f5b14a..fd48fb20e9 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-opacity-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-opacity-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture opacity elements (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .box { diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-opacity.html b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-opacity.html index ee3e3e4cde..cd71c9dfaf 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-opacity.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-opacity.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: capture opacity elements</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="old-content-captures-opacity-ref.html"> <meta name=fuzzy content="old-content-captures-opacity-ref.html:0-1;0-50000"> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-root-ref.html b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-root-ref.html index 92bd70f6f4..ca4705f818 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-root-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-root-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture root elements (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .box { diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-root.html b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-root.html index 96acb9f455..51a22bc136 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-captures-root.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-captures-root.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: capture root elements</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="old-content-captures-root-ref.html"> <meta name="fuzzy" content="old-content-captures-root-ref.html:0-1;0-500"> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-container-writing-modes-ref.html b/testing/web-platform/tests/css/css-view-transitions/old-content-container-writing-modes-ref.html index be264399c8..b109c7c1dd 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-container-writing-modes-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-container-writing-modes-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: container of shared element writing-modes (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-container-writing-modes.html b/testing/web-platform/tests/css/css-view-transitions/old-content-container-writing-modes.html index 9896c27152..1a0d9b9428 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-container-writing-modes.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-container-writing-modes.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: container of shared element writing-modes</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="old-content-container-writing-modes-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-element-writing-modes-ref.html b/testing/web-platform/tests/css/css-view-transitions/old-content-element-writing-modes-ref.html index 8c57dba658..ac05756f05 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-element-writing-modes-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-element-writing-modes-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: shared element writing-modes (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-element-writing-modes.html b/testing/web-platform/tests/css/css-view-transitions/old-content-element-writing-modes.html index 5029c6aaaa..3b1ba1ad4f 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-element-writing-modes.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-element-writing-modes.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: shared element writing-modes</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="old-content-element-writing-modes-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-has-scrollbars-ref.html b/testing/web-platform/tests/css/css-view-transitions/old-content-has-scrollbars-ref.html index b349e80700..640777bf08 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-has-scrollbars-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-has-scrollbars-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: outgoing viewport has scrollbars (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7859"> <link rel="author" href="mailto:bokan@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-has-scrollbars.html b/testing/web-platform/tests/css/css-view-transitions/old-content-has-scrollbars.html index 781ff7c125..13e26c702d 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-has-scrollbars.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-has-scrollbars.html @@ -2,7 +2,7 @@ <meta name="timeout" content="long"> <html class=reftest-wait> <title>View transitions: outgoing viewport has scrollbars</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="help" href="https://github.com/w3c/csswg-drafts/issues/7859"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="old-content-has-scrollbars-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-intrinsic-aspect-ratio.html b/testing/web-platform/tests/css/css-view-transitions/old-content-intrinsic-aspect-ratio.html index b46a778217..bfca9d321a 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-intrinsic-aspect-ratio.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-intrinsic-aspect-ratio.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: different width container should keep aspect ratio (by default)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="intrinsic-aspect-ratio-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-is-empty-div-ref.html b/testing/web-platform/tests/css/css-view-transitions/old-content-is-empty-div-ref.html index b4b17ee780..8144bfe43c 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-is-empty-div-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-is-empty-div-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: old content captures an empty div (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> div { diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-is-empty-div.html b/testing/web-platform/tests/css/css-view-transitions/old-content-is-empty-div.html index 137060c1dd..34aa580b34 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-is-empty-div.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-is-empty-div.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: old content captures an empty div</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="old-content-is-empty-div-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-object-fit-fill.html b/testing/web-platform/tests/css/css-view-transitions/old-content-object-fit-fill.html index 9ad8b14843..0652b30a07 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-object-fit-fill.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-object-fit-fill.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: display content in a pseudo with object-fit: fill</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="content-object-fit-fill-ref.html"> <meta name="fuzzy" content="content-object-fit-fill-ref.html:0-60;0-20"> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-object-fit-none.html b/testing/web-platform/tests/css/css-view-transitions/old-content-object-fit-none.html index 1275aece7c..cfe733230b 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-object-fit-none.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-object-fit-none.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: display content in a pseudo with object-fit: none</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="content-object-fit-none-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path-ref.html b/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path-ref.html index 28e22b8ca2..1a17f0b8e0 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: object-view-box with larger overflow (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path-reference-ref.html b/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path-reference-ref.html index b1871141c2..41b5c41db2 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path-reference-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path-reference-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: object-view-box with larger overflow (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path-reference.html b/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path-reference.html index 14a1ee83d9..5e6969d9cc 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path-reference.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path-reference.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: object-view-box with larger clip-path</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="old-content-object-view-box-clip-path-reference-ref.html"> <meta name="fuzzy" content="old-content-object-view-box-clip-path-reference-ref.html:0-1;0-100"> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path.html b/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path.html index dff57c68f6..f894555154 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-clip-path.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: object-view-box with larger clip-path</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="old-content-object-view-box-clip-path-ref.html"> <meta name="fuzzy" content="old-content-object-view-box-clip-path-ref.html:0-1;0-30"> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-overflow-ref.html b/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-overflow-ref.html index d6016c950e..89d2331971 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-overflow-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-overflow-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: object-view-box with larger overflow (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .target { diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-overflow.html b/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-overflow.html index ff9bb4e7d2..09285ddfe4 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-overflow.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-object-view-box-overflow.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: object-view-box with larger overflow</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="old-content-object-view-box-overflow-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-with-object-view-box.html b/testing/web-platform/tests/css/css-view-transitions/old-content-with-object-view-box.html index f24c8e60d1..df32005546 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-with-object-view-box.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-with-object-view-box.html @@ -2,7 +2,7 @@ <meta name="timeout" content="long"> <html class=reftest-wait> <title>View transitions: capture elements with object view box on the pseudo</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="content-with-object-view-box-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-with-overflow-zoomed.html b/testing/web-platform/tests/css/css-view-transitions/old-content-with-overflow-zoomed.html index 10257b761f..fa701deb33 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-with-overflow-zoomed.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-with-overflow-zoomed.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: shared element with overflow</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="content-with-overflow-zoomed-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-content-with-overflow.html b/testing/web-platform/tests/css/css-view-transitions/old-content-with-overflow.html index 6ed31d375c..79c7ba160e 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-content-with-overflow.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-content-with-overflow.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: shared element with overflow</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="content-with-overflow-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-root-vertical-writing-mode-ref.html b/testing/web-platform/tests/css/css-view-transitions/old-root-vertical-writing-mode-ref.html index bb4f2df0f8..2927b468d0 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-root-vertical-writing-mode-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-root-vertical-writing-mode-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: container of shared element writing-modes (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/old-root-vertical-writing-mode.html b/testing/web-platform/tests/css/css-view-transitions/old-root-vertical-writing-mode.html index 5f4425f64d..e94610e261 100644 --- a/testing/web-platform/tests/css/css-view-transitions/old-root-vertical-writing-mode.html +++ b/testing/web-platform/tests/css/css-view-transitions/old-root-vertical-writing-mode.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: container of shared element writing-modes</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="new-root-vertical-writing-mode-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/only-child-on-root-element-with-view-transition.html b/testing/web-platform/tests/css/css-view-transitions/only-child-on-root-element-with-view-transition.html index 8b81c35ff6..eaecbfe852 100644 --- a/testing/web-platform/tests/css/css-view-transitions/only-child-on-root-element-with-view-transition.html +++ b/testing/web-platform/tests/css/css-view-transitions/only-child-on-root-element-with-view-transition.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class="reftest-wait foo"> <title>View transitions: ensure :only-child is supported on view-transition</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <script src="/resources/testharness.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/only-child-view-transition.html b/testing/web-platform/tests/css/css-view-transitions/only-child-view-transition.html index 2089e9225e..02683423c0 100644 --- a/testing/web-platform/tests/css/css-view-transitions/only-child-view-transition.html +++ b/testing/web-platform/tests/css/css-view-transitions/only-child-view-transition.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: ensure :only-child is supported on view-transition</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <script src="/resources/testharness.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/parsing/pseudo-elements-invalid.html b/testing/web-platform/tests/css/css-view-transitions/parsing/pseudo-elements-invalid.html index 5a2624d09d..14bd768f76 100644 --- a/testing/web-platform/tests/css/css-view-transitions/parsing/pseudo-elements-invalid.html +++ b/testing/web-platform/tests/css/css-view-transitions/parsing/pseudo-elements-invalid.html @@ -60,9 +60,7 @@ for (const fn of functionPseudoElements) { test_invalid_selector(`${fn}(foo bar)`); test_invalid_selector_combinations(`${fn}(foo bar)`); - // Test function with selector arguments. - test_invalid_selector(`${fn}(.foo)`); - test_invalid_selector_combinations(`${fn}(.foo)`); + // Test function with selector arguments. test_invalid_selector(`${fn}(#bar)`); test_invalid_selector_combinations(`${fn}(#bar)`); } diff --git a/testing/web-platform/tests/css/css-view-transitions/pseudo-rendering-invalidation-ref.html b/testing/web-platform/tests/css/css-view-transitions/pseudo-rendering-invalidation-ref.html index f622f94b5f..6b3b493b6b 100644 --- a/testing/web-platform/tests/css/css-view-transitions/pseudo-rendering-invalidation-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/pseudo-rendering-invalidation-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: invalidating VT pseudo elements renders correctly (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/pseudo-rendering-invalidation.html b/testing/web-platform/tests/css/css-view-transitions/pseudo-rendering-invalidation.html index 55a9d5d373..e1f1718618 100644 --- a/testing/web-platform/tests/css/css-view-transitions/pseudo-rendering-invalidation.html +++ b/testing/web-platform/tests/css/css-view-transitions/pseudo-rendering-invalidation.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: invalidating VT pseudo elements renders correctly</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="pseudo-rendering-invalidation-ref.html"> <meta name="fuzzy" content="pseudo-rendering-invalidation-ref.html:0-20;0-300"> diff --git a/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard-no-star.html b/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard-no-star.html new file mode 100644 index 0000000000..2dc7d00cd1 --- /dev/null +++ b/testing/web-platform/tests/css/css-view-transitions/pseudo-with-classes-match-wildcard-no-star.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<title>View transition classes: selector should match with wildcard</title> +<link rel="help" href="https://drafts.csswg.org/css-transitions-2/"> +<link rel="author" href="mailto:nrosenthal@chromium.org"> +<link rel="match" href="pseudo-with-classes-ref.html"> +<script src="/common/rendering-utils.js"></script> +<script src="/common/reftest-wait.js"></script> + +<style> + div { + width: 100px; + height: 100px; + position: absolute; + } + + #target { + background: green; + view-transition-name: target; + view-transition-class: cls; + } + + ::view-transition-group(*) { + animation-duration: 1s; + } + + ::view-transition-new(.cls), + ::view-transition-old(.cls) { + left: 100px; + } +</style> +<div id=target></div> + +<script> + failIfNot(document.startViewTransition, "Missing document.startViewTransition"); + + window.addEventListener("load", () => { + document.startViewTransition().ready.then(takeScreenshot); + }); +</script> diff --git a/testing/web-platform/tests/css/css-view-transitions/ready_resolves_after_dom_before_raf.html b/testing/web-platform/tests/css/css-view-transitions/ready_resolves_after_dom_before_raf.html index adf3dc6c16..043d01c932 100644 --- a/testing/web-platform/tests/css/css-view-transitions/ready_resolves_after_dom_before_raf.html +++ b/testing/web-platform/tests/css/css-view-transitions/ready_resolves_after_dom_before_raf.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: promise resolution ordering</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <script src="/resources/testharness.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/root-captured-as-different-tag-ref.html b/testing/web-platform/tests/css/css-view-transitions/root-captured-as-different-tag-ref.html index 92bd70f6f4..ca4705f818 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-captured-as-different-tag-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-captured-as-different-tag-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: capture root elements (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> .box { diff --git a/testing/web-platform/tests/css/css-view-transitions/root-captured-as-different-tag.html b/testing/web-platform/tests/css/css-view-transitions/root-captured-as-different-tag.html index 4264db8169..a4d6f11ad4 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-captured-as-different-tag.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-captured-as-different-tag.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: capture root elements</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="old-content-captures-root-ref.html"> <meta name="fuzzy" content="old-content-captures-root-ref.html:0-1;0-500"> diff --git a/testing/web-platform/tests/css/css-view-transitions/root-element-display-none-crash.html b/testing/web-platform/tests/css/css-view-transitions/root-element-display-none-crash.html index fc55719c7e..d228be8581 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-element-display-none-crash.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-element-display-none-crash.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: html display none</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/root-element-display-none-during-transition-crash.html b/testing/web-platform/tests/css/css-view-transitions/root-element-display-none-during-transition-crash.html index 19f4143aa6..b9c384d94a 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-element-display-none-during-transition-crash.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-element-display-none-during-transition-crash.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: entry animation from root display none</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/root-scrollbar-with-fixed-background-ref.html b/testing/web-platform/tests/css/css-view-transitions/root-scrollbar-with-fixed-background-ref.html index a4010b5f2f..7b2a6103f3 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-scrollbar-with-fixed-background-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-scrollbar-with-fixed-background-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html > <title>View transitions: capture root element with scrollbar (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/root-scrollbar-with-fixed-background.html b/testing/web-platform/tests/css/css-view-transitions/root-scrollbar-with-fixed-background.html index c657fa19fb..2fa0132727 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-scrollbar-with-fixed-background.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-scrollbar-with-fixed-background.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait style="background: lightblue;"> <title>When the root element has scrollbars, these should be excluded in snapshot</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="root-scrollbar-with-fixed-background-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/root-style-change-during-animation-ref.html b/testing/web-platform/tests/css/css-view-transitions/root-style-change-during-animation-ref.html index c180c384f4..33fbea0064 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-style-change-during-animation-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-style-change-during-animation-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: root element style changes during transition</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> #target { diff --git a/testing/web-platform/tests/css/css-view-transitions/root-style-change-during-animation.html b/testing/web-platform/tests/css/css-view-transitions/root-style-change-during-animation.html index d1d291124b..0b8fb19946 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-style-change-during-animation.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-style-change-during-animation.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: root element style changes during transition</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="root-style-change-during-animation-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-end-ref.html b/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-end-ref.html index 24b50fa65e..d7d702e09a 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-end-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-end-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>Root to shared animation test (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-end.html b/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-end.html index 6ba07bd9e6..52a7d771ef 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-end.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-end.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <html class=reftest-wait> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="root-to-shared-animation-end-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-incoming-ref.html b/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-incoming-ref.html index 3075d2480c..df0ec1a9d8 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-incoming-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-incoming-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>Root to shared animation test (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-incoming.html b/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-incoming.html index 0620e911cf..adddf377c6 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-incoming.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-incoming.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <html class=reftest-wait> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="root-to-shared-animation-incoming-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-start-ref.html b/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-start-ref.html index d04ab3f767..8b777a2c8c 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-start-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-start-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>Root to shared animation test (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-start.html b/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-start.html index 0a1cb9617e..e7e669539d 100644 --- a/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-start.html +++ b/testing/web-platform/tests/css/css-view-transitions/root-to-shared-animation-start.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <html class=reftest-wait> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="root-to-shared-animation-start-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/rtl-with-scrollbar-ref.html b/testing/web-platform/tests/css/css-view-transitions/rtl-with-scrollbar-ref.html index c429136a2d..dc9d6ae245 100644 --- a/testing/web-platform/tests/css/css-view-transitions/rtl-with-scrollbar-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/rtl-with-scrollbar-ref.html @@ -2,7 +2,7 @@ <html dir="rtl"> <head> <title>Transition is correctly positioned on RTL page (ref)</title> - <link rel="help" href="https://github.com/WICG/view-transitions"> + <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/rtl-with-scrollbar.html b/testing/web-platform/tests/css/css-view-transitions/rtl-with-scrollbar.html index de2570605a..c8248b5d89 100644 --- a/testing/web-platform/tests/css/css-view-transitions/rtl-with-scrollbar.html +++ b/testing/web-platform/tests/css/css-view-transitions/rtl-with-scrollbar.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html dir="rtl" class=reftest-wait> <title>Transition is correctly positioned on RTL page</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="rtl-with-scrollbar-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/set-universal-specificity-ref.html b/testing/web-platform/tests/css/css-view-transitions/set-universal-specificity-ref.html index 4bf2164c7a..cce8d5eec3 100644 --- a/testing/web-platform/tests/css/css-view-transitions/set-universal-specificity-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/set-universal-specificity-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: universal specificity (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> body { diff --git a/testing/web-platform/tests/css/css-view-transitions/set-universal-specificity.html b/testing/web-platform/tests/css/css-view-transitions/set-universal-specificity.html index ec50b1e407..2e9e9c245c 100644 --- a/testing/web-platform/tests/css/css-view-transitions/set-universal-specificity.html +++ b/testing/web-platform/tests/css/css-view-transitions/set-universal-specificity.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: universal specificity</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <link rel="match" href="set-universal-specificity-ref.html"> <script src="/common/reftest-wait.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-absolute-ref.html b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-absolute-ref.html index 3d307ce3c7..5f2e780c58 100644 --- a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-absolute-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-absolute-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: use snapshot containing block for absolute position (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-absolute.html b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-absolute.html index ef986cd75c..4a619f29cc 100644 --- a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-absolute.html +++ b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-absolute.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: use snapshot containing block for absolute position</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="snapshot-containing-block-absolute-ref.html"> <meta name="fuzzy" content="snapshot-containing-block-absolute-ref.html:0-20;0-100"> diff --git a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-includes-scrollbar-gutter-ref.html b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-includes-scrollbar-gutter-ref.html index 8a74bd2164..c219fb1edd 100644 --- a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-includes-scrollbar-gutter-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-includes-scrollbar-gutter-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: snapshot containing block includes scrollbar gutters (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <style> :root { diff --git a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-includes-scrollbar-gutter.html b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-includes-scrollbar-gutter.html index 6e9bf568f6..8e47a056bd 100644 --- a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-includes-scrollbar-gutter.html +++ b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-includes-scrollbar-gutter.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: snapshot containing block includes scrollbar gutters</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="snapshot-containing-block-includes-scrollbar-gutter-ref.html"> <meta name="fuzzy" content="snapshot-containing-block-includes-scrollbar-gutter-ref.html:0-20;0-100"> diff --git a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-static-ref.html b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-static-ref.html index 8ed60934ca..0bff68efb2 100644 --- a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-static-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-static-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: use snapshot containing block for static position (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-static.html b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-static.html index 5e03480c27..fc0c7033c9 100644 --- a/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-static.html +++ b/testing/web-platform/tests/css/css-view-transitions/snapshot-containing-block-static.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: use snapshot containing block for static position</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="snapshot-containing-block-static-ref.html"> <meta name="fuzzy" content="snapshot-containing-block-static-ref.html:0-20;0-100"> diff --git a/testing/web-platform/tests/css/css-view-transitions/style-inheritance.html b/testing/web-platform/tests/css/css-view-transitions/style-inheritance.html index d0cef9be63..ed4705c56d 100644 --- a/testing/web-platform/tests/css/css-view-transitions/style-inheritance.html +++ b/testing/web-platform/tests/css/css-view-transitions/style-inheritance.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: ensure correct style inheritance for pseudo tree</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <script src="/resources/testharness.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/transition-in-empty-iframe-ref.html b/testing/web-platform/tests/css/css-view-transitions/transition-in-empty-iframe-ref.html index 6747612285..29e4c32e5b 100644 --- a/testing/web-platform/tests/css/css-view-transitions/transition-in-empty-iframe-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/transition-in-empty-iframe-ref.html @@ -2,7 +2,7 @@ <html> <head> <title>View transitions: Transition from an empty iframe (ref)</title> - <link rel="help" href="https://github.com/WICG/view-transitions"> + <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <style> iframe { diff --git a/testing/web-platform/tests/css/css-view-transitions/transition-in-empty-iframe.html b/testing/web-platform/tests/css/css-view-transitions/transition-in-empty-iframe.html index 869967a57e..7cd621fbfd 100644 --- a/testing/web-platform/tests/css/css-view-transitions/transition-in-empty-iframe.html +++ b/testing/web-platform/tests/css/css-view-transitions/transition-in-empty-iframe.html @@ -2,7 +2,7 @@ <html class=reftest-wait> <head> <title>View transitions: Transition from an empty iframe</title> - <link rel="help" href="https://github.com/WICG/view-transitions"> + <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <link rel="match" href="transition-in-empty-iframe-ref.html"> <meta name=fuzzy content="transition-in-empty-iframe-ref.html:0-80;0-1000"> diff --git a/testing/web-platform/tests/css/css-view-transitions/transition-in-hidden-page.html b/testing/web-platform/tests/css/css-view-transitions/transition-in-hidden-page.html new file mode 100644 index 0000000000..f23d30f96c --- /dev/null +++ b/testing/web-platform/tests/css/css-view-transitions/transition-in-hidden-page.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<html> + <title>View transitions: Transition in a hidden page</title> + <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> + <meta name="timeout" content="long"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-vendor.js"></script> + <script src="/page-visibility/resources/window_state_context.js"></script> + <style> + ::view-transition-group(*) { + animation-duration: 5s; + } + </style> + <script> + promise_test(async t => { + assert_implements(document.startViewTransition, "Missing document.startViewTransition"); + + const wsc = window_state_context(t); + await wsc.minimize(); + assert_true(document.hidden); + const transition = document.startViewTransition(); + await wsc.restore(); + await promise_rejects_dom(t, "InvalidStateError", transition.ready); + }, "A view transition should be immediately skipped if started when document is hidden"); + + promise_test(async t => { + assert_implements(document.startViewTransition, "Missing document.startViewTransition"); + + const wsc = window_state_context(t); + const transition = document.startViewTransition(async () => { + await wsc.minimize(); + }); + + let event_fired = false; + + window.addEventListener("visibilitychange", () => { + if (document.hidden) + event_fired = true; + }); + + // Restoring so that the document has an opportunity to present the real + // frame and start the transition's animation. + await wsc.restore(); + + const [readyResult] = await Promise.allSettled([transition.ready]); + assert_true(event_fired, "visibilitychange event should fire before skipping the transition"); + await transition.finished; + assert_equals(readyResult.status, "rejected"); + }, "A view transition should be skipped when a document becomes hidden while processing update callback"); + + promise_test(async t => { + assert_implements(document.startViewTransition, "Missing document.startViewTransition"); + assert_false(document.hidden); + const wsc = window_state_context(t); + const transition = document.startViewTransition(() => { }); + await transition.ready; + await new Promise(resolve => requestAnimationFrame(resolve)); + await wsc.minimize(); + const result = await new Promise(resolve => { + transition.finished.then(() => resolve("finished")); + t.step_timeout(() => resolve("timeout"), 1000); + }); + + assert_equals(result, "finished"); + }, "A view transition should be skipped when a document becomes hidden while animating"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-view-transitions/update-callback-timeout.html b/testing/web-platform/tests/css/css-view-transitions/update-callback-timeout.html new file mode 100644 index 0000000000..9e96e97e11 --- /dev/null +++ b/testing/web-platform/tests/css/css-view-transitions/update-callback-timeout.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<html> + <title>View transitions: Transition has implementation-defined timeout.</title> + <link rel="author" title="Tim Nguyen" href="https://github.com/nt1m"> + <link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> + <meta name="timeout" content="long"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script> + promise_test(async t => { + assert_implements(document.startViewTransition, "Missing document.startViewTransition"); + const transition = document.startViewTransition(() => { + return new Promise(() => {}); + }); + transition.updateCallbackDone.then(() => { + assert_unreached(); + }); + transition.finished.then(() => { + assert_unreached(); + }); + await promise_rejects_dom(t, "TimeoutError", transition.ready); + }, "View transition should have an implementation-defined timeout on the update callback"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/css-view-transitions/view-transition-name-on-document-root-ref.html b/testing/web-platform/tests/css/css-view-transitions/view-transition-name-on-document-root-ref.html index cc0250bc59..8b86e2a161 100644 --- a/testing/web-platform/tests/css/css-view-transitions/view-transition-name-on-document-root-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/view-transition-name-on-document-root-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: view-transition-name is limited to document root (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <head> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/view-transition-name-on-document-root.html b/testing/web-platform/tests/css/css-view-transitions/view-transition-name-on-document-root.html index afd5d56f61..a25b329b03 100644 --- a/testing/web-platform/tests/css/css-view-transitions/view-transition-name-on-document-root.html +++ b/testing/web-platform/tests/css/css-view-transitions/view-transition-name-on-document-root.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: view-transition-name is limited to document root</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="view-transition-name-on-document-root-ref.html"> <head> diff --git a/testing/web-platform/tests/css/css-view-transitions/view-transition-name-removed-mid-transition-ref.html b/testing/web-platform/tests/css/css-view-transitions/view-transition-name-removed-mid-transition-ref.html index 9ec14f60cd..e86fa3f3e2 100644 --- a/testing/web-platform/tests/css/css-view-transitions/view-transition-name-removed-mid-transition-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/view-transition-name-removed-mid-transition-ref.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <title>View transitions: view-transition-name removed mid transition (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <style> diff --git a/testing/web-platform/tests/css/css-view-transitions/view-transition-name-removed-mid-transition.html b/testing/web-platform/tests/css/css-view-transitions/view-transition-name-removed-mid-transition.html index b18df68511..265dc1fef0 100644 --- a/testing/web-platform/tests/css/css-view-transitions/view-transition-name-removed-mid-transition.html +++ b/testing/web-platform/tests/css/css-view-transitions/view-transition-name-removed-mid-transition.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: view-transition-name removed mid transition</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="view-transition-name-removed-mid-transition-ref.html"> diff --git a/testing/web-platform/tests/css/css-view-transitions/web-animations-api-ref.html b/testing/web-platform/tests/css/css-view-transitions/web-animations-api-ref.html index ab66e7d16d..d71d6e1375 100644 --- a/testing/web-platform/tests/css/css-view-transitions/web-animations-api-ref.html +++ b/testing/web-platform/tests/css/css-view-transitions/web-animations-api-ref.html @@ -1,6 +1,6 @@ <!DOCTYPE html> <title>View transitions: one element captured for two tags (ref)</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:vmpstr@chromium.org"> <style> div { diff --git a/testing/web-platform/tests/css/css-view-transitions/web-animations-api.html b/testing/web-platform/tests/css/css-view-transitions/web-animations-api.html index 6d8395ebb7..c739e416c8 100644 --- a/testing/web-platform/tests/css/css-view-transitions/web-animations-api.html +++ b/testing/web-platform/tests/css/css-view-transitions/web-animations-api.html @@ -1,7 +1,7 @@ <!DOCTYPE html> <html class=reftest-wait> <title>View transitions: capture opacity elements</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:khushalsagar@chromium.org"> <link rel="match" href="web-animations-api-ref.html"> <meta name="fuzzy" content="web-animations-api-ref.html:0-2;0-500"> diff --git a/testing/web-platform/tests/css/css-view-transitions/window-resize-aborts-transition-before-ready.html b/testing/web-platform/tests/css/css-view-transitions/window-resize-aborts-transition-before-ready.html index 6caadeba89..28abd8452d 100644 --- a/testing/web-platform/tests/css/css-view-transitions/window-resize-aborts-transition-before-ready.html +++ b/testing/web-platform/tests/css/css-view-transitions/window-resize-aborts-transition-before-ready.html @@ -2,7 +2,7 @@ <title> View transitions: Resizing viewport before animating rejects the ready promise. </title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <script src="/resources/testharness.js"></script> diff --git a/testing/web-platform/tests/css/css-view-transitions/window-resize-aborts-transition.html b/testing/web-platform/tests/css/css-view-transitions/window-resize-aborts-transition.html index e2424cad8c..fd83562316 100644 --- a/testing/web-platform/tests/css/css-view-transitions/window-resize-aborts-transition.html +++ b/testing/web-platform/tests/css/css-view-transitions/window-resize-aborts-transition.html @@ -2,7 +2,7 @@ <html> <head> <title>View transitions: Resizing viewport skips the transition</title> -<link rel="help" href="https://github.com/WICG/view-transitions"> +<link rel="help" href="https://drafts.csswg.org/css-view-transitions-1/"> <link rel="author" href="mailto:bokan@chromium.org"> <script src="/common/rendering-utils.js"></script> diff --git a/testing/web-platform/tests/css/css-viewport/computedStyle-zoom.html b/testing/web-platform/tests/css/css-viewport/computedStyle-zoom.html index 30ed78e30b..82af111bbe 100644 --- a/testing/web-platform/tests/css/css-viewport/computedStyle-zoom.html +++ b/testing/web-platform/tests/css/css-viewport/computedStyle-zoom.html @@ -5,23 +5,23 @@ <link rel="help" href="https://drafts.csswg.org/css-viewport/"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<head> - <style> - div { +<style> + div { width: 64px; height: 64px; + line-height: 64px; + text-indent: 64px; background-color: blue - } - div.x4_zoom { + } + div.x4_zoom { zoom: 4.0; background-color: blueviolet; - } - div.x2_zoom { + } + div.x2_zoom { background-color: chartreuse; zoom: 2.0; - } - - </style> + } +</style> </head> <body> <div id="no_zoom"></div> @@ -29,7 +29,7 @@ <div class="x2_zoom" id="parent_div"> <div class="x4_zoom" id="nested_zoom"></div> </div> - <div class="x2_zoom" id="testing_set_style" style="height: 1px; width: 1px;"></div> + <div class="x2_zoom" id="testing_set_style" style="height: 1px; width: 1px; line-height: 1px; text-indent: 1px;"></div> <script> test(function() { assert_true(!!no_zoom, "no zoom target exists"); @@ -37,38 +37,37 @@ assert_true(!!nested_zoom, "zoom target exists"); assert_true(!!parent_div, "parent div with zoom exists") }); + function assert_length_props(style, expected) { + for (let prop of ["width", "height", "line-height", "text-indent"]) { + assert_equals(style.getPropertyValue(prop), expected, prop); + } + } test(function(){ - noZoomStyle = window.getComputedStyle(no_zoom); - assert_equals(noZoomStyle.getPropertyValue("width"), "64px"); - assert_equals(noZoomStyle.getPropertyValue("height"), "64px"); + let noZoomStyle = getComputedStyle(no_zoom); + assert_length_props(noZoomStyle, "64px"); assert_equals(noZoomStyle.getPropertyValue("zoom"), "1"); }); test(function(){ - withZoomStyle = window.getComputedStyle(with_zoom); - assert_equals(withZoomStyle.getPropertyValue("width"), "64px"); - assert_equals(withZoomStyle.getPropertyValue("height"), "64px"); + let withZoomStyle = getComputedStyle(with_zoom); + assert_length_props(withZoomStyle, "64px"); assert_equals(withZoomStyle.getPropertyValue("zoom"), "4"); }); test(function(){ - parentWithZoomStyle = window.getComputedStyle(parent_div); - assert_equals(parentWithZoomStyle.getPropertyValue("width"), "64px"); - assert_equals(parentWithZoomStyle.getPropertyValue("height"), "64px"); + let parentWithZoomStyle = getComputedStyle(parent_div); + assert_length_props(parentWithZoomStyle, "64px"); assert_equals(parentWithZoomStyle.getPropertyValue("zoom"), "2"); }); test(function(){ - nestedZoomStyle = window.getComputedStyle(nested_zoom); - assert_equals(nestedZoomStyle.getPropertyValue("width"), "64px"); - assert_equals(nestedZoomStyle.getPropertyValue("height"), "64px"); + nestedZoomStyle = getComputedStyle(nested_zoom); + assert_length_props(nestedZoomStyle, "64px"); assert_equals(nestedZoomStyle.getPropertyValue("zoom"), "4"); }); test(function(){ - testDivStyle = window.getComputedStyle(testing_set_style); - assert_equals(testDivStyle.getPropertyValue("width"), "1px"); - assert_equals(testDivStyle.getPropertyValue("height"), "1px"); + testDivStyle = getComputedStyle(testing_set_style); + assert_length_props(testDivStyle, "1px"); assert_equals(testDivStyle.getPropertyValue("zoom"), "2"); - window.testing_set_style.setAttribute("style", "width: 64px; height: 64px;"); - assert_equals(testDivStyle.getPropertyValue("width"), "64px"); - assert_equals(testDivStyle.getPropertyValue("height"), "64px"); + testing_set_style.setAttribute("style", "width: 64px; height: 64px; line-height: 64px; text-indent: 64px;"); + assert_length_props(testDivStyle, "64px"); assert_equals(testDivStyle.getPropertyValue("zoom"), "2"); }); </script> diff --git a/testing/web-platform/tests/css/zoom/tentative/background-image-ref.html b/testing/web-platform/tests/css/css-viewport/zoom/background-image-ref.html index 6fe548f343..6fe548f343 100644 --- a/testing/web-platform/tests/css/zoom/tentative/background-image-ref.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/background-image-ref.html diff --git a/testing/web-platform/tests/css/zoom/tentative/background-image.html b/testing/web-platform/tests/css/css-viewport/zoom/background-image.html index db6baa60db..805dad187d 100644 --- a/testing/web-platform/tests/css/zoom/tentative/background-image.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/background-image.html @@ -1,5 +1,6 @@ <!doctype html> <title>Zoom affects background-image intrinsic sizes</title> +<link rel="help" href="https://drafts.csswg.org/css-viewport/#zoom-property"> <link rel="match" href="background-image-ref.html"> <style> div { diff --git a/testing/web-platform/tests/css/zoom/tentative/basic-ref.html b/testing/web-platform/tests/css/css-viewport/zoom/basic-ref.html index 5de90caf7f..5de90caf7f 100644 --- a/testing/web-platform/tests/css/zoom/tentative/basic-ref.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/basic-ref.html diff --git a/testing/web-platform/tests/css/zoom/tentative/basic.html b/testing/web-platform/tests/css/css-viewport/zoom/basic.html index fcd1761cfa..580d3c83cd 100644 --- a/testing/web-platform/tests/css/zoom/tentative/basic.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/basic.html @@ -3,6 +3,7 @@ <title>zoom property: basic test</title> <link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> <link rel="author" href="https://mozilla.com" title="Mozilla"> +<link rel="help" href="https://drafts.csswg.org/css-viewport/#zoom-property"> <link rel="match" href="basic-ref.html"> <style> div { diff --git a/testing/web-platform/tests/css/css-viewport/zoom/font-size-ref.html b/testing/web-platform/tests/css/css-viewport/zoom/font-size-ref.html new file mode 100644 index 0000000000..5714a878b3 --- /dev/null +++ b/testing/web-platform/tests/css/css-viewport/zoom/font-size-ref.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<style>CSS Test reference</style> +<div style="font-size: 12px"> + 12px text +</div> + +<hr> + +<div style="font-size: 24px;"> + 12px zoomed text +</div> + +<hr> + +<div style="font-size: 12px"> + <div style="font-size: 24px"> + 12px zoomed inherited text + </div> +</div> diff --git a/testing/web-platform/tests/css/css-viewport/zoom/font-size.html b/testing/web-platform/tests/css/css-viewport/zoom/font-size.html new file mode 100644 index 0000000000..e4b20c3c93 --- /dev/null +++ b/testing/web-platform/tests/css/css-viewport/zoom/font-size.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<style>CSS zoom applies to font-size when specified and inherited</style> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/css-viewport/"> +<link rel="match" href="font-size-ref.html"> +<div style="font-size: 12px"> + 12px text +</div> +<hr> +<div style="font-size: 12px; zoom: 2"> + 12px zoomed text +</div> +<hr> +<div style="font-size: 12px"> + <div style="zoom:2"> + 12px zoomed inherited text + </div> +</div> diff --git a/testing/web-platform/tests/css/zoom/tentative/green-square-100px.html b/testing/web-platform/tests/css/css-viewport/zoom/green-square-100px.html index 6677176230..6677176230 100644 --- a/testing/web-platform/tests/css/zoom/tentative/green-square-100px.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/green-square-100px.html diff --git a/testing/web-platform/tests/css/zoom/tentative/image-intrinsic-size.html b/testing/web-platform/tests/css/css-viewport/zoom/image-intrinsic-size.html index d152bb24ee..d152bb24ee 100644 --- a/testing/web-platform/tests/css/zoom/tentative/image-intrinsic-size.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/image-intrinsic-size.html diff --git a/testing/web-platform/tests/css/zoom/tentative/inherited-length.html b/testing/web-platform/tests/css/css-viewport/zoom/inherited-length.html index 2bc04ff0cb..d83111a435 100644 --- a/testing/web-platform/tests/css/zoom/tentative/inherited-length.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/inherited-length.html @@ -3,6 +3,7 @@ <title>zoom property: inherited length into zoom</title> <link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> <link rel="author" href="https://mozilla.com" title="Mozilla"> +<link rel="help" href="https://drafts.csswg.org/css-viewport/#zoom-property"> <link rel="match" href="green-square-100px.html"> <style> div { diff --git a/testing/web-platform/tests/css/zoom/tentative/inherited.html b/testing/web-platform/tests/css/css-viewport/zoom/inherited.html index baa7f7ed8a..2c8ad1694a 100644 --- a/testing/web-platform/tests/css/zoom/tentative/inherited.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/inherited.html @@ -3,6 +3,7 @@ <title>Effective zoom value is inherited</title> <link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez"> <link rel="author" href="https://mozilla.com" title="Mozilla"> +<link rel="help" href="https://drafts.csswg.org/css-viewport/#zoom-property"> <link rel="match" href="basic-ref.html"> <style> .container { diff --git a/testing/web-platform/tests/css/css-viewport/line-height-ref.html b/testing/web-platform/tests/css/css-viewport/zoom/line-height-ref.html index c75539243c..c75539243c 100644 --- a/testing/web-platform/tests/css/css-viewport/line-height-ref.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/line-height-ref.html diff --git a/testing/web-platform/tests/css/css-viewport/line-height.html b/testing/web-platform/tests/css/css-viewport/zoom/line-height.html index fa333be32f..fa333be32f 100644 --- a/testing/web-platform/tests/css/css-viewport/line-height.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/line-height.html diff --git a/testing/web-platform/tests/css/zoom/tentative/parsing/zoom-computed.html b/testing/web-platform/tests/css/css-viewport/zoom/parsing/zoom-computed.html index 3737901490..41fb1b24ae 100644 --- a/testing/web-platform/tests/css/zoom/tentative/parsing/zoom-computed.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/parsing/zoom-computed.html @@ -1,6 +1,7 @@ <!DOCTYPE html> <meta charset="utf-8"> <title>CSS Test: getComputedStyle().zoom</title> +<link rel="help" href="https://drafts.csswg.org/css-viewport/#zoom-property"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/css/support/computed-testcommon.js"></script> diff --git a/testing/web-platform/tests/css/zoom/tentative/parsing/zoom-valid.html b/testing/web-platform/tests/css/css-viewport/zoom/parsing/zoom-valid.html index 3db16e0748..3f36508b8b 100644 --- a/testing/web-platform/tests/css/zoom/tentative/parsing/zoom-valid.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/parsing/zoom-valid.html @@ -3,6 +3,7 @@ <title>CSS Test: parsing zoom with valid and invalid values</title> <link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> <link rel="author" title="Mozilla" href="https://mozilla.com"> +<link rel="help" href="https://drafts.csswg.org/css-viewport/#zoom-property"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/css/support/parsing-testcommon.js"></script> diff --git a/testing/web-platform/tests/css/css-viewport/zoom/relative-units-from-parent-ref.html b/testing/web-platform/tests/css/css-viewport/zoom/relative-units-from-parent-ref.html new file mode 100644 index 0000000000..4e7456ce71 --- /dev/null +++ b/testing/web-platform/tests/css/css-viewport/zoom/relative-units-from-parent-ref.html @@ -0,0 +1,4 @@ +<!doctype html> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width"> +<div style="font-size: 4em; line-height: 4lh">ABC</div> diff --git a/testing/web-platform/tests/css/css-viewport/zoom/relative-units-from-parent.html b/testing/web-platform/tests/css/css-viewport/zoom/relative-units-from-parent.html new file mode 100644 index 0000000000..57df82b6f1 --- /dev/null +++ b/testing/web-platform/tests/css/css-viewport/zoom/relative-units-from-parent.html @@ -0,0 +1,13 @@ +<!doctype html> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1892676"> +<link rel="help" href="https://drafts.csswg.org/css-viewport/#zoom-property"> +<link rel="match" href="relative-units-from-parent-ref.html"> +<!-- + The font-size and line-height use the parent sizes, but should still + multiply by our own zoom. +--> +<div style="zoom: 2; font-size: 2em; line-height: 2lh">ABC</div> diff --git a/testing/web-platform/tests/css/css-viewport/zoom/relative-units.html b/testing/web-platform/tests/css/css-viewport/zoom/relative-units.html new file mode 100644 index 0000000000..8cfa27c93a --- /dev/null +++ b/testing/web-platform/tests/css/css-viewport/zoom/relative-units.html @@ -0,0 +1,42 @@ +<!doctype html> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width"> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1892676"> +<link rel="help" href="https://drafts.csswg.org/css-viewport/#zoom-property"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + :root, + #zoomed { + font-size: 10px; + line-height: 10px; + zoom: 2; + } +</style> +<div id="outside"></div> +<div id="zoomed"> + <div id="inside"></div> +</div> +<script> + function test_unit(unit, outside, zoomed, inside = zoomed) { + test(function() { + let values = { outside, zoomed, inside }; + for (let id of ["outside", "zoomed", "inside"]) { + let el = document.getElementById(id); + el.style.height = "1" + unit; + // approx_equals is needed because innerHeight / innerWidth round. + assert_approx_equals(el.getBoundingClientRect().height, values[id], 1, `${unit} in ${id}`); + el.style.height = ""; + } + }); + } + + test_unit("em", 20, 40); + test_unit("rem", 20, 40); + test_unit("lh", 20, 40); + test_unit("rlh", 20, 40); + test_unit("vh", 2 * innerHeight / 100, 4 * innerHeight / 100); + test_unit("vw", 2 * innerWidth / 100, 4 * innerWidth / 100); +</script> diff --git a/testing/web-platform/tests/css/zoom/tentative/scroll-corner-crash.html b/testing/web-platform/tests/css/css-viewport/zoom/scroll-corner-crash.html index 0ec88deb74..0ec88deb74 100644 --- a/testing/web-platform/tests/css/zoom/tentative/scroll-corner-crash.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/scroll-corner-crash.html diff --git a/testing/web-platform/tests/css/zoom/tentative/scrollbar-crash.html b/testing/web-platform/tests/css/css-viewport/zoom/scrollbar-crash.html index 791022407f..791022407f 100644 --- a/testing/web-platform/tests/css/zoom/tentative/scrollbar-crash.html +++ b/testing/web-platform/tests/css/css-viewport/zoom/scrollbar-crash.html diff --git a/testing/web-platform/tests/css/css-writing-modes/forms/select-multiple-options-visual-order.html b/testing/web-platform/tests/css/css-writing-modes/forms/select-multiple-options-visual-order.html index 359c09279e..e3ae8a27a9 100644 --- a/testing/web-platform/tests/css/css-writing-modes/forms/select-multiple-options-visual-order.html +++ b/testing/web-platform/tests/css/css-writing-modes/forms/select-multiple-options-visual-order.html @@ -29,6 +29,7 @@ for (const writingMode of ["horizontal-tb", "vertical-lr", "vertical-rl", "sidew select.style.writingMode = writingMode; this.add_cleanup(() => { select.removeAttribute("style"); + select.value = ""; }); const elementBox = select.getBoundingClientRect(); diff --git a/testing/web-platform/tests/css/cssom-view/Element-currentCSSZoom.html b/testing/web-platform/tests/css/cssom-view/Element-currentCSSZoom.html new file mode 100644 index 0000000000..2f477e8e2d --- /dev/null +++ b/testing/web-platform/tests/css/cssom-view/Element-currentCSSZoom.html @@ -0,0 +1,32 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Element.currentCSSZoom</title> +<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io"> +<link rel="author" title="Mozilla" href="https://mozilla.org"> +<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-currentcsszoom"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<div id="unzoomed"> + <div id="unzoomedChild"></div> +</div> +<div style="zoom: 2" id="outer"> + <div style="zoom: 2" id="inner"> + <div id="renderedChild"></div> + <div style="display: none" id="nonRenderedChild"></div> + </div> +</div> +<script> +test(() => { + assert_equals(unzoomed.currentCSSZoom, 1, "Unzoomed content"); + assert_equals(outer.currentCSSZoom, 2, "Zoomed content"); + assert_equals(inner.currentCSSZoom, 4, "Effective zoom gets multiplied properly"); + assert_equals(renderedChild.currentCSSZoom, 4, "Effective zoom gets propagated to children"); + assert_equals(nonRenderedChild.currentCSSZoom, 1, "Non-rendered elements return 1 for currentCSSZoom"); +}, "Element.currentCSSZoom basic test"); + +test(() => { + unzoomed.style.zoom = 2; + assert_equals(unzoomed.currentCSSZoom, 2, "currentCSSZoom reacts to style changes"); + assert_equals(unzoomedChild.currentCSSZoom, 2, "currentCSSZoom propagates to descendants after style changes"); +}, "Element.currentCSSZoom reacts to style changes"); +</script> diff --git a/testing/web-platform/tests/css/cssom-view/WEB_FEATURES.yml b/testing/web-platform/tests/css/cssom-view/WEB_FEATURES.yml new file mode 100644 index 0000000000..a545dbadcb --- /dev/null +++ b/testing/web-platform/tests/css/cssom-view/WEB_FEATURES.yml @@ -0,0 +1,5 @@ +features: +- name: scroll-into-view + files: + - scrollIntoView-* + - scrollintoview.html diff --git a/testing/web-platform/tests/css/cssom-view/range-bounding-client-rect-with-nested-text.html b/testing/web-platform/tests/css/cssom-view/range-bounding-client-rect-with-nested-text.html new file mode 100644 index 0000000000..78ca5421df --- /dev/null +++ b/testing/web-platform/tests/css/cssom-view/range-bounding-client-rect-with-nested-text.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>All the rectangles for the text nodes must included in getClientRects</title> +<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-range-getclientrects"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + .nullDims { + width: 0; + height: 0; + } + + .nullDims > div { + position: absolute; + left: 10px; + } +</style> +<div id="container"> + <div class="nullDims"> + <div id="first" style="top: 10px">Hello</div> + </div> + <div class="nullDims"> + <div style="top: 40px">Firefox</div> + </div> + <div class="nullDims"> + <div style="top: 70px">Firefox again</div> + </div> + <div class="nullDims"> + <div id="last" style="top: 100px">World</div> + </div> +</div> +<script> + test(function () { + const first = document.getElementById("first"); + const last = document.getElementById("last"); + const range = document.createRange(); + range.setStart(first.firstChild, 0); + range.setEnd(last.firstChild, 5); + + const selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + let rects = Array.from(range.getClientRects()); + assert_equals(rects.length, 6, "Number of rectangles"); + rects = rects.filter(({ width, height }) => width > 0 && height > 0); + assert_equals(rects.length, 4, "Number of non-empty rectangles"); + }, "getClientRects should return non-empty rectangles for nested text nodes") +</script> diff --git a/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-setter.window.js b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-setter.window.js new file mode 100644 index 0000000000..4474358ed0 --- /dev/null +++ b/testing/web-platform/tests/css/cssom/cssstyledeclaration-csstext-setter.window.js @@ -0,0 +1,64 @@ +// https://drafts.csswg.org/cssom/#dom-cssstyledeclaration-csstext + +[ + document.body, + document.createElement("cool-beans") +].forEach(element => { + test(t => { + t.add_cleanup(() => element.removeAttribute("style")); + + element.style.background = "red"; + assert_equals(element.getAttribute("style"), "background: red;"); + + element.style.cssText = "background:red"; + assert_equals(element.getAttribute("style"), "background: red;"); + }, `cssText setter should set style attribute even when there are no style changes (${element.localName})`); + + test(t => { + t.add_cleanup(() => element.removeAttribute("style")); + + element.setAttribute("style", "background: red"); + assert_equals(element.getAttribute("style"), "background: red"); + + element.style.cssText = "background:red"; + assert_equals(element.getAttribute("style"), "background: red;"); + }, `cssText setter should set style attribute even when there are no style changes, part 2 (${element.localName})`); + + test(t => { + t.add_cleanup(() => element.removeAttribute("style")); + + element.setAttribute("style", "background: red"); + assert_equals(element.getAttribute("style"), "background: red"); + + element.style.cssText = "background:red "; // trailing space + assert_equals(element.getAttribute("style"), "background: red;"); + }, `cssText setter should set style attribute even when there are no style changes, part 3 (${element.localName})`); + + test(t => { + t.add_cleanup(() => element.removeAttribute("style")); + + element.setAttribute("style", "background: red"); + assert_equals(element.getAttribute("style"), "background: red"); + + element.style.cssText = "background:red;"; + assert_equals(element.getAttribute("style"), "background: red;"); + }, `cssText setter should set style attribute even when there are no style changes, part 4 (${element.localName})`); +}); + +test(t => { + const style = document.createElement("style"); + t.add_cleanup(() => { + document.body.removeAttribute("style"); + style.remove(); + }); + style.textContent = `[style="background: red;"] { background:white !important; }`; + document.body.appendChild(style); + + document.body.setAttribute("style", "background:red"); + assert_true(document.body.matches("[style=\"background:red\"]")); + assert_equals(getComputedStyle(document.body).backgroundColor, "rgb(255, 0, 0)"); + + document.body.style.cssText = "background:red"; + assert_equals(getComputedStyle(document.body).backgroundColor, "rgb(255, 255, 255)"); + assert_true(document.body.matches("[style=\"background: red;\"]")); +}, `cssText setter and selector invalidation`); diff --git a/testing/web-platform/tests/css/filter-effects/WEB_FEATURES.yml b/testing/web-platform/tests/css/filter-effects/WEB_FEATURES.yml new file mode 100644 index 0000000000..a882be3079 --- /dev/null +++ b/testing/web-platform/tests/css/filter-effects/WEB_FEATURES.yml @@ -0,0 +1,7 @@ +features: +- name: backdrop-filter + files: + - backdrop-filter-* + - backdrop-filters-* + - css-backdrop-filters-* + - repaint-added-backdrop-filter.html diff --git a/testing/web-platform/tests/css/filter-effects/animation/WEB_FEATURES.yml b/testing/web-platform/tests/css/filter-effects/animation/WEB_FEATURES.yml new file mode 100644 index 0000000000..cd7e6695d4 --- /dev/null +++ b/testing/web-platform/tests/css/filter-effects/animation/WEB_FEATURES.yml @@ -0,0 +1,4 @@ +features: +- name: backdrop-filter + files: + - backdrop-filter-* diff --git a/testing/web-platform/tests/css/filter-effects/parsing/WEB_FEATURES.yml b/testing/web-platform/tests/css/filter-effects/parsing/WEB_FEATURES.yml new file mode 100644 index 0000000000..cd7e6695d4 --- /dev/null +++ b/testing/web-platform/tests/css/filter-effects/parsing/WEB_FEATURES.yml @@ -0,0 +1,4 @@ +features: +- name: backdrop-filter + files: + - backdrop-filter-* diff --git a/testing/web-platform/tests/css/motion/animation/offset-path-interpolation-008.html b/testing/web-platform/tests/css/motion/animation/offset-path-interpolation-008.html new file mode 100644 index 0000000000..30d789f4eb --- /dev/null +++ b/testing/web-platform/tests/css/motion/animation/offset-path-interpolation-008.html @@ -0,0 +1,264 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <title>offset-path interpolation with allow-discrete</title> + <link rel="help" href="https://drafts.fxtf.org/motion-1/#offset-path-property"> + <link rel="help" href="https://drafts.csswg.org/css-shapes-2/#interpolating-shape"> + <meta name="assert" content="offset-path:shape() supports animation."> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="/css/support/interpolation-testcommon.js"></script> + </head> + <style> + html { + font-size: 16px; + } + .parent { + offset-path: shape(from -5px 5px, move to 5% 1px); + } + .target { + offset-path: shape(from 5px 5px, line to 10px 10%); + } + </style> + <body> + <script> + 'use strict'; + + test_interpolation({ + property: 'offset-path', + from: neutralKeyframe, + to: 'shape(from -5px 5px, line to 20px 20%)', + }, [ + {at: -0.3, expect: 'shape(from 8px 5px, line to 7px 7%)'}, + {at: 0, expect: 'shape(from 5px 5px, line to 10px 10%)'}, + {at: 0.6, expect: 'shape(from -1px 5px, line to 16px 16%)'}, + {at: 1, expect: 'shape(from -5px 5px, line to 20px 20%)'}, + {at: 1.5, expect: 'shape(from -10px 5px, line to 25px 25%)'}, + ]); + + test_no_interpolation({ + property: 'offset-path', + from: 'initial', + to: 'shape(from 8px 5px, line to 7px 7%)', + }); + + test_interpolation({ + property: 'offset-path', + from: 'inherit', + to: 'shape(from 15% 15px, move to 20% -10px)', + }, [ + {at: -0.3, expect: 'shape(from calc(-4.5% - 6.5px) 2px, move to 0.5% 4.3px)'}, + {at: 0, expect: 'shape(from calc(0% - 5px) 5px, move to 5% 1px)'}, + {at: 0.5, expect: 'shape(from calc(7.5% - 2.5px) 10px, move to 12.5% -4.5px)'}, + {at: 1, expect: 'shape(from 15% 15px, move to 20% -10px'}, + {at: 1.5, expect: 'shape(from calc(22.5% + 2.5px) 20px, move to 27.5% -15.5px)'}, + ]); + + test_no_interpolation({ + property: 'offset-path', + from: 'unset', + to: 'shape(from 10px 10px, close)', + }); + + test_no_interpolation({ + property: 'offset-path', + from: 'none', + to: 'shape(from 10px 10px, close)', + }); + + test_no_interpolation({ + property: 'offset-path', + from: 'shape(from 10px 10px, move to 10% 10%)', + to: 'shape(from 10px 10px, close)', + }); + + test_interpolation({ + property: 'offset-path', + from: 'shape(from 5% 5px, hline to 5%, vline to -5px, close)', + to: 'shape(from 15% 15px, hline to 25%, vline to -15px, close)', + }, [ + {at: -0.3, expect: 'shape(from 2% 2px, hline to -1%, vline to -2px, close)'}, + {at: 0, expect: 'shape(from 5% 5px, hline to 5%, vline to -5px, close)'}, + {at: 0.5, expect: 'shape(from 10% 10px, hline to 15% , vline to -10px, close)'}, + {at: 1, expect: 'shape(from 15% 15px, hline to 25%, vline to -15px, close)'}, + {at: 1.5, expect: 'shape(from 20% 20px, hline to 35%, vline to -20px, close)'}, + ]); + + test_interpolation({ + property: 'offset-path', + from: 'shape(from 5% 5px, curve to 10% 10px via 0% 80px, curve to 30% 20px via 20% 50px 25% 70px)', + to: 'shape(from 15% 15px, curve to 20% 0px via 10% 60px, curve to 20% 30px via 30% 40px -5% 100px)', + }, [ + {at: -0.3, expect: 'shape(from 2% 2px, curve to 7% 13px via -3% 86px, curve to 33% 17px via 17% 53px 34% 61px)'}, + {at: 0, expect: 'shape(from 5% 5px, curve to 10% 10px via 0% 80px, curve to 30% 20px via 20% 50px 25% 70px)'}, + {at: 0.5, expect: 'shape(from 10% 10px, curve to 15% 5px via 5% 70px, curve to 25% 25px via 25% 45px 10% 85px)'}, + {at: 1, expect: 'shape(from 15% 15px, curve to 20% 0px via 10% 60px, curve to 20% 30px via 30% 40px -5% 100px)'}, + {at: 1.5, expect: 'shape(from 20% 20px, curve to 25% -5px via 15% 50px, curve to 15% 35px via 35% 35px -20% 115px)'}, + ]); + + test_interpolation({ + property: 'offset-path', + from: 'shape(from 5% 5px, curve by 10% 10px via 0% 80px, curve by 30% 20px via 20% 50px 25% 70px)', + to: 'shape(from 15% 15px, curve by 20% 0px via 10% 60px, curve by 20% 30px via 30% 40px -5% 100px)', + }, [ + {at: -0.3, expect: 'shape(from 2% 2px, curve by 7% 13px via -3% 86px, curve by 33% 17px via 17% 53px 34% 61px)'}, + {at: 0, expect: 'shape(from 5% 5px, curve by 10% 10px via 0% 80px, curve by 30% 20px via 20% 50px 25% 70px)'}, + {at: 0.5, expect: 'shape(from 10% 10px, curve by 15% 5px via 5% 70px, curve by 25% 25px via 25% 45px 10% 85px)'}, + {at: 1.5, expect: 'shape(from 20% 20px, curve by 25% -5px via 15% 50px, curve by 15% 35px via 35% 35px -20% 115px)'}, + ]); + + test_interpolation({ + property: 'offset-path', + from: 'shape(from 5% 5px, smooth to 10% 10px via 0% 80px, smooth to 30% 20px)', + to: 'shape(from 15% 15px, smooth to 20% 0px via 10% 60px, smooth to 20% 30px)', + }, [ + {at: -0.3, expect: 'shape(from 2% 2px, smooth to 7% 13px via -3% 86px, smooth to 33% 17px)'}, + {at: 0, expect: 'shape(from 5% 5px, smooth to 10% 10px via 0% 80px, smooth to 30% 20px)'}, + {at: 0.5, expect: 'shape(from 10% 10px, smooth to 15% 5px via 5% 70px, smooth to 25% 25px)'}, + {at: 1.5, expect: 'shape(from 20% 20px, smooth to 25% -5px via 15% 50px, smooth to 15% 35px)'}, + ]); + + test_interpolation({ + property: 'offset-path', + from: 'shape(from 5% 5px, smooth by 10% 10px via 0% 80px, smooth by 30% 20px)', + to: 'shape(from 15% 15px, smooth by 20% 0px via 10% 60px, smooth by 20% 30px)', + }, [ + {at: -0.3, expect: 'shape(from 2% 2px, smooth by 7% 13px via -3% 86px, smooth by 33% 17px)'}, + {at: 0, expect: 'shape(from 5% 5px, smooth by 10% 10px via 0% 80px, smooth by 30% 20px)'}, + {at: 0.5, expect: 'shape(from 10% 10px, smooth by 15% 5px via 5% 70px, smooth by 25% 25px)'}, + {at: 1.5, expect: 'shape(from 20% 20px, smooth by 25% -5px via 15% 50px, smooth by 15% 35px)'}, + ]); + + test_interpolation({ + property: 'offset-path', + from: 'shape(from 5% 5px, arc to 15% -15px of 10px 20px, arc by 15% -5px of 30px cw rotate 30deg large, arc to 25% 20px of 10px 5px small)', + to: 'shape(from 15% 15px, arc to 5% -25px of 20px 30px, arc by 25% -15px of 20px cw rotate 270deg small, arc to 25% 20px of 10px 5px small cw)' + }, [ + {at: -0.3, expect: 'shape(from 2% 2px, arc to 18% -12px of 7px 17px ccw small, arc by 12% -2px of 33px 33px rotate -42deg cw large , arc to 25% 20px of 10px 5px ccw small)'}, + {at: 0, expect: 'shape(from 5% 5px, arc to 15% -15px of 10px 20px, arc by 15% -5px of 30px cw rotate 30deg large, arc to 25% 20px of 10px 5px small)'}, + {at: 0.3, expect: 'shape(from 8% 8px, arc to 12% -18px of 13px 23px ccw small, arc by 18% -8px of 27px 27px rotate 102deg cw large, arc to 25% 20px of 10px 5px ccw small )'}, + {at: 0.5, expect: 'shape(from 10% 10px, arc to 10% -20px of 15px 25px ccw small, arc by 20% -10px of 25px rotate 150deg cw large, arc to 25% 20px of 10px 5px cw small)'}, + {at: 1, expect: 'shape(from 15% 15px, arc to 5% -25px of 20px 30px, arc by 25% -15px of 20px rotate 270deg cw small, arc to 25% 20px of 10px 5px cw small)'}, + {at: 1.5, expect: 'shape(from 20% 20px, arc to 0% -30px of 25px 35px ccw small, arc by 30% -20px of 15px rotate 390deg cw small, arc to 25% 20px of 10px 5px cw small)'}, + ]); + + test_interpolation({ + property: 'offset-path', + from: 'shape(from 5px -5%, hline to 10px, vline by 10rem, hline by 8.25px, close, vline by 3pt)', + to: 'shape(from -5px 5px, hline to 20px, vline by 10%, hline by 1em, close, vline by 6pt)', + }, [ + {at: -0.3, expect: 'shape(from 8px calc(-6.5% - 1.5px), hline to 7px, vline by calc(-3% + 208px), hline by 5.92px, close, vline by 2.8px)'}, + {at: 0, expect: 'shape(from 5px -5%, hline to 10px, vline by calc(0% + 160px), hline by 8.25px, close, vline by 4px)'}, + {at: 0.6, expect: 'shape(from -1px calc(-2% + 3px), hline to 16px, vline by calc(6% + 64px), hline by 12.9px, close ,vline by 6.4px)'}, + {at: 1, expect: 'shape(from -5px calc(0% + 5px), hline to 20px, vline by 10%, hline by 16px, close, vline by 8px)'}, + {at: 1.5, expect: 'shape(from -10px calc(2.5% + 7.5px), hline to 25px, vline by calc(15% - 80px), hline by 19.88px, close, vline by 10px)'}, + ]); + + test_no_interpolation({ + property: 'offset-path', + from: 'shape(from 10px 10px, move to 10% 10%)', + to: 'path("M10 10 z")', + }); + + test_no_interpolation({ + property: 'offset-path', + from: 'path("M10 10 M10 10")', + to: 'shape(from 10px 10px, close)', + }); + + test_no_interpolation({ + property: 'offset-path', + from: 'path("M10 10 h 5")', + to: 'shape(from 10px 10px, hline to 5px)', + }); + + test_interpolation({ + property: 'offset-path', + from: 'shape(from 5px 5px, hline to 5px, vline to -5px, close)', + to: 'path("M 15 15 H 25 V -15 Z")', + }, [ + {at: -0.3, expect: 'shape(from 2px 2px, hline to -1px, vline to -2px, close)'}, + {at: 0, expect: 'shape(from 5px 5px, hline to 5px, vline to -5px, close)'}, + {at: 0.5, expect: 'shape(from 10px 10px, hline to 15px, vline to -10px, close)'}, + {at: 1, expect: 'shape(from 15px 15px, hline to 25px, vline to -15px, close)'}, + {at: 1.5, expect: 'shape(from 20px 20px, hline to 35px, vline to -20px, close)'}, + ]); + + test_interpolation({ + property: 'offset-path', + from: 'shape(from 5% 5px, curve to 10% 10px via 0% 80px, curve to 30% 20px via 20% 50px 25% 70px)', + to: 'path("M 15 15 Q 10 60 20 0 C 30 40 -5 100 20 30")', + }, [ + {at: -0.3, expect: 'shape(from calc(6.5% - 4.5px) 2px, curve to calc(13% - 6px) 13px via calc(0% - 3px) 86px, curve to calc(39% - 6px) 17px via calc(26% - 9px) 53px calc(32.5% + 1.5px) 61px)'}, + {at: 0, expect: 'shape(from 5% 5px, curve to 10% 10px via 0% 80px, curve to 30% 20px via 20% 50px 25% 70px)'}, + {at: 0.5, expect: 'shape(from calc(2.5% + 7.5px) 10px, curve to calc(5% + 10px) 5px via calc(0% + 5px) 70px, curve to calc(15% + 10px) 25px via calc(10% + 15px) 45px calc(12.5% - 2.5px) 85px)'}, + {at: 1, expect: 'shape(from calc(0% + 15px) 15px, curve to calc(0% + 20px) 0px via calc(0% + 10px) 60px, curve to calc(0% + 20px) 30px via calc(0% + 30px) 40px calc(0% - 5px) 100px)'}, + {at: 1.5, expect: 'shape(from calc(-2.5% + 22.5px) 20px, curve to calc(-5% + 30px) -5px via calc(0% + 15px) 50px, curve to calc(-15% + 30px) 35px via calc(-10% + 45px) 35px calc(-12.5% - 7.5px) 115px)'}, + ]); + + test_interpolation({ + property: 'offset-path', + from: 'path("M 5 5 q 0 80 10 10 c 20 50 25 70 30 20")', + to: 'shape(from 15% 15px, curve by 20% 0px via 10% 60px, curve by 20% 30px via 30% 40px -5% 100px)', + }, [ + {at: -0.3, expect: 'shape(from calc(-4.5% + 6.5px) 2px, curve by calc(-6% + 13px) 13px via -3% 86px, curve by calc(-6% + 39px) 17px via calc(-9% + 26px) 53px calc(1.5% + 32.5px) 61px)'}, + {at: 0, expect: 'shape(from calc(0% + 5px) 5px, curve by calc(0% + 10px) 10px via 0% 80px, curve by calc(0% + 30px) 20px via calc(0% + 20px) 50px calc(0% + 25px) 70px)'}, + {at: 0.5, expect: 'shape(from calc(7.5% + 2.5px) 10px, curve by calc(10% + 5px) 5px via 5% 70px, curve by calc(10% + 15px) 25px via calc(15% + 10px) 45px calc(-2.5% + 12.5px) 85px)'}, + {at: 1, expect: 'shape(from 15% 15px, curve by 20% 0px via 10% 60px, curve by 20% 30px via 30% 40px -5% 100px)'}, + {at: 1.5, expect: 'shape(from calc(22.5% - 2.5px) 20px, curve by calc(30% - 5px) -5px via 15% 50px, curve by calc(30% - 15px) 35px via calc(45% - 10px) 35px calc(-7.5% - 12.5px) 115px)'}, + ]); + + test_interpolation({ + property: 'offset-path', + from: 'shape(from 5% 5px, smooth to 10% 10px via 0% 80px, smooth to 30% 20px)', + to: 'path("M 15 15 S 10 60 20 0 T 20 30")', + }, [ + {at: -0.3, expect: 'shape(from calc(6.5% - 4.5px) 2px, smooth to calc(13% - 6px) 13px via calc(0% - 3px) 86px, smooth to calc(39% - 6px) 17px)'}, + {at: 0, expect: 'shape(from 5% 5px, smooth to 10% 10px via 0% 80px, smooth to 30% 20px)'}, + {at: 0.5, expect: 'shape(from calc(2.5% + 7.5px) 10px, smooth to calc(5% + 10px) 5px via calc(0% + 5px) 70px, smooth to calc(15% + 10px) 25px)'}, + {at: 1, expect: 'shape(from calc(0% + 15px) 15px, smooth to calc(0% + 20px) 0px via calc(0% + 10px) 60px, smooth to calc(0% + 20px) 30px)'}, + {at: 1.5, expect: 'shape(from calc(-2.5% + 22.5px) 20px, smooth to calc(-5% + 30px) -5px via calc(0% + 15px) 50px, smooth to calc(-15% + 30px) 35px)'}, + ]); + + test_interpolation({ + property: 'offset-path', + from: 'path("M 5 5 s 0 80 10 10 t 30 20")', + to: 'shape(from 15px 15px, smooth by 20px 0px via 10px 60px, smooth by 20px 30px)', + }, [ + {at: -0.3, expect: 'shape(from 2px 2px, smooth by 7px 13px via -3px 86px, smooth by 33px 17px)'}, + {at: 0, expect: 'shape(from 5px 5px, smooth by 10px 10px via 0px 80px, smooth by 30px 20px)'}, + {at: 0.5, expect: 'shape(from 10px 10px, smooth by 15px 5px via 5px 70px, smooth by 25px 25px)'}, + {at: 1, expect: 'shape(from 15px 15px, smooth by 20px 0px via 10px 60px, smooth by 20px 30px)'}, + {at: 1.5, expect: 'shape(from 20px 20px, smooth by 25px -5px via 15px 50px, smooth by 15px 35px)'}, + ]); + + test_interpolation({ + property: 'offset-path', + from: 'shape(from 5% 5px, arc to 15% -15px of 10px 20px, arc by 15% -5px of 30px cw rotate 30deg large, arc to 25% 20px of 10px 5px small)', + to: 'path("M 15 15 A 20,30 0 0,0 5,-25 a 20,20 270 0,1 25,-15 A 10,5 0 0,0 25 20")', + }, [ + {at: -0.3, expect: 'shape(from calc(6.5% - 4.5px) 2px, arc to calc(19.5% - 1.5px) -12px of 7px 17px, arc by calc(19.5% - 7.5px) -2px of 33px cw large rotate -42deg, arc to calc(32.5% - 7.5px) 20px of 10px 5px)'}, + {at: 0, expect: 'shape(from 5% 5px, arc to 15% -15px of 10px 20px, arc by 15% -5px of 30px cw rotate 30deg large, arc to 25% 20px of 10px 5px small)'}, + {at: 0.3, expect: 'shape(from calc(3.5% + 4.5px) 8px, arc to calc(10.5% + 1.5px) -18px of 13px 23px, arc by calc(10.5% + 7.5px) -8px of 27px cw large rotate 102deg, arc to calc(17.5% + 7.5px) 20px of 10px 5px)'}, + {at: 0.5, expect: 'shape(from calc(2.5% + 7.5px) 10px, arc to calc(7.5% + 2.5px) -20px of 15px 25px, arc by calc(7.5% + 12.5px) -10px of 25px cw large rotate 150deg, arc to calc(12.5% + 12.5px) 20px of 10px 5px)'}, + {at: 1, expect: 'shape(from calc(0% + 15px) 15px, arc to calc(0% + 5px) -25px of 20px 30px, arc by calc(0% + 25px) -15px of 20px cw rotate 270deg, arc to calc(0% + 25px) 20px of 10px 5px)'}, + {at: 1.5, expect: 'shape(from calc(-2.5% + 22.5px) 20px, arc to calc(-7.5% + 7.5px) -30px of 25px 35px, arc by calc(-7.5% + 37.5px) -20px of 15px cw rotate 390deg, arc to calc(-12.5% + 37.5px) 20px of 10px 5px)'}, + ]); + + test_interpolation({ + property: 'offset-path', + from: 'path("M 5 5 A 10,20 0 0,0 15,-15 a 30,30 30 1,1 15,-5 A 10,5 0 0,0 25 20")', + to: 'shape(from 15px 15px, arc to 5px -25px of 20px 30px, arc by 25px -15px of 20px cw rotate 270deg small, arc to 25px 20px of 10px 5px small cw)' + }, [ + {at: -0.3, expect: 'shape(from 2px 2px, arc to 18px -12px of 7px 17px ccw small, arc by 12px -2px of 33px 33px rotate -42deg cw large , arc to 25px 20px of 10px 5px ccw small)'}, + {at: 0, expect: 'shape(from 5px 5px, arc to 15px -15px of 10px 20px, arc by 15px -5px of 30px cw rotate 30deg large, arc to 25px 20px of 10px 5px small)'}, + {at: 0.3, expect: 'shape(from 8px 8px, arc to 12px -18px of 13px 23px ccw small, arc by 18px -8px of 27px 27px rotate 102deg cw large, arc to 25px 20px of 10px 5px ccw small )'}, + {at: 0.5, expect: 'shape(from 10px 10px, arc to 10px -20px of 15px 25px ccw small, arc by 20px -10px of 25px rotate 150deg cw large, arc to 25px 20px of 10px 5px cw small)'}, + {at: 1, expect: 'shape(from 15px 15px, arc to 5px -25px of 20px 30px, arc by 25px -15px of 20px rotate 270deg cw small, arc to 25px 20px of 10px 5px cw small)'}, + {at: 1.5, expect: 'shape(from 20px 20px, arc to 0px -30px of 25px 35px ccw small, arc by 30px -20px of 15px rotate 390deg cw small, arc to 25px 20px of 10px 5px cw small)'}, + ]); + + </script> + </body> +</html> diff --git a/testing/web-platform/tests/css/motion/offset-path-shape-shape-001-ref.html b/testing/web-platform/tests/css/motion/offset-path-shape-shape-001-ref.html new file mode 100644 index 0000000000..b1737a27df --- /dev/null +++ b/testing/web-platform/tests/css/motion/offset-path-shape-shape-001-ref.html @@ -0,0 +1,24 @@ +<!doctype html> +<meta charset="utf-8"> +<title>CSS Motion Path test reference: <basic-shape> shape() path</title> + +<style> +#outer { + top: 100px; + left: 100px; + position: relative; + width: 600px; + height: 400px; +} +#box { + background-color: green; + transform: translate(550px, 150px) rotate(90deg); + width: 100px; + height: 100px; + border-radius: 50% 50% 0 0; +} +</style> + +<div id="outer"> + <div id="box"></div> +</div> diff --git a/testing/web-platform/tests/css/motion/offset-path-shape-shape-001.html b/testing/web-platform/tests/css/motion/offset-path-shape-shape-001.html new file mode 100644 index 0000000000..4cca1744d0 --- /dev/null +++ b/testing/web-platform/tests/css/motion/offset-path-shape-shape-001.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Motion Path test: <basic-shape> shape() function</title> +<link rel="help" href="https://drafts.fxtf.org/motion/#valdef-offset-path-basic-shape"> +<link rel="help" href="https://drafts.csswg.org/css-shapes-2/#shape-function"> +<link rel="match" href="offset-path-shape-shape-001-ref.html"> +<meta name="assert" content="This tests that shape() generates a rotation and translation."> + +<style> +#outer { + top: 100px; + left: 100px; + position: relative; + width: 600px; + height: 400px; +} +#box { + width: 100px; + height: 100px; + background-color: green; + offset-path: shape(from 0px 0%, + hline by 100%, + vline to 400px, + hline by -100%, + close); + offset-distance: 40%; + border-radius: 50% 50% 0 0; +} +</style> + +<div id="outer"> + <div id="box"></div> +</div> diff --git a/testing/web-platform/tests/css/motion/offset-path-shape-shape-002.html b/testing/web-platform/tests/css/motion/offset-path-shape-shape-002.html new file mode 100644 index 0000000000..51030cab14 --- /dev/null +++ b/testing/web-platform/tests/css/motion/offset-path-shape-shape-002.html @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Motion Path test: <basic-shape> shape() function with padding-box</title> +<link rel="help" href="https://drafts.fxtf.org/motion/#valdef-offset-path-basic-shape"> +<link rel="help" href="https://drafts.csswg.org/css-shapes-2/#shape-function"> +<link rel="match" href="offset-path-shape-shape-001-ref.html"> +<meta name="assert" content="This tests that shape() generates a rotation and translation."> + +<style> +#outer { + top: 50px; + left: 50px; + position: relative; + width: 600px; + height: 400px; + padding: 50px; + box-sizing: content-box; +} +#box { + width: 100px; + height: 100px; + background-color: green; + offset-path: shape(from 50px 10%, + hline by calc(100% - 100px), + vline to calc(100% - 50px), + hline to 50px, + close) + padding-box; + offset-distance: 40%; + border-radius: 50% 50% 0 0; +} +</style> + +<div id="outer"> + <div id="box"></div> +</div> diff --git a/testing/web-platform/tests/css/motion/offset-path-shape-shape-003.html b/testing/web-platform/tests/css/motion/offset-path-shape-shape-003.html new file mode 100644 index 0000000000..2b83f89cdc --- /dev/null +++ b/testing/web-platform/tests/css/motion/offset-path-shape-shape-003.html @@ -0,0 +1,30 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Motion Path test: <basic-shape> shape() function with content-box</title> +<link rel="help" href="https://drafts.fxtf.org/motion/#valdef-offset-path-basic-shape"> +<link rel="help" href="https://drafts.csswg.org/css-shapes-2/#shape-function"> +<link rel="match" href="offset-path-shape-shape-001-ref.html"> +<meta name="assert" content="This tests that shape() generates a rotation and translation."> + +<style> +#outer { + width: 600px; + height: 400px; + border: 50px solid transparent; +} +#box { + width: 100px; + height: 100px; + background-color: green; + offset-path: shape(from 50px calc(-10% + 90px), + hline by 100%, + vline to calc(100% + 50px)) + content-box; + offset-distance: 80%; + border-radius: 50% 50% 0 0; +} +</style> + +<div id="outer"> + <div id="box"></div> +</div> diff --git a/testing/web-platform/tests/css/motion/parsing/offset-path-shape-computed.html b/testing/web-platform/tests/css/motion/parsing/offset-path-shape-computed.html new file mode 100644 index 0000000000..8904eaf16a --- /dev/null +++ b/testing/web-platform/tests/css/motion/parsing/offset-path-shape-computed.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Motion Path Module Level 1: getComputedStyle for offset-path with shape()</title> +<link rel="help" href="https://drafts.fxtf.org/motion-1/#offset-path-property"> +<link rel="help" href="https://drafts.csswg.org/css-shapes-2/#shape-function"> +<meta name="assert" content="offset-path has absolute shape commands."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/computed-testcommon.js"></script> +<style> +html { + font-size: 16px; +} +</style> +</head> +<body> +<div id="target"></div> +<script> +test_computed_value("offset-path", "shape(from 0px 0px, line to 10px 10px)"); +test_computed_value("offset-path", "shape(from 1em 50px, line to 10rem 10%)", "shape(from 16px 50px, line to 160px 10%)"); +test_computed_value("offset-path", "shape(from 10px 10px, move by 10px 5px, line by 20px 40%, close)"); +test_computed_value("offset-path", "shape(from 10px 10px, hline by 10px, vline to 5rem)", "shape(from 10px 10px, hline by 10px, vline to 80px)"); +test_computed_value("offset-path", "shape(from 10px 10px, vline by 5%, hline to 1px)"); +test_computed_value("offset-path", "shape(from 10px 10px, curve to 50px 20px via 10rem 1%)", "shape(from 10px 10px, curve to 50px 20px via 160px 1%)"); +test_computed_value("offset-path", "shape(from 10px 10px, curve to 50px 20px via 10rem 1px 20% 1em)", "shape(from 10px 10px, curve to 50px 20px via 160px 1px 20% 16px)"); +test_computed_value("offset-path", "shape(from 10px 10px, smooth to 50px 20px via 10rem 1%)", "shape(from 10px 10px, smooth to 50px 20px via 160px 1%)"); +test_computed_value("offset-path", "shape(from 10px 10px, smooth to 50px 3pt)", "shape(from 10px 10px, smooth to 50px 4px)"); +test_computed_value("offset-path", "shape(from 10px 10px, arc to 50px 3pt of 10px 10px)", "shape(from 10px 10px, arc to 50px 4px of 10px)"); +test_computed_value("offset-path", "shape(from 10px 10px, arc to 50px 3pt of 10px 10px small rotate 0deg)", "shape(from 10px 10px, arc to 50px 4px of 10px)"); +test_computed_value("offset-path", "shape(from 10% 1rem, arc to 50px 3pt of 20% cw large rotate 25deg)", "shape(from 10% 16px, arc to 50px 4px of 20% cw large rotate 25deg)"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/motion/parsing/offset-path-shape-parsing.html b/testing/web-platform/tests/css/motion/parsing/offset-path-shape-parsing.html new file mode 100644 index 0000000000..6ca288b660 --- /dev/null +++ b/testing/web-platform/tests/css/motion/parsing/offset-path-shape-parsing.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<html> +<head> +<meta charset="utf-8"> +<title>Motion Path Module Level 1: parsing offset-path with shape()</title> +<link rel="help" href="https://drafts.fxtf.org/motion-1/#offset-path-property"> +<link rel="help" href="https://drafts.csswg.org/css-shapes-2/#shape-function"> +<meta name="assert" content="offset-path supports the full shape() grammar."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/css/support/parsing-testcommon.js"></script> +</head> +<body> +<script> +// basic +test_valid_value("offset-path", "shape(from 0px 0px, line to 10px 10px)"); +test_valid_value("offset-path", "shape( from 0px 0px, line to 10px 10px )", "shape(from 0px 0px, line to 10px 10px)"); +test_valid_value("offset-path", "shape(from 1em 50%, line to 10px 10px)"); +test_valid_value("offset-path", "shape(from 1ch 50px, line to 10rem 10vh)"); +test_valid_value("offset-path", "shape(from 1ch -50px, line to -10% 12px)"); + +// segment types +test_valid_value("offset-path", "shape(from 10px 10px, move by 10px 5px, line by 20px 40%, close)"); +test_valid_value("offset-path", "shape(from 10px 10px, hline by 10px, vline to 5rem)"); +test_valid_value("offset-path", "shape(from 10px 10px, vline by 5%, hline to 1vw)"); +test_valid_value("offset-path", "shape(from 10px 10px, curve to 50px 20px via 10rem 1%)"); +test_valid_value("offset-path", "shape(from 10px 10px, curve to 50px 20px via 10rem 1px 20vh 1ch)"); +test_valid_value("offset-path", "shape(from 10px 10px, curve by 50px 20px via 10rem 1px 20vh 1ch)"); +test_valid_value("offset-path", "shape(from 10px 10px, smooth to 50px 20px via 10rem 1%)"); +test_valid_value("offset-path", "shape(from 10px 10px, smooth to 50px 1pt)"); +test_valid_value("offset-path", "shape(from 10px 10px, arc to 50px 1pt of 10px 10px)", "shape(from 10px 10px, arc to 50px 1pt of 10px)"); +test_valid_value("offset-path", "shape(from 10px 10px, arc to 50px 1pt of 10px 10px small rotate 0deg)", "shape(from 10px 10px, arc to 50px 1pt of 10px)"); +test_valid_value("offset-path", "shape(from 10% 1rem, arc to 50px 1pt of 20% cw large rotate 25deg)", "shape(from 10% 1rem, arc to 50px 1pt of 20% cw large rotate 25deg)"); + +// nonsense +test_invalid_value("offset-path", "shape(evenodd from 0px 0px, line to 10px 10px)"); +test_invalid_value("offset-path", "shape(nonzero from 0px 0px, line to 10px 10px)"); +test_invalid_value("offset-path", "shape(evenodd from 0px 0px, close)"); +test_invalid_value("offset-path", "shape(from 0px 0px, close path)"); +test_invalid_value("offset-path", "shape(from 10px 10px, curve to 50px 20px via 10rem)"); +test_invalid_value("offset-path", "shape(from 10px 10px, curve to 50px 20px via 10rem 1% 12px)"); +test_invalid_value("offset-path", "shape(from 10px 10px, hline byy 10px, vline to 5rem)"); +test_invalid_value("offset-path", "shape(from 10px 10px, vline by 5% hline by 1vw"); +test_invalid_value("offset-path", "shape(from 10px 10px, smooth to 50px 20px via 10rem)"); +test_invalid_value("offset-path", "shape(from 10px 10px, smooth to 50px 20px via 10rem 2px 2pt)"); +test_invalid_value("offset-path", "shape()"); +test_invalid_value("offset-path", "shape(from)"); +test_invalid_value("offset-path", "shape(from 0px)"); +test_invalid_value("offset-path", "shape(from 0px 20px,)"); +test_invalid_value("offset-path", "shape(close)"); +test_invalid_value("offset-path", "shape(nonzero close)"); +test_invalid_value("offset-path", "shape(from 0px 10px)"); +test_invalid_value("offset-path", "shape(allkindsofnonsense)"); +test_invalid_value("offset-path", "shape(arc)"); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/css/printing/page-name-001-print.html b/testing/web-platform/tests/css/printing/page-name-001-print.html deleted file mode 100644 index 56281bb4ed..0000000000 --- a/testing/web-platform/tests/css/printing/page-name-001-print.html +++ /dev/null @@ -1,10 +0,0 @@ -<!DOCTYPE html> -<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> -<link rel="help" href="https://drafts.csswg.org/css-page-3/#using-named-pages"> -<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1414718"> -<link rel="match" href="page-name-001-print-ref.html"> -<div style="page:foo;"> - <div style="float:left;">First page</div> - <div style="clear:both;">Also first page</div> - <div style="page:bar;">Second page</div> -</div> diff --git a/testing/web-platform/tests/css/printing/transform-001-print.html b/testing/web-platform/tests/css/printing/transform-001-print.html deleted file mode 100644 index f19b889d07..0000000000 --- a/testing/web-platform/tests/css/printing/transform-001-print.html +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE html> -<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> -<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1442522"> -<link rel="match" href="transform-001-print-ref.html"> -<style> - body { margin: 0; } -</style> -<div style="height:0;"> - There should be 5 pages. -</div> -<div style="transform:rotate(180deg); height:500vh;"></div> diff --git a/testing/web-platform/tests/css/printing/transform-002-print.html b/testing/web-platform/tests/css/printing/transform-002-print.html deleted file mode 100644 index 3ab37bf5bd..0000000000 --- a/testing/web-platform/tests/css/printing/transform-002-print.html +++ /dev/null @@ -1,11 +0,0 @@ -<!DOCTYPE html> -<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> -<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1371426"> -<link rel="match" href="transform-002-print-ref.html"> -<p>There should be a green square on the second page, and no red.</p> -<div style="break-before:page; width:100px; height:100px; background:red;"> - <div style="position:absolute; width:0; height:0; transform:translateX(49px);"> - <div style="position:absolute; width:51px; height:100px; background:green;"></div> - </div> - <div style="position:absolute; width:50px; height:100px; background:green;"></div> -</div> diff --git a/testing/web-platform/tests/css/printing/transform-003-print.html b/testing/web-platform/tests/css/printing/transform-003-print.html deleted file mode 100644 index c8effbb8f3..0000000000 --- a/testing/web-platform/tests/css/printing/transform-003-print.html +++ /dev/null @@ -1,21 +0,0 @@ -<!DOCTYPE html> -<link rel="author" title="Morten Stenshorne" href="mailto:mstensho@chromium.org"> -<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=1371426"> -<link rel="match" href="transform-003-print-ref.html"> -<style> - body { margin: 0; } -</style> -<div style="position:relative; z-index:1; height:100vh;"> - There should be five pages. Each page should have a unique background - but no - red should be seen. -</div> -<!-- This tests that the transform on the container is applied to all the - absolutely positioned descendants, and also that the transform origin is - correct. --> -<div style="transform:rotate(180deg); height:250vh; background:red;"> - <div style="position:absolute; width:100%; transform:translateY(-100vh); top:-100vh; height:100vh; background:yellow;"></div> - <div style="position:absolute; width:100%; height:100vh; background:pink;"></div> - <div style="position:absolute; width:100%; top:100vh; height:100vh; background:cyan;"></div> - <div style="position:absolute; width:100%; transform:translateY(-50vh); top:200vh; height:100vh; background:papayawhip;"></div> - <div style="position:absolute; width:100%; transform:translateY(-100vh); top:300vh; height:100vh; background:olive;"></div> -</div> diff --git a/testing/web-platform/tests/css/selectors/WEB_FEATURES.yml b/testing/web-platform/tests/css/selectors/WEB_FEATURES.yml new file mode 100644 index 0000000000..47cf05a2c0 --- /dev/null +++ b/testing/web-platform/tests/css/selectors/WEB_FEATURES.yml @@ -0,0 +1,7 @@ +features: +- name: focus-visible + files: + - focus-visible-* +- name: has + files: + - has-* diff --git a/testing/web-platform/tests/css/selectors/dir-pseudo-on-input-element.html b/testing/web-platform/tests/css/selectors/dir-pseudo-on-input-element.html index 25f7a080d7..b1427bf42d 100644 --- a/testing/web-platform/tests/css/selectors/dir-pseudo-on-input-element.html +++ b/testing/web-platform/tests/css/selectors/dir-pseudo-on-input-element.html @@ -1,14 +1,10 @@ <!DOCTYPE html> -<html> -<head> <meta name="author" title="Ryosuke Niwa" href="mailto:rniwa@webkit.org"> <link rel="help" href="https://html.spec.whatwg.org/multipage/dom.html#the-directionality"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -</head> <body> <script> - test(() => { const input = document.createElement('input'); input.type = 'tel'; @@ -195,9 +191,17 @@ for (const type of ['date', 'time', 'number', 'range', 'color', 'checkbox', 'rad input.removeAttribute('dir'); assert_true(input.matches(':dir(ltr)')); assert_false(input.matches(':dir(rtl)')); + + let rtlParent = document.createElement("div"); + rtlParent.dir = "rtl"; + input.dir = "auto"; + rtlParent.appendChild(input); + document.body.appendChild(rtlParent); // Just for good measure. + assert_true(input.matches(':dir(ltr)')); + assert_false(input.matches(':dir(rtl)')); + rtlParent.remove(); }, `input element whose type attribute is in the ${type} state`); } </script> -</body> </html> diff --git a/testing/web-platform/tests/css/selectors/invalidation/WEB_FEATURES.yml b/testing/web-platform/tests/css/selectors/invalidation/WEB_FEATURES.yml new file mode 100644 index 0000000000..4eaa2f3931 --- /dev/null +++ b/testing/web-platform/tests/css/selectors/invalidation/WEB_FEATURES.yml @@ -0,0 +1,6 @@ +features: +- name: has + files: + - has-* + - "*-in-has.*" + - "*-in-has-*" diff --git a/testing/web-platform/tests/css/selectors/invalidation/is-where-pseudo-containing-hard-pseudo.html b/testing/web-platform/tests/css/selectors/invalidation/is-where-pseudo-containing-hard-pseudo.html new file mode 100644 index 0000000000..416aacca8c --- /dev/null +++ b/testing/web-platform/tests/css/selectors/invalidation/is-where-pseudo-containing-hard-pseudo.html @@ -0,0 +1,100 @@ +<!DOCTYPE html> +<title>CSS Selectors Invalidation: :is and :where selectors containing "hard" selectors</title> +<link rel="author" title="David Shin" href="dshin@mozilla.com"> +<link rel="help" href="https://drafts.csswg.org/selectors/#logical-combination"> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1874042"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +.container { + color: grey; +} + +#subject1:is(.other-match, :has(.descendant)) { + color: red; +} + +#subject1:is(.parent > .other-match, .parent > :has(.descendant)) { + color: orangered; +} + +#subject2:where(.other-match, :has(.descendant)) { + color: darkred; +} + +#subject2:where(.parent > .other-match, .parent > :has(.descendant)) { + color: pink; +} + +#subject3:is(.other-match, :nth-child(1000 of .another-match)) { + color: green; +} + +#subject3:is(.parent > .other-match, .parent > :nth-child(1000 of .another-match)) { + color: lightgreen; +} + +#subject4:where(.other-match, :nth-child(1000 of .another-match)) { + color: darkgreen; +} + +#subject4:where(.parent > .other-match, .parent > :nth-child(1000 of .another-match)) { + color: yellowgreen; +} +</style> +<div id="par"> + <div id="subject1" class="container"></div> + <div id="subject2" class="container"></div> + <div id="subject3" class="container another-match"></div> + <div id="subject4" class="container another-match"></div> +</div> +<script> +const colors = { + grey: "rgb(128, 128, 128)", + red: "rgb(255, 0, 0)", + orangered: "rgb(255, 69, 0)", + darkred: "rgb(139, 0, 0)", + pink: "rgb(255, 192, 203)", + green: "rgb(0, 128, 0)", + lightgreen: "rgb(144, 238, 144)", + darkgreen: "rgb(0, 100, 0)", + yellowgreen: "rgb(154, 205, 50)" +}; + +function testClassChange(subject, before, after, afterParent) { + const cls = "other-match"; + const parentCls = "parent"; + const beforeColor = colors[before]; + + test(() => { + assert_equals(getComputedStyle(subject).color, beforeColor); + }, subject.id + " initial color is " + before); + + subject.classList.add(cls); + const afterColor = colors[after]; + test(() => { + assert_equals(getComputedStyle(subject).color, afterColor); + }, subject.id + " is " + after + " when ." + cls + " added"); + + par.classList.add(parentCls); + const afterParentColor = colors[afterParent]; + test(() => { + assert_equals(getComputedStyle(subject).color, afterParentColor); + }, subject.id + " is " + afterParent + " when ." + parentCls + " added to parent"); + + par.classList.remove(parentCls); + test(() => { + assert_equals(getComputedStyle(subject).color, afterColor); + }, subject.id + " is " + afterParent + " when ." + parentCls + " removed from parent"); + + subject.classList.remove(cls); + test(() => { + assert_equals(getComputedStyle(subject).color, beforeColor); + }, subject.id + " is " + after + " when ." + cls + " removed"); +} + +testClassChange(subject1, "grey", "red", "orangered"); +testClassChange(subject2, "grey", "darkred", "pink"); +testClassChange(subject3, "grey", "green", "lightgreen"); +testClassChange(subject4, "grey", "darkgreen", "yellowgreen"); +</script> diff --git a/testing/web-platform/tests/css/selectors/parsing/WEB_FEATURES.yml b/testing/web-platform/tests/css/selectors/parsing/WEB_FEATURES.yml new file mode 100644 index 0000000000..261019c003 --- /dev/null +++ b/testing/web-platform/tests/css/selectors/parsing/WEB_FEATURES.yml @@ -0,0 +1,8 @@ +features: +- name: focus-visible + files: + - parse-focus-visible.html +- name: has + files: + - parse-has.html + - parse-has-* diff --git a/testing/web-platform/tests/css/zoom/iframe-zoom-nested.html b/testing/web-platform/tests/css/zoom/iframe-zoom-nested.html new file mode 100644 index 0000000000..22a491eb0b --- /dev/null +++ b/testing/web-platform/tests/css/zoom/iframe-zoom-nested.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> +<title>nested iframes with CSS zoom</title> +<link rel="author" title="Yotam Hacohen" href="mailto:yotha@chromium.org"> +<link rel="author" title="Google" href="http://www.google.com/"> +<link href="reference/iframe-zoom-nested-ref.html" rel="match"> +<link rel="help" href="https://drafts.csswg.org/css-viewport/"> +<head> + <style> + body { + overflow: hidden; + } + + div { + margin: 0px; + padding: 0px; + overflow: visible; + } + + iframe { + overflow: visible; + border: none; + } + </style> +</head> +<body> + <div id="no_zoom"> + <iframe src="resources/nested-iframe-no-zoom.html" scrolling="no"></iframe> + </div> + <div id="no_zoom2"> + <iframe src="resources/nested-iframe-with-zoom.html" scrolling="no"></iframe> + </div> + <div id="with_zoom" style="zoom: 2;"> + <iframe src="resources/nested-iframe-no-zoom.html" scrolling="no"></iframe> + </div> + <div id="another_with_zoom" style="zoom: 2;"> + <iframe src="resources/nested-iframe-with-zoom.html" scrolling="no"></iframe> + </div> +</body> diff --git a/testing/web-platform/tests/css/zoom/iframe-zoom.sub.html b/testing/web-platform/tests/css/zoom/iframe-zoom.sub.html new file mode 100644 index 0000000000..82a202161b --- /dev/null +++ b/testing/web-platform/tests/css/zoom/iframe-zoom.sub.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<title>iframe in an element with CSS zoom</title> +<link rel="author" title="Yotam Hacohen" href="mailto:yotha@chromium.org"> +<link rel="author" title="Google" href="http://www.google.com/"> +<link href="reference/iframe-zoom-ref.html" rel="match"> +<link rel="help" href="https://drafts.csswg.org/css-viewport/"> + +<head> + <style> + body { + overflow: hidden; + } + + div { + margin: 0px; + padding: 0px; + } + + iframe { + height: 80px; + width: 80px; + border: none; + } + </style> +</head> + +<body> + + <div id="no_zoom"> + <iframe src="resources/iframe_content.html"></iframe> + </div> + + <div id="with_zoom" style="zoom: 3;"> + <iframe src="resources/iframe_content.html"></iframe> + </div> + + <div id="another_with_zoom" style="zoom: 3;"> + <iframe src="http://{{hosts[alt][]}}:{{ports[http][0]}}/css/zoom/tentative/resources/iframe_content.html"></iframe> + </div> + +</body> diff --git a/testing/web-platform/tests/css/zoom/reference/iframe-zoom-nested-ref.html b/testing/web-platform/tests/css/zoom/reference/iframe-zoom-nested-ref.html new file mode 100644 index 0000000000..b855278516 --- /dev/null +++ b/testing/web-platform/tests/css/zoom/reference/iframe-zoom-nested-ref.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<title>ref for nested iframes with css zoom</title> +<link rel="author" title="Yotam Hacohen" href="mailto:yotha@chromium.org"> +<link rel="author" title="Google" href="http://www.google.com/"> +<link rel="help" href="https://drafts.csswg.org/css-viewport/"> + +<head> + <style> + iframe { + border: none; + margin: 0px; + padding: 0px; + } + </style> +</head> + +<body> + + <div id="no_zoom"> + <iframe style="height: 80px;" srcdoc='<body style="margin: 0;"><div id="target" style="background-color: aqua; width: 64px; height: 64px;"></div></body>'></iframe> + </div> + + <div id="with_zoom"> + <iframe style="height: 248px;" srcdoc='<body style="margin: 0;"><div id="target" style="background-color: aqua; width: 64px; height: 64px; zoom: 2;"></div></body>'></iframe> + </div> + + <div id="another_with_zoom"> + <iframe style="height: 248px;" srcdoc='<body style="margin: 0;"><div id="target" style="background-color: aqua; width: 64px; height: 64px; zoom: 2;"></div></body>'></iframe> + </div> + + <div id="another_with_zoom"> + <iframe style="height: 260px;" srcdoc='<body style="margin: 0;"><div id="target" style="background-color: aqua; width: 64px; height: 64px; zoom: 4;"></div></body>'></iframe> + </div> + +</body> diff --git a/testing/web-platform/tests/css/zoom/reference/iframe-zoom-ref.html b/testing/web-platform/tests/css/zoom/reference/iframe-zoom-ref.html new file mode 100644 index 0000000000..43bc3e24cf --- /dev/null +++ b/testing/web-platform/tests/css/zoom/reference/iframe-zoom-ref.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<title>ref for iframe in an element with css zoom</title> +<link rel="author" title="Yotam Hacohen" href="mailto:yotha@chromium.org"> +<link rel="author" title="Google" href="http://www.google.com/"> +<link rel="help" href="https://drafts.csswg.org/css-viewport/"> + +<head> + <style> + iframe { + border: none; + margin: 0px; + padding: 0px; + } + </style> +</head> + +<body> + + <div id="no_zoom"> + <iframe style="height: 80px;" srcdoc='<body style="margin: 0;"><div id="target" style="background-color: aqua; width: 64px; height: 64px;"></div></body>'></iframe> + </div> + + <div id="with_zoom"> + <iframe style="height: 248px;" srcdoc='<body style="margin: 0;"><div id="target" style="background-color: aqua; width: 64px; height: 64px; zoom: 3;"></div></body>'></iframe> + </div> + + <div id="another_with_zoom"> + <iframe style="height: 240px;" srcdoc='<body style="margin: 0;"><div id="target" style="background-color: aqua; width: 64px; height: 64px; zoom: 3;"></div></body>'></iframe> + </div> + +</body> diff --git a/testing/web-platform/tests/css/zoom/resources/iframe_content.html b/testing/web-platform/tests/css/zoom/resources/iframe_content.html new file mode 100644 index 0000000000..58c4d03a46 --- /dev/null +++ b/testing/web-platform/tests/css/zoom/resources/iframe_content.html @@ -0,0 +1,4 @@ +<!DOCTYPE html> +<body style="margin: 0"> + <div id="target" style="background-color: aqua; width: 64px; height: 64px;"></div> +</body> diff --git a/testing/web-platform/tests/css/zoom/resources/nested-iframe-no-zoom.html b/testing/web-platform/tests/css/zoom/resources/nested-iframe-no-zoom.html new file mode 100644 index 0000000000..60b1fd6481 --- /dev/null +++ b/testing/web-platform/tests/css/zoom/resources/nested-iframe-no-zoom.html @@ -0,0 +1,4 @@ +<!DOCTYPE html> +<div> +<iframe src="iframe_content.html" style="overflow: visible; width: 80px; border: none;" scrolling="no"></iframe> +</div> diff --git a/testing/web-platform/tests/css/zoom/resources/nested-iframe-with-zoom.html b/testing/web-platform/tests/css/zoom/resources/nested-iframe-with-zoom.html new file mode 100644 index 0000000000..e7de64aafb --- /dev/null +++ b/testing/web-platform/tests/css/zoom/resources/nested-iframe-with-zoom.html @@ -0,0 +1,4 @@ +<!DOCTYPE html> +<div style="zoom: 3;"> +<iframe src="iframe_content.html" style="overflow: visible; height: 80px; width: 80px; border: none;" scrolling="no"></iframe> +</div> diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMediaElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMediaElement.html index 58e002c52c..dd830b7767 100644 --- a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMediaElement.html +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLMediaElement.html @@ -62,7 +62,7 @@ testReflectBooleanAttributeWithDependentAttributes( ); testReflectAttribute( - 'src', 'src', '/media/video.ogv', + 'src', 'src', '/media/video.webm', '/media/movie_5.mp4', 'src on HTMLMediaElement in video', 'video', HTMLVideoElement ); diff --git a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLSourceElement.html b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLSourceElement.html index f7d567ebcb..8168fd1c2e 100644 --- a/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLSourceElement.html +++ b/testing/web-platform/tests/custom-elements/reactions/customized-builtins/HTMLSourceElement.html @@ -22,7 +22,7 @@ function getParentElement(id) { } testReflectAttributeWithParentNode( - 'src', 'src', '/media/video.ogv', + 'src', 'src', '/media/video.webm', '/media/white.mp4', 'src on HTMLSourceElement', 'source', () => getParentElement('video'), HTMLSourceElement ); diff --git a/testing/web-platform/tests/custom-elements/state/state-css-selector-nth-of.html b/testing/web-platform/tests/custom-elements/state/state-css-selector-nth-of.html new file mode 100644 index 0000000000..c7b7183ccb --- /dev/null +++ b/testing/web-platform/tests/custom-elements/state/state-css-selector-nth-of.html @@ -0,0 +1,92 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="utf-8"> + <meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> + <link rel="help" href="https://html.spec.whatwg.org/multipage/custom-elements.html#custom-state-pseudo-class" /> + <title>:state() css selector applies to nth-of selectors</title> + + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + </head> + <body> + <custom-state id="myCE">First Element</custom-state> + <p id="mySibling">First Sibling</p> + <custom-state id="myCE2">Second Element</custom-state> + <p id="mySibling2">Second Sibling</p> + <style> + :nth-child(1), :nth-child(2) { + color: #f00; + } + :nth-child(2 of :state(--green)) { + color: #0f0; + } + :nth-child(2 of :state(--green)) + p { + color: #00f; + } + </style> + <script> + customElements.define('custom-state', class extends HTMLElement { + connectedCallback() { + this.elementInternals = this.attachInternals(); + } + }); + + test(function(t) { + t.add_cleanup(() => { myCE.elementInternals.states.delete('--green') }); + t.add_cleanup(() => { myCE2.elementInternals.states.delete('--green') }); + assert_false(myCE.elementInternals.states.has('--green')); + assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(myCE2).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(mySibling2).getPropertyValue('color'), 'rgb(255, 0, 0)'); + myCE.elementInternals.states.add('--green'); + assert_true(myCE.elementInternals.states.has('--green')); + assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(myCE2).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(mySibling2).getPropertyValue('color'), 'rgb(255, 0, 0)'); + myCE2.elementInternals.states.add('--green'); + assert_true(myCE2.elementInternals.states.has('--green')); + assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(myCE2).getPropertyValue('color'), 'rgb(0, 255, 0)'); + assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(mySibling2).getPropertyValue('color'), 'rgb(0, 0, 255)'); + }, "state selector has influence on nth-of when state is applied"); + + test(function(t) { + t.add_cleanup(() => { myCE.elementInternals.states.delete('--foo') }); + t.add_cleanup(() => { myCE2.elementInternals.states.delete('--foo') }); + myCE.elementInternals.states.add('--foo'); + myCE2.elementInternals.states.add('--foo'); + assert_false(myCE.elementInternals.states.has('--green')); + assert_true(myCE.elementInternals.states.has('--foo')); + assert_false(myCE2.elementInternals.states.has('--green')); + assert_true(myCE2.elementInternals.states.has('--foo')); + assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(myCE2).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(mySibling2).getPropertyValue('color'), 'rgb(255, 0, 0)'); + }, "state selector only applies on given ident"); + + test(function(t) { + myCE.elementInternals.states.add('--green'); + myCE.elementInternals.states.add('--foo'); + myCE2.elementInternals.states.add('--green'); + myCE2.elementInternals.states.add('--foo'); + assert_true(myCE.elementInternals.states.has('--green')); + assert_true(myCE.elementInternals.states.has('--foo')); + assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(myCE2).getPropertyValue('color'), 'rgb(0, 255, 0)'); + assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(mySibling2).getPropertyValue('color'), 'rgb(0, 0, 255)'); + myCE.elementInternals.states.clear(); + assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(myCE2).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_equals(getComputedStyle(mySibling2).getPropertyValue('color'), 'rgb(255, 0, 0)'); + }, "style is invalided on clear()"); + + </script> + </body> +</html> diff --git a/testing/web-platform/tests/custom-elements/state/state-css-selector.html b/testing/web-platform/tests/custom-elements/state/state-css-selector.html index d29c927223..8ead8d7809 100644 --- a/testing/web-platform/tests/custom-elements/state/state-css-selector.html +++ b/testing/web-platform/tests/custom-elements/state/state-css-selector.html @@ -99,6 +99,32 @@ assert_equals(getComputedStyle(mySibling).getPropertyValue('color'), 'rgb(255, 0, 0)'); }, "state selector only applies to has() on given ident"); + test(function(t) { + myCE.elementInternals.states.add('--green'); + myCE.elementInternals.states.add('--green'); + myCE.elementInternals.states.add('--green'); + t.add_cleanup(() => { myCE.elementInternals.states.delete('--green') }); + assert_true(myCE.elementInternals.states.has('--green')); + assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(0, 255, 0)'); + assert_true(myCE.elementInternals.states.delete('--green')); + assert_false(myCE.elementInternals.states.has('--green')); + assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)'); + assert_false(myCE.elementInternals.states.delete('--green')); + }, "states added multiple times counts as one"); + + test(function(t) { + myCE.elementInternals.states.add('--green'); + myCE.elementInternals.states.add('--foo'); + t.add_cleanup(() => { myCE.elementInternals.states.clear() }); + assert_true(myCE.elementInternals.states.has('--green')); + assert_true(myCE.elementInternals.states.has('--foo')); + assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(0, 255, 0)'); + myCE.elementInternals.states.clear(); + assert_false(myCE.elementInternals.states.has('--green')); + assert_false(myCE.elementInternals.states.has('--foo')); + assert_equals(getComputedStyle(myCE).getPropertyValue('color'), 'rgb(255, 0, 0)'); + }, "style is invalided on clear()"); + </script> </body> </html> diff --git a/testing/web-platform/tests/device-posture/WEB_FEATURES.yml b/testing/web-platform/tests/device-posture/WEB_FEATURES.yml new file mode 100644 index 0000000000..7bafe10430 --- /dev/null +++ b/testing/web-platform/tests/device-posture/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: device-posture + files: "**" diff --git a/testing/web-platform/tests/docs/writing-tests/assumptions.md b/testing/web-platform/tests/docs/writing-tests/assumptions.md index 5afa416121..9a7bf8b4e4 100644 --- a/testing/web-platform/tests/docs/writing-tests/assumptions.md +++ b/testing/web-platform/tests/docs/writing-tests/assumptions.md @@ -4,7 +4,7 @@ The tests make a number of assumptions of the user agent, and new tests can freely rely on these assumptions being true: * The device is a full-color device. - * The device has a viewport width of at least 800px. + * The device has viewport dimensions of at least 800px by 600px. * The UA imposes no minimum font size. * The `medium` `font-size` computes to 16px. * The canvas background is `white`. diff --git a/testing/web-platform/tests/document-picture-in-picture/hide-return-to-opener-button-manual.https.html b/testing/web-platform/tests/document-picture-in-picture/hide-return-to-opener-button-manual.https.html new file mode 100644 index 0000000000..17cd6657dd --- /dev/null +++ b/testing/web-platform/tests/document-picture-in-picture/hide-return-to-opener-button-manual.https.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<title>Test that using documentPictureInPicture's disallowReturnToOpener + parameter hides the return to opener button</title> +<body> + <p> + This tests that a document picture-in-picture window opened with the `disallowReturnToOpener` + parameter set to `true` does not display a button to return to the opener. + <ol> + <li>Click on the "Open document picture-in-picture window" button below.</li> + <li>Check that it does not display that button.</li> + </ol> + </p> + <input type="button" id="btnOpenPip" value="Open document picture-in-picture window" /> +<script> +const btnOpenPip = document.getElementById('btnOpenPip'); +btnOpenPip.addEventListener('click', async () => { + const pipWindow = await documentPictureInPicture.requestWindow({ disallowReturnToOpener: true }); + pipWindow.document.body.innerText = 'This document picture-in-picture window should not display a button to return to the opener'; +}); +</script> +</body> diff --git a/testing/web-platform/tests/document-policy/experimental-features/resources/video.ogv b/testing/web-platform/tests/document-policy/experimental-features/resources/video.ogv Binary files differdeleted file mode 100644 index c9ee910fc6..0000000000 --- a/testing/web-platform/tests/document-policy/experimental-features/resources/video.ogv +++ /dev/null diff --git a/testing/web-platform/tests/document-policy/experimental-features/resources/video.webm b/testing/web-platform/tests/document-policy/experimental-features/resources/video.webm Binary files differnew file mode 100644 index 0000000000..10a1ae5d07 --- /dev/null +++ b/testing/web-platform/tests/document-policy/experimental-features/resources/video.webm diff --git a/testing/web-platform/tests/document-policy/experimental-features/unsized-media.tentative.https.sub.html b/testing/web-platform/tests/document-policy/experimental-features/unsized-media.tentative.https.sub.html index d7bb725249..4e5fbc6555 100644 --- a/testing/web-platform/tests/document-policy/experimental-features/unsized-media.tentative.https.sub.html +++ b/testing/web-platform/tests/document-policy/experimental-features/unsized-media.tentative.https.sub.html @@ -60,7 +60,7 @@ for (var test of test_cases) { })); var expected_width = test.expected_width; var expected_height = test.expected_height; - video.setAttribute("src", "/document-policy/experimental-features/resources/video.ogv"); + video.setAttribute("src", "/document-policy/experimental-features/resources/video.webm"); if (typeof test.attribute !== "undefined") { video.setAttribute(test.attribute, test.value); } diff --git a/testing/web-platform/tests/dom/abort/WEB_FEATURES.yml b/testing/web-platform/tests/dom/abort/WEB_FEATURES.yml new file mode 100644 index 0000000000..169de93ae9 --- /dev/null +++ b/testing/web-platform/tests/dom/abort/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: aborting + files: "**" diff --git a/testing/web-platform/tests/dom/events/event-global.html b/testing/web-platform/tests/dom/events/event-global.html index 3e8d25ecb5..f70606fb65 100644 --- a/testing/web-platform/tests/dom/events/event-global.html +++ b/testing/web-platform/tests/dom/events/event-global.html @@ -114,4 +114,14 @@ async_test(t => { target.dispatchEvent(new Event("click")); }, "window.event is set to the current event, which is the event passed to dispatch"); + +async_test(t => { + let target = new XMLHttpRequest(); + + target.onload = t.step_func_done(e => { + assert_equals(e, window.event); + }); + + target.dispatchEvent(new Event("load")); +}, "window.event is set to the current event, which is the event passed to dispatch (2)"); </script> diff --git a/testing/web-platform/tests/dom/events/scrolling/scrollend-event-fires-on-visual-viewport.html b/testing/web-platform/tests/dom/events/scrolling/scrollend-event-fires-on-visual-viewport.html index 5e3af7966e..99a281480f 100644 --- a/testing/web-platform/tests/dom/events/scrolling/scrollend-event-fires-on-visual-viewport.html +++ b/testing/web-platform/tests/dom/events/scrolling/scrollend-event-fires-on-visual-viewport.html @@ -20,28 +20,48 @@ </style> <div class="large"></div> <script> - window.onload = () => { - promise_test(async () => { - await waitForCompositorCommit(); - - await pinchZoomIn(); - assert_greater_than(visualViewport.scale, 1, "page should be zoomed in."); - + window.onload = async () => { + async function pan_viewport_test(add_event_listener_func) { const preScrollVisualViewportOffsetTop = visualViewport.offsetTop; const preScrollWindowScrollOffset = window.scrollY; - const scrollend_promise = new Promise((resolve) => { - visualViewport.addEventListener("scrollend", resolve); - }); + + const scrollend_promise = add_event_listener_func(); const scrollAmount = 50; await touchScrollInTarget(scrollAmount, document.documentElement, "up"); await scrollend_promise; - assert_less_than(visualViewport.offsetTop, preScrollVisualViewportOffsetTop, + assert_less_than(visualViewport.offsetTop, + preScrollVisualViewportOffsetTop, `visualViewport should be scrolled.`); assert_equals(window.scrollY, preScrollWindowScrollOffset, "the window should not scroll."); - }, "scrollend fires when visual viewport is panned."); + // No need to undo scroll; subsequent test has room to scroll further. + } + + await waitForCompositorCommit(); + await pinchZoomIn(); + assert_greater_than(visualViewport.scale, 1, "page should be zoomed in."); + + promise_test(async (t) => { + await pan_viewport_test(() => { + return new Promise((resolve) => { + visualViewport.addEventListener("scrollend", resolve, { once: true}); + }); + }); + }, "scrollend listener added via addEventlistener fires when the visual " + + "viewport is panned."); + + promise_test(async (t) => { + await pan_viewport_test((t) => { + return new Promise((resolve) => { + visualViewport.onscrollend = () => { + visualViewport.onscrollend = undefined; + resolve(); + } + }); + }); + }, "visualviewport.onscrollend fires when the visual viewport is panned."); } </script> </body> diff --git a/testing/web-platform/tests/dom/nodes/insertion-removing-steps/Node-appendChild-script-and-default-style-meta-from-fragment.tentative.html b/testing/web-platform/tests/dom/nodes/insertion-removing-steps/Node-appendChild-script-and-default-style-meta-from-fragment.tentative.html index a9b7ba633e..fa4a987751 100644 --- a/testing/web-platform/tests/dom/nodes/insertion-removing-steps/Node-appendChild-script-and-default-style-meta-from-fragment.tentative.html +++ b/testing/web-platform/tests/dom/nodes/insertion-removing-steps/Node-appendChild-script-and-default-style-meta-from-fragment.tentative.html @@ -7,29 +7,53 @@ <div id="div">hello</div> <script> let scriptRan = false; -let computedStyleDuringInsertion = null; +let computedStyleInPreScript = null; +let computedStyleInPostScript = null; test(() => { const div = document.getElementById("div"); + + // 1. Gets inserted *before* the `<meta>` tag. Cannot observe the meta tag's + // effect, because this script runs before the meta tag's post-insertion steps + // run, and the meta tag's post-insertion steps is where the default style + // sheet actually changes. + const preScript = document.createElement("script"); + preScript.textContent = ` + computedStyleInPreScript = getComputedStyle(div).display; + scriptRan = true; + `; + + // 2. The `<meta>` tag itself. const meta = document.createElement("meta"); meta.httpEquiv = "default-style"; meta.content = "alternative"; - const script = document.createElement("script"); - script.textContent = ` - computedStyleDuringInsertion = getComputedStyle(div).display; + + // 3. Gets inserted *after* the `<meta>` tag. Observes the meta tag's effect, + // because this script runs after the meta tag's post-insertion steps, which + // has the script-observable change to the default style sheet. + const postScript = document.createElement("script"); + postScript.textContent = ` + computedStyleInPostScript = getComputedStyle(div).display; scriptRan = true; `; + const df = document.createDocumentFragment(); - df.appendChild(script); - df.appendChild(meta); - assert_equals(getComputedStyle(div).display, "block", "div has block display"); + df.append(preScript, meta, postScript); + + assert_equals(getComputedStyle(div).display, "block", + "div still has block display before meta insertion"); assert_false(scriptRan, "script has not run before insertion"); + document.head.appendChild(df); assert_true(scriptRan, "script has run after insertion"); - assert_equals(computedStyleDuringInsertion, "none", - "display: none; style was applied during DOM insertion, before " + - "later-inserted script runs"); + assert_equals(computedStyleInPreScript, "block", + "display: none; style was NOT applied during DOM insertion steps, " + + "before earlier-inserted script post-insertion steps run"); + assert_equals(computedStyleInPostScript, "none", + "display: none; style WAS applied during DOM post-insertion steps, " + + "before later-inserted script runs"); assert_equals(getComputedStyle(div).display, "none", "style remains display: none; after insertion"); + }, "Inserting <meta> that uses alternate stylesheets, applies the style " + - "during DOM insertion, and before script runs as a result of any atomic insertions"); + "during DOM post-insertion steps"); </script> diff --git a/testing/web-platform/tests/dom/nodes/insertion-removing-steps/blur-event.window.js b/testing/web-platform/tests/dom/nodes/insertion-removing-steps/blur-event.window.js index 4c8cd85cbf..fdca02dcda 100644 --- a/testing/web-platform/tests/dom/nodes/insertion-removing-steps/blur-event.window.js +++ b/testing/web-platform/tests/dom/nodes/insertion-removing-steps/blur-event.window.js @@ -12,8 +12,17 @@ test(() => { const button = document.body.appendChild(document.createElement('button')); button.focus(); - let blurCalled = false; - button.onblur = e => blurCalled = true; + let blur_called = false; + let focus_out_called = false; + let focus_called = false; + + button.onblur = () => { blur_called = true; } + button.onfocusout = () => { focus_out_called = true; } + document.body.addEventListener("focus", + () => { focus_called = true; }, {capture: true}); button.remove(); - assert_false(blurCalled, "Blur event was not fired"); -}, "<button> element does not fire blur event upon DOM removal"); + + assert_false(blur_called, "Blur event was not fired"); + assert_false(focus_out_called, "FocusOut event was not fired"); + assert_false(focus_called, "Focus was not fired"); +}, "<button> element does not fire blur/focusout events upon DOM removal"); diff --git a/testing/web-platform/tests/dom/observable/tentative/observable-first.any.js b/testing/web-platform/tests/dom/observable/tentative/observable-first.any.js new file mode 100644 index 0000000000..7c99066dc2 --- /dev/null +++ b/testing/web-platform/tests/dom/observable/tentative/observable-first.any.js @@ -0,0 +1,114 @@ +promise_test(async () => { + const results = []; + + const source = new Observable(subscriber => { + subscriber.addTeardown(() => results.push('teardown')); + subscriber.next(1); + results.push(subscriber.active ? 'active' : 'inactive'); + results.push(subscriber.signal.aborted ? 'aborted' : 'not aborted') + + // Ignored. + subscriber.next(2); + subscriber.complete(); + }); + + const value = await source.first(); + + assert_array_equals(results, ['teardown', 'inactive', 'aborted']); + assert_equals(value, 1, + "Promise resolves with the first value from the source Observable"); +}, "first(): Promise resolves with the first value from the source Observable"); + +promise_test(async () => { + const error = new Error("error from source"); + const source = new Observable(subscriber => { + subscriber.error(error); + }); + + let rejection; + try { + await source.first(); + } catch (e) { + rejection = e; + } + + assert_equals(rejection, error, "Promise rejects with source Observable error"); +}, "first(): Promise rejects with the error emitted from the source Observable"); + +promise_test(async () => { + const source = new Observable(subscriber => { + subscriber.complete(); + }); + + let rejection; + try { + await source.first(); + } catch (e) { + rejection = e; + } + + assert_true(rejection instanceof RangeError, + "Upon complete(), first() Promise rejects with RangeError"); + assert_equals(rejection.message, "No values in Observable"); +}, "first(): Promise rejects with RangeError when source Observable " + + "completes without emitting any values"); + +promise_test(async () => { + const source = new Observable(subscriber => {}); + + const controller = new AbortController(); + const promise = source.first({ signal: controller.signal }); + + controller.abort(); + + let rejection; + try { + await promise; + } catch (e) { + rejection = e; + } + + assert_true(rejection instanceof DOMException, + "Promise rejects with a DOMException for abortion"); + assert_equals(rejection.name, "AbortError", + "Rejected with 'AbortError' DOMException"); + assert_equals(rejection.message, "signal is aborted without reason"); +}, "first(): Aborting a signal rejects the Promise with an AbortError DOMException"); + +promise_test(async () => { + const results = []; + + const source = new Observable(subscriber => { + results.push("source subscribe"); + subscriber.addTeardown(() => results.push("source teardown")); + subscriber.signal.addEventListener("abort", () => results.push("source abort")); + results.push("before source next 1"); + subscriber.next(1); + results.push("after source next 1"); + }); + + results.push("calling first"); + const promise = source.first(); + + assert_array_equals(results, [ + "calling first", + "source subscribe", + "before source next 1", + "source teardown", + "source abort", + "after source next 1" + ], "Array values after first() is called"); + + const firstValue = await promise; + results.push(`first resolved with: ${firstValue}`); + + assert_array_equals(results, [ + "calling first", + "source subscribe", + "before source next 1", + "source teardown", + "source abort", + "after source next 1", + "first resolved with: 1", + ], "Array values after Promise is awaited"); +}, "first(): Lifecycle"); diff --git a/testing/web-platform/tests/dom/observable/tentative/observable-flatMap.any.js b/testing/web-platform/tests/dom/observable/tentative/observable-flatMap.any.js new file mode 100644 index 0000000000..7cbfa6cb60 --- /dev/null +++ b/testing/web-platform/tests/dom/observable/tentative/observable-flatMap.any.js @@ -0,0 +1,315 @@ +test(() => { + const source = new Observable(subscriber => { + subscriber.next(1); + subscriber.next(2); + subscriber.next(3); + subscriber.complete(); + }); + + let projectionCalls = 0; + + const results = []; + + const flattened = source.flatMap(value => { + projectionCalls++; + return new Observable((subscriber) => { + subscriber.next(value * 10); + subscriber.next(value * 100); + subscriber.complete(); + }); + }); + + assert_true(flattened instanceof Observable, "flatMap() returns an Observable"); + assert_equals(projectionCalls, 0, + "Projection is not called until subscription starts"); + + flattened.subscribe({ + next: v => results.push(v), + error: () => results.push("error"), + complete: () => results.push("complete"), + }); + + assert_equals(projectionCalls, 3, + "Mapper is called three times, once for each source Observable value"); + assert_array_equals(results, [10, 100, 20, 200, 30, 300, "complete"], + "flatMap() results are correct"); +}, "flatMap(): Flattens simple source Observable properly"); + +test(() => { + const error = new Error("error"); + const source = new Observable(subscriber => { + subscriber.next(1); + subscriber.next(2); + subscriber.error(error); + subscriber.next(3); + }); + + const flattened = source.flatMap(value => { + return new Observable(subscriber => { + subscriber.next(value * 10); + subscriber.next(value * 100); + subscriber.complete(); + }); + }); + + const results = []; + + flattened.subscribe({ + next: v => results.push(v), + error: e => results.push(e), + complete: () => results.push("complete"), + }); + + assert_array_equals(results, [10, 100, 20, 200, error], + "Source error is passed through to the flatMap() Observable"); +}, "flatMap(): Returned Observable passes through source Observable errors"); + +test(() => { + const results = []; + const error = new Error("error"); + const source = new Observable(subscriber => { + subscriber.next(1); + results.push(subscriber.active ? "active" : "inactive"); + subscriber.next(2); + results.push(subscriber.active ? "active" : "inactive"); + subscriber.next(3); + subscriber.complete(); + }); + + const flattened = source.flatMap((value) => { + return new Observable((subscriber) => { + subscriber.next(value * 10); + subscriber.next(value * 100); + if (value === 2) { + subscriber.error(error); + } else { + subscriber.complete(); + } + }); + }); + + flattened.subscribe({ + next: v => results.push(v), + error: e => results.push(e), + complete: () => results.push("complete"), + }); + + assert_array_equals(results, [10, 100, "active", 20, 200, error, "inactive"], + "Inner subscription error gets surfaced"); +}, "flatMap(): Outer Subscription synchronously becomes inactive when an " + + "'inner' Observable emits an error"); + +test(() => { + const results = []; + const error = new Error("error"); + const source = new Observable(subscriber => { + subscriber.next(1); + subscriber.next(2); + subscriber.next(3); + results.push(subscriber.active ? "active" : "inactive"); + subscriber.complete(); + }); + + const flattened = source.flatMap(value => { + if (value === 3) { + throw error; + } + return new Observable(subscriber => { + subscriber.next(value * 10); + subscriber.next(value * 100); + subscriber.complete(); + }); + }); + + flattened.subscribe({ + next: v => results.push(v), + error: e => results.push(e), + complete: () => results.push("complete"), + }); + + assert_array_equals(results, [10, 100, 20, 200, error, "inactive"], + "Inner subscriber thrown error gets surfaced"); +}, "flatMap(): Outer Subscription synchronously becomes inactive when an " + + "'inner' Observable throws an error"); + +test(() => { + const source = createTestSubject(); + const inner1 = createTestSubject(); + const inner2 = createTestSubject(); + + const flattened = source.flatMap(value => { + if (value === 1) { + return inner1; + } + + return inner2; + }); + + const results = []; + + flattened.subscribe({ + next: v => results.push(v), + error: e => results.push(e), + complete: () => results.push("complete"), + }); + + assert_array_equals(results, []); + + source.next(1); + assert_equals(inner1.subscriberCount(), 1, "inner1 gets subscribed to"); + + source.next(2); + assert_equals(inner2.subscriberCount(), 0, + "inner2 is queued, not subscribed to until inner1 completes"); + + assert_array_equals(results, []); + + inner1.next(100); + inner1.next(101); + + assert_array_equals(results, [100, 101]); + + inner1.complete(); + assert_equals(inner1.subscriberCount(), 0, + "inner1 becomes inactive once it completes"); + assert_equals(inner2.subscriberCount(), 1, + "inner2 gets un-queued and subscribed to once inner1 completes"); + + inner2.next(200); + inner2.next(201); + assert_array_equals(results, [100, 101, 200, 201]); + + inner2.complete(); + assert_equals(inner2.subscriberCount(), 0, + "inner2 becomes inactive once it completes"); + assert_equals(source.subscriberCount(), 1, + "source is not unsubscribed from yet, since it has not completed"); + assert_array_equals(results, [100, 101, 200, 201]); + + source.complete(); + assert_equals(source.subscriberCount(), 0, + "source unsubscribed from after it completes"); + + assert_array_equals(results, [100, 101, 200, 201, "complete"]); +}, "flatMap(): result Observable does not complete until source and inner " + + "Observables all complete"); + +test(() => { + const source = createTestSubject(); + const inner1 = createTestSubject(); + const inner2 = createTestSubject(); + + const flattened = source.flatMap(value => { + if (value === 1) { + return inner1; + } + + return inner2; + }); + + const results = []; + + flattened.subscribe({ + next: v => results.push(v), + error: e => results.push(e), + complete: () => results.push("complete"), + }); + + assert_array_equals(results, []); + + source.next(1); + source.next(2); + assert_equals(inner1.subscriberCount(), 1, "inner1 gets subscribed to"); + assert_equals(inner2.subscriberCount(), 0, + "inner2 is queued, not subscribed to until inner1 completes"); + + assert_array_equals(results, []); + + // Before `inner1` pushes any values, we first complete the source Observable. + // This will not fire completion of the Observable returned from `flatMap()`, + // because there are two values (corresponding to inner Observables) that are + // queued to the inner queue that need to be processed first. Once the last + // one of *those* completes (i.e., `inner2.complete()` further down), then the + // returned Observable can finally complete. + source.complete(); + assert_equals(source.subscriberCount(), 0, + "source becomes inactive once it completes"); + + inner1.next(100); + inner1.next(101); + + assert_array_equals(results, [100, 101]); + + inner1.complete(); + assert_array_equals(results, [100, 101], + "Outer completion not triggered after inner1 completes"); + assert_equals(inner2.subscriberCount(), 1, + "inner2 gets un-queued and subscribed after inner1 completes"); + + inner2.next(200); + inner2.next(201); + assert_array_equals(results, [100, 101, 200, 201]); + + inner2.complete(); + assert_equals(inner2.subscriberCount(), 0, + "inner2 becomes inactive once it completes"); + assert_array_equals(results, [100, 101, 200, 201, "complete"]); +}, "flatMap(): result Observable does not complete after source Observable " + + "completes while there are still queued inner Observables to process " + + "Observables all complete"); + +test(() => { + const source = createTestSubject(); + const inner = createTestSubject(); + const result = source.flatMap(() => inner); + + const ac = new AbortController(); + + result.subscribe({}, { signal: ac.signal, }); + + source.next(1); + + assert_equals(inner.subscriberCount(), 1, + "inner Observable subscribed to once source emits it"); + + ac.abort(); + + assert_equals(source.subscriberCount(), 0, + "source unsubscribed from, once outer signal is aborted"); + + assert_equals(inner.subscriberCount(), 0, + "inner Observable unsubscribed from once the outer Observable is " + + "subscribed from, as a result of the outer signal being aborted"); +}, "flatMap(): source and inner active Observables are both unsubscribed " + + "from once the outer subscription signal is aborted"); + +// A helper function to create an Observable that can be externally controlled +// and examined for testing purposes. +function createTestSubject() { + const subscribers = new Set(); + const subject = new Observable(subscriber => { + subscribers.add(subscriber); + subscriber.addTeardown(() => subscribers.delete(subscriber)); + }); + + subject.next = value => { + for (const subscriber of Array.from(subscribers)) { + subscriber.next(value); + } + }; + subject.error = error => { + for (const subscriber of Array.from(subscribers)) { + subscriber.error(error); + } + }; + subject.complete = () => { + for (const subscriber of Array.from(subscribers)) { + subscriber.complete(); + } + }; + subject.subscriberCount = () => { + return subscribers.size; + }; + + return subject; +} diff --git a/testing/web-platform/tests/dom/observable/tentative/observable-from.any.js b/testing/web-platform/tests/dom/observable/tentative/observable-from.any.js new file mode 100644 index 0000000000..80408ddced --- /dev/null +++ b/testing/web-platform/tests/dom/observable/tentative/observable-from.any.js @@ -0,0 +1,354 @@ +// Because we test that the global error handler is called at various times. +setup({allow_uncaught_exception: true}); + +test(() => { + assert_equals(typeof Observable.from, "function", + "Observable.from() is a function"); +}, "from(): Observable.from() is a function"); + +test(() => { + assert_throws_js(TypeError, () => Observable.from(10), + "Number cannot convert to an Observable"); + assert_throws_js(TypeError, () => Observable.from(true), + "Boolean cannot convert to an Observable"); + assert_throws_js(TypeError, () => Observable.from("String"), + "String cannot convert to an Observable"); + assert_throws_js(TypeError, () => Observable.from({a: 10}), + "Object cannot convert to an Observable"); + assert_throws_js(TypeError, () => Observable.from(Symbol.iterator), + "Bare Symbol.iterator cannot convert to an Observable"); + assert_throws_js(TypeError, () => Observable.from(Promise), + "Promise constructor cannot convert to an Observable"); +}, "from(): Failed conversions"); + +test(() => { + const target = new EventTarget(); + const observable = target.on('custom'); + const from_observable = Observable.from(observable); + assert_equals(observable, from_observable); +}, "from(): Given an observable, it returns that exact observable"); + +test(() => { + let completeCalled = false; + const results = []; + const array = [1, 2, 3, 'a', new Date(), 15, [12]]; + const observable = Observable.from(array); + observable.subscribe({ + next: v => results.push(v), + error: e => assert_unreached('error is not called'), + complete: () => completeCalled = true + }); + + assert_array_equals(results, array); + assert_true(completeCalled); +}, "from(): Given an array"); + +test(() => { + const iterable = { + [Symbol.iterator]() { + let n = 0; + return { + next() { + n++; + if (n <= 3) { + return { value: n, done: false }; + } + return { value: undefined, done: true }; + }, + }; + }, + }; + + const observable = Observable.from(iterable); + + assert_true(observable instanceof Observable, "Observable.from() returns an Observable"); + + const results = []; + + observable.subscribe({ + next: (value) => results.push(value), + error: () => assert_unreached("should not error"), + complete: () => results.push("complete"), + }); + + assert_array_equals(results, [1, 2, 3, "complete"], + "Subscription pushes iterable values out to Observable"); + + // A second subscription should restart iteration. + observable.subscribe({ + next: (value) => results.push(value), + error: () => assert_unreached("should not error"), + complete: () => results.push("complete2"), + }); + + assert_array_equals(results, [1, 2, 3, "complete", 1, 2, 3, "complete2"], + "Subscribing again causes another fresh iteration on an un-exhausted iterable"); +}, "from(): Iterable converts to Observable"); + +// The result of the @@iterator method of the converted object is called: +// 1. Once on conversion (to test that the value is an iterable). +// 2. Once on subscription, to re-pull the iterator implementation from the +// raw JS object that the Observable owns once synchronous iteration is +// about to begin. +test(() => { + let numTimesSymbolIteratorCalled = 0; + let numTimesNextCalled = 0; + + const iterable = { + [Symbol.iterator]() { + numTimesSymbolIteratorCalled++; + return { + next() { + numTimesNextCalled++; + return {value: undefined, done: true}; + } + }; + } + }; + + const observable = Observable.from(iterable); + + assert_equals(numTimesSymbolIteratorCalled, 1, + "Observable.from(iterable) invokes the @@iterator method getter once"); + assert_equals(numTimesNextCalled, 0, + "Iterator next() is not called until subscription"); + + // Override iterable's `[Symbol.iterator]` protocol with an error-throwing + // function. We assert that on subscription, this method (the new `@@iterator` + // implementation), is called because only the raw JS object gets stored in + // the Observable that results in conversion. This raw value must get + // re-converted to an iterable once iteration is about to start. + const customError = new Error('@@iterator override error'); + iterable[Symbol.iterator] = () => { + throw customError; + }; + + let thrownError = null; + observable.subscribe({ + error: e => thrownError = e, + }); + + assert_equals(thrownError, customError, + "Error thrown from next() is passed to the error() handler"); + + assert_equals(numTimesSymbolIteratorCalled, 1, + "Subscription re-invokes @@iterator method, which now is a different " + + "method that does *not* increment our assertion value"); + assert_equals(numTimesNextCalled, 0, "Iterator next() is never called"); +}, "from(): [Symbol.iterator] side-effects (one observable)"); + +// Similar to the above test, but with more Observables! +test(() => { + let numTimesSymbolIteratorCalled = 0; + let numTimesNextCalled = 0; + + const iterable = { + [Symbol.iterator]() { + numTimesSymbolIteratorCalled++; + return { + next() { + numTimesNextCalled++; + return {value: undefined, done: true}; + } + }; + } + }; + + const obs1 = Observable.from(iterable); + const obs2 = Observable.from(iterable); + const obs3 = Observable.from(iterable); + const obs4 = Observable.from(obs3); + + assert_equals(numTimesSymbolIteratorCalled, 3, "Observable.from(iterable) invokes the iterator method getter once"); + assert_equals(numTimesNextCalled, 0, "Iterator next() is not called until subscription"); + + iterable[Symbol.iterator] = () => { + throw new Error('Symbol.iterator override error'); + }; + + let errorCount = 0; + + const observer = {error: e => errorCount++}; + obs1.subscribe(observer); + obs2.subscribe(observer); + obs3.subscribe(observer); + obs4.subscribe(observer); + assert_equals(errorCount, 4, + "Error-throwing `@@iterator` implementation is called once per " + + "subscription"); + + assert_equals(numTimesSymbolIteratorCalled, 3, + "Subscription re-invokes the iterator method getter once"); + assert_equals(numTimesNextCalled, 0, "Iterator next() is never called"); +}, "from(): [Symbol.iterator] side-effects (many observables)"); + +test(() => { + const customError = new Error('@@iterator next() error'); + const iterable = { + [Symbol.iterator]() { + return { + next() { + throw customError; + } + }; + } + }; + + let thrownError = null; + Observable.from(iterable).subscribe({ + error: e => thrownError = e, + }); + + assert_equals(thrownError, customError, + "Error thrown from next() is passed to the error() handler"); +}, "from(): [Symbol.iterator] next() throws error"); + +promise_test(async () => { + const promise = Promise.resolve('value'); + const observable = Observable.from(promise); + + assert_true(observable instanceof Observable, "Converts to Observable"); + + const results = []; + + observable.subscribe({ + next: (value) => results.push(value), + error: () => assert_unreached("error() is not called"), + complete: () => results.push("complete()"), + }); + + assert_array_equals(results, [], "Observable does not emit synchronously"); + + await promise; + + assert_array_equals(results, ["value", "complete()"], "Observable emits and completes after Promise resolves"); +}, "from(): Converts Promise to Observable"); + +promise_test(async t => { + let unhandledRejectionHandlerCalled = false; + const unhandledRejectionHandler = () => { + unhandledRejectionHandlerCalled = true; + }; + + self.addEventListener("unhandledrejection", unhandledRejectionHandler); + t.add_cleanup(() => self.removeEventListener("unhandledrejection", unhandledRejectionHandler)); + + const promise = Promise.reject("reason"); + const observable = Observable.from(promise); + + assert_true(observable instanceof Observable, "Converts to Observable"); + + const results = []; + + observable.subscribe({ + next: (value) => assert_unreached("next() not called"), + error: (error) => results.push(error), + complete: () => assert_unreached("complete() not called"), + }); + + assert_array_equals(results, [], "Observable does not emit synchronously"); + + let catchBlockEntered = false; + try { + await promise; + } catch { + catchBlockEntered = true; + } + + assert_true(catchBlockEntered, "Catch block entered"); + assert_false(unhandledRejectionHandlerCalled, "No unhandledrejection event"); + assert_array_equals(results, ["reason"], + "Observable emits error() after Promise rejects"); +}, "from(): Converts rejected Promise to Observable. No " + + "`unhandledrejection` event when error is handled by subscription"); + +promise_test(async t => { + let unhandledRejectionHandlerCalled = false; + const unhandledRejectionHandler = () => { + unhandledRejectionHandlerCalled = true; + }; + + self.addEventListener("unhandledrejection", unhandledRejectionHandler); + t.add_cleanup(() => self.removeEventListener("unhandledrejection", unhandledRejectionHandler)); + + let errorReported = null; + self.addEventListener("error", e => errorReported = e, { once: true }); + + let catchBlockEntered = false; + try { + const promise = Promise.reject("custom reason"); + const observable = Observable.from(promise); + + observable.subscribe(); + await promise; + } catch { + catchBlockEntered = true; + } + + assert_true(catchBlockEntered, "Catch block entered"); + assert_false(unhandledRejectionHandlerCalled, + "No unhandledrejection event, because error got reported to global"); + assert_not_equals(errorReported, null, "Error was reported to the global"); + + assert_true(errorReported.message.includes("custom reason"), + "Error message matches"); + assert_equals(errorReported.lineno, 0, "Error lineno is 0"); + assert_equals(errorReported.colno, 0, "Error lineno is 0"); + assert_equals(errorReported.error, "custom reason", + "Error object is equivalent"); +}, "from(): Rejections not handled by subscription are reported to the " + + "global, and still not sent as an unhandledrejection event"); + +test(() => { + const results = []; + const observable = new Observable(subscriber => { + subscriber.next('from Observable'); + subscriber.complete(); + }); + + observable[Symbol.iterator] = () => { + results.push('Symbol.iterator() called'); + return { + next() { + return {value: 'from @@iterator', done: true}; + } + }; + }; + + Observable.from(observable).subscribe({ + next: v => results.push(v), + complete: () => results.push("complete"), + }); + + assert_array_equals(results, ["from Observable", "complete"]); +}, "from(): Observable that implements @@iterator protocol gets converted " + + "as an Observable, not iterator"); + +test(() => { + const results = []; + const promise = new Promise(resolve => { + resolve('from Promise'); + }); + + promise[Symbol.iterator] = () => { + let done = false; + return { + next() { + if (!done) { + done = true; + return {value: 'from @@iterator', done: false}; + } else { + return {value: undefined, done: true}; + } + } + }; + }; + + Observable.from(promise).subscribe({ + next: v => results.push(v), + complete: () => results.push("complete"), + }); + + assert_array_equals(results, ["from @@iterator", "complete"]); +}, "from(): Promise that implements @@iterator protocol gets converted as " + + "an iterable, not Promise"); diff --git a/testing/web-platform/tests/dom/observable/tentative/observable-last.any.js b/testing/web-platform/tests/dom/observable/tentative/observable-last.any.js new file mode 100644 index 0000000000..cd39a3700a --- /dev/null +++ b/testing/web-platform/tests/dom/observable/tentative/observable-last.any.js @@ -0,0 +1,113 @@ +promise_test(async () => { + const source = new Observable(subscriber => { + // Never exposed to the `last()` promise. + subscriber.next(1); + + subscriber.next(2); + subscriber.complete(); + }); + + const value = await source.last(); + + assert_equals(value, 2); +}, "last(): Promise resolves to last value"); + +promise_test(async () => { + const error = new Error("error from source"); + const source = new Observable(subscriber => { + subscriber.error(error); + }); + + let rejection = null; + try { + await source.last(); + } catch (e) { + rejection = e; + } + + assert_equals(rejection, error); +}, "last(): Promise rejects with emitted error"); + +promise_test(async () => { + const source = new Observable(subscriber => { + subscriber.complete(); + }); + + let rejection = null; + try { + await source.last(); + } catch (e) { + rejection = e; + } + + assert_true(rejection instanceof RangeError, + "Promise rejects with RangeError"); + assert_equals(rejection.message, "No values in Observable"); +}, "last(): Promise rejects with RangeError when source Observable " + + "completes without emitting any values"); + +promise_test(async () => { + const source = new Observable(subscriber => {}); + + const controller = new AbortController(); + const promise = source.last({ signal: controller.signal }); + + controller.abort(); + + let rejection = null; + try { + await promise; + } catch (e) { + rejection = e; + } + + assert_true(rejection instanceof DOMException, + "Promise rejects with a DOMException for abortion"); + assert_equals(rejection.name, "AbortError", + "Rejected with 'AbortError' DOMException"); + assert_equals(rejection.message, "signal is aborted without reason"); +}, "last(): Aborting a signal rejects the Promise with an AbortError DOMException"); + +promise_test(async () => { + const results = []; + const source = new Observable(subscriber => { + results.push("source subscribe"); + subscriber.addTeardown(() => results.push("source teardown")); + subscriber.signal.addEventListener("abort", () => results.push("source abort")); + results.push("before source next 1"); + subscriber.next(1); + results.push("after source next 1"); + results.push("before source complete"); + subscriber.complete(); + results.push("after source complete"); + }); + + results.push("calling last"); + const promise = source.last(); + + assert_array_equals(results, [ + "calling last", + "source subscribe", + "before source next 1", + "after source next 1", + "before source complete", + "source teardown", + "source abort", + "after source complete", + ], "Array values after last() is called"); + + const lastValue = await promise; + results.push(`last resolved with: ${lastValue}`); + + assert_array_equals(results, [ + "calling last", + "source subscribe", + "before source next 1", + "after source next 1", + "before source complete", + "source teardown", + "source abort", + "after source complete", + "last resolved with: 1", + ], "Array values after Promise is awaited"); +}, "last(): Lifecycle"); diff --git a/testing/web-platform/tests/dom/observable/tentative/observable-switchMap.any.js b/testing/web-platform/tests/dom/observable/tentative/observable-switchMap.any.js new file mode 100644 index 0000000000..836a39a68e --- /dev/null +++ b/testing/web-platform/tests/dom/observable/tentative/observable-switchMap.any.js @@ -0,0 +1,252 @@ +test(() => { + const source = createTestSubject(); + const inner1 = createTestSubject(); + const inner2 = createTestSubject(); + + const result = source.switchMap((value, index) => { + if (value === 1) { + return inner1; + } + if (value === 2) { + return inner2; + } + throw new Error("invalid "); + }); + + const results = []; + + result.subscribe({ + next: v => results.push(v), + error: e => results.push(e), + complete: () => results.push("complete"), + }); + + assert_equals(source.subscriberCount(), 1, + "source observable is subscribed to"); + + source.next(1); + assert_equals(inner1.subscriberCount(), 1, + "inner1 observable is subscribed to"); + + inner1.next("1a"); + assert_array_equals(results, ["1a"]); + + inner1.next("1b"); + assert_array_equals(results, ["1a", "1b"]); + + source.next(2); + assert_equals(inner1.subscriberCount(), 0, + "inner1 observable is unsubscribed from"); + assert_equals(inner2.subscriberCount(), 1, + "inner2 observable is subscribed to"); + + inner2.next("2a"); + assert_array_equals(results, ["1a", "1b", "2a"]); + + inner2.next("2b"); + assert_array_equals(results, ["1a", "1b", "2a", "2b"]); + + inner2.complete(); + assert_array_equals(results, ["1a", "1b", "2a", "2b"]); + + source.complete(); + assert_array_equals(results, ["1a", "1b", "2a", "2b", "complete"]); +}, "switchMap(): result subscribes to one inner observable at a time, " + + "unsubscribing from the previous active one when a new one replaces it"); + +test(() => { + const source = createTestSubject(); + const inner = createTestSubject(); + + const result = source.switchMap(() => inner); + + const results = []; + + result.subscribe({ + next: v => results.push(v), + error: e => results.push(e), + complete: () => results.push("complete"), + }); + + assert_equals(source.subscriberCount(), 1, + "source observable is subscribed to"); + assert_equals(inner.subscriberCount(), 0, + "inner observable is not subscribed to"); + + source.next(1); + assert_equals(inner.subscriberCount(), 1, + "inner observable is subscribed to"); + + inner.next("a"); + assert_array_equals(results, ["a"]); + + inner.next("b"); + assert_array_equals(results, ["a", "b"]); + + source.complete(); + assert_array_equals(results, ["a", "b"], + "Result observable does not complete when source observable completes, " + + "because inner is still active"); + + inner.next("c"); + assert_array_equals(results, ["a", "b", "c"]); + + inner.complete(); + assert_array_equals(results, ["a", "b", "c", "complete"], + "Result observable completes when inner observable completes, because " + + "source is already complete"); +}, "switchMap(): result does not complete when the source observable " + + "completes, if the inner observable is still active"); + +test(() => { + const source = createTestSubject(); + + const e = new Error('thrown from mapper'); + const result = source.switchMap(() => { + throw e; + }); + + const results = []; + + result.subscribe({ + next: v => results.push(v), + error: e => results.push(e), + complete: () => results.push("complete"), + }); + + assert_equals(source.subscriberCount(), 1, + "source observable is subscribed to"); + + source.next(1); + assert_array_equals(results, [e]); + assert_equals(source.subscriberCount(), 0, + "source observable is unsubscribed from"); +}, "switchMap(): result emits an error if Mapper callback throws an error"); + +test(() => { + const source = createTestSubject(); + const inner = createTestSubject(); + + const result = source.switchMap(() => inner); + + const results = []; + + result.subscribe({ + next: v => results.push(v), + error: e => results.push(e), + complete: () => results.push("complete"), + }); + + source.next(1); + inner.next("a"); + assert_array_equals(results, ["a"]); + + const e = new Error('error from source'); + source.error(e); + assert_array_equals(results, ["a", e], + "switchMap result emits an error if the source emits an error"); + assert_equals(inner.subscriberCount(), 0, + "inner observable is unsubscribed from"); + assert_equals(source.subscriberCount(), 0, + "source observable is unsubscribed from"); +}, "switchMap(): result emits an error if the source observable emits an " + + "error"); + +test(() => { + const source = createTestSubject(); + const inner = createTestSubject(); + + const result = source.switchMap(() => inner); + + const results = []; + + result.subscribe({ + next: v => results.push(v), + error: e => results.push(e), + complete: () => results.push("complete"), + }); + + source.next(1); + inner.next("a"); + assert_array_equals(results, ["a"]); + + const e = new Error("error from inner"); + inner.error(e); + assert_array_equals(results, ["a", e], + "result emits an error if the inner observable emits an error"); + assert_equals(inner.subscriberCount(), 0, + "inner observable is unsubscribed from"); + assert_equals(source.subscriberCount(), 0, + "source observable is unsubscribed from"); +}, "switchMap(): result emits an error if the inner observable emits an error"); + +test(() => { + const results = []; + const source = new Observable(subscriber => { + subscriber.next(1); + subscriber.addTeardown(() => { + results.push('source teardown'); + }); + subscriber.signal.onabort = e => { + results.push('source onabort'); + }; + }); + + const inner = new Observable(subscriber => { + subscriber.addTeardown(() => { + results.push('inner teardown'); + }); + subscriber.signal.onabort = () => { + results.push('inner onabort'); + }; + }); + + const result = source.switchMap(() => inner); + + const ac = new AbortController(); + result.subscribe({ + next: v => results.push(v), + error: e => results.error(e), + complete: () => results.complete("complete"), + }, {signal: ac.signal}); + + ac.abort(); + assert_array_equals(results, [ + "source teardown", + "source onabort", + "inner teardown", + "inner onabort", + ], "Unsubscription order is correct"); +}, "switchMap(): should unsubscribe in the correct order when user aborts " + + "the subscription"); + +// A helper function to create an Observable that can be externally controlled +// and examined for testing purposes. +function createTestSubject() { + const subscribers = new Set(); + const subject = new Observable(subscriber => { + subscribers.add(subscriber); + subscriber.addTeardown(() => subscribers.delete(subscriber)); + }); + + subject.next = value => { + for (const subscriber of Array.from(subscribers)) { + subscriber.next(value); + } + }; + subject.error = error => { + for (const subscriber of Array.from(subscribers)) { + subscriber.error(error); + } + }; + subject.complete = () => { + for (const subscriber of Array.from(subscribers)) { + subscriber.complete(); + } + }; + subject.subscriberCount = () => { + return subscribers.size; + }; + + return subject; +} diff --git a/testing/web-platform/tests/domxpath/fn-lang.html b/testing/web-platform/tests/domxpath/fn-lang.html index c7c102945d..1fbd0a2ee4 100644 --- a/testing/web-platform/tests/domxpath/fn-lang.html +++ b/testing/web-platform/tests/domxpath/fn-lang.html @@ -1,6 +1,5 @@ <!DOCTYPE html> <link rel="help" href="https://www.w3.org/TR/1999/REC-xpath-19991116/#function-lang"> -<link rel="help" href="https://www.w3.org/TR/xpath-functions-31/#func-lang"> <body> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> @@ -35,13 +34,7 @@ testFirstChild('lang("ja")', '<root xml:lang="ja"><match/></root>'); // equal to the argument ignoring that suffix of the attribute value testFirstChild('lang("ja")', '<root xml:lang="ja-jp"><unmatch xml:lang="ja_JP"/></root>'); -// U+212A should match to ASCII 'k'. -// XPath 1.0 says: -// ... such that the attribute value is equal to the argument ignoring that suffix -// of the attribute value and ignoring case. -// XPath 3.1 says: -// ... true if and only if, based on a caseless default match as specified in -// section 3.13 of The Unicode Standard, -testFirstChild('lang("ko")', '<root><match xml:lang="Ko"/></root>'); +// XPath 3.1 is not to be followed as per: https://github.com/whatwg/dom/issues/1199 +testFirstChild('lang("ko")', '<root><unmatch xml:lang="Ko"/></root>'); </script> </body> diff --git a/testing/web-platform/tests/editing/crashtests/caret-display-list-002.html b/testing/web-platform/tests/editing/crashtests/caret-display-list-002.html new file mode 100644 index 0000000000..23f0496efd --- /dev/null +++ b/testing/web-platform/tests/editing/crashtests/caret-display-list-002.html @@ -0,0 +1,35 @@ +<html class="test-wait reftest-wait"> +<style> +#a { + float: none; +} +#c { + transition-delay: 1s; +} +* { + border-style: solid inset dashed; + translate: 0px 0px; + perspective: 0em; + display: inline; + float: left; + contain: size layout paint; +} +</style> +<script> +function func_0() { + a.focus() + b.cellSpacing = "73px" + document.execCommand("indent", false) + requestAnimationFrame(() => requestAnimationFrame(() => { + document.documentElement.className = ""; + })); +} +document.addEventListener("DOMContentLoaded", () => { + window.ontransitionend = func_0 + document.execCommand("outdent", false) +}) +</script> +<details ontoggle="func_0()" open=""> +<summary id="a" contenteditable="true"> +<table id="b"> +<tr id="c"> diff --git a/testing/web-platform/tests/editing/crashtests/caret-display-list.html b/testing/web-platform/tests/editing/crashtests/caret-display-list.html new file mode 100644 index 0000000000..f7c7a740e1 --- /dev/null +++ b/testing/web-platform/tests/editing/crashtests/caret-display-list.html @@ -0,0 +1,26 @@ +<html class="test-wait reftest-wait"> +<style> +* { + backdrop-filter: hue-rotate(0deg); + offset: path('M 72 1 h 0 v 90') 0px 0rad; + mask-image: url(#x) +} +</style> +<script> +let stop = false; +function func_01() { + document.execCommand("justifyCenter", false) + document.getSelection().collapse(a) + requestAnimationFrame(() => requestAnimationFrame(() => { + if (stop) { + document.documentElement.classList = ""; + return; + } + // Ensure that we've painted at least twice. + stop = true; + func_01(); + })); +} +document.addEventListener("DOMContentLoaded", func_01) +</script> +<meter id="a" contenteditable="true">AA</meter> diff --git a/testing/web-platform/tests/editing/crashtests/designMode-caret-change.html b/testing/web-platform/tests/editing/crashtests/designMode-caret-change.html new file mode 100644 index 0000000000..be80afd4ab --- /dev/null +++ b/testing/web-platform/tests/editing/crashtests/designMode-caret-change.html @@ -0,0 +1,37 @@ +<!doctype html> +<html class="test-wait reftest-wait"> +<style> +button { + background-repeat: no-repeat; +} +*:last-child { + opacity: 0; + animation: kf ease-in, steps(65, start) 0.92 paused; + border-radius: inherit +} +@keyframes kf {} +</style> +<script> +let animationEnded = false; +let selectionChanged = false; +function maybeFinishTest() { + if (animationEnded && selectionChanged) { + requestAnimationFrame(() => requestAnimationFrame(() => { + document.documentElement.className = ""; + })); + } +} +document.addEventListener("DOMContentLoaded", () => { + document.designMode = "on" + window.onanimationend = () => { + document.execCommand("insertHTML", false, "A") + animationEnded = true; + maybeFinishTest(); + } + document.onselectionchange = () => { + document.execCommand("selectAll", false) + selectionChanged = true; + maybeFinishTest(); + } +}) +</script> diff --git a/testing/web-platform/tests/event-timing/interactionid-orphan-pointerup.html b/testing/web-platform/tests/event-timing/interactionid-orphan-pointerup.html new file mode 100644 index 0000000000..9c4a0ecf58 --- /dev/null +++ b/testing/web-platform/tests/event-timing/interactionid-orphan-pointerup.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<html> +<meta charset=utf-8 /> +<title>Event Timing: interactionId-orphan-pointerup.</title> +<button id='testButtonId'>Orphan Pointerup</button> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/resources/testdriver.js></script> +<script src=/resources/testdriver-actions.js></script> +<script src=/resources/testdriver-vendor.js></script> +<script src=resources/event-timing-test-utils.js></script> + +<script> + let observedEntries = []; + const map = new Map(); + const events = ['pointerup']; + + promise_test(async t => { + assert_implements(window.PerformanceEventTiming, 'Event Timing is not supported.'); + + const callback = (entryList) => { observedEntries = observedEntries.concat(entryList.getEntries().filter(filterAndAddToMap(events, map))); }; + const readyToResolve = () => { return observedEntries.length >= 1; }; + const observerPromise = createPerformanceObserverPromise(['event'], callback, readyToResolve); + + await interactAndObserve('orphan-pointerup', document.getElementById('testButtonId'), observerPromise); + assert_equals(map.get('pointerup'), 0, 'Should have a trivial interactionId for orphan pointerup event.'); + }, "Event Timing: Orphan pointerup should not be measured as an interaction."); + +</script> + +</html>
\ No newline at end of file diff --git a/testing/web-platform/tests/event-timing/resources/event-timing-test-utils.js b/testing/web-platform/tests/event-timing/resources/event-timing-test-utils.js index 9670d8d26f..7b281a4e0e 100644 --- a/testing/web-platform/tests/event-timing/resources/event-timing-test-utils.js +++ b/testing/web-platform/tests/event-timing/resources/event-timing-test-utils.js @@ -357,6 +357,13 @@ async function pointerdown(target) { .send(); } +async function pointerup(target) { + const actions = new test_driver.Actions(); + return actions.addPointer("mousePointer", "mouse") + .pointerMove(0, 0, { origin: target }) + .pointerUp() + .send(); +} async function auxPointerdown(target) { const actions = new test_driver.Actions(); return actions.addPointer("mousePointer", "mouse") @@ -443,6 +450,11 @@ async function interactAndObserve(interactionType, target, observerPromise) { interactionPromise = Promise.all([auxPointerdown(target), pointerdown(target)]); break; } + case 'orphan-pointerup': { + addListeners(target, ['pointerup']); + interactionPromise = pointerup(target); + break; + } } return Promise.all([interactionPromise, observerPromise]); } diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-anchor-click-handler.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-anchor-click-handler.https.html index 8ee1cb517f..a451c48f73 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-anchor-click-handler.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-anchor-click-handler.https.html @@ -16,7 +16,7 @@ promise_test(async(t) => { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext( - {generator_api: 'fledge', automatic_beacon: true, + {generator_api: 'fledge', register_beacon: true, origin: get_host_info().HTTPS_REMOTE_ORIGIN}); const new_url = new URL("resources/dummy.html", location.href); let beacon_event = { diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-click-handler.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-click-handler.https.html index 31392fdb99..55f18fd42b 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-click-handler.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-click-handler.https.html @@ -16,7 +16,7 @@ promise_test(async(t) => { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext( - {generator_api: 'fledge', automatic_beacon: true, + {generator_api: 'fledge', register_beacon: true, origin: get_host_info().HTTPS_REMOTE_ORIGIN}); await fencedframe.execute(() => { diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-component-ad.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-component-ad.https.html index 1b1ef2798a..fffff548b4 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-component-ad.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-component-ad.https.html @@ -17,7 +17,7 @@ promise_test(async(t) => { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext({ generator_api: 'fledge', - automatic_beacon: true, + register_beacon: true, num_components: 1, // These headers will also be given to the component ad. headers: [["Allow-Fenced-Frame-Automatic-Beacons", "true"]] diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-false.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-false.https.html index 24440e4b67..a3627036fc 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-false.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-false.https.html @@ -18,7 +18,7 @@ promise_test(async(t) => { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext({ generator_api: 'fledge', - automatic_beacon: true, + register_beacon: true, }); const beacon_event = { eventType: "reserved.top_navigation_start", diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-navigation.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-navigation.https.html index c476e80443..7e974ad63e 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-navigation.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-navigation.https.html @@ -17,7 +17,7 @@ promise_test(async(t) => { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext({ generator_api: 'fledge', - automatic_beacon: true + register_beacon: true }); let beacon_event = { @@ -36,7 +36,7 @@ promise_test(async(t) => { .send(); const received_beacon_data = - await nextAutomaticBeacon(beacon_event.eventType, beacon_event.eventData); + await nextBeacon(beacon_event.eventType, beacon_event.eventData); }, 'Automatic beacon in a cross-origin subframe'); </script> </body> diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-no-data.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-no-data.https.html index dd00721dd8..887a06847f 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-no-data.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-no-data.https.html @@ -17,7 +17,7 @@ promise_test(async(t) => { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext({ generator_api: 'fledge', - automatic_beacon: true + register_beacon: true }); const new_url = new URL("resources/close.html", location.href); diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-no-opt-in.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-no-opt-in.https.html index fa19d17f89..f1817b76dd 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-no-opt-in.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-cross-origin-no-opt-in.https.html @@ -17,7 +17,7 @@ promise_test(async(t) => { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext({ generator_api: 'fledge', - automatic_beacon: true + register_beacon: true }); let beacon_event = { diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-no-destination.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-no-destination.https.html index 696c17f765..1779ba6573 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-no-destination.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-no-destination.https.html @@ -17,7 +17,7 @@ promise_test(async (t) => { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext({ generator_api: "fledge", - automatic_beacon: true, + register_beacon: true, origin: get_host_info().HTTPS_REMOTE_ORIGIN }); diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-no-opt-in.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-no-opt-in.https.html index 177a7c6a51..b6dac31dbd 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-no-opt-in.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-no-opt-in.https.html @@ -17,7 +17,7 @@ promise_test(async (t) => { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext({ generator_api: "fledge", - automatic_beacon: true, + register_beacon: true, }); const new_url = new URL("resources/dummy.html", location.href); diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-two-events-clear.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-two-events-clear.https.html index f759c0620b..ff35398105 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-two-events-clear.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-two-events-clear.https.html @@ -16,7 +16,7 @@ promise_test(async(t) => { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext( - {generator_api: 'fledge', automatic_beacon: true, + {generator_api: 'fledge', register_beacon: true, origin: get_host_info().HTTPS_REMOTE_ORIGIN}); let beacon_event = { diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-two-events-persist.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-two-events-persist.https.html index 906a7a0d9f..b9e582e43e 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-two-events-persist.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-two-events-persist.https.html @@ -16,7 +16,7 @@ promise_test(async(t) => { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext( - {generator_api: 'fledge', automatic_beacon: true, + {generator_api: 'fledge', register_beacon: true, origin: get_host_info().HTTPS_REMOTE_ORIGIN}); // `once` defaults to false. let beacon_event = { diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-unfenced-top.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-unfenced-top.https.html index 342e13321f..0a595c5260 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-unfenced-top.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-unfenced-top.https.html @@ -26,7 +26,7 @@ async function init() { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext( - {generator_api: 'fledge', automatic_beacon: true, + {generator_api: 'fledge', register_beacon: true, origin: get_host_info().HTTPS_REMOTE_ORIGIN}); const beacon_event = { diff --git a/testing/web-platform/tests/fenced-frame/automatic-beacon-use-ancestor-data.https.html b/testing/web-platform/tests/fenced-frame/automatic-beacon-use-ancestor-data.https.html index 39df6f5c73..c5169d4a47 100644 --- a/testing/web-platform/tests/fenced-frame/automatic-beacon-use-ancestor-data.https.html +++ b/testing/web-platform/tests/fenced-frame/automatic-beacon-use-ancestor-data.https.html @@ -15,7 +15,7 @@ promise_test(async(t) => { const actions = new test_driver.Actions(); const fencedframe = await attachFencedFrameContext({ generator_api: 'fledge', - automatic_beacon: true + register_beacon: true }); const new_url = new URL("resources/close.html", location.href); const beacon_data = "this is the beacon data"; @@ -56,7 +56,7 @@ promise_test(async(t) => { // have any beacon data set for `reserved.top_navigation_start`, even though // it does have beacon data set. const received_beacon_data = - await nextAutomaticBeacon(beacon_type, beacon_data); + await nextBeacon(beacon_type, beacon_data); }, 'Documents should use ancestor beacon data if not available'); </script> </body> diff --git a/testing/web-platform/tests/fenced-frame/can-load-api.https.html b/testing/web-platform/tests/fenced-frame/can-load-api.https.html index f9996dd5e9..3b47c87ac0 100644 --- a/testing/web-platform/tests/fenced-frame/can-load-api.https.html +++ b/testing/web-platform/tests/fenced-frame/can-load-api.https.html @@ -11,7 +11,7 @@ async function runTest(expected_result, generator_api, attribute_list, header_list, use_fencedframe=false) { const frame = use_fencedframe ? await attachFencedFrameContext({generator_api: generator_api, attributes:attribute_list, headers:header_list}) : - await attachIFrameContext({generator_api, generator_api, attributes:attribute_list, headers:header_list}); + await attachIFrameContext({generator_api: generator_api, attributes:attribute_list, headers:header_list}); await frame.execute(async (expected_result, attribute_list) => { assert_equals(navigator.canLoadAdAuctionFencedFrame(), expected_result, "A frame with attributes " + attribute_list + " should return " + diff --git a/testing/web-platform/tests/fenced-frame/csp-allowed-transparent.https.html b/testing/web-platform/tests/fenced-frame/csp-allowed-transparent.https.html new file mode 100644 index 0000000000..66259b3a1c --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/csp-allowed-transparent.https.html @@ -0,0 +1,40 @@ +<!DOCTYPE html> +<title>Test transparent fenced frame navigations with allowed CSP</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> +<script src="resources/utils.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/common/dispatcher/dispatcher.js"></script> + +<body> +<script> +const allowedCSPs = [ + "*", + "https://*:*", + get_host_info().HTTPS_ORIGIN, + 'https://' + get_host_info().ORIGINAL_HOST + ":*" +]; +allowedCSPs.forEach((csp) => { + promise_test(async() => { + const iframe = setupCSP(csp); + const key = token(); + + await iframe.execute(async (key) => { + window.addEventListener('securitypolicyviolation', function(e) { + // Write to the server even though the listener is in the same file in + // the test below. + writeValueToServer(key, e.violatedDirective + ";" + e.blockedURI); + }, {once: true}); + + const url = generateURL("/fenced-frame/resources/embeddee.html", [key]); + attachFencedFrame(url); + }, [key]); + + const result = await nextValueFromServer(key); + assert_equals(result, "PASS", + "The fenced frame should load for CSP fenced-frame-src " + csp); + }, "Fenced frame loaded for CSP fenced-frame-src " + csp); +}); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/csp-allowed.https.html b/testing/web-platform/tests/fenced-frame/csp-allowed.https.html index 8c002bc8a9..4b2fadd59e 100644 --- a/testing/web-platform/tests/fenced-frame/csp-allowed.https.html +++ b/testing/web-platform/tests/fenced-frame/csp-allowed.https.html @@ -4,24 +4,31 @@ <script src="/resources/testharnessreport.js"></script> <script src="/common/utils.js"></script> <script src="resources/utils.js"></script> +<script src="/common/dispatcher/dispatcher.js"></script> <body> <script> -for (const resolve_to_config of [true, false]) { - const allowedCSPs = ["*", "https:", "https://*:*"]; - allowedCSPs.forEach((csp) => { +const allowedCSPs = [ + "*", + "https:", + "https://*:*" +]; +allowedCSPs.forEach((csp) => { + for (const resolve_to_config of [true, false]) { promise_test(async() => { - setupCSP(csp); - + const iframe = setupCSP(csp); const key = token(); - window.addEventListener('securitypolicyviolation', function(e) { - // Write to the server even though the listener is in the same file in - // the test below. - writeValueToServer(key, e.violatedDirective + ";" + e.blockedURI); - }, {once: true}); - attachFencedFrame(await runSelectURL("resources/embeddee.html", - [key], resolve_to_config)); + await iframe.execute(async (key, resolve_to_config) => { + window.addEventListener('securitypolicyviolation', function(e) { + // Write to the server even though the listener is in the same file in + // the test below. + writeValueToServer(key, e.violatedDirective + ";" + e.blockedURI); + }, {once: true}); + + attachFencedFrame(await runSelectURL( + "/fenced-frame/resources/embeddee.html", [key], resolve_to_config)); + }, [key, resolve_to_config]); const result = await nextValueFromServer(key); assert_equals(result, "PASS", @@ -29,12 +36,13 @@ for (const resolve_to_config of [true, false]) { }, "Fenced frame loaded for CSP fenced-frame-src " + csp + " using " + (resolve_to_config ? "config" : "urn:uuid")); - promise_test(async() => { - setupCSP(csp); + } + promise_test(async() => { + const iframe = setupCSP(csp); + await iframe.execute(() => { assert_true(navigator.canLoadAdAuctionFencedFrame()); - }, "Opaque-ads can load API returns true for " + csp + " using " + - (resolve_to_config ? "config" : "urn:uuid")); - }); -} + }); + }, "Opaque-ads can load API returns true for " + csp); +}); </script> </body> diff --git a/testing/web-platform/tests/fenced-frame/csp-blocked-transparent.https.html b/testing/web-platform/tests/fenced-frame/csp-blocked-transparent.https.html new file mode 100644 index 0000000000..16a3d94fce --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/csp-blocked-transparent.https.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<title>Test transparent fenced frame navigations with blocked CSP</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> +<script src="resources/utils.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/common/dispatcher/dispatcher.js"></script> + +<body> +<script> +const blockedCSPs = [ + "none", + "https://localhost:80", + "https://*:80", + "https://localhost:*" +]; +blockedCSPs.forEach((csp) => { + promise_test(async() => { + const iframe = setupCSP(csp); + const key = token(); + const url = generateURL("/fenced-frame/resources/embeddee.html", [key]); + + await iframe.execute(async (key, url, csp) => { + let promise = new Promise((resolve) => { + window.addEventListener('securitypolicyviolation', function(e) { + resolve(e.violatedDirective + ";" + e.blockedURI); + }, {once: true}); + }); + + attachFencedFrame(url); + + await promise.then((result) => { + assert_equals(result, "fenced-frame-src;" + url, + "The fenced frame should not load for CSP fenced-frame-src " + csp); + }); + }, [key, url, csp]); + }, "Fenced frame loaded for CSP fenced-frame-src " + csp); +}); +</script> +</body> diff --git a/testing/web-platform/tests/fenced-frame/csp-blocked.https.html b/testing/web-platform/tests/fenced-frame/csp-blocked.https.html index 3826fdd7f4..197c624937 100644 --- a/testing/web-platform/tests/fenced-frame/csp-blocked.https.html +++ b/testing/web-platform/tests/fenced-frame/csp-blocked.https.html @@ -1,65 +1,83 @@ <!DOCTYPE html> <title>Test opaque fenced frame navigations with disallowed CSP blocked</title> +<meta name="timeout" content="long"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/common/utils.js"></script> <script src="resources/utils.js"></script> +<script src="/common/dispatcher/dispatcher.js"></script> <body> <script> +const blockedCSPs = [ + "'none'", + "'self'", + "data:", + "https://*", + "https://*:80", + "https://b.test:*" +]; for (const resolve_to_config of [true, false]) { - const blockedCSPs = ["'none'", "'self'", "data:", "https://*", "https://*:80", - "https://b.test:*"]; blockedCSPs.forEach((csp) => { - promise_test(async() => { - setupCSP(csp); - + promise_test(async(t) => { + const iframe = setupCSP(csp); const key = token(); - window.addEventListener('securitypolicyviolation', function(e) { - // Write to the server even though the listener is in the same file in - // the test below. - writeValueToServer(key, e.violatedDirective + ";" + e.blockedURI); - }, {once: true}); - attachFencedFrame(await runSelectURL("resources/embeddee.html", - [key], resolve_to_config)); + await iframe.execute(async(key, resolve_to_config, csp) => { + let promise = new Promise((resolve) => { + window.addEventListener('securitypolicyviolation', function(e) { + resolve(e.violatedDirective + ";" + e.blockedURI); + }, {once: true}); + }); - const result = await nextValueFromServer(key); - assert_equals(result, "fenced-frame-src;", - "The fenced frame should not load for CSP fenced-frame-src " + csp); - }, "Fenced frame blocked for CSP fenced-frame-src " + csp + " using " + - (resolve_to_config ? "config" : "urn:uuid")); + attachFencedFrame(await runSelectURL( + "/fenced-frame/resources/embeddee.html", [key], resolve_to_config)); - promise_test(async() => { - setupCSP(csp); - assert_false(navigator.canLoadAdAuctionFencedFrame()); - }, "Opaque-ads can load API returns false for " + csp + " using " + + await promise.then((result) => { + assert_equals(result, "fenced-frame-src;", + "The fenced frame should not load for CSP fenced-frame-src " + + csp); + }); + }, [key, resolve_to_config, csp]); + }, "Fenced frame blocked for CSP fenced-frame-src " + csp + " using " + (resolve_to_config ? "config" : "urn:uuid")); }); - promise_test(async() => { - setupCSP("*", "'self'"); - + promise_test(async(t) => { + const iframe = setupCSP("*", "'self'"); const key = token(); - window.addEventListener('securitypolicyviolation', function(e) { - // Write to the server even though the listener is in the same file in - // the test below. - writeValueToServer(key, e.violatedDirective + ";" + e.blockedURI); - }, {once: true}); - attachFencedFrame(await runSelectURL("resources/embeddee.html", - [key], resolve_to_config)); + await iframe.execute(async(key, resolve_to_config) => { + window.addEventListener('securitypolicyviolation', function(e) { + // Write to the server even though the listener is in the same file in + // the test below. + writeValueToServer(key, e.violatedDirective + ";" + e.blockedURI); + }, {once: true}); + attachFencedFrame(await runSelectURL("resources/embeddee.html", + [key], resolve_to_config)); + }, [key, resolve_to_config]); const result = await nextValueFromServer(key); assert_equals(result, "fenced-frame-src;", "The fenced frame should not load for CSP frame-src 'self' even if " + "another CSP allows loading a fenced frame."); - // Test the canLoadOpaqueURL API to ensure it arrives at the same result. - assert_false(navigator.canLoadAdAuctionFencedFrame()); + await iframe.execute(() => { + // Test the canLoadOpaqueURL API to ensure it arrives at the same result. + assert_false(navigator.canLoadAdAuctionFencedFrame()); + }); }, "Fenced frame not loaded using " + (resolve_to_config ? "config" : "urn:uuid") + " if any of CSPs in place disallow loading"); } + +blockedCSPs.forEach((csp) => { + promise_test(async() => { + const iframe = setupCSP(csp); + await iframe.execute(() => { + assert_false(navigator.canLoadAdAuctionFencedFrame()); + }) + }, "Opaque-ads can load API returns false for " + csp); +}); </script> </body> diff --git a/testing/web-platform/tests/fenced-frame/csp-transparent-url.https.html b/testing/web-platform/tests/fenced-frame/csp-transparent-url.https.html deleted file mode 100644 index c1c815d49e..0000000000 --- a/testing/web-platform/tests/fenced-frame/csp-transparent-url.https.html +++ /dev/null @@ -1,53 +0,0 @@ -<!DOCTYPE html> -<title>Test transparent url navigated in fenced frame interacting with CSP</title> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/common/utils.js"></script> -<script src="/common/dispatcher/dispatcher.js"></script> -<script src="resources/utils.js"></script> - -<body> -<script> -function setupCSP(csp) { - let meta = document.createElement('meta'); - meta.httpEquiv = "Content-Security-Policy"; - meta.content = "fenced-frame-src " + csp; - document.head.appendChild(meta); -} - -const allowedCSPs = ["*", "https:", "'self'"]; -allowedCSPs.forEach((csp) => { - promise_test(async(t) => { - setupCSP(csp); - - t.step_timeout(t.unreached_func( - "The fenced frame should load for CSP fenced-frame-src " + csp), 3000); - - const fencedframe = attachFencedFrameContext(); - await fencedframe.execute(() => {}); - }, "Fenced frame loaded for CSP fenced-frame-src " + csp); -}); - -const blockedCSPs = ["'none'"]; -blockedCSPs.forEach((csp) => { - promise_test(async(t) => { - setupCSP(csp); - - const csp_violation = new Promise(resolve => { - window.addEventListener("securitypolicyviolation", resolve); - }); - - const fencedframe = attachFencedFrameContext(); - - const fencedframe_loaded = fencedframe.execute(() => {}); - fencedframe_loaded.then(t.unreached_func( - "The fenced frame should not load for CSP fenced-frame-src " + csp)); - - const csp_violation_event = await csp_violation; - const remote_url = getRemoteContextURL(location.origin).toString(); - assert_true(csp_violation_event.blockedURI.includes(remote_url), - "blockedURI should include the url"); - }, "Fenced frame blocked for CSP fenced-frame-src " + csp); -}); -</script> -</body> diff --git a/testing/web-platform/tests/fenced-frame/disable-untrusted-network.https.html b/testing/web-platform/tests/fenced-frame/disable-untrusted-network.https.html deleted file mode 100644 index 726728e489..0000000000 --- a/testing/web-platform/tests/fenced-frame/disable-untrusted-network.https.html +++ /dev/null @@ -1,49 +0,0 @@ -<!DOCTYPE html> -<title>Test window.fence.disableUntrustedNetwork availability.</title> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/common/utils.js"></script> -<script src="/common/dispatcher/dispatcher.js"></script> -<script src="resources/utils.js"></script> - -<body> -<script> -promise_test(async(t) => { - const fencedframe = await attachFencedFrameContext(); - await fencedframe.execute(async () => { - const cross_origin_fenced_frame = await attachFencedFrameContext({ - origin: get_host_info().HTTPS_REMOTE_ORIGIN}); - await cross_origin_fenced_frame.execute(async () => { - const promise = window.fence.disableUntrustedNetwork(); - assert_true(typeof promise.then != 'undefined'); - await promise; - }); - - const same_origin_iframe = await attachIFrameContext(); - await same_origin_iframe.execute(async () => { - const promise = window.fence.disableUntrustedNetwork(); - assert_true(typeof promise.then != 'undefined'); - await promise; - }); - - const cross_origin_iframe = await attachIFrameContext({ - origin: get_host_info().HTTPS_REMOTE_ORIGIN}); - await cross_origin_iframe.execute(async () => { - try { - const promise = window.fence.disableUntrustedNetwork(); - await promise; - assert_unreached( - 'disableUntrustedNetwork should fail when not same-origin to the ' - + 'mapped url.'); - } catch (e) { - assert_equals(e.name, 'TypeError'); - } - }); - - const promise = window.fence.disableUntrustedNetwork(); - assert_true(typeof promise.then != 'undefined'); - await promise; - }); -}, 'window.fence.disableUntrustedNetwork availability'); -</script> -</body> diff --git a/testing/web-platform/tests/fenced-frame/notify-event-iframe.https.html b/testing/web-platform/tests/fenced-frame/notify-event-iframe.https.html index 854db2f303..1f8e1b8a32 100644 --- a/testing/web-platform/tests/fenced-frame/notify-event-iframe.https.html +++ b/testing/web-platform/tests/fenced-frame/notify-event-iframe.https.html @@ -1,4 +1,5 @@ <!DOCTYPE html> +<meta name="timeout" content="long"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> <script src="/resources/testdriver.js"></script> @@ -20,8 +21,10 @@ fencedframe.element.addEventListener('fencedtreeclick', () => notified = true); await fencedframe.execute(async (frame_type) => { - window.addEventListener('message', (event) => { - window.click_error = event.data; + window.click_error = new Promise((resolve, reject) => { + window.addEventListener('message', (event) => { + resolve(event.data); + }); }); let iframe = null; @@ -53,9 +56,10 @@ await multiClick(10, 10, fencedframe.element); // Ensure the correct exception was thrown. - await fencedframe.execute(() => { - assert_equals(window.click_error.name, 'SecurityError'); - assert_equals(window.click_error.message, + await fencedframe.execute(async () => { + let err = await window.click_error; + assert_equals(err.name, 'SecurityError'); + assert_equals(err.message, "Failed to execute 'notifyEvent' on 'Fence': notifyEvent is only available in fenced frame roots."); }); @@ -73,8 +77,10 @@ }, "Test that fenced frame notifyEvent() fails in a nested cross-origin iframe."); promise_test(async (t) => { - window.addEventListener('message', (event) => { - window.click_error = event.data; + window.click_error = new Promise((resolve, reject) => { + window.addEventListener('message', (event) => { + resolve(event.data); + }); }); const urn_iframe = await attachIFrameContext( @@ -94,8 +100,9 @@ await multiClick(10, 10, urn_iframe.element); - assert_equals(window.click_error.name, 'SecurityError'); - assert_equals(window.click_error.message, + let err = await window.click_error + assert_equals(err.name, 'SecurityError'); + assert_equals(err.message, "Failed to execute 'notifyEvent' on 'Fence': notifyEvent is only available in fenced frame roots."); }, "Test that notifyEvent() fails in a URN iframe."); </script> diff --git a/testing/web-platform/tests/fenced-frame/report-event-reserved-event.https.html b/testing/web-platform/tests/fenced-frame/report-event-reserved-event.https.html index 0a541bb0c5..6baa3cedea 100644 --- a/testing/web-platform/tests/fenced-frame/report-event-reserved-event.https.html +++ b/testing/web-platform/tests/fenced-frame/report-event-reserved-event.https.html @@ -11,7 +11,7 @@ promise_test(async(t) => { const fencedframe = await attachFencedFrameContext({ generator_api: "fledge", - automatic_beacon: true, + register_beacon: true, }); const new_url = new URL("resources/dummy.html", location.href); const beacon_data = "This is the beacon data!"; @@ -31,7 +31,7 @@ promise_test(async(t) => { const timeout = new Promise(resolve => t.step_timeout(resolve, 1000)); const result = await Promise.race( - [nextAutomaticBeacon(beacon_type, beacon_data), timeout]); + [nextBeacon(beacon_type, beacon_data), timeout]); assert_true(typeof result === "undefined", "A beacon should not have been sent."); diff --git a/testing/web-platform/tests/fenced-frame/report-event.https.html b/testing/web-platform/tests/fenced-frame/report-event.https.html new file mode 100644 index 0000000000..c06765b8f9 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/report-event.https.html @@ -0,0 +1,42 @@ +<!DOCTYPE html> +<title>Test that window.fence.reportEvent() succeeds in a fenced frame.</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> +<script src="/common/dispatcher/dispatcher.js"></script> +<script src="resources/utils.js"></script> + +<body> + <script> + promise_test(async(t) => { + const fencedframe = await attachFencedFrameContext({generator_api: 'fledge', + register_beacon: true}); + + // This page will call reportEvent twice: Once for an enum event, and once + // for a custom URL event. Both beacons are expected to send successfully. + await fencedframe.execute(() => { + const destination_enum_event = { + eventType: 'click', + eventData: 'enum', + destination: ['buyer'] + } + window.fence.reportEvent(destination_enum_event); + + const destination_url = new URL(BEACON_URL + "?type=url", + get_host_info().HTTPS_ORIGIN); + + const destination_url_event = { + destinationURL: destination_url + } + window.fence.reportEvent(destination_url_event); + }); + + let enum_data = await nextBeacon('click', 'enum'); + assert_equals(enum_data, location.origin); + + let url_data = await nextBeacon('url', '<No data>'); + assert_equals(url_data, '<No data>'); + + }, 'Test that window.fence.reportEvent() succeeds in a fenced frame.'); + </script> + </body>
\ No newline at end of file diff --git a/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-helper.js b/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-helper.js index d0a4133e84..8286c94fec 100644 --- a/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-helper.js +++ b/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-helper.js @@ -92,13 +92,12 @@ async function setupAutomaticBeacon( async function verifyBeaconData( event_type, event_data, expected_success = true, t) { if (expected_success) { - const beacon_initiator_origin = - await nextAutomaticBeacon(event_type, event_data); + const beacon_initiator_origin = await nextBeacon(event_type, event_data); assert_equals(beacon_initiator_origin, get_host_info().HTTPS_ORIGIN); } else { const timeout = new Promise(r => t.step_timeout(r, 1000)); - const result = await Promise.race( - [nextAutomaticBeacon(event_type, event_data), timeout]); + const result = + await Promise.race([nextBeacon(event_type, event_data), timeout]); assert_true(typeof result === 'undefined'); } } diff --git a/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-store.py b/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-store.py deleted file mode 100644 index ba1b73201b..0000000000 --- a/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-store.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -Automatic beacon store server. - -- When a request body is specified, stores the data in the body and serves a 200 - response without body. -- When a request body is not specified, serves a 200 response whose body - contains the stored value from the automatic beacon. Since the data is stored - using a hash of the data as the key, it expects an `expected_body` query - parameter to know what key to look up. If the stored value doesn't exist, - serves a 200 response with an empty body. -""" -import uuid -import hashlib - -NO_DATA_STRING = b"<No data>" -NOT_SET_STRING = b"<Not set>" - -# The server stash requires a uuid to store data. Use a hash of the automatic -# beacon data as the uuid to store and retrieve the data. -def string_to_uuid(input): - hash_value = hashlib.md5(str(input).encode("UTF-8")).hexdigest() - return str(uuid.UUID(hex=hash_value)) - -def main(request, response): - stash = request.server.stash; - event_type = request.GET.first(b"type", NO_DATA_STRING) - - # The stash is accessed concurrently by many clients. A lock is used to - # avoid interleaved read/write from different clients. - with stash.lock: - # Requests with a body imply they were sent as an automatic beacon. Note - # that this only stores the most recent beacon that was sent. - if request.method == "POST": - request_body = request.body or NO_DATA_STRING - request_headers = request.headers.get("Origin") or NO_DATA_STRING - stash.put(string_to_uuid(event_type + request_body), - request_headers) - return (200, [], b"") - - # Requests without a body imply they were sent as the request from - # nextAutomaticBeacon(). - expected_body = request.GET.first(b"expected_body", NO_DATA_STRING) - data = stash.take(string_to_uuid(event_type + expected_body)) or NOT_SET_STRING - return(200, [], data) diff --git a/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-unfenced-page.html b/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-unfenced-page.html index 4ce7e0d78a..7f17c1a904 100644 --- a/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-unfenced-page.html +++ b/testing/web-platform/tests/fenced-frame/resources/automatic-beacon-unfenced-page.html @@ -13,7 +13,7 @@ // '../automatic-beacon-unfenced-top.https.html'. An automatic beacon will // have been sent as a result of the navigation. const beacon_data = "This is the beacon data!"; - const beacon_initiator_origin = await nextAutomaticBeacon( + const beacon_initiator_origin = await nextBeacon( "reserved.top_navigation_commit", beacon_data); assert_equals(beacon_initiator_origin, get_host_info().HTTPS_ORIGIN); }); diff --git a/testing/web-platform/tests/fenced-frame/resources/beacon-store.py b/testing/web-platform/tests/fenced-frame/resources/beacon-store.py new file mode 100644 index 0000000000..4c89687f4a --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/beacon-store.py @@ -0,0 +1,56 @@ +""" +Event beacon store server. + +- When a request body is specified, stores the data in the body for the 'type' + specified in the query parameters and serves a 200 response without body. +- When a request body is not specified and the request is not served with an + 'expected_body' parameter, stores an empty body for the 'type' specified in + the query parameters and serves a 200 response without body. +- When a request body is not specified and the request is served with an + 'expected_body' parameter, serves a 200 response whose body contains the + stored value from the automatic beacon. Since the data is stored using a hash + of the data as the key, it uses the `expected_body` query parameter to know + what key to look up. If the stored value doesn't exist, serves a 200 response + with an empty body. +""" +import uuid +import hashlib + +NO_DATA_STRING = b"<No data>" +NOT_SET_STRING = b"<Not set>" + +# The server stash requires a uuid to store data. Use a hash of the automatic +# beacon data as the uuid to store and retrieve the data. +def string_to_uuid(input): + hash_value = hashlib.md5(str(input).encode("UTF-8")).hexdigest() + return str(uuid.UUID(hex=hash_value)) + +def main(request, response): + stash = request.server.stash; + event_type = request.GET.first(b"type", NO_DATA_STRING) + + # The stash is accessed concurrently by many clients. A lock is used to + # avoid interleaved read/write from different clients. + with stash.lock: + # GET requests with an 'expected_body' parameter imply they were sent as + # the request from nextBeacon(). + if request.method == "GET" and b"expected_body" in request.GET: + expected_body = request.GET.first(b"expected_body", NO_DATA_STRING) + data = stash.take(string_to_uuid(event_type + expected_body)) or NOT_SET_STRING + return (200, [], data) + + # Requests with a body imply they were sent as a reporting beacon + # (either through reportEvent() or through an automatic beacon). + if request.method == "POST" and event_type: + request_body = request.body or NO_DATA_STRING + request_headers = request.headers.get("Origin") or NO_DATA_STRING + stash.put(string_to_uuid(event_type + request_body), + request_headers) + return (200, [], b"") + # GET requests without an 'expected_body' parameter imply they were sent + # as a destination URL reporting beacon. + if request.method == "GET" and event_type: + stash.put(string_to_uuid(event_type + NO_DATA_STRING), NO_DATA_STRING) + return (200, [], b"") + + return (400, [], u"") diff --git a/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.py b/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.py index c91b31fd02..9acab47203 100644 --- a/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.py +++ b/testing/web-platform/tests/fenced-frame/resources/fledge-bidding-logic.py @@ -17,7 +17,7 @@ def main(request, response): # Parse URL params. requested_size = request.GET.first(b"requested-size", None) ad_with_size = request.GET.first(b"ad-with-size", None) - automatic_beacon = request.GET.first(b"automatic-beacon", None) + beacon = request.GET.first(b"beacon", None) # Use URL params to modify Javascript. requested_size_check = '' @@ -53,15 +53,18 @@ def main(request, response): ) register_ad_beacon = '' - if automatic_beacon is not None: + if beacon is not None: register_ad_beacon = ( '''registerAdBeacon({ 'reserved.top_navigation_start': browserSignals.interestGroupOwner + - '/fenced-frame/resources/automatic-beacon-store.py?type=reserved.top_navigation_start', + '/fenced-frame/resources/beacon-store.py?type=reserved.top_navigation_start', 'reserved.top_navigation_commit': browserSignals.interestGroupOwner + - '/fenced-frame/resources/automatic-beacon-store.py?type=reserved.top_navigation_commit', + '/fenced-frame/resources/beacon-store.py?type=reserved.top_navigation_commit', + 'click': + browserSignals.interestGroupOwner + + '/fenced-frame/resources/beacon-store.py?type=click', }); ''' ) diff --git a/testing/web-platform/tests/fenced-frame/resources/shared-worker.js b/testing/web-platform/tests/fenced-frame/resources/shared-worker.js new file mode 100644 index 0000000000..ac59fe6498 --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/resources/shared-worker.js @@ -0,0 +1,14 @@ +// This is loaded as a SharedWorker in a WPT. When postMessaged to, forwards +// that message to all registered ports through a postMessage call. +const ports = []; + +onconnect = function (event) { + const port = event.ports[0]; + ports.push(port); + + port.onmessage = async function(e) { + ports.forEach(curPort => { + curPort.postMessage(e.data); + }); + } +} diff --git a/testing/web-platform/tests/fenced-frame/resources/unreached.https.html b/testing/web-platform/tests/fenced-frame/resources/unreached.https.html deleted file mode 100644 index bd389ec4fb..0000000000 --- a/testing/web-platform/tests/fenced-frame/resources/unreached.https.html +++ /dev/null @@ -1,15 +0,0 @@ -<!DOCTYPE html> -<title>File used to assert that navigations do not succeed.</title> - -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="utils.js"></script> -<script src="/common/get-host-info.sub.js"></script> - -<body> -<script> - promise_test(async(t) => { - assert_unreached('This navigation should not have succeeded.'); - }); -</script> -</body> diff --git a/testing/web-platform/tests/fenced-frame/resources/utils.js b/testing/web-platform/tests/fenced-frame/resources/utils.js index d6cca91437..462bda37fc 100644 --- a/testing/web-platform/tests/fenced-frame/resources/utils.js +++ b/testing/web-platform/tests/fenced-frame/resources/utils.js @@ -1,5 +1,5 @@ const STORE_URL = '/fenced-frame/resources/key-value-store.py'; -const BEACON_URL = '/fenced-frame/resources/automatic-beacon-store.py'; +const BEACON_URL = '/fenced-frame/resources/beacon-store.py'; const REMOTE_EXECUTOR_URL = '/fenced-frame/resources/remote-context-executor.https.html'; // If your test needs to modify FLEDGE bidding or decision logic, you should @@ -78,7 +78,7 @@ async function runSelectURL(href, keylist = [], resolve_to_config = false) { async function generateURNFromFledgeRawURL( href, nested_urls, resolve_to_config = false, ad_with_size = false, - requested_size = null, automatic_beacon = false) { + requested_size = null, register_beacon = false) { const bidding_token = token(); const seller_token = token(); @@ -88,16 +88,19 @@ async function generateURNFromFledgeRawURL( { renderURL: url } }); - let interestGroup = - { - name: 'testAd1', - owner: location.origin, - biddingLogicURL: new URL(FLEDGE_BIDDING_URL, location.origin), - ads: [{renderURL: href, bid: 1}], - userBiddingSignals: {biddingToken: bidding_token}, - trustedBiddingSignalsKeys: ['key1'], - adComponents: ad_components_list, - }; + let interestGroup = { + name: 'testAd1', + owner: location.origin, + biddingLogicURL: new URL(FLEDGE_BIDDING_URL, location.origin), + ads: [{ + renderURL: href, + bid: 1, + allowedReportingOrigins: [location.origin], + }], + userBiddingSignals: {biddingToken: bidding_token}, + trustedBiddingSignalsKeys: ['key1'], + adComponents: ad_components_list, + }; let biddingURLParams = new URLSearchParams(interestGroup.biddingLogicURL.search); @@ -106,8 +109,8 @@ async function generateURNFromFledgeRawURL( 'requested-size', requested_size[0] + '-' + requested_size[1]); if (ad_with_size) biddingURLParams.set('ad-with-size', 1); - if (automatic_beacon) - biddingURLParams.set('automatic-beacon', 1); + if (register_beacon) + biddingURLParams.set('beacon', 1); interestGroup.biddingLogicURL.search = biddingURLParams; if (ad_with_size) { @@ -158,16 +161,16 @@ async function generateURNFromFledgeRawURL( // frame config. // @param {boolean} [ad_with_size = false] - Determines whether the auction is // run with ad sizes specified. -// @param {boolean} [automatic_beacon = false] - If true, FLEDGE logic will -// register an automatic beacon +// @param {boolean} [register_beacon = false] - If true, FLEDGE logic will +// register reporting beacons // after completion. async function generateURNFromFledge( href, keylist, nested_urls = [], resolve_to_config = false, - ad_with_size = false, requested_size = null, automatic_beacon = false) { + ad_with_size = false, requested_size = null, register_beacon = false) { const full_url = generateURL(href, keylist); return generateURNFromFledgeRawURL( full_url, nested_urls, resolve_to_config, ad_with_size, requested_size, - automatic_beacon); + register_beacon); } // Extracts a list of UUIDs from the from the current page's URL. @@ -279,7 +282,7 @@ function attachContext(object_constructor, html, headers, origin) { // 2. crbug.com/1394559: unfenced-top.https.html async function attachOpaqueContext( generator_api, resolve_to_config, ad_with_size, requested_size, - automatic_beacon, object_constructor, html, headers, origin, + register_beacon, object_constructor, html, headers, origin, num_components) { const [uuid, url] = generateRemoteContextURL(headers, origin); @@ -298,7 +301,7 @@ async function attachOpaqueContext( generator_api == 'fledge' ? generateURNFromFledge( url, [], components_list, resolve_to_config, ad_with_size, - requested_size, automatic_beacon) : + requested_size, register_beacon) : runSelectURL(url, [], resolve_to_config)); const object = object_constructor(id); return buildRemoteContextForObject(object, uuid, html); @@ -306,13 +309,12 @@ async function attachOpaqueContext( function attachPotentiallyOpaqueContext( generator_api, resolve_to_config, ad_with_size, requested_size, - automatic_beacon, frame_constructor, html, headers, origin, - num_components) { + register_beacon, frame_constructor, html, headers, origin, num_components) { generator_api = generator_api.toLowerCase(); if (generator_api == 'fledge' || generator_api == 'sharedstorage') { return attachOpaqueContext( generator_api, resolve_to_config, ad_with_size, requested_size, - automatic_beacon, frame_constructor, html, headers, origin, + register_beacon, frame_constructor, html, headers, origin, num_components); } else { return attachContext(frame_constructor, html, headers, origin); @@ -321,7 +323,7 @@ function attachPotentiallyOpaqueContext( function attachFrameContext( element_name, generator_api, resolve_to_config, ad_with_size, - requested_size, automatic_beacon, html, headers, attributes, origin, + requested_size, register_beacon, html, headers, attributes, origin, num_components) { frame_constructor = (id) => { frame = document.createElement(element_name); @@ -341,7 +343,7 @@ function attachFrameContext( }; return attachPotentiallyOpaqueContext( generator_api, resolve_to_config, ad_with_size, requested_size, - automatic_beacon, frame_constructor, html, headers, origin, + register_beacon, frame_constructor, html, headers, origin, num_components); } @@ -350,7 +352,7 @@ function replaceFrameContext(frame_proxy, { resolve_to_config = false, ad_with_size = false, requested_size = null, - automatic_beacon = false, + register_beacon = false, html = '', headers = [], origin = '' @@ -368,15 +370,15 @@ function replaceFrameContext(frame_proxy, { }; return attachPotentiallyOpaqueContext( generator_api, resolve_to_config, ad_with_size, requested_size, - automatic_beacon, frame_constructor, html, headers, origin); + register_beacon, frame_constructor, html, headers, origin); } -// Attach a fenced frame that waits for scripts to execute. -// Takes as input a(n optional) dictionary of configs: +// Attach a fenced frame that waits for scripts to execute. Takes as input a(n +// optional) dictionary of configs: // - generator_api: the name of the API that should generate the urn/config. // Supports (case-insensitive) "fledge" and "sharedstorage", or any other -// value as a default. -// If you generate a urn, then you need to await the result of this function. +// value as a default. If you generate a urn, then you need to await the +// result of this function. // - resolve_to_config: whether a config should be used. (currently only works // for FLEDGE and sharedStorage generator_api) // - ad_with_size: whether an ad auction is run with size specified for the ads @@ -385,22 +387,22 @@ function replaceFrameContext(frame_proxy, { // requestedSize in the FLEDGE auction config. This is different from // ad_with_size, which refers to size information provided alongside the ads // themselves. -// - automatic_beacon: If true and generator_api = "fledge", an automatic beacon -// will be registered for a top-level navigation after the FLEDGE auction -// completes. +// - register_beacon: If true and generator_api = "fledge", an automatic beacon +// and a destination URL reportEvent() beacon will be registered after the +// FLEDGE auction completes. // - html: extra HTML source code to inject into the loaded frame // - headers: an array of header pairs [[key, value], ...] // - attributes: an array of attribute pairs to set on the frame [[key, value], -// ...] -// - origin: origin of the url, default to location.origin if not set -// Returns a proxy that acts like the frame HTML element, but with an extra -// function `execute`. See `attachFrameContext` or the README for more details. +// ...] +// - origin: origin of the url, default to location.origin if not set. Returns a +// proxy that acts like the frame HTML element, but with an extra function +// `execute`. See `attachFrameContext` or the README for more details. function attachFencedFrameContext({ generator_api = '', resolve_to_config = false, ad_with_size = false, requested_size = null, - automatic_beacon = false, + register_beacon = false, html = '', headers = [], attributes = [], @@ -409,7 +411,7 @@ function attachFencedFrameContext({ } = {}) { return attachFrameContext( 'fencedframe', generator_api, resolve_to_config, ad_with_size, - requested_size, automatic_beacon, html, headers, attributes, origin, + requested_size, register_beacon, html, headers, attributes, origin, num_components); } @@ -417,7 +419,7 @@ function attachFencedFrameContext({ // See `attachFencedFrameContext` for more details. function attachIFrameContext({ generator_api = '', - automatic_beacon = false, + register_beacon = false, html = '', headers = [], attributes = [], @@ -426,8 +428,8 @@ function attachIFrameContext({ } = {}) { return attachFrameContext( 'iframe', generator_api, resolve_to_config = false, ad_with_size = false, - requested_size = null, automatic_beacon, html, headers, attributes, - origin, num_components); + requested_size = null, register_beacon, html, headers, attributes, origin, + num_components); } // Open a window that waits for scripts to execute. @@ -563,9 +565,9 @@ async function nextValueFromServer(key) { } } -// Checks the automatic beacon data server to see if it has received an -// automatic beacon with a given event type and body. -async function readAutomaticBeaconDataFromServer(event_type, expected_body) { +// Checks the beacon data server to see if it has received a beacon with a given +// event type and body. +async function readBeaconDataFromServer(event_type, expected_body) { let serverURL = `${BEACON_URL}`; const response = await fetch(serverURL + "?" + new URLSearchParams({ type: event_type, @@ -586,11 +588,11 @@ async function readAutomaticBeaconDataFromServer(event_type, expected_body) { // available on the server. The server uses a hash of the concatenated event // type and beacon data as the key when storing the beacon in the database. To // retrieve it, we need to supply the endpoint with both pieces of information. -async function nextAutomaticBeacon(event_type, expected_body) { +async function nextBeacon(event_type, expected_body) { while (true) { // Fetches the test result from the server. - const { status, value } = - await readAutomaticBeaconDataFromServer(event_type, expected_body); + const {status, value} = + await readBeaconDataFromServer(event_type, expected_body); if (!status) { // The test result has not been stored yet. Retry after a while. await new Promise(resolve => setTimeout(resolve, 20)); @@ -639,17 +641,16 @@ function createLocalSource(key, url) { } function setupCSP(csp, second_csp=null) { - let meta = document.createElement('meta'); - meta.httpEquiv = "Content-Security-Policy"; - meta.content = "fenced-frame-src " + csp; - document.head.appendChild(meta); + let headers = []; + headers.push(["Content-Security-Policy", "fenced-frame-src " + csp]); if (second_csp != null) { - let second_meta = document.createElement('meta'); - second_meta.httpEquiv = "Content-Security-Policy"; - second_meta.content = "frame-src " + second_csp; - document.head.appendChild(second_meta); + headers.push(["Content-Security-Policy", "frame-src " + second_csp]); } + + const iframe = attachIFrameContext({headers: headers}); + + return iframe; } // Clicking in WPT tends to be flaky (https://crbug.com/1066891), so you may diff --git a/testing/web-platform/tests/fenced-frame/revoke-nested-fenced-frame-in-iframe-navigation.https.html b/testing/web-platform/tests/fenced-frame/revoke-nested-fenced-frame-in-iframe-navigation.https.html deleted file mode 100644 index 3a44c3c2f4..0000000000 --- a/testing/web-platform/tests/fenced-frame/revoke-nested-fenced-frame-in-iframe-navigation.https.html +++ /dev/null @@ -1,75 +0,0 @@ -<!DOCTYPE html> -<title>Test that window.fence.disableUntrustedNetwork disables - embedder-initiated navigation of FF -> IF -> FF.</title> -<meta name="timeout" content="long"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/common/utils.js"></script> -<script src="/common/dispatcher/dispatcher.js"></script> -<script src="resources/utils.js"></script> - -<body> -<script> - -// Run a test with a fenced frame nested in an iframe nested in a fenced frame. -// If `should_disable_network` is true, window.fence.disableUntrustedNetwork -// will be called before creating the nested fenced frame. -// If `use_urn_iframe` is true, the nested iframe will be a urn iframe. -async function ff_if_ff_test(t, should_disable_network, use_urn_iframe, should_succeed) { -const fencedframe = await attachFencedFrameContext({generator_api: 'sharedstorage'}); - const navigation_promise = - fencedframe.execute(async (should_disable_network, use_urn_iframe) => { - let args = {}; - if (use_urn_iframe) { - args = {generator_api: 'sharedstorage'}; - } - const nested_iframe = await attachIFrameContext(args); - await nested_iframe.execute(() => {}); - if (should_disable_network) { - await window.fence.disableUntrustedNetwork(); - } - return nested_iframe.execute(async () => { - const nested_fenced_frame = await attachFencedFrameContext({ - generator_api: 'sharedstorage'}); - return nested_fenced_frame.execute(() => { return 'nav success'; }); - }); - }, - [should_disable_network, use_urn_iframe]); - if (should_succeed) { - const result = await navigation_promise; - assert_equals(result, 'nav success'); - } else { - const result = await Promise.race([navigation_promise, - new Promise((resolve, reject) => t.step_timeout( - () => resolve('timeout'), 1000)) - ]); - assert_equals(result, 'timeout'); - } -} - -promise_test(async(t) => { - await ff_if_ff_test(t, /*should_disable_network=*/false, - /*use_urn_iframe=*/false, - /*should_succeed=*/true); -}, 'FF->IF->FF navigation works'); - -promise_test(async(t) => { - await ff_if_ff_test(t, /*should_disable_network=*/false, - /*use_urn_iframe=*/true, - /*should_succeed=*/true); -}, 'FF->UIF->FF navigation works'); - -promise_test(async(t) => { - await ff_if_ff_test(t, /*should_disable_network=*/true, - /*use_urn_iframe=*/false, - /*should_succeed=*/false); -}, 'window.fence.disableUntrustedNetwork disables FF->IF->FF navigation'); - -promise_test(async(t) => { - await ff_if_ff_test(t, /*should_disable_network=*/true, - /*use_urn_iframe=*/true, - /*should_succeed=*/false); -}, 'window.fence.disableUntrustedNetwork disables FF->UF->FF navigation'); - -</script> -</body> diff --git a/testing/web-platform/tests/fenced-frame/revoke-nested-fenced-frame-navigation.https.html b/testing/web-platform/tests/fenced-frame/revoke-nested-fenced-frame-navigation.https.html deleted file mode 100644 index b80350a588..0000000000 --- a/testing/web-platform/tests/fenced-frame/revoke-nested-fenced-frame-navigation.https.html +++ /dev/null @@ -1,50 +0,0 @@ -<!DOCTYPE html> -<title>Test that window.fence.disableUntrustedNetwork disables - embedder-initiated navigation of nested fenced frames.</title> -<meta name="timeout" content="long"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/common/utils.js"></script> -<script src="/common/dispatcher/dispatcher.js"></script> -<script src="resources/utils.js"></script> - -<body> -<script> - -// Run a test with a fenced frame nested in a fenced frame. -// If `should_disable_network` is true, window.fence.disableUntrustedNetwork -// will be called before creating the nested fenced frame. -async function ff_ff_test(t, should_disable_network, should_succeed) { - const fencedframe = await attachFencedFrameContext(); - const navigation_promise = - fencedframe.execute(async (should_disable_network) => { - if (should_disable_network) { - await window.fence.disableUntrustedNetwork(); - } - const nested_fenced_frame = await attachFencedFrameContext(); - return nested_fenced_frame.execute(() => { return 'nav success'; }); }, - [should_disable_network]); - if (should_succeed) { - const result = await navigation_promise; - assert_equals(result, 'nav success'); - } else { - const result = await Promise.race([ - navigation_promise, - new Promise((resolve, reject) => t.step_timeout( - () => resolve('timeout'), 2000))]); - assert_equals(result, 'timeout'); - } -} - -promise_test(async(t) => { - await ff_ff_test(t, /*should_disable_network=*/false, - /*should_succeed=*/true); -}, 'FF->FF navigation works'); - -promise_test(async(t) => { - await ff_ff_test(t, /*should_disable_network=*/true, - /*should_succeed=*/false); -}, 'window.fence.disableUntrustedNetwork disables FF->FF navigation'); - -</script> -</body> diff --git a/testing/web-platform/tests/fenced-frame/revoke-popup.https.html b/testing/web-platform/tests/fenced-frame/revoke-popup.https.html deleted file mode 100644 index e4a2bb26ad..0000000000 --- a/testing/web-platform/tests/fenced-frame/revoke-popup.https.html +++ /dev/null @@ -1,47 +0,0 @@ -<!DOCTYPE html> -<title>Test that window.fence.disableUntrustedNetwork disables - popup navigations.</title> -<meta name="timeout" content="long"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/common/utils.js"></script> -<script src="/common/dispatcher/dispatcher.js"></script> -<script src="resources/utils.js"></script> -<script src="/resources/testdriver.js"></script> -<script src="/resources/testdriver-actions.js"></script> -<script src="/resources/testdriver-vendor.js"></script> - -<body> -<script> - -promise_test(async(t) => { - const fencedframe = await attachFencedFrameContext({generator_api: 'fledge'}); - await fencedframe.execute(() => {}); - - const actions = new test_driver.Actions(); - await actions.setContext(window) - .pointerMove(0, 0, {origin: fencedframe.element}) - .pointerDown() - .pointerUp() - .send(); - - await fencedframe.execute(async () => { - await window.fence.disableUntrustedNetwork(); - // After disabling network, popup navigations should not work. - assert_true(navigator.userActivation.isActive, - 'The frame should have user activation.'); - window.popup = attachWindowContext(); - }); - - const result = await Promise.race([ - fencedframe.execute(async () => { - return await window.popup.execute(() => { return 'popup_loaded'; }); - }), - new Promise((resolve) => t.step_timeout( - () => resolve('timeout'), 2000)) - ]); - assert_equals(result, 'timeout'); -}, 'window.fence.disableUntrustedNetwork disables popup navigations'); - -</script> -</body> diff --git a/testing/web-platform/tests/fenced-frame/revoke-unfenced-top-navigation.https.html b/testing/web-platform/tests/fenced-frame/revoke-unfenced-top-navigation.https.html deleted file mode 100644 index 873404768f..0000000000 --- a/testing/web-platform/tests/fenced-frame/revoke-unfenced-top-navigation.https.html +++ /dev/null @@ -1,47 +0,0 @@ -<!DOCTYPE html> -<title>Test that window.fence.disableUntrustedNetwork disables - _unfencedTop navigations.</title> -<meta name="timeout" content="long"> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<script src="/common/utils.js"></script> -<script src="/common/dispatcher/dispatcher.js"></script> -<script src="resources/utils.js"></script> -<script src="/resources/testdriver.js"></script> -<script src="/resources/testdriver-actions.js"></script> -<script src="/resources/testdriver-vendor.js"></script> - -<body> -<script> - -promise_test(async(t) => { - const fencedframe = await attachFencedFrameContext({generator_api: 'fledge'}); - await fencedframe.execute(() => {}); - - const actions = new test_driver.Actions(); - await actions.setContext(window) - .pointerMove(0, 0, {origin: fencedframe.element}) - .pointerDown() - .pointerUp() - .send(); - - const destination_url = new URL('resources/unreached.https.html', location.href); - fencedframe.execute(async (url) => { - await window.fence.disableUntrustedNetwork(); - // After disabling network, _unfencedTop navigations should not work. - assert_true(navigator.userActivation.isActive, - 'The frame should have user activation.') - const result = window.open(url, '_unfencedTop'); - assert_equals(result, null, '_unfencedTop did not return a window.'); - }, [destination_url]); - - // Wait a few seconds. - await new Promise((resolve, reject) => - t.step_timeout(() => resolve('timeout'), 3000)); - - // Confirm that the fenced frame is still there. - await fencedframe.execute(() => {}); -}, 'window.fence.disableUntrustedNetwork disables _unfencedTop navigations'); - -</script> -</body> diff --git a/testing/web-platform/tests/fenced-frame/shared-workers.https.html b/testing/web-platform/tests/fenced-frame/shared-workers.https.html new file mode 100644 index 0000000000..0e08d6857f --- /dev/null +++ b/testing/web-platform/tests/fenced-frame/shared-workers.https.html @@ -0,0 +1,42 @@ + +<!DOCTYPE html> +<title>Test shared workers aren't shared across fenced frame boundaries.</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/utils.js"></script> +<script src="/common/dispatcher/dispatcher.js"></script> +<script src="resources/utils.js"></script> +<body> +<script> +promise_test(async(t) => { + const fencedframe = await attachFencedFrameContext(); + const worker = new SharedWorker( + "/fenced-frame/resources/shared-worker.js"); + + const messagePromise = new Promise((resolve, reject) => { + worker.port.onmessage = evt => { + // The main frame should not get a postMessage from the fenced frame's + // SharedWorker, even though the main frame and fenced frame are + // same-origin to each other. + reject(); + }; + }); + await fencedframe.execute(async () => { + // The worker will take anything postMessaged to it and postMessage it to + // all registered ports. + const worker = new SharedWorker( + "/fenced-frame/resources/shared-worker.js"); + worker.port.postMessage("message"); + await new Promise((resolve) => { + // Sanity check that the postMessage() is making to the fenced frame. + worker.port.onmessage = evt => { + resolve(); + }; + }); + }); + const timeout = new Promise(r => t.step_timeout(r, 1000)); + await Promise.race([messagePromise, timeout]); + +}, 'Shared workers should not be shared across fenced frame boundaries'); +</script> +</body> diff --git a/testing/web-platform/tests/fetch/api/credentials/authentication-redirection.any.js b/testing/web-platform/tests/fetch/api/credentials/authentication-redirection.any.js index 16656b5435..5a15507437 100644 --- a/testing/web-platform/tests/fetch/api/credentials/authentication-redirection.any.js +++ b/testing/web-platform/tests/fetch/api/credentials/authentication-redirection.any.js @@ -24,6 +24,6 @@ promise_test(async test => { }, "getAuthorizationHeaderValue - same origin redirection"); promise_test(async (test) => { - const result = await getAuthorizationHeaderValue(get_host_info().HTTPS_REMOTE_ORIGIN + "/fetch/api/resources/redirect.py?allow_headers=Authorization&location=" + encodeURIComponent(get_host_info().HTTPS_ORIGIN + "/fetch/api/resources/dump-authorization-header.py")); + const result = await getAuthorizationHeaderValue(get_host_info().HTTPS_REMOTE_ORIGIN + "/fetch/api/resources/redirect.py?allow_headers=Authorization&location=" + encodeURIComponent(get_host_info().HTTPS_ORIGIN + "/fetch/api/resources/dump-authorization-header.py?strip_auth_header=true")); assert_equals(result, "none"); }, "getAuthorizationHeaderValue - cross origin redirection"); diff --git a/testing/web-platform/tests/fetch/api/request/destination/resources/dummy_video.ogv b/testing/web-platform/tests/fetch/api/request/destination/resources/dummy_video.ogv Binary files differdeleted file mode 100644 index de99616ece..0000000000 --- a/testing/web-platform/tests/fetch/api/request/destination/resources/dummy_video.ogv +++ /dev/null diff --git a/testing/web-platform/tests/fetch/api/request/request-bad-port.any.js b/testing/web-platform/tests/fetch/api/request/request-bad-port.any.js index b0684d4be0..5c29823eaa 100644 --- a/testing/web-platform/tests/fetch/api/request/request-bad-port.any.js +++ b/testing/web-platform/tests/fetch/api/request/request-bad-port.any.js @@ -72,6 +72,7 @@ var BLOCKED_PORTS_LIST = [ 2049, // nfs 3659, // apple-sasl 4045, // lockd + 4190, // sieve 5060, // sip 5061, // sips 6000, // x11 @@ -81,6 +82,7 @@ var BLOCKED_PORTS_LIST = [ 6667, // irc (default) 6668, // irc (alternate) 6669, // irc (alternate) + 6679, // osaut 6697, // irc+tls 10080, // amanda ]; diff --git a/testing/web-platform/tests/fetch/api/resources/dump-authorization-header.py b/testing/web-platform/tests/fetch/api/resources/dump-authorization-header.py index a651aeb4e8..0d82809f59 100644 --- a/testing/web-platform/tests/fetch/api/resources/dump-authorization-header.py +++ b/testing/web-platform/tests/fetch/api/resources/dump-authorization-header.py @@ -2,6 +2,11 @@ def main(request, response): headers = [(b"Content-Type", "text/html"), (b"Cache-Control", b"no-cache")] + if (request.GET.first(b"strip_auth_header", False) and request.method == "OPTIONS" and + b"authorization" in request.headers.get(b"Access-Control-Request-Headers", b"").lower()): + # Auth header should not be sent for preflight after cross-origin redirect. + return 500, headers, "fail" + if b"Origin" in request.headers: headers.append((b"Access-Control-Allow-Origin", request.headers.get(b"Origin", b""))) headers.append((b"Access-Control-Allow-Credentials", b"true")) diff --git a/testing/web-platform/tests/fetch/compression-dictionary/dictionary-clear-site-data.tentative.https.html b/testing/web-platform/tests/fetch/compression-dictionary/dictionary-clear-site-data.tentative.https.html new file mode 100644 index 0000000000..b583834831 --- /dev/null +++ b/testing/web-platform/tests/fetch/compression-dictionary/dictionary-clear-site-data.tentative.https.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<head> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/compression-dictionary-util.js"></script> +</head> +<body> +<script> + +compression_dictionary_promise_test(async (t) => { + const dict = await (await fetch(kRegisterDictionaryPath)).text(); + // Wait until `available-dictionary` header is available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {}), + kDefaultDictionaryHashBase64); + // Clear site data. + assert_equals(await clearSiteData(/*directive=*/'cache'), 'OK'); + // Check if `available-dictionary` header is not available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {max_retry: 0}), + '"available-dictionary" header is not available'); +}, 'Clear-Site-Data with "cache" directive must unregister dictionary'); + +compression_dictionary_promise_test(async (t) => { + const dict = await (await fetch(kRegisterDictionaryPath)).text(); + // Wait until `available-dictionary` header is available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {}), + kDefaultDictionaryHashBase64); + // Clear site data. + assert_equals(await clearSiteData(/*directive=*/'cookies'), 'OK'); + // Check if `available-dictionary` header is not available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {max_retry: 0}), + '"available-dictionary" header is not available'); +}, 'Clear-Site-Data with "cookies" directive must unregister dictionary'); + +compression_dictionary_promise_test(async (t) => { + const dict = await (await fetch(kRegisterDictionaryPath)).text(); + // Wait until `available-dictionary` header is available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {}), + kDefaultDictionaryHashBase64); + // Clear site data. + assert_equals(await clearSiteData(/*directive=*/'storage'), 'OK'); + // Check if `available-dictionary` header is not available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {max_retry: 0}), + kDefaultDictionaryHashBase64); +}, 'Clear-Site-Data with "storage" directive must not unregister dictionary'); + +</script> +</body> diff --git a/testing/web-platform/tests/fetch/compression-dictionary/dictionary-decompression.tentative.https.html b/testing/web-platform/tests/fetch/compression-dictionary/dictionary-decompression.tentative.https.html new file mode 100644 index 0000000000..cd20625816 --- /dev/null +++ b/testing/web-platform/tests/fetch/compression-dictionary/dictionary-decompression.tentative.https.html @@ -0,0 +1,57 @@ +<!DOCTYPE html> +<head> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="./resources/compression-dictionary-util.js"></script> +</head> +<body> +<script> + +compression_dictionary_promise_test(async (t) => { + const dict = await (await fetch(kRegisterDictionaryPath)).text(); + assert_equals(dict, kDefaultDictionaryContent); + // Wait until `available-dictionary` header is available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {}), + kDefaultDictionaryHashBase64); + + // Check if the data compressed using Brotli with the dictionary can be + // decompressed. + const data_url = `${kCompressedDataPath}?content_encoding=br-d`; + assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData); +}, 'Decompresion using Brotli with the dictionary works as expected'); + +compression_dictionary_promise_test(async (t) => { + const dict = await (await fetch(kRegisterDictionaryPath)).text(); + assert_equals(dict, kDefaultDictionaryContent); + // Wait until `available-dictionary` header is available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {}), + kDefaultDictionaryHashBase64); + + // Check if the data compressed using Zstandard with the dictionary can be + // decompressed. + const data_url = `${kCompressedDataPath}?content_encoding=zstd-d`; + assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData); +}, 'Decompresion using Zstandard with the dictionary works as expected'); + +compression_dictionary_promise_test(async (t) => { + const dict = + await (await fetch(getRemoteHostUrl(kRegisterDictionaryPath))).text(); + assert_equals(dict, kDefaultDictionaryContent); + // Wait until `available-dictionary` header is available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {check_remote: true}), + kDefaultDictionaryHashBase64); + + // Check if the data compressed using Brotli with the dictionary can be + // decompressed. + const data_url = + getRemoteHostUrl(`${kCompressedDataPath}?content_encoding=br-d`); + assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData); +}, 'Decompresion of a cross origin resource works as expected'); + +</script> +</body> diff --git a/testing/web-platform/tests/fetch/compression-dictionary/dictionary-fetch-with-link-element.tentative.https.html b/testing/web-platform/tests/fetch/compression-dictionary/dictionary-fetch-with-link-element.tentative.https.html new file mode 100644 index 0000000000..71a9b1c050 --- /dev/null +++ b/testing/web-platform/tests/fetch/compression-dictionary/dictionary-fetch-with-link-element.tentative.https.html @@ -0,0 +1,70 @@ +<!DOCTYPE html> +<head> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/common/utils.js"></script> +<script src="./resources/compression-dictionary-util.js"></script> +</head> +<body> +<script> + +function addLinkRelDictionaryElement(url, crossOrigin) { + const link = document.createElement('link'); + link.rel = 'dictionary'; + link.href = url; + if (crossOrigin) { + link.crossOrigin = crossOrigin; + } + document.head.appendChild(link); +} + +test(t => { + assert_true(document.createElement('link').relList.supports('dictionary')); + }, "Browser supports link element with dictionary rel."); + +compression_dictionary_promise_test(async (t) => { + const dict_token = token(); + const url = `${kRegisterDictionaryPath}?save_header=${dict_token}`; + addLinkRelDictionaryElement(url); + // Wait for a while to ensure that the dictionary is fetched. + await new Promise(resolve => window.requestIdleCallback(resolve)); + const headers = await waitUntilPreviousRequestHeaders(t, dict_token); + assert_true(headers !== undefined, 'Headers should be available'); + assert_equals(headers['sec-fetch-mode'], 'cors'); + // Wait until `available-dictionary` header is available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {}), + kDefaultDictionaryHashBase64); + // Check if the data compressed using Brotli with the dictionary can be + // decompressed. + const data_url = `${kCompressedDataPath}?content_encoding=br-d`; + assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData); +}, 'Fetch same origin dictionary using link element'); + +compression_dictionary_promise_test(async (t) => { + const dict_token = token(); + const url = + getRemoteHostUrl(`${kRegisterDictionaryPath}?save_header=${dict_token}`); + addLinkRelDictionaryElement(url, 'anonymous'); + // Wait for a while to ensure that the dictionary is fetched. + await new Promise(resolve => window.requestIdleCallback(resolve)); + const headers = await waitUntilPreviousRequestHeaders( + t, dict_token, /*check_remote=*/ true); + assert_true(headers !== undefined, 'Headers should be available'); + assert_equals(headers['sec-fetch-mode'], 'cors'); + + // Wait until `available-dictionary` header is available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {check_remote: true}), + kDefaultDictionaryHashBase64); + // Check if the data compressed using Brotli with the dictionary can be + // decompressed. + const data_url = + getRemoteHostUrl(`${kCompressedDataPath}?content_encoding=br-d`); + assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData); +}, 'Fetch cross origin dictionary using link element'); + +</script> +</body> diff --git a/testing/web-platform/tests/fetch/compression-dictionary/dictionary-fetch-with-link-header.tentative.https.html b/testing/web-platform/tests/fetch/compression-dictionary/dictionary-fetch-with-link-header.tentative.https.html new file mode 100644 index 0000000000..a3ffd8ba74 --- /dev/null +++ b/testing/web-platform/tests/fetch/compression-dictionary/dictionary-fetch-with-link-header.tentative.https.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<head> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/common/get-host-info.sub.js"></script> +<script src="/common/utils.js"></script> +<script src="./resources/compression-dictionary-util.js"></script> +</head> +<body> +<script> + +async function addIframeWithLinkRelDictionaryHeader(dict_url) { + return new Promise((resolve) => { + const base_page_url = './resources/empty.html'; + const page_url = + base_page_url + `?pipe=header(link,<${dict_url}>; rel="dictionary")`; + const iframe = document.createElement('iframe'); + iframe.src = page_url; + iframe.addEventListener('load', () => { + resolve(iframe); + }); + document.body.appendChild(iframe); + }) +} + +compression_dictionary_promise_test(async (t) => { + const dict_token = token(); + const url = new URL( + `${kRegisterDictionaryPath}?save_header=${dict_token}`, location.href); + const iframe = await addIframeWithLinkRelDictionaryHeader(url.href); + t.add_cleanup(() => { + iframe.remove(); + }); + // Wait for a while to ensure that the dictionary is fetched. + await new Promise(resolve => window.requestIdleCallback(resolve)); + const headers = await waitUntilPreviousRequestHeaders(t, dict_token); + assert_true(headers !== undefined, 'Headers should be available'); + assert_equals(headers['sec-fetch-mode'], 'cors'); + // Wait until `available-dictionary` header is available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {}), + kDefaultDictionaryHashBase64); + // Check if the data compressed using Brotli with the dictionary can be + // decompressed. + const data_url = `${kCompressedDataPath}?content_encoding=br-d`; + assert_equals(await (await fetch(data_url)).text(), kExpectedCompressedData); +}, 'Fetch same origin dictionary using link header'); + +</script> +</body> diff --git a/testing/web-platform/tests/fetch/compression-dictionary/dictionary-registration.tentative.https.html b/testing/web-platform/tests/fetch/compression-dictionary/dictionary-registration.tentative.https.html new file mode 100644 index 0000000000..7921b12946 --- /dev/null +++ b/testing/web-platform/tests/fetch/compression-dictionary/dictionary-registration.tentative.https.html @@ -0,0 +1,60 @@ +<!DOCTYPE html> +<head> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="./resources/compression-dictionary-util.js"></script> +</head> +<body> +<script> + +compression_dictionary_promise_test(async (t) => { + const dict = await (await fetch(kRegisterDictionaryPath)).text(); + assert_equals(dict, kDefaultDictionaryContent); + // Wait until `available-dictionary` header is available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {}), + kDefaultDictionaryHashBase64); +}, 'Simple dictionary registration and unregistration'); + +compression_dictionary_promise_test(async (t) => { + const dict = await (await fetch(`${kRegisterDictionaryPath}?id=test`)).text(); + // Wait until `available-dictionary` header is available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {}), + kDefaultDictionaryHashBase64); + assert_equals((await checkHeaders())['dictionary-id'], '"test"'); +}, 'Dictionary registration with dictionary ID'); + +compression_dictionary_promise_test(async (t) => { + // Registers a first dictionary. + const dictionary_path1 = `${kRegisterDictionaryPath}?id=id1`; + const dict1 = await (await fetch(dictionary_path1)).text(); + // Wait until `available-dictionary` header is available. + assert_equals( + await waitUntilAvailableDictionaryHeader(t, {}), + kDefaultDictionaryHashBase64); + // Check the `dictionary-id` header. + assert_equals((await checkHeaders())['dictionary-id'], '"id1"'); + + // Registers a second dictionary. + const kAlternativeDictionaryContent = + 'This is an alternative test dictionary.'; + const dictionary_path2 = + `${kRegisterDictionaryPath}?content=${kAlternativeDictionaryContent}&id=id2`; + const expected_dictionary_header = + await calculateDictionaryHash(kAlternativeDictionaryContent); + const dict2 = await (await fetch(dictionary_path2)).text(); + assert_equals(dict2, kAlternativeDictionaryContent); + // Wait until `available-dictionary` header is available. + // Note: Passing `expected_header` to ignore the old dictionary. + assert_equals( + await waitUntilAvailableDictionaryHeader( + t, {expected_header: expected_dictionary_header}), + expected_dictionary_header); + // Check the `dictionary-id` header. + assert_equals((await checkHeaders())['dictionary-id'], '"id2"'); +}, 'New dictionary registration overrides the existing one'); + +</script> +</body> diff --git a/testing/web-platform/tests/fetch/compression-dictionary/resources/clear-site-data.py b/testing/web-platform/tests/fetch/compression-dictionary/resources/clear-site-data.py new file mode 100644 index 0000000000..0db51bf797 --- /dev/null +++ b/testing/web-platform/tests/fetch/compression-dictionary/resources/clear-site-data.py @@ -0,0 +1,4 @@ +def main(request, response): + directive = request.GET.first(b"directive") + response.headers.set(b"Clear-Site-Data", b"\"" + directive + b"\"") + return b"OK" diff --git a/testing/web-platform/tests/fetch/compression-dictionary/resources/compressed-data.py b/testing/web-platform/tests/fetch/compression-dictionary/resources/compressed-data.py new file mode 100644 index 0000000000..4be4b55564 --- /dev/null +++ b/testing/web-platform/tests/fetch/compression-dictionary/resources/compressed-data.py @@ -0,0 +1,28 @@ +def main(request, response): + response.headers.set(b"Access-Control-Allow-Origin", b"*") + response.headers.set(b"Content-Type", b"text/plain") + response.headers.set( + b"Content-Dictionary", + b":U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:") + + # `br_d_data` and `zstd_d_data` are generated using the following commands: + # + # $ echo "This is a test dictionary." > /tmp/dict + # $ echo -n "This is compressed test data using a test dictionary" \ + # > /tmp/data + # $ brotli -o /tmp/out.brd -D /tmp/dict /tmp/data + # $ xxd -p /tmp/out.brd | tr -d '\n' | sed 's/\(..\)/\\x\1/g' + br_d_data = b"\xa1\x98\x01\x80\x62\xa4\x4c\x1d\xdf\x12\x84\x8c\xae\xc2\xca\x60\x22\x07\x6e\x81\x05\x14\xc9\xb7\xc3\x44\x8e\xbc\x16\xe0\x15\x0e\xec\xc1\xee\x34\x33\x3e\x0d" + # $ zstd -o /tmp/out.zstdd -D /tmp/dict /tmp/data + # $ xxd -p /tmp/out.zstdd | tr -d '\n' | sed 's/\(..\)/\\x\1/g' + zstd_d_data = b"\x28\xb5\x2f\xfd\x24\x34\xf5\x00\x00\x98\x63\x6f\x6d\x70\x72\x65\x73\x73\x65\x64\x61\x74\x61\x20\x75\x73\x69\x6e\x67\x03\x00\x59\xf9\x73\x54\x46\x27\x26\x10\x9e\x99\xf2\xbc" + + if b'content_encoding' in request.GET: + content_encoding = request.GET.first(b"content_encoding") + response.headers.set(b"Content-Encoding", content_encoding) + if content_encoding == b"br-d": + # Send the pre compressed file + response.content = br_d_data + if content_encoding == b"zstd-d": + # Send the pre compressed file + response.content = zstd_d_data diff --git a/testing/web-platform/tests/fetch/compression-dictionary/resources/compression-dictionary-util.js b/testing/web-platform/tests/fetch/compression-dictionary/resources/compression-dictionary-util.js new file mode 100644 index 0000000000..46d95041d8 --- /dev/null +++ b/testing/web-platform/tests/fetch/compression-dictionary/resources/compression-dictionary-util.js @@ -0,0 +1,120 @@ + +const kDefaultDictionaryContent = 'This is a test dictionary.\n'; +const kDefaultDictionaryHashBase64 = + ':U5abz16WDg7b8KS93msLPpOB4Vbef1uRzoORYkJw9BY=:'; +const kRegisterDictionaryPath = './resources/register-dictionary.py'; +const kCompressedDataPath = './resources/compressed-data.py'; +const kExpectedCompressedData = + `This is compressed test data using a test dictionary`; +const kCheckAvailableDictionaryHeaderMaxRetry = 5; +const kCheckAvailableDictionaryHeaderRetryTimeout = 100; +const kCheckPreviousRequestHeadersMaxRetry = 5; +const kCheckPreviousRequestHeadersRetryTimeout = 250; + +// Gets the remote URL corresponding to `relative_path`. +function getRemoteHostUrl(relative_path) { + const remote_origin = new URL(get_host_info().HTTPS_REMOTE_ORIGIN); + let result = new URL(relative_path, location.href); + result.protocol = remote_origin.protocol; + result.hostname = remote_origin.hostname; + result.port = remote_origin.port; + return result.href; +} + +// Calculates the Structured Field Byte Sequence containing the SHA-256 hash of +// the contents of the dictionary text. +async function calculateDictionaryHash(dictionary_text) { + const encoded = (new TextEncoder()).encode(dictionary_text); + const digest = await crypto.subtle.digest('SHA-256', encoded) + return ':' + btoa(String.fromCharCode(...new Uint8Array(digest))) + ':'; +} + +// Checks the HTTP request headers which is sent to the server. +async function checkHeaders(check_remote = false) { + let url = './resources/echo-headers.py'; + if (check_remote) { + url = getRemoteHostUrl(url); + } + return await (await fetch(url)).json(); +} + +// Checks the "available-dictionary" header in the HTTP request headers. +async function checkAvailableDictionaryHeader(check_remote = false) { + return (await checkHeaders(check_remote))['available-dictionary']; +} + +// Waits until the "available-dictionary" header is available in the HTTP +// request headers, and returns the header. If the header is not available after +// the specified number of retries, returns an error message. If the +// `expected_header` is specified, this method waits until the header is +// available and matches the `expected_header`. +async function waitUntilAvailableDictionaryHeader(test, { + max_retry = kCheckAvailableDictionaryHeaderMaxRetry, + expected_header = undefined, + check_remote = false +}) { + for (let retry_count = 0; retry_count <= max_retry; retry_count++) { + const header = await checkAvailableDictionaryHeader(check_remote); + if (header) { + if (expected_header === undefined || header == expected_header) { + return header; + } + } + await new Promise( + (resolve) => test.step_timeout( + resolve, kCheckAvailableDictionaryHeaderRetryTimeout)); + } + return '"available-dictionary" header is not available'; +} + +// Checks the HTTP request headers which was sent to the server with `token` +// to register a dictionary. +async function checkPreviousRequestHeaders(token, check_remote = false) { + let url = `./resources/register-dictionary.py?get_previous_header=${token}`; + if (check_remote) { + url = getRemoteHostUrl(url); + } + return await (await fetch(url)).json(); +} + +// Waits until the HTTP request headers which was sent to the server with +// `token` to register a dictionary is available, and returns the header. If the +// header is not available after the specified number of retries, returns +// `undefined`. +async function waitUntilPreviousRequestHeaders( + test, token, check_remote = false) { + for (let retry_count = 0; retry_count <= kCheckPreviousRequestHeadersMaxRetry; + retry_count++) { + const header = + (await checkPreviousRequestHeaders(token, check_remote))['headers']; + if (header) { + return header; + } + await new Promise( + (resolve) => test.step_timeout( + resolve, kCheckPreviousRequestHeadersRetryTimeout)); + } + return undefined; +} + +// Clears the site data for the specified directive by sending a request to +// `./resources/clear-site-data.py` which returns `Clear-Site-Data` response +// header. +// Note: When `directive` is 'cache' or 'cookies' is specified, registered +// compression dictionaries should be also cleared. +async function clearSiteData(directive = 'cache') { + return await (await fetch( + `./resources/clear-site-data.py?directive=${directive}`)) + .text(); +} + +// A utility test method that adds the `clearSiteData()` method to the +// testharness cleanup function. This is intended to ensure that registered +// dictionaries are cleared in tests and that registered dictionaries do not +// interfere with subsequent tests. +function compression_dictionary_promise_test(func, name, properties) { + promise_test(async (test) => { + test.add_cleanup(clearSiteData); + await func(test); + }, name, properties); +} diff --git a/testing/web-platform/tests/fetch/compression-dictionary/resources/echo-headers.py b/testing/web-platform/tests/fetch/compression-dictionary/resources/echo-headers.py new file mode 100644 index 0000000000..aabd99eb10 --- /dev/null +++ b/testing/web-platform/tests/fetch/compression-dictionary/resources/echo-headers.py @@ -0,0 +1,10 @@ +import json + +def main(request, response): + response.headers.set(b"Access-Control-Allow-Origin", b"*") + headers = {} + for header in request.headers: + key = header.decode('utf-8') + value = request.headers.get(header).decode('utf-8') + headers[key] = value + return json.dumps(headers) diff --git a/testing/web-platform/tests/fetch/compression-dictionary/resources/empty.html b/testing/web-platform/tests/fetch/compression-dictionary/resources/empty.html new file mode 100644 index 0000000000..0e76edd65b --- /dev/null +++ b/testing/web-platform/tests/fetch/compression-dictionary/resources/empty.html @@ -0,0 +1 @@ +<!DOCTYPE html> diff --git a/testing/web-platform/tests/fetch/compression-dictionary/resources/register-dictionary.py b/testing/web-platform/tests/fetch/compression-dictionary/resources/register-dictionary.py new file mode 100644 index 0000000000..0bd57225ef --- /dev/null +++ b/testing/web-platform/tests/fetch/compression-dictionary/resources/register-dictionary.py @@ -0,0 +1,37 @@ +import json + +def main(request, response): + response.headers.set(b"Access-Control-Allow-Origin", b"*") + match = b"/fetch/compression-dictionary/resources/*" + content = b"This is a test dictionary.\n" + if b"match" in request.GET: + match = request.GET.first(b"match") + if b"content" in request.GET: + content = request.GET.first(b"content") + + token = request.GET.first(b"save_header", None) + if token is not None: + headers = {} + for header in request.headers: + key = header.decode('utf-8') + value = request.headers.get(header).decode('utf-8') + headers[key] = value + with request.server.stash.lock: + request.server.stash.put(token, json.dumps(headers)) + + previous_token = request.GET.first(b"get_previous_header", None) + if previous_token is not None: + result = {} + with request.server.stash.lock: + store = request.server.stash.take(previous_token) + if store is not None: + headers = json.loads(store) + result["headers"] = headers + return json.dumps(result) + + options = b"match=\"" + match + b"\"" + if b"id" in request.GET: + options += b", id=\"" + request.GET.first(b"id") + b"\"" + response.headers.set(b"Use-As-Dictionary", options) + response.headers.set(b"Cache-Control", b"max-age=3600") + return content diff --git a/testing/web-platform/tests/fetch/content-encoding/br/bad-br-body.https.any.js b/testing/web-platform/tests/fetch/content-encoding/br/bad-br-body.https.any.js new file mode 100644 index 0000000000..43ea90a336 --- /dev/null +++ b/testing/web-platform/tests/fetch/content-encoding/br/bad-br-body.https.any.js @@ -0,0 +1,12 @@ +// META: global=window + +[ + "arrayBuffer", +].forEach(method => { + promise_test(t => { + return fetch("resources/bad-br-body.py").then(res => { + assert_equals(res.status, 200); + return promise_rejects_js(t, TypeError, res[method]()); + }); + }, "Consuming the body of a resource with bad br content with " + method + "() should reject"); +}); diff --git a/testing/web-platform/tests/fetch/content-encoding/br/big-br-body.https.any.js b/testing/web-platform/tests/fetch/content-encoding/br/big-br-body.https.any.js new file mode 100644 index 0000000000..1427dd7302 --- /dev/null +++ b/testing/web-platform/tests/fetch/content-encoding/br/big-br-body.https.any.js @@ -0,0 +1,55 @@ +// META: global=window,worker + +const EXPECTED_SIZE = 27000000; +const EXPECTED_SHA256 = [ + 74, 100, 37, 243, 147, 61, 116, 60, 241, 221, 126, + 18, 24, 71, 204, 28, 50, 62, 201, 130, 152, 225, + 217, 183, 10, 201, 143, 214, 102, 155, 212, 248, + ]; + +promise_test(async () => { + const response = await fetch('resources/big.text.br'); + assert_true(response.ok); + const arrayBuffer = await response.arrayBuffer(); + assert_equals(arrayBuffer.byteLength, EXPECTED_SIZE, + 'uncompressed size should match'); + const sha256 = await crypto.subtle.digest('SHA-256', arrayBuffer); + assert_array_equals(new Uint8Array(sha256), EXPECTED_SHA256, + 'digest should match'); +}, 'large br data should be decompressed successfully'); + +promise_test(async () => { + const response = await fetch('resources/big.text.br'); + assert_true(response.ok); + const reader = response.body.getReader({mode: 'byob'}); + let offset = 0; + // Pre-allocate space for the output. The response body will be read + // chunk-by-chunk into this array. + let ab = new ArrayBuffer(EXPECTED_SIZE); + while (offset < EXPECTED_SIZE) { + // To stress the data pipe, we want to use a different size read each + // time. Unfortunately, JavaScript doesn't have a seeded random number + // generator, so this creates the possibility of making this test flaky if + // it doesn't work for some edge cases. + let size = Math.floor(Math.random() * 65535 + 1); + if (size + offset > EXPECTED_SIZE) { + size = EXPECTED_SIZE - offset; + } + const u8 = new Uint8Array(ab, offset, size); + const { value, done } = await reader.read(u8); + ab = value.buffer; + // Check that we got our original array back. + assert_equals(ab.byteLength, EXPECTED_SIZE, + 'backing array should be the same size'); + assert_equals(offset, value.byteOffset, 'offset should match'); + assert_less_than_equal(value.byteLength, size, + 'we should not have got more than we asked for'); + offset = value.byteOffset + value.byteLength; + if (done) break; + } + assert_equals(offset, EXPECTED_SIZE, + 'we should have read the whole thing'); + const sha256 = await crypto.subtle.digest('SHA-256', new Uint8Array(ab)); + assert_array_equals(new Uint8Array(sha256), EXPECTED_SHA256, + 'digest should match'); +}, 'large br data should be decompressed successfully with byte stream'); diff --git a/testing/web-platform/tests/fetch/content-encoding/br/br-body.https.any.js b/testing/web-platform/tests/fetch/content-encoding/br/br-body.https.any.js new file mode 100644 index 0000000000..2c2dbb5d29 --- /dev/null +++ b/testing/web-platform/tests/fetch/content-encoding/br/br-body.https.any.js @@ -0,0 +1,15 @@ +// META: global=window,worker + +const expectedDecompressedSize = 10500; +[ + "text", + "octetstream" +].forEach(contentType => { + promise_test(async t => { + let response = await fetch(`resources/foo.${contentType}.br`); + assert_true(response.ok); + let arrayBuffer = await response.arrayBuffer() + let u8 = new Uint8Array(arrayBuffer); + assert_equals(u8.length, expectedDecompressedSize); + }, `fetched br data with content type ${contentType} should be decompressed.`); +}); diff --git a/testing/web-platform/tests/fetch/content-encoding/br/resources/bad-br-body.py b/testing/web-platform/tests/fetch/content-encoding/br/resources/bad-br-body.py new file mode 100644 index 0000000000..0710e7ffde --- /dev/null +++ b/testing/web-platform/tests/fetch/content-encoding/br/resources/bad-br-body.py @@ -0,0 +1,3 @@ +def main(request, response): + headers = [(b"Content-Encoding", b"br")] + return headers, b"not actually br" diff --git a/testing/web-platform/tests/fetch/content-encoding/br/resources/big.text.br b/testing/web-platform/tests/fetch/content-encoding/br/resources/big.text.br Binary files differnew file mode 100644 index 0000000000..b3a530d757 --- /dev/null +++ b/testing/web-platform/tests/fetch/content-encoding/br/resources/big.text.br diff --git a/testing/web-platform/tests/fetch/content-encoding/br/resources/big.text.br.headers b/testing/web-platform/tests/fetch/content-encoding/br/resources/big.text.br.headers new file mode 100644 index 0000000000..aba00bd5d4 --- /dev/null +++ b/testing/web-platform/tests/fetch/content-encoding/br/resources/big.text.br.headers @@ -0,0 +1,3 @@ +Content-type: text/plain +Content-Encoding: br +Cache-Control: no-store diff --git a/testing/web-platform/tests/fetch/content-encoding/br/resources/foo.octetstream.br b/testing/web-platform/tests/fetch/content-encoding/br/resources/foo.octetstream.br Binary files differnew file mode 100644 index 0000000000..30cb2f7095 --- /dev/null +++ b/testing/web-platform/tests/fetch/content-encoding/br/resources/foo.octetstream.br diff --git a/testing/web-platform/tests/fetch/content-encoding/br/resources/foo.octetstream.br.headers b/testing/web-platform/tests/fetch/content-encoding/br/resources/foo.octetstream.br.headers new file mode 100644 index 0000000000..c0c19bc82a --- /dev/null +++ b/testing/web-platform/tests/fetch/content-encoding/br/resources/foo.octetstream.br.headers @@ -0,0 +1,2 @@ +Content-type: application/octet-stream +Content-Encoding: br diff --git a/testing/web-platform/tests/fetch/content-encoding/br/resources/foo.text.br b/testing/web-platform/tests/fetch/content-encoding/br/resources/foo.text.br Binary files differnew file mode 100644 index 0000000000..30cb2f7095 --- /dev/null +++ b/testing/web-platform/tests/fetch/content-encoding/br/resources/foo.text.br diff --git a/testing/web-platform/tests/fetch/content-encoding/br/resources/foo.text.br.headers b/testing/web-platform/tests/fetch/content-encoding/br/resources/foo.text.br.headers new file mode 100644 index 0000000000..8c03b823e0 --- /dev/null +++ b/testing/web-platform/tests/fetch/content-encoding/br/resources/foo.text.br.headers @@ -0,0 +1,2 @@ +Content-type: text/plain +Content-Encoding: br diff --git a/testing/web-platform/tests/fetch/fetch-later/activate-after.tentative.https.window.js b/testing/web-platform/tests/fetch/fetch-later/activate-after.tentative.https.window.js index 18b368066b..e62da0508a 100644 --- a/testing/web-platform/tests/fetch/fetch-later/activate-after.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/fetch-later/activate-after.tentative.https.window.js @@ -3,7 +3,7 @@ // META: script=/common/utils.js // META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js // META: script=/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; diff --git a/testing/web-platform/tests/fetch/fetch-later/iframe.tentative.https.window.js b/testing/web-platform/tests/fetch/fetch-later/iframe.tentative.https.window.js index 1e9fed1117..305272af41 100644 --- a/testing/web-platform/tests/fetch/fetch-later/iframe.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/fetch-later/iframe.tentative.https.window.js @@ -1,6 +1,6 @@ // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; diff --git a/testing/web-platform/tests/fetch/fetch-later/new-window.tentative.https.window.js b/testing/web-platform/tests/fetch/fetch-later/new-window.tentative.https.window.js index 93705418f2..27922f4626 100644 --- a/testing/web-platform/tests/fetch/fetch-later/new-window.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/fetch-later/new-window.tentative.https.window.js @@ -1,6 +1,6 @@ // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; diff --git a/testing/web-platform/tests/fetch/fetch-later/policies/csp-allowed.tentative.https.window.js b/testing/web-platform/tests/fetch/fetch-later/policies/csp-allowed.tentative.https.window.js index 60730e0242..32a3e106a0 100644 --- a/testing/web-platform/tests/fetch/fetch-later/policies/csp-allowed.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/fetch-later/policies/csp-allowed.tentative.https.window.js @@ -1,7 +1,7 @@ // META: title=FetchLater: allowed by CSP // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; const { diff --git a/testing/web-platform/tests/fetch/fetch-later/policies/csp-blocked.tentative.https.window.js b/testing/web-platform/tests/fetch/fetch-later/policies/csp-blocked.tentative.https.window.js index b32ddaecfc..ca9d881e8c 100644 --- a/testing/web-platform/tests/fetch/fetch-later/policies/csp-blocked.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/fetch-later/policies/csp-blocked.tentative.https.window.js @@ -1,7 +1,7 @@ // META: title=FetchLater: blocked by CSP // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; const { diff --git a/testing/web-platform/tests/fetch/fetch-later/policies/csp-redirect-to-blocked.tentative.https.window.js b/testing/web-platform/tests/fetch/fetch-later/policies/csp-redirect-to-blocked.tentative.https.window.js index 3c18727156..584f476b45 100644 --- a/testing/web-platform/tests/fetch/fetch-later/policies/csp-redirect-to-blocked.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/fetch-later/policies/csp-redirect-to-blocked.tentative.https.window.js @@ -1,7 +1,7 @@ // META: title=FetchLater: redirect blocked by CSP // META: script=/common/utils.js // META: script=/common/get-host-info.sub.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js // META: timeout=long 'use strict'; diff --git a/testing/web-platform/tests/fetch/fetch-later/quota.tentative.https.window.js b/testing/web-platform/tests/fetch/fetch-later/quota.tentative.https.window.js index 1b5b85563d..9d0ae4287d 100644 --- a/testing/web-platform/tests/fetch/fetch-later/quota.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/fetch-later/quota.tentative.https.window.js @@ -1,6 +1,6 @@ // META: script=/common/get-host-info.sub.js // META: script=/common/utils.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; diff --git a/testing/web-platform/tests/fetch/fetch-later/resources/fetch-later-helper.js b/testing/web-platform/tests/fetch/fetch-later/resources/fetch-later-helper.js new file mode 100644 index 0000000000..566b3e0a1a --- /dev/null +++ b/testing/web-platform/tests/fetch/fetch-later/resources/fetch-later-helper.js @@ -0,0 +1,206 @@ +'use strict'; + +const ROOT_NAME = 'fetch/fetch-later'; + +function parallelPromiseTest(func, description) { + async_test((t) => { + Promise.resolve(func(t)).then(() => t.done()).catch(t.step_func((e) => { + throw e; + })); + }, description); +} + +/** @enum {string} */ +const BeaconDataType = { + String: 'String', + ArrayBuffer: 'ArrayBuffer', + FormData: 'FormData', + URLSearchParams: 'URLSearchParams', + Blob: 'Blob', + File: 'File', +}; + +/** @enum {string} */ +const BeaconDataTypeToSkipCharset = { + String: '', + ArrayBuffer: '', + FormData: '\n\r', // CRLF characters will be normalized by FormData + URLSearchParams: ';,/?:@&=+$', // reserved URI characters + Blob: '', + File: '', +}; + +const BEACON_PAYLOAD_KEY = 'payload'; + +// Creates beacon data of the given `dataType` from `data`. +// @param {string} data - A string representation of the beacon data. Note that +// it cannot contain UTF-16 surrogates for all `BeaconDataType` except BLOB. +// @param {BeaconDataType} dataType - must be one of `BeaconDataType`. +// @param {string} contentType - Request Content-Type. +function makeBeaconData(data, dataType, contentType) { + switch (dataType) { + case BeaconDataType.String: + return data; + case BeaconDataType.ArrayBuffer: + return new TextEncoder().encode(data).buffer; + case BeaconDataType.FormData: + const formData = new FormData(); + if (data.length > 0) { + formData.append(BEACON_PAYLOAD_KEY, data); + } + return formData; + case BeaconDataType.URLSearchParams: + if (data.length > 0) { + return new URLSearchParams(`${BEACON_PAYLOAD_KEY}=${data}`); + } + return new URLSearchParams(); + case BeaconDataType.Blob: { + const options = {type: contentType || undefined}; + return new Blob([data], options); + } + case BeaconDataType.File: { + const options = {type: contentType || 'text/plain'}; + return new File([data], 'file.txt', options); + } + default: + throw Error(`Unsupported beacon dataType: ${dataType}`); + } +} + +// Create a string of `end`-`begin` characters, with characters starting from +// UTF-16 code unit `begin` to `end`-1. +function generateSequentialData(begin, end, skip) { + const codeUnits = Array(end - begin).fill().map((el, i) => i + begin); + if (skip) { + return String.fromCharCode( + ...codeUnits.filter(c => !skip.includes(String.fromCharCode(c)))); + } + return String.fromCharCode(...codeUnits); +} + +function generatePayload(size) { + if (size == 0) { + return ''; + } + const prefix = String(size) + ':'; + if (size < prefix.length) { + return Array(size).fill('*').join(''); + } + if (size == prefix.length) { + return prefix; + } + + return prefix + Array(size - prefix.length).fill('*').join(''); +} + +function generateSetBeaconURL(uuid, options) { + const host = (options && options.host) || ''; + let url = `${host}/${ROOT_NAME}/resources/set_beacon.py?uuid=${uuid}`; + if (options) { + if (options.expectOrigin !== undefined) { + url = `${url}&expectOrigin=${options.expectOrigin}`; + } + if (options.expectPreflight !== undefined) { + url = `${url}&expectPreflight=${options.expectPreflight}`; + } + if (options.expectCredentials !== undefined) { + url = `${url}&expectCredentials=${options.expectCredentials}`; + } + + if (options.useRedirectHandler) { + const redirect = `${host}/common/redirect.py` + + `?location=${encodeURIComponent(url)}`; + url = redirect; + } + } + return url; +} + +async function poll(asyncFunc, expected) { + const maxRetries = 30; + const waitInterval = 100; // milliseconds. + const delay = ms => new Promise(res => setTimeout(res, ms)); + + let result = {data: []}; + for (let i = 0; i < maxRetries; i++) { + result = await asyncFunc(); + if (!expected(result)) { + await delay(waitInterval); + continue; + } + return result; + } + return result; +} + +// Waits until the `options.count` number of beacon data available from the +// server. Defaults to 1. +// If `options.data` is set, it will be used to compare with the data from the +// response. +async function expectBeacon(uuid, options) { + const expectedCount = + (options && options.count !== undefined) ? options.count : 1; + + const res = await poll( + async () => { + const res = await fetch( + `/${ROOT_NAME}/resources/get_beacon.py?uuid=${uuid}`, + {cache: 'no-store'}); + return await res.json(); + }, + (res) => { + if (expectedCount == 0) { + // If expecting no beacon, we should try to wait as long as possible. + // So always returning false here until `poll()` decides to terminate + // itself. + return false; + } + return res.data.length == expectedCount; + }); + if (!options || !options.data) { + assert_equals( + res.data.length, expectedCount, + 'Number of sent beacons does not match expected count:'); + return; + } + + if (expectedCount == 0) { + assert_equals( + res.data.length, 0, + 'Number of sent beacons does not match expected count:'); + return; + } + + const decoder = options && options.percentDecoded ? (s) => { + // application/x-www-form-urlencoded serializer encodes space as '+' + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent + s = s.replace(/\+/g, '%20'); + return decodeURIComponent(s); + } : (s) => s; + + assert_equals( + res.data.length, options.data.length, + `The size of beacon data ${ + res.data.length} from server does not match expected value ${ + options.data.length}.`); + for (let i = 0; i < options.data.length; i++) { + assert_equals( + decoder(res.data[i]), options.data[i], + 'The beacon data does not match expected value.'); + } +} + +function generateHTML(script) { + return `<!DOCTYPE html><body><script>${script}</script></body>`; +} + +// Loads `script` into an iframe and appends it to the current document. +// Returns the loaded iframe element. +async function loadScriptAsIframe(script) { + const iframe = document.createElement('iframe'); + iframe.srcdoc = generateHTML(script); + const iframeLoaded = new Promise(resolve => iframe.onload = resolve); + document.body.appendChild(iframe); + await iframeLoaded; + return iframe; +} diff --git a/testing/web-platform/tests/pending-beacon/resources/get_beacon.py b/testing/web-platform/tests/fetch/fetch-later/resources/get_beacon.py index 32cb9a9ba3..32cb9a9ba3 100644 --- a/testing/web-platform/tests/pending-beacon/resources/get_beacon.py +++ b/testing/web-platform/tests/fetch/fetch-later/resources/get_beacon.py diff --git a/testing/web-platform/tests/pending-beacon/resources/set_beacon.py b/testing/web-platform/tests/fetch/fetch-later/resources/set_beacon.py index 1c71f23e57..1c71f23e57 100644 --- a/testing/web-platform/tests/pending-beacon/resources/set_beacon.py +++ b/testing/web-platform/tests/fetch/fetch-later/resources/set_beacon.py diff --git a/testing/web-platform/tests/fetch/fetch-later/send-on-deactivate.tentative.https.window.js b/testing/web-platform/tests/fetch/fetch-later/send-on-deactivate.tentative.https.window.js index d91c73580a..3bcf07483a 100644 --- a/testing/web-platform/tests/fetch/fetch-later/send-on-deactivate.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/fetch-later/send-on-deactivate.tentative.https.window.js @@ -3,7 +3,7 @@ // META: script=/common/utils.js // META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js // META: script=/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; diff --git a/testing/web-platform/tests/fetch/fetch-later/send-on-discard/not-send-after-abort.tentative.https.window.js b/testing/web-platform/tests/fetch/fetch-later/send-on-discard/not-send-after-abort.tentative.https.window.js index ff8d9520e0..6ddafd7813 100644 --- a/testing/web-platform/tests/fetch/fetch-later/send-on-discard/not-send-after-abort.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/fetch-later/send-on-discard/not-send-after-abort.tentative.https.window.js @@ -1,5 +1,5 @@ // META: script=/common/utils.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js 'use strict'; diff --git a/testing/web-platform/tests/fetch/fetch-later/send-on-discard/send-multiple-with-activate-after.tentative.https.window.js b/testing/web-platform/tests/fetch/fetch-later/send-on-discard/send-multiple-with-activate-after.tentative.https.window.js index 11e85b31a7..0bbe94c39f 100644 --- a/testing/web-platform/tests/fetch/fetch-later/send-on-discard/send-multiple-with-activate-after.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/fetch-later/send-on-discard/send-multiple-with-activate-after.tentative.https.window.js @@ -1,5 +1,5 @@ // META: script=/common/utils.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js // META: timeout=long 'use strict'; diff --git a/testing/web-platform/tests/fetch/fetch-later/send-on-discard/send-multiple.tentative.https.window.js b/testing/web-platform/tests/fetch/fetch-later/send-on-discard/send-multiple.tentative.https.window.js index df34ec9ac0..05bb2dc114 100644 --- a/testing/web-platform/tests/fetch/fetch-later/send-on-discard/send-multiple.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/fetch-later/send-on-discard/send-multiple.tentative.https.window.js @@ -1,5 +1,5 @@ // META: script=/common/utils.js -// META: script=/pending-beacon/resources/pending_beacon-helper.js +// META: script=/fetch/fetch-later/resources/fetch-later-helper.js // META: timeout=long 'use strict'; diff --git a/testing/web-platform/tests/fetch/private-network-access/anchor.tentative.https.window.js b/testing/web-platform/tests/fetch/private-network-access/anchor.tentative.https.window.js index f5473868b7..4e860ad381 100644 --- a/testing/web-platform/tests/fetch/private-network-access/anchor.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/private-network-access/anchor.tentative.https.window.js @@ -149,44 +149,6 @@ subsetTestByKey("from-public", promise_test_parallel, t => anchorTest(t, { expected: NavigationTestResult.SUCCESS, }), "public to public: no preflight required."); -subsetTestByKey( - 'from-public', promise_test_parallel, - t => anchorTest(t, { - source: {server: Server.HTTPS_PUBLIC}, - target: { - server: Server.HTTPS_PUBLIC, - behavior: { - redirect: preflightUrl({ - server: Server.HTTPS_PRIVATE, - behavior: { - preflight: PreflightBehavior.noCorsHeader(token()), - } - }), - } - }, - expected: NavigationTestResult.FAILURE, - }), - 'public to public redirected to private: missing CORS headers.'); - -subsetTestByKey( - 'from-public', promise_test_parallel, - t => anchorTest(t, { - source: {server: Server.HTTPS_PUBLIC}, - target: { - server: Server.HTTPS_PUBLIC, - behavior: { - redirect: preflightUrl({ - server: Server.HTTPS_PRIVATE, - behavior: { - preflight: PreflightBehavior.navigation(token()), - } - }), - } - }, - expected: NavigationTestResult.SUCCESS, - }), - 'public to public to private: success.'); - // The following tests verify that `CSP: treat-as-public-address` makes // documents behave as if they had been served from a public IP address. diff --git a/testing/web-platform/tests/fetch/private-network-access/resources/support.sub.js b/testing/web-platform/tests/fetch/private-network-access/resources/support.sub.js index 1cb432b787..7d133b0288 100644 --- a/testing/web-platform/tests/fetch/private-network-access/resources/support.sub.js +++ b/testing/web-platform/tests/fetch/private-network-access/resources/support.sub.js @@ -480,13 +480,6 @@ const NavigationTestResult = { }; async function windowOpenTest(t, { source, target, expected }) { - if (target.behavior && target.behavior.redirect) { - target.behavior.redirect.searchParams.set('file', 'openee.html'); - target.behavior.redirect.searchParams.set( - 'file-if-no-preflight-received', - 'no-preflight-received.html', - ); - } const targetUrl = preflightUrl(target); targetUrl.searchParams.set("file", "openee.html"); targetUrl.searchParams.set( @@ -514,13 +507,6 @@ async function windowOpenTest(t, { source, target, expected }) { } async function windowOpenExistingTest(t, { source, target, expected }) { - if (target.behavior && target.behavior.redirect) { - target.behavior.redirect.searchParams.set('file', 'openee.html'); - target.behavior.redirect.searchParams.set( - 'file-if-no-preflight-received', - 'no-preflight-received.html', - ); - } const targetUrl = preflightUrl(target); targetUrl.searchParams.set("file", "openee.html"); targetUrl.searchParams.set( @@ -549,13 +535,6 @@ async function windowOpenExistingTest(t, { source, target, expected }) { } async function anchorTest(t, { source, target, expected }) { - if (target.behavior && target.behavior.redirect) { - target.behavior.redirect.searchParams.set('file', 'openee.html'); - target.behavior.redirect.searchParams.set( - 'file-if-no-preflight-received', - 'no-preflight-received.html', - ); - } const targetUrl = preflightUrl(target); targetUrl.searchParams.set("file", "openee.html"); targetUrl.searchParams.set( diff --git a/testing/web-platform/tests/fetch/private-network-access/window-open-existing.tentative.https.window.js b/testing/web-platform/tests/fetch/private-network-access/window-open-existing.tentative.https.window.js index 565a2117a8..6a2a624fc8 100644 --- a/testing/web-platform/tests/fetch/private-network-access/window-open-existing.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/private-network-access/window-open-existing.tentative.https.window.js @@ -167,44 +167,6 @@ subsetTestByKey( }), 'public to public: no preflight required.'); -subsetTestByKey( - 'from-public', promise_test_parallel, - t => windowOpenExistingTest(t, { - source: {server: Server.HTTPS_PUBLIC}, - target: { - server: Server.HTTPS_PUBLIC, - behavior: { - redirect: preflightUrl({ - server: Server.HTTPS_PRIVATE, - behavior: { - preflight: PreflightBehavior.noCorsHeader(token()), - } - }), - } - }, - expected: NavigationTestResult.FAILURE, - }), - 'public to public redirected to private: missing CORS headers.'); - -subsetTestByKey( - 'from-public', promise_test_parallel, - t => windowOpenExistingTest(t, { - source: {server: Server.HTTPS_PUBLIC}, - target: { - server: Server.HTTPS_PUBLIC, - behavior: { - redirect: preflightUrl({ - server: Server.HTTPS_PRIVATE, - behavior: { - preflight: PreflightBehavior.navigation(token()), - } - }), - } - }, - expected: NavigationTestResult.SUCCESS, - }), - 'public to public to private: success.'); - // The following tests verify that `CSP: treat-as-public-address` makes // documents behave as if they had been served from a public IP address. diff --git a/testing/web-platform/tests/fetch/private-network-access/window-open.tentative.https.window.js b/testing/web-platform/tests/fetch/private-network-access/window-open.tentative.https.window.js index 42d70af4e4..6793d1f3b4 100644 --- a/testing/web-platform/tests/fetch/private-network-access/window-open.tentative.https.window.js +++ b/testing/web-platform/tests/fetch/private-network-access/window-open.tentative.https.window.js @@ -149,44 +149,6 @@ subsetTestByKey("from-public", promise_test_parallel, t => windowOpenTest(t, { expected: NavigationTestResult.SUCCESS, }), "public to public: no preflight required."); -subsetTestByKey( - 'from-public', promise_test_parallel, - t => windowOpenTest(t, { - source: {server: Server.HTTPS_PUBLIC}, - target: { - server: Server.HTTPS_PUBLIC, - behavior: { - redirect: preflightUrl({ - server: Server.HTTPS_PRIVATE, - behavior: { - preflight: PreflightBehavior.noCorsHeader(token()), - } - }), - } - }, - expected: NavigationTestResult.FAILURE, - }), - 'public to public redirected to private: missing CORS headers.'); - -subsetTestByKey( - 'from-public', promise_test_parallel, - t => windowOpenTest(t, { - source: {server: Server.HTTPS_PUBLIC}, - target: { - server: Server.HTTPS_PUBLIC, - behavior: { - redirect: preflightUrl({ - server: Server.HTTPS_PRIVATE, - behavior: { - preflight: PreflightBehavior.navigation(token()), - } - }), - } - }, - expected: NavigationTestResult.SUCCESS, - }), - 'public to public to private: success.'); - // The following tests verify that `CSP: treat-as-public-address` makes // documents behave as if they had been served from a public IP address. diff --git a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.html b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.html deleted file mode 100644 index 66456a8876..0000000000 --- a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.html +++ /dev/null @@ -1,26 +0,0 @@ -<!DOCTYPE html> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<body> -<script> - const blank = 'about:blank'; - const dangling_url = 'resources/empty.html?\n<'; - const api_calls = [ - `window.open(\`${dangling_url}\`,'_self')`, - `location.replace(\`${dangling_url}\`)`, - ]; - - api_calls.forEach(call => { - async_test(t => { - const iframe = - document.body.appendChild(document.createElement('iframe')); - t.step(() => { - iframe.contentWindow.eval(call) - t.step_timeout(()=>{ - assert_false(iframe.contentWindow.location.href.endsWith(blank)); - t.done(); - }, 500); - }); - }, `Does not block ${call}`); - }); -</script> diff --git a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.tentative.https.html b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.tentative.https.html new file mode 100644 index 0000000000..428decfc58 --- /dev/null +++ b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-allowed-apis.tentative.https.html @@ -0,0 +1,80 @@ +<!DOCTYPE html> +<meta name="timeout" content="long"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> + const blank = 'about:blank'; + const dangling_url = 'resources/empty.html?\n<'; + const navigation_api_calls = [ + `window.open(\`${dangling_url}\`,'_self')`, + `location.replace(\`${dangling_url}\`)`, + ]; + + function get_requests(worker, expected) { + return new Promise(resolve => { + navigator.serviceWorker.addEventListener('message', function onMsg(evt) { + if (evt.data.size >= expected) { + navigator.serviceWorker.removeEventListener('message', onMsg); + resolve(evt.data); + } else { + worker.postMessage(""); + } + }); + worker.postMessage(""); + }); + } + + navigation_api_calls.forEach(call => { + async_test(t => { + const iframe = + document.body.appendChild(document.createElement('iframe')); + t.step(() => { + iframe.contentWindow.eval(call); + t.step_timeout(() => { + assert_false(iframe.contentWindow.location.href.endsWith(blank)); + t.done(); + }, 500); + }); + }, `Does not block ${call}`); + }); + + const dangling_resource = "404?type=text/javascript&\n<" + const api_calls = [ + [`const xhr = new XMLHttpRequest(); + xhr.open("GET", \`${"xhr" + dangling_resource}\`); + xhr.send(null);`, "xhr"], + [`new EventSource(\`${"EventSource" + dangling_resource}\`)`,"EventSource"], + [`fetch(\`${"fetch" + dangling_resource}\`).catch(()=>{})`, "fetch"], + [`new Worker(\`${"Worker" + dangling_resource}\`)`, "Worker"], + [`let text = \`try{importScripts(\\\`${location.href + "/../importScripts" + dangling_resource}\\\`)}catch(e){}\`; + let blob = new Blob([text], {type : 'text/javascript'}); + let url = URL.createObjectURL(blob); + new Worker(url)`, "importScripts"], + + ]; + + navigator.serviceWorker.register('service-worker.js'); + const iframe = document.createElement('iframe'); + iframe.src = "resources/empty.html"; + document.body.appendChild(iframe); + api_calls.forEach(call => { + promise_test(t => { + return new Promise(resolve => { + navigator.serviceWorker.ready.then(t.step_func(registration => { + iframe.contentWindow.eval(call[0]); + get_requests(registration.active, 0).then(t.step_func(requests => { + resolve(assert_true(requests.has(call[1] + dangling_resource))); + })); + })); + }); + }, `Does not block ${call[1]}`); + }); + + async_test(t => { + let url = new URL(location.origin + "/" + dangling_url); + // Newlines are removed by the URL parser. + assert_true(url.href.endsWith(encodeURI(dangling_url.replace("\n","")))); + t.done(); + }, `Does not block new URL()`); +</script> diff --git a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-data-url.sub.html b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-data-url.tentative.sub.html index f27735daa1..f27735daa1 100644 --- a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-data-url.sub.html +++ b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation-data-url.tentative.sub.html diff --git a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.html b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.html index 61a931608b..61a931608b 100644 --- a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.html +++ b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.html diff --git a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.https.html b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.https.html index 3f038cbb7b..3f038cbb7b 100644 --- a/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.https.html +++ b/testing/web-platform/tests/fetch/security/dangling-markup/dangling-markup-mitigation.tentative.https.html diff --git a/testing/web-platform/tests/fetch/security/dangling-markup/service-worker.js b/testing/web-platform/tests/fetch/security/dangling-markup/service-worker.js index 837e216a01..99d5456a87 100644 --- a/testing/web-platform/tests/fetch/security/dangling-markup/service-worker.js +++ b/testing/web-platform/tests/fetch/security/dangling-markup/service-worker.js @@ -16,18 +16,24 @@ addEventListener('fetch', evt => { const url = new URL(evt.request.url); const path = url.pathname; const search = url.search || "?"; + const params = new URLSearchParams(search); + const type = params.get('type'); if (path.includes('404')) { const dir = path.split('/'); const request = dir[dir.length-1] + search; if (!requests.has(request)) { requests.add(request); } - evt.respondWith(new Response("")); + evt.respondWith(new Response("", { + headers: { + "Content-Type": type || "text/plain" + } + })); } else if (path.endsWith('resources.html')) { - const html = (new URLSearchParams(search)).get('html'); + const html = params.get('html') || ""; evt.respondWith(new Response(html, { headers: { - "Content-Type": "text/html" + "Content-Type": type || "text/html" } })); } diff --git a/testing/web-platform/tests/fledge/tentative/TODO b/testing/web-platform/tests/fledge/tentative/TODO index 0f68a7c914..6fd378c035 100644 --- a/testing/web-platform/tests/fledge/tentative/TODO +++ b/testing/web-platform/tests/fledge/tentative/TODO @@ -47,6 +47,7 @@ Need tests for (likely not a complete list): * adAuctionConfig passed to reportResult(). * Component auctions. * Including cross-origin sellers. + * Timeouts (seller timeout, buyer timeout, reporting timeout). * browserSignals fields in scoring/bidding methods. * In reporting methods, browserSignals fields: topLevelSeller, componentSeller, modifiedBid, adCost, madeHighestScoringOtherBid @@ -88,3 +89,4 @@ If possible: * Signals request batching. This is an optional feature, so can't require it, but maybe a test where batching could be used, and make sure things work, whether batching is used or not? +* reporting timeout being 0. diff --git a/testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js b/testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js index 0e1d22c261..965f9a60c7 100644 --- a/testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js +++ b/testing/web-platform/tests/fledge/tentative/additional-bids.https.window.js @@ -10,15 +10,8 @@ // This file contains tests for additional bids and negative targeting. // // TODO: -// - test that an negatively targeted additional bid is suppressed. -// - test that an incorrectly signed additional bid is not negative targeted. -// - test that an missing-signature additional bid is not negative targeted. -// - test that an additional bid with some correct signatures can be negative. -// negative targeted for those negative interest groups whose signatures -// match. -// - test an additional bid with multiple negative interest groups. -// - test that multiple negative interest groups with mismatched joining origins -// is not negative targeted. +// - test that an additional bid with some correct signatures can be negative +// targeted for those negative interest groups whose signatures match. // - test that additional bids can be fetched using an iframe navigation. // - test that additional bids are not fetched using an iframe navigation for // which the `adAuctionHeaders=true` attribute is not specified. @@ -26,12 +19,19 @@ // `adAuctionHeaders: true` is not specified. // - test that an additional bid with an incorrect auction nonce is not used // included in an auction. Same for seller and top-level seller. +// - lots of tests for different types of malformed additional bids, e.g. +// missing fields, malformed signature, invalid currency code, +// missing joining origin for multiple negative interest groups, etc. // - test that correctly formatted additional bids are included in an auction // when fetched alongside malformed additional bid headers by a Fetch -// request. +// request (both invalid headers and invalid additional bids) +// - test that an additional bid is rejected if its from a buyer who is not +// allowed to participate in the auction. +// - test that an additional bid is rejected if its currency doesn't match the +// buyer's associated per-buyer currency from the auction config. // - test that correctly formatted additional bids are included in an auction // when fetched alongside malformed additional bid headers by an iframe -// navigation. +// navigation (both invalid headers and invalid additional bids). // - test that reportWin is not used for reporting an additional bid win. // - test that additional bids can *not* be fetched from iframe subresource // requests. @@ -80,6 +80,9 @@ // for ad auction headers interception to associate it with this auction. const SINGLE_SELLER_AUCTION_SELLER = window.location.origin; +const ADDITIONAL_BID_SECRET_KEY = 'nWGxne/9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A='; +const ADDITIONAL_BID_PUBLIC_KEY = '11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo='; + // Single-seller auction with a single buyer who places a single additional // bid. As the only bid, this wins. subsetTest(promise_test, async test => { @@ -88,12 +91,12 @@ subsetTest(promise_test, async test => { const seller = SINGLE_SELLER_AUCTION_SELLER; const buyer = OTHER_ORIGIN1; - const additionalBid = createAdditionalBid( + const additionalBid = additionalBidHelper.createAdditionalBid( uuid, auctionNonce, seller, buyer, 'horses', 1.99); await runAdditionalBidTest( test, uuid, [buyer], auctionNonce, - fetchAdditionalBids(seller, [additionalBid]), + additionalBidHelper.fetchAdditionalBids(seller, [additionalBid]), /*highestScoringOtherBid=*/0, /*winningAdditionalBidId=*/'horses'); }, 'single valid additional bid'); @@ -105,16 +108,17 @@ subsetTest(promise_test, async test => { const seller = SINGLE_SELLER_AUCTION_SELLER; const buyer1 = OTHER_ORIGIN1; - const additionalBid1 = createAdditionalBid( + const additionalBid1 = additionalBidHelper.createAdditionalBid( uuid, auctionNonce, seller, buyer1, 'horses', 1.99); const buyer2 = OTHER_ORIGIN2; - const additionalBid2 = createAdditionalBid( + const additionalBid2 = additionalBidHelper.createAdditionalBid( uuid, auctionNonce, seller, buyer2, 'planes', 2.99); await runAdditionalBidTest( test, uuid, [buyer1, buyer2], auctionNonce, - fetchAdditionalBids(seller, [additionalBid1, additionalBid2]), + additionalBidHelper.fetchAdditionalBids( + seller, [additionalBid1, additionalBid2]), /*highestScoringOtherBid=*/1.99, /*winningAdditionalBidId=*/'planes'); }, 'two valid additional bids'); @@ -127,20 +131,241 @@ subsetTest(promise_test, async test => { const seller = SINGLE_SELLER_AUCTION_SELLER; const buyer1 = OTHER_ORIGIN1; - const additionalBid1 = createAdditionalBid( + const additionalBid1 = additionalBidHelper.createAdditionalBid( uuid, auctionNonce, seller, buyer1, 'horses', 1.99); const buyer2 = OTHER_ORIGIN2; - const additionalBid2 = createAdditionalBid( + const additionalBid2 = additionalBidHelper.createAdditionalBid( uuid, auctionNonce, seller, buyer2, 'planes', 2.99); - await runAdditionalBidTest( test, uuid, [buyer1, buyer2], auctionNonce, Promise.all([ - fetchAdditionalBids(seller, [additionalBid1]), - fetchAdditionalBids(seller, [additionalBid2]) + additionalBidHelper.fetchAdditionalBids(seller, [additionalBid1]), + additionalBidHelper.fetchAdditionalBids(seller, [additionalBid2]) ]), /*highestScoringOtherBid=*/1.99, /*winningAdditionalBidId=*/'planes'); }, 'two valid additional bids from two distinct Fetch requests'); + +// Single-seller auction with a single additional bid. Because this additional +// bid is filtered by negative targeting, this auction has no winner. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const auctionNonce = await navigator.createAuctionNonce(); + const seller = SINGLE_SELLER_AUCTION_SELLER; + + const negativeInterestGroupName = 'already-owns-a-plane'; + + const buyer = OTHER_ORIGIN1; + const additionalBid = additionalBidHelper.createAdditionalBid( + uuid, auctionNonce, seller, buyer, 'planes', 2.99); + additionalBidHelper.addNegativeInterestGroup( + additionalBid, negativeInterestGroupName); + additionalBidHelper.signWithSecretKeys( + additionalBid, [ADDITIONAL_BID_SECRET_KEY]); + + await joinNegativeInterestGroup( + test, buyer, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY); + + await runBasicFledgeTestExpectingNoWinner( + test, uuid, + { interestGroupBuyers: [buyer], + auctionNonce: auctionNonce, + additionalBids: additionalBidHelper.fetchAdditionalBids( + seller, [additionalBid])}); +}, 'one additional bid filtered by negative targeting, so auction has no ' + + 'winner'); + +// Single-seller auction with a two buyers competing with additional bids. +// The higher of these has a negative interest group specified, and that +// negative interest group has been joined, so the lower bid wins. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const auctionNonce = await navigator.createAuctionNonce(); + const seller = SINGLE_SELLER_AUCTION_SELLER; + + const negativeInterestGroupName = 'already-owns-a-plane'; + + const buyer1 = OTHER_ORIGIN1; + const additionalBid1 = additionalBidHelper.createAdditionalBid( + uuid, auctionNonce, seller, buyer1, 'horses', 1.99); + + const buyer2 = OTHER_ORIGIN2; + const additionalBid2 = additionalBidHelper.createAdditionalBid( + uuid, auctionNonce, seller, buyer2, 'planes', 2.99); + additionalBidHelper.addNegativeInterestGroup( + additionalBid2, negativeInterestGroupName); + additionalBidHelper.signWithSecretKeys( + additionalBid2, [ADDITIONAL_BID_SECRET_KEY]); + + await joinNegativeInterestGroup( + test, buyer2, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY); + + await runAdditionalBidTest( + test, uuid, [buyer1, buyer2], auctionNonce, + additionalBidHelper.fetchAdditionalBids( + seller, [additionalBid1, additionalBid2]), + /*highestScoringOtherBid=*/0, + /*winningAdditionalBidId=*/'horses'); +}, 'higher additional bid is filtered by negative targeting, so ' + + 'lower additional bid win'); + +// Same as above, except that the bid is missing a signature, so that the +// negative targeting interest group is ignored, and the higher bid, which +// would have otherwise been filtered by negative targeting, wins. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const auctionNonce = await navigator.createAuctionNonce(); + const seller = SINGLE_SELLER_AUCTION_SELLER; + + const negativeInterestGroupName = 'already-owns-a-plane'; + + const buyer1 = OTHER_ORIGIN1; + const additionalBid1 = additionalBidHelper.createAdditionalBid( + uuid, auctionNonce, seller, buyer1, 'horses', 1.99); + + const buyer2 = OTHER_ORIGIN2; + const additionalBid2 = additionalBidHelper.createAdditionalBid( + uuid, auctionNonce, seller, buyer2, 'planes', 2.99); + additionalBidHelper.addNegativeInterestGroup( + additionalBid2, negativeInterestGroupName); + + await joinNegativeInterestGroup( + test, buyer2, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY); + + await runAdditionalBidTest( + test, uuid, [buyer1, buyer2], auctionNonce, + additionalBidHelper.fetchAdditionalBids( + seller, [additionalBid1, additionalBid2]), + /*highestScoringOtherBid=*/1.99, + /*winningAdditionalBidId=*/'planes'); +}, 'higher additional bid is filtered by negative targeting, but it is ' + + 'missing a signature, so it still wins'); + +// Same as above, except that the bid is signed incorrectly, so that the +// negative targeting interest group is ignored, and the higher bid, which +// would have otherwise been filtered by negative targeting, wins. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const auctionNonce = await navigator.createAuctionNonce(); + const seller = SINGLE_SELLER_AUCTION_SELLER; + + const negativeInterestGroupName = 'already-owns-a-plane'; + + const buyer1 = OTHER_ORIGIN1; + const additionalBid1 = additionalBidHelper.createAdditionalBid( + uuid, auctionNonce, seller, buyer1, 'horses', 1.99); + + const buyer2 = OTHER_ORIGIN2; + const additionalBid2 = additionalBidHelper.createAdditionalBid( + uuid, auctionNonce, seller, buyer2, 'planes', 2.99); + additionalBidHelper.addNegativeInterestGroup( + additionalBid2, negativeInterestGroupName); + additionalBidHelper.incorrectlySignWithSecretKeys( + additionalBid2, [ADDITIONAL_BID_SECRET_KEY]); + + await joinNegativeInterestGroup( + test, buyer2, negativeInterestGroupName, ADDITIONAL_BID_PUBLIC_KEY); + + await runAdditionalBidTest( + test, uuid, [buyer1, buyer2], auctionNonce, + additionalBidHelper.fetchAdditionalBids( + seller, [additionalBid1, additionalBid2]), + /*highestScoringOtherBid=*/1.99, + /*winningAdditionalBidId=*/'planes'); +}, 'higher additional bid is filtered by negative targeting, but it has an ' + + 'invalid signature, so it still wins'); + +// A test of an additional bid with multiple negative interest groups. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const auctionNonce = await navigator.createAuctionNonce(); + const seller = SINGLE_SELLER_AUCTION_SELLER; + + const negativeInterestGroupName1 = 'already-owns-a-plane'; + const negativeInterestGroupName2 = 'another-negative-interest-group'; + + const buyer1 = OTHER_ORIGIN1; + const additionalBid1 = additionalBidHelper.createAdditionalBid( + uuid, auctionNonce, seller, buyer1, 'horses', 1.99); + + const buyer2 = OTHER_ORIGIN2; + const additionalBid2 = additionalBidHelper.createAdditionalBid( + uuid, auctionNonce, seller, buyer2, 'planes', 2.99); + additionalBidHelper.addNegativeInterestGroups( + additionalBid2, [negativeInterestGroupName1, negativeInterestGroupName2], + /*joiningOrigin=*/window.location.origin); + additionalBidHelper.signWithSecretKeys( + additionalBid2, [ADDITIONAL_BID_SECRET_KEY]); + + await joinNegativeInterestGroup( + test, buyer2, negativeInterestGroupName1, ADDITIONAL_BID_PUBLIC_KEY); + + await runAdditionalBidTest( + test, uuid, [buyer1, buyer2], auctionNonce, + additionalBidHelper.fetchAdditionalBids( + seller, [additionalBid1, additionalBid2]), + /*highestScoringOtherBid=*/0, + /*winningAdditionalBidId=*/'horses'); +}, 'higher additional bid is filtered by negative targeting by two negative ' + + 'interest groups, and since one is on the device, the lower bid wins'); + +// Same as above, but with a mismatched joining origin. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const auctionNonce = await navigator.createAuctionNonce(); + const seller = SINGLE_SELLER_AUCTION_SELLER; + + const negativeInterestGroupName1 = 'already-owns-a-plane'; + const negativeInterestGroupName2 = 'another-negative-interest-group'; + + const buyer1 = OTHER_ORIGIN1; + const additionalBid1 = additionalBidHelper.createAdditionalBid( + uuid, auctionNonce, seller, buyer1, 'horses', 1.99); + + const buyer2 = OTHER_ORIGIN2; + const additionalBid2 = additionalBidHelper.createAdditionalBid( + uuid, auctionNonce, seller, buyer2, 'planes', 2.99); + additionalBidHelper.addNegativeInterestGroups( + additionalBid2, [negativeInterestGroupName1, negativeInterestGroupName2], + /*joiningOrigin=*/OTHER_ORIGIN1); + additionalBidHelper.signWithSecretKeys( + additionalBid2, [ADDITIONAL_BID_SECRET_KEY]); + + await joinNegativeInterestGroup( + test, buyer2, negativeInterestGroupName1, ADDITIONAL_BID_PUBLIC_KEY); + + await runAdditionalBidTest( + test, uuid, [buyer1, buyer2], auctionNonce, + additionalBidHelper.fetchAdditionalBids( + seller, [additionalBid1, additionalBid2]), + /*highestScoringOtherBid=*/1.99, + /*winningAdditionalBidId=*/'planes'); +}, 'higher additional bid is filtered by negative targeting by two negative ' + + 'interest groups, but because of a joining origin mismatch, it still wins'); + +// Ensure that trusted seller signals are retrieved for additional bids. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const auctionNonce = await navigator.createAuctionNonce(); + const seller = SINGLE_SELLER_AUCTION_SELLER; + + const buyer = OTHER_ORIGIN1; + const additionalBid = additionalBidHelper.createAdditionalBid( + uuid, auctionNonce, seller, buyer, 'horses', 1.99); + + let renderURL = createRenderURL(uuid); + await runBasicFledgeTestExpectingWinner( + test, uuid, + { interestGroupBuyers: [buyer], + auctionNonce: auctionNonce, + additionalBids: additionalBidHelper.fetchAdditionalBids( + seller, [additionalBid]), + decisionLogicURL: createDecisionScriptURL( + uuid, + { scoreAd: + `if(!"${renderURL}" in trustedScoringSignals.renderURL) ` + + 'throw "missing trusted signals";'}), + trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL}); +}, 'trusted seller signals retrieved for additional bids'); diff --git a/testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js b/testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js index c78a27bb87..9b12d077ba 100644 --- a/testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js +++ b/testing/web-platform/tests/fledge/tentative/auction-config-passed-to-worklets.https.window.js @@ -6,7 +6,8 @@ // META: variant=?1-5 // META: variant=?6-10 // META: variant=?11-15 -// META: variant=?16-last +// META: variant=?16-20 +// META: variant=?21-last "use strict;" @@ -206,3 +207,23 @@ makeTest({ [{width: ' 100', height: '200.50px '}, {width: ' 70.00sh ', height: '80.50sw'}]} }); + +makeTest({ + name: 'AuctionConfig.reportingTimeout with positive within-cap value.', + fieldName: 'reportingTimeout', + fieldValue: 100, +}); + +makeTest({ + name: 'AuctionConfig.reportingTimeout above the cap value.', + fieldName: 'reportingTimeout', + fieldValue: 5000, + auctionConfigOverrides: {fieldValue: 1234567890} +}); + +makeTest({ + name: 'AuctionConfig.reportingTimeout not provided', + fieldName: 'reportingTimeout', + fieldValue: 50, + auctionConfigOverrides: {fieldValue: undefined} +});
\ No newline at end of file diff --git a/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js b/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js index 8fbdc95dfc..5fa4fa252f 100644 --- a/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js +++ b/testing/web-platform/tests/fledge/tentative/auction-config.https.window.js @@ -10,7 +10,9 @@ // META: variant=?21-25 // META: variant=?26-30 // META: variant=?31-35 -// META: variant=?36-last +// META: variant=?36-40 +// META: variant=?40-45 +// META: variant=?46-last "use strict;" @@ -86,6 +88,14 @@ const EXPECT_NO_WINNER = auctionResult => { assert_equals(auctionResult, null, 'Auction unexpected had a winner'); }; +// Expect a winner (FencedFrameConfig). +const EXPECT_WINNER = + auctionResult => { + assert_true( + auctionResult instanceof FencedFrameConfig, + 'Auction did not return expected FencedFrameConfig'); + } + // Expect an exception of the given type. const EXPECT_EXCEPTION = exceptionType => auctionResult => { assert_not_equals(auctionResult, null, "got null instead of expected error"); @@ -130,6 +140,50 @@ makeTest({ }); makeTest({ + name: 'valid trustedScoringSignalsURL', + expect: EXPECT_WINNER, + auctionConfigOverrides: + {trustedScoringSignalsURL: window.location.origin + '/resource.json'} +}); + +makeTest({ + name: 'trustedScoringSignalsURL should not have a fragment', + expect: EXPECT_EXCEPTION(TypeError), + auctionConfigOverrides: + {trustedScoringSignalsURL: window.location.origin + '/resource.json#foo'} +}); + +makeTest({ + name: 'trustedScoringSignalsURL with an empty fragment is not OK', + expect: EXPECT_EXCEPTION(TypeError), + auctionConfigOverrides: + {trustedScoringSignalsURL: window.location.origin + '/resource.json#'} +}); + +makeTest({ + name: 'trustedScoringSignalsURL should not have a query', + expect: EXPECT_EXCEPTION(TypeError), + auctionConfigOverrides: + {trustedScoringSignalsURL: window.location.origin + '/resource.json?foo'} +}); + +makeTest({ + name: 'trustedScoringSignalsURL with an empty query is not OK', + expect: EXPECT_EXCEPTION(TypeError), + auctionConfigOverrides: + {trustedScoringSignalsURL: window.location.origin + '/resource.json?'} +}); + +makeTest({ + name: 'trustedScoringSignalsURL should not have embedded credentials', + expect: EXPECT_EXCEPTION(TypeError), + auctionConfigOverrides: { + trustedScoringSignalsURL: (window.location.origin + '/resource.json') + .replace('https://', 'https://user:pass@') + } +}); + +makeTest({ name: 'trustedScoringSignalsURL is cross-origin with seller', expect: EXPECT_EXCEPTION(TypeError), auctionConfigOverrides: { trustedScoringSignalsURL: "https://example.com" }, @@ -420,9 +474,9 @@ subsetTest(promise_test, async test => { let bid = browserSignals.forDebuggingOnlyInCooldownOrLockout ? 1 : 2; return {bid: bid, render: '${renderURL}'};`, reportWin: ` - if (browserSignals.bid == 1) + if (browserSignals.bid === 1) sendReportTo('${bidderReportURL1}'); - if (browserSignals.bid == 2) + if (browserSignals.bid === 2) sendReportTo('${bidderReportURL2}');` }) @@ -443,9 +497,9 @@ subsetTest(promise_test, async test => { browserSignals.forDebuggingOnlyInCooldownOrLockout ? 1 : 2; return {desirability: desirability};`, reportResult: ` - if (browserSignals.desirability == 1) + if (browserSignals.desirability === 1) sendReportTo('${sellerReportURL1}'); - if (browserSignals.desirability == 2) + if (browserSignals.desirability === 2) sendReportTo('${sellerReportURL2}');` }) }; diff --git a/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js b/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js index 7e98570b9e..6b22585d57 100644 --- a/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js +++ b/testing/web-platform/tests/fledge/tentative/component-ads.https.window.js @@ -61,7 +61,7 @@ async function runComponentAdLoadingTest(test, uuid, numComponentAdsInInterestGr `// "status" is passed to the beacon URL, to be verified by waitForObservedRequests(). let status = "ok"; const componentAds = window.fence.getNestedConfigs() - if (componentAds.length != 40) + if (componentAds.length !== 40) status = "unexpected getNestedConfigs() length"; for (let i of ${JSON.stringify(componentAdsToLoad)}) { let fencedFrame = document.createElement("fencedframe"); @@ -144,7 +144,7 @@ subsetTest(promise_test, async test => { const nestedConfigsLength = window.fence.getNestedConfigs().length // "getNestedConfigs()" should return a list of 40 configs, to avoid leaking // whether there were any component URLs to the page. - if (nestedConfigsLength != 40) + if (nestedConfigsLength !== 40) status = "unexpected getNestedConfigs() length: " + nestedConfigsLength; window.fence.reportEvent({eventType: "beacon", eventData: status, diff --git a/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js b/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js index c70532024c..015c20a5c2 100644 --- a/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js +++ b/testing/web-platform/tests/fledge/tentative/component-auction.https.window.js @@ -685,7 +685,7 @@ subsetTest(promise_test, async test => { auctionConfig.componentAuctions[0].decisionLogicURL = createDecisionScriptURL( uuid, - { scoreAd: `if (browserSignals.renderURL != '${renderURL1}') + { scoreAd: `if (browserSignals.renderURL !== '${renderURL1}') throw 'Wrong ad';`, reportResult: `sendReportTo('${seller1ReportURL}');`} ); @@ -696,7 +696,7 @@ subsetTest(promise_test, async test => { decisionLogicURL: createDecisionScriptURL( uuid, { origin: OTHER_ORIGIN1, - scoreAd: `if (browserSignals.renderURL != '${renderURL2}') + scoreAd: `if (browserSignals.renderURL !== '${renderURL2}') throw 'Wrong ad';`, reportResult: `sendReportTo('${seller2ReportURL}');`} ) diff --git a/testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js b/testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js index a8cf93049f..eed74c522f 100644 --- a/testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js +++ b/testing/web-platform/tests/fledge/tentative/cross-origin.https.window.js @@ -356,7 +356,7 @@ subsetTest(promise_test, async test => { throw "Wrong origin: " + interestGroup.owner; if (!interestGroup.biddingLogicURL.startsWith("${bidderOrigin}")) throw "Wrong origin: " + interestGroup.biddingLogicURL; - if (interestGroup.ads[0].renderURL != "${renderURL}") + if (interestGroup.ads[0].renderURL !== "${renderURL}") throw "Wrong renderURL: " + interestGroup.ads[0].renderURL; if (browserSignals.seller !== "${sellerOrigin}") throw "Wrong origin: " + browserSignals.seller;`, diff --git a/testing/web-platform/tests/fledge/tentative/currency.https.window.js b/testing/web-platform/tests/fledge/tentative/currency.https.window.js index 9a33d12148..99943cecbf 100644 --- a/testing/web-platform/tests/fledge/tentative/currency.https.window.js +++ b/testing/web-platform/tests/fledge/tentative/currency.https.window.js @@ -501,7 +501,7 @@ subsetTest(promise_test, async test => { topLevelSellerScriptParamsOverride: { scoreAd: ` // scoreAd sees what's actually passed in. - if (bid != 9) + if (bid !== 9) throw 'Wrong bid'; if (browserSignals.bidCurrency !== '???') throw 'Wrong currency';` diff --git a/testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js b/testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js index 8687e3f296..c7078ae08a 100644 --- a/testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js +++ b/testing/web-platform/tests/fledge/tentative/generate-bid-browser-signals.https.window.js @@ -939,7 +939,7 @@ subsetTest(promise_test, async test => { if (!deepEquals(Object.keys(instance.exports), ["increment"])) throw "Unexpected exports: " + JSON.stringify(instance.exports); - if (instance.exports.increment(1) != 2) + if (instance.exports.increment(1) !== 2) throw "Unexpected increment result: " + instance.exports.increment(1);` }) } }); diff --git a/testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js b/testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js index c7c7120240..db6ef2d35a 100644 --- a/testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js +++ b/testing/web-platform/tests/fledge/tentative/reporting-arguments.https.window.js @@ -259,7 +259,7 @@ subsetTest(promise_test, async test => { // reportResultSuccessCondition: `browserSignals.interestGroupName === undefined`, // reportWinSuccessCondition: - `browserSignals.interestGroupName === ''` + `browserSignals.interestGroupName === 'default name'` ); }, 'browserSignals.interestGroupName test.'); @@ -303,3 +303,53 @@ await runReportArgumentValidationTest( uuid ); }, 'browserSignals.madeHighestScoringOtherBid with other bid.'); + +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + await runReportTest( + test, uuid, + { reportResultSuccessCondition: + `browserSignals.reportingTimeout === undefined`, + reportResult: + `sendReportTo('${createSellerReportURL(uuid)}');`, + reportWinSuccessCondition: + 'browserSignals.reportingTimeout === 100', + reportWin: + `sendReportTo('${createBidderReportURL(uuid)}');` }, + // expectedReportURLs: + [createSellerReportURL(uuid), createBidderReportURL(uuid)], + // renderURLOverride + null, + // auctionConfigOverrides + {reportingTimeout: 100}); +}, 'browserSignals.reportingTimeout with custom value from auction config.'); + +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + await runReportTest( + test, uuid, + { reportResultSuccessCondition: + `browserSignals.reportingTimeout === undefined`, + reportResult: + `sendReportTo('${createSellerReportURL(uuid)}');`, + reportWinSuccessCondition: + 'browserSignals.reportingTimeout === 5000', + reportWin: + `sendReportTo('${createBidderReportURL(uuid)}');` }, + // expectedReportURLs: + [createSellerReportURL(uuid), createBidderReportURL(uuid)], + // renderURLOverride + null, + // auctionConfigOverrides + {reportingTimeout: 1234567890}); +}, 'browserSignals.reportingTimeout above the cap value.'); + +subsetTest(promise_test, async test => { + await runReportArgumentValidationTest( + test, + // reportResultSuccessCondition: + `browserSignals.reportingTimeout === undefined`, + // reportWinSuccessCondition: + `browserSignals.reportingTimeout === 50` + ); +}, 'browserSignals.reportingTimeout default value.'); diff --git a/testing/web-platform/tests/fledge/tentative/resources/additional-bids.py b/testing/web-platform/tests/fledge/tentative/resources/additional-bids.py index 060606b41d..721909a045 100644 --- a/testing/web-platform/tests/fledge/tentative/resources/additional-bids.py +++ b/testing/web-platform/tests/fledge/tentative/resources/additional-bids.py @@ -13,6 +13,7 @@ with a value of b"?1"; this entrypoint otherwise returns a 400 response. import json import base64 +import fledge.tentative.resources.ed25519 as ed25519 import fledge.tentative.resources.fledge_http_server_util as fledge_http_server_util @@ -20,6 +21,57 @@ class BadRequestError(Exception): pass +def _generate_signature(message, base64_encoded_secret_key): + """Returns a signature entry for a signed additional bid. + + Args: + base64_encoded_secret_key: base64-encoded Ed25519 key with which to sign + the message. From this secret key, the public key can be deduced, which + becomes part of the signature entry. + message: The additional bid text (or other text if generating an invalid + signature) to sign. + """ + secret_key = base64.b64decode(base64_encoded_secret_key.encode("utf-8")) + public_key = ed25519.publickey_unsafe(secret_key) + signature = ed25519.signature_unsafe( + message.encode("utf-8"), secret_key, public_key) + return { + "key": base64.b64encode(public_key).decode("utf-8"), + "signature": base64.b64encode(signature).decode("utf-8") + } + + +def _sign_additional_bid(additional_bid_string, + secret_keys_for_valid_signatures, + secret_keys_for_invalid_signatures): + """Returns a signed additional bid given an additional bid and secret keys. + + Args: + additional_bid_string: string representation of the additional bid + secret_keys_for_valid_signatures: a list of strings, each a base64-encoded + Ed25519 secret key with which to sign the additional bid + secret_keys_for_invalid_signatures: a list of strings, each a base64-encoded + Ed25519 secret key with which to incorrectly sign the additional bid + """ + signatures = [] + signatures.extend( + _generate_signature(additional_bid_string, secret_key) + for secret_key in secret_keys_for_valid_signatures) + + # For invalid signatures, we use the correct secret key to sign a different + # message - the additional bid prepended by 'invalid' - so that the signature + # is a structually valid signature but can't be used to verify the additional + # bid. + signatures.extend( + _generate_signature("invalid" + additional_bid_string, secret_key) + for secret_key in secret_keys_for_invalid_signatures) + + return json.dumps({ + "bid": additional_bid_string, + "signatures": signatures + }) + + def main(request, response): try: if fledge_http_server_util.handle_cors_headers_and_preflight(request, response): @@ -34,14 +86,16 @@ def main(request, response): if not additional_bids: raise BadRequestError("Missing 'additionalBids' parameter") for additional_bid in json.loads(additional_bids): - additional_bid_string = json.dumps(additional_bid) + # Each additional bid may have associated testMetadata. Remove this from + # the additional bid and use it to adjust the behavior of this handler. + test_metadata = additional_bid.pop("testMetadata", {}) auction_nonce = additional_bid.get("auctionNonce", None) if not auction_nonce: raise BadRequestError("Additional bid missing required 'auctionNonce' field") - signed_additional_bid = json.dumps({ - "bid": additional_bid_string, - "signatures": [] - }) + signed_additional_bid = _sign_additional_bid( + json.dumps(additional_bid), + test_metadata.get("secretKeysForValidSignatures", []), + test_metadata.get("secretKeysForInvalidSignatures", [])) additional_bid_header_value = (auction_nonce.encode("utf-8") + b":" + base64.b64encode(signed_additional_bid.encode("utf-8"))) response.headers.append(b"Ad-Auction-Additional-Bid", additional_bid_header_value) diff --git a/testing/web-platform/tests/fledge/tentative/resources/ed25519.py b/testing/web-platform/tests/fledge/tentative/resources/ed25519.py new file mode 100644 index 0000000000..53e548ab8e --- /dev/null +++ b/testing/web-platform/tests/fledge/tentative/resources/ed25519.py @@ -0,0 +1,289 @@ +# ed25519.py - Optimized version of the reference implementation of Ed25519 +# +# Written in 2011? by Daniel J. Bernstein <djb@cr.yp.to> +# 2013 by Donald Stufft <donald@stufft.io> +# 2013 by Alex Gaynor <alex.gaynor@gmail.com> +# 2013 by Greg Price <price@mit.edu> +# +# To the extent possible under law, the author(s) have dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty. +# +# You should have received a copy of the CC0 Public Domain Dedication along +# with this software. If not, see +# <http://creativecommons.org/publicdomain/zero/1.0/>. +# +# Downloaded from https://raw.githubusercontent.com/pyca/ed25519/main/ed25519.py +# on April 1, 2024. + +""" +NB: This code is not safe for use with secret keys or secret data. +The only safe use of this code is for verifying signatures on public messages. + +Functions for computing the public key of a secret key and for signing +a message are included, namely publickey_unsafe and signature_unsafe, +for testing purposes only. + +The root of the problem is that Python's long-integer arithmetic is +not designed for use in cryptography. Specifically, it may take more +or less time to execute an operation depending on the values of the +inputs, and its memory access patterns may also depend on the inputs. +This opens it to timing and cache side-channel attacks which can +disclose data to an attacker. We rely on Python's long-integer +arithmetic, so we cannot handle secrets without risking their disclosure. +""" + +import hashlib + + +__version__ = "1.0.dev0" + + +b = 256 +q = 2**255 - 19 +l = 2**252 + 27742317777372353535851937790883648493 + + +def H(m): + return hashlib.sha512(m).digest() + + +def pow2(x, p): + """== pow(x, 2**p, q)""" + while p > 0: + x = x * x % q + p -= 1 + return x + + +def inv(z): + r"""$= z^{-1} \mod q$, for z != 0""" + # Adapted from curve25519_athlon.c in djb's Curve25519. + z2 = z * z % q # 2 + z9 = pow2(z2, 2) * z % q # 9 + z11 = z9 * z2 % q # 11 + z2_5_0 = (z11 * z11) % q * z9 % q # 31 == 2^5 - 2^0 + z2_10_0 = pow2(z2_5_0, 5) * z2_5_0 % q # 2^10 - 2^0 + z2_20_0 = pow2(z2_10_0, 10) * z2_10_0 % q # ... + z2_40_0 = pow2(z2_20_0, 20) * z2_20_0 % q + z2_50_0 = pow2(z2_40_0, 10) * z2_10_0 % q + z2_100_0 = pow2(z2_50_0, 50) * z2_50_0 % q + z2_200_0 = pow2(z2_100_0, 100) * z2_100_0 % q + z2_250_0 = pow2(z2_200_0, 50) * z2_50_0 % q # 2^250 - 2^0 + return pow2(z2_250_0, 5) * z11 % q # 2^255 - 2^5 + 11 = q - 2 + + +d = -121665 * inv(121666) % q +I = pow(2, (q - 1) // 4, q) + + +def xrecover(y): + xx = (y * y - 1) * inv(d * y * y + 1) + x = pow(xx, (q + 3) // 8, q) + + if (x * x - xx) % q != 0: + x = (x * I) % q + + if x % 2 != 0: + x = q - x + + return x + + +By = 4 * inv(5) +Bx = xrecover(By) +B = (Bx % q, By % q, 1, (Bx * By) % q) +ident = (0, 1, 1, 0) + + +def edwards_add(P, Q): + # This is formula sequence 'addition-add-2008-hwcd-3' from + # http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + (x1, y1, z1, t1) = P + (x2, y2, z2, t2) = Q + + a = (y1 - x1) * (y2 - x2) % q + b = (y1 + x1) * (y2 + x2) % q + c = t1 * 2 * d * t2 % q + dd = z1 * 2 * z2 % q + e = b - a + f = dd - c + g = dd + c + h = b + a + x3 = e * f + y3 = g * h + t3 = e * h + z3 = f * g + + return (x3 % q, y3 % q, z3 % q, t3 % q) + + +def edwards_double(P): + # This is formula sequence 'dbl-2008-hwcd' from + # http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + (x1, y1, z1, t1) = P + + a = x1 * x1 % q + b = y1 * y1 % q + c = 2 * z1 * z1 % q + # dd = -a + e = ((x1 + y1) * (x1 + y1) - a - b) % q + g = -a + b # dd + b + f = g - c + h = -a - b # dd - b + x3 = e * f + y3 = g * h + t3 = e * h + z3 = f * g + + return (x3 % q, y3 % q, z3 % q, t3 % q) + + +def scalarmult(P, e): + if e == 0: + return ident + Q = scalarmult(P, e // 2) + Q = edwards_double(Q) + if e & 1: + Q = edwards_add(Q, P) + return Q + + +# Bpow[i] == scalarmult(B, 2**i) +Bpow = [] + + +def make_Bpow(): + P = B + for i in range(253): + Bpow.append(P) + P = edwards_double(P) + + +make_Bpow() + + +def scalarmult_B(e): + """ + Implements scalarmult(B, e) more efficiently. + """ + # scalarmult(B, l) is the identity + e = e % l + P = ident + for i in range(253): + if e & 1: + P = edwards_add(P, Bpow[i]) + e = e // 2 + assert e == 0, e + return P + + +def encodeint(y): + bits = [(y >> i) & 1 for i in range(b)] + return bytes( + [sum([bits[i * 8 + j] << j for j in range(8)]) for i in range(b // 8)] + ) + + +def encodepoint(P): + (x, y, z, t) = P + zi = inv(z) + x = (x * zi) % q + y = (y * zi) % q + bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1] + return bytes( + [sum([bits[i * 8 + j] << j for j in range(8)]) for i in range(b // 8)] + ) + + +def bit(h, i): + return (h[i // 8] >> (i % 8)) & 1 + + +def publickey_unsafe(sk): + """ + Not safe to use with secret keys or secret data. + + See module docstring. This function should be used for testing only. + """ + h = H(sk) + a = 2 ** (b - 2) + sum(2**i * bit(h, i) for i in range(3, b - 2)) + A = scalarmult_B(a) + return encodepoint(A) + + +def Hint(m): + h = H(m) + return sum(2**i * bit(h, i) for i in range(2 * b)) + + +def signature_unsafe(m, sk, pk): + """ + Not safe to use with secret keys or secret data. + + See module docstring. This function should be used for testing only. + """ + h = H(sk) + a = 2 ** (b - 2) + sum(2**i * bit(h, i) for i in range(3, b - 2)) + r = Hint(bytes([h[j] for j in range(b // 8, b // 4)]) + m) + R = scalarmult_B(r) + S = (r + Hint(encodepoint(R) + pk + m) * a) % l + return encodepoint(R) + encodeint(S) + + +def isoncurve(P): + (x, y, z, t) = P + return ( + z % q != 0 + and x * y % q == z * t % q + and (y * y - x * x - z * z - d * t * t) % q == 0 + ) + + +def decodeint(s): + return sum(2**i * bit(s, i) for i in range(0, b)) + + +def decodepoint(s): + y = sum(2**i * bit(s, i) for i in range(0, b - 1)) + x = xrecover(y) + if x & 1 != bit(s, b - 1): + x = q - x + P = (x, y, 1, (x * y) % q) + if not isoncurve(P): + raise ValueError("decoding point that is not on curve") + return P + + +class SignatureMismatch(Exception): + pass + + +def checkvalid(s, m, pk): + """ + Not safe to use when any argument is secret. + + See module docstring. This function should be used only for + verifying public signatures of public messages. + """ + if len(s) != b // 4: + raise ValueError("signature length is wrong") + + if len(pk) != b // 8: + raise ValueError("public-key length is wrong") + + R = decodepoint(s[: b // 8]) + A = decodepoint(pk) + S = decodeint(s[b // 8 : b // 4]) + h = Hint(encodepoint(R) + pk + m) + + (x1, y1, z1, t1) = P = scalarmult_B(S) + (x2, y2, z2, t2) = Q = edwards_add(R, scalarmult(A, h)) + + if ( + not isoncurve(P) + or not isoncurve(Q) + or (x1 * z2 - x2 * z1) % q != 0 + or (y1 * z2 - y2 * z1) % q != 0 + ): + raise SignatureMismatch("signature does not pass verification") diff --git a/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js b/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js index 5819357e29..7be02e34ff 100644 --- a/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js +++ b/testing/web-platform/tests/fledge/tentative/resources/fledge-util.sub.js @@ -177,7 +177,7 @@ async function waitForObservedRequestsIgnoreDebugOnlyReports( function createBiddingScriptURL(params = {}) { let origin = params.origin ? params.origin : new URL(BASE_URL).origin; let url = new URL(`${origin}${RESOURCE_PATH}bidding-logic.sub.py`); - // These checks use "==" to ignore null and not provided arguments, while + // These checks use "!=" to ignore null and not provided arguments, while // treating '' as a valid argument. if (params.generateBid != null) url.searchParams.append('generateBid', params.generateBid); @@ -213,7 +213,7 @@ function createDecisionScriptURL(uuid, params = {}) { let origin = params.origin ? params.origin : new URL(BASE_URL).origin; let url = new URL(`${origin}${RESOURCE_PATH}decision-logic.sub.py`); url.searchParams.append('uuid', uuid); - // These checks use "==" to ignore null and not provided arguments, while + // These checks use "!=" to ignore null and not provided arguments, while // treating '' as a valid argument. if (params.scoreAd != null) url.searchParams.append('scoreAd', params.scoreAd); @@ -230,8 +230,8 @@ function createDecisionScriptURL(uuid, params = {}) { // be last. "signalsParams" also has no effect, but is used by // trusted-scoring-signals.py to affect the response. function createRenderURL(uuid, script, signalsParams, origin) { - // These checks use "==" to ignore null and not provided arguments, while - // treating '' as a valid argument. + // These checks use "==" and "!=" to ignore null and not provided + // arguments, while treating '' as a valid argument. if (origin == null) origin = new URL(BASE_URL).origin; let url = new URL(`${origin}${RESOURCE_PATH}fenced-frame.sub.py`); @@ -260,6 +260,15 @@ function createInterestGroupForOrigin(uuid, origin, }; } +// Waits for the join command to complete. Adds cleanup command to `test` to +// leave the interest group when the test completes. +async function joinInterestGroupWithoutDefaults(test, interestGroup, + durationSeconds = 60) { + await navigator.joinAdInterestGroup(interestGroup, durationSeconds); + test.add_cleanup( + async () => { await navigator.leaveAdInterestGroup(interestGroup); }); +} + // Joins an interest group that, by default, is owned by the current frame's // origin, is named DEFAULT_INTEREST_GROUP_NAME, has a bidding script that // issues a bid of 9 with a renderURL of "https://not.checked.test/${uuid}", @@ -271,12 +280,33 @@ function createInterestGroupForOrigin(uuid, origin, // interest group. async function joinInterestGroup(test, uuid, interestGroupOverrides = {}, durationSeconds = 60) { - let interestGroup = createInterestGroupForOrigin(uuid, window.location.origin, - interestGroupOverrides); - - await navigator.joinAdInterestGroup(interestGroup, durationSeconds); - test.add_cleanup( - async () => { await navigator.leaveAdInterestGroup(interestGroup) }); + await joinInterestGroupWithoutDefaults( + test, createInterestGroupForOrigin( + uuid, window.location.origin, interestGroupOverrides), + durationSeconds); +} + +// Joins a negative interest group with the specified owner, name, and +// additionalBidKey. Because these are the only valid fields for a negative +// interest groups, this function doesn't expose an 'overrides' parameter. +// Adds cleanup command to `test` to leave the interest group when the test +// completes. +async function joinNegativeInterestGroup( + test, owner, name, additionalBidKey) { + let interestGroup = { + owner: owner, + name: name, + additionalBidKey: additionalBidKey + }; + if (owner !== window.location.origin) { + let iframe = await createIframe(test, owner, 'join-ad-interest-group'); + await runInFrame( + test, iframe, + `await joinInterestGroupWithoutDefaults(` + + `test_instance, ${JSON.stringify(interestGroup)})`); + } else { + await joinInterestGroupWithoutDefaults(test_instance, interestGroup); + } } // Similar to joinInterestGroup, but leaves the interest group instead. @@ -487,6 +517,17 @@ async function runReportTest(test, uuid, codeToInsert, expectedReportURLs, await waitForObservedRequests(uuid, expectedReportURLs); } +// Helper function for running a standard test of the additional bid and +// negative targeting features. This helper verifies that the auction produces a +// winner. It takes the following arguments: +// - test/uuid: the test object and uuid from the test case (see generateUuid) +// - buyers: array of strings, each a domain for a buyer participating in this +// auction +// - actionNonce: string, the auction nonce for this auction, typically +// retrieved from a prior call to navigator.createAuctionNonce +// - highestScoringOtherBid: the amount of the second-highest bid, +// or zero if there's no second-highest bid +// - winningAdditionalBidId: the label of the winning bid async function runAdditionalBidTest(test, uuid, buyers, auctionNonce, additionalBidsPromise, highestScoringOtherBid, @@ -516,7 +557,7 @@ async function runInFrame(test, child_window, script, param) { let promise = new Promise(function(resolve, reject) { function WaitForMessage(event) { - if (event.data.messageUuid != messageUuid) + if (event.data.messageUuid !== messageUuid) return; receivedResponse = event.data; if (event.data.result === 'success') { @@ -548,7 +589,7 @@ async function createFrame(test, origin, is_iframe = true, permissions = null) { `${origin}${RESOURCE_PATH}subordinate-frame.sub.html?uuid=${frameUuid}`; let promise = new Promise(function(resolve, reject) { function WaitForMessage(event) { - if (event.data.messageUuid != frameUuid) + if (event.data.messageUuid !== frameUuid) return; if (event.data.result === 'load complete') { resolve(); @@ -662,79 +703,130 @@ function directFromSellerSignalsValidatorCode(uuid, expectedSellerSignals, return { // Seller worklets scoreAd: - `if (directFromSellerSignals === null || + `if (directFromSellerSignals == null || directFromSellerSignals.sellerSignals !== ${expectedSellerSignals} || directFromSellerSignals.auctionSignals !== ${expectedAuctionSignals} || - Object.keys(directFromSellerSignals).length != 2) { + Object.keys(directFromSellerSignals).length !== 2) { throw 'Failed to get expected directFromSellerSignals in scoreAd(): ' + JSON.stringify(directFromSellerSignals); }`, reportResultSuccessCondition: - `directFromSellerSignals !== null && + `directFromSellerSignals != null && directFromSellerSignals.sellerSignals === ${expectedSellerSignals} && directFromSellerSignals.auctionSignals === ${expectedAuctionSignals} && - Object.keys(directFromSellerSignals).length == 2`, + Object.keys(directFromSellerSignals).length === 2`, reportResult: `sendReportTo("${createSellerReportURL(uuid)}");`, // Bidder worklets generateBid: - `if (directFromSellerSignals === null || + `if (directFromSellerSignals == null || directFromSellerSignals.perBuyerSignals !== ${expectedPerBuyerSignals} || directFromSellerSignals.auctionSignals !== ${expectedAuctionSignals} || - Object.keys(directFromSellerSignals).length != 2) { + Object.keys(directFromSellerSignals).length !== 2) { throw 'Failed to get expected directFromSellerSignals in generateBid(): ' + JSON.stringify(directFromSellerSignals); }`, reportWinSuccessCondition: - `directFromSellerSignals !== null && + `directFromSellerSignals != null && directFromSellerSignals.perBuyerSignals === ${expectedPerBuyerSignals} && directFromSellerSignals.auctionSignals === ${expectedAuctionSignals} && - Object.keys(directFromSellerSignals).length == 2`, + Object.keys(directFromSellerSignals).length === 2`, reportWin: `sendReportTo("${createBidderReportURL(uuid)}");`, }; } -// Creates an additional bid with the given parameters. This additional bid -// specifies a biddingLogicURL that provides an implementation of -// reportAdditionalBidWin that triggers a sendReportTo() to the bidder report -// URL of the winning additional bid. Additional bids are described in more -// detail at -// https://github.com/WICG/turtledove/blob/main/FLEDGE.md#6-additional-bids. -function createAdditionalBid(uuid, auctionNonce, seller, buyer, interestGroupName, bidAmount, - additionalBidOverrides = {}) { - return { - interestGroup: { - name: interestGroupName, - biddingLogicURL: createBiddingScriptURL( - { - origin: buyer, - reportAdditionalBidWin: `sendReportTo("${createBidderReportURL(uuid, interestGroupName)}");` - }), - owner: buyer - }, - bid: { - ad: ['metadata'], - bid: bidAmount, - render: createRenderURL(uuid) - }, - auctionNonce: auctionNonce, - seller: seller, - ...additionalBidOverrides +let additionalBidHelper = function() { + // Creates an additional bid with the given parameters. This additional bid + // specifies a biddingLogicURL that provides an implementation of + // reportAdditionalBidWin that triggers a sendReportTo() to the bidder report + // URL of the winning additional bid. Additional bids are described in more + // detail at + // https://github.com/WICG/turtledove/blob/main/FLEDGE.md#6-additional-bids. + function createAdditionalBid(uuid, auctionNonce, seller, buyer, interestGroupName, bidAmount, + additionalBidOverrides = {}) { + return { + interestGroup: { + name: interestGroupName, + biddingLogicURL: createBiddingScriptURL( + { + origin: buyer, + reportAdditionalBidWin: `sendReportTo("${createBidderReportURL(uuid, interestGroupName)}");` + }), + owner: buyer + }, + bid: { + ad: ['metadata'], + bid: bidAmount, + render: createRenderURL(uuid) + }, + auctionNonce: auctionNonce, + seller: seller, + ...additionalBidOverrides + }; } -} -// Fetch some number of additional bid from a seller and verify that the -// 'Ad-Auction-Additional-Bid' header is not visible in this JavaScript context. -// The `additionalBids` parameter is a list of additional bids. -async function fetchAdditionalBids(seller, additionalBids) { - const url = new URL(`${seller}${RESOURCE_PATH}additional-bids.py`); - url.searchParams.append('additionalBids', JSON.stringify(additionalBids)); - const response = await fetch(url.href, {adAuctionHeaders: true}); + // Gets the testMetadata for an additional bid, initializing it if needed. + function getAndMaybeInitializeTestMetadata(additionalBid) { + if (additionalBid.testMetadata === undefined) { + additionalBid.testMetadata = {}; + } + return additionalBid.testMetadata; + } - assert_equals(response.status, 200, 'Failed to fetch additional bid: ' + await response.text()); - assert_false( - response.headers.has('Ad-Auction-Additional-Bid'), - 'Header "Ad-Auction-Additional-Bid" should not be available in JavaScript context.'); -} + // Tells the additional bid endpoint to correctly sign the additional bid with + // the given secret keys before returning that as a signed additional bid. + function signWithSecretKeys(additionalBid, secretKeys) { + getAndMaybeInitializeTestMetadata(additionalBid). + secretKeysForValidSignatures = secretKeys; + } + + // Tells the additional bid endpoint to incorrectly sign the additional bid with + // the given secret keys before returning that as a signed additional bid. This + // is used for testing the behavior when the auction encounters an invalid + // signature. + function incorrectlySignWithSecretKeys(additionalBid, secretKeys) { + getAndMaybeInitializeTestMetadata(additionalBid). + secretKeysForInvalidSignatures = secretKeys; + } + + // Adds a single negative interest group to an additional bid, as described at: + // https://github.com/WICG/turtledove/blob/main/FLEDGE.md#622-how-additional-bids-specify-their-negative-interest-groups + function addNegativeInterestGroup(additionalBid, negativeInterestGroup) { + additionalBid["negativeInterestGroup"] = negativeInterestGroup; + } + + // Adds multiple negative interest groups to an additional bid, as described at: + // https://github.com/WICG/turtledove/blob/main/FLEDGE.md#622-how-additional-bids-specify-their-negative-interest-groups + function addNegativeInterestGroups(additionalBid, negativeInterestGroups, + joiningOrigin) { + additionalBid["negativeInterestGroups"] = { + joiningOrigin: joiningOrigin, + interestGroupNames: negativeInterestGroups + }; + } + + // Fetch some number of additional bid from a seller and verify that the + // 'Ad-Auction-Additional-Bid' header is not visible in this JavaScript context. + // The `additionalBids` parameter is a list of additional bids. + async function fetchAdditionalBids(seller, additionalBids) { + const url = new URL(`${seller}${RESOURCE_PATH}additional-bids.py`); + url.searchParams.append('additionalBids', JSON.stringify(additionalBids)); + const response = await fetch(url.href, {adAuctionHeaders: true}); + + assert_equals(response.status, 200, 'Failed to fetch additional bid: ' + await response.text()); + assert_false( + response.headers.has('Ad-Auction-Additional-Bid'), + 'Header "Ad-Auction-Additional-Bid" should not be available in JavaScript context.'); + } + + return { + createAdditionalBid: createAdditionalBid, + signWithSecretKeys: signWithSecretKeys, + incorrectlySignWithSecretKeys: incorrectlySignWithSecretKeys, + addNegativeInterestGroup: addNegativeInterestGroup, + addNegativeInterestGroups: addNegativeInterestGroups, + fetchAdditionalBids: fetchAdditionalBids + }; +}(); diff --git a/testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py b/testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py index 45bede2c45..f9ca9031f1 100644 --- a/testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py +++ b/testing/web-platform/tests/fledge/tentative/resources/trusted-bidding-signals.py @@ -110,6 +110,8 @@ def main(request, response): value = request.GET.first(b"slotSize", b"not-found").decode("ASCII") elif key == "allSlotsRequestedSizes": value = request.GET.first(b"allSlotsRequestedSizes", b"not-found").decode("ASCII") + elif key == "url": + value = request.url responseBody["keys"][key] = value if "data-version" in interestGroupNames: diff --git a/testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py b/testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py index eccef5e762..ce53e76295 100644 --- a/testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py +++ b/testing/web-platform/tests/fledge/tentative/resources/trusted-scoring-signals.py @@ -122,6 +122,8 @@ def main(request, response): value = request.GET.first(b"hostname", b"not-found").decode("ASCII") elif signalsParam == "headers": value = fledge_http_server_util.headers_to_ascii(request.headers) + elif signalsParam == "url": + value = request.url if addValue: if urlList["type"] not in responseBody: responseBody[urlList["type"]] = {} diff --git a/testing/web-platform/tests/fledge/tentative/resources/worklet-helpers.js b/testing/web-platform/tests/fledge/tentative/resources/worklet-helpers.js index 2147a026ae..0bac1b99a9 100644 --- a/testing/web-platform/tests/fledge/tentative/resources/worklet-helpers.js +++ b/testing/web-platform/tests/fledge/tentative/resources/worklet-helpers.js @@ -11,10 +11,10 @@ function deepEquals(a, b) { return a === b; let aKeys = Object.keys(a); - if (aKeys.length != Object.keys(b).length) + if (aKeys.length !== Object.keys(b).length) return false; for (let key of aKeys) { - if (a.hasOwnProperty(key) != b.hasOwnProperty(key) || + if (a.hasOwnProperty(key) !== b.hasOwnProperty(key) || !deepEquals(a[key], b[key])) { return false; } diff --git a/testing/web-platform/tests/fledge/tentative/tie.https.window.js b/testing/web-platform/tests/fledge/tentative/tie.https.window.js index 48d6e95e5c..c87d10f201 100644 --- a/testing/web-platform/tests/fledge/tentative/tie.https.window.js +++ b/testing/web-platform/tests/fledge/tentative/tie.https.window.js @@ -98,7 +98,7 @@ promise_test(async test => { auctionConfigOverrides.decisionLogicURL = createDecisionScriptURL( uuid, - {scoreAd: `if (browserSignals.renderURL == "${winningAdURL}") + {scoreAd: `if (browserSignals.renderURL === "${winningAdURL}") return 0;`}); // Add an abort controller, so can cancel extra auctions. diff --git a/testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js b/testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js index 9799af6ac1..d0b9a82086 100644 --- a/testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js +++ b/testing/web-platform/tests/fledge/tentative/trusted-bidding-signals.https.window.js @@ -16,7 +16,8 @@ // META: variant=?51-55 // META: variant=?56-60 // META: variant=?61-65 -// META: variant=?66-last +// META: variant=?66-70 +// META: variant=?71-last "use strict"; @@ -785,3 +786,156 @@ subsetTest(promise_test, async test => { auctionConfigOverrides, uuid); }, 'all-slots-requested-sizes trustedBiddingSignalsSlotSizeMode in a component auction'); + +///////////////////////////////////////////////////////////////////////////// +// maxTrustedBiddingSignalsURLLength tests +///////////////////////////////////////////////////////////////////////////// + +// Trusted bidding signals can be retrieved when `maxTrustedBiddingSignalsURLLength` is set to 0, +// which means infinite length limit. +// In the following three tests, the generated request URL contains approximately 166 characters. +// The target of the tests is primarily to make sure all the signals are fetched with the full URL. +subsetTest(promise_test, async test => { + const name = 'group'; + await runTrustedBiddingSignalsTest( + test, + // Check the URL length is within an approximate range to ensure the URL is not truncated. + ` trustedBiddingSignals["interest-group-names"] === '["${name}"]' && + trustedBiddingSignals["url"].length > 150 && + trustedBiddingSignals["url"].length < 180 `, + { + name: name, + trustedBiddingSignalsKeys: ['interest-group-names', 'url'], + trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL, + maxTrustedBiddingSignalsURLLength: 0 + }); +}, 'Trusted bidding signals request works with a URL length limit set to 0.'); + +// Trusted bidding signals can be retrieved when `maxTrustedBiddingSignalsURLLength` is set to +// a non-zero value smaller than the length of the request URL. It also tests that multiple +// bidding keys from the same interest group will not be separated even the full URL length is +// larger than the limit. +subsetTest(promise_test, async test => { + const name = 'group'; + await runTrustedBiddingSignalsTest( + test, + ` trustedBiddingSignals["interest-group-names"] === '["${name}"]' && + trustedBiddingSignals["url"].length > 150 && + trustedBiddingSignals["url"].length < 180 `, + { + name: name, + trustedBiddingSignalsKeys: ['interest-group-names', 'url'], + trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL, + maxTrustedBiddingSignalsURLLength: 1 + }); +}, 'Trusted bidding signals request works with a URL length limit smaller than the URL length.'); + +// Trusted bidding signals can be retrieved when `maxTrustedBiddingSignalsURLLength` is set to +// a value larger than the length of the request URL. +subsetTest(promise_test, async test => { + const name = 'group'; + await runTrustedBiddingSignalsTest( + test, + ` trustedBiddingSignals["interest-group-names"] === '["${name}"]' && + trustedBiddingSignals["url"].length < 180 `, + { + name: name, + trustedBiddingSignalsKeys: ['interest-group-names', 'url'], + trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL, + maxTrustedBiddingSignalsURLLength: 1000 + }); +}, 'Trusted bidding signals request works with a URL length limit larger than the URL length.'); + +// Test whether an oversized trusted bidding signals request URL, generated from two interest +// groups, will be split into two parts when `maxTrustedBiddingSignalsURLLength` is set to a +// value larger than a single URL length and smaller than the combined URL length. A request +// URL from a single interest group contains about 188 characters, while a request URL from +// two interest groups contains about 216 characters. Note that this test can only verifies +// the fetch status of the winner's trusted bidding signal, which is the second interest +// group. We consider the request to be split if the URL length check passes for the second +// interest group. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const name1 = 'extraordinarilyLongNameGroup1'; + const name2 = 'extraordinarilyLongNameGroup2'; + + await Promise.all( + [ joinInterestGroup( + test, uuid, + { + name: name1, + trustedBiddingSignalsKeys: ['interest-group-names', 'url'], + trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL, + maxTrustedBiddingSignalsURLLength: 200, + biddingLogicURL: createBiddingScriptURL( + { + // Return 0 as bid to force the second interest group to win. This interest group + // is considered as fetching trusted bidding signals by itself if the winner's + // URL length passes the limit check. + generateBid: + `return { bid: 0, render: interestGroup.ads[0].renderURL };` + }) + }), + joinInterestGroup( + test, uuid, + { + name: name2, + trustedBiddingSignalsKeys: ['interest-group-names', 'url'], + trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL, + maxTrustedBiddingSignalsURLLength: 200, + biddingLogicURL: createBiddingScriptURL( + { + generateBid: + `if (trustedBiddingSignals["interest-group-names"] !== '["${name2}"]' || + trustedBiddingSignals["url"].length > 200) { + throw "unexpected trustedBiddingSignals"; + } + return { bid: 10, render: interestGroup.ads[0].renderURL };`}) + }) + ] + ); + runBasicFledgeTestExpectingWinner(test, uuid); +}, 'Trusted bidding signals splits the request if the combined URL length exceeds the limit of regular value.'); + +// Test whether an oversized trusted bidding signals request URL, generated from two interest +// groups, will be split into two parts when `maxTrustedBiddingSignalsURLLength` is set to a +// value smaller than a single URL length. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const name1 = 'extraordinaryLongNameGroup1'; + const name2 = 'extraordinaryLongNameGroup2'; + + await Promise.all( + [ joinInterestGroup( + test, uuid, + { + name: name1, + trustedBiddingSignalsKeys: ['interest-group-names', 'url'], + trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL, + maxTrustedBiddingSignalsURLLength: 1, + biddingLogicURL: createBiddingScriptURL( + { + generateBid: + `return { bid: 0, render: interestGroup.ads[0].renderURL };` + }) + }), + joinInterestGroup( + test, uuid, + { + name: name2, + trustedBiddingSignalsKeys: ['interest-group-names', 'url'], + trustedBiddingSignalsURL: TRUSTED_BIDDING_SIGNALS_URL, + maxTrustedBiddingSignalsURLLength: 1, + biddingLogicURL: createBiddingScriptURL( + { + generateBid: + `if (trustedBiddingSignals["interest-group-names"] !== '["${name2}"]' || + trustedBiddingSignals["url"].length > 200) { + throw "unexpected trustedBiddingSignals"; + } + return { bid: 10, render: interestGroup.ads[0].renderURL };`}) + }) + ] + ); + runBasicFledgeTestExpectingWinner(test, uuid); +}, 'Trusted bidding signals splits the request if the combined URL length exceeds the limit of small value.');
\ No newline at end of file diff --git a/testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js b/testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js index 4de5cfc0f3..105b09fb3b 100644 --- a/testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js +++ b/testing/web-platform/tests/fledge/tentative/trusted-scoring-signals.https.window.js @@ -11,7 +11,8 @@ // META: variant=?26-30 // META: variant=?31-35 // META: variant=?36-40 -// META: variant=?41-last +// META: variant=?41-45 +// META: variant=?45-last "use strict"; @@ -510,3 +511,151 @@ subsetTest(promise_test, async test => { }) }); }, 'Component ads trusted scoring signals.'); + +///////////////////////////////////////////////////////////////////////////// +// maxTrustedBiddingSignalsURLLength tests +///////////////////////////////////////////////////////////////////////////// + +// Trusted scoring signals can be retrieved when `maxTrustedScoringSignalsURLLength` is set to 0. +// In the following three tests, the generated request URL contains approximately 294 characters. +// The target of the tests is primarily to make sure the signals were fetched with the full URL. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const renderURL = createRenderURL(uuid, /*script=*/null, 'url'); + const interestGroupOverrides = { ads: [{ renderURL: renderURL }] }; + const auctionConfigOverrides = { + trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL, + maxTrustedScoringSignalsURLLength: 0, + decisionLogicURL: + createDecisionScriptURL(uuid, { + // Check the URL length is within an approximate range to ensure the URL is not truncated. + scoreAd: + `if (trustedScoringSignals.renderURL["${renderURL}"].length < 280 || + trustedScoringSignals.renderURL["${renderURL}"].length > 300) + throw "error";` + }) + }; + + await joinGroupAndRunBasicFledgeTestExpectingWinner( + test, + { + uuid: uuid, + interestGroupOverrides: interestGroupOverrides, + auctionConfigOverrides: auctionConfigOverrides + }); +}, 'Trusted scoring signals request works with a URL length limit set to 0.'); + +// Trusted scoring signals can be retrieved when `maxTrustedScoringSignalsURLLength` is set to +// a non-zero value smaller than the length of the request URL. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const renderURL = createRenderURL(uuid, /*script=*/null, 'url'); + const interestGroupOverrides = { ads: [{ renderURL: renderURL }] }; + const auctionConfigOverrides = { + trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL, + maxTrustedScoringSignalsURLLength: 1, + decisionLogicURL: + createDecisionScriptURL(uuid, { + // Check the URL length is within an approximate range to ensure the URL is not truncated. + scoreAd: + `if (trustedScoringSignals.renderURL["${renderURL}"].length < 280 || + trustedScoringSignals.renderURL["${renderURL}"].length > 300) + throw "error";` + }) + }; + + await joinGroupAndRunBasicFledgeTestExpectingWinner( + test, + { + uuid: uuid, + interestGroupOverrides: interestGroupOverrides, + auctionConfigOverrides: auctionConfigOverrides + }); +}, 'Trusted scoring signals request works with a URL length limit smaller than the URL length.'); + +// Trusted scoring signals can be retrieved when `maxTrustedScoringSignalsURLLength` is set to +// a value larger than the length of the request URL. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const renderURL = createRenderURL(uuid, /*script=*/null, 'url'); + const interestGroupOverrides = { ads: [{ renderURL: renderURL }] }; + const auctionConfigOverrides = { + trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL, + maxTrustedScoringSignalsURLLength: 1000, + decisionLogicURL: + createDecisionScriptURL(uuid, { + scoreAd: `if (trustedScoringSignals.renderURL["${renderURL}"].length > 300) throw "error";` + }) + }; + + await joinGroupAndRunBasicFledgeTestExpectingWinner( + test, + { + uuid: uuid, + interestGroupOverrides: interestGroupOverrides, + auctionConfigOverrides: auctionConfigOverrides + }); +}, 'Trusted scoring signals request works with a URL length limit larger than the URL length.'); + +// Test whether an oversized trusted scoring signals request URL, generated from two interest +// groups, will be split into two parts when `maxTrustedScoringSignalsURLLength` is set to a +// value larger than a single URL length and smaller than the combined URL length. A request +// URL from a single interest group contains about 294 characters, while a request URL from +// two interest groups contains about 466 characters. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const renderURL1 = createRenderURL(uuid, /*script=*/null, 'url,group1'); + const renderURL2 = createRenderURL(uuid, /*script=*/null, 'url,group2'); + const auctionConfigOverrides = { + trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL, + maxTrustedScoringSignalsURLLength: 300, + decisionLogicURL: + createDecisionScriptURL(uuid, { + // This will make the auction reject `renderURL2`, and if `renderURL1` passes the URL + // length check, we consider `renderURL2` is fetched by itself in the trusted scoring + // signals request. + scoreAd: + `if (!trustedScoringSignals.renderURL.has("${renderURL1}") || + trustedScoringSignals.renderURL.has("${renderURL2}") || + trustedScoringSignals.renderURL["${renderURL1}"].length > 300) { + throw "error"; + }` + }) + }; + + await Promise.all( + [ joinInterestGroup(test, uuid, { name: 'group 1', ads: [{ renderURL: renderURL1 }] }), + joinInterestGroup(test, uuid, { name: 'group 2', ads: [{ renderURL: renderURL2 }] }) ] + ); + + runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides); +}, 'Trusted scoring signals splits the request if the combined URL length exceeds the limit of regular value.'); + +// Test whether an oversized trusted scoring signals request URL, generated from two interest +// groups, will be split into two parts when `maxTrustedScoringSignalsURLLength` is set to a +// value smaller than a single URL length. +subsetTest(promise_test, async test => { + const uuid = generateUuid(test); + const renderURL1 = createRenderURL(uuid, /*script=*/null, 'url,group1'); + const renderURL2 = createRenderURL(uuid, /*script=*/null, 'url,group2'); + const auctionConfigOverrides = { + trustedScoringSignalsURL: TRUSTED_SCORING_SIGNALS_URL, + maxTrustedScoringSignalsURLLength: 1, + decisionLogicURL: + createDecisionScriptURL(uuid, { + scoreAd: + `if (!trustedScoringSignals.renderURL.has("${renderURL1}") || + trustedScoringSignals.renderURL.has("${renderURL2}") || + trustedScoringSignals.renderURL["${renderURL1}"].length > 300) { + throw "error"; + }` + }) + }; + + await Promise.all( + [ joinInterestGroup(test, uuid, { name: 'group 1', ads: [{ renderURL: renderURL1 }] }), + joinInterestGroup(test, uuid, { name: 'group 2', ads: [{ renderURL: renderURL2 }] }) ] + ); + + runBasicFledgeTestExpectingWinner(test, uuid, auctionConfigOverrides); +}, 'Trusted scoring signals splits the request if the combined URL length exceeds the limit of small value.');
\ No newline at end of file diff --git a/testing/web-platform/tests/focus/ancestor-activeelement-after-child-lose-focus.html b/testing/web-platform/tests/focus/ancestor-activeelement-after-child-lose-focus.html new file mode 100644 index 0000000000..38f31d64ae --- /dev/null +++ b/testing/web-platform/tests/focus/ancestor-activeelement-after-child-lose-focus.html @@ -0,0 +1,46 @@ +<!doctype html> +<head> +<meta charset=utf-8> +<title>Ancestor's activeElement should be cleared when child loses focus</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +</head> +<body> +<input placeholder="input in top level"/> +<iframe srcdoc="<iframe srcdoc='<input>'></iframe>"></iframe> +<script> +const outerIFrame = document.querySelector('iframe'); +function runTest() { + test(() => { + assert_true(outerIFrame.contentDocument.activeElement === outerIFrame.contentDocument.body, + "Initially, the activeElement of the outer iframe should be <body>"); + + const innerIFrame = outerIFrame.contentDocument.querySelector("iframe"); + const inputInInner = innerIFrame.contentDocument.querySelector('input'); + + // Now we focus the input in the inner iframe + inputInInner.focus(); + // outerIframe is the ancestor of inner iframe, so the activeElement of + // it should be the inner iframe. + assert_true(outerIFrame.contentDocument.activeElement === innerIFrame, + "The activeElement of the outer iframe should be the inner iframe"); + + // Now we focus the input in the top level + document.querySelector("input").focus(); + // Since inner iframe lost its focus, the activeElement of outer iframe + // should be cleared as well, hence <body> should be focused. + assert_true(outerIFrame.contentDocument.activeElement === outerIFrame.contentDocument.body, + "The activeElement of the outer iframe should be reverted back to <body>"); + + assert_true(document.activeElement === document.querySelector("input"), + "The activeElement of the top-level document should be the input"); + }); +} + +window.onload = function() { + runTest(); +} +</script> +</body> diff --git a/testing/web-platform/tests/focus/cross-origin-ancestor-activeelement-after-child-lose-focus.sub.html b/testing/web-platform/tests/focus/cross-origin-ancestor-activeelement-after-child-lose-focus.sub.html new file mode 100644 index 0000000000..35844bc91f --- /dev/null +++ b/testing/web-platform/tests/focus/cross-origin-ancestor-activeelement-after-child-lose-focus.sub.html @@ -0,0 +1,67 @@ +<!doctype html> +<head> +<meta charset=utf-8> +<title>Ancestor's activeElement should be cleared when child loses focus</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +</head> +<body> +<input placeholder="input in top level"/> +<iframe></iframe> +<script> +const outerIFrame = document.querySelector('iframe'); +setup({ explicit_done: true }); +function runTest() { + test(() => { + assert_equals(outerIFrame.contentDocument.activeElement, outerIFrame.contentDocument.body, + "Initially, the activeElement of the outer iframe should be <body>"); + + const innerIFrame = outerIFrame.contentDocument.createElement("iframe"); + + window.onmessage = function() { + // Wait for a bit to let whatever the code that might change the focus to run + window.requestAnimationFrame(function() { + window.requestAnimationFrame(function() { + window.requestAnimationFrame(function() { + + // We receive an message when the innerIFrame is ready and its input is focused. + // outerIframe is the ancestor of inner iframe, so the activeElement of + // it should be the inner iframe. + assert_equals(outerIFrame.contentDocument.activeElement, innerIFrame, + "The activeElement of the outer iframe should be the inner iframe"); + + // Now we focus the input in the top level + document.querySelector("input").focus(); + + // Wait for a bit to let whatever the code that might change the focus to run + window.requestAnimationFrame(function() { + window.requestAnimationFrame(function() { + window.requestAnimationFrame(function() { + // Since inner iframe lost its focus, the activeElement of outer iframe + // should be cleared as well, hence <body> should be focused. + assert_equals(outerIFrame.contentDocument.activeElement, outerIFrame.contentDocument.body, + "The activeElement of the outer iframe should be reverted back to <body>"); + assert_equals(document.activeElement, document.querySelector("input"), + "The activeElement of the top-level document should the input"); + done(); + }); + }); + }); + }); + }); + }); + } + + // Opens the cross-origin inner iframe with a page that contains an input element. + innerIFrame.src = "http://{{domains[www1]}}:{{ports[http][0]}}/focus/support/cross-origin-ancestor-activeelement-after-child-lose-focus-helper.html"; + outerIFrame.contentDocument.body.appendChild(innerIFrame); + }); +} + +window.onload = function() { + runTest(); +} +</script> +</body> diff --git a/testing/web-platform/tests/focus/support/cross-origin-ancestor-activeelement-after-child-lose-focus-helper.html b/testing/web-platform/tests/focus/support/cross-origin-ancestor-activeelement-after-child-lose-focus-helper.html new file mode 100644 index 0000000000..83d39d5c70 --- /dev/null +++ b/testing/web-platform/tests/focus/support/cross-origin-ancestor-activeelement-after-child-lose-focus-helper.html @@ -0,0 +1,9 @@ +<html> +<body> + <input /> + <script> + document.querySelector("input").focus(); + window.parent.parent.postMessage("ready", '*'); + </script> +</body> +</html> diff --git a/testing/web-platform/tests/fs/script-tests/FileSystemWritableFileStream-write.js b/testing/web-platform/tests/fs/script-tests/FileSystemWritableFileStream-write.js index 246f420d0f..5918b45644 100644 --- a/testing/web-platform/tests/fs/script-tests/FileSystemWritableFileStream-write.js +++ b/testing/web-platform/tests/fs/script-tests/FileSystemWritableFileStream-write.js @@ -362,3 +362,13 @@ directory_test(async (t, root) => { const newStream = await handle.createWritable({mode: 'exclusive'}); await newStream.close(); }, 'an errored writable stream releases its lock'); + +directory_test(async (t, root) => { + const handle = await createFileWithContents(t, 'file.txt', 'contents', root); + const stream = await handle.createWritable({mode: 'exclusive'}); + + const writer = stream.getWriter(); + + await promise_rejects_js(t, TypeError, writer.write(null), 'write with null data'); + await promise_rejects_js(t, TypeError, writer.write("foo"), 'write with text data'); +}, 'an errored writable stream should reject the next write call'); diff --git a/testing/web-platform/tests/html-aam/roles.html b/testing/web-platform/tests/html-aam/roles.html index 7cfb2852c2..387acc260d 100644 --- a/testing/web-platform/tests/html-aam/roles.html +++ b/testing/web-platform/tests/html-aam/roles.html @@ -66,7 +66,7 @@ <!-- todo: figcaption --> <figure data-testname="el-figure" data-expectedrole="figure" class="ex"><img alt="x" src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=="><figcaption>x</figcaption></figure> <!-- footer -> ./roles-contextual.html --> -<form data-testname="el-form" data-expectedrole="form" class="ex"><input></form> +<form aria-label="form" data-testname="el-form" data-expectedrole="form" class="ex"><input></form> <!-- todo: form-associated custom element --> <!-- el-h1-h6 --> @@ -203,4 +203,4 @@ AriaUtils.verifyGenericRolesBySelector(".ex-generic"); </script> </body> -</html>
\ No newline at end of file +</html> diff --git a/testing/web-platform/tests/html-media-capture/WEB_FEATURES.yml b/testing/web-platform/tests/html-media-capture/WEB_FEATURES.yml new file mode 100644 index 0000000000..e794b88c3e --- /dev/null +++ b/testing/web-platform/tests/html-media-capture/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: html-media-capture + files: "**" diff --git a/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html b/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html index 5f3e95cce4..a3badfcb5e 100644 --- a/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html +++ b/testing/web-platform/tests/html/browsers/browsing-the-web/read-media/cross-origin-video.html @@ -22,8 +22,7 @@ promise_test(async () => { + 'VIDEO%26PartialContent'; let v = document.createElement("video"); - if (v.canPlayType("video/ogv") == "") - frame.src += "%26mp4"; + frame.src += "%26mp4"; document.body.appendChild(frame); await new Promise(resolve => frame.onload = resolve); diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_reload_referrer-1.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_reload_referrer-1.html deleted file mode 100644 index d083a2a1d3..0000000000 --- a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_reload_referrer-1.html +++ /dev/null @@ -1,16 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title> - Ensure referrer header persists after - history.pushState/replaceState/fragment navigation and reload - </title> - </head> - <body> - <noscript><p>Enable JavaScript and reload</p></noscript> - <div id="log"></div> - <script type="text/javascript"> - location.href = "history_reload_referrer-2.html?pipe=sub"; - </script> - </body> -</html> diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_reload_referrer-2.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_reload_referrer-2.html deleted file mode 100644 index d85517a15f..0000000000 --- a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_reload_referrer-2.html +++ /dev/null @@ -1,56 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title> - Ensure referrer header persists after - history.pushState/replaceState/fragment navigation and reload - </title> - </head> - <body> - <a id="fragment" href="#fragment">fragment</a> - <noscript><p>Enable JavaScript and reload</p></noscript> - <div id="log"></div> - <script type="text/javascript"> - const step = localStorage.getItem("history_reload_referrer_step") || "1"; - localStorage.setItem("history_reload_referrer_step", parseInt(step) + 1); - - var httpReferer = unescape("{{headers[referer]}}"); - var expectedReferrer = location.href.replace( - /\/[^\/]*$/, - "\/history_reload_referrer-1.html" - ); - - parent.test(function () { - parent.assert_equals(httpReferer, expectedReferrer); - }, `Step ${step}: Checking HTTP referrer (is "${httpReferer}")`); - parent.test(function () { - parent.assert_equals(document.referrer, expectedReferrer); - }, `Step ${step}: Checking document.referrer (is "${httpReferer}")`); - - switch (step) { - case "1": - history.pushState("", "", location); - location.reload(); - break; - - case "2": - history.replaceState("", "", location); - location.reload(); - break; - - case "3": - document.getElementById("fragment").click(); - location.reload(); - break; - - case "4": - localStorage.removeItem("history_reload_referrer_step"); - parent.done(); - break; - - default: - throw new Error(`Unexpected step "${step}"`); - } - </script> - </body> -</html> diff --git a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_reload_referrer.html b/testing/web-platform/tests/html/browsers/history/the-history-interface/history_reload_referrer.html deleted file mode 100644 index d11ee89702..0000000000 --- a/testing/web-platform/tests/html/browsers/history/the-history-interface/history_reload_referrer.html +++ /dev/null @@ -1,26 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title> - Ensure referrer header persists after - history.pushState/replaceState/fragment navigation and reload - </title> - <script type="text/javascript" src="/resources/testharness.js"></script> - <script - type="text/javascript" - src="/resources/testharnessreport.js" - ></script> - </head> - <body> - <noscript><p>Enable JavaScript and reload</p></noscript> - <div id="log"></div> - <script type="text/javascript"> - setup({ explicit_done: true }); - var iframe = document.createElement("iframe"); - window.onload = function () { - iframe.setAttribute("src", "history_reload_referrer-1.html"); - document.body.appendChild(iframe); - }; - </script> - </body> -</html> diff --git a/testing/web-platform/tests/html/browsers/sandboxing/window-open-blank-from-different-initiator.html b/testing/web-platform/tests/html/browsers/sandboxing/window-open-blank-from-different-initiator.html index 91817c3db4..3e227b456f 100644 --- a/testing/web-platform/tests/html/browsers/sandboxing/window-open-blank-from-different-initiator.html +++ b/testing/web-platform/tests/html/browsers/sandboxing/window-open-blank-from-different-initiator.html @@ -68,8 +68,7 @@ const runTest = (description, createNewPopup) => { // Open a new window and start loading from an unresponsive server. The frame // will be left with the initial empty document and a pending navigation. runTest("One pending navigation", async (test, popup_name) => { - const unresponsive_path = - "/fetch/api/resources/infinite-slow-response.py"; + const unresponsive_path = "/common/slow.py?delay=1000000"; return window.open(same_origin + unresponsive_path, popup_name); }); diff --git a/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.colormix.html b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.colormix.html new file mode 100644 index 0000000000..f0dff89ae9 --- /dev/null +++ b/testing/web-platform/tests/html/canvas/element/fill-and-stroke-styles/2d.gradient.colormix.html @@ -0,0 +1,33 @@ +<!DOCTYPE html> +<!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. --> +<title>Canvas test: 2d.gradient.colormix</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/html/canvas/resources/canvas-tests.js"></script> +<link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css"> +<body class="show_output"> + +<h1>2d.gradient.colormix</h1> +<p class="desc">color-mix works as CanvasGradient color input</p> + + +<p class="output">Actual output:</p> +<canvas id="c" class="output" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas> + +<ul id="d"></ul> +<script> +var t = async_test("color-mix works as CanvasGradient color input"); +_addTest(function(canvas, ctx) { + + var g = ctx.createLinearGradient(0, 0, 100, 0); + g.addColorStop(0, '#f00'); + g.addColorStop(1, 'color-mix(in srgb, #0f0, #00f)'); + ctx.fillStyle = g; + ctx.fillRect(0, 0, 100, 50); + _assertPixelApprox(canvas, 25,25, 212,81,61,255, 3); + _assertPixelApprox(canvas, 50,25, 167,106,88,255, 3); + _assertPixelApprox(canvas, 75,25, 113,120,109,255, 3); + +}); +</script> + diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html index 05984a47f8..8fc6377f83 100644 --- a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html +++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html @@ -77,63 +77,6 @@ _addTest(function(canvas, ctx) { {name: 'dropShadow', floodOpacity: [20]}); ctx.filter = new CanvasFilter( {name: 'dropShadow', floodOpacity: '30'}); - // dx - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: 10}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: -1}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: 0.5}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: null}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: true}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: false}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: []}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: [20]}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: '30'}); - // dy - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: 10}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: -1}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: 0.5}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: null}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: true}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: false}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: []}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: [20]}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: '30'}); - // floodOpacity - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: 10}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: -1}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: 0.5}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: null}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: true}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: false}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: []}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: [20]}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: '30'}); // stdDeviation ctx.filter = new CanvasFilter( {name: 'dropShadow', stdDeviation: 10}); diff --git a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.layers.dropShadow.exceptions.html b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.layers.dropShadow.exceptions.html index df5e0c7dc3..0c3b6a8ce9 100644 --- a/testing/web-platform/tests/html/canvas/element/filters/2d.filter.layers.dropShadow.exceptions.html +++ b/testing/web-platform/tests/html/canvas/element/filters/2d.filter.layers.dropShadow.exceptions.html @@ -77,63 +77,6 @@ _addTest(function(canvas, ctx) { {name: 'dropShadow', floodOpacity: [20]}}); ctx.endLayer(); ctx.beginLayer({filter: {name: 'dropShadow', floodOpacity: '30'}}); ctx.endLayer(); - // dx - ctx.beginLayer({filter: - {name: 'dropShadow', dx: 10}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: -1}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: 0.5}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: null}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: true}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: false}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: []}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: [20]}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: '30'}}); ctx.endLayer(); - // dy - ctx.beginLayer({filter: - {name: 'dropShadow', dy: 10}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: -1}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: 0.5}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: null}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: true}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: false}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: []}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: [20]}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: '30'}}); ctx.endLayer(); - // floodOpacity - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: 10}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: -1}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: 0.5}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: null}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: true}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: false}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: []}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: [20]}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: '30'}}); ctx.endLayer(); // stdDeviation ctx.beginLayer({filter: {name: 'dropShadow', stdDeviation: 10}}); ctx.endLayer(); diff --git a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html index 9283fd39b7..5d755cfc49 100644 --- a/testing/web-platform/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html +++ b/testing/web-platform/tests/html/canvas/element/layers/2d.layer.layer-rendering-state-reset-in-layer.html @@ -16,8 +16,9 @@ <ul id="d"></ul> <script> -var t = async_test("Tests that layers ignore the global context filter."); -_addTest(function(canvas, ctx) { +test(t => { + var canvas = document.getElementById('c'); + var ctx = canvas.getContext('2d'); ctx.globalAlpha = 0.5; ctx.globalCompositeOperation = 'xor'; @@ -51,6 +52,6 @@ _addTest(function(canvas, ctx) { _assertSame(ctx.shadowOffsetY, 20, "ctx.shadowOffsetY", "20"); _assertSame(ctx.shadowBlur, 30, "ctx.shadowBlur", "30"); -}); +}, "Tests that layers ignore the global context filter."); </script> diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js index 20953417cc..1a181a1cc1 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.image.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -21,6 +20,5 @@ promise_test(async t => { ctx.drawImage(bitmap, 0, 0); _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js index c6ab163b65..cb8f1413a2 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/compositing/2d.composite.globalAlpha.imagepattern.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -22,6 +21,5 @@ promise_test(async t => { ctx.globalAlpha = 0.01; ctx.fillRect(0, 0, 100, 50); _assertPixelApprox(canvas, 50,25, 2,253,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js index b2f860bc60..e91416b036 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.3arg.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2); _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2); _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js index f725acf011..a852f59e55 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.5arg.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -29,6 +28,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2); _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2); _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js index b2d6f7d860..ca5b6c223c 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.basic.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -22,6 +21,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2); _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2); _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js index 380d526f9b..aff6c21108 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destpos.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2); _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2); _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js index 76e3e8baaf..a3c14535c5 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.destsize.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -30,6 +29,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2); _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2); _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js index d3525435fa..0c060f7008 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcepos.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -21,6 +20,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 99,0, 0,255,0,255, 2); _assertPixelApprox(canvas, 0,49, 0,255,0,255, 2); _assertPixelApprox(canvas, 99,49, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js index aa15efb705..7d7f064b26 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.9arg.sourcesize.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 80,20, 0,255,0,255, 2); _assertPixelApprox(canvas, 20,30, 0,255,0,255, 2); _assertPixelApprox(canvas, 80,30, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js index 958a2431b6..6854a06d9f 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.alpha.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -20,6 +19,5 @@ promise_test(async t => { ctx.drawImage(bitmap, 0, 0); _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js index dc695b5459..1b2b0f83ed 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.broken.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -15,6 +14,5 @@ promise_test(async t => { const blob = await response.blob(); await promise_rejects_dom(t, 'InvalidStateError', createImageBitmap(blob), 'The source image could not be decoded.'); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js index fea2a7bac7..ce4231c2ac 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.clip.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -25,6 +24,5 @@ promise_test(async t => { ctx.clip(); ctx.drawImage(bitmap, 0, 0); _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js index 0b17673b68..e9d0d9c77a 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.composite.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -20,6 +19,5 @@ promise_test(async t => { ctx.drawImage(bitmap, 0, 0); _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js index 1efc17d04d..18ecb11aac 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.floatsource.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -16,6 +15,5 @@ promise_test(async t => { const bitmap = await createImageBitmap(blob); ctx.drawImage(bitmap, 10.1, 10.1, 0.1, 0.1, 0, 0, 100, 50); _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js index ce5cf5efad..36c4b27472 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedest.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 51,48, 0,255,0,255, 2); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, "Negative destination width/height represents the correct rectangle"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js index c82b9ba1be..f774deef61 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativedir.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 51,48, 0,255,0,255, 2); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, "Negative dimensions do not affect the direction of the image"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js index f92d15774d..804a09f699 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.negativesource.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 51,48, 0,255,0,255, 2); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, "Negative source width/height represents the correct rectangle"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js index a56f09e0b2..e9ca406fd2 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nonfinite.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -319,6 +318,5 @@ promise_test(async t => { ctx.drawImage(bitmap, 0, 0, 100, 50, 0, Infinity, 100, Infinity); ctx.drawImage(bitmap, 0, 0, 100, 50, 0, 0, Infinity, Infinity); _assertPixel(canvas, 50,25, 0,255,0,255); - t.done(); }, "drawImage() with Infinity/NaN is ignored"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js index 47a77834c0..5728d424d3 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.nowrap.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -21,6 +20,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 45,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 55,25, 0,255,0,255, 2); - t.done(); }, "Stretched images do not get pixels wrapping around the edges"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js index 3259ff0258..b18d021074 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.path.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -20,6 +19,5 @@ promise_test(async t => { ctx.drawImage(bitmap, 0, 0); ctx.fill(); _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js index 4a4cd07be9..1ba832155c 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.transform.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -20,6 +19,5 @@ promise_test(async t => { ctx.drawImage(bitmap, 0, 0); _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js index a80bfff562..61b70b0c47 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/drawing-images-to-the-canvas/2d.drawImage.zerosource.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -20,6 +19,5 @@ promise_test(async t => { ctx.drawImage(bitmap, 10, 10, 1, 0, 0, 0, 100, 50); ctx.drawImage(bitmap, 10, 10, 0, 0, 0, 0, 100, 50); _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2); - t.done(); }, "drawImage with zero-sized source rectangle draws nothing without exception"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js index 14cfcbc993..cc2dc301ca 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.image.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -24,6 +23,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js index 56a8a7d23c..382214a49e 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.basic.type.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -20,6 +19,5 @@ promise_test(async t => { var img = await createImageBitmap(blob); var pattern = ctx.createPattern(img, 'no-repeat'); _assert(pattern.thisImplementsCanvasPattern, "pattern.thisImplementsCanvasPattern"); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js index d9004fc028..ff2acc6851 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.crosscanvas.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -22,6 +21,5 @@ promise_test(async t => { ctx.fillRect(0, 0, 100, 50); _assertPixel(canvas, 50,25, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js index 9ac4e1605c..a093e61d25 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.basic.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -25,6 +24,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js index 6c4a1409b7..3b4365d6e7 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord1.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js index d2fdd86022..3314efdd3a 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord2.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -29,6 +28,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js index 584a5d6cfd..6120bd0549 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.coord3.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -29,6 +28,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js index 0b5fef95ce..b029f90a37 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.norepeat.outside.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -31,6 +30,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js index b303b2d813..b3e61d53e9 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.orientation.image.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -31,6 +30,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, "Image patterns do not get flipped when painted"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js index 37f4c04228..872acb1c20 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.basic.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -25,6 +24,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js index efbd1c5846..009b072b22 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord1.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -26,6 +25,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js index db707ecb0a..21e3349003 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord2.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -22,6 +21,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js index e58cddccfe..39c6037ad0 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.coord3.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -25,6 +24,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js index 4a173af0b4..2b4739fbb4 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeat.outside.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -26,6 +25,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js index e776f7a008..0b93221e07 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.basic.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js index 57c2102cc8..9dec7cea47 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.coord1.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -31,6 +30,5 @@ promise_test(async t => { _assertPixel(canvas, 98,25, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js index e40f6aa207..65cfe4e5ed 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeatx.outside.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js index 6a53b5dc4d..ee717dcabc 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.basic.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js index d59abb6e07..e35777a8db 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.coord1.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -31,6 +30,5 @@ promise_test(async t => { _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 50,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js index c4feba10ca..531ff55637 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.paint.repeaty.outside.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js index 5aefc0dab3..c2ed94a4bb 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/fill-and-stroke-styles/2d.pattern.repeat.empty.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -24,6 +23,5 @@ promise_test(async t => { _assertPixel(canvas, 98,1, 0,255,0,255); _assertPixel(canvas, 1,48, 0,255,0,255); _assertPixel(canvas, 98,48, 0,255,0,255); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html index cdd501ba8e..4a4ce5a59a 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html +++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.html @@ -78,63 +78,6 @@ t.step(function() { {name: 'dropShadow', floodOpacity: [20]}); ctx.filter = new CanvasFilter( {name: 'dropShadow', floodOpacity: '30'}); - // dx - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: 10}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: -1}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: 0.5}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: null}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: true}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: false}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: []}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: [20]}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: '30'}); - // dy - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: 10}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: -1}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: 0.5}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: null}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: true}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: false}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: []}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: [20]}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: '30'}); - // floodOpacity - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: 10}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: -1}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: 0.5}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: null}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: true}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: false}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: []}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: [20]}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: '30'}); // stdDeviation ctx.filter = new CanvasFilter( {name: 'dropShadow', stdDeviation: 10}); diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js index 1d56865e68..b51af5757c 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.canvasFilterObject.dropShadow.exceptions.tentative.worker.js @@ -74,63 +74,6 @@ t.step(function() { {name: 'dropShadow', floodOpacity: [20]}); ctx.filter = new CanvasFilter( {name: 'dropShadow', floodOpacity: '30'}); - // dx - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: 10}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: -1}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: 0.5}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: null}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: true}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: false}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: []}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: [20]}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dx: '30'}); - // dy - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: 10}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: -1}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: 0.5}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: null}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: true}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: false}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: []}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: [20]}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', dy: '30'}); - // floodOpacity - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: 10}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: -1}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: 0.5}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: null}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: true}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: false}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: []}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: [20]}); - ctx.filter = new CanvasFilter( - {name: 'dropShadow', floodOpacity: '30'}); // stdDeviation ctx.filter = new CanvasFilter( {name: 'dropShadow', stdDeviation: 10}); diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.html b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.html index 10392dea5a..45d81a6f2b 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.html +++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.html @@ -78,63 +78,6 @@ t.step(function() { {name: 'dropShadow', floodOpacity: [20]}}); ctx.endLayer(); ctx.beginLayer({filter: {name: 'dropShadow', floodOpacity: '30'}}); ctx.endLayer(); - // dx - ctx.beginLayer({filter: - {name: 'dropShadow', dx: 10}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: -1}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: 0.5}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: null}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: true}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: false}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: []}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: [20]}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: '30'}}); ctx.endLayer(); - // dy - ctx.beginLayer({filter: - {name: 'dropShadow', dy: 10}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: -1}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: 0.5}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: null}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: true}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: false}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: []}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: [20]}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: '30'}}); ctx.endLayer(); - // floodOpacity - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: 10}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: -1}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: 0.5}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: null}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: true}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: false}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: []}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: [20]}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: '30'}}); ctx.endLayer(); // stdDeviation ctx.beginLayer({filter: {name: 'dropShadow', stdDeviation: 10}}); ctx.endLayer(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.worker.js b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.worker.js index 86b8c56af6..65fc751215 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/filters/2d.filter.layers.dropShadow.exceptions.worker.js @@ -74,63 +74,6 @@ t.step(function() { {name: 'dropShadow', floodOpacity: [20]}}); ctx.endLayer(); ctx.beginLayer({filter: {name: 'dropShadow', floodOpacity: '30'}}); ctx.endLayer(); - // dx - ctx.beginLayer({filter: - {name: 'dropShadow', dx: 10}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: -1}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: 0.5}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: null}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: true}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: false}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: []}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: [20]}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dx: '30'}}); ctx.endLayer(); - // dy - ctx.beginLayer({filter: - {name: 'dropShadow', dy: 10}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: -1}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: 0.5}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: null}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: true}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: false}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: []}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: [20]}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', dy: '30'}}); ctx.endLayer(); - // floodOpacity - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: 10}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: -1}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: 0.5}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: null}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: true}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: false}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: []}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: [20]}}); ctx.endLayer(); - ctx.beginLayer({filter: - {name: 'dropShadow', floodOpacity: '30'}}); ctx.endLayer(); // stdDeviation ctx.beginLayer({filter: {name: 'dropShadow', stdDeviation: 10}}); ctx.endLayer(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html index 867553b45e..cebdfd5f27 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html +++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.html @@ -10,13 +10,7 @@ <script> -var t = async_test("Tests that layers ignore the global context filter."); -var t_pass = t.done.bind(t); -var t_fail = t.step_func(function(reason) { - throw reason; -}); -t.step(function() { - +test(t => { var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -51,7 +45,6 @@ t.step(function() { _assertSame(ctx.shadowOffsetX, 10, "ctx.shadowOffsetX", "10"); _assertSame(ctx.shadowOffsetY, 20, "ctx.shadowOffsetY", "20"); _assertSame(ctx.shadowBlur, 30, "ctx.shadowBlur", "30"); - t.done(); -}); +}, "Tests that layers ignore the global context filter."); </script> diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js index 5c2bb73e8f..a566cccf14 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.layer-rendering-state-reset-in-layer.worker.js @@ -6,13 +6,7 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); -var t = async_test("Tests that layers ignore the global context filter."); -var t_pass = t.done.bind(t); -var t_fail = t.step_func(function(reason) { - throw reason; -}); -t.step(function() { - +test(t => { var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -47,6 +41,5 @@ t.step(function() { _assertSame(ctx.shadowOffsetX, 10, "ctx.shadowOffsetX", "10"); _assertSame(ctx.shadowOffsetY, 20, "ctx.shadowOffsetY", "20"); _assertSame(ctx.shadowBlur, 30, "ctx.shadowBlur", "30"); - t.done(); -}); +}, "Tests that layers ignore the global context filter."); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js index be0f4f2cde..8361e19108 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.convertToBlob.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(200, 200); var ctx = canvas.getContext('2d'); @@ -18,6 +17,5 @@ promise_test(async t => { // Calling again inside a layer should throw. ctx.beginLayer(); await promise_rejects_dom(t, 'InvalidStateError', canvas.convertToBlob()); - t.done(); }, "Check that exceptions are thrown for operations that are malformed while layers are open."); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js index b286b7a9e7..d64f693864 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/layers/2d.layer.malformed-operations-with-promises.createImageBitmap.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(200, 200); var ctx = canvas.getContext('2d'); @@ -18,6 +17,5 @@ promise_test(async t => { // Calling again inside a layer should throw. ctx.beginLayer(); await promise_rejects_dom(t, 'InvalidStateError', createImageBitmap(canvas)); - t.done(); }, "Check that exceptions are thrown for operations that are malformed while layers are open."); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js index 827bd2ce65..dee8927564 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.alpha.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -21,6 +20,5 @@ promise_test(async t => { ctx.drawImage(img, 0, -50); _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2); - t.done(); }, "Shadows are drawn correctly for partially-transparent images"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js index 2d02232c68..7aa17da784 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.basic.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -21,6 +20,5 @@ promise_test(async t => { ctx.drawImage(img, 0, -50); _assertPixel(canvas, 50,25, 0,255,0,255); - t.done(); }, "Shadows are drawn for images"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js index 2c10381f98..3c1cf872ec 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.scale.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -23,6 +22,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, "Shadows are drawn correctly for scaled images"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js index 94affdfb6a..c357a3a3ab 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.section.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -23,6 +22,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 50,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, "Shadows are not drawn for areas outside image source rectangles"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js index c5421d7042..5440944e93 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.1.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -21,6 +20,5 @@ promise_test(async t => { ctx.drawImage(img, 0, -50); _assertPixel(canvas, 50,25, 0,255,0,255); - t.done(); }, "Shadows are not drawn for transparent images"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js index 071c584fa8..0554c03750 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.image.transparent.2.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixel(canvas, 25,25, 0,255,0,255); _assertPixel(canvas, 50,25, 0,255,0,255); _assertPixel(canvas, 75,25, 0,255,0,255); - t.done(); }, "Shadows are not drawn for transparent parts of images"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js index 41a826a158..9d5e5b07a9 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.alpha.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -23,6 +22,5 @@ promise_test(async t => { ctx.fillRect(0, -50, 100, 50); _assertPixelApprox(canvas, 50,25, 127,0,127,255, 2); - t.done(); }, "Shadows are drawn correctly for partially-transparent fill patterns"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js index 7a8f7e8479..d2db2427c5 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.basic.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -23,6 +22,5 @@ promise_test(async t => { ctx.fillRect(0, -50, 100, 50); _assertPixel(canvas, 50,25, 0,255,0,255); - t.done(); }, "Shadows are drawn for fill patterns"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js index 8414db6d08..af079851e6 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -23,6 +22,5 @@ promise_test(async t => { ctx.fillRect(0, -50, 100, 50); _assertPixel(canvas, 50,25, 0,255,0,255); - t.done(); }, "Shadows are not drawn for transparent fill patterns"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js index 726a42d6cd..1cf4f3ee36 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixel(canvas, 25,25, 0,255,0,255); _assertPixel(canvas, 50,25, 0,255,0,255); _assertPixel(canvas, 75,25, 0,255,0,255); - t.done(); }, "Shadows are not drawn for transparent parts of fill patterns"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js index 44f093cdac..c11c8437e2 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.center.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, "textAlign center is the center of the em squares (not the bounding box)"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js index 4bd0f5a314..f5c5769470 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.ltr.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, "textAlign end with ltr is the right edge"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js index b3e62d8538..90e21f41dc 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.end.rtl.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, "textAlign end with rtl is the left edge"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js index c7dc0bed42..0f92969632 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.left.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, "textAlign left is the left of the first em square (not the bounding box)"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js index 58fe85e0b7..594192812a 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.right.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, "textAlign right is the right of the last em square (not the bounding box)"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js index c0793d9af6..32e6fc0f20 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.ltr.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, "textAlign start with ltr is the left edge"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js index ed706c287a..67a196daf7 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.align.start.rtl.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -28,6 +27,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, "textAlign start with rtl is the right edge"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js index a98a5863d9..a0ab72915f 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.alphabetic.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js index 30f60c574b..e171388834 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.bottom.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, "textBaseline bottom is the bottom of the em square (not the bounding box)"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js index 638506a279..906fdbbf27 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.hanging.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js index 7c682fb5f1..76ac1d9da2 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.ideographic.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js index c373ab6c72..c288ab4c86 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.middle.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, "textBaseline middle is the middle of the em square (not the bounding box)"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js index effc4418c3..e7d0b167c0 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.baseline.top.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 5,45, 0,255,0,255, 2); _assertPixelApprox(canvas, 95,45, 0,255,0,255, 2); - t.done(); }, "textBaseline top is the top of the em square (not the bounding box)"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js index adcb45faaa..a314870f11 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.bound.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -24,6 +23,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, "fillText handles maxWidth based on line size, not bounding box size"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js index 8df519b7d1..e225e70351 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -24,6 +23,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, "fillText works on @font-face fonts"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js index 628cb39506..c35c82d71f 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.notinpage.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -24,6 +23,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, "@font-face fonts should work even if they are not used in the page"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js index 7567c09558..fb1f7980a6 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.repeat.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -27,6 +26,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, "Draw with the font immediately, then wait a bit until and draw again. (This crashes some version of WebKit.)"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js index 02257f0a06..a09a0f88d7 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.fontface.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -24,6 +23,5 @@ promise_test(async t => { _assertPixelApprox(canvas, 95,5, 0,255,0,255, 2); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, ""); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js index ef09925c55..96bd4159c7 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.basic.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -22,6 +21,5 @@ promise_test(async t => { ctx.fillText('E EE', -100, 37.5); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, "U+0020 is rendered the correct size (1em wide)"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js index 9a9afa1e74..e9cd9dc829 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.end.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -23,6 +22,5 @@ promise_test(async t => { ctx.fillText('EE ', 100, 37.5); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 255,0,0,255, 2); - t.done(); }, "Space characters at the end of a line are NOT collapsed"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js index a12c2f9a48..a37094e28c 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.nonspace.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -22,6 +21,5 @@ promise_test(async t => { ctx.fillText('E\x0b EE', -150, 37.5); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, "Non-space characters are not converted to U+0020 and collapsed"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js index bbf9e684be..1f090cb426 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.other.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -22,6 +21,5 @@ promise_test(async t => { ctx.fillText('E \x09\x0a\x0c\x0d \x09\x0a\x0c\x0dEE', 0, 37.5); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 255,0,0,255, 2); - t.done(); }, "Space characters are converted to U+0020, and are NOT collapsed"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js index e5ab7b67f4..02551b7de7 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.space.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -22,6 +21,5 @@ promise_test(async t => { ctx.fillText('E EE', 0, 37.5); _assertPixelApprox(canvas, 25,25, 0,255,0,255, 2); _assertPixelApprox(canvas, 75,25, 255,0,0,255, 2); - t.done(); }, "Space characters are converted to U+0020, and are NOT collapsed"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js index 20fec7801f..bd6e523283 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.draw.space.collapse.start.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -22,6 +21,5 @@ promise_test(async t => { ctx.fillText(' EE', 0, 37.5); _assertPixelApprox(canvas, 25,25, 255,0,0,255, 2); _assertPixelApprox(canvas, 75,25, 0,255,0,255, 2); - t.done(); }, "Space characters at the start of a line are NOT collapsed"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js index c3d18ff878..aaec2e7e8e 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.actualBoundingBox.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -41,6 +40,5 @@ promise_test(async t => { _assert(ctx.measureText('ABCD').actualBoundingBoxRight >= 200, "ctx.measureText('ABCD').actualBoundingBoxRight >= 200"); _assert(ctx.measureText('ABCD').actualBoundingBoxAscent >= 85, "ctx.measureText('ABCD').actualBoundingBoxAscent >= 85"); _assert(ctx.measureText('ABCD').actualBoundingBoxDescent >= 37, "ctx.measureText('ABCD').actualBoundingBoxDescent >= 37"); - t.done(); }, "Testing actualBoundingBox"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js index 4b02f06f2d..3bf2d9f719 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.advances.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -32,6 +31,5 @@ promise_test(async t => { _assertSame(ctx.measureText('Hello').advances[2], tm.advances[2], "ctx.measureText('Hello').advances[\""+(2)+"\"]", "tm.advances[\""+(2)+"\"]"); _assertSame(ctx.measureText('Hello').advances[3], tm.advances[3], "ctx.measureText('Hello').advances[\""+(3)+"\"]", "tm.advances[\""+(3)+"\"]"); _assertSame(ctx.measureText('Hello').advances[4], tm.advances[4], "ctx.measureText('Hello').advances[\""+(4)+"\"]", "tm.advances[\""+(4)+"\"]"); - t.done(); }, "Testing width advances"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js index ddb48f4fe0..6e7b064ac8 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.baselines.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -25,6 +24,5 @@ promise_test(async t => { _assertSame(Math.abs(ctx.measureText('ABCD').alphabeticBaseline), 0, "Math.abs(ctx.measureText('ABCD').alphabeticBaseline)", "0"); _assertSame(ctx.measureText('ABCD').ideographicBaseline, 6.25, "ctx.measureText('ABCD').ideographicBaseline", "6.25"); _assertSame(ctx.measureText('ABCD').hangingBaseline, 25, "ctx.measureText('ABCD').hangingBaseline", "25"); - t.done(); }, "Testing baselines"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js index 09ee787309..cbcb5ec004 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-low-ascent.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -25,6 +24,5 @@ promise_test(async t => { _assertSame(ctx.measureText('ABCD').emHeightAscent, 20, "ctx.measureText('ABCD').emHeightAscent", "20"); _assertSame(ctx.measureText('ABCD').emHeightDescent, 20, "ctx.measureText('ABCD').emHeightDescent", "20"); _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40"); - t.done(); }, "Testing emHeights with reduced ascent metric"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js index a2f09782c9..4b62bd5665 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights-zero-descent.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -25,6 +24,5 @@ promise_test(async t => { _assertSame(ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightAscent", "40"); _assertSame(ctx.measureText('ABCD').emHeightDescent, 0, "ctx.measureText('ABCD').emHeightDescent", "0"); _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40"); - t.done(); }, "Testing emHeights with zero descent metric"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js index 0385d3d737..163394ff1b 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.emHeights.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -25,6 +24,5 @@ promise_test(async t => { _assertSame(ctx.measureText('ABCD').emHeightAscent, 30, "ctx.measureText('ABCD').emHeightAscent", "30"); _assertSame(ctx.measureText('ABCD').emHeightDescent, 10, "ctx.measureText('ABCD').emHeightDescent", "10"); _assertSame(ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent, 40, "ctx.measureText('ABCD').emHeightDescent + ctx.measureText('ABCD').emHeightAscent", "40"); - t.done(); }, "Testing emHeights"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js index dea67ff8f6..6ea01b1496 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-reduced-ascent.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -23,6 +22,5 @@ promise_test(async t => { _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 10, "ctx.measureText('ABCD').fontBoundingBoxAscent", "10"); _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10"); - t.done(); }, "Testing fontBoundingBox for OffscreenCanvas with reduced ascent metric"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js index 99a886d015..c8175eec3c 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox-zero-descent.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -23,6 +22,5 @@ promise_test(async t => { _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 30, "ctx.measureText('ABCD').fontBoundingBoxAscent", "30"); _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 0, "ctx.measureText('ABCD').fontBoundingBoxDescent", "0"); - t.done(); }, "Testing fontBoundingBox for OffscreenCanvas with zero descent metric"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js index 255bcd108a..080a4d45b8 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.ahem.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -22,6 +21,5 @@ promise_test(async t => { _assertSame(ctx.measureText('A').fontBoundingBoxDescent, 10, "ctx.measureText('A').fontBoundingBoxDescent", "10"); _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 40, "ctx.measureText('ABCD').fontBoundingBoxAscent", "40"); _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10"); - t.done(); }, "Testing fontBoundingBox for font ahem"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js index b2cde04c0d..5c28b8f688 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.fontBoundingBox.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -23,6 +22,5 @@ promise_test(async t => { _assertSame(ctx.measureText('ABCD').fontBoundingBoxAscent, 30, "ctx.measureText('ABCD').fontBoundingBoxAscent", "30"); _assertSame(ctx.measureText('ABCD').fontBoundingBoxDescent, 10, "ctx.measureText('ABCD').fontBoundingBoxDescent", "10"); - t.done(); }, "Testing fontBoundingBox measurements"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js index 866ad8b3c5..f653f20f8e 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.basic.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -22,6 +21,5 @@ promise_test(async t => { ctx.font = '100px CanvasTest'; _assertSame(ctx.measureText('A').width, 100, "ctx.measureText('A').width", "100"); - t.done(); }, "The width of character is same as font used"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js index 6fb89c5ba7..932b82e129 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.empty.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -17,6 +16,5 @@ promise_test(async t => { await self.fonts.ready; ctx.font = '50px CanvasTest'; _assertSame(ctx.measureText("").width, 0, "ctx.measureText(\"\").width", "0"); - t.done(); }, "The empty string has zero width"); done(); diff --git a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js index d832feec5f..3f0e6d65da 100644 --- a/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js +++ b/testing/web-platform/tests/html/canvas/offscreen/text/2d.text.measure.width.space.worker.js @@ -7,7 +7,6 @@ importScripts("/resources/testharness.js"); importScripts("/html/canvas/resources/canvas-tests.js"); promise_test(async t => { - var canvas = new OffscreenCanvas(100, 50); var ctx = canvas.getContext('2d'); @@ -23,6 +22,5 @@ promise_test(async t => { _assertSame(ctx.measureText(' AB').width, 150, "ctx.measureText(' AB').width", "150"); _assertSame(ctx.measureText('AB ').width, 150, "ctx.measureText('AB ').width", "150"); - t.done(); }, "Space characters are converted to U+0020 and NOT collapsed"); done(); diff --git a/testing/web-platform/tests/html/canvas/tools/gentest.py b/testing/web-platform/tests/html/canvas/tools/gentest.py index 36cb7fe0f6..784005fc9a 100644 --- a/testing/web-platform/tests/html/canvas/tools/gentest.py +++ b/testing/web-platform/tests/html/canvas/tools/gentest.py @@ -1,3 +1,4 @@ +"""Script invoking the old and the new Canvas test generator.""" # To use this script: # -make a python virtual environment somewhere (it doesn't matter where) # python3 -m venv venv @@ -9,10 +10,10 @@ # python3 gentest.py from gentestutils import genTestUtils -from gentestutilsunion import genTestUtils_union +import gentestutilsunion genTestUtils('../element', '../element', 'templates.yaml', 'name2dir-canvas.yaml', False) genTestUtils('../offscreen', '../offscreen', 'templates.yaml', 'name2dir-offscreen.yaml', True) -genTestUtils_union('name2dir.yaml') +gentestutilsunion.generate_test_files('name2dir.yaml') diff --git a/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py b/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py index d7042810be..57077f6057 100644 --- a/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py +++ b/testing/web-platform/tests/html/canvas/tools/gentestutilsunion.py @@ -1,3 +1,4 @@ +"""Generates Canvas tests from YAML file definitions.""" # Current code status: # # This was originally written by Philip Taylor for use at @@ -28,7 +29,8 @@ # # * Test the tests, add new ones to Git, remove deleted ones from Git, etc. -from typing import Any, DefaultDict, List, Mapping, Optional, Set, Tuple +from typing import Any, DefaultDict, FrozenSet, List, Mapping, MutableMapping +from typing import Optional, Set, Tuple import re import collections @@ -36,10 +38,12 @@ import dataclasses import enum import importlib import itertools -import jinja2 import os import pathlib import sys +import textwrap + +import jinja2 try: import cairocffi as cairo # type: ignore @@ -61,12 +65,12 @@ class InvalidTestDefinitionError(Error): """Raised on invalid test definition.""" -def _doubleQuoteEscape(string: str) -> str: +def _double_quote_escape(string: str) -> str: return string.replace('\\', '\\\\').replace('"', '\\"') -def _escapeJS(string: str) -> str: - string = _doubleQuoteEscape(string) +def _escape_js(string: str) -> str: + string = _double_quote_escape(string) # Kind of an ugly hack, for nicer failure-message output. string = re.sub(r'\[(\w+)\]', r'[\\""+(\1)+"\\"]', string) return string @@ -135,15 +139,15 @@ def _expand_nonfinite(method: str, argstr: str, tail: str) -> str: match = re.match('<(.*)>', arg) if match is None: raise InvalidTestDefinitionError( - f"Expected arg to match format '<(.*)>', but was: {arg}") + f'Expected arg to match format "<(.*)>", but was: {arg}') a = match.group(1) args.append(a.split(' ')) calls = [] # Start with the valid argument list. call = [args[j][0] for j in range(len(args))] # For each argument alone, try setting it to all its invalid values: - for i in range(len(args)): - for a in args[i][1:]: + for i, arg in enumerate(args): + for a in arg[1:]: c2 = call[:] c2[i] = a calls.append(c2) @@ -162,7 +166,8 @@ def _expand_nonfinite(method: str, argstr: str, tail: str) -> str: f(call, 0, 0) - return '\n'.join('%s(%s)%s' % (method, ', '.join(c), tail) for c in calls) + str_calls = (', '.join(c) for c in calls) + return '\n'.join(f'{method}({params}){tail}' for params in str_calls) def _get_test_sub_dir(name: str, name_to_sub_dir: Mapping[str, str]) -> str: @@ -170,7 +175,7 @@ def _get_test_sub_dir(name: str, name_to_sub_dir: Mapping[str, str]) -> str: if name.startswith(prefix): return name_to_sub_dir[prefix] raise InvalidTestDefinitionError( - 'Test "%s" has no defined target directory mapping' % name) + f'Test "{name}" has no defined target directory mapping') def _remove_extra_newlines(text: str) -> str: @@ -183,6 +188,7 @@ def _remove_extra_newlines(text: str) -> str: text = re.sub(r'\\-\n\s*', '', text, flags=re.MULTILINE | re.DOTALL) return text + def _expand_test_code(code: str) -> str: code = re.sub(r' @moz-todo', '', code) @@ -215,48 +221,59 @@ def _expand_test_code(code: str) -> str: flags=re.MULTILINE | re.DOTALL) code = re.sub( - r'@assert (.*) === (.*);', lambda m: '_assertSame(%s, %s, "%s", "%s");' - % (m.group(1), m.group(2), _escapeJS(m.group(1)), _escapeJS(m.group(2)) - ), code) + r'@assert (.*) === (.*);', lambda m: + (f'_assertSame({m.group(1)}, {m.group(2)}, ' + f'"{_escape_js(m.group(1))}", "{_escape_js(m.group(2))}");'), code) code = re.sub( r'@assert (.*) !== (.*);', lambda m: - '_assertDifferent(%s, %s, "%s", "%s");' % (m.group(1), m.group( - 2), _escapeJS(m.group(1)), _escapeJS(m.group(2))), code) + (f'_assertDifferent({m.group(1)}, {m.group(2)}, ' + f'"{_escape_js(m.group(1))}", "{_escape_js(m.group(2))}");'), code) code = re.sub( - r'@assert (.*) =~ (.*);', lambda m: 'assert_regexp_match(%s, %s);' % ( - m.group(1), m.group(2)), code) + r'@assert (.*) =~ (.*);', + lambda m: f'assert_regexp_match({m.group(1)}, {m.group(2)});', code) code = re.sub( - r'@assert (.*);', lambda m: '_assert(%s, "%s");' % (m.group( - 1), _escapeJS(m.group(1))), code) + r'@assert (.*);', + lambda m: f'_assert({m.group(1)}, "{_escape_js(m.group(1))}");', code) - assert ('@' not in code) + assert '@' not in code return code -class CanvasType(str, enum.Enum): - HTML_CANVAS = 'htmlcanvas' - OFFSCREEN_CANVAS = 'offscreencanvas' - WORKER = 'worker' +_TestParams = Mapping[str, Any] +_MutableTestParams = MutableMapping[str, Any] + +class _CanvasType(str, enum.Enum): + HTML_CANVAS = 'HtmlCanvas' + OFFSCREEN_CANVAS = 'OffscreenCanvas' + WORKER = 'Worker' -def _get_enabled_canvas_types(test: Mapping[str, Any]) -> Set[CanvasType]: - return {CanvasType(t.lower()) for t in test.get('canvasType', CanvasType)} + +class _TemplateType(str, enum.Enum): + REFERENCE = 'reference' + HTML_REFERENCE = 'html_reference' + TESTHARNESS = 'testharness' @dataclasses.dataclass -class TestConfig: - out_dir: str - image_out_dir: str +class _OutputPaths: + element: str + offscreen: str + + def sub_path(self, sub_dir: str): + """Create a new _OutputPaths that is a subpath of this _OutputPath.""" + return _OutputPaths(element=os.path.join(self.element, sub_dir), + offscreen=os.path.join(self.offscreen, sub_dir)) -def _validate_test(test: Mapping[str, Any]): +def _validate_test(test: _TestParams): if test.get('expected', '') == 'green' and re.search( r'@assert pixel .* 0,0,0,0;', test['code']): - print('Probable incorrect pixel test in %s' % test['name']) + print(f'Probable incorrect pixel test in {test["name"]}') if 'size' in test and (not isinstance(test['size'], list) or len(test['size']) != 2): @@ -264,21 +281,20 @@ def _validate_test(test: Mapping[str, Any]): f'Invalid canvas size "{test["size"]}" in test {test["name"]}. ' 'Expected an array with two numbers.') - if 'test_type' in test and test['test_type'] != 'promise': - raise InvalidTestDefinitionError( - f'Test {test["name"]}\' test_type is invalid, it only accepts ' - '"promise" now for creating promise test type in the template ' - 'file.') + if test['template_type'] == _TemplateType.TESTHARNESS: + valid_test_types = {'sync', 'async', 'promise'} + else: + valid_test_types = {'promise'} - if 'reference' in test and 'html_reference' in test: + test_type = test.get('test_type') + if test_type is not None and test_type not in valid_test_types: raise InvalidTestDefinitionError( - f'Test {test["name"]} is invalid, "reference" and "html_reference" ' - 'can\'t both be specified at the same time.') + f'Invalid test_type: {test_type}. ' + f'Valid values are: {valid_test_types}.') -def _render_template(jinja_env: jinja2.Environment, - template: jinja2.Template, - params: Mapping[str, Any]) -> str: +def _render_template(jinja_env: jinja2.Environment, template: jinja2.Template, + params: _TestParams) -> str: """Renders the specified jinja template. The template is repetitively rendered until no more changes are observed. @@ -294,236 +310,276 @@ def _render_template(jinja_env: jinja2.Environment, def _render(jinja_env: jinja2.Environment, template_name: str, - params: Mapping[str, Any]): - params = dict(params) - params.update({ - # Render the code on its own, as it could contain templates expanding - # to multuple lines. This is needed to get proper indentation of the - # code in the main template. - 'code': _render_template(jinja_env, - jinja_env.from_string(params['code']), - params) - }) - - return _render_template(jinja_env, jinja_env.get_template(template_name), - params) - - -def _write_reference_test(jinja_env: jinja2.Environment, - params: Mapping[str, Any], - enabled_tests: Set[CanvasType], - canvas_path: str, offscreen_path: str): - if CanvasType.HTML_CANVAS in enabled_tests: - html_params = dict(params) - html_params.update({'canvas_type': CanvasType.HTML_CANVAS.value}) - pathlib.Path(f'{canvas_path}.html').write_text( - _render(jinja_env, "reftest_element.html", html_params), 'utf-8') - if CanvasType.OFFSCREEN_CANVAS in enabled_tests: - offscreen_params = dict(params) - offscreen_params.update({ - 'canvas_type': CanvasType.OFFSCREEN_CANVAS.value - }) - pathlib.Path(f'{offscreen_path}.html').write_text( - _render(jinja_env, "reftest_offscreen.html", offscreen_params), - 'utf-8') - if CanvasType.WORKER in enabled_tests: - worker_params = dict(params) - worker_params.update({'canvas_type': CanvasType.WORKER.value}) - pathlib.Path(f'{offscreen_path}.w.html').write_text( - _render(jinja_env, "reftest_worker.html", worker_params), 'utf-8') - - js_ref = params.get('reference', '') - html_ref = params.get('html_reference', '') - ref_params = dict(params) - ref_params.update({ - 'is_test_reference': True, - 'code': js_ref or html_ref - }) - ref_template_name = 'reftest_element.html' if js_ref else 'reftest.html' - if CanvasType.HTML_CANVAS in enabled_tests: - pathlib.Path(f'{canvas_path}-expected.html').write_text( - _render(jinja_env, ref_template_name, ref_params), 'utf-8') - if {CanvasType.OFFSCREEN_CANVAS, CanvasType.WORKER} & enabled_tests: - pathlib.Path(f'{offscreen_path}-expected.html').write_text( - _render(jinja_env, ref_template_name, ref_params), 'utf-8') - - -def _write_testharness_test(jinja_env: jinja2.Environment, - params: Mapping[str, Any], - enabled_tests: Set[CanvasType], - canvas_path: str, - offscreen_path: str): - # Create test cases for canvas and offscreencanvas. - if CanvasType.HTML_CANVAS in enabled_tests: - html_params = dict(params) - html_params.update({'canvas_type': CanvasType.HTML_CANVAS.value}) - pathlib.Path(f'{canvas_path}.html').write_text( - _render(jinja_env, "testharness_element.html", html_params), - 'utf-8') - - if CanvasType.OFFSCREEN_CANVAS in enabled_tests: - offscreen_params = dict(params) - offscreen_params.update({ - 'canvas_type': CanvasType.OFFSCREEN_CANVAS.value - }) - pathlib.Path(f'{offscreen_path}.html').write_text( - _render(jinja_env, "testharness_offscreen.html", offscreen_params), - 'utf-8') - - if CanvasType.WORKER in enabled_tests: - worker_params = dict(params) - worker_params.update({'canvas_type': CanvasType.WORKER.value}) - pathlib.Path(f'{offscreen_path}.worker.js').write_text( - _render(jinja_env, "testharness_worker.js", worker_params), - 'utf-8') - - -def _generate_expected_image(expected: str, name: str, sub_dir: str, - enabled_canvas_types: Set[CanvasType], - html_canvas_cfg: TestConfig, - offscreen_canvas_cfg: TestConfig) -> str: - """Creates a reference image using Cairo and returns the file location.""" - if expected == 'green': - return '/images/green-100x50.png' - if expected == 'clear': - return '/images/clear-100x50.png' - if ';' in expected: - print('Found semicolon in %s' % name) - expected = re.sub( - r'^size (\d+) (\d+)', - r'surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \1, \2)' - r'\ncr = cairo.Context(surface)', expected) - - if CanvasType.HTML_CANVAS in enabled_canvas_types: - expected_canvas = ( - expected + "\nsurface.write_to_png('%s.png')\n" % - os.path.join(html_canvas_cfg.image_out_dir, sub_dir, name)) - eval(compile(expected_canvas, '<test %s>' % name, 'exec'), {}, - {'cairo': cairo}) - - if {CanvasType.OFFSCREEN_CANVAS, CanvasType.WORKER} & enabled_canvas_types: - expected_offscreen = ( - expected + "\nsurface.write_to_png('%s.png')\n" % - os.path.join(offscreen_canvas_cfg.image_out_dir, sub_dir, name)) - eval(compile(expected_offscreen, '<test %s>' % name, 'exec'), {}, - {'cairo': cairo}) - - return '%s.png' % name - - -def _generate_test(test: Mapping[str, Any], jinja_env: jinja2.Environment, - name_to_sub_dir: Mapping[str, str], - used_tests: DefaultDict[str, Set[CanvasType]], - html_canvas_cfg: TestConfig, - offscreen_canvas_cfg: TestConfig) -> None: - _validate_test(test) - - name = test['name'] - - sub_dir = _get_test_sub_dir(name, name_to_sub_dir) - enabled_canvas_types = _get_enabled_canvas_types(test) - - # Defaults: - params = { - 'desc': '', - 'size': [100, 50], - } - - params.update(test) - - # Render parameters used in the test name. - name = jinja_env.from_string(name).render(params) - print('\r(%s)' % name, ' ' * 32, '\t') - - expected_img = None - if 'expected' in test and test['expected'] is not None: - expected_img = _generate_expected_image(test['expected'], name, - sub_dir, enabled_canvas_types, - html_canvas_cfg, - offscreen_canvas_cfg) - - params.update({ - 'code': _expand_test_code(test['code']), - 'expected_img': expected_img - }) - - already_tested = used_tests[name].intersection(enabled_canvas_types) - if already_tested: - raise InvalidTestDefinitionError( - f'Test {name} is defined twice for types {already_tested}') - used_tests[name].update(enabled_canvas_types) + params: _TestParams, output_file_name: str): + template = jinja_env.get_template(template_name) + file_content = _render_template(jinja_env, template, params) + pathlib.Path(output_file_name).write_text(file_content, 'utf-8') + + +def _preprocess_code(jinja_env: jinja2.Environment, code: str, + params: _TestParams) -> str: + code = _expand_test_code(code) + # Render the code on its own, as it could contain templates expanding + # to multiple lines. This is needed to get proper indentation of the + # code in the main template. + code = _render_template(jinja_env, jinja_env.from_string(code), params) + return code - canvas_path = os.path.join(html_canvas_cfg.out_dir, sub_dir, name) - offscreen_path = os.path.join(offscreen_canvas_cfg.out_dir, sub_dir, name) - if 'manual' in test: - canvas_path += '-manual' - offscreen_path += '-manual' - if 'reference' in test or 'html_reference' in test: - _write_reference_test(jinja_env, params, enabled_canvas_types, - canvas_path, offscreen_path) - else: - _write_testharness_test(jinja_env, params, enabled_canvas_types, - canvas_path, offscreen_path) +class _Variant(): + + def __init__(self, params: _MutableTestParams) -> None: + self._params = params + + @property + def params(self) -> _TestParams: + """Read-only getter for this variant's param dict.""" + return self._params + + @staticmethod + def create_with_defaults(test: _TestParams) -> '_Variant': + """Create a _Variant from the specified params. + + Default values are added for certain parameters, if missing.""" + params = { + 'desc': '', + 'size': [100, 50], + 'variant_names': [], + } + params.update(test) + return _Variant(params) + def _get_variant_name(self, jinja_env: jinja2.Environment) -> str: + name = self.params['name'] + if self.params.get('append_variants_to_name', True): + name = '.'.join([name] + self.params['variant_names']) -def _recursive_expand_variant_matrix(test_list: List[Mapping[str, Any]], - variant_matrix: List[Mapping[str, Any]], + name = jinja_env.from_string(name).render(self.params) + return name + + def _get_file_name(self) -> str: + file_name = self.params['name'] + + if 'manual' in self.params: + file_name += '-manual' + + return file_name + + def _get_canvas_types(self) -> FrozenSet[_CanvasType]: + canvas_types = self.params.get('canvas_types', _CanvasType) + invalid_types = { + type + for type in canvas_types if type not in list(_CanvasType) + } + if invalid_types: + raise InvalidTestDefinitionError( + f'Invalid canvas_types: {list(invalid_types)}. ' + f'Accepted values are: {[t.value for t in _CanvasType]}') + return frozenset(_CanvasType(t) for t in canvas_types) + + def _get_template_type(self) -> _TemplateType: + if 'reference' in self.params and 'html_reference' in self.params: + raise InvalidTestDefinitionError( + f'Test {self.params["name"]} is invalid, "reference" and ' + '"html_reference" can\'t both be specified at the same time.') + + if 'reference' in self.params: + return _TemplateType.REFERENCE + if 'html_reference' in self.params: + return _TemplateType.HTML_REFERENCE + return _TemplateType.TESTHARNESS + + def finalize_params(self, jinja_env: jinja2.Environment) -> None: + """Finalize this variant by adding computed param fields.""" + self._params['name'] = self._get_variant_name(jinja_env) + self._params['file_name'] = self._get_file_name() + self._params['canvas_types'] = self._get_canvas_types() + self._params['template_type'] = self._get_template_type() + + if 'reference' in self._params: + self._params['reference'] = _preprocess_code( + jinja_env, self._params['reference'], self._params) + + if 'html_reference' in self._params: + self._params['html_reference'] = _preprocess_code( + jinja_env, self._params['html_reference'], self._params) + + code_params = dict(self.params) + if _CanvasType.HTML_CANVAS in self.params['canvas_types']: + code_params['canvas_type'] = _CanvasType.HTML_CANVAS.value + self._params['code_element'] = _preprocess_code( + jinja_env, self._params['code'], code_params) + + if _CanvasType.OFFSCREEN_CANVAS in self.params['canvas_types']: + code_params['canvas_type'] = _CanvasType.OFFSCREEN_CANVAS.value + self._params['code_offscreen'] = _preprocess_code( + jinja_env, self._params['code'], code_params) + + if _CanvasType.WORKER in self.params['canvas_types']: + code_params['canvas_type'] = _CanvasType.WORKER.value + self._params['code_worker'] = _preprocess_code( + jinja_env, self._params['code'], code_params) + + _validate_test(self._params) + + def generate_expected_image(self, output_dirs: _OutputPaths) -> None: + """Creates a reference image using Cairo and save filename in params.""" + if 'expected' not in self.params: + return + + expected = self.params['expected'] + name = self.params['name'] + + if expected == 'green': + self._params['expected_img'] = '/images/green-100x50.png' + return + if expected == 'clear': + self._params['expected_img'] = '/images/clear-100x50.png' + return + if ';' in expected: + print(f'Found semicolon in {name}') + expected = re.sub( + r'^size (\d+) (\d+)', + r'surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, \1, \2)' + r'\ncr = cairo.Context(surface)', expected) + + output_paths = output_dirs.sub_path(name) + if _CanvasType.HTML_CANVAS in self.params['canvas_types']: + expected_canvas = ( + f'{expected}\n' + f'surface.write_to_png("{output_paths.element}.png")\n') + eval(compile(expected_canvas, f'<test {name}>', 'exec'), {}, + {'cairo': cairo}) + + if {_CanvasType.OFFSCREEN_CANVAS, _CanvasType.WORKER + } & self.params['canvas_types']: + expected_offscreen = ( + f'{expected}\n' + f'surface.write_to_png("{output_paths.offscreen}.png")\n') + eval(compile(expected_offscreen, f'<test {name}>', 'exec'), {}, + {'cairo': cairo}) + + self._params['expected_img'] = f'{name}.png' + + def _write_reference_test(self, jinja_env: jinja2.Environment, + output_files: _OutputPaths): + params = dict(self.params) + if _CanvasType.HTML_CANVAS in params['canvas_types']: + _render(jinja_env, 'reftest_element.html', params, + f'{output_files.element}.html') + if _CanvasType.OFFSCREEN_CANVAS in params['canvas_types']: + _render(jinja_env, 'reftest_offscreen.html', params, + f'{output_files.offscreen}.html') + if _CanvasType.WORKER in params['canvas_types']: + _render(jinja_env, 'reftest_worker.html', params, + f'{output_files.offscreen}.w.html') + + params['is_test_reference'] = True + is_html_ref = params['template_type'] == _TemplateType.HTML_REFERENCE + ref_template = 'reftest.html' if is_html_ref else 'reftest_element.html' + if _CanvasType.HTML_CANVAS in params['canvas_types']: + _render(jinja_env, ref_template, params, + f'{output_files.element}-expected.html') + if {_CanvasType.OFFSCREEN_CANVAS, _CanvasType.WORKER + } & params['canvas_types']: + _render(jinja_env, ref_template, params, + f'{output_files.offscreen}-expected.html') + + def _write_testharness_test(self, jinja_env: jinja2.Environment, + output_files: _OutputPaths): + # Create test cases for canvas and offscreencanvas. + if _CanvasType.HTML_CANVAS in self.params['canvas_types']: + _render(jinja_env, 'testharness_element.html', self.params, + f'{output_files.element}.html') + + if _CanvasType.OFFSCREEN_CANVAS in self.params['canvas_types']: + _render(jinja_env, 'testharness_offscreen.html', self.params, + f'{output_files.offscreen}.html') + + if _CanvasType.WORKER in self.params['canvas_types']: + _render(jinja_env, 'testharness_worker.js', self.params, + f'{output_files.offscreen}.worker.js') + + def generate_test(self, jinja_env: jinja2.Environment, + output_dirs: _OutputPaths) -> None: + """Generate the test files to the specified output dirs.""" + output_files = output_dirs.sub_path(self.params['file_name']) + + if self.params['template_type'] in (_TemplateType.REFERENCE, + _TemplateType.HTML_REFERENCE): + self._write_reference_test(jinja_env, output_files) + else: + self._write_testharness_test(jinja_env, output_files) + + +def _recursive_expand_variant_matrix(original_test: _TestParams, + variant_matrix: List[_TestParams], current_selection: List[Tuple[str, Any]], - original_test: Mapping[str, Any]): + test_variants: List[_Variant]): if len(current_selection) == len(variant_matrix): # Selection for each variant is done, so add a new test to test_list. - test = original_test.copy() + test = dict(original_test) variant_name_list = [] - should_append_variant_names = original_test.get( - 'append_variants_to_name', True) for variant_name, variant_params in current_selection: - variant_name_list.append(variant_name) - # Append variant name. Variant names starting with '_' are - # not appended, which is useful to create variants with the same - # name in different folders (element vs. offscreen). - if (should_append_variant_names - and not variant_name.startswith('_')): - test['name'] += '.' + variant_name test.update(variant_params) - # Expose variant names as a list so they can be used from the yaml - # files, which helps with better naming of tests. - test.update({'variant_names': variant_name_list}) - test_list.append(test) + variant_name_list.append(variant_name) + # Expose variant names as a list so they can be used from the yaml + # files, which helps with better naming of tests. + test.update({'variant_names': variant_name_list}) + test_variants.append(_Variant.create_with_defaults(test)) else: # Continue the recursion with each possible selection for the current # variant. variant = variant_matrix[len(current_selection)] for variant_options in variant.items(): current_selection.append(variant_options) - _recursive_expand_variant_matrix(test_list, variant_matrix, - current_selection, original_test) + _recursive_expand_variant_matrix(original_test, variant_matrix, + current_selection, test_variants) current_selection.pop() -def _expand_variant_matrix( - variant_matrix: List[Mapping[str, Any]], - original_test: Mapping[str, Any]) -> List[Mapping[str, Any]]: +def _get_variants(test: _TestParams) -> List[_Variant]: current_selection = [] - matrix_tests = [] - _recursive_expand_variant_matrix(matrix_tests, variant_matrix, - current_selection, original_test) - return matrix_tests + test_variants = [] + variants = test.get('variants', []) + if not isinstance(variants, list): + raise InvalidTestDefinitionError( + textwrap.dedent(""" + Variants must be specified as a list of variant dimensions, e.g.: + variants: + - dimension1-variant1: + param: ... + dimension1-variant2: + param: ... + - dimension2-variant1: + param: ... + dimension2-variant2: + param: ...""")) + _recursive_expand_variant_matrix(test, variants, current_selection, + test_variants) + return test_variants + + +def _check_uniqueness(tested: DefaultDict[str, Set[_CanvasType]], name: str, + canvas_types: FrozenSet[_CanvasType]) -> None: + already_tested = tested[name].intersection(canvas_types) + if already_tested: + raise InvalidTestDefinitionError( + f'Test {name} is defined twice for types {already_tested}') + tested[name].update(canvas_types) -def genTestUtils_union(NAME2DIRFILE: str) -> None: - CANVASOUTPUTDIR = '../element' - CANVASIMAGEOUTPUTDIR = '../element' - OFFSCREENCANVASOUTPUTDIR = '../offscreen' - OFFSCREENCANVASIMAGEOUTPUTDIR = '../offscreen' +def generate_test_files(name_to_dir_file: str) -> None: + """Generate Canvas tests from YAML file definition.""" + output_dirs = _OutputPaths(element='../element', offscreen='../offscreen') jinja_env = jinja2.Environment( - loader=jinja2.PackageLoader("gentestutilsunion"), + loader=jinja2.PackageLoader('gentestutilsunion'), keep_trailing_newline=True, trim_blocks=True, lstrip_blocks=True) - jinja_env.filters['double_quote_escape'] = _doubleQuoteEscape + jinja_env.filters['double_quote_escape'] = _double_quote_escape # Run with --test argument to run unit tests. if len(sys.argv) > 1 and sys.argv[1] == '--test': @@ -531,16 +587,19 @@ def genTestUtils_union(NAME2DIRFILE: str) -> None: doctest.testmod() sys.exit() - name_to_sub_dir = yaml.safe_load(pathlib.Path(NAME2DIRFILE).read_text()) + name_to_sub_dir = (yaml.safe_load( + pathlib.Path(name_to_dir_file).read_text(encoding='utf-8'))) tests = [] test_yaml_directory = 'yaml-new' - TESTSFILES = [ + yaml_files = [ os.path.join(test_yaml_directory, f) for f in os.listdir(test_yaml_directory) if f.endswith('.yaml') ] - for t in sum( - [yaml.safe_load(pathlib.Path(f).read_text()) for f in TESTSFILES], []): + for t in sum([ + yaml.safe_load(pathlib.Path(f).read_text(encoding='utf-8')) + for f in yaml_files + ], []): if 'DISABLED' in t: continue if 'meta' in t: @@ -550,14 +609,11 @@ def genTestUtils_union(NAME2DIRFILE: str) -> None: tests.append(t) # Ensure the test output directories exist. - testdirs = [ - CANVASOUTPUTDIR, OFFSCREENCANVASOUTPUTDIR, CANVASIMAGEOUTPUTDIR, - OFFSCREENCANVASIMAGEOUTPUTDIR - ] + test_dirs = [output_dirs.element, output_dirs.offscreen] for sub_dir in set(name_to_sub_dir.values()): - testdirs.append('%s/%s' % (CANVASOUTPUTDIR, sub_dir)) - testdirs.append('%s/%s' % (OFFSCREENCANVASOUTPUTDIR, sub_dir)) - for d in testdirs: + test_dirs.append(f'{output_dirs.element}/{sub_dir}') + test_dirs.append(f'{output_dirs.offscreen}/{sub_dir}') + for d in test_dirs: try: os.mkdir(d) except FileExistsError: @@ -565,24 +621,19 @@ def genTestUtils_union(NAME2DIRFILE: str) -> None: used_tests = collections.defaultdict(set) for test in tests: - if 'variant_matrix' in test: - variants = _expand_variant_matrix(test['variant_matrix'], test) - elif 'variants' in test: - variant_matrix = [test['variants']] - variants = _expand_variant_matrix(variant_matrix, test) - else: - variants = [test] - - for variant in variants: - _generate_test(variant, - jinja_env, - name_to_sub_dir, - used_tests, - html_canvas_cfg=TestConfig( - out_dir=CANVASOUTPUTDIR, - image_out_dir=CANVASIMAGEOUTPUTDIR), - offscreen_canvas_cfg=TestConfig( - out_dir=OFFSCREENCANVASOUTPUTDIR, - image_out_dir=OFFSCREENCANVASIMAGEOUTPUTDIR)) + print(test['name']) + for variant in _get_variants(test): + variant.finalize_params(jinja_env) + if test['name'] != variant.params['name']: + print(f' {variant.params["name"]}') + + sub_dir = _get_test_sub_dir(variant.params['file_name'], + name_to_sub_dir) + output_sub_dirs = output_dirs.sub_path(sub_dir) + + _check_uniqueness(used_tests, variant.params['name'], + variant.params['canvas_types']) + variant.generate_expected_image(output_sub_dirs) + variant.generate_test(jinja_env, output_sub_dirs) print() diff --git a/testing/web-platform/tests/html/canvas/tools/templates/reftest.html b/testing/web-platform/tests/html/canvas/tools/templates/reftest.html index 4c9affbbbb..f68d4c4621 100644 --- a/testing/web-platform/tests/html/canvas/tools/templates/reftest.html +++ b/testing/web-platform/tests/html/canvas/tools/templates/reftest.html @@ -5,7 +5,7 @@ <p class="desc">{{ desc }}</p> {% if notes %}<p class="notes">{{ notes }}{% endif %} -{{ code | trim }} +{{ html_reference | trim }} {% for image in images %} <img src="/images/{{ image }}" id="{{ image }}" class="resource"> {% endfor -%} diff --git a/testing/web-platform/tests/html/canvas/tools/templates/reftest_element.html b/testing/web-platform/tests/html/canvas/tools/templates/reftest_element.html index 6684e9c615..6f7a8c8507 100644 --- a/testing/web-platform/tests/html/canvas/tools/templates/reftest_element.html +++ b/testing/web-platform/tests/html/canvas/tools/templates/reftest_element.html @@ -20,7 +20,8 @@ const canvas = document.getElementById("canvas"); const ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %}); - {{ code | trim | indent(2) }} + {{ reference | trim | indent(2) if is_test_reference else + code_element | trim | indent(2) }} {% if test_type == 'promise' %} document.documentElement.classList.remove("reftest-wait"); {% endif %} diff --git a/testing/web-platform/tests/html/canvas/tools/templates/reftest_offscreen.html b/testing/web-platform/tests/html/canvas/tools/templates/reftest_offscreen.html index 1d0d93dfa1..abc840159f 100644 --- a/testing/web-platform/tests/html/canvas/tools/templates/reftest_offscreen.html +++ b/testing/web-platform/tests/html/canvas/tools/templates/reftest_offscreen.html @@ -18,7 +18,7 @@ const canvas = new OffscreenCanvas({{ size[0] }}, {{ size[1] }}); const ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %}); - {{ code | trim | indent(2) }} + {{ code_offscreen | trim | indent(2) }} const outputCanvas = document.getElementById("canvas"); outputCanvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %}).drawImage(canvas, 0, 0); diff --git a/testing/web-platform/tests/html/canvas/tools/templates/reftest_worker.html b/testing/web-platform/tests/html/canvas/tools/templates/reftest_worker.html index e636d3634b..02281af5d1 100644 --- a/testing/web-platform/tests/html/canvas/tools/templates/reftest_worker.html +++ b/testing/web-platform/tests/html/canvas/tools/templates/reftest_worker.html @@ -18,7 +18,7 @@ const canvas = new OffscreenCanvas({{ size[0] }}, {{ size[1] }}); const ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %}); - {{ code | trim | indent(4) }} + {{ code_worker | trim | indent(4) }} const bitmap = canvas.transferToImageBitmap(); self.postMessage(bitmap, bitmap); diff --git a/testing/web-platform/tests/html/canvas/tools/templates/testharness_element.html b/testing/web-platform/tests/html/canvas/tools/templates/testharness_element.html index 56cde7936a..c8de772ea6 100644 --- a/testing/web-platform/tests/html/canvas/tools/templates/testharness_element.html +++ b/testing/web-platform/tests/html/canvas/tools/templates/testharness_element.html @@ -42,21 +42,29 @@ {% if test_type == 'promise' %} promise_test(async t => { - var canvas = document.getElementById('c'); - var ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %}); -{% else %} +{% elif test_type == 'async' %} +async_test(t => { +{% elif test_type == 'sync' %} +test(t => { +{% endif %} +{% if not test_type %} var t = async_test("{{ desc | double_quote_escape }}"); _addTest(function(canvas, ctx) { + +{% else %} + var canvas = document.getElementById('c'); + var ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %}); + {% endif %} - {# Test body: #} - {{ code | trim | indent(2) }} + {#- Test body: #} + {{ code_element | trim | indent(2) }} {# Promise vs. async test footer: #} -{% if test_type == 'promise' %} -}, "{{ desc }}"); -{% else %} +{% if not test_type %} }{% if attributes %}, {{ attributes }}{% endif %}); +{% else %} +}, "{{ desc | double_quote_escape }}"); {% endif -%} </script> diff --git a/testing/web-platform/tests/html/canvas/tools/templates/testharness_offscreen.html b/testing/web-platform/tests/html/canvas/tools/templates/testharness_offscreen.html index 8ebbff77a6..68147680e1 100644 --- a/testing/web-platform/tests/html/canvas/tools/templates/testharness_offscreen.html +++ b/testing/web-platform/tests/html/canvas/tools/templates/testharness_offscreen.html @@ -15,6 +15,11 @@ {#- Promise vs. async test header: +#} {% if test_type == 'promise' %} promise_test(async t => { + +{% elif test_type == 'async' %} +async_test(t => { +{% elif test_type == 'sync' %} +test(t => { {% else %} var t = async_test("{{ desc | double_quote_escape }}"); var t_pass = t.done.bind(t); @@ -22,22 +27,23 @@ var t_fail = t.step_func(function(reason) { throw reason; }); t.step(function() { + {% endif %} -{# Test body: #} +{#- Test body: #} var canvas = new OffscreenCanvas({{ size[0] }}, {{ size[1] }}); var ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %}); - {{ code | trim | indent(2)}} + {{ code_offscreen | trim | indent(2)}} {#- Promise vs. async test footer: +#} -{% if test_type == 'promise' %} - -}, "{{ desc }}"); -{% else %} +{% if not test_type %} t.done(); }); +{% else %} + +}, "{{ desc | double_quote_escape }}"); {% endif %} </script> {% for svgimage in svgimages %} diff --git a/testing/web-platform/tests/html/canvas/tools/templates/testharness_worker.js b/testing/web-platform/tests/html/canvas/tools/templates/testharness_worker.js index f1f04e7bb6..570d479a0a 100644 --- a/testing/web-platform/tests/html/canvas/tools/templates/testharness_worker.js +++ b/testing/web-platform/tests/html/canvas/tools/templates/testharness_worker.js @@ -10,6 +10,10 @@ importScripts("/html/canvas/resources/canvas-tests.js"); {# Promise vs. async test header: #} {% if test_type == 'promise' %} promise_test(async t => { +{% elif test_type == 'async' %} +async_test(t => { +{% elif test_type == 'sync' %} +test(t => { {% else %} var t = async_test("{{ desc | double_quote_escape }}"); var t_pass = t.done.bind(t); @@ -17,19 +21,20 @@ var t_fail = t.step_func(function(reason) { throw reason; }); t.step(function() { + {% endif %} -{# Test body: #} +{#- Test body: #} var canvas = new OffscreenCanvas({{ size[0] }}, {{ size[1] }}); var ctx = canvas.getContext('2d'{% if attributes %}, {{ attributes }}{% endif %}); - {{ code | trim | indent(2)}} - t.done(); + {{ code_worker | trim | indent(2)}} {#- Promise vs. async test footer: +#} -{% if test_type == 'promise' %} -}, "{{ desc }}"); -{% else %} +{% if not test_type %} + t.done(); }); +{% else %} +}, "{{ desc | double_quote_escape }}"); {% endif %} done(); diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/color_space.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/color_space.yaml index 39556caf0a..56f6d01d30 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/color_space.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/color_space.yaml @@ -36,8 +36,7 @@ - name: 2d.color.space.p3.toBlob.p3.canvas desc: test if toblob returns p3 data from p3 color space canvas attributes: '{colorSpace: "display-p3"}' - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | ctx.fillStyle = "rgba(155, 27, 27, 1)"; ctx.fillRect(0, 0, 1, 1); @@ -68,8 +67,7 @@ - name: 2d.color.space.p3.toDataURL.p3.canvas desc: test if toDataURL returns p3 data from canvas with p3 color space attributes: '{colorSpace: "display-p3"}' - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | ctx.fillStyle = "rgba(155, 27, 27, 1)"; ctx.fillRect(0, 0, 1, 1); @@ -96,8 +94,7 @@ - name: 2d.color.space.p3.toDataURL.jpeg.p3.canvas desc: test if toDataURL('image/jpeg') returns p3 data from canvas with p3 color space attributes: '{colorSpace: "display-p3"}' - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | ctx.fillStyle = "rgba(155, 27, 27, 1)"; ctx.fillRect(0, 0, 1, 1); @@ -124,8 +121,7 @@ - name: 2d.color.space.p3.toBlob.with.putImageData desc: Use putImageData to put some p3 data in canvas and test if toBlob returns the same data attributes: '{colorSpace: "display-p3"}' - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | canvas.width = 2; canvas.height = 2; @@ -162,8 +158,7 @@ - name: 2d.color.space.p3.toDataURL.with.putImageData desc: Use putImageData to put some p3 data in canvas and test if toDataURL returns the same data attributes: '{colorSpace: "display-p3"}' - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | canvas.width = 2; canvas.height = 2; @@ -197,7 +192,7 @@ - name: 2d.color.space.p3.fillText desc: Test if fillText can be used with a solid display-p3 color attributes: '{colorSpace: "display-p3"}' - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | deferTest(); @@ -236,8 +231,7 @@ - name: 2d.color.space.p3.strokeText desc: Test if strokeText can be used with a solid display-p3 color attributes: '{colorSpace: "display-p3"}' - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | deferTest(); @@ -277,8 +271,7 @@ - name: 2d.color.space.p3.fillText.shadow desc: Test if fillText can be used with a display-p3 shadow color attributes: '{colorSpace: "display-p3"}' - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | deferTest(); diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/compositing.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/compositing.yaml index bd7fae1d62..6bd0aaad8a 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/compositing.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/compositing.yaml @@ -43,7 +43,7 @@ expected: green - name: 2d.composite.globalAlpha.canvas - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | var canvas2 = document.createElement('canvas'); canvas2.width = 100; @@ -61,7 +61,7 @@ expected: green - name: 2d.composite.globalAlpha.canvaspattern - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | var canvas2 = document.createElement('canvas'); canvas2.width = 100; @@ -80,7 +80,7 @@ expected: green - name: 2d.composite.globalAlpha.canvascopy - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | var canvas2 = document.createElement('canvas'); canvas2.width = 100; @@ -173,7 +173,7 @@ expected: green - name: 2d.composite.globalAlpha.canvas - canvasType: ['OffscreenCanvas', 'Worker'] + canvas_types: ['OffscreenCanvas', 'Worker'] code: | var offscreenCanvas2 = new OffscreenCanvas(100, 50); var ctx2 = offscreenCanvas2.getContext('2d'); @@ -203,7 +203,7 @@ expected: green - name: 2d.composite.globalAlpha.canvaspattern - canvasType: ['OffscreenCanvas', 'Worker'] + canvas_types: ['OffscreenCanvas', 'Worker'] code: | var offscreenCanvas2 = new OffscreenCanvas(100, 50); var ctx2 = offscreenCanvas2.getContext('2d'); @@ -218,7 +218,7 @@ @assert pixel 50,25 ==~ 2,253,0,255; - name: 2d.composite.globalAlpha.canvascopy - canvasType: ['OffscreenCanvas', 'Worker'] + canvas_types: ['OffscreenCanvas', 'Worker'] code: | var offscreenCanvas2 = new OffscreenCanvas(100, 50); var ctx2 = offscreenCanvas2.getContext('2d'); diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/conformance_requirements.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/conformance_requirements.yaml index 3483d115f4..a070c0a6ef 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/conformance_requirements.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/conformance_requirements.yaml @@ -1,7 +1,7 @@ - name: 2d.conformance.requirements.delete desc: window.CanvasRenderingContext2D is Configurable notes: &bindings Defined in "Web IDL" (draft) - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | @assert window.CanvasRenderingContext2D !== undefined; @assert delete window.CanvasRenderingContext2D === true; @@ -172,7 +172,6 @@ desc: void methods return undefined images: - yellow.png - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | @assert ctx.drawImage(document.getElementById('yellow.png'), 0, 0, 1, 1, 0, 0, 0, 0) === undefined; - diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml index 09e9e00186..7ebe9cca85 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-images-to-the-canvas.yaml @@ -1,5 +1,5 @@ - name: 2d.drawImage.canvas - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | var canvas2 = document.createElement('canvas'); canvas2.width = 100; @@ -70,12 +70,12 @@ desc: Incorrect image types in drawImage do not match any defined overloads, so WebIDL throws a TypeError notes: &bindings Defined in "Web IDL" (draft) - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | @assert throws TypeError ctx.drawImage(document.createElement('p'), 0, 0); - name: 2d.drawImage.incomplete.nosrc - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] mozilla: {throws: !!null ''} code: | ctx.fillStyle = '#0f0'; @@ -86,7 +86,7 @@ expected: green - name: 2d.drawImage.incomplete.immediate - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] images: - red.png code: | @@ -103,7 +103,7 @@ expected: green - name: 2d.drawImage.incomplete.reload - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] images: - yellow.png - red.png @@ -122,7 +122,7 @@ expected: green - name: 2d.drawImage.incomplete.emptysrc - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] images: - red.png mozilla: {throws: !!null ''} @@ -136,7 +136,7 @@ expected: green - name: 2d.drawImage.incomplete.removedsrc - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] images: - red.png mozilla: {throws: !!null ''} @@ -150,7 +150,7 @@ expected: green - name: 2d.drawImage.nonexistent - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] images: - not-found-at-all.png code: | @@ -159,7 +159,7 @@ - name: 2d.drawImage.zerocanvas desc: drawImage with zero-sized canvas as the source shoud throw exception - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | var canvas2 = document.createElement('canvas'); canvas2.width = 0; @@ -176,7 +176,7 @@ - name: 2d.drawImage.animated.gif desc: drawImage() of an animated GIF draws the first frame - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] images: - anim-gr.gif code: | @@ -330,7 +330,7 @@ @assert pixel 99,49 ==~ 0,255,0,255; - name: 2d.drawImage.canvas - canvasType: ['OffscreenCanvas', 'Worker'] + canvas_types: ['OffscreenCanvas', 'Worker'] code: | var offscreenCanvas2 = new OffscreenCanvas(100, 50); var ctx2 = offscreenCanvas2.getContext('2d'); @@ -344,7 +344,7 @@ @assert pixel 99,49 ==~ 0,255,0,255; - name: 2d.drawImage.zerocanvas - canvasType: ['OffscreenCanvas', 'Worker'] + canvas_types: ['OffscreenCanvas', 'Worker'] code: | var offscreenCanvas2 = new OffscreenCanvas(0, 10); @assert throws INVALID_STATE_ERR ctx.drawImage(offscreenCanvas2, 0, 0); @@ -385,7 +385,7 @@ - name: 2d.drawImage.zerosource.image desc: drawImage with zero-sized source rectangle from image draws nothing without exception test_type: promise - canvasType: ['HTMLCanvas', 'OffscreenCanvas'] + canvas_types: ['HtmlCanvas', 'OffscreenCanvas'] code: | ctx.fillStyle = '#0f0'; ctx.fillRect(0, 0, 100, 50); @@ -483,7 +483,7 @@ - name: 2d.drawImage.outsidesource DISABLED: fix this to match the current spec (transparent black outside source) - canvasType: ['OffscreenCanvas', 'Worker'] + canvas_types: ['OffscreenCanvas', 'Worker'] code: | const response_red = await fetch('/images/red.png'); const blob_red = await response_red.blob(); @@ -518,7 +518,7 @@ - name: 2d.drawImage.svg desc: drawImage() of an SVG image test_type: promise - canvasType: ['HTMLCanvas', 'OffscreenCanvas'] + canvas_types: ['HtmlCanvas', 'OffscreenCanvas'] code: | const img = new Image(); const imageLoadPromise = new Promise((resolve, reject) => { @@ -641,7 +641,7 @@ - name: 2d.drawImage.detachedcanvas desc: drawImage with detached OffscreenCanvas as the source should throw exception - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | var canvas2 = new OffscreenCanvas(80, 80); (new MessageChannel()).port1.postMessage(canvas2, [canvas2]); diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-rectangles-to-the-canvas.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-rectangles-to-the-canvas.yaml index 408e932abe..cb7ae0d858 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-rectangles-to-the-canvas.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/drawing-rectangles-to-the-canvas.yaml @@ -375,8 +375,7 @@ - name: 2d.fillStyle.colorObject desc: ctx.fillStyle works with color objects - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | ctx.fillStyle = {r: 1, g: 0, b: 0}; ctx.fillRect(0, 0, 100, 50); @@ -400,8 +399,7 @@ - name: 2d.fillStyle.colorObject.transparency desc: ctx.fillStyle with color objects has transparency - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | ctx.fillStyle = {r: 0, g: 1, b: 0, a: 0}; ctx.fillRect(0, 0, 100, 50); @@ -422,8 +420,7 @@ - name: 2d.strokeStyle.colorObject desc: ctx.strokeStyle works with color objects - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | ctx.lineWidth = 50; ctx.strokeStyle = {r: 1, g: 0, b: 0}; @@ -448,8 +445,7 @@ - name: 2d.strokeStyle.colorObject.transparency desc: ctx.strokeStyle with color objects has transparency - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | ctx.lineWidth = 50; ctx.strokeStyle = {r: 0, g: 1, b: 0, a: 0}; diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml index c992af6e7c..72d027a634 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/fill-and-stroke-styles.yaml @@ -1,6 +1,6 @@ - name: 2d.fillStyle.parse.current.basic desc: currentColor is computed from the canvas element - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | canvas.setAttribute('style', 'color: #0f0'); ctx.fillStyle = '#f00'; @@ -11,7 +11,7 @@ - name: 2d.fillStyle.parse.current.changed desc: currentColor is computed when the attribute is set, not when it is painted - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | canvas.setAttribute('style', 'color: #0f0'); ctx.fillStyle = '#f00'; @@ -23,7 +23,7 @@ - name: 2d.fillStyle.parse.current.removed desc: currentColor is solid black when the canvas element is not in a document - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | // Try not to let it undetectably incorrectly pick up opaque-black // from other parts of the document: @@ -119,7 +119,7 @@ desc: window.CanvasGradient exists and has the right properties notes: &bindings Defined in "Web IDL" (draft) code: | - {% set root = 'self' if canvas_type == 'worker' else 'window' %} + {% set root = 'self' if canvas_type == 'Worker' else 'window' %} @assert {{ root }}.CanvasGradient !== undefined; @assert {{ root }}.CanvasGradient.prototype.addColorStop !== undefined; @@ -127,7 +127,7 @@ desc: createLinearGradient() and createRadialGradient() returns objects implementing CanvasGradient code: | - {% set root = 'self' if canvas_type == 'worker' else 'window' %} + {% set root = 'self' if canvas_type == 'Worker' else 'window' %} {{ root }}.CanvasGradient.prototype.thisImplementsCanvasGradient = true; var g1 = ctx.createLinearGradient(0, 0, 100, 0); @@ -483,16 +483,17 @@ ctx.fillRect(0, 0, 100, 50); @assert pixel 50,25 ==~ 0,255,0,255; expected: green + append_variants_to_name: false variants: - _HtmlCanvas: - canvasType: ['HtmlCanvas'] + - HtmlCanvas: + canvas_types: ['HtmlCanvas'] create_canvas: document.createElement('canvas') - _OffscreenCanvas: - canvasType: ['OffscreenCanvas', 'Worker'] + OffscreenCanvas: + canvas_types: ['OffscreenCanvas', 'Worker'] create_canvas: new OffscreenCanvas(100, 50) - name: 2d.gradient.object.current - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | canvas.setAttribute('style', 'color: #f00'); @@ -1126,7 +1127,7 @@ images: - green.png code: | - {% set root = 'self' if canvas_type == 'worker' else 'window' %} + {% set root = 'self' if canvas_type == 'Worker' else 'window' %} @assert {{ root }}.CanvasPattern !== undefined; {{ root }}.CanvasPattern.prototype.thisImplementsCanvasPattern = true; @@ -1135,11 +1136,13 @@ var pattern = ctx.createPattern(img, 'no-repeat'); @assert pattern.thisImplementsCanvasPattern; variants: &load-image-variant-definition - _HtmlCanvas: - canvasType: ['HtmlCanvas'] + - HtmlCanvas: + append_variants_to_name: false + canvas_types: ['HtmlCanvas'] load_image: var img = document.getElementById('{{ (images or svgimages)[0] }}'); - _OffscreenCanvas: - canvasType: ['OffscreenCanvas', 'Worker'] + OffscreenCanvas: + append_variants_to_name: false + canvas_types: ['OffscreenCanvas', 'Worker'] test_type: promise load_image: |- var response = await fetch('/images/{{ (images or svgimages)[0] }}') @@ -1189,14 +1192,16 @@ @assert pixel 98,48 == 0,255,0,255; expected: green variants: &create-canvas2-variant-definition - _HtmlCanvas: - canvasType: ['HtmlCanvas'] + - HtmlCanvas: + append_variants_to_name: false + canvas_types: ['HtmlCanvas'] create_canvas2: |- var canvas2 = document.createElement('canvas'); canvas2.width = 100; canvas2.height = 50; - _OffscreenCanvas: - canvasType: ['OffscreenCanvas', 'Worker'] + OffscreenCanvas: + append_variants_to_name: false + canvas_types: ['OffscreenCanvas', 'Worker'] create_canvas2: |- var canvas2 = new OffscreenCanvas(100, 50); @@ -1299,13 +1304,13 @@ @assert throws TypeError ctx.createPattern('../images/red.png', 'repeat'); - name: 2d.pattern.image.incomplete.nosrc - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | var img = new Image(); @assert ctx.createPattern(img, 'repeat') === null; - name: 2d.pattern.image.incomplete.immediate - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] images: - red.png code: | @@ -1318,7 +1323,7 @@ @assert ctx.createPattern(img, 'repeat') === null; @moz-todo - name: 2d.pattern.image.incomplete.reload - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] images: - yellow.png - red.png @@ -1333,7 +1338,7 @@ @assert ctx.createPattern(img, 'repeat') === null; @moz-todo - name: 2d.pattern.image.incomplete.emptysrc - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] images: - red.png mozilla: {throws: !!null ''} @@ -1343,7 +1348,7 @@ @assert ctx.createPattern(img, 'repeat') === null; - name: 2d.pattern.image.incomplete.removedsrc - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] images: - red.png mozilla: {throws: !!null ''} @@ -1353,7 +1358,7 @@ @assert ctx.createPattern(img, 'repeat') === null; - name: 2d.pattern.image.broken - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] images: - broken.png code: | @@ -1361,7 +1366,7 @@ @assert throws INVALID_STATE_ERR ctx.createPattern(img, 'repeat'); - name: 2d.pattern.image.nonexistent - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] images: - no-such-image-really.png code: | @@ -1369,7 +1374,7 @@ @assert throws INVALID_STATE_ERR ctx.createPattern(img, 'repeat'); - name: 2d.pattern.svgimage.nonexistent - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] svgimages: - no-such-image-really.png code: | @@ -1377,7 +1382,7 @@ @assert throws INVALID_STATE_ERR ctx.createPattern(img, 'repeat'); - name: 2d.pattern.image.nonexistent-but-loading - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | var img = document.createElement("img"); img.src = "/images/no-such-image-really.png"; @@ -1387,7 +1392,7 @@ @assert ctx.createPattern(img, 'repeat') === null; - name: 2d.pattern.image.nosrc - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | var img = document.createElement("img"); @assert ctx.createPattern(img, 'repeat') === null; @@ -1395,7 +1400,7 @@ @assert ctx.createPattern(img, 'repeat') === null; - name: 2d.pattern.image.zerowidth - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] images: - red-zerowidth.svg code: | @@ -1403,7 +1408,7 @@ @assert ctx.createPattern(img, 'repeat') === null; - name: 2d.pattern.image.zeroheight - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] images: - red-zeroheight.svg code: | @@ -1411,7 +1416,7 @@ @assert ctx.createPattern(img, 'repeat') === null; - name: 2d.pattern.svgimage.zerowidth - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] svgimages: - red-zerowidth.svg code: | @@ -1419,7 +1424,7 @@ @assert ctx.createPattern(img, 'repeat') === null; - name: 2d.pattern.svgimage.zeroheight - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] svgimages: - red-zeroheight.svg code: | @@ -1469,7 +1474,7 @@ @assert throws SYNTAX_ERR ctx.createPattern(canvas, "repeat\0"); - name: 2d.pattern.modify.image1 - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] images: - green.png code: | @@ -1490,7 +1495,7 @@ expected: green - name: 2d.pattern.modify.image2 - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] images: - green.png code: | @@ -1515,7 +1520,7 @@ expected: green - name: 2d.pattern.modify.canvas1 - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | {{ create_canvas2 }} var ctx2 = canvas2.getContext('2d'); @@ -1577,13 +1582,14 @@ @assert pixel 50,25 == 0,255,0,255; expected: green + append_variants_to_name: false variants: - _HtmlCanvas: - canvasType: ['HtmlCanvas'] + - HtmlCanvas: + canvas_types: ['HtmlCanvas'] load_image: var img = document.getElementById('{{ images[0] }}'); create_canvas: document.createElement('canvas') - _OffscreenCanvas: - canvasType: ['OffscreenCanvas', 'Worker'] + OffscreenCanvas: + canvas_types: ['OffscreenCanvas', 'Worker'] test_type: promise load_image: |- var response = await fetch('/images/{{ images[0] }}') @@ -1987,7 +1993,7 @@ - name: 2d.pattern.animated.gif desc: createPattern() of an animated GIF draws the first frame - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] images: - anim-gr.gif code: | @@ -2006,7 +2012,7 @@ - name: 2d.fillStyle.CSSRGB desc: CSSRGB works as color input - canvasType: ['HtmlCanvas', 'OffscreenCanvas'] + canvas_types: ['HtmlCanvas', 'OffscreenCanvas'] code: | ctx.fillStyle = new CSSRGB(1, 0, 1); @assert ctx.fillStyle === '#ff00ff'; @@ -2046,7 +2052,7 @@ - name: 2d.fillStyle.CSSHSL desc: CSSHSL works as color input - canvasType: ['HtmlCanvas', 'OffscreenCanvas'] + canvas_types: ['HtmlCanvas', 'OffscreenCanvas'] code: | ctx.fillStyle = new CSSHSL(CSS.deg(180), 0.5, 0.5); ctx.fillRect(0, 0, 100, 50); @@ -2077,7 +2083,7 @@ - name: 2d.fillStyle.colormix desc: color-mix works as color input - canvasType: ['HtmlCanvas', 'OffscreenCanvas', 'Worker'] + canvas_types: ['HtmlCanvas', 'OffscreenCanvas', 'Worker'] code: | ctx.fillStyle = "color-mix(in srgb, red, blue)"; @assert ctx.fillStyle === 'color(srgb 0.5 0 0.5)'; @@ -2086,7 +2092,7 @@ - name: 2d.fillStyle.colormix.currentcolor desc: color-mix works as color input with currentcolor - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | canvas.setAttribute('style', 'color: magenta'); ctx.fillStyle = "color-mix(in srgb, black, currentcolor)"; @@ -2096,9 +2102,22 @@ - name: 2d.strokeStyle.colormix desc: color-mix works as color input - canvasType: ['HtmlCanvas', 'OffscreenCanvas'] + canvas_types: ['HtmlCanvas', 'OffscreenCanvas'] code: | ctx.strokeStyle = "color-mix(in srgb, red, blue)"; @assert ctx.strokeStyle === 'color(srgb 0.5 0 0.5)'; ctx.strokeStyle = "color-mix(in srgb, red, color(srgb 1 0 0))"; @assert ctx.strokeStyle === 'color(srgb 1 0 0)'; + +- name: 2d.gradient.colormix + desc: color-mix works as CanvasGradient color input + canvas_types: ['HtmlCanvas'] + code: | + var g = ctx.createLinearGradient(0, 0, 100, 0); + g.addColorStop(0, '#f00'); + g.addColorStop(1, 'color-mix(in srgb, #0f0, #00f)'); + ctx.fillStyle = g; + ctx.fillRect(0, 0, 100, 50); + @assert pixel 25,25 ==~ 212,81,61,255 +/- 3; + @assert pixel 50,25 ==~ 167,106,88,255 +/- 3; + @assert pixel 75,25 ==~ 113,120,109,255 +/- 3; diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml index f327b9fe94..1ce9d8ed74 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/filters.yaml @@ -34,8 +34,7 @@ - name: 2d.filter.canvasFilterObject.tentative desc: Test CanvasFilter() object - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | @assert ctx.filter == 'none'; ctx.filter = 'blur(5px)'; @@ -63,7 +62,7 @@ - name: 2d.filter.canvasFilterObject.tentative desc: Test CanvasFilter() object - canvasType: ['OffscreenCanvas', 'Worker'] + canvas_types: ['OffscreenCanvas', 'Worker'] code: | @assert ctx.filter == 'none'; ctx.filter = 'blur(5px)'; @@ -106,7 +105,7 @@ "{name: 'gaussianBlur', stdDeviation: {}}") }}; append_variants_to_name: false variants: - layers: + - layers: filter_declaration: |- ctx.beginLayer({filter: param}) @@ -199,7 +198,7 @@ @assert pixel 60,30 ==~ 0,255,0,255; append_variants_to_name: false variants: - layers: + - layers: filter_declaration: |- ctx.beginLayer({filter: param}) @@ -251,7 +250,7 @@ {{ close_layer -}} append_variants_to_name: false variants: - layers: + - layers: filter_declaration: |- ctx.beginLayer({filter: param}) @@ -325,7 +324,7 @@ {{ close_layer }} append_variants_to_name: false variants: - layers: + - layers: filter_declaration: |- ctx.beginLayer({filter: param}) close_layer: ctx.endLayer(); @@ -376,7 +375,7 @@ } append_variants_to_name: false variants: - layers: + - layers: filter_declaration: |- ctx.beginLayer({filter: param}) close_layer: ctx.endLayer(); @@ -451,7 +450,7 @@ } append_variants_to_name: false variants: - layers: + - layers: filter_declaration: |- ctx.beginLayer({filter: param}) close_layer: ctx.endLayer(); @@ -530,7 +529,7 @@ } append_variants_to_name: false variants: - layers: + - layers: filter_declaration: |- ctx.beginLayer({filter: param}) close_layer: ctx.endLayer(); @@ -608,7 +607,7 @@ } append_variants_to_name: false variants: - layers: + - layers: filter_declaration: |- ctx.beginLayer({filter: param}) close_layer: ctx.endLayer(); @@ -641,30 +640,30 @@ fill="teal" filter="url(#blur)" /> </svg> append_variants_to_name: false - variant_matrix: - - layers: - filter_declaration: |- - ctx.beginLayer({filter: param}); - close_layer: ctx.endLayer(); - canvasFilterObject: - filter_declaration: |- - ctx.filter = new CanvasFilter(param); - tentative: .tentative - - x-only: - blur_x: 4 - blur_y: 0 - mostly-x: - blur_x: 4 - blur_y: 1 - isotropic: - blur_x: 4 - blur_y: 4 - mostly-y: - blur_x: 1 - blur_y: 4 - y-only: - blur_x: 0 - blur_y: 4 + variants: + - layers: + filter_declaration: |- + ctx.beginLayer({filter: param}); + close_layer: ctx.endLayer(); + canvasFilterObject: + filter_declaration: |- + ctx.filter = new CanvasFilter(param); + tentative: .tentative + - x-only: + blur_x: 4 + blur_y: 0 + mostly-x: + blur_x: 4 + blur_y: 1 + isotropic: + blur_x: 4 + blur_y: 4 + mostly-y: + blur_x: 1 + blur_y: 4 + y-only: + blur_x: 0 + blur_y: 4 - name: 2d.filter.{{ variant_names[0] }}.dropShadow{{ tentative }} desc: Test CanvasFilter() dropShadow object. @@ -818,7 +817,7 @@ </svg> append_variants_to_name: false variants: - layers: + - layers: filter_declaration: |- ctx.beginLayer({filter: param}); close_layer: | @@ -838,10 +837,6 @@ <10 | -1 | 0.5 | null | true | false | [] | [20] | '30'>}") }}; @unroll {{ filter_declaration | replace("param", "{\- name: 'dropShadow', \- - <dx | dy | floodOpacity>: \- - <10 | -1 | 0.5 | null | true | false | [] | [20] | '30'>}") }}; - @unroll {{ filter_declaration | replace("param", "{\- - name: 'dropShadow', \- <stdDeviation>: \- <10 | -1 | 0.5 | null | true | false | [] | [20] | '30' | \- [10, -1] | [0.5, null] | [true, false] | [[], [20]] | \- @@ -869,7 +864,7 @@ <'test' | 'rgba(NaN, 3, 2, 1)' | 10 | undefined | null | NaN>}") }}; append_variants_to_name: false variants: - layers: + - layers: filter_declaration: |- ctx.beginLayer({filter: param}); ctx.endLayer() @@ -990,7 +985,7 @@ } append_variants_to_name: false variants: - layers: + - layers: filter_declaration: |- ctx.beginLayer({filter: param}) close_layer: "\n ctx.endLayer();" diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml index 437a70c3f7..d1e9a97043 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/layers.yaml @@ -41,7 +41,8 @@ ctx2.fillRect(70, 70, 75, 50); ctx.drawImage(canvas2, 0, 0); - variants: &global-state-variants + variants: + - &global-state-variants no-global-states: render_states: // No global states. alpha: &global-state-alpha @@ -168,7 +169,7 @@ }; img.src = 'data:image/svg+xml;base64,' + btoa(svg); variants: - <<: *global-state-variants + - <<: *global-state-variants alpha: <<: *global-state-alpha fuzzy: maxDifference=0-2; totalPixels=0-6766 @@ -287,6 +288,7 @@ - name: 2d.layer.layer-rendering-state-reset-in-layer desc: Tests that layers ignore the global context filter. + test_type: sync code: | ctx.globalAlpha = 0.5; ctx.globalCompositeOperation = 'xor'; @@ -556,7 +558,7 @@ - name: 2d.layer.flush-on-frame-presentation desc: Check that layers state stack is flushed and rebuilt on frame renders. size: [200, 200] - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] test_type: "promise" code: | ctx.fillStyle = 'purple'; @@ -612,7 +614,7 @@ assert_throws_dom("InvalidStateError", () => {{ operation }}); variants: - createPattern: + - createPattern: operation: ctx.createPattern(canvas, 'repeat') drawImage: setup: |- @@ -630,10 +632,10 @@ operation: |- ctx.putImageData(data, 0, 0) toDataURL: - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] operation: canvas.toDataURL() transferToImageBitmap: - canvasType: ['OffscreenCanvas', 'Worker'] + canvas_types: ['OffscreenCanvas', 'Worker'] operation: canvas.transferToImageBitmap() - name: 2d.layer.malformed-operations-with-promises @@ -651,14 +653,14 @@ ctx.beginLayer(); await promise_rejects_dom(t, 'InvalidStateError', {{ operation }}); variants: - convertToBlob: - canvasType: ['OffscreenCanvas', 'Worker'] + - convertToBlob: + canvas_types: ['OffscreenCanvas', 'Worker'] operation: |- canvas.convertToBlob() createImageBitmap: operation: createImageBitmap(canvas) toBlob: - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] operation: |- new Promise(resolve => canvas.toBlob(resolve)) @@ -824,7 +826,7 @@ - name: 2d.layer.valid-calls desc: No exception raised on {{ variant_desc }}. variants: - save: + - save: variant_desc: lone save() calls code: ctx.save(); beginLayer: @@ -867,7 +869,7 @@ {{ call_sequence | indent(2) }} }); variants: - endLayer: + - endLayer: variant_desc: lone endLayer calls call_sequence: ctx.endLayer(); save-endLayer: @@ -995,7 +997,7 @@ }; img.src = 'data:image/svg+xml;base64,' + btoa(svg); variants: - no-clipping: + - no-clipping: clipping: // No clipping. with-clipping: clipping: |- @@ -1029,7 +1031,7 @@ ctx.fillStyle = 'green'; ctx.fillRect(0, 0, 100, 100); variants: - short-distance: + - short-distance: distance: |- const delta = 1; clipping: // No clipping. @@ -1056,7 +1058,7 @@ desc: Checks that layer blending works inside opaque canvas size: [300, 300] code: | - {% if canvas_type == 'htmlcanvas' %} + {% if canvas_type == 'HtmlCanvas' %} const canvas2 = document.createElement('canvas'); canvas2.width = 200; canvas2.height = 200; @@ -1126,7 +1128,7 @@ </g> </svg> variants: - blur: + - blur: ctx_filter: |- 'blur(10px)' svg_filter: |- @@ -1149,7 +1151,7 @@ code: *filter-test-code html_reference: *filter-test-reference variants: - x-only: + - x-only: ctx_filter: |- { name: 'gaussianBlur', stdDeviation: [4, 0] } svg_filter: |- diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/pixel-manipulation.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/pixel-manipulation.yaml index 0643b047b1..6721f5ffde 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/pixel-manipulation.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/pixel-manipulation.yaml @@ -11,7 +11,7 @@ - name: 2d.imageData.create2.type desc: createImageData(sw, sh) returns an ImageData object containing a Uint8ClampedArray object - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | @assert window.ImageData !== undefined; @assert window.Uint8ClampedArray !== undefined; @@ -24,7 +24,7 @@ - name: 2d.imageData.create1.type desc: createImageData(imgdata) returns an ImageData object containing a Uint8ClampedArray object - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | @assert window.ImageData !== undefined; @assert window.Uint8ClampedArray !== undefined; @@ -36,7 +36,7 @@ - name: 2d.imageData.create2.this desc: createImageData(sw, sh) should throw when called with the wrong |this| - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] notes: &bindings Defined in "Web IDL" (draft) code: | @assert throws TypeError CanvasRenderingContext2D.prototype.createImageData.call(null, 1, 1); @moz-todo @@ -45,7 +45,7 @@ - name: 2d.imageData.create1.this desc: createImageData(imgdata) should throw when called with the wrong |this| - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] notes: *bindings code: | var imgdata = ctx.createImageData(1, 1); @@ -160,7 +160,7 @@ - name: 2d.imageData.create.and.resize desc: Verify no crash when resizing an image bitmap to zero. - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] images: - red.png code: | @@ -179,7 +179,7 @@ @assert ctx.getImageData(0, 0, 100, 50) !== null; - name: 2d.imageData.get.type - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] desc: getImageData() returns an ImageData object containing a Uint8ClampedArray object code: | @@ -502,7 +502,7 @@ @assert imgdata.data[3] === 0; - name: 2d.imageData.object.ctor.size - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] desc: ImageData has a usable constructor code: | @assert window.ImageData !== undefined; @@ -516,7 +516,7 @@ } - name: 2d.imageData.object.ctor.basics - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] desc: Testing different type of ImageData constructor code: | function setRGBA(imageData, i, rgba) @@ -608,7 +608,7 @@ - name: 2d.imageData.object.ctor.array desc: ImageData has a usable constructor - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | @assert window.ImageData !== undefined; @@ -620,7 +620,7 @@ - name: 2d.imageData.object.ctor.array.bounds desc: ImageData has a usable constructor - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | @assert window.ImageData !== undefined; @@ -803,7 +803,7 @@ - name: 2d.imageData.put.cross desc: putImageData() accepts image data got from a different canvas - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | var canvas2 = document.createElement('canvas'); var ctx2 = canvas2.getContext('2d'); @@ -818,7 +818,7 @@ - name: 2d.imageData.put.cross desc: putImageData() accepts image data got from a different canvas - canvasType: ['OffscreenCanvas', 'Worker'] + canvas_types: ['OffscreenCanvas', 'Worker'] code: | var offscreenCanvas2 = new OffscreenCanvas(100, 50); var ctx2 = offscreenCanvas2.getContext('2d'); diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/reset.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/reset.yaml index 086fb04e04..27764e4550 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/reset.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/reset.yaml @@ -24,7 +24,7 @@ @assert ctx.{{ state_name }} == default_value; variants: - letter_spacing: + - letter_spacing: state_name: letterSpacing new_value: "'12px'" diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/scroll.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/scroll.yaml index dd088aa396..0579a8b3d7 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/scroll.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/scroll.yaml @@ -1,7 +1,6 @@ - name: 2d.scrollPathIntoView.basic desc: scrollPathIntoView() works - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | var div = document.createElement('div'); div.style.cssText = 'width: 200vw; height: 200vh'; @@ -18,8 +17,7 @@ - name: 2d.scrollPathIntoView.verticalLR desc: scrollPathIntoView() works in vertical-lr writing mode - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | document.documentElement.style.cssText = 'writing-mode: vertical-lr'; var div = document.createElement('div'); @@ -37,8 +35,7 @@ - name: 2d.scrollPathIntoView.verticalRL desc: scrollPathIntoView() works in vertical-rl writing mode - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | document.documentElement.style.cssText = 'writing-mode: vertical-rl'; var div = document.createElement('div'); @@ -58,8 +55,7 @@ - name: 2d.scrollPathIntoView.path desc: scrollPathIntoView() with path argument works - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | var div = document.createElement('div'); div.style.cssText = 'width: 200vw; height: 200vh'; @@ -73,4 +69,3 @@ var rect = canvas.getBoundingClientRect(); @assert Math.round(rect.top) === -8; @assert Math.round(rect.left) === 200; - diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/shadows.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/shadows.yaml index 953ab2c555..e42237dadb 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/shadows.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/shadows.yaml @@ -136,7 +136,7 @@ - name: 2d.shadow.attributes.shadowColor.current.basic desc: currentColor is computed from the canvas element - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | canvas.style.color = '#0f0'; ctx.shadowColor = 'currentColor'; @@ -144,7 +144,7 @@ - name: 2d.shadow.attributes.shadowColor.current.changed desc: currentColor is computed when the attribute is set, not when it is painted - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | canvas.style.color = '#0f0'; ctx.shadowColor = 'currentColor'; @@ -153,7 +153,7 @@ - name: 2d.shadow.attributes.shadowColor.current.removed desc: currentColor is solid black when the canvas element is not in a document - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | // Try not to let it undetectably incorrectly pick up opaque-black // from other parts of the document: @@ -518,11 +518,13 @@ @assert pixel 50,25 == 0,255,0,255; expected: green variants: &load-image-variant-definition - _HtmlCanvas: - canvasType: ['HtmlCanvas'] + - HtmlCanvas: + append_variants_to_name: false + canvas_types: ['HtmlCanvas'] load_image: var img = document.getElementById('{{ images[0] }}'); - _OffscreenCanvas: - canvasType: ['OffscreenCanvas', 'Worker'] + OffscreenCanvas: + append_variants_to_name: false + canvas_types: ['OffscreenCanvas', 'Worker'] test_type: promise load_image: |- var response = await fetch('/images/{{ images[0] }}') @@ -640,14 +642,16 @@ @assert pixel 50,25 == 0,255,0,255; expected: green variants: &create-canvas2-variant-definition - _HtmlCanvas: - canvasType: ['HtmlCanvas'] + - HtmlCanvas: + append_variants_to_name: false + canvas_types: ['HtmlCanvas'] create_canvas2: |- var canvas2 = document.createElement('canvas'); canvas2.width = 100; canvas2.height = 50; - _OffscreenCanvas: - canvasType: ['OffscreenCanvas', 'Worker'] + OffscreenCanvas: + append_variants_to_name: false + canvas_types: ['OffscreenCanvas', 'Worker'] create_canvas2: |- var canvas2 = new OffscreenCanvas(100, 50); diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/text.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/text.yaml index ca945c2953..7f2047bc9c 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/text.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/text.yaml @@ -38,7 +38,7 @@ - name: 2d.text.font.parse.size.percentage canvas: 'style="font-size: 144px"' - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | ctx.font = '50% serif'; @assert ctx.font === '72px serif'; @moz-todo @@ -46,7 +46,7 @@ @assert ctx.font === '72px serif'; @moz-todo - name: 2d.text.font.parse.size.percentage.default - canvasType: ['HtmlCanvas'] + canvas_types: ['HtmlCanvas'] code: | var canvas2 = document.createElement('canvas'); var ctx2 = canvas2.getContext('2d'); @@ -113,7 +113,7 @@ @assert ctx.font === '10px sans-serif'; - name: 2d.text.font.relative_size - canvasType: ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | var canvas2 = document.createElement('canvas'); var ctx2 = canvas2.getContext('2d'); @@ -121,7 +121,7 @@ @assert ctx2.font === '10px sans-serif'; - name: 2d.text.font.relative_size - canvasType: ['OffscreenCanvas', 'Worker'] + canvas_types: ['OffscreenCanvas', 'Worker'] code: | ctx.font = '1em sans-serif'; @assert ctx.font === '10px sans-serif'; @@ -236,16 +236,18 @@ @assert pixel 95,45 ==~ 0,255,0,255; expected: green variants: &load-font-variant-definition - _HtmlCanvas: - canvasType: ['HtmlCanvas'] + - HtmlCanvas: + append_variants_to_name: false + canvas_types: ['HtmlCanvas'] load_font: |- await document.fonts.ready; - _OffscreenCanvas: - canvasType: ['OffscreenCanvas', 'Worker'] + OffscreenCanvas: + append_variants_to_name: false + canvas_types: ['OffscreenCanvas', 'Worker'] load_font: |- var f = new FontFace("{{ fonts[0] }}", "url('/fonts/{{ fonts[0] }}.ttf')"); f.load(); - {% set root = 'self' if canvas_type == 'worker' else 'document' %} + {% set root = 'self' if canvas_type == 'Worker' else 'document' %} {{ root }}.fonts.add(f); await {{ root }}.fonts.ready; @@ -813,7 +815,7 @@ code: | {{ load_font }} ctx.font = '50px CanvasTest'; - {% if canvas_type != 'htmlcanvas' %} + {% if canvas_type != 'HtmlCanvas' %} ctx.direction = 'ltr'; {% endif %} ctx.fillStyle = '#f00'; @@ -839,7 +841,7 @@ code: | {{ load_font }} ctx.font = '50px CanvasTest'; - {% if canvas_type != 'htmlcanvas' %} + {% if canvas_type != 'HtmlCanvas' %} ctx.direction = 'rtl'; {% endif %} ctx.fillStyle = '#f00'; @@ -865,7 +867,7 @@ code: | {{ load_font }} ctx.font = '50px CanvasTest'; - {% if canvas_type != 'htmlcanvas' %} + {% if canvas_type != 'HtmlCanvas' %} ctx.direction = 'ltr'; {% endif %} ctx.fillStyle = '#f00'; @@ -891,7 +893,7 @@ code: | {{ load_font }} ctx.font = '50px CanvasTest'; - {% if canvas_type != 'htmlcanvas' %} + {% if canvas_type != 'HtmlCanvas' %} ctx.direction = 'rtl'; {% endif %} ctx.fillStyle = '#f00'; diff --git a/testing/web-platform/tests/html/canvas/tools/yaml-new/video.yaml b/testing/web-platform/tests/html/canvas/tools/yaml-new/video.yaml index f9b48fb8da..ca8a1ab816 100644 --- a/testing/web-platform/tests/html/canvas/tools/yaml-new/video.yaml +++ b/testing/web-platform/tests/html/canvas/tools/yaml-new/video.yaml @@ -1,7 +1,6 @@ - name: 2d.video.invalid desc: Verify test doesn't crash with invalid video. - canvasType: - ['HTMLCanvas'] + canvas_types: ['HtmlCanvas'] code: | var v = document.createElement('video'); v.play(); diff --git a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-assorted.window.js b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-assorted.window.js index 2017269f0b..93f798e600 100644 --- a/testing/web-platform/tests/html/dom/elements/global-attributes/dir-assorted.window.js +++ b/testing/web-platform/tests/html/dom/elements/global-attributes/dir-assorted.window.js @@ -64,6 +64,39 @@ test(() => { assert_true(ele2.matches(":dir(ltr)"), "child is LTR after change"); }, "Non-HTML element text contents influence dir=auto"); + +for (const tag of ["style", "script"]) { + test(() => { + const e1 = document.createElement("div"); + e1.dir = "auto"; + + const e2 = document.createElement(tag); + const node = document.createTextNode("\u05D0"); + e2.appendChild(node); + e1.appendChild(e2); + assert_true(e1.matches(":dir(ltr)", "is LTR before change")); + node.data = "ABC"; + assert_true(e1.matches(":dir(ltr)", "is LTR after change")); + + }, `${tag} element text contents do not influence dir=auto`); +} + +for (const tag of ["style", "script", "input", "textarea"]) { + test(() => { + const e1 = document.createElement("div"); + e1.dir = "auto"; + const svg = document.createElement("svg"); + const e2 = document.createElementNS("http://www.w3.org/2000/svg", tag); + const node = document.createTextNode("\u05D0"); + e2.appendChild(node); + svg.appendChild(e2); + e1.appendChild(svg); + assert_true(e1.matches(":dir(rtl)", "is RTL before change")); + node.data = "ABC"; + assert_true(e1.matches(":dir(ltr)", "is LTR after change")); + }, `non-html ${tag} element text contents influence dir=auto`); +} + test(() => { const e1 = document.createElement("div"); e1.dir = "auto"; diff --git a/testing/web-platform/tests/html/dom/historical.html b/testing/web-platform/tests/html/dom/historical.html index 396e57a391..2563810492 100644 --- a/testing/web-platform/tests/html/dom/historical.html +++ b/testing/web-platform/tests/html/dom/historical.html @@ -52,4 +52,9 @@ test(() => { test(() => { assert_false("HTMLTableHeaderCellElement" in window); }, "HTMLTableHeaderCellElement interface is removed") + +// removed in https://github.com/whatwg/html/commit/6e4bcf5630d08e03212ad4e1a3c78beecf2a92fa +test(() => { + assert_false("initHashChangeEvent" in HashChangeEvent.prototype); +}, "HashChangeEvent's initHashChangeEvent method is removed") </script> diff --git a/testing/web-platform/tests/html/dom/idlharness.https.html b/testing/web-platform/tests/html/dom/idlharness.https.html index 7d693d3c0a..357a4fbe0b 100644 --- a/testing/web-platform/tests/html/dom/idlharness.https.html +++ b/testing/web-platform/tests/html/dom/idlharness.https.html @@ -38,7 +38,7 @@ const waitForLoad = new Promise(resolve => { addEventListener('load', resolve); idl_test( ['html'], - ['wai-aria', 'SVG', 'cssom', 'touch-events', 'uievents', 'dom', 'xhr', 'FileAPI', 'mediacapture-streams', 'performance-timeline'], + ['wai-aria', 'SVG', 'cssom', 'touch-events', 'uievents', 'dom', 'xhr', 'FileAPI', 'mediacapture-streams', 'performance-timeline', 'trusted-types'], async idlArray => { self.documentWithHandlers = new Document(); const handler = function(e) {}; diff --git a/testing/web-platform/tests/html/editing/dnd/drop/events-contenteditable-manual.tentative.html b/testing/web-platform/tests/html/editing/dnd/drop/events-contenteditable-manual.tentative.html new file mode 100644 index 0000000000..9e513eb836 --- /dev/null +++ b/testing/web-platform/tests/html/editing/dnd/drop/events-contenteditable-manual.tentative.html @@ -0,0 +1,19 @@ +<!doctype html> +<meta charset=utf-8> +<title>Selection drag and drop: events for contenteditable</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> +[data-placeholder]:empty::before { content: attr(data-placeholder); } +</style> +<body data-expected-events=" + b:drop:, + a:beforeinput:deleteByDrag, + a:input:deleteByDrag, + b:beforeinput:insertFromDrop, + b:textInput:, + b:input:insertFromDrop"> +<div><input id=a data-select="0,7" value="Drag me"></div> +<div contenteditable=true id=b data-placeholder="...to here"></div> +<script src="/uievents/textInput/support/common.js"></script> +<script src="support/events.js"></script> diff --git a/testing/web-platform/tests/html/editing/dnd/drop/events-contenteditable-same-element-manual.tentative.html b/testing/web-platform/tests/html/editing/dnd/drop/events-contenteditable-same-element-manual.tentative.html new file mode 100644 index 0000000000..907306301f --- /dev/null +++ b/testing/web-platform/tests/html/editing/dnd/drop/events-contenteditable-same-element-manual.tentative.html @@ -0,0 +1,15 @@ +<!doctype html> +<meta charset=utf-8> +<title>Selection drag and drop: events for contenteditable (same element)</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body data-expected-events=" + b:drop:, + b:beforeinput:deleteByDrag, + b:input:deleteByDrag, + b:beforeinput:insertFromDrop, + b:textInput:, + b:input:insertFromDrop"> +<div contenteditable=true id=b data-select="0,7">Drag me ...to here:</div> +<script src="/uievents/textInput/support/common.js"></script> +<script src="support/events.js"></script> diff --git a/testing/web-platform/tests/html/editing/dnd/drop/events-input-manual.tentative.html b/testing/web-platform/tests/html/editing/dnd/drop/events-input-manual.tentative.html new file mode 100644 index 0000000000..2f9914cca9 --- /dev/null +++ b/testing/web-platform/tests/html/editing/dnd/drop/events-input-manual.tentative.html @@ -0,0 +1,16 @@ +<!doctype html> +<meta charset=utf-8> +<title>Selection drag and drop: events for <input></title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body data-expected-events=" + b:drop:, + a:beforeinput:deleteByDrag, + a:input:deleteByDrag, + b:beforeinput:insertFromDrop, + b:textInput:, + b:input:insertFromDrop"> +<div><input id=a data-select="0,7" value="Drag me"></div> +<div><input id=b placeholder="...to here"></div> +<script src="/uievents/textInput/support/common.js"></script> +<script src="support/events.js"></script> diff --git a/testing/web-platform/tests/html/editing/dnd/drop/events-input-same-element-manual.tentative.html b/testing/web-platform/tests/html/editing/dnd/drop/events-input-same-element-manual.tentative.html new file mode 100644 index 0000000000..8a578d51ad --- /dev/null +++ b/testing/web-platform/tests/html/editing/dnd/drop/events-input-same-element-manual.tentative.html @@ -0,0 +1,15 @@ +<!doctype html> +<meta charset=utf-8> +<title>Selection drag and drop: events for <input> (same element)</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body data-expected-events=" + b:drop:, + b:beforeinput:deleteByDrag, + b:input:deleteByDrag, + b:beforeinput:insertFromDrop, + b:textInput:, + b:input:insertFromDrop"> +<input id=b data-select="0,7" value="Drag me ...to here:"> +<script src="/uievents/textInput/support/common.js"></script> +<script src="support/events.js"></script> diff --git a/testing/web-platform/tests/html/editing/dnd/drop/events-textarea-manual.tentative.html b/testing/web-platform/tests/html/editing/dnd/drop/events-textarea-manual.tentative.html new file mode 100644 index 0000000000..7fb8bf437f --- /dev/null +++ b/testing/web-platform/tests/html/editing/dnd/drop/events-textarea-manual.tentative.html @@ -0,0 +1,16 @@ +<!doctype html> +<meta charset=utf-8> +<title>Selection drag and drop: events for <textarea></title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body data-expected-events=" + b:drop:, + a:beforeinput:deleteByDrag, + a:input:deleteByDrag, + b:beforeinput:insertFromDrop, + b:textInput:, + b:input:insertFromDrop"> +<div><input id=a data-select="0,7" value="Drag me"></div> +<div><textarea id=b placeholder="...to here"></textarea></div> +<script src="/uievents/textInput/support/common.js"></script> +<script src="support/events.js"></script> diff --git a/testing/web-platform/tests/html/editing/dnd/drop/events-textarea-same-element-manual.tentative.html b/testing/web-platform/tests/html/editing/dnd/drop/events-textarea-same-element-manual.tentative.html new file mode 100644 index 0000000000..c856fd4fbe --- /dev/null +++ b/testing/web-platform/tests/html/editing/dnd/drop/events-textarea-same-element-manual.tentative.html @@ -0,0 +1,15 @@ +<!doctype html> +<meta charset=utf-8> +<title>Selection drag and drop: events for <textarea> (same element)</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body data-expected-events=" + b:drop:, + b:beforeinput:deleteByDrag, + b:input:deleteByDrag, + b:beforeinput:insertFromDrop, + b:textInput:, + b:input:insertFromDrop"> +<textarea id=b data-select="0,7">Drag me ...to here:</textarea> +<script src="/uievents/textInput/support/common.js"></script> +<script src="support/events.js"></script> diff --git a/testing/web-platform/tests/html/editing/dnd/drop/support/events.js b/testing/web-platform/tests/html/editing/dnd/drop/support/events.js new file mode 100644 index 0000000000..015cead385 --- /dev/null +++ b/testing/web-platform/tests/html/editing/dnd/drop/support/events.js @@ -0,0 +1,31 @@ +setup({ explicit_timeout: true, single_test: true }); +function rAF() { + return new Promise(resolve => { + requestAnimationFrame(resolve); + }); +} +const a = document.getElementById('a'); +const b = document.getElementById('b'); +const actualEvents = []; +const expectedEvents = document.body.dataset.expectedEvents.replace(/\s+/g, '').split(','); +const eventTypes = new Set(expectedEvents.map(s => s.split(':')[1])); +for (const eventType of eventTypes) { + if (a) { + a.addEventListener(eventType, e => { + actualEvents.push(`a:${e.type}:${e.inputType || ''}`); + }); + } + b.addEventListener(eventType, async (e) => { + actualEvents.push(`b:${e.type}:${e.inputType || ''}`); + if (e.type === "input") { + await rAF(); + await rAF(); + assert_array_equals(actualEvents, expectedEvents); + done(); + } + }); +} +const dragMeElement = document.querySelector('[data-select]'); +const [selectionStart, selectionEnd] = dragMeElement.dataset.select.split(',').map(s => parseInt(s, 10)); +setSelection(dragMeElement, selectionStart, selectionEnd); +dragMeElement.focus(); diff --git a/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/dnd-datatransfer-setdragimage-manual.html b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/dnd-datatransfer-setdragimage-manual.html new file mode 100644 index 0000000000..acd8450308 --- /dev/null +++ b/testing/web-platform/tests/html/editing/dnd/the-datatransfer-interface/dnd-datatransfer-setdragimage-manual.html @@ -0,0 +1,108 @@ +<!DOCTYPE html> +<html> + +<head> + <style> + div { + margin: 0em; + padding: 2em; + } + + #source1, + #source2 { + color: blue; + border: 1px solid black; + } + + #target { + border: 1px solid black; + } + </style> + <script> + function getSolidColorImageBase64(color) { + var canvas = document.createElement('canvas'); + canvas.width = 256; + canvas.height = 256; + var ctx = canvas.getContext('2d'); + ctx.fillStyle = color; + ctx.fillRect(0, 0, canvas.width, canvas.height); + return canvas.toDataURL(); + } + function setDragImage(ev) { + var dragImage = document.createElement('img'); + if (ev.type === 'dragstart') { + dragImage = document.getElementById('dragImage'); + } + if (ev.type === 'dragover') { + // Red color image + dragImage.src = getSolidColorImageBase64('#FF0000'); + } + if (ev.type === 'dragenter') { + // Green color image + dragImage.src = getSolidColorImageBase64('#00FF00'); + } + if (ev.type === 'drop') { + // Yellow color image + dragImage.src = getSolidColorImageBase64('#FFFF00'); + } + ev.dataTransfer.setDragImage(dragImage, 10, 10); + } + + function dragstart_with_image_handler(ev) { + ev.dataTransfer.setData("text/plain", ev.target.id); + setDragImage(ev); + } + + function dragstart_without_image_handler(ev) { + ev.dataTransfer.setData("text/plain", ev.target.id); + } + + function dragover_handler(ev) { + setDragImage(ev); + ev.preventDefault(); + } + + function drag_enter(ev) { + setDragImage(ev); + ev.preventDefault(); + } + + function drop_handler(ev) { + setDragImage(ev); + ev.preventDefault(); + var data = ev.dataTransfer.getData("text"); + ev.target.appendChild(document.getElementById(data)); + } + </script> +</head> + +<body> + <div id="dragImageDiv"> + </div> + <div> + <p id="source1" ondragstart="dragstart_with_image_handler(event);" draggable="true"> + Select this element, drag it to the Drop Zone and drag image + should be visible. The drag image should be identical to the above image. + And the drag image should not change through out the drag and drop operation. + </p> + </div> + <div> + <p id="source2" ondragstart="dragstart_without_image_handler(event);" draggable="true"> + Select this element, drag it to the Drop Zone and drag image + should not be visible. + </p> + </div> + <div id="target" ondragenter="drag_enter(event);" ondrop="drop_handler(event);" ondragover="dragover_handler(event);"> + Drop Zone + </div> + <script> + var initialDragImage = document.createElement('img') + // Blue color image + initialDragImage.src = getSolidColorImageBase64('#0000FF') + initialDragImage.id = "dragImage" + var dragImageDiv = document.getElementById('dragImageDiv') + dragImageDiv.appendChild(initialDragImage); + </script> +</body> + +</html> diff --git a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js index 74c4c2e997..8422ea4b3c 100644 --- a/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js +++ b/testing/web-platform/tests/html/infrastructure/urls/resolving-urls/query-encoding/resources/resolve-url.js @@ -201,8 +201,6 @@ onload = function() { var video_ext = ''; if (elm.canPlayType('video/webm; codecs="vp9,opus"')) { video_ext = 'webm'; - } else if (elm.canPlayType('video/ogg; codecs="theora,flac"')) { - video_ext = 'ogv'; } else if (elm.canPlayType('video/mp4; codecs="avc1.42E01E,mp4a.40.2"')) { video_ext = 'mp4'; } diff --git a/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-overflow.html b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-overflow.html new file mode 100644 index 0000000000..44b149952c --- /dev/null +++ b/testing/web-platform/tests/html/obsolete/requirements-for-implementations/the-marquee-element-0/marquee-overflow.html @@ -0,0 +1,18 @@ +<!doctype html> +<meta charset="utf-8"> +<title>Marquee forces overflow: hidden</title> +<link rel="help" href="https://html.spec.whatwg.org/#the-marquee-element"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<marquee style="overflow: visible"> </marquee> +<marquee style="overflow: scroll"> </marquee> +<marquee style="overflow: clip"> </marquee> +<marquee style="overflow: auto"> </marquee> + +<script> +test(() => { + for (let m of document.querySelectorAll("marquee")) { + assert_equals(getComputedStyle(m).overflow, "hidden", m.style); + } +}, "Marquee should have overflow: hidden !important in the UA stylesheet"); +</script> diff --git a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/resets.html b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/resets.html index db21188ee3..1a17aeac2d 100644 --- a/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/resets.html +++ b/testing/web-platform/tests/html/rendering/non-replaced-elements/form-controls/resets.html @@ -50,6 +50,7 @@ input:not([type=image i], [type=range i], [type=checkbox i], [type=radio i]) { } input[type=hidden i] { display: none !important; } marquee { + overflow: hidden; text-align: initial; } table { display: table; box-sizing: border-box; } diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/display-none-or-contents-ref.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/display-none-or-contents-ref.html new file mode 100644 index 0000000000..c20a3065e4 --- /dev/null +++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/display-none-or-contents-ref.html @@ -0,0 +1,3 @@ +<!DOCTYPE html> +<title>button (in button layout) with display: none/contents</title> +2
\ No newline at end of file diff --git a/testing/web-platform/tests/html/rendering/widgets/button-layout/display-none-or-contents.html b/testing/web-platform/tests/html/rendering/widgets/button-layout/display-none-or-contents.html new file mode 100644 index 0000000000..8f0ad3ab29 --- /dev/null +++ b/testing/web-platform/tests/html/rendering/widgets/button-layout/display-none-or-contents.html @@ -0,0 +1,12 @@ +<!DOCTYPE html> +<title>button (in button layout) with display: none/contents</title> +<link rel=match href=display-none-or-contents-ref.html> +<link rel=help href=https://html.spec.whatwg.org/multipage/rendering.html#button-layout-2> +<style> +#none{ display: none} +#contents { display: contents; font: initial } +</style> +<!-- Button layout should not impact "display: none" or "display: contents" on button elements --> +<!-- https://github.com/whatwg/html/pull/10244 --> +<button id=none>1</button> +<button id=contents>2</button>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html index 56edf25aa8..855f02d3b1 100644 --- a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html +++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/mime-types/canPlayType.html @@ -55,10 +55,13 @@ function type_codecs_test(type, audioCodecs, videoCodecs) { var typeSupported = false; var codecSupported = false; + var mimeSupported = canPlayType(type); + // Test 'type' without codecs. // Spec: Generally, a user agent should never return "probably" for a type // that allows the codecs parameter if that parameter is not present. test(function() { + assert_implements_optional(mimeSupported, type) t(type, 'maybe'); t(type + ';', 'maybe'); t(type + ';codecs', 'maybe'); @@ -69,6 +72,7 @@ function type_codecs_test(type, audioCodecs, videoCodecs) { function test_codec(codec) { var typeWithCodec = mime(type, [codec]); test(function() { + assert_implements_optional(canPlayType(typeWithCodec), type) t(typeWithCodec, 'probably'); codecSupported = true; }, typeWithCodec + ' (optional)'); @@ -81,6 +85,7 @@ function type_codecs_test(type, audioCodecs, videoCodecs) { // Test different pairings and orderings of audio+video codecs. if (audioCodecs.length > 0 && videoCodecs.length > 0) { test(function() { + assert_implements_optional(mimeSupported, type) audioCodecs.forEach(function(ac) { videoCodecs.forEach(function(vc) { var canPlayBoth = canPlayType(mime(type, [ac, vc])); @@ -93,6 +98,7 @@ function type_codecs_test(type, audioCodecs, videoCodecs) { }, type + ' codecs subset'); test(function() { + assert_implements_optional(mimeSupported, type) audioCodecs.forEach(function(ac) { videoCodecs.forEach(function(vc) { assert_equals(canPlayType(mime(type, [ac, vc])), diff --git a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html index 3b4c3542a9..1a8aabcaff 100644 --- a/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html +++ b/testing/web-platform/tests/html/semantics/embedded-content/media-elements/track/track-element/track-cue-inline.html @@ -4,7 +4,7 @@ <script src="/resources/testharnessreport.js"></script> <video> <source src="/media/test.mp4" type="video/mp4"> - <source src="/media/test.ogv" type="video/ogg"> + <source src="/media/test.webm" type="video/webm"> </video> <script> test(function() { @@ -13,4 +13,4 @@ test(function() { track.addCue(new VTTCue(0.0, 10.0, 'wow wow')); track.mode = 'showing'; }); -</script>
\ No newline at end of file +</script> diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-cross-origin-delivered.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-cross-origin-delivered.tentative.sub.window.js new file mode 100644 index 0000000000..8074314557 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-cross-origin-delivered.tentative.sub.window.js @@ -0,0 +1,18 @@ +// META: title=Top-level navigation tests with cross origin & user activated child frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe(main, + "HTTP_REMOTE_ORIGIN", "", "allow-top-navigation"); + + await attemptTopNavigation(iframe_1, false); +}, "A cross-origin frame with delivered sandbox flags can not navigate top"); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-cross-origin-frame.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-cross-origin-frame.tentative.sub.window.js new file mode 100644 index 0000000000..a2191b352a --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-cross-origin-frame.tentative.sub.window.js @@ -0,0 +1,18 @@ +// META: title=Top-level navigation tests with cross origin & user activated child frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe( + main, 'HTTP_REMOTE_ORIGIN', 'allow-top-navigation', ''); + + await attemptTopNavigation(iframe_1, true); +}, 'A cross-origin frame with frame sandbox flags can navigate top'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-cross-origin.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-cross-origin.tentative.sub.window.js deleted file mode 100644 index 95d53e1fe3..0000000000 --- a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-cross-origin.tentative.sub.window.js +++ /dev/null @@ -1,28 +0,0 @@ -// META: title=Top-level navigation tests with cross origin & user activated child frames -// META: script=/common/dispatcher/dispatcher.js -// META: script=/common/get-host-info.sub.js -// META: script=/common/utils.js -// META: script=/resources/testdriver.js -// META: script=/resources/testdriver-vendor.js -// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js -// META: script=./resources/sandbox-top-navigation-helper.sub.js - -'use strict'; - -// /* ---------------------- CROSS ORIGIN (A -> B) TESTS ---------------------- */ - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_REMOTE_ORIGIN", "allow-top-navigation", ""); - - await attemptTopNavigation(iframe_1, true); -}, "A cross-origin frame with frame sandbox flags can navigate top"); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_REMOTE_ORIGIN", "", "allow-top-navigation"); - - await attemptTopNavigation(iframe_1, false); -}, "A cross-origin frame with delivered sandbox flags can not navigate top"); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-delivered-both.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-delivered-both.tentative.sub.window.js new file mode 100644 index 0000000000..540cc338c9 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-delivered-both.tentative.sub.window.js @@ -0,0 +1,20 @@ +// META: title=Top-level navigation tests with child frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe( + main, 'HTTP_ORIGIN', '', + 'allow-top-navigation allow-top-navigation-by-user-activation allow-same-origin'); + + await attemptTopNavigation(iframe_1, true); +}, 'A frame with both top navigation delivered sandbox flags uses the less \ + restrictive one'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-delivered.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-delivered.tentative.sub.window.js new file mode 100644 index 0000000000..c020513012 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-delivered.tentative.sub.window.js @@ -0,0 +1,18 @@ +// META: title=Top-level navigation tests with child frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe( + main, 'HTTP_ORIGIN', '', 'allow-top-navigation allow-same-origin'); + + await attemptTopNavigation(iframe_1, true); +}, 'A same-origin frame with delivered sandbox flags can navigate top'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-frame-both.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-frame-both.tentative.sub.window.js new file mode 100644 index 0000000000..ff7d2eb584 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-frame-both.tentative.sub.window.js @@ -0,0 +1,20 @@ +// META: title=Top-level navigation tests with child frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe( + main, 'HTTP_ORIGIN', + 'allow-top-navigation allow-top-navigation-by-user-activation', ''); + + await attemptTopNavigation(iframe_1, true); +}, 'A frame with both top navigation frame sandbox flags uses the less \ + restrictive one'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-frame.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-frame.tentative.sub.window.js new file mode 100644 index 0000000000..35abc554b6 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-frame.tentative.sub.window.js @@ -0,0 +1,18 @@ +// META: title=Top-level navigation tests with child frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe( + main, 'HTTP_ORIGIN', 'allow-top-navigation allow-same-origin', ''); + + await attemptTopNavigation(iframe_1, true); +}, 'A same-origin frame with frame sandbox flags can navigate top'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-unsandboxed.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-unsandboxed.tentative.sub.window.js new file mode 100644 index 0000000000..c2d78b6dd5 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-unsandboxed.tentative.sub.window.js @@ -0,0 +1,17 @@ +// META: title=Top-level navigation tests with child frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe(main, 'HTTP_ORIGIN', '', ''); + + await attemptTopNavigation(iframe_1, true); +}, 'A same-origin unsandboxed frame can navigate top'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child.tentative.sub.window.js deleted file mode 100644 index 1d5ea93830..0000000000 --- a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child.tentative.sub.window.js +++ /dev/null @@ -1,56 +0,0 @@ -// META: title=Top-level navigation tests with child frames -// META: script=/common/dispatcher/dispatcher.js -// META: script=/common/get-host-info.sub.js -// META: script=/common/utils.js -// META: script=/resources/testdriver.js -// META: script=/resources/testdriver-vendor.js -// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js -// META: script=./resources/sandbox-top-navigation-helper.sub.js - -'use strict'; - -/* ----------------------- SAME ORIGIN (A -> A) TESTS ----------------------- */ - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_ORIGIN", "", "allow-top-navigation allow-same-origin"); - - await attemptTopNavigation(iframe_1, true); -}, "A same-origin frame with delivered sandbox flags can navigate top"); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_ORIGIN", "allow-top-navigation allow-same-origin", ""); - - await attemptTopNavigation(iframe_1, true); -}, "A same-origin frame with frame sandbox flags can navigate top"); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_ORIGIN", "", ""); - - await attemptTopNavigation(iframe_1, true); -}, "A same-origin unsandboxed frame can navigate top"); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_ORIGIN", "", - "allow-top-navigation allow-top-navigation-by-user-activation allow-same-origin"); - - await attemptTopNavigation(iframe_1, true); -}, "A frame with both top navigation delivered sandbox flags uses the less \ - restrictive one"); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_ORIGIN", - "allow-top-navigation allow-top-navigation-by-user-activation", ""); - - await attemptTopNavigation(iframe_1, true); -}, "A frame with both top navigation frame sandbox flags uses the less \ - restrictive one"); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-cross-origin-escalate.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-cross-origin-escalate.tentative.sub.window.js new file mode 100644 index 0000000000..c394699d85 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-cross-origin-escalate.tentative.sub.window.js @@ -0,0 +1,20 @@ +// META: title=Top-level navigation tests with frames that try to give themselves top-nav permission +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe(main, 'HTTP_REMOTE_ORIGIN', '', ''); + const iframe_2 = await createNestedIframe( + iframe_1, 'HTTP_REMOTE_ORIGIN', 'allow-top-navigation', ''); + + await attemptTopNavigation(iframe_2, false); +}, 'A cross origin unsandboxed frame can\'t escalate privileges in a child \ + frame'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-cross-site.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-cross-site.tentative.sub.window.js index 26db4eeaca..cacc5bd983 100644 --- a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-cross-site.tentative.sub.window.js +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-cross-site.tentative.sub.window.js @@ -20,24 +20,3 @@ promise_test(async t => { await attemptTopNavigation(new_iframe, false); }, "A cross-site unsandboxed iframe navigation consumes user activation and " + "disallows top-level navigation."); - -promise_test(async t => { - const main = await setupTest(); - - const iframe = await createNestedIframe(main, "HTTP_ORIGIN", "", ""); - await activate(iframe); - - const new_iframe = await navigateFrameTo(iframe, "HTTP_REMOTE_ORIGIN"); - await attemptTopNavigation(new_iframe, true); -}, "A same-site unsandboxed iframe navigation does not consume user " + - "activation and allows top-level navigation."); - -promise_test(async t => { - const main = await setupTest(); - - const iframe = await createNestedIframe(main, "HTTP_ORIGIN", "", ""); - - const new_iframe = await navigateFrameTo(iframe, "HTTP_REMOTE_ORIGIN"); - await attemptTopNavigation(new_iframe, false); -}, "A same-site unsandboxed iframe navigation without sticky user activation " + - "does not allow top-level navigation."); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-escalate-privileges.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-escalate-privileges.tentative.sub.window.js deleted file mode 100644 index 2ea0ba606e..0000000000 --- a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-escalate-privileges.tentative.sub.window.js +++ /dev/null @@ -1,63 +0,0 @@ -// META: title=Top-level navigation tests with frames that try to give themselves top-nav permission -// META: script=/common/dispatcher/dispatcher.js -// META: script=/common/get-host-info.sub.js -// META: script=/common/utils.js -// META: script=/resources/testdriver.js -// META: script=/resources/testdriver-vendor.js -// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js -// META: script=./resources/sandbox-top-navigation-helper.sub.js - -'use strict'; - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_REMOTE_ORIGIN", "", ""); - const iframe_2 = await createNestedIframe(iframe_1, - "HTTP_REMOTE_ORIGIN", "allow-top-navigation", ""); - - await attemptTopNavigation(iframe_2, false); -}, "A cross origin unsandboxed frame can't escalate privileges in a child \ - frame"); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_REMOTE_ORIGIN", "allow-top-navigation", ""); - const iframe_2 = await createNestedIframe(iframe_1, - "OTHER_ORIGIN", "", ""); - - await attemptTopNavigation(iframe_2, true); -}, "An unsandboxed grandchild inherits its parents ability to navigate top."); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_ORIGIN", "", ""); - const iframe_2 = await createNestedIframe(iframe_1, - "HTTP_ORIGIN", "allow-top-navigation", ""); - - await attemptTopNavigation(iframe_2, true); -}, "A same-origin grandchild with frame allow-top can navigate top"); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_ORIGIN", "", ""); - const iframe_2 = await createNestedIframe(iframe_1, - "HTTP_ORIGIN", "", "allow-top-navigation"); - - await attemptTopNavigation(iframe_2, false); -}, "A sandboxed same-origin grandchild without allow-same-origin can't \ - escalate its own top-nav privileges"); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_ORIGIN", "", ""); - const iframe_2 = await createNestedIframe(iframe_1, - "HTTP_ORIGIN", "", "allow-same-origin allow-top-navigation"); - - await attemptTopNavigation(iframe_2, true); -}, "A sandboxed same-origin grandchild with allow-same-origin can \ - give itself top-nav privileges"); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-allow-same-origin.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-allow-same-origin.tentative.sub.window.js new file mode 100644 index 0000000000..2be6cd66a7 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-allow-same-origin.tentative.sub.window.js @@ -0,0 +1,20 @@ +// META: title=Top-level navigation tests with frames that try to give themselves top-nav permission +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe(main, 'HTTP_ORIGIN', '', ''); + const iframe_2 = await createNestedIframe( + iframe_1, 'HTTP_ORIGIN', '', 'allow-same-origin allow-top-navigation'); + + await attemptTopNavigation(iframe_2, true); +}, 'A sandboxed same-origin grandchild with allow-same-origin can \ + give itself top-nav privileges'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-frame-allow-top.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-frame-allow-top.tentative.sub.window.js new file mode 100644 index 0000000000..43ca5eb404 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-frame-allow-top.tentative.sub.window.js @@ -0,0 +1,19 @@ +// META: title=Top-level navigation tests with frames that try to give themselves top-nav permission +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe(main, 'HTTP_ORIGIN', '', ''); + const iframe_2 = await createNestedIframe( + iframe_1, 'HTTP_ORIGIN', 'allow-top-navigation', ''); + + await attemptTopNavigation(iframe_2, true); +}, 'A same-origin grandchild with frame allow-top can navigate top'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-sandboxed-cross-origin-parent.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-sandboxed-cross-origin-parent.tentative.sub.window.js new file mode 100644 index 0000000000..8833ad42bb --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-sandboxed-cross-origin-parent.tentative.sub.window.js @@ -0,0 +1,19 @@ +// META: title=Top-level navigation tests with grandchild frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe(main, 'HTTP_REMOTE_ORIGIN', '', ''); + const iframe_2 = await createNestedIframe( + iframe_1, 'HTTP_ORIGIN', 'allow-top-navigation allow-same-origin', ''); + + await attemptTopNavigation(iframe_2, true); +}, 'A same-origin sandboxed grandchild in a cross-origin parent can navigate top'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-sandboxed-escalate.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-sandboxed-escalate.tentative.sub.window.js new file mode 100644 index 0000000000..448925144a --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-sandboxed-escalate.tentative.sub.window.js @@ -0,0 +1,20 @@ +// META: title=Top-level navigation tests with frames that try to give themselves top-nav permission +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe(main, 'HTTP_ORIGIN', '', ''); + const iframe_2 = await createNestedIframe( + iframe_1, 'HTTP_ORIGIN', '', 'allow-top-navigation'); + + await attemptTopNavigation(iframe_2, false); +}, 'A sandboxed same-origin grandchild without allow-same-origin can\'t \ + escalate its own top-nav privileges'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-sandboxed.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-sandboxed.tentative.sub.window.js new file mode 100644 index 0000000000..a4b43a7b1f --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-sandboxed.tentative.sub.window.js @@ -0,0 +1,19 @@ +// META: title=Top-level navigation tests with grandchild frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe(main, 'HTTP_ORIGIN', '', ''); + const iframe_2 = + await createNestedIframe(iframe_1, 'HTTP_ORIGIN', 'allow-scripts', ''); + + await attemptTopNavigation(iframe_2, false); +}, 'A fully sandboxed same-origin grandchild can\'t navigate top'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-unsandboxed-cross-origin-parent.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-unsandboxed-cross-origin-parent.tentative.sub.window.js new file mode 100644 index 0000000000..5abbb9c30c --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-unsandboxed-cross-origin-parent.tentative.sub.window.js @@ -0,0 +1,18 @@ +// META: title=Top-level navigation tests with grandchild frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe(main, 'HTTP_REMOTE_ORIGIN', '', ''); + const iframe_2 = await createNestedIframe(iframe_1, 'HTTP_ORIGIN', '', ''); + + await attemptTopNavigation(iframe_2, true); +}, 'A same-origin grandchild in a cross-origin parent can navigate top'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-unsandboxed-inherit.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-unsandboxed-inherit.tentative.sub.window.js new file mode 100644 index 0000000000..a31c56b935 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-unsandboxed-inherit.tentative.sub.window.js @@ -0,0 +1,19 @@ +// META: title=Top-level navigation tests with frames that try to give themselves top-nav permission +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe( + main, 'HTTP_REMOTE_ORIGIN', 'allow-top-navigation', ''); + const iframe_2 = await createNestedIframe(iframe_1, 'OTHER_ORIGIN', '', ''); + + await attemptTopNavigation(iframe_2, true); +}, 'An unsandboxed grandchild inherits its parents ability to navigate top.'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-unsandboxed.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-unsandboxed.tentative.sub.window.js new file mode 100644 index 0000000000..7fe80dfa1b --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-unsandboxed.tentative.sub.window.js @@ -0,0 +1,18 @@ +// META: title=Top-level navigation tests with grandchild frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe(main, 'HTTP_ORIGIN', '', ''); + const iframe_2 = await createNestedIframe(iframe_1, 'HTTP_ORIGIN', '', ''); + + await attemptTopNavigation(iframe_2, true); +}, 'An unsandboxed same-origin grandchild can navigate top'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild.tentative.sub.window.js deleted file mode 100644 index 326c1dd54a..0000000000 --- a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild.tentative.sub.window.js +++ /dev/null @@ -1,50 +0,0 @@ -// META: title=Top-level navigation tests with grandchild frames -// META: script=/common/dispatcher/dispatcher.js -// META: script=/common/get-host-info.sub.js -// META: script=/common/utils.js -// META: script=/resources/testdriver.js -// META: script=/resources/testdriver-vendor.js -// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js -// META: script=./resources/sandbox-top-navigation-helper.sub.js - -'use strict'; - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_ORIGIN", "", ""); - const iframe_2 = await createNestedIframe(iframe_1, - "HTTP_ORIGIN", "allow-scripts", ""); - - await attemptTopNavigation(iframe_2, false); -}, "A fully sandboxed same-origin grandchild can't navigate top"); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_ORIGIN", "", ""); - const iframe_2 = await createNestedIframe(iframe_1, - "HTTP_ORIGIN", "", ""); - - await attemptTopNavigation(iframe_2, true); -}, "An unsandboxed same-origin grandchild can navigate top"); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_REMOTE_ORIGIN", "", ""); - const iframe_2 = await createNestedIframe(iframe_1, - "HTTP_ORIGIN", "", ""); - - await attemptTopNavigation(iframe_2, true); -}, "A same-origin grandchild in a cross-origin parent can navigate top"); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_REMOTE_ORIGIN", "", ""); - const iframe_2 = await createNestedIframe(iframe_1, - "HTTP_ORIGIN", "allow-top-navigation allow-same-origin", ""); - - await attemptTopNavigation(iframe_2, true); -}, "A same-origin sandboxed grandchild in a cross-origin parent can navigate top");
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-same-site-no-activation.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-same-site-no-activation.tentative.sub.window.js new file mode 100644 index 0000000000..03350e76e2 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-same-site-no-activation.tentative.sub.window.js @@ -0,0 +1,23 @@ +// META: title=Top-level navigation tests with cross origin & user activated child frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-actions.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test( + async t => { + const main = await setupTest(); + + const iframe = await createNestedIframe(main, 'HTTP_ORIGIN', '', ''); + + const new_iframe = await navigateFrameTo(iframe, 'HTTP_REMOTE_ORIGIN'); + await attemptTopNavigation(new_iframe, false); + }, + 'A same-site unsandboxed iframe navigation without sticky user activation ' + + 'does not allow top-level navigation.'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-same-site.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-same-site.tentative.sub.window.js new file mode 100644 index 0000000000..0ee6b8edcc --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-same-site.tentative.sub.window.js @@ -0,0 +1,24 @@ +// META: title=Top-level navigation tests with cross origin & user activated child frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-actions.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test( + async t => { + const main = await setupTest(); + + const iframe = await createNestedIframe(main, 'HTTP_ORIGIN', '', ''); + await activate(iframe); + + const new_iframe = await navigateFrameTo(iframe, 'HTTP_REMOTE_ORIGIN'); + await attemptTopNavigation(new_iframe, true); + }, + 'A same-site unsandboxed iframe navigation does not consume user ' + + 'activation and allows top-level navigation.'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-user-activation-no-sticky.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-user-activation-no-sticky.tentative.sub.window.js new file mode 100644 index 0000000000..c62155ce30 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-user-activation-no-sticky.tentative.sub.window.js @@ -0,0 +1,19 @@ +// META: title=Top-level navigation tests with cross origin & user activated child frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-actions.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe( + main, 'HTTP_ORIGIN', 'allow-top-navigation-by-user-activation', ''); + + await attemptTopNavigation(iframe_1, false); +}, 'allow-top-navigation-by-user-activation set but no sticky activation'); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-user-activation-sticky.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-user-activation-sticky.tentative.sub.window.js new file mode 100644 index 0000000000..e62fbdfb22 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-user-activation-sticky.tentative.sub.window.js @@ -0,0 +1,20 @@ +// META: title=Top-level navigation tests with cross origin & user activated child frames +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/resources/testdriver.js +// META: script=/resources/testdriver-actions.js +// META: script=/resources/testdriver-vendor.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=./resources/sandbox-top-navigation-helper.sub.js + +'use strict'; + +promise_test(async t => { + const main = await setupTest(); + const iframe_1 = await createNestedIframe(main, + "HTTP_ORIGIN", "allow-top-navigation-by-user-activation", ""); + await activate(iframe_1); + + await attemptTopNavigation(iframe_1, true); +}, "Allow top with user activation + user activation"); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-user-activation.tentative.sub.window.js b/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-user-activation.tentative.sub.window.js deleted file mode 100644 index 5079c8ad14..0000000000 --- a/testing/web-platform/tests/html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-user-activation.tentative.sub.window.js +++ /dev/null @@ -1,30 +0,0 @@ -// META: title=Top-level navigation tests with cross origin & user activated child frames -// META: script=/common/dispatcher/dispatcher.js -// META: script=/common/get-host-info.sub.js -// META: script=/common/utils.js -// META: script=/resources/testdriver.js -// META: script=/resources/testdriver-actions.js -// META: script=/resources/testdriver-vendor.js -// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js -// META: script=./resources/sandbox-top-navigation-helper.sub.js - -'use strict'; - -/* ------------------------- USER ACTIVATION TESTS ------------------------- */ - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_ORIGIN", "allow-top-navigation-by-user-activation", ""); - await activate(iframe_1); - - await attemptTopNavigation(iframe_1, true); -}, "Allow top with user activation + user activation"); - -promise_test(async t => { - const main = await setupTest(); - const iframe_1 = await createNestedIframe(main, - "HTTP_ORIGIN", "allow-top-navigation-by-user-activation", ""); - - await attemptTopNavigation(iframe_1, false); -}, "allow-top-navigation-by-user-activation set but no sticky activation"); diff --git a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/usemap-casing.html b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/usemap-casing.html index 114a472fb6..9431c73fe7 100644 --- a/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/usemap-casing.html +++ b/testing/web-platform/tests/html/semantics/embedded-content/the-object-element/usemap-casing.html @@ -8,53 +8,53 @@ <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> -<div id="log"></div> +<object data="/images/threecolors.png" usemap="#sanityCheck" width="100" height="100"></object> +<map name="sanityCheck"><area shape="rect" coords="0,0,100,100"></map> + +<object data="/images/threecolors.png" usemap="#sImPlE" width="100" height="100"></object> +<map name="simple"><area shape="rect" coords="0,0,100,100"></map> +<map name="SIMPLE"><area shape="rect" coords="0,0,100,100"></map> + +<object data="/images/threecolors.png" usemap="#paSSfield-killroyß" width="100" height="100"></object> +<map name="passfield-killroyß"><area shape="rect" coords="0,0,100,100"></map> +<map name="PASSFIELD-KILLROYß"><area shape="rect" coords="0,0,100,100"></map> +<map name="paſſfield-killroyß"><area shape="rect" coords="0,0,100,100"></map> +<map name="passfield-Killroyß"><area shape="rect" coords="0,0,100,100"></map> +<map name="paßfield-killroyß"><area shape="rect" coords="0,0,100,100"></map> +<map name="paẞfield-killroyß"><area shape="rect" coords="0,0,100,100"></map> +<map name="passfield-killroyẞ"><area shape="rect" coords="0,0,100,100"></map> +<map name="passfield-killroyß"><area shape="rect" coords="0,0,100,100"></map> +<map name="passfıeld-killroyß"><area shape="rect" coords="0,0,100,100"></map> +<map name="passfİeld-killroyß"><area shape="rect" coords="0,0,100,100"></map> + +<object data="/images/threecolors.png" usemap="#глупый" width="100" height="100"></object> +<map name="глупый"><area shape="rect" coords="0,0,100,100"></map> +<map name="ГЛУПЫЙ"><area shape="rect" coords="0,0,100,100"></map> +<map name="ГЛУПЫЙ"><area shape="rect" coords="0,0,100,100"></map> + +<object data="/images/threecolors.png" usemap="#åωk" width="100" height="100"></object> +<map name="ÅΩK"><area shape="rect" coords="0,0,100,100"></map> +<map name="Åωk"><area shape="rect" coords="0,0,100,100"></map> +<map name="åΩk"><area shape="rect" coords="0,0,100,100"></map> +<map name="åωK"><area shape="rect" coords="0,0,100,100"></map> + +<object data="/images/threecolors.png" usemap="#blah1" width="100" height="100"></object> +<map name="blah①"><area shape="rect" coords="0,0,100,100"></map> +<map name="blⒶh1"><area shape="rect" coords="0,0,100,100"></map> +<map name="blⓐh1"><area shape="rect" coords="0,0,100,100"></map> + +<object data="/images/threecolors.png" usemap="#tÉdz5アパートFi" width="100" height="100"></object> +<map name="TÉDZ5アパートFi"><area shape="rect" coords="0,0,100,100"></map> +<map name="TéDZ⁵アパートFi"><area shape="rect" coords="0,0,100,100"></map> +<map name="tÉdz5㌀Fi"><area shape="rect" coords="0,0,100,100"></map> +<map name="tÉdz5アパートFi"><area shape="rect" coords="0,0,100,100"></map> +<map name="TÉDZ⁵アパートFi"><area shape="rect" coords="0,0,100,100"></map> +<map name="TÉDZ5アパートfi"><area shape="rect" coords="0,0,100,100"></map> + +<object data="/images/threecolors.png" usemap="#ΣΣ" width="100" height="100"></object> +<map name="σς"><area shape="rect" coords="0,0,100,100"></map> -<object data="/images/threecolors.png" usemap="#sanityCheck" width="300" height="300"></object> -<map name="sanityCheck"><area shape="rect" coords="0,0,300,300"></map> - -<object data="/images/threecolors.png" usemap="#sImPlE" width="300" height="300"></object> -<map name="simple"><area shape="rect" coords="0,0,300,300"></map> -<map name="SIMPLE"><area shape="rect" coords="0,0,300,300"></map> - -<object data="/images/threecolors.png" usemap="#paSSfield-killroyß" width="300" height="300"></object> -<map name="passfield-killroyß"><area shape="rect" coords="0,0,300,300"></map> -<map name="PASSFIELD-KILLROYß"><area shape="rect" coords="0,0,300,300"></map> -<map name="paſſfield-killroyß"><area shape="rect" coords="0,0,300,300"></map> -<map name="passfield-Killroyß"><area shape="rect" coords="0,0,300,300"></map> -<map name="paßfield-killroyß"><area shape="rect" coords="0,0,300,300"></map> -<map name="paẞfield-killroyß"><area shape="rect" coords="0,0,300,300"></map> -<map name="passfield-killroyẞ"><area shape="rect" coords="0,0,300,300"></map> -<map name="passfield-killroyß"><area shape="rect" coords="0,0,300,300"></map> -<map name="passfıeld-killroyß"><area shape="rect" coords="0,0,300,300"></map> -<map name="passfİeld-killroyß"><area shape="rect" coords="0,0,300,300"></map> - -<object data="/images/threecolors.png" usemap="#глупый" width="300" height="300"></object> -<map name="глупый"><area shape="rect" coords="0,0,300,300"></map> -<map name="ГЛУПЫЙ"><area shape="rect" coords="0,0,300,300"></map> -<map name="ГЛУПЫЙ"><area shape="rect" coords="0,0,300,300"></map> - -<object data="/images/threecolors.png" usemap="#åωk" width="300" height="300"></object> -<map name="ÅΩK"><area shape="rect" coords="0,0,300,300"></map> -<map name="Åωk"><area shape="rect" coords="0,0,300,300"></map> -<map name="åΩk"><area shape="rect" coords="0,0,300,300"></map> -<map name="åωK"><area shape="rect" coords="0,0,300,300"></map> - -<object data="/images/threecolors.png" usemap="#blah1" width="300" height="300"></object> -<map name="blah①"><area shape="rect" coords="0,0,300,300"></map> -<map name="blⒶh1"><area shape="rect" coords="0,0,300,300"></map> -<map name="blⓐh1"><area shape="rect" coords="0,0,300,300"></map> - -<object data="/images/threecolors.png" usemap="#tÉdz5アパートFi" width="300" height="300"></object> -<map name="TÉDZ5アパートFi"><area shape="rect" coords="0,0,300,300"></map> -<map name="TéDZ⁵アパートFi"><area shape="rect" coords="0,0,300,300"></map> -<map name="tÉdz5㌀Fi"><area shape="rect" coords="0,0,300,300"></map> -<map name="tÉdz5アパートFi"><area shape="rect" coords="0,0,300,300"></map> -<map name="TÉDZ⁵アパートFi"><area shape="rect" coords="0,0,300,300"></map> -<map name="TÉDZ5アパートfi"><area shape="rect" coords="0,0,300,300"></map> - -<object data="/images/threecolors.png" usemap="#ΣΣ" width="300" height="300"></object> -<map name="σς"><area shape="rect" coords="0,0,300,300"></map> +<div id="log"></div> <script> "use strict"; diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/resources/stylable-select-styles.css b/testing/web-platform/tests/html/semantics/forms/the-select-element/resources/stylable-select-styles.css deleted file mode 100644 index a7e9a87cdf..0000000000 --- a/testing/web-platform/tests/html/semantics/forms/the-select-element/resources/stylable-select-styles.css +++ /dev/null @@ -1,18 +0,0 @@ -.stylable-select-datalist { - box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11); - box-sizing: border-box; - overflow: auto; - border: 1px solid rgba(0, 0, 0, 0.15); - border-radius: 0.25em; - padding: 0.25em 0; - background-color: Field; - margin: 0; - inset: auto; - min-inline-size: anchor-size(self-inline); - min-block-size: 1lh; - inset-block-start: anchor(self-end); - inset-inline-start: anchor(self-start); - - font-family: Arial; - font-size: 13.3333px; -} diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist-invalidation.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist-invalidation.tentative.html deleted file mode 100644 index f71c1e52fc..0000000000 --- a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist-invalidation.tentative.html +++ /dev/null @@ -1,23 +0,0 @@ -<!DOCTYPE html> -<html class=reftest-wait> -<link rel=author href="mailto:jarhar@chromium.org"> -<link rel=help href="https://github.com/whatwg/html/issues/9799"> -<link rel=match href="select-child-button-and-datalist-ref.html"> -<script src="/resources/testdriver.js"></script> -<script src="/resources/testdriver-vendor.js"></script> - -<select> - <button type=popover>button</button> - <datalist> - <option>one</option> - <option>two</option> - </datalist> -</select> - -<script> -requestAnimationFrame(() => { - document.querySelector('select').style = 'appearance:bikeshed'; - document.querySelector('button').click(); - document.documentElement.classList.remove('reftest-wait'); -}); -</script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist-ref.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist-ref.html deleted file mode 100644 index 46bbd0ccd0..0000000000 --- a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist-ref.html +++ /dev/null @@ -1,12 +0,0 @@ -<!DOCTYPE html> -<link rel=stylesheet href="resources/stylable-select-styles.css"> - -<button popovertarget=popover id=button>button</button> -<div id=popover popover=auto anchor=button class=stylable-select-datalist> - <option>one</option> - <option>two</option> -</div> - -<script> -document.getElementById('popover').showPopover(); -</script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist.tentative.html deleted file mode 100644 index 54785ace95..0000000000 --- a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-child-button-and-datalist.tentative.html +++ /dev/null @@ -1,18 +0,0 @@ -<!DOCTYPE html> -<link rel=author href="mailto:jarhar@chromium.org"> -<link rel=help href="https://github.com/whatwg/html/issues/9799"> -<link rel=match href="select-child-button-and-datalist-ref.html"> -<script src="/resources/testdriver.js"></script> -<script src="/resources/testdriver-vendor.js"></script> - -<select style="appearance:bikeshed"> - <button type=popover>button</button> - <datalist> - <option>one</option> - <option>two</option> - </datalist> -</select> - -<script> -document.querySelector('button').click(); -</script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/resources/select-reset-non-interoperable-styles.css b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/resources/select-reset-non-interoperable-styles.css new file mode 100644 index 0000000000..d2b9d9df26 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/resources/select-reset-non-interoperable-styles.css @@ -0,0 +1,5 @@ +/* TODO(crbug.com/1511354): linux.css sets background-color on select, consider + * removing it. */ +select { + background-color: Field; +} diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/resources/stylable-select-styles.css b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/resources/stylable-select-styles.css new file mode 100644 index 0000000000..042de838d1 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/resources/stylable-select-styles.css @@ -0,0 +1,37 @@ +/* These are UA styles for select and stylable select. */ + +.stylable-select-container { + background-color: Field; + border: 1px solid rgba(0, 0, 0, 0); + border-radius: 0; + box-sizing: border-box; + display: inline-block; +} + +.stylable-select-datalist { + box-shadow: 0px 12.8px 28.8px rgba(0, 0, 0, 0.13), 0px 0px 9.2px rgba(0, 0, 0, 0.11); + box-sizing: border-box; + overflow: auto; + border: 1px solid rgba(0, 0, 0, 0.15); + border-radius: 0.25em; + padding: 0.25em 0; + background-color: Field; + margin: 0; + inset: auto; + min-inline-size: anchor-size(self-inline); + min-block-size: 1lh; + inset-block-start: anchor(self-end); + inset-inline-start: anchor(self-start); + + font-family: Arial; + font-size: 13.3333px; +} + +/* These are the UA styles for <option> in chromium. + * They will either have to be specced or also added to the main test file. + * TODO(crbug.com/1511354): Spec these UA styles. */ +.stylable-select-option { + min-height: 1.2em; + padding: 0px 2px 1px; + white-space: nowrap; +} diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-custom-button-no-datalist.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-custom-button-no-datalist.tentative.html new file mode 100644 index 0000000000..94d7fd53b3 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-custom-button-no-datalist.tentative.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<link rel=author href="mailto:jarhar@chromium.org"> +<link rel=help href="https://github.com/whatwg/html/issues/9799"> +<link rel=match href="select-appearance-no-button-custom-datalist-ref.html"> +<link rel=stylesheet href="resources/select-reset-non-interoperable-styles.css"> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<select style="appearance:base-select"> + <button type=popover>one</button> + <option>one</option> + <option>two</option> +</select> + +<script> +(async () => { + await test_driver.bless(); + document.querySelector('select').showPicker(); + document.documentElement.classList.remove('reftest-wait'); +})(); +</script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-custom-datalist-ref.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-custom-datalist-ref.html new file mode 100644 index 0000000000..8e5eadaf57 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-custom-datalist-ref.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<link rel=stylesheet href="resources/stylable-select-styles.css"> + +<div id=container class=stylable-select-container> + <button popovertarget=popover id=button>one</button> + <div id=popover popover=auto anchor=container class=stylable-select-datalist> + <div class=stylable-select-option>one</div> + <div class=stylable-select-option>two</div> + </div> +</div> + +<script> +document.getElementById('popover').showPopover(); +</script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-custom-datalist.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-custom-datalist.tentative.html new file mode 100644 index 0000000000..87425cf7a3 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-custom-datalist.tentative.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<link rel=author href="mailto:jarhar@chromium.org"> +<link rel=help href="https://github.com/whatwg/html/issues/9799"> +<link rel=match href="select-appearance-no-button-custom-datalist-ref.html"> +<link rel=stylesheet href="resources/select-reset-non-interoperable-styles.css"> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<select style="appearance:base-select"> + <datalist> + <option>one</option> + <option>two</option> + </datalist> +</select> + +<script> +(async () => { + await test_driver.bless(); + document.querySelector('select').showPicker(); + document.documentElement.classList.remove('reftest-wait'); +})(); +</script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-no-datalist.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-no-datalist.tentative.html new file mode 100644 index 0000000000..b2a6b5a6d3 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-appearance-no-button-no-datalist.tentative.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<link rel=author href="mailto:jarhar@chromium.org"> +<link rel=help href="https://github.com/whatwg/html/issues/9799"> +<link rel=match href="select-appearance-no-button-custom-datalist-ref.html"> +<link rel=stylesheet href="resources/select-reset-non-interoperable-styles.css"> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<select style="appearance:base-select"> + <option>one</option> + <option>two</option> +</select> + +<script> +(async () => { + await test_driver.bless(); + document.querySelector('select').showPicker(); + document.documentElement.classList.remove('reftest-wait'); +})(); +</script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-invalidation.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-invalidation.tentative.html new file mode 100644 index 0000000000..b6d85ac90a --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-invalidation.tentative.html @@ -0,0 +1,32 @@ +<!DOCTYPE html> +<html class=reftest-wait> +<link rel=author href="mailto:jarhar@chromium.org"> +<link rel=help href="https://github.com/whatwg/html/issues/9799"> +<link rel=match href="select-child-button-and-datalist-ref.html"> +<link rel=stylesheet href="resources/select-reset-non-interoperable-styles.css"> + +<style> +.blue { + color: blue; +} +</style> + +<select> + <button type=popover>button</button> + <datalist> + <option> + <span class=blue>option</span> one + </option> + <option> + <span class=blue>option</span> two + </option> + </datalist> +</select> + +<script> +requestAnimationFrame(() => { + document.querySelector('select').style = 'appearance:base-select'; + document.querySelector('button').click(); + document.documentElement.classList.remove('reftest-wait'); +}); +</script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-ref.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-ref.html new file mode 100644 index 0000000000..e99ca4d57a --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist-ref.html @@ -0,0 +1,25 @@ +<!DOCTYPE html> +<link rel=stylesheet href="resources/stylable-select-styles.css"> + +<style> +.blue { + color: blue; +} +</style> + +<div id=container class=stylable-select-container> + <button popovertarget=popover id=button>button</button> + <div id=popover popover=auto anchor=container class=stylable-select-datalist> + <div tabindex=0 class=stylable-select-option> + <span class=blue>option</span> one + </div> + <div tabindex=0 class=stylable-select-option> + <span class=blue>option</span> two + </div> + </div> +</div> + +<script> +document.getElementById('popover').showPopover(); +document.querySelector('div.stylable-select-option').focus(); +</script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist.tentative.html new file mode 100644 index 0000000000..610861aad8 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-child-button-and-datalist.tentative.html @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<link rel=author href="mailto:jarhar@chromium.org"> +<link rel=help href="https://github.com/whatwg/html/issues/9799"> +<link rel=match href="select-child-button-and-datalist-ref.html"> +<link rel=stylesheet href="resources/select-reset-non-interoperable-styles.css"> + +<style> +.blue { + color: blue; +} +</style> + +<select style="appearance:base-select"> + <button type=popover>button</button> + <datalist> + <option> + <span class=blue>option</span> one + </option> + <option> + <span class=blue>option</span> two + </option> + </datalist> +</select> + +<script> +document.querySelector('button').click(); +</script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-datalist-options-idl.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-datalist-options-idl.tentative.html index 993f6e126c..92eabdc5d8 100644 --- a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-datalist-options-idl.tentative.html +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-datalist-options-idl.tentative.html @@ -12,6 +12,9 @@ </div> <option class=three>three</option> </datalist> + <datalist> + <option>ignored since not in first datalist</option> + </datalist> </select> <script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-keyboard-behavior.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-keyboard-behavior.tentative.html new file mode 100644 index 0000000000..2fb11ba68b --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-keyboard-behavior.tentative.html @@ -0,0 +1,208 @@ +<!DOCTYPE html> +<link rel=author href="mailto:jarhar@chromium.org"> +<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1422275"> +<link rel=help href="https://github.com/openui/open-ui/issues/433#issuecomment-1452461404"> +<link rel=help href="https://github.com/openui/open-ui/issues/386#issuecomment-1452469497"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> + +<style> +select { + appearance: base-select; +} +</style> + +<form></form> + +<div id=notform> + <select id=defaultbutton-defaultdatalist> + <option class=one>one</option> + <option class=two>two</option> + <option class=three>three</option> + </select> + + <select id=defaultbutton-customdatalist> + <datalist> + <option class=one>one</option> + <option class=two>two</option> + <option class=three>three</option> + </datalist> + </select> + + <select id=custombutton-defaultdatalist> + <button type=popover>custom button</button> + <option class=one>one</option> + <option class=two>two</option> + <option class=three>three</option> + </select> + + <select id=custombutton-customdatalist> + <button type=popover>custom button</button> + <datalist> + <option class=one>one</option> + <option class=two>two</option> + <option class=three>three</option> + </datalist> + </select> +</div> + +<script> +const Enter = '\uE007'; +const Escape = '\uE00C'; +const ArrowLeft = '\uE012'; +const ArrowUp = '\uE013'; +const ArrowRight = '\uE014'; +const ArrowDown = '\uE015'; +const Space = ' '; +const form = document.querySelector('form'); +const notform = document.getElementById('notform'); + +for (const id of ['defaultbutton-defaultdatalist', + 'defaultbutton-customdatalist', + 'custombutton-defaultdatalist', + 'custombutton-customdatalist']) { + const select = document.getElementById(id); + + async function closeListbox() { + await test_driver.click(select); + } + + function addCloseCleanup(t) { + t.add_cleanup(async () => { + if (select.matches(':open')) { + await closeListbox(); + } + if (select.matches(':open')) { + throw new Error('select failed to close!'); + } + select.value = 'one'; + }); + } + + promise_test(async t => { + addCloseCleanup(t); + // TODO(http://crbug.com/1350299): When focus for custom buttons is fixed, + // then we shouldn't need to explicitly focus the custom button like this + // anymore. + const customButton = select.querySelector('button'); + if (customButton) { + customButton.focus(); + } else { + select.focus(); + } + assert_false(select.matches(':open'), + 'The select should initially be closed.'); + await test_driver.send_keys(document.activeElement, Space); + assert_true(select.matches(':open'), + 'The select should be open after pressing space.'); + }, `${id}: When the listbox is closed, spacebar should open the listbox.`); + + promise_test(async t => { + addCloseCleanup(t); + select.value = 'two'; + select.focus(); + assert_false(select.matches(':open'), + 'The select should initially be closed.'); + + await test_driver.send_keys(document.activeElement, ArrowLeft); + assert_true(select.matches(':open'), + 'Arrow left should open the listbox.'); + assert_equals(select.value, 'two', + 'Arrow left should not change the selected value.'); + await closeListbox(); + + await test_driver.send_keys(document.activeElement, ArrowUp); + assert_true(select.matches(':open'), + 'Arrow up should open the listbox.'); + assert_equals(select.value, 'two', + 'Arrow up should not change the selected value.'); + await closeListbox(); + + await test_driver.send_keys(document.activeElement, ArrowRight); + assert_true(select.matches(':open'), + 'Arrow right should open the listbox.'); + assert_equals(select.value, 'two', + 'Arrow right should not change the selected value.'); + await closeListbox(); + + await test_driver.send_keys(document.activeElement, ArrowDown); + assert_true(select.matches(':open'), + 'Arrow down should open the listbox.'); + assert_equals(select.value, 'two', + 'Arrow down should not change the selected value.'); + }, `${id}: When the listbox is closed, all arrow keys should open the listbox.`); + + promise_test(async t => { + addCloseCleanup(t); + + // TODO(http://crbug.com/1350299): When focus for custom buttons is fixed, + // then we shouldn't need to explicitly use the custom button like this + // anymore. + const customButton = select.querySelector('button'); + if (customButton) { + await test_driver.send_keys(customButton, Enter); + } else { + await test_driver.send_keys(select, Enter); + } + assert_false(select.matches(':open'), + 'Enter should not open the listbox when outside a form.'); + + form.appendChild(select); + let formWasSubmitted = false; + form.addEventListener('submit', event => { + event.preventDefault(); + formWasSubmitted = true; + }, {once: true}); + if (customButton) { + await test_driver.send_keys(customButton, Enter); + } else { + await test_driver.send_keys(select, Enter); + } + assert_true(formWasSubmitted, + 'Enter should submit the form when the listbox is closed.'); + assert_false(select.matches(':open'), + 'Enter should not open the listbox when it is in a form.'); + }, `${id}: When the listbox is closed, the enter key should submit the form or do nothing.`); + + promise_test(async t => { + addCloseCleanup(t); + const optionOne = select.querySelector('.one'); + const optionTwo = select.querySelector('.two'); + const optionThree = select.querySelector('.three'); + + select.value = 'two'; + await test_driver.click(select); + assert_true(select.matches(':open'), + 'The select should open when clicked.'); + assert_equals(document.activeElement, optionTwo, + 'The selected option should receive initial focus.'); + + await test_driver.send_keys(document.activeElement, ArrowDown); + assert_equals(document.activeElement, optionThree, + 'The next option should receive focus when the down arrow key is pressed.'); + assert_equals(select.value, 'two', + 'The selects value should not change when focusing another option.'); + + await test_driver.send_keys(document.activeElement, ArrowUp); + assert_equals(document.activeElement, optionTwo, + 'The previous option should receive focus when the up arrow key is pressed.'); + assert_equals(select.value, 'two', + 'The selects value should not change when focusing another option.'); + + await test_driver.send_keys(document.activeElement, ArrowUp); + assert_equals(document.activeElement, optionOne, + 'The first option should be selected.'); + assert_equals(select.value, 'two', + 'The selects value should not change when focusing another option.'); + + await test_driver.send_keys(document.activeElement, Enter); + assert_false(select.matches(':open'), + 'The listbox should be closed after pressing enter.'); + assert_equals(select.value, 'one', + 'The selects value should change after pressing enter on a different option.'); + }, `${id}: When the listbox is open, the enter key should commit the selected option.`); +} +</script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-mouse-behavior.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-mouse-behavior.tentative.html new file mode 100644 index 0000000000..aff976d1ad --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-mouse-behavior.tentative.html @@ -0,0 +1,62 @@ +<!DOCTYPE html> +<link rel=author href="mailto:jarhar@chromium.org"> +<link rel=help href="https://bugs.chromium.org/p/chromium/issues/detail?id=1422275"> +<link rel=help href="https://github.com/openui/open-ui/issues/433#issuecomment-1452461404"> +<link rel=help href="https://github.com/openui/open-ui/issues/386#issuecomment-1452469497"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="/resources/testdriver-actions.js"></script> + +<style> +select { + appearance: base-select; +} +</style> + +<!-- TODO(http://crbug.com/1511354): Add test cases with no <button> and no <datalist>. --> +<select> + <button type=popover>button</button> + <datalist> + <option class=one>one</option> + <option class=two>two</option> + </datalist> +</select> + +<script> +const select = document.querySelector('select'); +const button = document.querySelector('button'); +const optionOne = document.querySelector('option.one'); +const optionTwo = document.querySelector('option.two'); + +promise_test(async () => { + assert_false(select.matches(':open'), + 'Select should be closed at the start of the test.'); + + await test_driver.click(button); + assert_true(select.matches(':open'), + 'Select should be open after clicking the button.'); + + await test_driver.click(button); + assert_false(select.matches(':open'), + 'Select should be closed after clicking the button a second time.'); +}, 'Select with appearance:base-select should open and close when clicking the button.'); + +promise_test(async () => { + assert_false(select.matches(':open'), + 'Select should be closed at the start of the test.'); + assert_equals(select.value, 'one', + 'Select.value should be one at the start of the test.'); + + await test_driver.click(button); + assert_true(select.matches(':open'), + 'Select should be open after clicking the button.'); + + await test_driver.click(optionTwo); + assert_false(select.matches(':open'), + 'Select should be closed after clicking an option.'); + assert_equals(select.value, 'two', + 'Select.value should be two after clicking the option.'); +}, 'Clicking an option in an appearance:base-select select should choose the option and close the popover.'); +</script> diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-parsing.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-parsing.tentative.html index 31133446d4..31133446d4 100644 --- a/testing/web-platform/tests/html/semantics/forms/the-select-element/select-parsing.tentative.html +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/select-parsing.tentative.html diff --git a/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/selectedoption.tentative.html b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/selectedoption.tentative.html new file mode 100644 index 0000000000..16d711515c --- /dev/null +++ b/testing/web-platform/tests/html/semantics/forms/the-select-element/stylable-select/selectedoption.tentative.html @@ -0,0 +1,82 @@ +<!DOCTYPE html> +<link rel=author href="mailto:jarhar@chromium.org"> +<link rel=help href="https://github.com/whatwg/html/issues/9799"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<form> + <select style="appearance:base-select"> + <button> + <selectedoption></selectedoption> + </button> + <datalist> + <option class=one value=one> + <span class=one>span</span> one + </option> + <option class=two value=two> + <span class=two>span</span> two + </option> + </datalist> + </select> +</form> + +<script> +promise_test(async () => { + const optionOne = document.querySelector('option.one'); + const optionTwo = document.querySelector('option.two'); + const selectedOption = document.querySelector('selectedoption'); + const select = document.querySelector('select'); + const spanTwo = document.querySelector('span.two'); + const form = document.querySelector('form'); + const button = document.querySelector('button'); + + assert_equals(selectedOption.innerHTML, optionOne.innerHTML, + 'The innerHTML of <selectedoption> should initially match the innerHTML of the selected <option>.'); + + select.value = 'two'; + assert_equals(selectedOption.innerHTML, optionTwo.innerHTML, + 'The innerHTML of <selectedoption> should change after the selected option is changed.'); + + spanTwo.textContent = 'new span'; + assert_equals(selectedOption.innerHTML, optionTwo.innerHTML, + '<selectedoption> should respond to text content changes.'); + + spanTwo.appendChild(document.createElement('div')); + assert_equals(selectedOption.innerHTML, optionTwo.innerHTML, + '<selectedoption> should respond to new elements being added to descendants.'); + + spanTwo.setAttribute('data-foo', 'bar'); + assert_equals(selectedOption.innerHTML, optionTwo.innerHTML, + '<selectedoption> should respond to attributes being added to descendants.'); + + form.reset(); + assert_equals(select.value, 'one', + 'form.reset() should change the selects value to one.'); + assert_equals(selectedOption.innerHTML, optionOne.innerHTML, + 'The innerHTML of <selectedoption> should be updated in response to a form reset.'); + + await test_driver.bless(); + select.showPicker(); + await test_driver.click(optionTwo); + assert_equals(select.value, 'two', + 'Clicking on another option should change select.value.'); + assert_equals(selectedOption.innerHTML, optionTwo.innerHTML, + 'Clicking on an option element should update the <selectedoption>.'); + + selectedOption.remove(); + assert_equals(selectedOption.innerHTML, '', + 'Removing the <selectedoption> from the <select> should make it clear its contents.'); + button.appendChild(selectedOption); + assert_equals(selectedOption.innerHTML, optionTwo.innerHTML, + 'Re-inserting the <selectedoption> should make it update its contents.'); + + optionTwo.remove(); + assert_equals(selectedOption.innerHTML, optionOne.innerHTML, + 'The innerHTML of <selectedoption> should be updated in response to selected <option> removal.'); + optionOne.remove(); + assert_equals(selectedOption.innerHTML, '', + 'The content of <selectedoption> should be cleared if there is no selected <option>.'); +}, 'The <selectedoption> element should reflect the HTML contents of the selected <option>.'); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/idlharness.tentative.html b/testing/web-platform/tests/html/semantics/invokers/idlharness.tentative.html index b215f65813..8a86a5aaa1 100644 --- a/testing/web-platform/tests/html/semantics/invokers/idlharness.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/idlharness.tentative.html @@ -1,6 +1,6 @@ <!doctype html> <meta charset="utf-8" /> -<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> <link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> diff --git a/testing/web-platform/tests/html/semantics/invokers/interestelement-interface.tentative.html b/testing/web-platform/tests/html/semantics/invokers/interestelement-interface.tentative.html index dc119de833..8b1e375695 100644 --- a/testing/web-platform/tests/html/semantics/invokers/interestelement-interface.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/interestelement-interface.tentative.html @@ -1,5 +1,6 @@ <!doctype html> <meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> <meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" /> <link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" /> <script src="/resources/testharness.js"></script> diff --git a/testing/web-platform/tests/html/semantics/invokers/interestevent-dispatch-shadow.tentative.html b/testing/web-platform/tests/html/semantics/invokers/interestevent-dispatch-shadow.tentative.html new file mode 100644 index 0000000000..d96907ec84 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/interestevent-dispatch-shadow.tentative.html @@ -0,0 +1,104 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" /> +<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<div id="div"></div> +<button id="button"></button> + +<script> + test(function () { + const host = document.createElement("div"); + const shadow = host.attachShadow({ mode: "closed" }); + const slot = shadow.appendChild(document.createElement("slot")); + let childEvent = null; + let childEventTarget = null; + let childEventInvoker = null; + let hostEvent = null; + let hostEventTarget = null; + let hostEventInvoker = null; + slot.addEventListener( + "interest", + (e) => { + childEvent = e; + childEventTarget = e.target; + childEventInvoker = e.invoker; + }, + { once: true }, + ); + host.addEventListener( + "interest", + (e) => { + hostEvent = e; + hostEventTarget = e.target; + hostEventInvoker = e.invoker; + }, + { once: true }, + ); + const event = new InterestEvent("interest", { + bubbles: true, + invoker: slot, + composed: true, + }); + slot.dispatchEvent(event); + assert_true(childEvent instanceof InterestEvent, "slot saw interest event"); + assert_equals( + childEventTarget, + slot, + "target is child inside shadow boundary", + ); + assert_equals( + childEventInvoker, + slot, + "invoker is child inside shadow boundary", + ); + assert_equals( + hostEvent, + childEvent, + "event dispatch propagates across shadow boundary", + ); + assert_equals( + hostEventTarget, + host, + "target is retargeted to shadowroot host", + ); + assert_equals( + hostEventInvoker, + host, + "invoker is retargeted to shadowroot host", + ); + }, "InterestEvent propagates across shadow boundaries retargeting invoker"); + + test(function (t) { + const host = document.createElement("div"); + document.body.append(host); + t.add_cleanup(() => host.remove()); + const shadow = host.attachShadow({ mode: "open" }); + const button = shadow.appendChild(document.createElement("button")); + const interestee = host.appendChild(document.createElement("div")); + button.interestTargetElement = interestee; + let event = null; + let eventTarget = null; + let eventInvoker = null; + interestee.addEventListener( + "interest", + (e) => { + event = e; + eventTarget = e.target; + eventInvoker = e.invoker; + }, + { once: true }, + ); + button.focus(); + assert_true(event instanceof InterestEvent); + assert_equals(eventTarget, interestee, "target is interestee"); + assert_equals(eventInvoker, host, "interestee is host"); + }, "cross shadow InterestEvent retargets interestee to host element"); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/interestevent-interface.tentative.html b/testing/web-platform/tests/html/semantics/invokers/interestevent-interface.tentative.html new file mode 100644 index 0000000000..ed7d82f1fb --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/interestevent-interface.tentative.html @@ -0,0 +1,167 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" /> +<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<div id="div"></div> +<button id="button"></button> + +<script> + test(function () { + const event = new InterestEvent("test"); + assert_equals(event.action, ""); + assert_readonly(event, "action", "readonly attribute value"); + }, "action is a readonly defaulting to ''"); + + test(function () { + const event = new InterestEvent("test"); + assert_equals(event.invoker, null); + assert_readonly(event, "invoker", "readonly attribute value"); + }, "invoker is readonly defaulting to null"); + + test(function () { + const event = new InterestEvent("test", { action: "sAmPle" }); + assert_equals(event.action, "sAmPle"); + }, "action reflects initialized attribute"); + + test(function () { + const event = new InterestEvent("test", { action: undefined }); + assert_equals(event.action, ""); + }, "action set to undefined"); + + test(function () { + const event = new InterestEvent("test", { action: null }); + assert_equals(event.action, "null"); + }, "action set to null"); + + test(function () { + const event = new InterestEvent("test", { action: false }); + assert_equals(event.action, "false"); + }, "action set to false"); + + test(function () { + const event = new InterestEvent("test", { action: "" }); + assert_equals(event.action, ""); + }, "action explicitly set to empty string"); + + test(function () { + const event = new InterestEvent("test", { action: true }); + assert_equals(event.action, "true"); + }, "action set to true"); + + test(function () { + const event = new InterestEvent("test", { action: 0.5 }); + assert_equals(event.action, "0.5"); + }, "action set to a number"); + + test(function () { + const event = new InterestEvent("test", { action: [] }); + assert_equals(event.action, ""); + }, "action set to []"); + + test(function () { + const event = new InterestEvent("test", { action: [1, 2, 3] }); + assert_equals(event.action, "1,2,3"); + }, "action set to [1, 2, 3]"); + + test(function () { + const event = new InterestEvent("test", { action: { sample: 0.5 } }); + assert_equals(event.action, "[object Object]"); + }, "action set to an object"); + + test(function () { + const event = new InterestEvent("test", { + action: { + toString() { + return "sample"; + }, + }, + }); + assert_equals(event.action, "sample"); + }, "action set to an object with a toString function"); + + test(function () { + const eventInit = { action: "sample", invoker: document.body }; + const event = new InterestEvent("test", eventInit); + assert_equals(event.action, "sample"); + assert_equals(event.invoker, document.body); + }, "InterestEventInit properties set value"); + + test(function () { + const eventInit = { + action: "open", + invoker: document.getElementById("div"), + }; + const event = new InterestEvent("beforetoggle", eventInit); + assert_equals(event.action, "open"); + assert_equals(event.invoker, document.getElementById("div")); + }, "InterestEventInit properties set value 2"); + + test(function () { + const eventInit = { + action: "closed", + invoker: document.getElementById("button"), + }; + const event = new InterestEvent("toggle", eventInit); + assert_equals(event.action, "closed"); + assert_equals(event.invoker, document.getElementById("button")); + }, "InterestEventInit properties set value 3"); + + test(function () { + const event = new InterestEvent("test", { invoker: undefined }); + assert_equals(event.invoker, null); + }, "invoker set to undefined"); + + test(function () { + const event = new InterestEvent("test", { invoker: null }); + assert_equals(event.invoker, null); + }, "invoker set to null"); + + test(function () { + assert_throws_js( + TypeError, + function () { + new InterestEvent("test", { invoker: false }); + }, + "invoker is not an object", + ); + }, "invoker set to false"); + + test(function () { + assert_throws_js( + TypeError, + function () { + const event = new InterestEvent("test", { invoker: true }); + }, + "invoker is not an object", + ); + }, "invoker set to true"); + + test(function () { + assert_throws_js( + TypeError, + function () { + const event = new InterestEvent("test", { invoker: {} }); + }, + "invoker is not an object", + ); + }, "invoker set to {}"); + + test(function () { + assert_throws_js( + TypeError, + function () { + const eventInit = { action: "closed", invoker: new XMLHttpRequest() }; + const event = new InterestEvent("toggle", eventInit); + }, + "invoker is not an Element", + ); + }, "invoker set to non-Element EventTarget"); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/interesttarget-button-event-dispatch.tentative.html b/testing/web-platform/tests/html/semantics/invokers/interesttarget-button-event-dispatch.tentative.html new file mode 100644 index 0000000000..7fdfdfaa70 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/interesttarget-button-event-dispatch.tentative.html @@ -0,0 +1,155 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<div id="interestee"></div> +<button id="interestbutton" interesttarget="interestee">Button</button> +<a href="/" id="interestanchor" interesttarget="interestee">Anchor</a> +<button id="otherbutton">Other Button</button> + +<script> + promise_test(async function (t) { + t.add_cleanup(() => otherbutton.focus()); + let event = null; + interestee.addEventListener("interest", (e) => (event = e), { once: true }); + interestbutton.focus(); + assert_true(event instanceof InterestEvent, "event is InterestEvent"); + assert_equals(event.type, "interest", "type"); + assert_equals(event.bubbles, false, "bubbles"); + assert_equals(event.composed, true, "composed"); + assert_equals(event.isTrusted, true, "isTrusted"); + assert_equals(event.action, "", "action"); + assert_equals(event.target, interestee, "target"); + assert_equals(event.invoker, interestbutton, "invoker"); + }, "InterestEvent dispatches on button focus"); + + promise_test(async function (t) { + t.add_cleanup(() => otherbutton.focus()); + let event = null; + interestee.addEventListener("interest", (e) => (event = e), { once: true }); + await hoverOver(interestbutton); + assert_true(event instanceof InterestEvent, "event is InterestEvent"); + assert_equals(event.type, "interest", "type"); + assert_equals(event.bubbles, false, "bubbles"); + assert_equals(event.composed, true, "composed"); + assert_equals(event.isTrusted, true, "isTrusted"); + assert_equals(event.action, "", "action"); + assert_equals(event.target, interestee, "target"); + assert_equals(event.invoker, interestbutton, "invoker"); + }, "InterestEvent dispatches on button hover"); + + promise_test(async function (t) { + t.add_cleanup(() => otherbutton.focus()); + let event = null; + interestee.addEventListener("interest", (e) => (event = e), { once: true }); + interestanchor.focus(); + assert_true(event instanceof InterestEvent, "event is InterestEvent"); + assert_equals(event.type, "interest", "type"); + assert_equals(event.bubbles, false, "bubbles"); + assert_equals(event.composed, true, "composed"); + assert_equals(event.isTrusted, true, "isTrusted"); + assert_equals(event.action, "", "action"); + assert_equals(event.target, interestee, "target"); + assert_equals(event.invoker, interestanchor, "invoker"); + }, "InterestEvent dispatches on anchor focus"); + + promise_test(async function (t) { + t.add_cleanup(() => otherbutton.focus()); + let event = null; + interestee.addEventListener("interest", (e) => (event = e), { once: true }); + await hoverOver(interestanchor); + assert_true(event instanceof InterestEvent, "event is InterestEvent"); + assert_equals(event.type, "interest", "type"); + assert_equals(event.bubbles, false, "bubbles"); + assert_equals(event.composed, true, "composed"); + assert_equals(event.isTrusted, true, "isTrusted"); + assert_equals(event.action, "", "action"); + assert_equals(event.target, interestee, "target"); + assert_equals(event.invoker, interestanchor, "invoker"); + }, "InterestEvent dispatches on anchor hover"); + + promise_test(async function (t) { + t.add_cleanup(() => otherbutton.focus()); + let event = null; + interestee.addEventListener("interest", (e) => (event = e), { once: true }); + interestbutton.interestAction = "fooBar"; + interestbutton.focus(); + assert_true(event instanceof InterestEvent, "event is InterestEvent"); + assert_equals(event.type, "interest", "type"); + assert_equals(event.bubbles, false, "bubbles"); + assert_equals(event.composed, true, "composed"); + assert_equals(event.isTrusted, true, "isTrusted"); + assert_equals(event.action, "fooBar", "action"); + assert_equals(event.target, interestee, "target"); + assert_equals(event.invoker, interestbutton, "invoker"); + }, "event action is set to interestAction"); + + promise_test(async function (t) { + t.add_cleanup(() => otherbutton.focus()); + let event = null; + interestee.addEventListener("interest", (e) => (event = e), { once: true }); + interestbutton.setAttribute("interestaction", "BaRbAz"); + interestbutton.focus(); + assert_true(event instanceof InterestEvent, "event is InterestEvent"); + assert_equals(event.type, "interest", "type"); + assert_equals(event.bubbles, false, "bubbles"); + assert_equals(event.composed, true, "composed"); + assert_equals(event.isTrusted, true, "isTrusted"); + assert_equals(event.action, "BaRbAz", "action"); + assert_equals(event.target, interestee, "target"); + assert_equals(event.invoker, interestbutton, "invoker"); + }, "event action is set to interestaction attribute"); + + promise_test(async function (t) { + t.add_cleanup(() => { + interestbutton.removeAttribute('disabled'); + otherbutton.focus(); + }); + let called = false; + interestee.addEventListener( + "interest", + () => { + called = true; + }, + { once: true }, + ); + interestbutton.setAttribute("disabled", ""); + interestbutton.focus(); + assert_false(called, "event was not called"); + }, "event does not dispatch if invoker is disabled"); + + promise_test(async function (t) { + svgInterestee = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + t.add_cleanup(() => { + interestbutton.interestTargetElement = interestee; + svgInterestee.remove(); + otherbutton.focus(); + }); + document.body.append(svgInterestee); + let called = false; + assert_false(svgInterestee instanceof HTMLElement); + assert_true(svgInterestee instanceof Element); + let event = null; + svgInterestee.addEventListener( + "interest", + (e) => { + event = e; + called = true; + }, + { once: true }, + ); + interestbutton.interestTargetElement = svgInterestee; + interestbutton.focus(); + assert_true(called, "event was called"); + assert_equals(event.invoker, interestbutton, "event.invoker is set to right element"); + assert_equals(event.target, svgInterestee, "event.target is set to right element"); + }, "event dispatches if interestee is non-HTML Element"); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/interesttarget-on-popover-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/interesttarget-on-popover-behavior.tentative.html new file mode 100644 index 0000000000..b930fc645d --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/interesttarget-on-popover-behavior.tentative.html @@ -0,0 +1,113 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<meta name="author" title="Luke Warlow" href="mailto:lwarlow@igalia.com" /> +<link rel="help" href="https://open-ui.org/components/interest-invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<div id="interestee" popover> + Popover Content +</div> +<button id="interestbutton" interesttarget="interestee">Interest Invoker</button> +<button id="otherbutton">Other button</button> + +<script> + function reset() { + hoverOver(otherbutton); + otherbutton.focus(); + interestee.hidePopover(); + interestbutton.removeAttribute("interestaction"); + } + + // auto + + promise_test(async function (t) { + t.add_cleanup(reset); + assert_false(interestee.matches(":popover-open")); + await hoverOver(interestbutton); + assert_true(interestee.matches(":popover-open")); + }, "hover interest invoking (as auto) closed popover opens"); + + promise_test(async function (t) { + t.add_cleanup(reset); + interestee.showPopover(); + assert_true(interestee.matches(":popover-open")); + await hoverOver(interestbutton); + assert_false(interestee.matches(":popover-open")); + }, "hover interest invoking (as auto) open popover closes"); + + promise_test(async function (t) { + t.add_cleanup(reset); + assert_false(interestee.matches(":popover-open")); + interestbutton.focus(); + assert_true(interestee.matches(":popover-open")); + }, "focus interest invoking (as auto) closed popover opens"); + + promise_test(async function (t) { + t.add_cleanup(reset); + interestee.showPopover(); + assert_true(interestee.matches(":popover-open")); + interestbutton.focus(); + assert_false(interestee.matches(":popover-open")); + }, "focus interest invoking (as auto) open popover closes"); + + promise_test(async function (t) { + t.add_cleanup(reset); + assert_false(interestee.matches(":popover-open")); + interestee.addEventListener("interest", (e) => e.preventDefault(), { + once: true, + }); + await hoverOver(interestbutton); + assert_false(interestee.matches(":popover-open")); + }, "interest invoking (as auto) closed popover with preventDefault does not open"); + + // togglepopover + + promise_test(async function (t) { + t.add_cleanup(reset); + assert_false(interestee.matches(":popover-open")); + interestbutton.setAttribute("interestaction", "togglepopover"); + await hoverOver(interestbutton); + assert_true(interestee.matches(":popover-open")); + }, "hover interest invoking (as togglepopover) closed popover opens"); + + promise_test(async function (t) { + t.add_cleanup(reset); + interestee.showPopover(); + assert_true(interestee.matches(":popover-open")); + interestbutton.setAttribute("interestaction", "togglepopover"); + await hoverOver(interestbutton); + assert_false(interestee.matches(":popover-open")); + }, "hover interest invoking (as togglepopover) open popover closes"); + + promise_test(async function (t) { + t.add_cleanup(reset); + assert_false(interestee.matches(":popover-open")); + interestbutton.setAttribute("interestaction", "togglepopover"); + interestbutton.focus(); + assert_true(interestee.matches(":popover-open")); + }, "focus interest invoking (as togglepopover) closed popover opens"); + + promise_test(async function (t) { + t.add_cleanup(reset); + interestee.showPopover(); + assert_true(interestee.matches(":popover-open")); + interestbutton.setAttribute("interestaction", "togglepopover"); + interestbutton.focus(); + assert_false(interestee.matches(":popover-open")); + }, "focus interest invoking (as togglepopover) open popover closes"); + + promise_test(async function (t) { + t.add_cleanup(reset); + assert_false(interestee.matches(":popover-open")); + interestbutton.setAttribute("interestaction", "tOgGlEpOpOvEr"); + interestbutton.focus(); + assert_true(interestee.matches(":popover-open")); + }, "interest invoking (as togglepopover - case insensitive) closed popover opens"); + +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html index 5a2854fe31..5adacadabb 100644 --- a/testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/invokeelement-interface.tentative.html @@ -1,6 +1,6 @@ <!doctype html> <meta charset="utf-8" /> -<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> <link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invokeevent-dispatch-shadow.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invokeevent-dispatch-shadow.tentative.html index 84337d5723..1ecff88760 100644 --- a/testing/web-platform/tests/html/semantics/invokers/invokeevent-dispatch-shadow.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/invokeevent-dispatch-shadow.tentative.html @@ -1,6 +1,6 @@ <!doctype html> <meta charset="utf-8" /> -<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> <link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html index 382f808071..0cfb4d5ee5 100644 --- a/testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/invokeevent-interface.tentative.html @@ -1,6 +1,6 @@ <!doctype html> <meta charset="utf-8" /> -<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> <link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html index d8d9c04022..9120cc3192 100644 --- a/testing/web-platform/tests/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-button-event-dispatch.tentative.html @@ -1,6 +1,7 @@ <!doctype html> <meta charset="utf-8" /> -<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> +<meta name="timeout" content="long" /> <link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> @@ -27,35 +28,63 @@ assert_equals(event.invoker, invokerbutton, "invoker"); }, "event dispatches on click"); - promise_test(async function (t) { - let event = null; - invokee.addEventListener("invoke", (e) => (event = e), { once: true }); - invokerbutton.invokeAction = "fooBar"; - await clickOn(invokerbutton); - assert_true(event instanceof InvokeEvent, "event is InvokeEvent"); - assert_equals(event.type, "invoke", "type"); - assert_equals(event.bubbles, false, "bubbles"); - assert_equals(event.composed, true, "composed"); - assert_equals(event.isTrusted, true, "isTrusted"); - assert_equals(event.action, "fooBar", "action"); - assert_equals(event.target, invokee, "target"); - assert_equals(event.invoker, invokerbutton, "invoker"); - }, "event action is set to invokeAction"); + // valid custom invokeactions + ["-foo", "foo-", "cAsE-cArRiEs", "-", "-a-", "a-b", "---", "show-picker"].forEach( + (action) => { + promise_test(async function (t) { + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + let event = null; + invokee.addEventListener("invoke", (e) => (event = e), { once: true }); + invokerbutton.invokeAction = action; + await clickOn(invokerbutton); + assert_true(event instanceof InvokeEvent, "event is InvokeEvent"); + assert_equals(event.type, "invoke", "type"); + assert_equals(event.bubbles, false, "bubbles"); + assert_equals(event.composed, true, "composed"); + assert_equals(event.isTrusted, true, "isTrusted"); + assert_equals(event.action, action, "action"); + assert_equals(event.target, invokee, "target"); + assert_equals(event.invoker, invokerbutton, "invoker"); + }, `setting custom invokeAction property to ${action} (must include dash) sets event action`); - promise_test(async function (t) { - let event = null; - invokee.addEventListener("invoke", (e) => (event = e), { once: true }); - invokerbutton.setAttribute("invokeaction", "BaRbAz"); - await clickOn(invokerbutton); - assert_true(event instanceof InvokeEvent, "event is InvokeEvent"); - assert_equals(event.type, "invoke", "type"); - assert_equals(event.bubbles, false, "bubbles"); - assert_equals(event.composed, true, "composed"); - assert_equals(event.isTrusted, true, "isTrusted"); - assert_equals(event.action, "BaRbAz", "action"); - assert_equals(event.target, invokee, "target"); - assert_equals(event.invoker, invokerbutton, "invoker"); - }, "event action is set to invokeaction attribute"); + promise_test(async function (t) { + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + let event = null; + invokee.addEventListener("invoke", (e) => (event = e), { once: true }); + invokerbutton.setAttribute("invokeaction", action); + await clickOn(invokerbutton); + assert_true(event instanceof InvokeEvent, "event is InvokeEvent"); + assert_equals(event.type, "invoke", "type"); + assert_equals(event.bubbles, false, "bubbles"); + assert_equals(event.composed, true, "composed"); + assert_equals(event.isTrusted, true, "isTrusted"); + assert_equals(event.action, action, "action"); + assert_equals(event.target, invokee, "target"); + assert_equals(event.invoker, invokerbutton, "invoker"); + }, `setting custom invokeaction attribute to ${action} (must include dash) sets event action`); + }, + ); + + // invalid custom invokeactions + ["foo", "foobar", "foo bar", "em—dash", "hidedocument"].forEach((action) => { + promise_test(async function (t) { + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + let event = null; + invokee.addEventListener("invoke", (e) => (event = e), { once: true }); + invokerbutton.invokeAction = action; + await clickOn(invokerbutton); + assert_equals(event, null, "event should not have fired"); + }, `setting custom invokeAction property to ${action} (no dash) did not dispatch an event`); + + promise_test(async function (t) { + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + let event = null; + invokee.addEventListener("invoke", (e) => (event = e), { once: true }); + invokerbutton.setAttribute("invokeaction", action); + await clickOn(invokerbutton); + assert_equals(event, null, "event should not have fired"); + }, `setting custom invokeaction attribute to ${action} (no dash) did not dispatch an event`); + }); promise_test(async function (t) { let called = false; @@ -78,7 +107,7 @@ }, "event does not dispatch if click:preventDefault is called"); promise_test(async function (t) { - t.add_cleanup(() => invokerbutton.removeAttribute('disabled')); + t.add_cleanup(() => invokerbutton.removeAttribute("disabled")); let called = false; invokee.addEventListener( "invoke", @@ -93,7 +122,7 @@ }, "event does not dispatch if invoker is disabled"); promise_test(async function (t) { - svgInvokee = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svgInvokee = document.createElementNS("http://www.w3.org/2000/svg", "svg"); t.add_cleanup(() => { invokerbutton.invokeTargetElement = invokee; svgInvokee.remove(); @@ -115,7 +144,15 @@ invokerbutton.invokeTargetElement = svgInvokee; await clickOn(invokerbutton); assert_true(called, "event was called"); - assert_equals(eventInvoker, invokerbutton, "event.invoker is set to right element"); - assert_equals(eventTarget, svgInvokee, "event.target is set to right element"); + assert_equals( + eventInvoker, + invokerbutton, + "event.invoker is set to right element", + ); + assert_equals( + eventTarget, + svgInvokee, + "event.target is set to right element", + ); }, "event dispatches if invokee is non-HTML Element"); </script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-fullscreen-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-fullscreen-behavior.tentative.html index b72020283e..2e2c5f683f 100644 --- a/testing/web-platform/tests/html/semantics/invokers/invoketarget-fullscreen-behavior.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-fullscreen-behavior.tentative.html @@ -1,6 +1,7 @@ <!doctype html> <meta charset="utf-8" /> <meta name="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<meta name="timeout" content="long" /> <link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-behavior.tentative.html index f3abeae165..37acb7a539 100644 --- a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-behavior.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-behavior.tentative.html @@ -1,6 +1,7 @@ <!doctype html> <meta charset="utf-8" /> <meta name="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<meta name="timeout" content="long" /> <link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> @@ -25,7 +26,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", ""); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -43,7 +44,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "playpause"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_false(invokee.paused); @@ -59,7 +60,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "playpause"); invokerbutton.click(); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_false(invokee.paused); @@ -78,7 +79,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "playpause"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -91,12 +92,12 @@ invokee.currentTime = 0; invokee.muted = false; }); - await test_driver.bless('play audio'); + await test_driver.bless("play audio"); invokee.play(); assert_false(invokee.paused); invokerbutton.setAttribute("invokeaction", "playpause"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -114,7 +115,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "play"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_false(invokee.paused); @@ -130,7 +131,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "play"); invokerbutton.click(); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_false(invokee.paused); @@ -149,7 +150,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "play"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -162,12 +163,12 @@ invokee.currentTime = 0; invokee.muted = false; }); - await test_driver.bless('play audio'); + await test_driver.bless("play audio"); invokee.play(); assert_false(invokee.paused); invokerbutton.setAttribute("invokeaction", "play"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_false(invokee.paused); @@ -185,8 +186,8 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "pause"); await clickOn(invokerbutton); - await new Promise(resolve => { - requestAnimationFrame(resolve); + await new Promise((resolve) => { + requestAnimationFrame(resolve); }); assert_true(invokee.paused); }, "invoking audio with pause action is a no-op"); @@ -204,7 +205,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "pause"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -217,12 +218,12 @@ invokee.currentTime = 0; invokee.muted = false; }); - await test_driver.bless('play audio'); + await test_driver.bless("play audio"); invokee.play(); assert_false(invokee.paused); invokerbutton.setAttribute("invokeaction", "pause"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -240,7 +241,7 @@ assert_false(invokee.muted); invokerbutton.setAttribute("invokeaction", "toggleMuted"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.muted); @@ -259,7 +260,7 @@ assert_false(invokee.muted); invokerbutton.setAttribute("invokeaction", "toggleMuted"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_false(invokee.muted); @@ -276,10 +277,9 @@ assert_true(invokee.muted); invokerbutton.setAttribute("invokeaction", "toggleMuted"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_false(invokee.muted); }, "invoking muted audio with toggleMuted action unmutes it"); - </script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-invalid-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-invalid-behavior.tentative.html new file mode 100644 index 0000000000..9e15ce38e8 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-audio-invalid-behavior.tentative.html @@ -0,0 +1,37 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> +<meta name="timeout" content="long" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<audio controls id="invokee" src="/media/sound_5.mp3"></audio> +<button id="invokerbutton" invoketarget="invokee"></button> + +<script> + // invalid actions on audio + [ + "foo-bar", + "showpopover", + "showmodal", + "showpicker", + "open", + "close", + ].forEach((action) => { + promise_test(async function (t) { + t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + invokerbutton.setAttribute("invokeaction", action); + assert_true(invokee.paused); + assert_false(invokee.muted); + await clickOn(invokerbutton); + await waitForRender(); + assert_true(invokee.paused); + assert_false(invokee.muted); + }, `invoking (as ${action}) on audio does nothing`); + }); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-behavior.tentative.html index c6735e2611..ad9b6caa57 100644 --- a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-behavior.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-behavior.tentative.html @@ -1,6 +1,7 @@ <!doctype html> <meta charset="utf-8" /> <meta name="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<meta name="timeout" content="long" /> <link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> @@ -9,210 +10,136 @@ <script src="/resources/testdriver-vendor.js"></script> <script src="resources/invoker-utils.js"></script> -<details id="invokee"> - Details Contents -</details> +<details id="invokee">Details Contents</details> <button id="invokerbutton" invoketarget="invokee"></button> <script> - // auto - - promise_test(async function (t) { - assert_false(invokee.matches("[open]")); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.removeAttribute('open')); - assert_true(invokee.matches("[open]")); - }, "invoking closed details with auto action opens"); - - promise_test(async function (t) { - assert_false(invokee.matches("[open]")); - invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, - }); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.removeAttribute('open')); - assert_false(invokee.matches("[open]")); - }, "invoking closed details with auto action and preventDefault does not open"); - - promise_test(async function (t) { - invokee.setAttribute('open', ''); - assert_true(invokee.matches("[open]")); - await clickOn(invokerbutton); - assert_false(invokee.matches("[open]")); - }, "invoking open details with auto action closes"); - - promise_test(async function (t) { - invokee.setAttribute('open', ''); - t.add_cleanup(() => invokee.removeAttribute('open')); - invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, - }); - assert_true(invokee.matches("[open]")); - await clickOn(invokerbutton); - assert_true(invokee.matches("[open]")); - }, "invoking open details with auto action and preventDefault does not close"); - - promise_test(async function (t) { - t.add_cleanup(() => invokee.removeAttribute('open')); - invokee.addEventListener("invoke", (e) => { - invokee.setAttribute('open', ''); - }, { - once: true, - }); - assert_false(invokee.matches("[open]")); - await clickOn(invokerbutton); - assert_false(invokee.matches("[open]")); - }, "invoking details with auto action where event listener opens leads to a closed details"); - - promise_test(async function (t) { - invokee.setAttribute('open', ''); - t.add_cleanup(() => invokee.removeAttribute('open')); - invokee.addEventListener("invoke", (e) => { - invokee.removeAttribute('open'); - }, { - once: true, - }); - assert_true(invokee.matches("[open]")); - await clickOn(invokerbutton); - assert_true(invokee.matches("[open]")); - }, "invoking open details with auto action where event listener closes leads to an open details"); - - // toggle - - promise_test(async function (t) { - assert_false(invokee.matches("[open]")); - invokerbutton.setAttribute("invokeaction", "toggle"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.removeAttribute('open')); - assert_true(invokee.matches("[open]")); - }, "invoking closed details with toggle action opens"); - - promise_test(async function (t) { - assert_false(invokee.matches("[open]")); - invokerbutton.setAttribute("invokeaction", "tOgGlE"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.removeAttribute('open')); - assert_true(invokee.matches("[open]")); - }, "invoking closed details with toggle (case-insensitive) action opens"); - - promise_test(async function (t) { - assert_false(invokee.matches("[open]")); - invokerbutton.setAttribute("invokeaction", "toggle"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - invokee.addEventListener("invoke", (e) => e.preventDefault(), { + function resetState() { + invokerbutton.removeAttribute("invokeaction"); + invokee.removeAttribute("open"); + } + + // Open actions + [ + null, + "", + "toggle", + "open", + /* test case sensitivity */ + "tOgGlE", + "oPeN", + ].forEach((action) => { + promise_test( + async function (t) { + t.add_cleanup(resetState); + if (action !== null) invokerbutton.invokeAction = action; + assert_false(invokee.matches("[open]")); + await clickOn(invokerbutton); + assert_true(invokee.matches("[open]")); + }, + `invoking (as ${ + action === null ? "auto" : action || "explicit empty" + }) closed details opens`, + ); + + promise_test( + async function (t) { + t.add_cleanup(resetState); + if (action !== null) invokerbutton.invokeAction = action; + assert_false(invokee.matches("[open]")); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(invokerbutton); + t.add_cleanup(() => invokee.removeAttribute("open")); + assert_false(invokee.matches("[open]")); + }, + `invoking (as ${ + action === null ? "auto" : action || "explicit empty" + }) closed details with preventDefault does not open`, + ); + }); + + // Close actions + [ + null, + "", + "toggle", + "close", + /* test case sensitivity */ + "tOgGlE", + "cLoSe", + ].forEach((action) => { + promise_test( + async function (t) { + t.add_cleanup(resetState); + if (action !== null) invokerbutton.invokeAction = action; + invokee.setAttribute("open", ""); + assert_true(invokee.matches("[open]")); + await clickOn(invokerbutton); + assert_false(invokee.matches("[open]")); + }, + `invoking (as ${ + action === null ? "auto" : action || "explicit empty" + }) open details closes`, + ); + + promise_test( + async function (t) { + t.add_cleanup(resetState); + if (action !== null) invokerbutton.invokeAction = action; + invokee.setAttribute("open", ""); + invokerbutton.setAttribute("invokeaction", "toggle"); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_true(invokee.matches("[open]")); + await clickOn(invokerbutton); + assert_true(invokee.matches("[open]")); + }, + `invoking (as ${ + action === null ? "auto" : action || "explicit empty" + }) open details with prevent default closes`, + ); + }); + + // toggle specific + + promise_test(async function (t) { + t.add_cleanup(resetState); + invokerbutton.invokeAction = "toggle"; + invokee.addEventListener( + "invoke", + (e) => { + invokee.setAttribute("open", ""); + }, + { once: true, - }); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.removeAttribute('open')); + }, + ); assert_false(invokee.matches("[open]")); - }, "invoking closed details with toggle action and preventDefault does not open"); - - promise_test(async function (t) { - invokee.setAttribute('open', ''); - invokerbutton.setAttribute("invokeaction", "toggle"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - assert_true(invokee.matches("[open]")); await clickOn(invokerbutton); assert_false(invokee.matches("[open]")); - }, "invoking open details with toggle action closes"); + }, "invoking (as toggle) closed details where event listener opens leads to a closed details"); - promise_test(async function (t) { - invokee.setAttribute('open', ''); - t.add_cleanup(() => invokee.removeAttribute('open')); - invokerbutton.setAttribute("invokeaction", "toggle"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, - }); - assert_true(invokee.matches("[open]")); - await clickOn(invokerbutton); - assert_true(invokee.matches("[open]")); - }, "invoking open details with toggle action and preventDefault does not close"); - - // open - - promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", "open"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - assert_false(invokee.matches("[open]")); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.removeAttribute('open')); - assert_true(invokee.matches("[open]")); - }, "invoking closed details with open action opens"); - - promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", "oPeN"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - assert_false(invokee.matches("[open]")); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.removeAttribute('open')); - assert_true(invokee.matches("[open]")); - }, "invoking closed details with open (case insensitive) action opens"); + // open specific promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", "open"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - invokee.setAttribute('open', ''); + t.add_cleanup(resetState); + invokerbutton.invokeAction = "open"; + invokee.setAttribute("open", ""); assert_true(invokee.matches("[open]")); await clickOn(invokerbutton); - t.add_cleanup(() => invokee.removeAttribute('open')); assert_true(invokee.matches("[open]")); }, "invoking open details with open action is noop"); - promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", "open"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - assert_false(invokee.matches("[open]")); - invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, - }); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.removeAttribute('open')); - assert_false(invokee.matches("[open]")); - }, "invoking closed popover with open action and preventDefault does not open"); - // close promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", "close"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); + t.add_cleanup(resetState); + invokerbutton.invokeAction = "close"; assert_false(invokee.matches("[open]")); await clickOn(invokerbutton); assert_false(invokee.matches("[open]")); }, "invoking closed details with close action is noop"); - - promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", "close"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - invokee.setAttribute('open', ''); - assert_true(invokee.matches("[open]")); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.removeAttribute('open')); - assert_false(invokee.matches("[open]")); - }, "invoking open details with close action closes"); - - promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", "cLoSe"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - invokee.setAttribute('open', ''); - assert_true(invokee.matches("[open]")); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.removeAttribute('open')); - assert_false(invokee.matches("[open]")); - }, "invoking open details with close (case insensitive) action closes"); - - promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", "close"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - invokee.setAttribute('open', ''); - t.add_cleanup(() => invokee.removeAttribute('open')); - assert_true(invokee.matches("[open]")); - invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, - }); - await clickOn(invokerbutton); - assert_true(invokee.matches("[open]")); - }, "invoking open details with close action with preventDefault does not close"); </script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-invalid-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-invalid-behavior.tentative.html new file mode 100644 index 0000000000..d5e90af9c0 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-details-invalid-behavior.tentative.html @@ -0,0 +1,49 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> +<meta name="timeout" content="long" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<details id="invokee">Details Contents</details> +<button id="invokerbutton" invoketarget="invokee"></button> + +<script> + function resetState() { + invokerbutton.removeAttribute("invokeaction"); + invokee.removeAttribute("open"); + } + + // invalid actions on details + [ + "foo-bar", + "showpopover", + "showmodal", + "showpicker", + "hidepopover", + "hide", + "toggleopen", + ].forEach((action) => { + promise_test(async function (t) { + t.add_cleanup(resetState); + invokerbutton.invokeAction = action; + assert_false(invokee.matches("[open]")); + await clickOn(invokerbutton); + assert_false(invokee.matches("[open]")); + }, `invoking (as ${action}) on details does nothing`); + + promise_test(async function (t) { + t.add_cleanup(resetState); + invokerbutton.invokeAction = action; + invokee.setAttribute("open", ""); + assert_true(invokee.matches("[open]")); + await clickOn(invokerbutton); + assert_true(invokee.matches("[open]")); + }, `invoking (as ${action}) on open details does nothing`); + }); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-dialog-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-dialog-behavior.tentative.html index 774d308703..9f73e092b0 100644 --- a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-dialog-behavior.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-dialog-behavior.tentative.html @@ -295,67 +295,6 @@ assert_false(invokee.matches(":modal"), "invokee :modal"); }, "invoking (as close) already closed dialog is noop"); - // invalid - [ - "foo", - "foo-bar", - "auto", - "showpopover", - "hidepopover", - "togglepopover", - "showpicker", - ].forEach((action) => { - promise_test(async function (t) { - t.add_cleanup(resetState); - invokerbutton.setAttribute("invokeaction", action); - assert_false(invokee.open, "invokee.open"); - assert_false(invokee.matches(":modal"), "invokee :modal"); - await clickOn(invokerbutton); - assert_false(invokee.open, "invokee.open"); - assert_false(invokee.matches(":modal"), "invokee :modal"); - }, `invoking (as ${action}) on dialog does nothing`); - - promise_test(async function (t) { - t.add_cleanup(resetState); - containedinvoker.setAttribute("invokeaction", action); - invokee.show(); - assert_true(invokee.open, "invokee.open"); - assert_false(invokee.matches(":modal"), "invokee :modal"); - await clickOn(containedinvoker); - assert_true(invokee.open, "invokee.open"); - assert_false(invokee.matches(":modal"), "invokee :modal"); - }, `invoking (as ${action}) on open dialog does nothing`); - - promise_test(async function (t) { - t.add_cleanup(resetState); - containedinvoker.setAttribute("invokeaction", action); - invokee.showModal(); - assert_true(invokee.open, "invokee.open"); - assert_true(invokee.matches(":modal"), "invokee :modal"); - await clickOn(containedinvoker); - assert_true(invokee.open, "invokee.open"); - assert_true(invokee.matches(":modal"), "invokee :modal"); - }, `invoking (as ${action}) on open modal dialog does nothing`); - - promise_test(async function (t) { - t.add_cleanup(resetState); - containedinvoker.setAttribute("invokeaction", action); - invokee.showModal(); - assert_true(invokee.open, "invokee.open"); - assert_true(invokee.matches(":modal"), "invokee :modal"); - invokee.addEventListener( - "invoke", - (e) => { - containedinvoker.setAttribute("invokeaction", ""); - }, - { once: true }, - ); - await clickOn(containedinvoker); - assert_true(invokee.open, "invokee.open"); - assert_true(invokee.matches(":modal"), "invokee :modal"); - }, `invoking (as ${action}) on open modal while changing the attributer does nothing`); - }); - // Open Popovers using Dialog actions ["showmodal", "close", ""].forEach((action) => { ["manual", "auto"].forEach((popoverState) => { diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-dialog-invalid-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-dialog-invalid-behavior.tentative.html new file mode 100644 index 0000000000..af84c22594 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-dialog-invalid-behavior.tentative.html @@ -0,0 +1,120 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> +<meta name="timeout" content="long"> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<dialog id="invokee"> + <button id="containedinvoker" invoketarget="invokee"></button> +</dialog> +<button id="invokerbutton" invoketarget="invokee"></button> + +<script> + function resetState() { + invokee.close(); + try { invokee.hidePopover(); } catch {} + invokee.removeAttribute("popover"); + invokerbutton.removeAttribute("invokeaction"); + containedinvoker.removeAttribute("invokeaction"); + } + + // invalid + [ + "foo", + "foo-bar", + "auto", + "showpopover", + "hidepopover", + "togglepopover", + "showpicker", + ].forEach((action) => { + promise_test(async function (t) { + t.add_cleanup(resetState); + invokerbutton.setAttribute("invokeaction", action); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + await clickOn(invokerbutton); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, `invoking (as ${action}) on dialog does nothing`); + + promise_test(async function (t) { + t.add_cleanup(resetState); + containedinvoker.setAttribute("invokeaction", action); + invokee.show(); + assert_true(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + await clickOn(containedinvoker); + assert_true(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, `invoking (as ${action}) on open dialog does nothing`); + + promise_test(async function (t) { + t.add_cleanup(resetState); + containedinvoker.setAttribute("invokeaction", action); + invokee.showModal(); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + await clickOn(containedinvoker); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + }, `invoking (as ${action}) on open modal dialog does nothing`); + + promise_test(async function (t) { + t.add_cleanup(resetState); + containedinvoker.setAttribute("invokeaction", action); + invokee.showModal(); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + invokee.addEventListener( + "invoke", + (e) => { + containedinvoker.setAttribute("invokeaction", ""); + }, + { once: true }, + ); + await clickOn(containedinvoker); + assert_true(invokee.open, "invokee.open"); + assert_true(invokee.matches(":modal"), "invokee :modal"); + }, `invoking (as ${action}) on open modal while changing the attributer does nothing`); + }); + + // Open Popovers using Dialog actions + ["showmodal", "close", ""].forEach((action) => { + ["manual", "auto"].forEach((popoverState) => { + promise_test( + async function (t) { + t.add_cleanup(resetState); + invokee.setAttribute("popover", popoverState); + invokee.showPopover(); + containedinvoker.setAttribute("invokeaction", action); + assert_true( + invokee.matches(":popover-open"), + "invokee :popover-open", + ); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(containedinvoker); + assert_true( + invokee.matches(":popover-open"), + "invokee :popover-open", + ); + assert_false(invokee.open, "invokee.open"); + assert_false(invokee.matches(":modal"), "invokee :modal"); + }, + `invoking (as ${ + action || "explicit empty" + }) dialog as open popover=${popoverState} is noop`, + ); + }); + }); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-behavior.tentative.html index 2bddfa7621..f414559e55 100644 --- a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-behavior.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-behavior.tentative.html @@ -1,6 +1,7 @@ <!doctype html> <meta charset="utf-8" /> -<meta name="author" title="Keith Cirkel" href="mailto:keithamus@github.com" /> +<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> +<meta name="timeout" content="long" /> <link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> @@ -19,215 +20,138 @@ promise_test(async function (t) { assert_false(invokee.matches(":popover-open")); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.hidePopover()); - assert_true(invokee.matches(":popover-open")); - }, "invoking (as auto) closed popover opens"); - - promise_test(async function (t) { - assert_false(invokee.matches(":popover-open")); - invokee.addEventListener("invoke", (e) => e.preventDefault(), { + invokee.addEventListener("invoke", (e) => { invokerbutton.setAttribute('invokeaction', 'hidepopover'); }, { once: true, }); await clickOn(invokerbutton); - t.add_cleanup(() => invokee.hidePopover()); - assert_false(invokee.matches(":popover-open")); - }, "invoking (as auto) closed popover with preventDefault does not open"); - - promise_test(async function (t) { - invokee.showPopover(); - assert_true(invokee.matches(":popover-open")); - await clickOn(invokerbutton); - assert_false(invokee.matches(":popover-open")); - }, "invoking (as auto) open popover closes"); - - promise_test(async function (t) { - invokee.showPopover(); - assert_true(invokee.matches(":popover-open")); - await clickOn(containedinvoker); - assert_false(invokee.matches(":popover-open")); - }, "invoking (as auto) from within open popover closes"); - - promise_test(async function (t) { - invokee.showPopover(); - t.add_cleanup(() => invokee.hidePopover()); - invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, + t.add_cleanup(() => { + invokee.hidePopover(); + invokerbutton.removeAttribute("invokeaction"); }); assert_true(invokee.matches(":popover-open")); - await clickOn(containedinvoker); - assert_true(invokee.matches(":popover-open")); - }, "invoking (as auto) open popover with preventDefault does not close"); - - // togglepopover - - promise_test(async function (t) { - assert_false(invokee.matches(":popover-open")); - invokerbutton.setAttribute("invokeaction", "togglepopover"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.hidePopover()); - assert_true(invokee.matches(":popover-open")); - }, "invoking (as togglepopover) closed popover opens"); - - promise_test(async function (t) { - assert_false(invokee.matches(":popover-open")); - invokerbutton.setAttribute("invokeaction", "tOgGlEpOpOvEr"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.hidePopover()); - assert_true(invokee.matches(":popover-open")); - }, "invoking (as togglepopover - case insensitive) closed popover opens"); - - promise_test(async function (t) { - assert_false(invokee.matches(":popover-open")); - invokerbutton.setAttribute("invokeaction", "togglepopover"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, - }); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.hidePopover()); - assert_false(invokee.matches(":popover-open")); - }, "invoking (as togglepopover) closed popover with preventDefault does not open"); - - promise_test(async function (t) { - invokee.showPopover(); - containedinvoker.setAttribute("invokeaction", "togglepopover"); - t.add_cleanup(() => containedinvoker.removeAttribute("invokeaction")); - assert_true(invokee.matches(":popover-open")); - await clickOn(invokerbutton); - assert_false(invokee.matches(":popover-open")); - }, "invoking (as togglepopover) open popover closes"); - - promise_test(async function (t) { - invokee.showPopover(); - containedinvoker.setAttribute("invokeaction", "togglepopover"); - t.add_cleanup(() => containedinvoker.removeAttribute("invokeaction")); - assert_true(invokee.matches(":popover-open")); - await clickOn(containedinvoker); - assert_false(invokee.matches(":popover-open")); - }, "invoking (as togglepopover) from within open popover closes"); - - promise_test(async function (t) { - invokee.showPopover(); - t.add_cleanup(() => invokee.hidePopover()); - containedinvoker.setAttribute("invokeaction", "togglepopover"); - t.add_cleanup(() => containedinvoker.removeAttribute("invokeaction")); - invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, - }); - assert_true(invokee.matches(":popover-open")); - await clickOn(containedinvoker); - assert_true(invokee.matches(":popover-open")); - }, "invoking (as togglepopover) open popover with preventDefault does not close"); - - // showpopover - - promise_test(async function (t) { + }, "changing invokeaction attribute inside invokeevent doesn't impact the invocation"); + + function resetState() { + invokerbutton.removeAttribute("invokeaction"); + containedinvoker.removeAttribute("invokeaction"); + try { + invokee.hidePopover(); + } catch {} + invokee.setAttribute("popover", ""); + } + + // Open actions + [ + null, + "", + "togglepopover", + "showpopover", + /* test case sensitivity */ + "tOgGlEpOpOvEr", + "sHoWpOpOvEr", + ].forEach((action) => { + promise_test( + async function (t) { + t.add_cleanup(resetState); + if (action !== null) invokerbutton.invokeAction = action; + assert_false(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + assert_true(invokee.matches(":popover-open")); + }, + `invoking (as ${ + action === null ? "auto" : action || "explicit empty" + }) closed popover opens`, + ); + + promise_test( + async function (t) { + t.add_cleanup(resetState); + if (action !== null) invokerbutton.invokeAction = action; + assert_false(invokee.matches(":popover-open")); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + await clickOn(invokerbutton); + assert_false(invokee.matches(":popover-open")); + }, + `invoking (as ${ + action === null ? "auto" : action || "explicit empty" + }) closed popover with preventDefault does not open`, + ); + }); + + // Close actions + [ + null, + "", + "togglepopover", + "hidepopover", + /* test case sensitivity */ + "tOgGlEpOpOvEr", + "hIdEpOpOvEr", + ].forEach((action) => { + promise_test( + async function (t) { + t.add_cleanup(resetState); + if (action !== null) invokerbutton.invokeAction = action; + invokee.showPopover(); + assert_true(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + assert_false(invokee.matches(":popover-open")); + }, + `invoking (as ${ + action === null ? "auto" : action || "explicit empty" + }) open popover closes`, + ); + + promise_test( + async function (t) { + t.add_cleanup(resetState); + if (action !== null) containedinvoker.invokeAction = action; + invokee.showPopover(); + assert_true(invokee.matches(":popover-open")); + await clickOn(containedinvoker); + assert_false(invokee.matches(":popover-open")); + }, + `invoking (as ${ + action === null ? "auto" : action || "explicit empty" + }) from within open popover closes`, + ); + + promise_test( + async function (t) { + t.add_cleanup(resetState); + if (action !== null) invcontainedinvokervokeaction = action; + invokee.showPopover(); + invokee.addEventListener("invoke", (e) => e.preventDefault(), { + once: true, + }); + assert_true(invokee.matches(":popover-open")); + await clickOn(containedinvoker); + assert_true(invokee.matches(":popover-open")); + }, + `invoking (as ${ + action === null ? "auto" : action || "explicit empty" + }) open popover with preventDefault does not close`, + ); + }); + + // showpopover specific + promise_test(async function (t) { + t.add_cleanup(resetState); invokerbutton.setAttribute("invokeaction", "showpopover"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - assert_false(invokee.matches(":popover-open")); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.hidePopover()); - assert_true(invokee.matches(":popover-open")); - }, "invoking (as showpopover) closed popover opens"); - - promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", "sHoWpOpOvEr"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - assert_false(invokee.matches(":popover-open")); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.hidePopover()); - assert_true(invokee.matches(":popover-open")); - }, "invoking (as showpopover - case insensitive) closed popover opens"); - - promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", "showpopover"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); invokee.showPopover(); assert_true(invokee.matches(":popover-open")); await clickOn(invokerbutton); - t.add_cleanup(() => invokee.hidePopover()); assert_true(invokee.matches(":popover-open")); }, "invoking (as showpopover) open popover is noop"); + // hidepopover specific promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", "showpopover"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - assert_false(invokee.matches(":popover-open")); - invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, - }); - await clickOn(invokerbutton); - t.add_cleanup(() => invokee.hidePopover()); - assert_false(invokee.matches(":popover-open")); - }, "invoking (as showpopover) closed popover with preventDefault does not open"); - - // hidepopover - - promise_test(async function (t) { + t.add_cleanup(resetState); invokerbutton.setAttribute("invokeaction", "hidepopover"); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); assert_false(invokee.matches(":popover-open")); await clickOn(invokerbutton); assert_false(invokee.matches(":popover-open")); }, "invoking (as hidepopover) closed popover is noop"); - - promise_test(async function (t) { - containedinvoker.setAttribute("invokeaction", "hidepopover"); - t.add_cleanup(() => containedinvoker.removeAttribute("invokeaction")); - invokee.showPopover(); - assert_true(invokee.matches(":popover-open")); - await clickOn(containedinvoker); - t.add_cleanup(() => invokee.hidePopover()); - assert_false(invokee.matches(":popover-open")); - }, "invoking (as hidepopover) open popover closes"); - - promise_test(async function (t) { - containedinvoker.setAttribute("invokeaction", "hIdEpOpOvEr"); - t.add_cleanup(() => containedinvoker.removeAttribute("invokeaction")); - invokee.showPopover(); - assert_true(invokee.matches(":popover-open")); - await clickOn(containedinvoker); - t.add_cleanup(() => invokee.hidePopover()); - assert_false(invokee.matches(":popover-open")); - }, "invoking (as hidepopover - case insensitive) open popover closes"); - - promise_test(async function (t) { - containedinvoker.setAttribute("invokeaction", "hidepopover"); - t.add_cleanup(() => containedinvoker.removeAttribute("invokeaction")); - invokee.showPopover(); - t.add_cleanup(() => invokee.hidePopover()); - assert_true(invokee.matches(":popover-open")); - invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, - }); - await clickOn(containedinvoker); - assert_true(invokee.matches(":popover-open")); - }, "invoking (as hidepopover) open popover with preventDefault does not close"); - - // invalid - - ["foo", "togglemodal", "showpicker", "toggle", "open", "close"].forEach(action => { - promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", action); - t.add_cleanup(() => invokerbutton.removeAttribute("invokeaction")); - assert_false(invokee.matches(":popover-open")); - await clickOn(invokerbutton); - assert_false(invokee.matches(":popover-open")); - }, `invoking (as ${action}) on popover does nothing`); - - promise_test(async function (t) { - invokerbutton.setAttribute("invokeaction", action); - t.add_cleanup(() => { - invokerbutton.removeAttribute("invokeaction") - invokee.hidePopover(); - }); - invokee.showPopover() - assert_true(invokee.matches(":popover-open")); - await clickOn(invokerbutton); - assert_true(invokee.matches(":popover-open")); - }, `invoking (as ${action}) on open popover does nothing`); - }) </script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-invalid-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-invalid-behavior.tentative.html new file mode 100644 index 0000000000..755f3a6777 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-popover-invalid-behavior.tentative.html @@ -0,0 +1,47 @@ +<!doctype html> +<meta charset="utf-8" /> +<meta name="author" title="Keith Cirkel" href="mailto:wpt@keithcirkel.co.uk" /> +<meta name="timeout" content="long" /> +<link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src="/resources/testdriver-vendor.js"></script> +<script src="resources/invoker-utils.js"></script> + +<div id="invokee" popover> + <button id="containedinvoker" invoketarget="invokee"></button> +</div> +<button id="invokerbutton" invoketarget="invokee"></button> + +<script> + function resetState() { + invokerbutton.removeAttribute("invokeaction"); + containedinvoker.removeAttribute("invokeaction"); + try { + invokee.hidePopover(); + } catch {} + invokee.setAttribute("popover", ""); + } + + // invalid actions on showpopover + ["foo-bar", "showmodal", "showpicker", "open", "close"].forEach((action) => { + promise_test(async function (t) { + t.add_cleanup(resetState); + invokerbutton.invokeAction = action; + assert_false(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + assert_false(invokee.matches(":popover-open")); + }, `invoking (as ${action}) on popover does nothing`); + + promise_test(async function (t) { + t.add_cleanup(resetState); + invokerbutton.invokeAction = action; + invokee.showPopover(); + assert_true(invokee.matches(":popover-open")); + await clickOn(invokerbutton); + assert_true(invokee.matches(":popover-open")); + }, `invoking (as ${action}) on open popover does nothing`); + }); +</script> diff --git a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html index 5bbcd83e72..d15d6f9584 100644 --- a/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html +++ b/testing/web-platform/tests/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html @@ -1,6 +1,7 @@ <!doctype html> <meta charset="utf-8" /> <meta name="author" title="Luke Warlow" href="mailto:luke@warlow.dev" /> +<meta name="timeout" content="long" /> <link rel="help" href="https://open-ui.org/components/invokers.explainer/" /> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> @@ -25,7 +26,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", ""); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -43,7 +44,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "playpause"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_false(invokee.paused); @@ -62,7 +63,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "playpause"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -75,12 +76,12 @@ invokee.currentTime = 0; invokee.muted = false; }); - await test_driver.bless('play video'); + await test_driver.bless("play video"); invokee.play(); assert_false(invokee.paused); invokerbutton.setAttribute("invokeaction", "playpause"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -98,7 +99,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "play"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_false(invokee.paused); @@ -112,12 +113,12 @@ invokee.muted = false; }); invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, + once: true, }); assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "play"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -130,12 +131,12 @@ invokee.currentTime = 0; invokee.muted = false; }); - await test_driver.bless('play video'); + await test_driver.bless("play video"); invokee.play(); assert_false(invokee.paused); invokerbutton.setAttribute("invokeaction", "play"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_false(invokee.paused); @@ -153,7 +154,7 @@ assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "pause"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -167,12 +168,12 @@ invokee.muted = false; }); invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, + once: true, }); assert_true(invokee.paused); invokerbutton.setAttribute("invokeaction", "pause"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -185,12 +186,12 @@ invokee.currentTime = 0; invokee.muted = false; }); - await test_driver.bless('play video'); + await test_driver.bless("play video"); invokee.play(); assert_false(invokee.paused); invokerbutton.setAttribute("invokeaction", "pause"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.paused); @@ -208,7 +209,7 @@ assert_false(invokee.muted); invokerbutton.setAttribute("invokeaction", "toggleMuted"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_true(invokee.muted); @@ -222,12 +223,12 @@ invokee.muted = false; }); invokee.addEventListener("invoke", (e) => e.preventDefault(), { - once: true, + once: true, }); assert_false(invokee.muted); invokerbutton.setAttribute("invokeaction", "toggleMuted"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_false(invokee.muted); @@ -244,10 +245,9 @@ assert_true(invokee.muted); invokerbutton.setAttribute("invokeaction", "toggleMuted"); await clickOn(invokerbutton); - await new Promise(resolve => { + await new Promise((resolve) => { requestAnimationFrame(resolve); }); assert_false(invokee.muted); }, "invoking muted video with toggleMuted action unmutes it"); - </script> diff --git a/testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js b/testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js index 8420f24b6f..4261f9c0d3 100644 --- a/testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js +++ b/testing/web-platform/tests/html/semantics/invokers/resources/invoker-utils.js @@ -14,3 +14,14 @@ async function clickOn(element) { .send(); await waitForRender(); } +async function hoverOver(element) { + await waitForRender(); + let rect = element.getBoundingClientRect(); + let actions = new test_driver.Actions(); + // FIXME: Switch to pointerMove(0, 0, {origin: element}) once + // https://github.com/web-platform-tests/wpt/issues/41257 is fixed. + await actions + .pointerMove(Math.round(rect.x + rect.width / 2), Math.round(rect.y + rect.height / 2), {}) + .send(); + await waitForRender(); +} diff --git a/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reference-expected.html b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reference-expected.html new file mode 100644 index 0000000000..c62ff5b24d --- /dev/null +++ b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reference-expected.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<body> + <div> + The permission element should have some limits for specific properties: + <ul> + <li>font-weight is adjusted to be at least 200.</li> + <li>font-style should only have "normal" or "italic" values.</li> + <li>word-spacing should be at most 0.5 of the font size, and non-negative.</li> + <li>letter-spacing should be between -0.05 and 0.2 of the font size.</li> + </ul> + </div> + +<style> + #id1 { + font-weight: 200; + font-style: normal; + word-spacing: 0.5em; + font-size: 100px; + height: auto; + width: auto; + letter-spacing: 20px; + } + #id2 { + word-spacing: 0em; + height: auto; + width: auto; + font-size: 10px; + letter-spacing: -0.5px; + } +</style> + +<permission id="id1" type="geolocation"> +<permission id="id2" type="camera"> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reference.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reference.tentative.html new file mode 100644 index 0000000000..b8337ab87d --- /dev/null +++ b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties-reference.tentative.html @@ -0,0 +1,37 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<link rel="match" href="bounded-css-properties-reference-expected.html"> +<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md#locking-the-pepc-style"> +<body> + <div> + The permission element should have some limits for specific properties: + <ul> + <li>font-weight is adjusted to be at least 200.</li> + <li>font-style should only have "normal" or "italic" values.</li> + <li>word-spacing should be at most 0.5 of the font size, and non-negative.</li> + <li>letter-spacing should be between -0.05 and 0.2 of the font size.</li> + </ul> + </div> + +<style> + #id1 { + font-weight: 100; + font-style: oblique 30deg; + word-spacing: 1em; + font-size: 100px; + height: auto; + width: auto; + letter-spacing: 25px; + } + #id2 { + word-spacing: -1000px; + height: auto; + width: auto; + font-size: 10px; + letter-spacing: -1px; + } +</style> + +<permission id="id1" type="geolocation"> +<permission id="id2" type="camera"> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties.html b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties.html deleted file mode 100644 index 98c3d70e98..0000000000 --- a/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties.html +++ /dev/null @@ -1,38 +0,0 @@ -<!DOCTYPE html> -<meta charset=utf-8> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<body> -<!--The permission element should have some limits for specific properties: - * font-weight is adjusted to be at least 200. - * font-style should only have "normal" or "italic" values. ---> -<style> - #id1 { - font-weight: 100; - font-style: oblique 30deg; - } - #id2 { - font-weight: 300; - font-style: italic; - } -</style> - - -<permission id="id1" type="geolocation"> -<permission id="id2" type="camera"> - -<script> - test(function(){ - var el_outside_bounds = document.getElementById("id1"); - assert_equals(getComputedStyle(el_outside_bounds).fontWeight, "200", "font-weight"); - assert_equals(getComputedStyle(el_outside_bounds).fontStyle, "normal", "font-style"); - }, "Properties with out-of-bounds values should be corrected"); - - test(function(){ - var el_inside_bounds = document.getElementById("id2"); - assert_equals(getComputedStyle(el_inside_bounds).fontWeight, "300", "font-weight"); - assert_equals(getComputedStyle(el_inside_bounds).fontStyle, "italic", "font-style"); - }, "Properties with values in bounds should not be modified"); -</script> -</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties.tentative.html new file mode 100644 index 0000000000..c0f0fe3454 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/permission-element/bounded-css-properties.tentative.html @@ -0,0 +1,64 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md#locking-the-pepc-style"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<!--The permission element should have some limits for specific properties: + * font-weight is adjusted to be at least 200. + * font-style should only have "normal" or "italic" values. + * word-spacing should be at most 0.5 of the font size, and non-negative. + * letter-spacing should be between -0.05 and 0.2 of the font size. +--> +<style> + #id-over-bounds { + font-weight: 100; + font-style: oblique 30deg; + word-spacing: 1em; + font-size: 100px; + letter-spacing: 21px; + } + #id-under-bounds { + word-spacing: -1px; + font-size: 100px; + letter-spacing: -6px; + } + #id-within-bounds { + font-weight: 300; + font-style: italic; + word-spacing: 0.4em; + font-size: 100px; + letter-spacing: 15px; + } +</style> + + +<permission id="id-over-bounds" type="geolocation"> +<permission id="id-under-bounds" type="camera"> +<permission id="id-within-bounds" type="microphone"> + +<script> + test(function(){ + var el = document.getElementById("id-over-bounds"); + assert_equals(getComputedStyle(el).fontWeight, "200", "font-weight"); + assert_equals(getComputedStyle(el).fontStyle, "normal", "font-style"); + assert_equals(getComputedStyle(el).wordSpacing, "50px", "word-spacing"); + assert_equals(getComputedStyle(el).letterSpacing, "20px", "letter-spacing"); + + el = document.getElementById("id-under-bounds"); + assert_equals(getComputedStyle(el).wordSpacing, "0px", "word-spacing, negative"); + assert_equals(getComputedStyle(el).letterSpacing, "-5px", "letter-spacing, negative"); + }, "Properties with out-of-bounds values should be corrected"); + + test(function(){ + var el = document.getElementById("id-within-bounds"); + assert_equals(getComputedStyle(el).fontWeight, "300", "font-weight"); + assert_equals(getComputedStyle(el).fontStyle, "italic", "font-style"); + assert_equals(getComputedStyle(el).wordSpacing, "40px", "word-spacing"); + assert_equals(getComputedStyle(el).letterSpacing, "15px", "letter-spacing"); + + el.style.letterSpacing = "-4px"; + assert_equals(getComputedStyle(el).letterSpacing, "-4px", "letter-spacing, negative"); + }, "Properties with values in bounds should not be modified"); +</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/display-css-property-reference-expected.html b/testing/web-platform/tests/html/semantics/permission-element/display-css-property-reference-expected.html new file mode 100644 index 0000000000..6a04c94c03 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/permission-element/display-css-property-reference-expected.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<body> + <div> + The permission element should either be display 'none' or 'inline-block' + </div> + +<style> + #id1 { + display: inline-block; + } +</style> + +<permission id="id1" type="geolocation"> +<span>After element</span> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/display-css-property-reference.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/display-css-property-reference.tentative.html new file mode 100644 index 0000000000..973a76d723 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/permission-element/display-css-property-reference.tentative.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<link rel="match" href="display-css-property-reference-expected.html"> +<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md#locking-the-pepc-style"> +<body> + <div> + The permission element should either be display 'none' or 'inline-block' + </div> + +<style> + #id1 { + display: flex; + } + #id2 { + display: none; + } +</style> + +<permission id="id1" type="geolocation"> +<permission id="id2" type="camera"> +<span>After element</span> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/display-css-property.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/display-css-property.tentative.html new file mode 100644 index 0000000000..7aa22ef4a2 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/permission-element/display-css-property.tentative.html @@ -0,0 +1,34 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md#locking-the-pepc-style"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<!-- + 'display' should either be 'none' or 'inline-block' +--> +<style> + #id1 { + display: inline-block; + } + #id2 { + display: block; + } + #id3 { + display: none; + } +</style> + + +<permission id="id1" type="geolocation"> +<permission id="id2" type="camera"> +<permission id="id3" type="microphone"> + +<script> + test(function(){ + assert_equals(getComputedStyle(document.getElementById("id1")).display, "inline-block", "'inline-block' should be kept"); + assert_equals(getComputedStyle(document.getElementById("id2")).display, "inline-block", "'block' should be changed to 'inline-block'"); + assert_equals(getComputedStyle(document.getElementById("id3")).display, "none", "'none' should be kept"); + }, "'display' should be either 'inline-block' or 'none'"); +</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/invalid-css-properties.html b/testing/web-platform/tests/html/semantics/permission-element/invalid-css-properties.html deleted file mode 100644 index c7186563f0..0000000000 --- a/testing/web-platform/tests/html/semantics/permission-element/invalid-css-properties.html +++ /dev/null @@ -1,34 +0,0 @@ -<!DOCTYPE html> -<meta charset=utf-8> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<body> -<!--The permission element does not allow certain CSS properties ---> -<style> - #id1 { - border-image: url('test-url'); - background-image: url('test-url'); - clip-path: circle(10px); - filter: blur(10px); - mask: url('test-url'); - padding-left: 10px; - transform: rotate(10); - } -</style> - -<permission id="id1" type="geolocation"> - -<script> - test(function(){ - var el_with_negatives = document.getElementById("id1"); - assert_equals(getComputedStyle(el_with_negatives).borderImage, "none", "border-image"); - assert_equals(getComputedStyle(el_with_negatives).backgroundImage, "none", "background-image"); - assert_equals(getComputedStyle(el_with_negatives).clipPath, "none", "clip-path"); - assert_equals(getComputedStyle(el_with_negatives).filter, "none", "filter"); - assert_equals(getComputedStyle(el_with_negatives).mask, "none", "mask"); - assert_equals(getComputedStyle(el_with_negatives).paddingLeft, "0px", "padding-left"); - assert_equals(getComputedStyle(el_with_negatives).transform, "none", "transform"); - }, "None of the listed properties should be applied"); -</script> -</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/invalid-css-properties.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/invalid-css-properties.tentative.html new file mode 100644 index 0000000000..334280c83b --- /dev/null +++ b/testing/web-platform/tests/html/semantics/permission-element/invalid-css-properties.tentative.html @@ -0,0 +1,35 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md#locking-the-pepc-style"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<!--The permission element does not allow certain CSS properties +--> +<style> + #id1 { + border-image: url('test-url'); + background-image: url('test-url'); + clip-path: circle(10px); + filter: blur(10px); + mask: url('test-url'); + padding-left: 10px; + transform: rotate(10); + } +</style> + +<permission id="id1" type="geolocation"> + +<script> + test(function(){ + var el_with_negatives = document.getElementById("id1"); + assert_equals(getComputedStyle(el_with_negatives).borderImage, "none", "border-image"); + assert_equals(getComputedStyle(el_with_negatives).backgroundImage, "none", "background-image"); + assert_equals(getComputedStyle(el_with_negatives).clipPath, "none", "clip-path"); + assert_equals(getComputedStyle(el_with_negatives).filter, "none", "filter"); + assert_equals(getComputedStyle(el_with_negatives).mask, "none", "mask"); + assert_equals(getComputedStyle(el_with_negatives).paddingLeft, "0px", "padding-left"); + assert_equals(getComputedStyle(el_with_negatives).transform, "none", "transform"); + }, "None of the listed properties should be applied"); +</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/negative-offset-and-margin.html b/testing/web-platform/tests/html/semantics/permission-element/negative-offset-and-margin.html deleted file mode 100644 index 97290bb4df..0000000000 --- a/testing/web-platform/tests/html/semantics/permission-element/negative-offset-and-margin.html +++ /dev/null @@ -1,67 +0,0 @@ -<!DOCTYPE html> -<meta charset=utf-8> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<body> -<!--The permission element should not allow setting negative margins or outline-offset ---> -<style> - #id1 { - outline-offset: -50px; - margin-top: -50px; - margin-left: -50px; - margin-bottom: -50px; - margin-right: -50px; - } - #id2 { - outline-offset: 50px; - margin-top: 50px; - margin-left: 50px; - margin-bottom: 50px; - margin-right: 50px; - } - - /* These various expressions all result in a negative value when calculated */ - #id3 { - outline-offset: min(-50px, 50px); - margin-top: min(10%, -50px); - margin-left: clamp(-100px, 1vw, -50px); - margin-bottom: 1% - 10000px; - margin-right: max(min(-1em, 10em), -5%); - } -</style> - - -<permission id="id1" type="geolocation"> -<permission id="id2" type="camera"> -<permission id="id3" type="microphone"> - -<script> - test(function(){ - var el_with_negatives = document.getElementById("id1"); - assert_equals(getComputedStyle(el_with_negatives).outlineOffset, "0px", "outline-offset"); - assert_equals(getComputedStyle(el_with_negatives).marginLeft, "0px", "margin-left"); - assert_equals(getComputedStyle(el_with_negatives).marginRight, "0px", "margin-right"); - assert_equals(getComputedStyle(el_with_negatives).marginTop, "0px", "margin-top"); - assert_equals(getComputedStyle(el_with_negatives).marginBottom, "0px", "margin-bottom"); - }, "Negative margins/offset should be changed to 0px"); - - test(function(){ - var el_with_positives = document.getElementById("id2"); - assert_equals(getComputedStyle(el_with_positives).outlineOffset, "50px", "outline-offset"); - assert_equals(getComputedStyle(el_with_positives).marginLeft, "50px", "margin-left"); - assert_equals(getComputedStyle(el_with_positives).marginRight, "50px", "margin-right"); - assert_equals(getComputedStyle(el_with_positives).marginTop, "50px", "margin-top"); - assert_equals(getComputedStyle(el_with_positives).marginBottom, "50px", "margin-bottom"); - }, "Positive margins/offset are unaffected"); - - test(function(){ - var el_with_negative_expr = document.getElementById("id3"); - assert_equals(getComputedStyle(el_with_negative_expr).outlineOffset, "0px", "outline-offset"); - assert_equals(getComputedStyle(el_with_negative_expr).marginLeft, "0px", "margin-left"); - assert_equals(getComputedStyle(el_with_negative_expr).marginRight, "0px", "margin-right"); - assert_equals(getComputedStyle(el_with_negative_expr).marginTop, "0px", "margin-top"); - assert_equals(getComputedStyle(el_with_negative_expr).marginBottom, "0px", "margin-bottom"); - }, "Expressions margins/offset should always return at least 0px"); -</script> -</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/negative-offset-and-margin.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/negative-offset-and-margin.tentative.html new file mode 100644 index 0000000000..de622bbb3e --- /dev/null +++ b/testing/web-platform/tests/html/semantics/permission-element/negative-offset-and-margin.tentative.html @@ -0,0 +1,68 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md#locking-the-pepc-style"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<!--The permission element should not allow setting negative margins or outline-offset +--> +<style> + #id1 { + outline-offset: -50px; + margin-top: -50px; + margin-left: -50px; + margin-bottom: -50px; + margin-right: -50px; + } + #id2 { + outline-offset: 50px; + margin-top: 50px; + margin-left: 50px; + margin-bottom: 50px; + margin-right: 50px; + } + + /* These various expressions all result in a negative value when calculated */ + #id3 { + outline-offset: min(-50px, 50px); + margin-top: min(10%, -50px); + margin-left: clamp(-100px, 1vw, -50px); + margin-bottom: 1% - 10000px; + margin-right: max(min(-1em, 10em), -5%); + } +</style> + + +<permission id="id1" type="geolocation"> +<permission id="id2" type="camera"> +<permission id="id3" type="microphone"> + +<script> + test(function(){ + var el_with_negatives = document.getElementById("id1"); + assert_equals(getComputedStyle(el_with_negatives).outlineOffset, "0px", "outline-offset"); + assert_equals(getComputedStyle(el_with_negatives).marginLeft, "0px", "margin-left"); + assert_equals(getComputedStyle(el_with_negatives).marginRight, "0px", "margin-right"); + assert_equals(getComputedStyle(el_with_negatives).marginTop, "0px", "margin-top"); + assert_equals(getComputedStyle(el_with_negatives).marginBottom, "0px", "margin-bottom"); + }, "Negative margins/offset should be changed to 0px"); + + test(function(){ + var el_with_positives = document.getElementById("id2"); + assert_equals(getComputedStyle(el_with_positives).outlineOffset, "50px", "outline-offset"); + assert_equals(getComputedStyle(el_with_positives).marginLeft, "50px", "margin-left"); + assert_equals(getComputedStyle(el_with_positives).marginRight, "50px", "margin-right"); + assert_equals(getComputedStyle(el_with_positives).marginTop, "50px", "margin-top"); + assert_equals(getComputedStyle(el_with_positives).marginBottom, "50px", "margin-bottom"); + }, "Positive margins/offset are unaffected"); + + test(function(){ + var el_with_negative_expr = document.getElementById("id3"); + assert_equals(getComputedStyle(el_with_negative_expr).outlineOffset, "0px", "outline-offset"); + assert_equals(getComputedStyle(el_with_negative_expr).marginLeft, "0px", "margin-left"); + assert_equals(getComputedStyle(el_with_negative_expr).marginRight, "0px", "margin-right"); + assert_equals(getComputedStyle(el_with_negative_expr).marginTop, "0px", "margin-top"); + assert_equals(getComputedStyle(el_with_negative_expr).marginBottom, "0px", "margin-bottom"); + }, "Expressions margins/offset should always return at least 0px"); +</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.html b/testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.html deleted file mode 100644 index 5fcce1421b..0000000000 --- a/testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.html +++ /dev/null @@ -1,27 +0,0 @@ -<!DOCTYPE html> -<meta charset=utf-8> -<script src="/resources/testharness.js"></script> -<script src="/resources/testharnessreport.js"></script> -<body> -<!--The permission element should have no end tag or content. - Therefore the parsing should stop after the beginning tag, and the following - element and text will become part of the 'body' element's contents. ---> -<permission type="geolocation"><span>this is some text</span></permission> - -<script> - test(function(){ - assert_equals(3, document.body.childElementCount); // permission, span, script - assert_equals(document.body.innerText, "this is some text", "The text should be part of the 'body' element's text"); - - assert_true(document.body.children[0] instanceof HTMLPermissionElement, "First element should be a permission element"); - var permission = document.body.children[0]; - assert_equals(permission.innerText, "", "The permission element should have no text"); - assert_equals(permission.childElementCount, 0, "The permission element should have no children"); - - assert_true(document.body.children[1] instanceof HTMLSpanElement, "Second element should be a span element"); - var span = document.body.children[1]; - assert_equals(span.innerText, "this is some text", "The span element should contain the text"); - }, "The permission element should have no end tag or contents"); -</script> -</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.tentative.html b/testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.tentative.html new file mode 100644 index 0000000000..bea3d7102c --- /dev/null +++ b/testing/web-platform/tests/html/semantics/permission-element/no-end-tag-no-contents.tentative.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<link rel="help" href="https://github.com/WICG/PEPC/blob/main/explainer.md#locking-the-pepc-style"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<!--The permission element should have no end tag or content. + Therefore the parsing should stop after the beginning tag, and the following + element and text will become part of the 'body' element's contents. +--> +<permission type="geolocation"><span>this is some text</span></permission> + +<script> + test(function(){ + assert_equals(3, document.body.childElementCount); // permission, span, script + assert_equals(document.body.innerText, "this is some text", "The text should be part of the 'body' element's text"); + + assert_true(document.body.children[0] instanceof HTMLPermissionElement, "First element should be a permission element"); + var permission = document.body.children[0]; + assert_equals(permission.innerText, "", "The permission element should have no text"); + assert_equals(permission.childElementCount, 0, "The permission element should have no children"); + + assert_true(document.body.children[1] instanceof HTMLSpanElement, "Second element should be a span element"); + var span = document.body.children[1]; + assert_equals(span.innerText, "this is some text", "The span element should contain the text"); + }, "The permission element should have no end tag or contents"); +</script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/html/semantics/popovers/button-type-reset-popovertarget.tentative.html b/testing/web-platform/tests/html/semantics/popovers/button-type-reset-popovertarget.tentative.html new file mode 100644 index 0000000000..975eab0d66 --- /dev/null +++ b/testing/web-platform/tests/html/semantics/popovers/button-type-reset-popovertarget.tentative.html @@ -0,0 +1,43 @@ +<!DOCTYPE html> +<link rel=author href="mailto:jarhar@chromium.org"> +<link rel=help href="https://issues.chromium.org/issues/329118508"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> + +<div id=mypopover popover=auto>popover</div> + +<iframe name=foo></iframe> +<form target=foo action="about:blank"> + <button id=reset-in-form type=reset popovertarget=mypopover>reset</button> + <button id=submit-in-form type=submit popovertarget=mypopover>submit</button> + <button id=button-in-form type=button popovertarget=mypopover>type=button</button> +</form> + +<button id=reset-outside-form type=reset popovertarget=mypopover>reset</button> +<button id=submit-outside-form type=submit popovertarget=mypopover>submit</button> +<button id=button-outside-form type=button popovertarget=mypopover>type=button</button> + +<script> +test(() => { + const testButton = (id, expectedToToggle) => { + document.getElementById(id).click(); + if (expectedToToggle) { + assert_true(mypopover.matches(':popover-open'), + `${id}: button should open the popover.`); + } else { + assert_false(mypopover.matches(':popover-open'), + `${id}: button should not open the popover.`); + } + if (mypopover.matches(':popover-open')) { + mypopover.hidePopover(); + } + }; + + testButton('reset-in-form', false); + testButton('submit-in-form', false); + testButton('button-in-form', true); + testButton('reset-outside-form', true); + testButton('submit-outside-form', true); + testButton('button-outside-form', true); +}, 'Button type=reset and type=submit should not run popover algorithms when in a form.'); +</script> diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display.tentative.html index d50dd6c857..bddc44006d 100644 --- a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display.tentative.html +++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-display.tentative.html @@ -94,7 +94,7 @@ showDefaultopenPopoversOnLoad(); top: anchor(top); } #popover5 { - anchor-default: --anchor1; /* shouldn't be used */ + position-anchor: --anchor1; /* shouldn't be used */ left: anchor(implicit right); top: anchor(implicit top); } @@ -102,7 +102,7 @@ showDefaultopenPopoversOnLoad(); anchor-name: --anchor6; } #popover6 { - anchor-default: --anchor6; + position-anchor: --anchor6; left: anchor(right); /* shouldn't use the implicit anchor */ top: anchor(top); } diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-scroll-display.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-scroll-display.tentative.html index 7ed6cf1adf..2c6b0bafb9 100644 --- a/testing/web-platform/tests/html/semantics/popovers/popover-anchor-scroll-display.tentative.html +++ b/testing/web-platform/tests/html/semantics/popovers/popover-anchor-scroll-display.tentative.html @@ -56,7 +56,7 @@ anchor-name: --anchor2; } #popover2 { - anchor-default: --anchor2; + position-anchor: --anchor2; left: anchor(right); top: anchor(top); } diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html index 916d52ef5e..45db242e91 100644 --- a/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html +++ b/testing/web-platform/tests/html/semantics/popovers/popover-light-dismiss.html @@ -11,6 +11,19 @@ <script src="/resources/testdriver-vendor.js"></script> <script src="resources/popover-utils.js"></script> +<style> + [popover] { + /* Position most popovers at the bottom-right, out of the way */ + inset:auto; + bottom:0; + right:0; + } + [popover]::backdrop { + /* This should *not* affect anything: */ + pointer-events: auto; + } +</style> + <button id=b1t popovertarget='p1'>Popover 1</button> <button id=b1s popovertarget='p1' popovertargetaction=show>Popover 1</button> <span id=outside>Outside all popovers</span> @@ -26,11 +39,6 @@ <style> #p1 {top: 50px;} #p2 {top: 120px;} - [popover] {bottom:auto;} - [popover]::backdrop { - /* This should *not* affect anything: */ - pointer-events: auto; - } </style> <script> const popover1 = document.querySelector('#p1'); @@ -584,24 +592,50 @@ promise_test(async () => { <div id=p29 popover>Popover 29</div> <button id=b29 popovertarget=p29>Open popover 29</button> -<iframe id=iframe29 width=100 height=100></iframe> +<iframe id=iframe29 width=100 height=30></iframe> <script> promise_test(async () => { let iframe_url = (new URL("/common/blank.html", location.href)).href; iframe29.src = iframe_url; iframe29.contentDocument.body.style.height = '100%'; - assert_false(p29.matches(':popover-open')); + assert_false(p29.matches(':popover-open'),'initially hidden'); p29.showPopover(); - assert_true(p29.matches(':popover-open')); + assert_true(p29.matches(':popover-open'),'showing'); let actions = new test_driver.Actions(); await actions.pointerMove(0,0,{origin: b29}) .pointerDown({button: actions.ButtonType.LEFT}) .send(); + await waitForRender(); + assert_true(p29.matches(':popover-open'),'showing after pointerdown'); actions = new test_driver.Actions(); await actions.pointerMove(0,0,{origin: iframe29.contentDocument.body}) .pointerUp({button: actions.ButtonType.LEFT}) .send(); - assert_true(p29.matches(':popover-open')); + await waitForRender(); + assert_true(p29.matches(':popover-open'),'showing after pointerup'); },`Pointer down in one document and pointer up in another document shouldn't dismiss popover`); </script> + +<div id=p30 popover>Popover 30</div> +<button id=b30 popovertarget=p30>Open popover 30</button> +<button id=b30b>Non-invoker</button> +<script> +promise_test(async () => { + assert_false(p30.matches(':popover-open'),'initially hidden'); + p30.showPopover(); + assert_true(p30.matches(':popover-open'),'showing'); + let actions = new test_driver.Actions(); + await actions.pointerMove(0,0,{origin: b30}) + .pointerDown({button: actions.ButtonType.LEFT}) + .send(); + await waitForRender(); + assert_true(p30.matches(':popover-open'),'showing after pointerdown'); + actions = new test_driver.Actions(); + await actions.pointerMove(0,0,{origin: b30b}) + .pointerUp({button: actions.ButtonType.LEFT}) + .send(); + await waitForRender(); + assert_true(p30.matches(':popover-open'),'showing after pointerup'); +},`Pointer down inside invoker and up outside that invoker shouldn't dismiss popover`); +</script> diff --git a/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting.tentative.html b/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting.html index a0b3b60b72..a0b3b60b72 100644 --- a/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting.tentative.html +++ b/testing/web-platform/tests/html/semantics/popovers/popover-top-layer-nesting.html diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/integrity.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/integrity.html index 1dd0dad470..7ee6452bcc 100644 --- a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/integrity.html +++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/css-module/integrity.html @@ -12,7 +12,7 @@ window.matchesEvents = []; window.mismatchesLog = []; window.mismatchesEvents = []; </script> -<script type="module" src="resources/integrity-matches.js" integrity="sha384-xvbfmg9iJFHqmCoOS4VNMCwnFPPxEoIlW1Ojzl+fgEd+Wf8Pyez+SMWue+KNovjA" onload="window.matchesEvents.push('load');" onerror="window.matchesEvents.push('error')"></script> +<script type="module" src="resources/integrity-matches.js" integrity="sha384-KtvB2Fgbhx2NAEizVeuGMa+QgvBzlBvVRxdpRnIECuGUvzzQsnVejyDL5J0fVP9M" onload="window.matchesEvents.push('load');" onerror="window.matchesEvents.push('error')"></script> <script type="module" src="resources/integrity-mismatches.js" integrity="sha384-doesnotmatch" onload="window.mismatchesEvents.push('load');" onerror="window.mismatchesEvents.push('error')"></script> <script type="module"> diff --git a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity.html b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity.html index 68a794b973..0aa92d9fdc 100644 --- a/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity.html +++ b/testing/web-platform/tests/html/semantics/scripting-1/the-script-element/json-module/integrity.html @@ -12,7 +12,7 @@ window.matchesEvents = []; window.mismatchesLog = []; window.mismatchesEvents = []; </script> -<script type="module" src="integrity-matches.js" integrity="sha384-VmQQfGzBiLKdyzw4FA4kL4ohu4tyujV68ddgW1aN/1v3cBZNNBn2gDFdVQxfL7+a" onload="window.matchesEvents.push('load');" onerror="window.matchesEvents.push('error')"></script> +<script type="module" src="integrity-matches.js" integrity="sha384-kc1K2KFKQhnYE1AdnpmUUpFVnxz1GCgGbQ19e3zmXrZw23rgpwa9il4/pHp7NYWA" onload="window.matchesEvents.push('load');" onerror="window.matchesEvents.push('error')"></script> <script type="module" src="integrity-mismatches.js" integrity="sha384-doesnotmatch" onload="window.mismatchesEvents.push('load');" onerror="window.mismatchesEvents.push('error')"></script> <script type="module"> diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-window-controls-overlay.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-window-controls-overlay.tentative.html index 9cff8d3163..9cff8d3163 100644 --- a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-window-controls-overlay.html +++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator-window-controls-overlay.tentative.html diff --git a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.https.html b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.https.tentative.html index 3fbe3eaa62..3fbe3eaa62 100644 --- a/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.https.html +++ b/testing/web-platform/tests/html/webappapis/system-state-and-capabilities/the-navigator-object/navigator_user_agent.https.tentative.html diff --git a/testing/web-platform/tests/import-maps/WEB_FEATURES.yml b/testing/web-platform/tests/import-maps/WEB_FEATURES.yml new file mode 100644 index 0000000000..dc3d7fdc23 --- /dev/null +++ b/testing/web-platform/tests/import-maps/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: import-maps + files: "**" diff --git a/testing/web-platform/tests/infrastructure/metadata/infrastructure/testdriver/minimize_restore_popup.html.ini b/testing/web-platform/tests/infrastructure/metadata/infrastructure/testdriver/minimize_restore_popup.html.ini new file mode 100644 index 0000000000..6075b58d09 --- /dev/null +++ b/testing/web-platform/tests/infrastructure/metadata/infrastructure/testdriver/minimize_restore_popup.html.ini @@ -0,0 +1,2 @@ +[minimize_restore_popup.html] + disabled: https://github.com/web-platform-tests/wpt/issues/45137 diff --git a/testing/web-platform/tests/infrastructure/metadata/infrastructure/testdriver/test_win_open_with_interaction.html.ini b/testing/web-platform/tests/infrastructure/metadata/infrastructure/testdriver/test_win_open_with_interaction.html.ini deleted file mode 100644 index b1e77bd8e5..0000000000 --- a/testing/web-platform/tests/infrastructure/metadata/infrastructure/testdriver/test_win_open_with_interaction.html.ini +++ /dev/null @@ -1,5 +0,0 @@ -[test_win_open_with_interaction.html] - expected: [OK, TIMEOUT] - - [Tests pointer move/click in a window opened with `window.open.`] - expected: [FAIL, TIMEOUT, PASS] diff --git a/testing/web-platform/tests/infrastructure/testdriver/minimize_restore_popup.html b/testing/web-platform/tests/infrastructure/testdriver/minimize_restore_popup.html new file mode 100644 index 0000000000..99245083b5 --- /dev/null +++ b/testing/web-platform/tests/infrastructure/testdriver/minimize_restore_popup.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>TestDriver minimize/restore method in popup</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<script> +const params = new URLSearchParams(location.search); +const is_popup_page = params.has('popup'); +const channel = new BroadcastChannel("testchannel"); + +if (is_popup_page) { + onload = async () => { + window.opener.events.push(document.visibilityState); + let rect = await test_driver.minimize_window(); + window.opener.events.push(document.visibilityState); + await test_driver.set_window_rect(rect); + window.opener.events.push(document.visibilityState); + channel.postMessage("done"); + }; +} else { + promise_test(async t => { + let popup; + onload = () => { + window.events = []; + popup = window.open("?popup"); + }; + + await new Promise(resolve => { + channel.addEventListener( + "message", t.step_func(async (e) => { + if (e.data === "done") { + const expectedEvents = ["visible", "hidden", "visible"]; + assert_array_equals(window.events, expectedEvents, 'incorrect event order'); + popup.close(); + resolve(); + } + })); + }); + }, `minimize and restore on popup`); +} +</script> diff --git a/testing/web-platform/tests/interfaces/compute-pressure.idl b/testing/web-platform/tests/interfaces/compute-pressure.idl index 3e35dc4ee2..c4dcb90af4 100644 --- a/testing/web-platform/tests/interfaces/compute-pressure.idl +++ b/testing/web-platform/tests/interfaces/compute-pressure.idl @@ -33,5 +33,5 @@ interface PressureRecord { }; dictionary PressureObserverOptions { - double sampleRate = 1.0; + [EnforceRange] unsigned long sampleInterval = 0; }; diff --git a/testing/web-platform/tests/interfaces/css-animations.idl b/testing/web-platform/tests/interfaces/css-animations.idl index 6620e0156d..14a9998069 100644 --- a/testing/web-platform/tests/interfaces/css-animations.idl +++ b/testing/web-platform/tests/interfaces/css-animations.idl @@ -24,7 +24,7 @@ partial interface CSSRule { [Exposed=Window] interface CSSKeyframeRule : CSSRule { attribute CSSOMString keyText; - [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style; + [SameObject, PutForwards=cssText] readonly attribute CSSStyleProperties style; }; [Exposed=Window] diff --git a/testing/web-platform/tests/interfaces/css-fonts.idl b/testing/web-platform/tests/interfaces/css-fonts.idl index 7a917662b5..678f31323b 100644 --- a/testing/web-platform/tests/interfaces/css-fonts.idl +++ b/testing/web-platform/tests/interfaces/css-fonts.idl @@ -4,8 +4,41 @@ // Source: CSS Fonts Module Level 4 (https://drafts.csswg.org/css-fonts-4/) [Exposed=Window] +interface CSSFontFaceDescriptors : CSSStyleDeclaration { + attribute [LegacyNullToEmptyString] CSSOMString src; + attribute [LegacyNullToEmptyString] CSSOMString fontFamily; + attribute [LegacyNullToEmptyString] CSSOMString font-family; + attribute [LegacyNullToEmptyString] CSSOMString fontStyle; + attribute [LegacyNullToEmptyString] CSSOMString font-style; + attribute [LegacyNullToEmptyString] CSSOMString fontWeight; + attribute [LegacyNullToEmptyString] CSSOMString font-weight; + attribute [LegacyNullToEmptyString] CSSOMString fontStretch; + attribute [LegacyNullToEmptyString] CSSOMString font-stretch; + attribute [LegacyNullToEmptyString] CSSOMString fontWidth; + attribute [LegacyNullToEmptyString] CSSOMString font-width; + attribute [LegacyNullToEmptyString] CSSOMString unicodeRange; + attribute [LegacyNullToEmptyString] CSSOMString unicode-range; + attribute [LegacyNullToEmptyString] CSSOMString fontFeatureSettings; + attribute [LegacyNullToEmptyString] CSSOMString font-feature-settings; + attribute [LegacyNullToEmptyString] CSSOMString fontVariationSettings; + attribute [LegacyNullToEmptyString] CSSOMString font-variation-settings; + attribute [LegacyNullToEmptyString] CSSOMString fontNamedInstance; + attribute [LegacyNullToEmptyString] CSSOMString font-named-instance; + attribute [LegacyNullToEmptyString] CSSOMString fontDisplay; + attribute [LegacyNullToEmptyString] CSSOMString font-display; + attribute [LegacyNullToEmptyString] CSSOMString fontLanguageOverride; + attribute [LegacyNullToEmptyString] CSSOMString font-language-override; + attribute [LegacyNullToEmptyString] CSSOMString ascentOverride; + attribute [LegacyNullToEmptyString] CSSOMString ascent-override; + attribute [LegacyNullToEmptyString] CSSOMString descentOverride; + attribute [LegacyNullToEmptyString] CSSOMString descent-override; + attribute [LegacyNullToEmptyString] CSSOMString lineGapOverride; + attribute [LegacyNullToEmptyString] CSSOMString line-gap-override; +}; + +[Exposed=Window] interface CSSFontFaceRule : CSSRule { - readonly attribute CSSStyleDeclaration style; + readonly attribute CSSFontFaceDescriptors style; }; partial interface CSSRule { const unsigned short FONT_FEATURE_VALUES_RULE = 14; diff --git a/testing/web-platform/tests/interfaces/css-scroll-snap-2.idl b/testing/web-platform/tests/interfaces/css-scroll-snap-2.idl index 50cbf6940a..a346969c56 100644 --- a/testing/web-platform/tests/interfaces/css-scroll-snap-2.idl +++ b/testing/web-platform/tests/interfaces/css-scroll-snap-2.idl @@ -3,31 +3,14 @@ // (https://github.com/w3c/webref) // Source: CSS Scroll Snap Module Level 2 (https://drafts.csswg.org/css-scroll-snap-2/) -[Exposed=Window] -interface SnapEvent : Event { - constructor(DOMString type, optional SnapEventInit eventInitDict = {}); - readonly attribute EventTarget? target; - readonly attribute SnapTargetList snappedTargets; - readonly attribute SnapTargetList snapTargets; - readonly attribute boolean invokedProgrammatically; - readonly attribute boolean smoothlyScrolled; -}; - -[Exposed=Window] -interface SnapTargetList { - readonly attribute SnapTargetArray x; - readonly attribute SnapTargetArray y; +dictionary SnapEventInit : EventInit { + Node? snapTargetBlock; + Node? snapTargetInline; }; [Exposed=Window] -interface SnapTargetArray { - readonly attribute unsigned long length; - getter EventTarget? item (unsigned long index); -}; - -dictionary SnapEventInit : EventModifierInit { - sequence<EventTarget> snappedTargetsX = []; - sequence<EventTarget> snappedTargetsY = []; - sequence<EventTarget> snapTargetsListX = []; - sequence<EventTarget> snapTargetsListY = []; +interface SnapEvent : Event { + constructor(DOMString type, optional SnapEventInit eventInitDict = {}); + readonly attribute Node? snapTargetBlock; + readonly attribute Node? snapTargetInline; }; diff --git a/testing/web-platform/tests/interfaces/css-view-transitions-2.idl b/testing/web-platform/tests/interfaces/css-view-transitions-2.idl index 89e57e251b..41337f4e1e 100644 --- a/testing/web-platform/tests/interfaces/css-view-transitions-2.idl +++ b/testing/web-platform/tests/interfaces/css-view-transitions-2.idl @@ -3,23 +3,39 @@ // (https://github.com/w3c/webref) // Source: CSS View Transitions Module Level 2 (https://drafts.csswg.org/css-view-transitions-2/) -dictionary StartViewTransitionOptions { - UpdateCallback? update = null; - sequence<DOMString>? type = null; +partial interface CSSRule { + const unsigned short VIEW_TRANSITION_RULE = 15; }; -partial interface Document { +enum ViewTransitionNavigation { "auto", "none" }; - ViewTransition startViewTransition(optional (UpdateCallback or StartViewTransitionOptions) callbackOptions = {}); +[Exposed=Window] +interface CSSViewTransitionTypeSet { + readonly setlike<CSSOMString>; }; -partial interface CSSRule { - const unsigned short VIEW_TRANSITION_RULE = 15; +[Exposed=Window] +interface CSSViewTransitionRule : CSSRule { + readonly attribute ViewTransitionNavigation navigation; + readonly attribute CSSViewTransitionTypeSet types; }; -enum ViewTransitionNavigation { "auto", "none" }; [Exposed=Window] -interface CSSViewTransitionRule : CSSRule { - attribute ViewTransitionNavigation navigation; - attribute DOMTokenList typeList; +interface ViewTransitionTypeSet { + setlike<DOMString>; +}; + +[Exposed=Window] +partial interface ViewTransition { + attribute ViewTransitionTypeSet types; +}; + +dictionary StartViewTransitionOptions { + UpdateCallback? update = null; + sequence<DOMString>? types = null; +}; + +partial interface Document { + + ViewTransition startViewTransition(optional (UpdateCallback or StartViewTransitionOptions) callbackOptions = {}); }; diff --git a/testing/web-platform/tests/interfaces/cssom-view.idl b/testing/web-platform/tests/interfaces/cssom-view.idl index 3f64113f57..57e559e7f1 100644 --- a/testing/web-platform/tests/interfaces/cssom-view.idl +++ b/testing/web-platform/tests/interfaces/cssom-view.idl @@ -130,6 +130,7 @@ partial interface Element { readonly attribute long clientLeft; readonly attribute long clientWidth; readonly attribute long clientHeight; + readonly attribute double currentCSSZoom; }; partial interface HTMLElement { diff --git a/testing/web-platform/tests/interfaces/cssom.idl b/testing/web-platform/tests/interfaces/cssom.idl index 94cd1912b9..005496e7ed 100644 --- a/testing/web-platform/tests/interfaces/cssom.idl +++ b/testing/web-platform/tests/interfaces/cssom.idl @@ -99,7 +99,7 @@ interface CSSRule { [Exposed=Window] interface CSSStyleRule : CSSGroupingRule { attribute CSSOMString selectorText; - [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style; + [SameObject, PutForwards=cssText] readonly attribute CSSStyleProperties style; }; [Exposed=Window] @@ -119,9 +119,25 @@ interface CSSGroupingRule : CSSRule { }; [Exposed=Window] +interface CSSPageDescriptors : CSSStyleDeclaration { + attribute [LegacyNullToEmptyString] CSSOMString margin; + attribute [LegacyNullToEmptyString] CSSOMString marginTop; + attribute [LegacyNullToEmptyString] CSSOMString marginRight; + attribute [LegacyNullToEmptyString] CSSOMString marginBottom; + attribute [LegacyNullToEmptyString] CSSOMString marginLeft; + attribute [LegacyNullToEmptyString] CSSOMString margin-top; + attribute [LegacyNullToEmptyString] CSSOMString margin-right; + attribute [LegacyNullToEmptyString] CSSOMString margin-bottom; + attribute [LegacyNullToEmptyString] CSSOMString margin-left; + attribute [LegacyNullToEmptyString] CSSOMString size; + attribute [LegacyNullToEmptyString] CSSOMString marks; + attribute [LegacyNullToEmptyString] CSSOMString bleed; +}; + +[Exposed=Window] interface CSSPageRule : CSSGroupingRule { attribute CSSOMString selectorText; - [SameObject, PutForwards=cssText] readonly attribute CSSStyleDeclaration style; + [SameObject, PutForwards=cssText] readonly attribute CSSPageDescriptors style; }; [Exposed=Window] @@ -146,6 +162,10 @@ interface CSSStyleDeclaration { [CEReactions] undefined setProperty(CSSOMString property, [LegacyNullToEmptyString] CSSOMString value, optional [LegacyNullToEmptyString] CSSOMString priority = ""); [CEReactions] CSSOMString removeProperty(CSSOMString property); readonly attribute CSSRule? parentRule; +}; + +[Exposed=Window] +interface CSSStyleProperties : CSSStyleDeclaration { [CEReactions] attribute [LegacyNullToEmptyString] CSSOMString cssFloat; }; diff --git a/testing/web-platform/tests/interfaces/document-picture-in-picture.idl b/testing/web-platform/tests/interfaces/document-picture-in-picture.idl index f54f437a93..888855b38f 100644 --- a/testing/web-platform/tests/interfaces/document-picture-in-picture.idl +++ b/testing/web-platform/tests/interfaces/document-picture-in-picture.idl @@ -20,6 +20,7 @@ interface DocumentPictureInPicture : EventTarget { dictionary DocumentPictureInPictureOptions { [EnforceRange] unsigned long long width = 0; [EnforceRange] unsigned long long height = 0; + boolean allowReturnToOpener = true; }; [Exposed=Window, SecureContext] diff --git a/testing/web-platform/tests/interfaces/html.idl b/testing/web-platform/tests/interfaces/html.idl index 7211231980..2f97e4dd60 100644 --- a/testing/web-platform/tests/interfaces/html.idl +++ b/testing/web-platform/tests/interfaces/html.idl @@ -48,7 +48,7 @@ typedef (HTMLScriptElement or SVGScriptElement) HTMLOrSVGScriptElement; [LegacyOverrideBuiltIns] partial interface Document { - static Document parseHTMLUnsafe(DOMString html); + static Document parseHTMLUnsafe(HTMLString html); // resource metadata management [PutForwards=href, LegacyUnforgeable] readonly attribute Location? location; @@ -77,8 +77,8 @@ partial interface Document { [CEReactions] Document open(optional DOMString unused1, optional DOMString unused2); // both arguments are ignored WindowProxy? open(USVString url, DOMString name, DOMString features); [CEReactions] undefined close(); - [CEReactions] undefined write(DOMString... text); - [CEReactions] undefined writeln(DOMString... text); + [CEReactions] undefined write(HTMLString... text); + [CEReactions] undefined writeln(HTMLString... text); // user interaction readonly attribute WindowProxy? defaultView; @@ -123,6 +123,7 @@ interface HTMLElement : Element { readonly attribute DOMString accessKeyLabel; [CEReactions] attribute boolean draggable; [CEReactions] attribute boolean spellcheck; + [CEReactions] attribute DOMString writingSuggestions; [CEReactions] attribute DOMString autocapitalize; [CEReactions] attribute [LegacyNullToEmptyString] DOMString innerText; @@ -451,7 +452,7 @@ interface HTMLIFrameElement : HTMLElement { [HTMLConstructor] constructor(); [CEReactions] attribute USVString src; - [CEReactions] attribute DOMString srcdoc; + [CEReactions] attribute HTMLString srcdoc; [CEReactions] attribute DOMString name; [SameObject, PutForwards=value] readonly attribute DOMTokenList sandbox; [CEReactions] attribute DOMString allow; @@ -2295,7 +2296,7 @@ WorkerGlobalScope includes WindowOrWorkerGlobalScope; interface DOMParser { constructor(); - [NewObject] Document parseFromString(DOMString string, DOMParserSupportedType type); + [NewObject] Document parseFromString(HTMLString string, DOMParserSupportedType type); }; enum DOMParserSupportedType { @@ -2307,11 +2308,11 @@ enum DOMParserSupportedType { }; partial interface Element { - [CEReactions] undefined setHTMLUnsafe(DOMString html); + [CEReactions] undefined setHTMLUnsafe(HTMLString html); }; partial interface ShadowRoot { - [CEReactions] undefined setHTMLUnsafe(DOMString html); + [CEReactions] undefined setHTMLUnsafe(HTMLString html); }; [Exposed=Window] @@ -2526,7 +2527,7 @@ interface WorkerGlobalScope : EventTarget { readonly attribute WorkerGlobalScope self; readonly attribute WorkerLocation location; readonly attribute WorkerNavigator navigator; - undefined importScripts(USVString... urls); + undefined importScripts(ScriptURLString... urls); attribute OnErrorEventHandler onerror; attribute EventHandler onlanguagechange; @@ -2564,7 +2565,7 @@ interface mixin AbstractWorker { [Exposed=(Window,DedicatedWorker,SharedWorker)] interface Worker : EventTarget { - constructor(USVString scriptURL, optional WorkerOptions options = {}); + constructor(ScriptURLString scriptURL, optional WorkerOptions options = {}); undefined terminate(); @@ -2586,7 +2587,7 @@ Worker includes AbstractWorker; [Exposed=Window] interface SharedWorker : EventTarget { - constructor(USVString scriptURL, optional (DOMString or WorkerOptions) options = {}); + constructor(ScriptURLString scriptURL, optional (DOMString or WorkerOptions) options = {}); readonly attribute MessagePort port; }; diff --git a/testing/web-platform/tests/interfaces/long-animation-frames.idl b/testing/web-platform/tests/interfaces/long-animation-frames.idl new file mode 100644 index 0000000000..79a42ca8f0 --- /dev/null +++ b/testing/web-platform/tests/interfaces/long-animation-frames.idl @@ -0,0 +1,54 @@ +// GENERATED CONTENT - DO NOT EDIT +// Content was automatically extracted by Reffy into webref +// (https://github.com/w3c/webref) +// Source: Long Animation Frames API (https://w3c.github.io/long-animation-frames/) + +[Exposed=Window] +interface PerformanceLongAnimationFrameTiming : PerformanceEntry { + /* Overloading PerformanceEntry */ + readonly attribute DOMHighResTimeStamp startTime; + readonly attribute DOMHighResTimeStamp duration; + readonly attribute DOMString name; + readonly attribute DOMString entryType; + + readonly attribute DOMHighResTimeStamp renderStart; + readonly attribute DOMHighResTimeStamp styleAndLayoutStart; + readonly attribute DOMHighResTimeStamp blockingDuration; + readonly attribute DOMHighResTimeStamp firstUIEventTimestamp; + [SameObject] readonly attribute FrozenArray<PerformanceScriptTiming> scripts; + [Default] object toJSON(); +}; + +enum ScriptInvokerType { + "classic-script", + "module-script", + "event-listener", + "user-callback", + "resolve-promise", + "reject-promise" +}; + +enum ScriptWindowAttribution { + "self", "descendant", "ancestor", "same-page", "other" +}; + +[Exposed=Window] +interface PerformanceScriptTiming : PerformanceEntry { + /* Overloading PerformanceEntry */ + readonly attribute DOMHighResTimeStamp startTime; + readonly attribute DOMHighResTimeStamp duration; + readonly attribute DOMString name; + readonly attribute DOMString entryType; + + readonly attribute ScriptInvokerType invokerType; + readonly attribute DOMString invoker; + readonly attribute DOMHighResTimeStamp executionStart; + readonly attribute DOMString sourceURL; + readonly attribute DOMString sourceFunctionName; + readonly attribute long long sourceCharPosition; + readonly attribute DOMHighResTimeStamp pauseDuration; + readonly attribute DOMHighResTimeStamp forcedStyleAndLayoutDuration; + readonly attribute Window? window; + readonly attribute ScriptWindowAttribution windowAttribution; + [Default] object toJSON(); +}; diff --git a/testing/web-platform/tests/interfaces/longtasks.idl b/testing/web-platform/tests/interfaces/longtasks.idl index e5b6ece16a..3717469fd0 100644 --- a/testing/web-platform/tests/interfaces/longtasks.idl +++ b/testing/web-platform/tests/interfaces/longtasks.idl @@ -29,53 +29,3 @@ interface TaskAttributionTiming : PerformanceEntry { readonly attribute DOMString containerName; [Default] object toJSON(); }; - -[Exposed=Window] -interface PerformanceLongAnimationFrameTiming : PerformanceEntry { - /* Overloading PerformanceEntry */ - readonly attribute DOMHighResTimeStamp startTime; - readonly attribute DOMHighResTimeStamp duration; - readonly attribute DOMString name; - readonly attribute DOMString entryType; - - readonly attribute DOMHighResTimeStamp renderStart; - readonly attribute DOMHighResTimeStamp styleAndLayoutStart; - readonly attribute DOMHighResTimeStamp blockingDuration; - readonly attribute DOMHighResTimeStamp firstUIEventTimestamp; - [SameObject] readonly attribute FrozenArray<PerformanceScriptTiming> scripts; - [Default] object toJSON(); -}; - -enum ScriptInvokerType { - "classic-script", - "module-script", - "event-listener", - "user-callback", - "resolve-promise", - "reject-promise" -}; - -enum ScriptWindowAttribution { - "self", "descendant", "ancestor", "same-page", "other" -}; - -[Exposed=Window] -interface PerformanceScriptTiming : PerformanceEntry { - /* Overloading PerformanceEntry */ - readonly attribute DOMHighResTimeStamp startTime; - readonly attribute DOMHighResTimeStamp duration; - readonly attribute DOMString name; - readonly attribute DOMString entryType; - - readonly attribute ScriptInvokerType invokerType; - readonly attribute DOMString invoker; - readonly attribute DOMHighResTimeStamp executionStart; - readonly attribute DOMString sourceURL; - readonly attribute DOMString sourceFunctionName; - readonly attribute long long sourceCharPosition; - readonly attribute DOMHighResTimeStamp pauseDuration; - readonly attribute DOMHighResTimeStamp forcedStyleAndLayoutDuration; - readonly attribute Window? window; - readonly attribute ScriptWindowAttribution windowAttribution; - [Default] object toJSON(); -}; diff --git a/testing/web-platform/tests/interfaces/media-source.idl b/testing/web-platform/tests/interfaces/media-source.idl index 7a86a7a43c..de153e615a 100644 --- a/testing/web-platform/tests/interfaces/media-source.idl +++ b/testing/web-platform/tests/interfaces/media-source.idl @@ -18,23 +18,25 @@ enum EndOfStreamError { interface MediaSource : EventTarget { constructor(); - [ SameObject, Exposed=DedicatedWorker ] - readonly attribute MediaSourceHandle handle; - - readonly attribute SourceBufferList sourceBuffers; - readonly attribute SourceBufferList activeSourceBuffers; - readonly attribute ReadyState readyState; - attribute unrestricted double duration; - attribute EventHandler onsourceopen; - attribute EventHandler onsourceended; - attribute EventHandler onsourceclose; - static readonly attribute boolean canConstructInDedicatedWorker; - SourceBuffer addSourceBuffer (DOMString type); - undefined removeSourceBuffer (SourceBuffer sourceBuffer); - undefined endOfStream (optional EndOfStreamError error); - undefined setLiveSeekableRange (double start, double end); - undefined clearLiveSeekableRange (); - static boolean isTypeSupported (DOMString type); + [SameObject, Exposed=DedicatedWorker] + readonly attribute MediaSourceHandle handle; + readonly attribute SourceBufferList sourceBuffers; + readonly attribute SourceBufferList activeSourceBuffers; + readonly attribute ReadyState readyState; + + attribute unrestricted double duration; + attribute EventHandler onsourceopen; + attribute EventHandler onsourceended; + attribute EventHandler onsourceclose; + + static readonly attribute boolean canConstructInDedicatedWorker; + + SourceBuffer addSourceBuffer(DOMString type); + undefined removeSourceBuffer(SourceBuffer sourceBuffer); + undefined endOfStream(optional EndOfStreamError error); + undefined setLiveSeekableRange(double start, double end); + undefined clearLiveSeekableRange(); + static boolean isTypeSupported(DOMString type); }; [Transferable, Exposed=(Window,DedicatedWorker)] @@ -47,32 +49,36 @@ enum AppendMode { [Exposed=(Window,DedicatedWorker)] interface SourceBuffer : EventTarget { - attribute AppendMode mode; - readonly attribute boolean updating; - readonly attribute TimeRanges buffered; - attribute double timestampOffset; - readonly attribute AudioTrackList audioTracks; - readonly attribute VideoTrackList videoTracks; - readonly attribute TextTrackList textTracks; - attribute double appendWindowStart; - attribute unrestricted double appendWindowEnd; - attribute EventHandler onupdatestart; - attribute EventHandler onupdate; - attribute EventHandler onupdateend; - attribute EventHandler onerror; - attribute EventHandler onabort; - undefined appendBuffer (BufferSource data); - undefined abort (); - undefined changeType (DOMString type); - undefined remove (double start, unrestricted double end); + attribute AppendMode mode; + readonly attribute boolean updating; + readonly attribute TimeRanges buffered; + attribute double timestampOffset; + readonly attribute AudioTrackList audioTracks; + readonly attribute VideoTrackList videoTracks; + readonly attribute TextTrackList textTracks; + attribute double appendWindowStart; + attribute unrestricted double appendWindowEnd; + + attribute EventHandler onupdatestart; + attribute EventHandler onupdate; + attribute EventHandler onupdateend; + attribute EventHandler onerror; + attribute EventHandler onabort; + + undefined appendBuffer(BufferSource data); + undefined abort(); + undefined changeType(DOMString type); + undefined remove(double start, unrestricted double end); }; [Exposed=(Window,DedicatedWorker)] interface SourceBufferList : EventTarget { - readonly attribute unsigned long length; - attribute EventHandler onaddsourcebuffer; - attribute EventHandler onremovesourcebuffer; - getter SourceBuffer (unsigned long index); + readonly attribute unsigned long length; + + attribute EventHandler onaddsourcebuffer; + attribute EventHandler onremovesourcebuffer; + + getter SourceBuffer (unsigned long index); }; [Exposed=(Window,DedicatedWorker)] @@ -103,15 +109,15 @@ interface ManagedSourceBuffer : SourceBuffer { [Exposed=(Window,DedicatedWorker)] partial interface AudioTrack { - readonly attribute SourceBuffer? sourceBuffer; + readonly attribute SourceBuffer? sourceBuffer; }; [Exposed=(Window,DedicatedWorker)] partial interface VideoTrack { - readonly attribute SourceBuffer? sourceBuffer; + readonly attribute SourceBuffer? sourceBuffer; }; [Exposed=(Window,DedicatedWorker)] partial interface TextTrack { - readonly attribute SourceBuffer? sourceBuffer; + readonly attribute SourceBuffer? sourceBuffer; }; diff --git a/testing/web-platform/tests/interfaces/mediacapture-streams.idl b/testing/web-platform/tests/interfaces/mediacapture-streams.idl index 373f0c328d..f2ca21389e 100644 --- a/testing/web-platform/tests/interfaces/mediacapture-streams.idl +++ b/testing/web-platform/tests/interfaces/mediacapture-streams.idl @@ -189,16 +189,6 @@ dictionary MediaStreamConstraints { (boolean or MediaTrackConstraints) audio = false; }; -partial interface Navigator { - [SecureContext] undefined getUserMedia(MediaStreamConstraints constraints, - NavigatorUserMediaSuccessCallback successCallback, - NavigatorUserMediaErrorCallback errorCallback); -}; - -callback NavigatorUserMediaSuccessCallback = undefined (MediaStream stream); - -callback NavigatorUserMediaErrorCallback = undefined (DOMException error); - dictionary DoubleRange { double max; double min; diff --git a/testing/web-platform/tests/interfaces/navigation-timing.idl b/testing/web-platform/tests/interfaces/navigation-timing.idl index 355950160e..b381b486e1 100644 --- a/testing/web-platform/tests/interfaces/navigation-timing.idl +++ b/testing/web-platform/tests/interfaces/navigation-timing.idl @@ -16,6 +16,7 @@ interface PerformanceNavigationTiming : PerformanceResourceTiming { readonly attribute NavigationTimingType type; readonly attribute unsigned short redirectCount; readonly attribute DOMHighResTimeStamp criticalCHRestart; + readonly attribute NotRestoredReasons? notRestoredReasons; [Default] object toJSON(); }; diff --git a/testing/web-platform/tests/interfaces/permissions.idl b/testing/web-platform/tests/interfaces/permissions.idl index fbcb674e56..62c2e3ad76 100644 --- a/testing/web-platform/tests/interfaces/permissions.idl +++ b/testing/web-platform/tests/interfaces/permissions.idl @@ -36,6 +36,6 @@ enum PermissionState { }; dictionary PermissionSetParameters { - required PermissionDescriptor descriptor; + required object descriptor; required PermissionState state; }; diff --git a/testing/web-platform/tests/interfaces/sanitizer-api.idl b/testing/web-platform/tests/interfaces/sanitizer-api.idl index 117a55fdf7..599d8f82ea 100644 --- a/testing/web-platform/tests/interfaces/sanitizer-api.idl +++ b/testing/web-platform/tests/interfaces/sanitizer-api.idl @@ -3,36 +3,49 @@ // (https://github.com/w3c/webref) // Source: HTML Sanitizer API (https://wicg.github.io/sanitizer-api/) -[ - Exposed=(Window), - SecureContext -] interface Sanitizer { - constructor(optional SanitizerConfig config = {}); +partial interface Element { + [CEReactions] undefined setHTMLUnsafe__TO_BE_MERGED(DOMString html, optional SanitizerConfig config = {}); + [CEReactions] undefined setHTML(DOMString html, optional SanitizerConfig config = {}); +}; - DocumentFragment sanitize((Document or DocumentFragment) input); - Element? sanitizeFor(DOMString element, DOMString input); +partial interface ShadowRoot { + [CEReactions] undefined setHTMLUnsafe__TO_BE_MERGED(DOMString html, optional SanitizerConfig config = {}); + [CEReactions] undefined setHTML(DOMString html, optional SanitizerConfig config = {}); +}; - SanitizerConfig getConfiguration(); - static SanitizerConfig getDefaultConfiguration(); +partial interface Document { + static Document parseHTMLUnsafe__TO_BE_MERGED(DOMString html, optional SanitizerConfig config = {}); + static Document parseHTML(DOMString html, optional SanitizerConfig config = {}); }; -dictionary SetHTMLOptions { - Sanitizer sanitizer; +dictionary SanitizerElementNamespace { + required DOMString name; + DOMString? _namespace = "http://www.w3.org/1999/xhtml"; }; -[SecureContext] -partial interface Element { - undefined setHTML(DOMString input, optional SetHTMLOptions options = {}); + +// Used by "elements" +dictionary SanitizerElementNamespaceWithAttributes : SanitizerElementNamespace { + sequence<SanitizerAttribute> attributes; + sequence<SanitizerAttribute> removeAttributes; }; -dictionary SanitizerConfig { - sequence<DOMString> allowElements; - sequence<DOMString> blockElements; - sequence<DOMString> dropElements; - AttributeMatchList allowAttributes; - AttributeMatchList dropAttributes; - boolean allowCustomElements; - boolean allowUnknownMarkup; - boolean allowComments; +typedef (DOMString or SanitizerElementNamespace) SanitizerElement; +typedef (DOMString or SanitizerElementNamespaceWithAttributes) SanitizerElementWithAttributes; + +dictionary SanitizerAttributeNamespace { + required DOMString name; + DOMString? _namespace = null; }; +typedef (DOMString or SanitizerAttributeNamespace) SanitizerAttribute; + +dictionary SanitizerConfig { + sequence<SanitizerElementWithAttributes> elements; + sequence<SanitizerElement> removeElements; + sequence<SanitizerElement> replaceWithChildrenElements; -typedef record<DOMString, sequence<DOMString>> AttributeMatchList; + sequence<SanitizerAttribute> attributes; + sequence<SanitizerAttribute> removeAttributes; + + boolean comments; + boolean dataAttributes; +}; diff --git a/testing/web-platform/tests/interfaces/turtledove.idl b/testing/web-platform/tests/interfaces/turtledove.idl index 8a2d7bb594..2547e1fb54 100644 --- a/testing/web-platform/tests/interfaces/turtledove.idl +++ b/testing/web-platform/tests/interfaces/turtledove.idl @@ -25,6 +25,7 @@ dictionary GenerateBidInterestGroup { boolean enableBiddingSignalsPrioritization = false; record<DOMString, double> priorityVector; + record<USVString, sequence<DOMString>> sellerCapabilities; DOMString executionMode = "compatibility"; USVString biddingLogicURL; USVString biddingWasmHelperURL; @@ -32,6 +33,7 @@ dictionary GenerateBidInterestGroup { USVString trustedBiddingSignalsURL; sequence<USVString> trustedBiddingSignalsKeys; DOMString trustedBiddingSignalsSlotSizeMode = "none"; + long maxTrustedBiddingSignalsURLLength; any userBiddingSignals; sequence<AuctionAd> ads; sequence<AuctionAd> adComponents; @@ -67,26 +69,30 @@ partial interface Navigator { dictionary AuctionAdConfig { required USVString seller; required USVString decisionLogicURL; + USVString trustedScoringSignalsURL; + long maxTrustedScoringSignalsURLLength; sequence<USVString> interestGroupBuyers; Promise<any> auctionSignals; - record<DOMString, DOMString> requestedSize; - sequence<record<DOMString, DOMString>> allSlotsRequestedSizes; Promise<any> sellerSignals; Promise<DOMString> directFromSellerSignalsHeaderAdSlot; + Promise<record<USVString, USVString>> deprecatedRenderURLReplacements; unsigned long long sellerTimeout; unsigned short sellerExperimentGroupId; - USVString sellerCurrency; Promise<record<USVString, any>> perBuyerSignals; Promise<record<USVString, unsigned long long>> perBuyerTimeouts; Promise<record<USVString, unsigned long long>> perBuyerCumulativeTimeouts; + USVString sellerCurrency; + Promise<record<USVString, USVString>> perBuyerCurrencies; record<USVString, unsigned short> perBuyerGroupLimits; record<USVString, unsigned short> perBuyerExperimentGroupIds; record<USVString, record<USVString, double>> perBuyerPrioritySignals; - Promise<record<USVString, USVString>> perBuyerCurrencies; - sequence<AuctionAdConfig> componentAuctions = []; + sequence<DOMString> requiredSellerCapabilities; + record<DOMString, DOMString> requestedSize; + sequence<record<DOMString, DOMString>> allSlotsRequestedSizes; Promise<undefined> additionalBids; DOMString auctionNonce; + sequence<AuctionAdConfig> componentAuctions = []; AbortSignal? signal; Promise<boolean> resolveToConfig; }; diff --git a/testing/web-platform/tests/interfaces/ua-client-hints.idl b/testing/web-platform/tests/interfaces/ua-client-hints.idl index 6a40e1bdc4..5d44b0dd80 100644 --- a/testing/web-platform/tests/interfaces/ua-client-hints.idl +++ b/testing/web-platform/tests/interfaces/ua-client-hints.idl @@ -12,7 +12,7 @@ dictionary UADataValues { DOMString architecture; DOMString bitness; sequence<NavigatorUABrandVersion> brands; - sequence<DOMString> formFactor; + sequence<DOMString> formFactors; sequence<NavigatorUABrandVersion> fullVersionList; DOMString model; boolean mobile; diff --git a/testing/web-platform/tests/interfaces/url.idl b/testing/web-platform/tests/interfaces/url.idl index a5e4d1eb49..cd18a66e31 100644 --- a/testing/web-platform/tests/interfaces/url.idl +++ b/testing/web-platform/tests/interfaces/url.idl @@ -8,6 +8,7 @@ interface URL { constructor(USVString url, optional USVString base); + static URL? parse(USVString url, optional USVString base); static boolean canParse(USVString url, optional USVString base); stringifier attribute USVString href; diff --git a/testing/web-platform/tests/interfaces/web-animations-2.idl b/testing/web-platform/tests/interfaces/web-animations-2.idl index f9f68a0d49..4c3af53514 100644 --- a/testing/web-platform/tests/interfaces/web-animations-2.idl +++ b/testing/web-platform/tests/interfaces/web-animations-2.idl @@ -14,6 +14,7 @@ partial interface AnimationTimeline { partial interface Animation { attribute CSSNumberish? startTime; attribute CSSNumberish? currentTime; + readonly attribute double? progress; }; [Exposed=Window] diff --git a/testing/web-platform/tests/interfaces/webcodecs-opus-codec-registration.idl b/testing/web-platform/tests/interfaces/webcodecs-opus-codec-registration.idl index 0d198a6bcd..782a87b37d 100644 --- a/testing/web-platform/tests/interfaces/webcodecs-opus-codec-registration.idl +++ b/testing/web-platform/tests/interfaces/webcodecs-opus-codec-registration.idl @@ -9,6 +9,8 @@ partial dictionary AudioEncoderConfig { dictionary OpusEncoderConfig { OpusBitstreamFormat format = "opus"; + OpusSignal signal = "auto"; + OpusApplication application = "audio"; [EnforceRange] unsigned long long frameDuration = 20000; [EnforceRange] unsigned long complexity; [EnforceRange] unsigned long packetlossperc = 0; @@ -20,3 +22,15 @@ enum OpusBitstreamFormat { "opus", "ogg", }; + +enum OpusSignal { + "auto", + "music", + "voice", +}; + +enum OpusApplication { + "voip", + "audio", + "lowdelay", +}; diff --git a/testing/web-platform/tests/interfaces/webcodecs.idl b/testing/web-platform/tests/interfaces/webcodecs.idl index 48d89d0b47..371546eb0d 100644 --- a/testing/web-platform/tests/interfaces/webcodecs.idl +++ b/testing/web-platform/tests/interfaces/webcodecs.idl @@ -158,8 +158,8 @@ dictionary VideoDecoderConfig { dictionary AudioEncoderConfig { required DOMString codec; - [EnforceRange] unsigned long sampleRate; - [EnforceRange] unsigned long numberOfChannels; + [EnforceRange] required unsigned long sampleRate; + [EnforceRange] required unsigned long numberOfChannels; [EnforceRange] unsigned long long bitrate; BitrateMode bitrateMode = "variable"; }; @@ -390,21 +390,37 @@ dictionary PlaneLayout { enum VideoPixelFormat { // 4:2:0 Y, U, V "I420", + "I420P10", + "I420P12", // 4:2:0 Y, U, V, A "I420A", + "I420AP10", + "I420AP12", // 4:2:2 Y, U, V "I422", + "I422P10", + "I422P12", + // 4:2:2 Y, U, V, A + "I422A", + "I422AP10", + "I422AP12", // 4:4:4 Y, U, V "I444", + "I444P10", + "I444P12", + // 4:4:4 Y, U, V, A + "I444A", + "I444AP10", + "I444AP12", // 4:2:0 Y, UV "NV12", - // 32bpp RGBA + // 4:4:4 RGBA "RGBA", - // 32bpp RGBX (opaque) + // 4:4:4 RGBX (opaque) "RGBX", - // 32bpp BGRA + // 4:4:4 BGRA "BGRA", - // 32bpp BGRX (opaque) + // 4:4:4 BGRX (opaque) "BGRX", }; diff --git a/testing/web-platform/tests/interfaces/webnn.idl b/testing/web-platform/tests/interfaces/webnn.idl index 50ee64b185..0b8ea7cb34 100644 --- a/testing/web-platform/tests/interfaces/webnn.idl +++ b/testing/web-platform/tests/interfaces/webnn.idl @@ -69,7 +69,7 @@ dictionary MLOperandDescriptor { // The dimensions field is empty for scalar operands, // and non-empty for tensor operands. - sequence<unsigned long> dimensions = []; + sequence<[EnforceRange] unsigned long> dimensions = []; }; [SecureContext, Exposed=(Window, DedicatedWorker)] @@ -102,7 +102,7 @@ interface MLGraphBuilder { }; dictionary MLArgMinMaxOptions { - sequence<unsigned long> axes; + sequence<[EnforceRange] unsigned long> axes; boolean keepDimensions = false; boolean selectLastIndex = false; }; @@ -115,7 +115,7 @@ partial interface MLGraphBuilder { dictionary MLBatchNormalizationOptions { MLOperand scale; MLOperand bias; - unsigned long axis = 1; + [EnforceRange] unsigned long axis = 1; float epsilon = 1e-5; MLActivation activation; }; @@ -140,7 +140,7 @@ partial interface MLGraphBuilder { }; partial interface MLGraphBuilder { - MLOperand concat(sequence<MLOperand> inputs, unsigned long axis); + MLOperand concat(sequence<MLOperand> inputs, [EnforceRange] unsigned long axis); }; enum MLConv2dFilterOperandLayout { @@ -151,10 +151,10 @@ enum MLConv2dFilterOperandLayout { }; dictionary MLConv2dOptions { - sequence<unsigned long> padding; - sequence<unsigned long> strides; - sequence<unsigned long> dilations; - unsigned long groups = 1; + sequence<[EnforceRange] unsigned long> padding; + sequence<[EnforceRange] unsigned long> strides; + sequence<[EnforceRange] unsigned long> dilations; + [EnforceRange] unsigned long groups = 1; MLInputOperandLayout inputLayout = "nchw"; MLConv2dFilterOperandLayout filterLayout = "oihw"; MLOperand bias; @@ -172,12 +172,12 @@ enum MLConvTranspose2dFilterOperandLayout { }; dictionary MLConvTranspose2dOptions { - sequence<unsigned long> padding; - sequence<unsigned long> strides; - sequence<unsigned long> dilations; - sequence<unsigned long> outputPadding; - sequence<unsigned long> outputSizes; - unsigned long groups = 1; + sequence<[EnforceRange] unsigned long> padding; + sequence<[EnforceRange] unsigned long> strides; + sequence<[EnforceRange] unsigned long> dilations; + sequence<[EnforceRange] unsigned long> outputPadding; + sequence<[EnforceRange] unsigned long> outputSizes; + [EnforceRange] unsigned long groups = 1; MLInputOperandLayout inputLayout = "nchw"; MLConvTranspose2dFilterOperandLayout filterLayout = "iohw"; MLOperand bias; @@ -234,11 +234,11 @@ partial interface MLGraphBuilder { }; partial interface MLGraphBuilder { - MLOperand expand(MLOperand input, sequence<unsigned long> newShape); + MLOperand expand(MLOperand input, sequence<[EnforceRange] unsigned long> newShape); }; dictionary MLGatherOptions { - unsigned long axis = 0; + [EnforceRange] unsigned long axis = 0; }; partial interface MLGraphBuilder { @@ -280,8 +280,11 @@ dictionary MLGruOptions { }; partial interface MLGraphBuilder { - sequence<MLOperand> gru(MLOperand input, MLOperand weight, MLOperand recurrentWeight, - unsigned long steps, unsigned long hiddenSize, + sequence<MLOperand> gru(MLOperand input, + MLOperand weight, + MLOperand recurrentWeight, + [EnforceRange] unsigned long steps, + [EnforceRange] unsigned long hiddenSize, optional MLGruOptions options = {}); }; @@ -294,8 +297,11 @@ dictionary MLGruCellOptions { }; partial interface MLGraphBuilder { - MLOperand gruCell(MLOperand input, MLOperand weight, MLOperand recurrentWeight, - MLOperand hiddenState, unsigned long hiddenSize, + MLOperand gruCell(MLOperand input, + MLOperand weight, + MLOperand recurrentWeight, + MLOperand hiddenState, + [EnforceRange] unsigned long hiddenSize, optional MLGruCellOptions options = {}); }; @@ -329,7 +335,7 @@ partial interface MLGraphBuilder { dictionary MLLayerNormalizationOptions { MLOperand scale; MLOperand bias; - sequence<unsigned long> axes; + sequence<[EnforceRange] unsigned long> axes; float epsilon = 1e-5; }; @@ -374,8 +380,11 @@ dictionary MLLstmOptions { }; partial interface MLGraphBuilder { - sequence<MLOperand> lstm(MLOperand input, MLOperand weight, MLOperand recurrentWeight, - unsigned long steps, unsigned long hiddenSize, + sequence<MLOperand> lstm(MLOperand input, + MLOperand weight, + MLOperand recurrentWeight, + [EnforceRange] unsigned long steps, + [EnforceRange] unsigned long hiddenSize, optional MLLstmOptions options = {}); }; @@ -388,8 +397,12 @@ dictionary MLLstmCellOptions { }; partial interface MLGraphBuilder { - sequence<MLOperand> lstmCell(MLOperand input, MLOperand weight, MLOperand recurrentWeight, - MLOperand hiddenState, MLOperand cellState, unsigned long hiddenSize, + sequence<MLOperand> lstmCell(MLOperand input, + MLOperand weight, + MLOperand recurrentWeight, + MLOperand hiddenState, + MLOperand cellState, + [EnforceRange] unsigned long hiddenSize, optional MLLstmCellOptions options = {}); }; @@ -411,8 +424,8 @@ dictionary MLPadOptions { partial interface MLGraphBuilder { MLOperand pad(MLOperand input, - sequence<unsigned long> beginningPadding, - sequence<unsigned long> endingPadding, + sequence<[EnforceRange] unsigned long> beginningPadding, + sequence<[EnforceRange] unsigned long> endingPadding, optional MLPadOptions options = {}); }; @@ -422,13 +435,13 @@ enum MLRoundingType { }; dictionary MLPool2dOptions { - sequence<unsigned long> windowDimensions; - sequence<unsigned long> padding; - sequence<unsigned long> strides; - sequence<unsigned long> dilations; + sequence<[EnforceRange] unsigned long> windowDimensions; + sequence<[EnforceRange] unsigned long> padding; + sequence<[EnforceRange] unsigned long> strides; + sequence<[EnforceRange] unsigned long> dilations; MLInputOperandLayout layout = "nchw"; MLRoundingType roundingType = "floor"; - sequence<unsigned long> outputSizes; + sequence<[EnforceRange] unsigned long> outputSizes; }; partial interface MLGraphBuilder { @@ -442,7 +455,7 @@ partial interface MLGraphBuilder { }; dictionary MLReduceOptions { - sequence<unsigned long> axes; + sequence<[EnforceRange] unsigned long> axes; boolean keepDimensions = false; }; @@ -472,8 +485,8 @@ enum MLInterpolationMode { dictionary MLResample2dOptions { MLInterpolationMode mode = "nearest-neighbor"; sequence<float> scales; - sequence<unsigned long> sizes; - sequence<unsigned long> axes; + sequence<[EnforceRange] unsigned long> sizes; + sequence<[EnforceRange] unsigned long> axes; }; partial interface MLGraphBuilder { @@ -481,7 +494,7 @@ partial interface MLGraphBuilder { }; partial interface MLGraphBuilder { - MLOperand reshape(MLOperand input, sequence<unsigned long> newShape); + MLOperand reshape(MLOperand input, sequence<[EnforceRange] unsigned long> newShape); }; partial interface MLGraphBuilder { @@ -490,7 +503,9 @@ partial interface MLGraphBuilder { }; partial interface MLGraphBuilder { - MLOperand slice(MLOperand input, sequence<unsigned long> starts, sequence<unsigned long> sizes); + MLOperand slice(MLOperand input, + sequence<[EnforceRange] unsigned long> starts, + sequence<[EnforceRange] unsigned long> sizes); }; partial interface MLGraphBuilder { @@ -513,12 +528,12 @@ partial interface MLGraphBuilder { }; dictionary MLSplitOptions { - unsigned long axis = 0; + [EnforceRange] unsigned long axis = 0; }; partial interface MLGraphBuilder { sequence<MLOperand> split(MLOperand input, - (unsigned long or sequence<unsigned long>) splits, + ([EnforceRange] unsigned long or sequence<[EnforceRange] unsigned long>) splits, optional MLSplitOptions options = {}); }; @@ -528,7 +543,7 @@ partial interface MLGraphBuilder { }; dictionary MLTransposeOptions { - sequence<unsigned long> permutation; + sequence<[EnforceRange] unsigned long> permutation; }; partial interface MLGraphBuilder { @@ -537,7 +552,7 @@ partial interface MLGraphBuilder { dictionary MLTriangularOptions { boolean upper = true; - long diagonal = 0; + [EnforceRange] long diagonal = 0; }; partial interface MLGraphBuilder { diff --git a/testing/web-platform/tests/intersection-observer/WEB_FEATURES.yml b/testing/web-platform/tests/intersection-observer/WEB_FEATURES.yml new file mode 100644 index 0000000000..1b6a42746f --- /dev/null +++ b/testing/web-platform/tests/intersection-observer/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: intersection-observer + files: "**" diff --git a/testing/web-platform/tests/intersection-observer/svg-container-element.html b/testing/web-platform/tests/intersection-observer/svg-container-element.html index e2b6ab5cb0..1c27acee51 100644 --- a/testing/web-platform/tests/intersection-observer/svg-container-element.html +++ b/testing/web-platform/tests/intersection-observer/svg-container-element.html @@ -14,6 +14,7 @@ <script> const viewportWidth = document.documentElement.clientWidth; const viewportHeight = document.documentElement.clientHeight; +const kEpsilon = 1; // clientHeight and clientWidth are rounded if the viewport is fractional. setup(() => { window.entries = []; window.target = document.getElementById("target"); @@ -40,7 +41,7 @@ function step0() { 0, 0, 0, 0, 0, viewportWidth, 0, viewportHeight, false, - ]); + ], kEpsilon); } function step1() { checkLastEntry(entries, 1, [ @@ -48,6 +49,6 @@ function step1() { 8, 8 + targetRect.width, 8, 8 + targetRect.height, 0, viewportWidth, 0, viewportHeight, true, - ]); + ], kEpsilon); } </script> diff --git a/testing/web-platform/tests/intersection-observer/v2/WEB_FEATURES.yml b/testing/web-platform/tests/intersection-observer/v2/WEB_FEATURES.yml new file mode 100644 index 0000000000..e57d67f641 --- /dev/null +++ b/testing/web-platform/tests/intersection-observer/v2/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: intersection-observer-v2 + files: "**" diff --git a/testing/web-platform/tests/jpegxl/WEB_FEATURES.yml b/testing/web-platform/tests/jpegxl/WEB_FEATURES.yml new file mode 100644 index 0000000000..a81700e1fb --- /dev/null +++ b/testing/web-platform/tests/jpegxl/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: jpegxl + files: "**" diff --git a/testing/web-platform/tests/largest-contentful-paint/transparent-text.html b/testing/web-platform/tests/largest-contentful-paint/transparent-text.html new file mode 100644 index 0000000000..9eb978ab5c --- /dev/null +++ b/testing/web-platform/tests/largest-contentful-paint/transparent-text.html @@ -0,0 +1,48 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<!-- + Transparent text should not be eligible for LCP. +--> +<style> + .large-transparent { + font-size: 2000px; + position: fixed; + top: 0; + left: 0; + padding: 0; + margin: 0; + pointer-events: none; + color: transparent; + z-index: -999; + } +</style> + +<body> + <img src='/images/lcp-133x106.png' id='lcp' /> + <p id="fake_lcp" class='large-transparent'>fake LCP</p> + + <script> + const LcpEntryListPromise = (t) => { + return new Promise(resolve => { + new PerformanceObserver((entryList, observer) => { + if (entryList.getEntries().length > 0) { + resolve(entryList.getEntries()); + + observer.disconnect(); + } + }).observe({ type: 'largest-contentful-paint', buffered: true }); + }); + } + + promise_test(async t => { + assert_implements(window.LargestContentfulPaint, "LargestContentfulPaint is not implemented"); + + lcpEntries = await LcpEntryListPromise(); + + assert_equals(lcpEntries.length, 1, "There should only be 1 entry."); + + assert_true(lcpEntries[0].url.includes('lcp-133x106.png'), "The LCP entry should be the image."); + }, "Transparent text should not be LCP.") + </script> +</body>
\ No newline at end of file diff --git a/testing/web-platform/tests/lint.ignore b/testing/web-platform/tests/lint.ignore index 6809f61642..61bca89ddc 100644 --- a/testing/web-platform/tests/lint.ignore +++ b/testing/web-platform/tests/lint.ignore @@ -162,6 +162,7 @@ SET TIMEOUT: custom-elements/scoped-registry/scoped-registry-define-upgrade-crit SET TIMEOUT: encrypted-media/polyfill/chrome-polyfill.js SET TIMEOUT: encrypted-media/polyfill/clearkey-polyfill.js SET TIMEOUT: encrypted-media/scripts/playback-temporary-events.js +SET TIMEOUT: fetch/fetch-later/resources/fetch-later-helper.js SET TIMEOUT: fetch/metadata/resources/helper.sub.js SET TIMEOUT: fetch/metadata/resources/message-opener.html SET TIMEOUT: fenced-frame/resources/history-length-fenced-navigations-replace-do-not-contribute-to-joint-inner.html @@ -276,7 +277,6 @@ SET TIMEOUT: shadow-dom/scroll-to-the-fragment-in-shadow-tree.html SET TIMEOUT: shadow-dom/slotchange-event.html SET TIMEOUT: trusted-types/block-string-assignment-to-DOMWindowTimers-setTimeout-setInterval.html SET TIMEOUT: trusted-types/DOMWindowTimers-setTimeout-setInterval.html -SET TIMEOUT: pending-beacon/resources/pending_beacon-helper.js SET TIMEOUT: user-timing/* SET TIMEOUT: web-animations/crashtests/reparent-animating-element-002.html SET TIMEOUT: web-animations/timing-model/animations/* @@ -580,6 +580,7 @@ MISSING-LINK: css/cssom-view/scrollTop-display-change.html # TODO https://github.com/web-platform-tests/wpt/issues/5770 MISSING-LINK: css/css-highlight-api/idlharness.window.js MISSING-LINK: css/css-highlight-api/historical.window.js +MISSING-LINK: css/cssom/cssstyledeclaration-csstext-setter.window.js MISSING-LINK: css/geometry/*.worker.js MISSING-LINK: css/geometry/*.any.js MISSING-LINK: css/filter-effects/*.any.js @@ -748,3 +749,7 @@ HTML INVALID SYNTAX: html/syntax/parsing/unclosed-svg-script.html HTML INVALID SYNTAX: mathml/crashtests/mozilla/411603-1.html HTML INVALID SYNTAX: quirks/percentage-height-calculation.html HTML INVALID SYNTAX: trusted-types/TrustedTypePolicyFactory-getAttributeType-namespace.html + +# Pre compressed data using Shared Brotli and Shared Zstandard. +TRAILING WHITESPACE, INDENT TABS, CR AT EOL: fetch/compression-dictionary/resources/compressed.br-d.data +TRAILING WHITESPACE, INDENT TABS, CR AT EOL: fetch/compression-dictionary/resources/compressed.zstd-d.data diff --git a/testing/web-platform/tests/mathml/WEB_FEATURES.yml b/testing/web-platform/tests/mathml/WEB_FEATURES.yml new file mode 100644 index 0000000000..4d03bc3eb4 --- /dev/null +++ b/testing/web-platform/tests/mathml/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: mathml + files: "**" diff --git a/testing/web-platform/tests/mathml/presentation-markup/operators/mo-no-vertical-adjustment-for-basic-binary-operators.html b/testing/web-platform/tests/mathml/presentation-markup/operators/mo-no-vertical-adjustment-for-basic-binary-operators.html new file mode 100644 index 0000000000..a318a71ce5 --- /dev/null +++ b/testing/web-platform/tests/mathml/presentation-markup/operators/mo-no-vertical-adjustment-for-basic-binary-operators.html @@ -0,0 +1,39 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>No vertical adjustment for basic binary operators</title> +<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" /> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1890531" /> +<meta name="assert" content="Verify vertical alignement of basic binary operators is not adjusted to align their centers with the math axis."> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/mathml/support/fonts.js"></script> +<style> + @font-face { + font-family: operators; + /* AxisHeight == 0, so the math axis matches the baseline */ + src: url("/fonts/math/operators.woff"); + } + math { + font: 25px operators; + } +</style> +<math> + <mn id="ref">↯</mn> + <mo>+</mo><mo>=</mo><mo>*</mo><mo>−</mo><mo>≤</mo><mo>≥</mo><mo>×</mo> +</math> +<script> + promise_test(async () => { + await new Promise(r => { window.addEventListener("DOMContentLoaded", r) }); + await loadAllFonts(); + function centerOf(element) { + const box = element.getBoundingClientRect(); + return (box.top + box.bottom) / 2; + } + const ref = centerOf(document.getElementById("ref")); + const epsilon = 1; + Array.from(document.getElementsByTagName("mo")).forEach(mo => { + assert_approx_equals(centerOf(mo), ref, epsilon, + `Position of "${mo.textContent}"`); + }); + }, "Vertical alignment of basic binary operators is not adjusted."); +</script> diff --git a/testing/web-platform/tests/mathml/presentation-markup/operators/stretchy-largeop-with-default-font-2.html b/testing/web-platform/tests/mathml/presentation-markup/operators/stretchy-largeop-with-default-font-2.html index 7c43c80acc..83f160a54e 100644 --- a/testing/web-platform/tests/mathml/presentation-markup/operators/stretchy-largeop-with-default-font-2.html +++ b/testing/web-platform/tests/mathml/presentation-markup/operators/stretchy-largeop-with-default-font-2.html @@ -11,8 +11,8 @@ function contentOf(aElement) { return document.getElementById(aElement).textContent; } - function heightOf(aElement) { - return document.getElementById(aElement).getBoundingClientRect().height; + function boxOf(aElement) { + return document.getElementById(aElement).getBoundingClientRect(); } promise_test(() => { return new Promise(resolve => { @@ -21,7 +21,10 @@ const numTests = 10; // zero indexed for (let i = 0; i < numTests; i++) { const operatorName = contentOf(`mo${i}`); - assert_greater_than(heightOf(`mo${i}`), heightOf(`moRef${i}`), `size of largeop '${operatorName}'`); + const box = boxOf(`mo${i}`); + const ref = boxOf(`moRef${i}`); + assert_greater_than(box.width, ref.width, `width of largeop '${operatorName}'`); + assert_greater_than(box.height, ref.height, `height of largeop '${operatorName}'`); } }); }, "Test that non-stretchy largeops are bigger in display mode."); diff --git a/testing/web-platform/tests/mathml/relations/css-styling/table-width-1-ref.xhtml b/testing/web-platform/tests/mathml/relations/css-styling/table-width-1-ref.xhtml index d76a350290..7653cacb1c 100644 --- a/testing/web-platform/tests/mathml/relations/css-styling/table-width-1-ref.xhtml +++ b/testing/web-platform/tests/mathml/relations/css-styling/table-width-1-ref.xhtml @@ -3,8 +3,8 @@ <style type="text/css"> html { background-color: grey; } td { border: 1px solid white; - padding-top: 0; - padding-bottom: 0; + padding-top: 1px; + padding-bottom: 1px; padding-right: 1px; padding-left: 1px; background-color: black; diff --git a/testing/web-platform/tests/mathml/relations/css-styling/table-width-1.xhtml b/testing/web-platform/tests/mathml/relations/css-styling/table-width-1.xhtml index c3689adb6f..9158948c72 100644 --- a/testing/web-platform/tests/mathml/relations/css-styling/table-width-1.xhtml +++ b/testing/web-platform/tests/mathml/relations/css-styling/table-width-1.xhtml @@ -6,8 +6,8 @@ <style type="text/css"> html { background-color: grey; } td { border: 1px solid white; - padding-top: 0; - padding-bottom: 0; + padding-top: 1px; + padding-bottom: 1px; padding-right: 1px; padding-left: 1px; background-color: black; diff --git a/testing/web-platform/tests/media/A4.ogv b/testing/web-platform/tests/media/A4.ogv Binary files differdeleted file mode 100644 index de99616ece..0000000000 --- a/testing/web-platform/tests/media/A4.ogv +++ /dev/null diff --git a/testing/web-platform/tests/media/counting.ogv b/testing/web-platform/tests/media/counting.ogv Binary files differdeleted file mode 100644 index ce03c19e50..0000000000 --- a/testing/web-platform/tests/media/counting.ogv +++ /dev/null diff --git a/testing/web-platform/tests/media/green-at-15.ogv b/testing/web-platform/tests/media/green-at-15.ogv Binary files differdeleted file mode 100644 index 50d59dfb38..0000000000 --- a/testing/web-platform/tests/media/green-at-15.ogv +++ /dev/null diff --git a/testing/web-platform/tests/media/movie_300.ogv b/testing/web-platform/tests/media/movie_300.ogv Binary files differdeleted file mode 100644 index 0f83996e5d..0000000000 --- a/testing/web-platform/tests/media/movie_300.ogv +++ /dev/null diff --git a/testing/web-platform/tests/media/movie_5.ogv b/testing/web-platform/tests/media/movie_5.ogv Binary files differdeleted file mode 100644 index e8990d1120..0000000000 --- a/testing/web-platform/tests/media/movie_5.ogv +++ /dev/null diff --git a/testing/web-platform/tests/media/test.ogv b/testing/web-platform/tests/media/test.ogv Binary files differdeleted file mode 100644 index 0c55f6c722..0000000000 --- a/testing/web-platform/tests/media/test.ogv +++ /dev/null diff --git a/testing/web-platform/tests/media/video.ogv b/testing/web-platform/tests/media/video.ogv Binary files differdeleted file mode 100644 index 5cb5f87848..0000000000 --- a/testing/web-platform/tests/media/video.ogv +++ /dev/null diff --git a/testing/web-platform/tests/mediacapture-streams/BrowserCaptureMediaStreamTrack-cropTo.https.html b/testing/web-platform/tests/mediacapture-streams/BrowserCaptureMediaStreamTrack-cropTo.https.html new file mode 100644 index 0000000000..f84029433f --- /dev/null +++ b/testing/web-platform/tests/mediacapture-streams/BrowserCaptureMediaStreamTrack-cropTo.https.html @@ -0,0 +1,66 @@ +<!doctype html> +<html> + +<head> + <title>BrowserCaptureMediaStreamTrack cropTo()</title> + <link rel="help" href="https://github.com/w3c/mediacapture-region/"> + <meta charset="utf-8" /> + <meta http-equiv="X-UA-Compatible" content="IE=edge" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> +</head> + +<body> + <p class="instructions"> + When prompted, accept to give permission to use your audio, video devices. + </p> + <h1 class="instructions">Description</h1> + <p class="instructions"> + This test checks that BrowserCaptureMediaStreamTrack cropping works as + expected. + </p> + + <button id="button">Start test</button> + <div id='test-div' width="500px" height="600px"></div> + + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src=/resources/testdriver.js></script> + <script src=/resources/testdriver-vendor.js></script> + <script src=permission-helper.js></script> + + <script> + "use strict"; + + async function getDisplayMedia() { + const p = new Promise(r => button.onclick = r); + await test_driver.click(button); + await p; + return navigator.mediaDevices.getDisplayMedia( + {video:{displaySurface:"browser"}, selfBrowserSurface:"include"}); + } + + promise_test(async t => { + const stream = await getDisplayMedia(); + assert_true(stream.active, "stream should be active."); + + assert_equals(stream.getVideoTracks().length, 1); + const [videoTrack] = stream.getVideoTracks(); + assert_true(videoTrack instanceof MediaStreamTrack, + "track should be either MediaStreamTrack or a subclass thereof."); + assert_equals(videoTrack.readyState, "live"); + + const div = document.getElementById('test-div'); + const cropTarget = await CropTarget.fromElement(div); + assert_true(!!videoTrack.cropTo, "cropTo exposed."); + assert_true(typeof videoTrack.cropTo === 'function', + "cropTo is a function."); + await videoTrack.cropTo(cropTarget); + + assert_true(stream.active, "stream should be active."); + assert_false(videoTrack.muted, "track should not be muted."); + }, "Tests that cropping MediaStreamTrack objects works as expected"); + + </script> +</body> + +</html> diff --git a/testing/web-platform/tests/mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html b/testing/web-platform/tests/mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html index eb7b443df3..44dfa80153 100644 --- a/testing/web-platform/tests/mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html +++ b/testing/web-platform/tests/mixed-content/tentative/autoupgrades/mixed-content-cors.https.sub.html @@ -67,11 +67,11 @@ var otherHost = get_host_info().HTTP_NOTSAMESITE_ORIGIN.slice(0, -4); // cut of http port var url = new URL( otherHost + - "{{ports[https][0]}}/mixed-content/tentative/resources/test.ogv?pipe=header(Access-Control-Allow-Origin,*)" + "{{ports[https][0]}}/mixed-content/tentative/resources/test.webm?pipe=header(Access-Control-Allow-Origin,*)" ); var i = document.createElement("video"); i.oncanplaythrough = test.step_func_done((_) => { - assert_equals(Math.floor(i.duration), 300, "Length. Other host"); + assert_equals(Math.floor(i.duration), 1, "Length. Other host"); }); i.crossOrigin = "anonymous"; i.onerror = test.unreached_func( diff --git a/testing/web-platform/tests/mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html b/testing/web-platform/tests/mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html index ea2d07309c..d9ad3dda39 100644 --- a/testing/web-platform/tests/mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html +++ b/testing/web-platform/tests/mixed-content/tentative/autoupgrades/video-upgrade.https.sub.html @@ -13,10 +13,10 @@ function assert_video_loads(test) { // Since autoupgrades don't upgrade custom ports, we use the https port with an HTTP scheme. A successful autoupgrade will result in the right URL loading (and no autoupgrade will result in failure). - var url = new URL("http://{{host}}:{{ports[https][0]}}/mixed-content/tentative/resources/test.ogv") + var url = new URL("http://{{host}}:{{ports[https][0]}}/mixed-content/tentative/resources/test.webm") var i = document.createElement('video'); i.oncanplaythrough = test.step_func_done(_ => { - assert_equals(Math.floor(i.duration), 300, "Length."); + assert_equals(Math.floor(i.duration), 1, "Length."); }); i.onerror = test.unreached_func("Video should load successfully from " + url); i.src = url; @@ -28,10 +28,10 @@ function assert_other_host_video_loads(test) { // Since autoupgrades don't upgrade custom ports, we use the https port with an HTTP scheme. A successful autoupgrade will result in the right URL loading (and no autoupgrade will result in failure). var otherHost = get_host_info().HTTP_NOTSAMESITE_ORIGIN.slice(0,-4); // cut of http port - var url = new URL( otherHost + "{{ports[https][0]}}/mixed-content/tentative/resources/test.ogv") + var url = new URL( otherHost + "{{ports[https][0]}}/mixed-content/tentative/resources/test.webm") var i = document.createElement('video'); i.oncanplaythrough = test.step_func_done(_ => { - assert_equals(Math.floor(i.duration), 300, "Length. Other host"); + assert_equals(Math.floor(i.duration), 1, "Length. Other host"); }); i.onerror = test.unreached_func("Video of other host should load successfully from " + url); i.src = url; diff --git a/testing/web-platform/tests/mixed-content/tentative/resources/test.ogv b/testing/web-platform/tests/mixed-content/tentative/resources/test.ogv Binary files differdeleted file mode 100644 index 0f83996e5d..0000000000 --- a/testing/web-platform/tests/mixed-content/tentative/resources/test.ogv +++ /dev/null diff --git a/testing/web-platform/tests/mixed-content/tentative/resources/test.webm b/testing/web-platform/tests/mixed-content/tentative/resources/test.webm Binary files differnew file mode 100644 index 0000000000..e3e4cb0bac --- /dev/null +++ b/testing/web-platform/tests/mixed-content/tentative/resources/test.webm diff --git a/testing/web-platform/tests/orientation-event/META.yml b/testing/web-platform/tests/orientation-event/META.yml index 88014849d0..cd5fcdfeb1 100644 --- a/testing/web-platform/tests/orientation-event/META.yml +++ b/testing/web-platform/tests/orientation-event/META.yml @@ -1,4 +1,3 @@ spec: https://w3c.github.io/deviceorientation/ suggested_reviewers: - reillyeon - - timvolodine diff --git a/testing/web-platform/tests/payment-method-basic-card/billing-address-is-null-manual.https.html b/testing/web-platform/tests/payment-method-basic-card/billing-address-is-null-manual.https.html index 3250e0a2c5..5eac71963e 100644 --- a/testing/web-platform/tests/payment-method-basic-card/billing-address-is-null-manual.https.html +++ b/testing/web-platform/tests/payment-method-basic-card/billing-address-is-null-manual.https.html @@ -101,8 +101,8 @@ function checkRedaction(billingAddress) { assert_true( - billingAddress instanceof PaymentAddress, - "Expected instance of PaymentAddress" + billingAddress instanceof ContactAddress, + "Expected instance of ContactAddress" ); for (const item of ["organization", "phone", "recipient"]) { assert_equals( @@ -134,7 +134,7 @@ <button onclick="requestBillingAddress()"> When billing address is requested,`PaymentMethodChangeEvent.methodData.billingAddress` is a - `PaymentAddress`. + `ContactAddress`. </button> </li> <li><button onclick="done()">Done!</button></li> diff --git a/testing/web-platform/tests/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html b/testing/web-platform/tests/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html new file mode 100644 index 0000000000..fc1ce3523e --- /dev/null +++ b/testing/web-platform/tests/payment-request/PaymentAddress/attributes-and-toJSON-method-manual.https.html @@ -0,0 +1,109 @@ +<!doctype html> +<meta charset="utf8"> +<link rel="help" href="https://www.w3.org/TR/payment-request/#ContactAddress-interface"> +<title> + PaymentResponse.prototype.shippingAddress +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../payment-response/helpers.js"></script> +<script> +const options = { requestShipping: true }; +function runManualTest(button, expected = {}) { + button.disabled = true; + promise_test(async () => { + const { response } = await getPaymentRequestResponse(options); + await response.complete(); + assert_idl_attribute(response, "shippingAddress"); + const { shippingAddress: addr } = response; + assert_true( + addr instanceof ContactAddress, + "Expect instance of ContactAddress" + ); + // An [ISO3166] alpha-2 code. The canonical form is upper case. + const { country } = addr; + assert_equals(country.length, 2, "Expected length is 2"); + assert_true(/^[A-Z]{2}$/.test(country), "Canonical form is upper case"); + assert_true( + addr.addressLine instanceof Array, + "Expected addressLine to be an array" + ); + assert_throws_js( + TypeError, + () => { + addr.addressLine.push("this must throw"); + }, + "Array must be frozen" + ); + for (let [attr, expectedValue] of Object.entries(expected)) { + assert_idl_attribute(addr, attr); + const msg = `Expected ContactAddress.${attr} to equal ${expectedValue}.`; + //.toString() flattens array addressLine, + //.toLowerCase() because case can't be enforced for some attributes + const actualValue = addr[attr].toString().toLowerCase(); + expectedValue = expectedValue.toString().toLowerCase(); + assert_equals(actualValue, expectedValue, msg); + } + // Check toJSON result + for (let [prop, jsonValue] of Object.entries(addr.toJSON())) { + const actualValue = jsonValue.toString().toLowerCase(); + const expectedValue = expected[prop].toString().toLowerCase(); + const msg = `Expected JSON ${prop} to be ${expectedValue}`; + assert_equals(actualValue, expectedValue, msg); + } + }, button.textContent.trim()); + done(); +} +</script> +<h2>ContactAddress interface</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + When prompted, please enter addresses as follows... +</p> +<ol> + <li> + <button onclick=" + const expectedAddress = { + country: 'AU', + regionCode: 'QLD', + addressLine: '55 test st', + city: 'Chapel Hill', + dependentLocality: '', + postalCode: '6095', + region: 'QLD', + sortingCode: '', + organization: 'w3c', + recipient: 'web platform test', + phone: '+61733780000', + }; + runManualTest(this, expectedAddress);"> + If the requestShipping member is true, then shippingAddress's ContactAddress must match the expected values. + </button> + Please use: + <dl> + <dt>Recipient:</dt> + <dd>web platform test</dd> + <dt>Address line:</dt> + <dd>55 test st</dd> + <dt>Country</dt> + <dd>Australia</dd> + <dt>City</dt> + <dd>Chapel Hill</dd> + <dd>State/Region</dd> + <dd>Queensland</dd> + <dt>postal code </dt> + <dd>6095</dd> + <dt>organization</dt> + <dd>w3c</dd> + <dt>Phone number</dt> + <dd>+61 7 3378 0000</dd> + </dl> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-call-immediate-manual.https.html b/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-call-immediate-manual.https.html new file mode 100644 index 0000000000..1365ecefeb --- /dev/null +++ b/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-call-immediate-manual.https.html @@ -0,0 +1,206 @@ +<!doctype html> +<meta charset="utf8"> +<link rel="help" href="https://www.w3.org/TR/payment-request/#updatewith()-method"> +<link rel="help" href="https://github.com/w3c/payment-request/pull/591"> +<title> + PaymentRequestUpdateEvent.updateWith() needs to be called immediately +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ explicit_done: true, explicit_timeout: true }); +const applePay = Object.freeze({ + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + } +}); +const validMethod = Object.freeze({ supportedMethods: "basic-card" }); +const validMethods = Object.freeze([validMethod, applePay]); +const validAmount = Object.freeze({ currency: "USD", value: "5.00" }); +const validTotal = Object.freeze({ + label: "label", + amount: validAmount, +}); +const validShippingOptionA = Object.freeze({ + id: "a-shipping-option", + label: "A shipping option", + amount: validAmount, + selected: true, +}); +const validShippingOptionB = Object.freeze({ + id: "b-shipping-option", + label: "B shipping option", + amount: validAmount, +}); +const validDetails = Object.freeze({ + total: validTotal, + shippingOptions: [validShippingOptionA, validShippingOptionB], +}); +const validOptions = Object.freeze({ + requestShipping: true, +}); + +function testImmediateUpdate({ textContent: testName }) { + promise_test(async t => { + const request = new PaymentRequest( + validMethods, + validDetails, + validOptions + ); + const eventPromise = new Promise((resolve, reject) => { + request.addEventListener( + "shippingaddresschange", + ev => { + // Forces updateWith() to be run in the next event loop tick so that + // [[waitForUpdate]] is already true when it runs. + t.step_timeout(() => { + try { + ev.updateWith(validDetails); + resolve(); // This is bad. + } catch (err) { + reject(err); // this is good. + } + }); + }, + { once: true } + ); + }); + const acceptPromise = request.show(); + await promise_rejects_dom( + t, + "InvalidStateError", + eventPromise, + "The event loop already spun, so [[waitForUpdate]] is now true" + ); + const response = await acceptPromise; + await response.complete(); + }, testName.trim()); +} + +function testSubsequentUpdateWithCalls({ textContent: testName }) { + promise_test(async t => { + const request = new PaymentRequest( + validMethods, + validDetails, + validOptions + ); + const eventPromise = new Promise((resolve, reject) => { + request.addEventListener("shippingaddresschange", async ev => { + const p = Promise.resolve(validDetails); + ev.updateWith(p); + await p; + try { + ev.updateWith(validDetails); + resolve(); // this is bad, we should never get to here. + } catch (err) { + reject(err); // this is good! + } + }); + }); + const responsePromise = request.show(); + await promise_rejects_dom( + t, + "InvalidStateError", + eventPromise, + "Expected eventPromise to have rejected, because updateWith() was a called twice" + ); + const response = await responsePromise; + await response.complete(); + }, testName.trim()); +} + +function testRecycleEvents({ textContent: testName }) { + promise_test(async t => { + const request = new PaymentRequest( + validMethods, + validDetails, + validOptions + ); + + // Register both listeners. + const addressChangedPromise = new Promise(resolve => { + request.addEventListener("shippingaddresschange", resolve, { + once: true, + }); + }); + + const optionChangedPromise = new Promise(resolve => { + request.addEventListener("shippingoptionchange", resolve, { + once: true, + }); + }); + + const responsePromise = request.show(); + + // Let's wait for the address to change. + const addressChangeEvent = await addressChangedPromise; + + // Sets [[waitingForUpdate]] to true. + addressChangeEvent.updateWith(validDetails); + + // Let's wait for the shippingOption. + const optionChangeEvent = await optionChangedPromise; + + // Here, we try to be sneaky, and reuse the addressChangeEvent to perform the update. + // However, addressChangeEvent [[waitingForUpdate]] is true, so it throws. + assert_throws_dom( + "InvalidStateError", + () => { + addressChangeEvent.updateWith(validDetails); + }, + "addressChangeEvent [[waitingForUpdate]] is true, so it must throw" + ); + + // But optionChangeEvent is still usable tho, so... + optionChangeEvent.updateWith(validDetails); + + assert_throws_dom( + "InvalidStateError", + () => { + optionChangeEvent.updateWith(validDetails); + }, + "optionChangeEvent [[waitingForUpdate]] is true, so it must throw" + ); + + const response = await responsePromise; + await response.complete(); + }, testName.trim()); +} +</script> +<h2>updateWith() method</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + When the payment sheet is shown, select a different shipping address once. Then pay. +</p> +<ol> + <li id="test-0"> + <button onclick="testImmediateUpdate(this);"> + updateWith() must be called immediately, otherwise must throw an InvalidStateError. + </button> + </li> + <li id="test-1"> + <button onclick="testSubsequentUpdateWithCalls(this);"> + Once the event has performed an update, subsequent calls to updateWith() must throw InvalidStateError. + </button> + </li> + <li id="test-2"> + <button onclick="testRecycleEvents(this);"> + Recycling events must not be possible. + </button> When the payment sheet is shown, select a different shipping address once, then change shipping option once. Then pay. + </li> + <li> + <button onclick="done();">Done!</button> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-duplicate-shipping-options-manual.https.html b/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-duplicate-shipping-options-manual.https.html new file mode 100644 index 0000000000..a4a7afd7f6 --- /dev/null +++ b/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-duplicate-shipping-options-manual.https.html @@ -0,0 +1,106 @@ +<!doctype html> +<meta charset="utf8"> +<link rel="help" href="https://w3c.github.io/payment-request/#updatewith()-method"> +<title> + updateWith() method - duplicate shippingOption ids +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ explicit_done: true, explicit_timeout: true }); +const applePay = Object.freeze({ + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + } +}); +const validMethod = Object.freeze({ supportedMethods: "basic-card" }); +const validMethods = [validMethod, applePay]; +const validAmount = Object.freeze({ + currency: "USD", + value: "5.00", +}); +const validShippingOption = Object.freeze({ + id: "option1", + label: "Option 1", + amount: validAmount, + selected: true, +}); +const validShippingOptions = Object.freeze([validShippingOption]); +const validDetails = Object.freeze({ + total: { + label: "Total due", + amount: validAmount, + }, + shippingOptions: validShippingOptions, +}); +const validOptions = Object.freeze({ + requestShipping: true, +}); + +test(() => { + try { + const request = new PaymentRequest(validMethods, validDetails); + } catch (err) { + done(); + throw err; + } +}, "Must construct a PaymentRequest (smoke test)"); + +function testFireEvents(button) { + button.disabled = true; + promise_test(async t => { + const request = new PaymentRequest( + validMethods, + validDetails, + validOptions + ); + request.addEventListener("shippingaddresschange", event => { + // Same option, so duplicate ids + const otherShippingOption = Object.assign({}, validShippingOption, { + id: "other", + }); + const shippingOptions = [ + validShippingOption, + otherShippingOption, + validShippingOption, + ]; + const newDetails = Object.assign({}, validDetails, { shippingOptions }); + event.updateWith(newDetails); + }); + const acceptPromise = request.show(); + await promise_rejects_js( + t, + TypeError, + acceptPromise, + "Duplicate shippingOption ids must abort with TypeError" + ); + }, button.textContent.trim()); + done(); +} +</script> +<h2>updateWith() method - duplicate shippingOptions ids</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + When the payment sheet is shown, select a different shipping address. + If you have to manually abort the test from the payment sheet, then the + test has failed. +</p> +<ol> + <li> + <button onclick="testFireEvents(this)"> + If there are duplicate shippingOption ids, then abort payment request. + </button> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-incremental-update-manual.https.html b/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-incremental-update-manual.https.html new file mode 100644 index 0000000000..c1ed1b5f68 --- /dev/null +++ b/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-incremental-update-manual.https.html @@ -0,0 +1,196 @@ +<!doctype html> +<meta charset="utf8"> +<link rel="help" href="https://w3c.github.io/payment-request/#updatewith-method"> +<title> + Incremental updates via updateWith() +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ + explicit_done: true, + explicit_timeout: true, +}); + +const methods = [{ + supportedMethods: "basic-card", +}, { + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + } +}]; + +const options = { + requestShipping: true, +}; + +const initialDetails = { + total: { + label: "Initial total", + amount: { + currency: "USD", + value: "1.0", + }, + }, + shippingOptions: [ + { + id: "neutral", + label: "NEUTRAL SHIPPING OPTION", + selected: true, + amount: { + currency: "USD", + value: "0.00", + }, + }, + ], +}; + +function testFireEvent(button, updateDetails) { + button.disabled = true; + const request = new PaymentRequest(methods, initialDetails, options); + const handlerPromise = new Promise(resolve => { + request.onshippingaddresschange = event => { + event.updateWith(updateDetails); + resolve(event); + }; + }); + promise_test(async t => { + const response = await request.show(); + const event = await handlerPromise; + await response.complete("success"); + }, button.textContent.trim()); +} + +</script> +<h2> + Incremental updates +</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + Unless stated otherwise, each test will update some part of the displayed payment sheet in + a manner indicated below. When prompted, please change or enter a new + shipping address, look for the tested change, and complete the payment. +</p> +<p> + If the payment request locks up or otherwise aborts, the test has failed. +</p> +<ol> + <li> + <button onclick="testFireEvent(this, {});"> + Passing an empty dictionary does not cause the sheet to change. + No values in the sheet must change. + </button> + </li> +</ol> + +<section> + <h3>Incremental updates via PaymentDetailsUpdate.total</h3> + <ol> + <li> + <button onclick=" + const total = { + label: 'PASS', + amount: { + currency: 'XXX', + value: '20', + }, + }; + const updatedDetails = { total }; + testFireEvent(this, updatedDetails);"> + After changing shipping address, the total becomes XXX20, with the label "PASS". + </button> + </li> + </ol> +</section> + +<section> + <h3>Incremental updates via PaymentDetailsBase.displayItems</h3> + <ol> + <li> + <button onclick=" + const item = { + label: 'PASS', + amount: { currency: 'ABC', value: '55.00' }, + }; + const updatedDetails = { + displayItems: [ item ] + }; + testFireEvent(this, updatedDetails);"> + After changing shipping address, a new display item is shown + with a with label PASS, and value of ABC55.00. + </button> + </li> + </ol> +</section> + +<section> + <h3>Incremental updates via PaymentDetailsBase.shippingOptions</h3> + <ol> + <li> + <button onclick=" + const shippingOptions = [ + { + id: 'pass', + label: 'PASS', + amount: { currency: 'USD', value: '1.00' }, + selected: true, + }, + { + id: 'fail', + label: 'FAIL IF THIS IS SELECTED', + amount: { currency: 'USD', value: '25.00' } + }, + ]; + const updatedDetails = { + shippingOptions + }; + testFireEvent(this, updatedDetails);"> + After changing shipping address, two new shipping options appear. + The shipping option labelled "PASS" with a value of USD1.0 is selected. + </button> + </li> + </ol> +</section> + +<section> + <h3>Incremental updates via PaymentDetailsBase.modifiers</h3> + <ol> + <li> + <button onclick=" + const additionalItem = { + label: 'PASS-DISPLAY-ITEM', + amount: { currency: 'USD', value: '3.00' }, + }; + const modifiers = [{ + additionalDisplayItems: [ additionalItem ], + supportedMethods: 'basic-card', + total: { + label: 'PASS-TOTAL', + amount: { currency: 'USD', value: '123.00' }, + }, + }]; + const updatedDetails = { modifiers }; + testFireEvent(this, updatedDetails);"> + After changing shipping address, a new display item is shown + with a with label PASS-DISPLAY-ITEM, and value of ABC55.00 and the total is + labelled PASS-TOTAL with a value of USD123.0 + </button> + </li> + <li> + <button onclick="done()">DONE - see results</button> + </li> + </ol> +</section> + +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-method-abort-update-manual.https.html b/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-method-abort-update-manual.https.html new file mode 100644 index 0000000000..e24452c2a9 --- /dev/null +++ b/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-method-abort-update-manual.https.html @@ -0,0 +1,286 @@ +<!doctype html> +<meta charset="utf8"> +<link rel="help" href="https://w3c.github.io/payment-request/#dfn-abort-the-update"> +<title> + updateWith() method - "abort the update" +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ explicit_done: true, explicit_timeout: true }); + +// PaymentMethod +const validMethod = Object.freeze({ + supportedMethods: "valid-but-wont-ever-match", +}); + +const validMethodBasicCard = Object.freeze({ + supportedMethods: "basic-card", +}); + +const applePay = Object.freeze({ + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + } +}); + +// Methods +const validMethods = Object.freeze([validMethodBasicCard, validMethod, applePay]); + +// Amounts +const validAmount = Object.freeze({ + currency: "USD", + value: "1.00", +}); + +const invalidAmount = Object.freeze({ + currency: "¡INVALID!", + value: "A1.0", +}); + +const negativeAmount = Object.freeze({ + currency: "USD", + value: "-1.00", +}); + +// Totals +const validTotal = Object.freeze({ + label: "Valid Total", + amount: validAmount, +}); + +const invalidTotal = Object.freeze({ + label: "Invalid Total", + amount: invalidAmount, +}); + +const invalidNegativeTotal = Object.freeze({ + label: "Invalid negative total", + amount: negativeAmount, +}); + +// PaymentDetailsInit +const validDetails = Object.freeze({ + total: validTotal, +}); + +const invalidDetailsNegativeTotal = Object.freeze({ + total: invalidNegativeTotal, +}); + +// PaymentOptions +const validOptions = Object.freeze({ + requestShipping: true, +}); + +// PaymentItem +const validPaymentItem = Object.freeze({ + amount: validAmount, + label: "Valid payment item", +}); + +const invalidPaymentItem = Object.freeze({ + amount: invalidAmount, + label: "Invalid payment item", +}); + +// PaymentItem +const validPaymentItems = Object.freeze([validPaymentItem]); +const invalidPaymentItems = Object.freeze([invalidPaymentItem]); + +// PaymentShippingOption +const invalidShippingOption = Object.freeze({ + id: "abc", + label: "Invalid shipping option", + amount: invalidAmount, + selected: true, +}); + +// PaymentShippingOptions +const validShippingOption = Object.freeze({ + id: "abc", + label: "valid shipping option", + amount: validAmount, +}); + +const validShippingOptions = Object.freeze([validShippingOption]); +const invalidShippingOptions = Object.freeze([invalidShippingOption]); + +// PaymentDetailsModifier +const validModifier = Object.freeze({ + additionalDisplayItems: validPaymentItems, + supportedMethods: "valid-but-wont-ever-match", + total: validTotal, +}); + +const modifierWithInvalidDisplayItems = Object.freeze({ + additionalDisplayItems: invalidPaymentItems, + supportedMethods: "basic-card", + total: validTotal, +}); + +const modifierWithValidDisplayItems = Object.freeze({ + additionalDisplayItems: validPaymentItems, + supportedMethods: "basic-card", + total: validTotal, +}); + +const modifierWithInvalidTotal = Object.freeze({ + additionalDisplayItems: validPaymentItems, + supportedMethods: "basic-card", + total: invalidTotal, +}); + +const recursiveData = {}; +recursiveData.foo = recursiveData; +Object.freeze(recursiveData); + +const modifierWithRecursiveData = Object.freeze({ + supportedMethods: "basic-card", + total: validTotal, + data: recursiveData, +}); + +function testBadUpdate(button, badDetails, expectedError, errorCode) { + button.disabled = true; + promise_test(async t => { + const request = new PaymentRequest( + validMethods, + validDetails, + validOptions + ); + request.onshippingaddresschange = event => { + event.updateWith(badDetails); + }; + // First we check the bad update. + const acceptPromise = request.show(); + let test_func; + if (typeof expectedError == "function") { + test_func = promise_rejects_js; + } else { + test_func = promise_rejects_dom; + } + await test_func( + t, + expectedError, + acceptPromise, + "badDetails must cause acceptPromise to reject with expectedError" + ); + // The request [[state]] is now "closed", so let's check for InvalidStateError + await promise_rejects_dom( + t, + "InvalidStateError", + request.show(), + "show() must reject with InvalidStateError" + ); + }, button.innerText.trim()); +} +</script> +<h2>updateWith() method - "abort the update"</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + When the payment sheet is shown, change the shipping address. +</p> +<ol> + <li> + <button onclick=" + const rejectedPromise = Promise.reject(new SyntaxError('test')); + testBadUpdate(this, rejectedPromise, 'AbortError'); + "> + Rejection of detailsPromise must abort the update with an "AbortError" DOMException. + </button> + </li> + <li> + <button onclick=" + const invalidDetails = { total: `this will cause a TypeError!` }; + testBadUpdate(this, invalidDetails, TypeError); + "> + Total in the update is a string, so converting to IDL must abort the update with a TypeError. + </button> + </li> + <li> + <button onclick=" + const invalidDetails = { total: recursiveData }; + testBadUpdate(this, invalidDetails, TypeError); + "> + Total is recursive, so converting to IDL must abort the update with a TypeError. + </button> + </li> + <li> + <button onclick=" + testBadUpdate(this, invalidDetailsNegativeTotal, TypeError); + "> + Updating with a negative total results in a TypeError. + </button> + </li> + <li> + <button onclick=" + const badDetails = Object.assign({}, validDetails, { displayItems: invalidPaymentItems }); + testBadUpdate(this, badDetails, RangeError); + "> + Updating with a displayItem with an invalid currency results in RangeError. + </button> + </li> + <li> + <button onclick=" + const duplicateShippingOptions = [validShippingOption, validShippingOption]; + const badDetails = Object.assign({}, validDetails, { shippingOptions: duplicateShippingOptions }); + testBadUpdate(this, badDetails, TypeError); + "> + Updating with duplicate shippingOptions (same IDs) results in a TypeError. + </button> + </li> + <li> + <button onclick=" + const badDetails = Object.assign({}, validDetails, { shippingOptions: invalidShippingOptions }); + testBadUpdate(this, badDetails, RangeError); + "> + Updating with a shippingOption with an invalid currency value results in a RangError. + </button> + </li> + <li> + <button onclick=" + // validModifier is there as to avoid false positives - it should just get ignored + const badModifiers = { modifiers: [ modifierWithInvalidTotal, validModifier ] }; + const badDetails = Object.assign({}, validDetails, badModifiers); + testBadUpdate(this, badDetails, RangeError); + "> + Must throw a RangeError when a modifier's total item has an invalid currency. + </button> + </li> + <li> + <button onclick=" + // validModifier is there as to avoid false positives - it should just get ignored + const badModifiers = { modifiers: [ modifierWithInvalidDisplayItems, validModifier ] }; + const badDetails = Object.assign({}, validDetails, badModifiers); + testBadUpdate(this, badDetails, RangeError); + "> + Must throw a RangeError when a modifier display item has an invalid currency. + </button> + </li> + <li> + <button onclick=" + // validModifier is there as to avoid false positives - it should just get ignored + const badModifiers = { modifiers: [ modifierWithRecursiveData, validModifier ] }; + const badDetails = Object.assign({}, validDetails, badModifiers); + testBadUpdate(this, badDetails, TypeError); + "> + Must throw as Modifier has a recursive dictionary. + </button> + </li> + <li> + <button onclick="done();">Done!</button> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-state-checks-manual.https.html b/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-state-checks-manual.https.html new file mode 100644 index 0000000000..fb16de5699 --- /dev/null +++ b/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updateWith-state-checks-manual.https.html @@ -0,0 +1,125 @@ +<!doctype html> +<meta charset="utf8"> +<link rel="help" href="https://www.w3.org/TR/payment-request/#updatewith()-method"> +<title>updateWith() method - state machine checks</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ explicit_done: true, explicit_timeout: true }); +const applePay = Object.freeze({ + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + } +}); +const validMethod = Object.freeze({ supportedMethods: "basic-card" }); +const validMethods = Object.freeze([validMethod, applePay]); +const validAmount = Object.freeze({ currency: "USD", value: "5.00" }); +const validTotal = Object.freeze({ + label: "label", + amount: validAmount, +}); +const validShippingOption = Object.freeze({ + id: "a-shipping-option", + label: "A shipping option", + amount: validAmount, + selected: true, +}); +const validDetails = Object.freeze({ + total: validTotal, + shippingOptions: [validShippingOption], +}); +const validOptions = Object.freeze({ + requestShipping: true, +}); + +function getPaymentPromises() { + const request = new PaymentRequest(validMethods, validDetails, validOptions); + const eventPromise = new Promise(resolve => { + request.addEventListener("shippingaddresschange", resolve); + }); + const responsePromise = request.show(); + return { eventPromise, responsePromise }; +} + +function testRequestIsClosed(button) { + button.disabled = "true"; + promise_test(async t => { + const { eventPromise, responsePromise } = getPaymentPromises(); + const event = await eventPromise; + // We are going to abort the responsePromise, so we can ignore error. + responsePromise.catch(err => err); + // Set request.[[state]] to closed + await event.target.abort(); + assert_throws_dom( + "InvalidStateError", + () => { + event.updateWith(validDetails); + }, + "request.[[state]] is not interactive, must throw an InvalidStateError." + ); + responsePromise.catch(err => err); + }, button.textContent.trim()); +} + +function testRequestIsUpdating(button) { + button.disabled = "true"; + promise_test(async t => { + const { eventPromise, responsePromise } = getPaymentPromises(); + const event = await eventPromise; + // We are going to put a promise into a pending state + // check that a second call to updateWith() throws, + // then resolve the pending promise below. + let resolver; + const pendingPromise = new Promise(resolve => { + resolver = resolve; + }); + // Set request.[[updating]] to true + event.updateWith(pendingPromise); + assert_throws_dom( + "InvalidStateError", + () => { + event.updateWith(validDetails); + }, + "request.[[updating]] to true, must throw an InvalidStateError." + ); + // We got the error we wanted, so let's resolve with valid details. + resolver(validDetails); + await pendingPromise; + await event.target.abort(); + responsePromise.catch(err => err); + }, button.textContent.trim()); +} + +</script> +<h2>updateWith() method - state machine checks</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + When the payment sheet is shown, select a different shipping address once. Then pay. +</p> +<ol> + <li id="test-0"> + <button onclick="testRequestIsClosed(this);"> + When updateWith() is called, if request.[[state]] is not "interactive", then throw an " InvalidStateError" DOMException. + </button> + </li> + <li id="test-1"> + <button onclick="testRequestIsUpdating(this);"> + When updateWith() is called, If request.[[updating]] is true, then throw an "InvalidStateError" DOMException. + </button> + </li> + <li> + <button onclick="done();">Done!</button> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updatewith-method.https.html b/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updatewith-method.https.html index 9a60fe7a4c..fffd3b3ec5 100644 --- a/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updatewith-method.https.html +++ b/testing/web-platform/tests/payment-request/PaymentRequestUpdateEvent/updatewith-method.https.html @@ -29,7 +29,9 @@ test(() => { // Github issue: https://github.com/w3c/browser-payment-api/issues/546 test(() => { const untrustedEvents = [ - new PaymentRequestUpdateEvent("just a test") + new PaymentRequestUpdateEvent("just a test"), + new PaymentRequestUpdateEvent("shippingaddresschange"), + new PaymentRequestUpdateEvent("shippingoptionchange"), ].forEach(ev => { assert_throws_dom( "InvalidStateError", @@ -45,7 +47,9 @@ test(() => { test(() => { const request = new PaymentRequest(defaultMethods, defaultDetails); const untrustedEvents = [ - new PaymentRequestUpdateEvent("just a test") + new PaymentRequestUpdateEvent("just a test"), + new PaymentRequestUpdateEvent("shippingaddresschange"), + new PaymentRequestUpdateEvent("shippingoptionchange"), ].map(ev => { request.dispatchEvent(ev); // set .target and dispatch flag // unstrusted event. diff --git a/testing/web-platform/tests/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html b/testing/web-platform/tests/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html new file mode 100644 index 0000000000..94e6fa5105 --- /dev/null +++ b/testing/web-platform/tests/payment-request/PaymentValidationErrors/retry-shows-shippingAddress-member-manual.https.html @@ -0,0 +1,103 @@ +<!doctype html> +<meta charset="utf8"> +<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentvalidationerrors-shippingaddress"> +<title> + PaymentValidationErrors' `shippingAddress` member (AddressErrors) +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../payment-response/helpers.js"></script> +<script> +function retryShowsShippingAddressMember(button, error) { + button.disabled = true; + promise_test(async t => { + const options = { + requestShipping: true, + } + const { response } = await getPaymentRequestResponse(options); + await response.retry({ shippingAddress: error }); + await response.complete("success"); + }, button.textContent.trim()); +} +</script> +<h2> + Manual Test for PaymentValidationErrors' `shippingAddress` member - Please run in order! +</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + When presented with the payment sheet, use any card and select to "Pay". + You will be asked to retry the payment and an error should be shown somewhere + in the UI. The expected error string is described in each individual test. + If you see the error, hit "Pay" again. If you don't see the error, + abort the payment request by hitting "esc" - which means that particular test + has failed. +</p> +<ol> + <li> + <button onclick="retryShowsShippingAddressMember(this, { addressLine: 'ADDRESSLINE ERROR' });"> + The payment sheet shows "ADDRESSLINE ERROR" for the shipping address' addressLine. + </button> + </li> + <li> + <button onclick="retryShowsShippingAddressMember(this, { city: 'CITY ERROR' });"> + The payment sheet shows "CITY ERROR" for the shipping address' city. + </button> + </li> + <li> + <button onclick="retryShowsShippingAddressMember(this, { country: 'COUNTRY ERROR' });"> + The payment sheet shows "COUNTRY ERROR" for the shipping address' country. + </button> + </li> + <li> + <button onclick="retryShowsShippingAddressMember(this, { dependentLocality: 'DEPENDENTLOCALITY ERROR' });"> + The payment sheet shows "DEPENDENTLOCALITY ERROR" for the shipping address' dependentLocality. + </button> + </li> + <li> + <button onclick="retryShowsShippingAddressMember(this, { organization: 'ORGANIZATION ERROR' });"> + The payment sheet shows "ORGANIZATION ERROR" for the shipping address' organization. + </button> + </li> + <li> + <button onclick="retryShowsShippingAddressMember(this, { phone: 'PHONE ERROR' });"> + The payment sheet shows "PHONE ERROR" for the shipping address' phone. + </button> + </li> + <li> + <button onclick="retryShowsShippingAddressMember(this, { postalCode: 'POSTALCODE ERROR' });"> + The payment sheet shows "POSTALCODE ERROR" for the shipping address' postal code. + </button> + </li> + <li> + <button onclick="retryShowsShippingAddressMember(this, { recipient: 'RECIPIENT ERROR' });"> + The payment sheet shows "RECIPIENT ERROR" for the shipping address' recipient. + </button> + </li> + <li> + <button onclick="retryShowsShippingAddressMember(this, { region: 'REGION ERROR' });"> + The payment sheet shows "REGION ERROR" for the shipping address' region. + </button> + </li> + <li> + <button onclick="retryShowsShippingAddressMember(this, { regionCode: 'REGIONCODE ERROR' });"> + The payment sheet shows "REGIONCODE ERROR" for the shipping address' region code. + </button> + </li> + <li> + <button onclick="retryShowsShippingAddressMember(this, { sortingCode: 'SORTINGCODE ERROR' });"> + The payment sheet shows "SORTINGCODE ERROR" for the shipping address' sorting code. + </button> + </li> + <li> + <button onclick="done();"> + Done! + </button> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">owners</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/algorithms-manual.https.html b/testing/web-platform/tests/payment-request/algorithms-manual.https.html new file mode 100644 index 0000000000..b90c312aba --- /dev/null +++ b/testing/web-platform/tests/payment-request/algorithms-manual.https.html @@ -0,0 +1,176 @@ +<!doctype html> +<meta charset="utf8"> +<link rel="help" href="https://w3c.github.io/payment-request/#algorithms"> +<title> + Payment Request algorithms +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ + explicit_done: true, + explicit_timeout: true, +}); +const methods = [ + { + supportedMethods: "basic-card", + }, + { + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + }, + } +]; +const shippingOptions = { + shippingOptions: [ + { + id: "fail", + label: "Option 1", + amount: { + currency: "USD", + value: "5.00", + }, + selected: true, + }, + { + id: "pass", + label: "Option 2", + amount: { + currency: "USD", + value: "5.00", + }, + }, + ], +}; + +const detailsNoShippingOptions = { + total: { + label: "Total due", + amount: { + currency: "USD", + value: "1.0", + }, + }, +}; + +const detailsWithShippingOptions = Object.assign( + { + total: { + label: "Total due", + amount: { + currency: "USD", + value: "1.0", + }, + }, + }, + shippingOptions +); + +const options = { + requestShipping: true, +}; + +function testFireEvent(button, details, eventName, expectRequestProps) { + button.disabled = true; + promise_test(async t => { + new PaymentRequest(methods, detailsNoShippingOptions, options); + const request = new PaymentRequest(methods, details, options); + const handlerPromise = new Promise(resolve => { + request[`on${eventName}`] = event => { + // "prevent immediate propagation" flag is set. + // This listener below won't fire! + event.updateWith(details); + resolve(event); + }; + }); + // This listener should never fire because the + // the event handler caused "prevent immediate propagation" to be set. + request.addEventListener( + eventName, + t.unreached_func("Second event listener should never fire") + ); + const response = await request.show(); + const event = await handlerPromise; + assert_true( + event instanceof window.PaymentRequestUpdateEvent, + "Expected instances of PaymentRequestUpdateEvent" + ); + await response.complete("success"); + }, button.textContent.trim()); +} + +async function runAbortTest(button) { + button.disabled = true; + const { textContent: testName } = button; + promise_test(async t => { + const request = new PaymentRequest(methods, detailsNoShippingOptions); + // Await the user to abort + await promise_rejects_dom(t, "AbortError", request.show()); + // [[state]] is now closed + await promise_rejects_dom(t, "InvalidStateError", request.show()); + }, testName.trim()); +} +</script> +<h2> + Tests for "algorithms" section +</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<section> + <h3 id="abort-algo"> + User aborts the payment request algorithm + </h3> + <link rel="help" href="https://w3c.github.io/payment-request/#user-aborts-the-payment-request-algorithm"> + <p> + When presented with the payment sheet, abort the payment request (e.g., by hitting the esc key or pressing a UA provided button). + </p> + <ol> + <li> + <button onclick="runAbortTest(this);"> + If the user aborts, the UA must run the user aborts the payment request algorithm. + </button> + </li> + </ol> +</section> + +<section> + <h3 id="shipping-address-changed-algo">Shipping address changed algorithm</h3> + <link rel="help" href="https://www.w3.org/TR/payment-request/#shipping-address-changed-algorithm"> + <p> + When prompted, please change or enter a new shipping address and then select Pay. + </p> + <ol> + <li> + <button onclick="testFireEvent(this, detailsWithShippingOptions, 'shippingaddresschange', {});"> + The shipping address changed algorithm runs when the user provides a new shipping address. + </button> + </li> + </ol> +</section> + +<section> + <h3 id="shipping-option-changed-algo">Shipping option changed algorithm</h3> + <link rel="help" href="https://w3c.github.io/payment-request/#shipping-option-changed-algorithm"> + <p> + Finally, when prompted, please select "shipping option 2" and then select Pay. + </p> + <ol> + <li> + <button onclick="testFireEvent(this, detailsWithShippingOptions, 'shippingoptionchange', {}, 'pass'); done();"> + The shipping option changed algorithm runs when the user chooses a new shipping option. + </button> + </li> + </ol> +</section> + +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/billing-address-changed-manual.https.html b/testing/web-platform/tests/payment-request/billing-address-changed-manual.https.html new file mode 100644 index 0000000000..d03f761518 --- /dev/null +++ b/testing/web-platform/tests/payment-request/billing-address-changed-manual.https.html @@ -0,0 +1,115 @@ +<!DOCTYPE html> <meta charset="utf-8" /> +<title>Test for requesting billing address</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + setup({ + explicit_done: true, + explicit_timeout: true, + }); + + const methods = [ + { supportedMethods: "basic-card" }, + { + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + }, + }, + ]; + + const details = { + total: { + label: "label", + amount: { currency: "USD", value: "5.00" }, + }, + }; + test(() => { + assert_true( + "onpaymentmethodchange" in PaymentRequest.prototype, + "The paymentmethodchange is not supported" + ); + }, "onpaymentmethodchange is in prototype"); + + function dontRequestBillingAddress() { + promise_test(async t => { + const request = new PaymentRequest(methods, details, {}); + const showPromise = request.show(); + + // Let's check the method data from event. + const { methodDetails } = await new Promise(resolve => + request.addEventListener("paymentmethodchange", resolve) + ); + + assert_true("billingAddress" in methodDetails); + assert_equals( + methodDetails.billingAddress, + null, + "Expected methodDetails.billingAddress to be null" + ); + await request.abort(); + }); + } + + function requestBillingAddress() { + promise_test(async t => { + const request = new PaymentRequest(methods, details, { + requestBillingAddress: true, + }); + const showPromise = request.show(); + + // Let's check the method data from event. + const { methodDetails } = await new Promise(resolve => + request.addEventListener("paymentmethodchange", resolve) + ); + + assert_true("billingAddress" in methodDetails); + + const { billingAddress } = methodDetails; + assert_true( + billingAddress instanceof ContactAddress, + "Expected instance of ContactAddress" + ); + await request.abort(); + }); + } +</script> + +<h2>Request billing address</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the + page. Each button will bring up the Payment Request UI window. +</p> +<p> + When the payment sheet is presented, select a payment method (e.g., a credit + card). +</p> +<ol> + <li> + <button onclick="dontRequestBillingAddress()"> + When no billing address is requested, + `PaymentMethodChangeEvent.methodDetails.billingAddress` is null. + </button> + </li> + <li> + <button onclick="requestBillingAddress()"> + When billing address is + requested,`PaymentMethodChangeEvent.methodDetails.billingAddress` is a + `ContactAddress`. + </button> + </li> + <li><button onclick="done()">Done!</button></li> +</ol> +<small> + If you find a buggy test, please + <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> and + tag one of the + <a + href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml" + >suggested reviewers</a + >. +</small> diff --git a/testing/web-platform/tests/payment-request/change-shipping-option-manual.https.html b/testing/web-platform/tests/payment-request/change-shipping-option-manual.https.html new file mode 100644 index 0000000000..438001804a --- /dev/null +++ b/testing/web-platform/tests/payment-request/change-shipping-option-manual.https.html @@ -0,0 +1,104 @@ +<!DOCTYPE html> +<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). --> +<meta charset="utf-8"> +<title>Test for PaymentRequest shippingOption attribute</title> +<link rel="help" href="https://w3c.github.io/payment-request/#shippingoption-attribute"> +<link rel="help" href="https://w3c.github.io/payment-request/#onshippingoptionchange-attribute"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ explicit_done: true, explicit_timeout: true }); +const validMethod = Object.freeze({ supportedMethods: "basic-card" }); +const applePayMethod = { + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + }, +}; +const validMethods = Object.freeze([validMethod, applePayMethod]); +const validAmount = Object.freeze({ currency: "USD", value: "5.00" }); +const validTotal = Object.freeze({ + label: "label", + amount: validAmount, +}); +const validDetails = Object.freeze({ total: validTotal }); + +const validShippingOption1 = Object.freeze({ + id: "valid-1", + label: "PICK ME!", + amount: validAmount, + selected: false, +}); + +const validShippingOption2 = Object.freeze({ + id: "initially-selected", + label: "Valid shipping option 2", + amount: validAmount, + selected: true, +}); + +const requestShipping = Object.freeze({ + requestShipping: true, +}); + +function testShippingOptionChanged() { + promise_test(async t => { + const detailsWithShippingOptions = Object.assign({}, validDetails, { + shippingOptions: [validShippingOption1, validShippingOption2], + }); + const request = new PaymentRequest( + validMethods, + detailsWithShippingOptions, + requestShipping + ); + assert_equals( + request.shippingOption, + "initially-selected", + "Must be 'initially-selected', as the selected member is true" + ); + const listenerPromise = new Promise(resolve => { + request.addEventListener("shippingoptionchange", () => { + resolve(request.shippingOption); + }); + }); + const handlerPromise = new Promise(resolve => { + request.onshippingoptionchange = () => { + resolve(request.shippingOption); + }; + }); + request.show().catch(err => err); + + const results = await Promise.all([listenerPromise, handlerPromise]); + assert_true( + results.every(result => result === "valid-1"), + "Expected valid-1 as the shippingOption" + ); + await request.abort(); + }); + done(); +} +</script> + +<h2>PaymentRequest shippingOption attribute</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + When the payment sheet is presented, select "PICK ME!" as the shipping option. +</p> +<ol> + <li> + <button onclick="testShippingOptionChanged()"> + When the shipping option is manually changed, request.shippingOption represents the user's choice. + </button> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/change-shipping-option-select-last-manual.https.html b/testing/web-platform/tests/payment-request/change-shipping-option-select-last-manual.https.html new file mode 100644 index 0000000000..4ad31d6531 --- /dev/null +++ b/testing/web-platform/tests/payment-request/change-shipping-option-select-last-manual.https.html @@ -0,0 +1,101 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Test for PaymentDetailsBase's shippingOptions member</title> +<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentdetailsbase-shippingoptions"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ explicit_done: true, explicit_timeout: true }); +const validMethods = Object.freeze([ + { supportedMethods: "basic-card" }, + { + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + }, + }, +]); +const validAmount = Object.freeze({ currency: "USD", value: "5.00" }); +const validTotal = Object.freeze({ + label: "label", + amount: validAmount, +}); +const validDetails = Object.freeze({ total: validTotal }); + +const validShippingOption1 = Object.freeze({ + id: "fail-if-selected-1", + label: "FAIL if selected 1", + amount: validAmount, + selected: true, +}); + +const validShippingOption2 = Object.freeze({ + id: "fail-if-selected-2", + label: "FAIL if selected 2", + amount: validAmount, + selected: false, +}); + +const validShippingOption3 = Object.freeze({ + id: "pass-if-selected", + label: "THIS MUST BE AUTOMATICALLY SELECTED", + amount: validAmount, + selected: true, +}); + +function testShippingOptionChanged(button) { + button.disabled = true; + promise_test(async t => { + const detailsWithShippingOptions = { + ...validDetails, + shippingOptions: [ + validShippingOption1, + validShippingOption2, + validShippingOption3, + ], + }; + const request = new PaymentRequest( + validMethods, + detailsWithShippingOptions, + { requestShipping: true } + ); + assert_equals( + request.shippingOption, + "pass-if-selected", + "Must be 'pass-if-selected', as the selected member is true" + ); + request.onshippingoptionchange = () => { + assert_unreached("onshippingoptionchange fired unexpectedly"); + }; + const response = await request.show(); + assert_equals(response.shippingOption, "pass-if-selected"); + response.complete(); + }, button.textContent.trim()); + done(); +} +</script> + +<h2>PaymentRequest shippingOption attribute</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + When the payment sheet is presented, hit pay. +</p> +<ol> + <li> + <button onclick="testShippingOptionChanged(this)"> + When default shipping option is pre-selected, must not fire onshippingoptionchange + and PaymentResponse must reflect the pre-selected option. + </button> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/dynamically-change-shipping-options-manual.https.html b/testing/web-platform/tests/payment-request/dynamically-change-shipping-options-manual.https.html new file mode 100644 index 0000000000..0e6670a1b8 --- /dev/null +++ b/testing/web-platform/tests/payment-request/dynamically-change-shipping-options-manual.https.html @@ -0,0 +1,142 @@ +<!DOCTYPE html> +<meta charset="utf-8" /> +<title>Test for PaymentRequest shippingOption dynamic updating</title> +<link + rel="help" + href="https://w3c.github.io/payment-request/#shippingoption-attribute" +/> +<link + rel="help" + href="https://w3c.github.io/payment-request/#onshippingoptionchange-attribute" +/> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + setup({ explicit_done: true, explicit_timeout: true }); + const validMethod = Object.freeze({ supportedMethods: "basic-card" }); + const applePayMethod = { + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + }, + }; + const validMethods = Object.freeze([validMethod, applePayMethod]); + const validAmount = Object.freeze({ currency: "USD", value: "5.00" }); + const validTotal = Object.freeze({ + label: "label", + amount: validAmount, + }); + const validDetails = Object.freeze({ total: validTotal }); + + const initialValidShippingOption = Object.freeze({ + id: "default-method", + label: "Default shipping method", + amount: validAmount, + selected: true, + }); + + const validDynamicShippingOption = Object.freeze({ + id: "dynamically-added-id", + label: "Dynamically added shipping option", + amount: validAmount, + selected: false, + }); + + const requestShipping = Object.freeze({ + requestShipping: true, + }); + + function testShippingOptionChanged() { + promise_test(async (t) => { + const detailsWithShippingOptions = { + ...validDetails, + shippingOptions: [initialValidShippingOption], + }; + const request = new PaymentRequest( + validMethods, + detailsWithShippingOptions, + requestShipping + ); + const shippingAddressChangeListener = new Promise((resolve) => { + request.addEventListener( + "shippingaddresschange", + (ev) => { + // resolve(request.shippingOption); + ev.updateWith({ + shippingOptions: [ + initialValidShippingOption, + validDynamicShippingOption, + ], + }); + resolve(); + }, + { once: true } + ); + }); + const handlerPromise = new Promise((resolve) => { + request.onshippingoptionchange = () => { + resolve(request.shippingOption); + }; + }); + request.show().catch((err) => err); + + const results = await Promise.all([ + shippingAddressChangeListener, + handlerPromise, + ]); + assert_true( + results[1] === "dynamically-added-id", + "Expected dynamically-added-id as the shippingOption" + ); + await request.abort(); + }); + } +</script> + +<h2>PaymentRequest shippingOption attribute</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the + page. Each button (except the 'Done' button) will bring up the Payment Request + UI window. +</p> +<ol> + <li> + When the payment sheet is presented, view options for Shipping Method. There + should only be one: "Default shipping method" + </li> + <li> + Change your Shipping Address - either update your existing one by changing + something (name, address, etc), or select a different Shipping Address, or + add a new Shipping Address and select it. + </li> + <li> + Go back to Shipping Method, and there is now an option called "Dynamically + added shipping option". Select it + </li> + <li> + Click on the 'Done' button + </li> +</ol> +<ul> + <li> + <button onclick="testShippingOptionChanged()"> + When the address is changed, shipping methods can be updated + </button> + </li> + <li> + <button onclick="done()">Done</button> + </li> +</ul> +<small> + If you find a buggy test, please + <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> and + tag one of the + <a + href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml" + >suggested reviewers</a + >. +</small> diff --git a/testing/web-platform/tests/payment-request/historical.https.html b/testing/web-platform/tests/payment-request/historical.https.html index aa183a58cd..3e881d0122 100644 --- a/testing/web-platform/tests/payment-request/historical.https.html +++ b/testing/web-platform/tests/payment-request/historical.https.html @@ -10,7 +10,7 @@ ["paymentRequestID", "PaymentResponse"], // https://github.com/w3c/browser-payment-api/pull/258 - ["careOf", "PaymentAddress"], + ["careOf", "ContactAddress"], // https://github.com/w3c/browser-payment-api/pull/219 ["totalAmount", "PaymentResponse"], @@ -20,7 +20,7 @@ ["paymentRequestId", "PaymentResponse"], // https://github.com/w3c/payment-request/pull/765 - ["languageCode", "PaymentAddress"], + ["languageCode", "ContactAddress"], //https://github.com/whatwg/html/pull/5915 ["allowPaymentRequest", "HTMLIFrameElement"], diff --git a/testing/web-platform/tests/payment-request/payment-request-constructor-thcrash.https.html b/testing/web-platform/tests/payment-request/payment-request-constructor-thcrash.https.html new file mode 100644 index 0000000000..b600307085 --- /dev/null +++ b/testing/web-platform/tests/payment-request/payment-request-constructor-thcrash.https.html @@ -0,0 +1,254 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<meta name="timeout" content="long"> +<title>Crash tests PaymentRequest Constructor</title> +<link rel="help" href="https://w3c.github.io/browser-payment-api/#constructor"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + +"use strict"; +const ABUSIVE_AMOUNT = 100000; + +const applePay = { + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + } +}; + +const basicCard = Object.freeze({ + supportedMethods: "basic-card", +}); + +const defaultAmount = Object.freeze({ + currency: "USD", + value: "1.00", +}); + +const evilAmount = Object.freeze({ + currency: "USD", + value: "1".repeat(ABUSIVE_AMOUNT), +}); + +const defaultMethods = Object.freeze([basicCard, applePay]); + +const defaultTotal = Object.freeze({ + label: "label", + amount: defaultAmount, +}); + +const evilTotal = Object.freeze({ + label: "a".repeat(ABUSIVE_AMOUNT), + amount: evilAmount, +}); + +const defaultDetails = Object.freeze({ + total: defaultTotal, + get id() { + return Math.random(); + }, +}); + +const defaultPaymentItem = Object.freeze({ + label: "label", + amount: defaultAmount, +}); + +const defaultShippingOption = { + get id() { + return "shipping option " + Math.random(); + }, + amount: defaultAmount, + label: "shipping option label", +}; + +// First argument is sequence<PaymentMethodData> methodData +test(() => { + let evilMethods = [Object.assign({}, basicCard)]; + // smoke test + try { + new PaymentRequest(evilMethods, defaultDetails); + } catch (err) { + assert_unreached("failed smoke test: " + err.stack); + } + // Now, let's add an abusive amount of methods. + while (evilMethods.length < ABUSIVE_AMOUNT) { + evilMethods.push({supportedMethods: "evil-method" + evilMethods.length}); + } + try { + new PaymentRequest(evilMethods, defaultDetails); + } catch (err) { + assert_equals(err.name, "TypeError", "must be a TypeError"); + } +}, "Don't crash if there is an abusive number of payment methods in the methodData sequence"); + +// PaymentMethodData.supportedMethods +test(() => { + const supportedMethods = "basic-card"; + // Smoke test + try { + new PaymentRequest([{ supportedMethods }], defaultDetails); + } catch (err) { + assert_unreached("failed smoke test: " + err.stack); + } + // Now, we make supportedMethods super large + const evilMethodData = [ + { + supportedMethods: supportedMethods.repeat(ABUSIVE_AMOUNT), + }, + ]; + try { + new PaymentRequest(evilMethodData, defaultDetails); + } catch (err) { + assert_equals(err.name, "TypeError", "must be a TypeError"); + } +}, "Don't crash if PaymentMethodData.supportedMethods is an abusive length"); + +// PaymentDetailsInit.id +test(() => { + const id = "abc"; + // Smoke Test + try { + new PaymentRequest( + defaultMethods, + Object.assign({}, defaultDetails, { id }) + ); + } catch (err) { + assert_unreached("failed smoke test: " + err.stack); + } + // Now, we make the id super large; + const evilDetails = Object.assign({}, defaultDetails, { + id: id.repeat(ABUSIVE_AMOUNT), + }); + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_equals(err.name, "TypeError", "must be a TypeError"); + } +}, "Don't crash if the request id has an abusive length"); + +// PaymentDetailsInit.total.label +test(() => { + const evilDetails = Object.assign({}, defaultDetails); + // Smoke Test + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_unreached("failed smoke test: " + err.stack); + } + // Now, we make the label super large; + evilDetails.total = { + label: "l".repeat(ABUSIVE_AMOUNT), + amount: defaultAmount, + }; + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_equals(err.name, "TypeError", "must be a TypeError"); + } +}, "Don't crash if PaymentDetailsInit.total.label is an abusive length"); + +test(() => { + const evilDetails = Object.assign({}, defaultDetails); + // Smoke Test + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_unreached("failed smoke test: " + err.stack); + } + // Now, we can use evilAmount + evilDetails.total = evilAmount; + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_equals(err.name, "TypeError", "must be a TypeError"); + } +}, "Don't crash if total.amount.value is an abusive length"); + +for (const [prop, defaultValue] of [ + ["displayItems", defaultPaymentItem], + ["shippingOptions", defaultShippingOption], +]) { + test(() => { + const evilDetails = Object.assign({}, defaultDetails); + evilDetails[prop] = [defaultValue]; + // Smoke Test + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_unreached("failed smoke test: " + err.stack); + } + while (evilDetails[prop].length < ABUSIVE_AMOUNT) { + evilDetails[prop] = evilDetails[prop].concat(evilDetails[prop]); + } + // Now, construct with evil items! + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_equals(err.name, "TypeError", "must be a TypeError"); + } + }, `Don't crash if details.${prop} has an abusive number of items`); +} + +test(() => { + const evilDetails = Object.assign({}, defaultDetails); + const evilShippingOption = Object.assign({}, defaultShippingOption); + evilDetails.shippingOptions = [evilShippingOption]; + // Smoke Test + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_unreached("failed smoke test: " + err.stack); + } + // Now, we make the label super large; + evilShippingOption.label = "l".repeat(ABUSIVE_AMOUNT); + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_equals(err.name, "TypeError", "must be a TypeError"); + } +}, "Don't crash if PaymentShippingOptions.label is an abusive length"); + +test(() => { + const evilDetails = Object.assign({}, defaultDetails); + const evilShippingOption = Object.assign({}, defaultShippingOption); + evilDetails.shippingOptions = [evilShippingOption]; + // Smoke Test + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_unreached("failed smoke test: " + err.stack); + } + // Now, we make use of evilAmount; + evilShippingOption.amount = evilAmount; + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_equals(err.name, "TypeError", "must be a TypeError"); + } +}, "Don't crash if the PaymentShippingOptions.amount.value is an abusive length"); + +test(() => { + const evilDetails = Object.assign({}, defaultDetails); + const evilDisplayItem = Object.assign({}, defaultPaymentItem); + evilDetails.displayItems = [evilDisplayItem]; + // Smoke Test + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_unreached("failed smoke test: " + err.stack); + } + // Now, we make the label super large; + evilDisplayItem.label = "l".repeat(ABUSIVE_AMOUNT); + try { + new PaymentRequest(defaultMethods, evilDetails); + } catch (err) { + assert_equals(err.name, "TypeError", "must be a TypeError"); + } +}, "Don't crash if PaymentItem.label is an abusive length"); +</script> diff --git a/testing/web-platform/tests/payment-request/payment-request-constructor.https.sub.html b/testing/web-platform/tests/payment-request/payment-request-constructor.https.sub.html index c1ecc22583..9b0ad06454 100644 --- a/testing/web-platform/tests/payment-request/payment-request-constructor.https.sub.html +++ b/testing/web-platform/tests/payment-request/payment-request-constructor.https.sub.html @@ -240,7 +240,7 @@ test(() => { test(() => { smokeTest(); - for (const prop in ["displayItems", "modifiers"]) { + for (const prop in ["displayItems", "shippingOptions", "modifiers"]) { try { const details = Object.assign({}, defaultDetails, { [prop]: [] }); new PaymentRequest(defaultMethods, details); @@ -361,6 +361,186 @@ test(() => { } }, "it handles high precision currency values without throwing"); +// Process shipping options: + +const defaultShippingOption = Object.freeze({ + id: "default", + label: "", + amount: defaultAmount, + selected: false, +}); +const defaultShippingOptions = Object.freeze([ + Object.assign({}, defaultShippingOption), +]); + +test(() => { + smokeTest(); + for (const amount of invalidAmounts) { + const invalidAmount = Object.assign({}, defaultAmount, { + value: amount, + }); + const invalidShippingOption = Object.assign({}, defaultShippingOption, { + amount: invalidAmount, + }); + const details = Object.assign({}, defaultDetails, { + shippingOptions: [invalidShippingOption], + }); + assert_throws_js( + TypeError, + () => { + new PaymentRequest(defaultMethods, details, { requestShipping: true }); + }, + `Expected TypeError for option.amount.value: "${amount}"` + ); + } +}, `For each option in details.shippingOptions: if option.amount.value is not a valid decimal monetary value, then throw a TypeError`); + +test(() => { + smokeTest(); + const shippingOptions = [defaultShippingOption]; + const details = Object.assign({}, defaultDetails, { shippingOptions }); + const request = new PaymentRequest(defaultMethods, details); + assert_equals( + request.shippingOption, + null, + "shippingOption must be null, as requestShipping is missing" + ); + // defaultDetails lacks shipping options + const request2 = new PaymentRequest(defaultMethods, defaultDetails, { + requestShipping: true, + }); + assert_equals( + request2.shippingOption, + null, + `request2.shippingOption must be null` + ); +}, "If there is no selected shipping option, then PaymentRequest.shippingOption remains null"); + +test(() => { + smokeTest(); + const selectedOption = Object.assign({}, defaultShippingOption, { + selected: true, + id: "the-id", + }); + const shippingOptions = [selectedOption]; + const details = Object.assign({}, defaultDetails, { shippingOptions }); + const requestNoShippingRequested1 = new PaymentRequest( + defaultMethods, + details + ); + assert_equals( + requestNoShippingRequested1.shippingOption, + null, + "Must be null when no shipping is requested (defaults to false)" + ); + const requestNoShippingRequested2 = new PaymentRequest( + defaultMethods, + details, + { requestShipping: false } + ); + assert_equals( + requestNoShippingRequested2.shippingOption, + null, + "Must be null when requestShipping is false" + ); + const requestWithShipping = new PaymentRequest(defaultMethods, details, { + requestShipping: "truthy value", + }); + assert_equals( + requestWithShipping.shippingOption, + "the-id", + "Selected option must be 'the-id'" + ); +}, "If there is a selected shipping option, and requestShipping is set, then that option becomes synchronously selected"); + +test(() => { + smokeTest(); + const failOption1 = Object.assign({}, defaultShippingOption, { + selected: true, + id: "FAIL1", + }); + const failOption2 = Object.assign({}, defaultShippingOption, { + selected: false, + id: "FAIL2", + }); + const passOption = Object.assign({}, defaultShippingOption, { + selected: true, + id: "the-id", + }); + const shippingOptions = [failOption1, failOption2, passOption]; + const details = Object.assign({}, defaultDetails, { shippingOptions }); + const requestNoShipping = new PaymentRequest(defaultMethods, details, { + requestShipping: false, + }); + assert_equals( + requestNoShipping.shippingOption, + null, + "shippingOption must be null, as requestShipping is false" + ); + const requestWithShipping = new PaymentRequest(defaultMethods, details, { + requestShipping: true, + }); + assert_equals( + requestWithShipping.shippingOption, + "the-id", + "selected option must 'the-id" + ); +}, "If requestShipping is set, and if there is a multiple selected shipping options, only the last is selected."); + +test(() => { + smokeTest(); + const selectedOption = Object.assign({}, defaultShippingOption, { + selected: true, + }); + const unselectedOption = Object.assign({}, defaultShippingOption, { + selected: false, + }); + const shippingOptions = [selectedOption, unselectedOption]; + const details = Object.assign({}, defaultDetails, { shippingOptions }); + const requestNoShipping = new PaymentRequest(defaultMethods, details); + assert_equals( + requestNoShipping.shippingOption, + null, + "shippingOption must be null, because requestShipping is false" + ); + assert_throws_js( + TypeError, + () => { + new PaymentRequest(defaultMethods, details, { requestShipping: true }); + }, + "Expected to throw a TypeError because duplicate IDs" + ); +}, "If there are any duplicate shipping option ids, and shipping is requested, then throw a TypeError"); + +test(() => { + smokeTest(); + const dupShipping1 = Object.assign({}, defaultShippingOption, { + selected: true, + id: "DUPLICATE", + label: "Fail 1", + }); + const dupShipping2 = Object.assign({}, defaultShippingOption, { + selected: false, + id: "DUPLICATE", + label: "Fail 2", + }); + const shippingOptions = [dupShipping1, defaultShippingOption, dupShipping2]; + const details = Object.assign({}, defaultDetails, { shippingOptions }); + const requestNoShipping = new PaymentRequest(defaultMethods, details); + assert_equals( + requestNoShipping.shippingOption, + null, + "shippingOption must be null, because requestShipping is false" + ); + assert_throws_js( + TypeError, + () => { + new PaymentRequest(defaultMethods, details, { requestShipping: true }); + }, + "Expected to throw a TypeError because duplicate IDs" + ); +}, "Throw when there are duplicate shippingOption ids, even if other values are different"); + // Process payment details modifiers: test(() => { smokeTest(); @@ -474,4 +654,44 @@ test(() => { }); }, "Rethrow any exceptions of JSON-serializing modifier.data"); +//Setting ShippingType attribute during construction +test(() => { + smokeTest(); + assert_throws_js(TypeError, () => { + new PaymentRequest(defaultMethods, defaultDetails, { + shippingType: "invalid", + }); + }); +}, "Shipping type should be valid"); + +test(() => { + smokeTest(); + const request = new PaymentRequest(defaultMethods, defaultDetails, {}); + assert_equals(request.shippingAddress, null, "must be null"); +}, "PaymentRequest.shippingAddress must initially be null"); + +test(() => { + smokeTest(); + const request1 = new PaymentRequest(defaultMethods, defaultDetails, {}); + assert_equals(request1.shippingType, null, "must be null"); + const request2 = new PaymentRequest(defaultMethods, defaultDetails, { + requestShipping: false, + }); + assert_equals(request2.shippingType, null, "must be null"); +}, "If options.requestShipping is not set, then request.shippingType attribute is null."); + +test(() => { + smokeTest(); + // option.shippingType defaults to 'shipping' + const request1 = new PaymentRequest(defaultMethods, defaultDetails, { + requestShipping: true, + }); + assert_equals(request1.shippingType, "shipping", "must be shipping"); + const request2 = new PaymentRequest(defaultMethods, defaultDetails, { + requestShipping: true, + shippingType: "delivery", + }); + assert_equals(request2.shippingType, "delivery", "must be delivery"); +}, "If options.requestShipping is true, request.shippingType will be options.shippingType."); + </script> diff --git a/testing/web-platform/tests/payment-request/payment-request-ctor-currency-code-checks.https.sub.html b/testing/web-platform/tests/payment-request/payment-request-ctor-currency-code-checks.https.sub.html index c608608c7e..b4ca2a0c40 100644 --- a/testing/web-platform/tests/payment-request/payment-request-ctor-currency-code-checks.https.sub.html +++ b/testing/web-platform/tests/payment-request/payment-request-ctor-currency-code-checks.https.sub.html @@ -178,6 +178,63 @@ test(() => { } }, "Check and canonicalize invalid details.displayItems amount and rethrow RangeError."); +// Process shipping options: +test(() => { + assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); + const shippingOptions = []; + for (const validCurrency of wellFormedCurrencyCodes) { + const shippingOption = { + id: `test` + Math.random(), + label: "shipping option", + amount: { currency: validCurrency, value: "5.00" }, + selected: !shippingOptions.length, + }; + const details = { + total: defaultTotal, + shippingOptions: [shippingOption], + }; + try { + new PaymentRequest(defaultMethods, details, { requestShipping: true }); + } catch (err) { + assert_unreached( + `Unexpected exception with valid shippingOption currency code "${validCurrency}": ${err.message}.` + ); + } + shippingOptions.push(shippingOption); + } + try { + const details = Object.assign({}, defaultDetails, { shippingOptions }); + new PaymentRequest(defaultMethods, details, { requestShipping: true }); + } catch (err) { + assert_unreached( + `Unexpected error with multiple valid shppingOptions: ${err.message}.` + ); + } +}, "Check and canonicalize valid details.shippingOptions amount."); + +test(() => { + assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); + for (const invalidCurrency of invalidCurrencyCodes) { + const shippingOption = { + id: "test", + label: "shipping option", + amount: { currency: invalidCurrency, value: "5.00" }, + selected: true, + }; + const details = { + total: defaultTotal, + shippingOptions: [shippingOption], + }; + assert_throws_js( + RANGE_ERROR, + () => { + new PaymentRequest(defaultMethods, details, { requestShipping: true }); + }, + `Expected RangeError with invalid shippingOption currency code "${invalidCurrency}".` + ); + } +}, "Check and canonicalize invalid details.shippingOptions amount and rethrow RangeError."); + // Process payment details modifiers: test(() => { assert_throws_js(RANGE_ERROR, smokeTest, "Expected smoke test to throw."); diff --git a/testing/web-platform/tests/payment-request/payment-request-onshippingaddresschange-attribute.https.html b/testing/web-platform/tests/payment-request/payment-request-onshippingaddresschange-attribute.https.html new file mode 100644 index 0000000000..5b2538992f --- /dev/null +++ b/testing/web-platform/tests/payment-request/payment-request-onshippingaddresschange-attribute.https.html @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). --> +<meta charset="utf-8"> +<title>Test for onshippingaddresschange attribute</title> +<link rel="help" href="https://w3c.github.io/browser-payment-api/#onshippingaddresschange-attribute"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +"use strict"; +const applePay = Object.freeze({ + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + } +}); +const basicCard = Object.freeze({ supportedMethods: "basic-card" }); +const defaultMethods = Object.freeze([basicCard, applePay]); +const defaultDetails = Object.freeze({ + total: { + label: "Total", + amount: { + currency: "USD", + value: "1.00", + }, + }, +}); + +test(() => { + const request = new PaymentRequest(defaultMethods, defaultDetails); + assert_idl_attribute(request, "onshippingaddresschange"); +}, "Must have a onshippingaddresschange IDL attribute"); + +test(() => { + const request = new PaymentRequest(defaultMethods, defaultDetails); + const ev = new Event("shippingaddresschange"); + let didHandle = false; + request.onshippingaddresschange = evt => { + assert_equals(ev, evt, "must be same event"); + didHandle = true; + }; + request.dispatchEvent(ev); + assert_true(didHandle, "event did not fire"); +}, `onshippingaddresschange attribute is a generic handler for "shippingaddresschange"`); + +test(() => { + const request = new PaymentRequest(defaultMethods, defaultDetails); + const ev = new PaymentRequestUpdateEvent("shippingaddresschange"); + let didHandle = false; + request.onshippingaddresschange = evt => { + assert_equals(ev, evt, "must be same event"); + didHandle = true; + }; + request.dispatchEvent(ev); + assert_true(didHandle, "event did not fire"); +}, `onshippingaddresschange attribute is a handler for PaymentRequestUpdateEvent`); + +test(() => { + const request = new PaymentRequest(defaultMethods, defaultDetails); + const ev = new PaymentRequestUpdateEvent("shippingaddresschange"); + let didHandle = false; + let didListen = false; + request.onshippingaddresschange = evt => { + assert_equals(ev, evt, "must be same event"); + didHandle = true; + }; + request.addEventListener("shippingaddresschange", evt => { + assert_equals(ev, evt, "must be same event"); + didListen = true; + }); + request.dispatchEvent(ev); + assert_true(didHandle, "onshippingaddresschange did not receive the event"); + assert_true(didListen, "addEventListener did not receive the event"); +}, `onshippingaddresschange attribute and listeners both work`); +</script> diff --git a/testing/web-platform/tests/payment-request/payment-request-onshippingoptionchange-attribute.https.html b/testing/web-platform/tests/payment-request/payment-request-onshippingoptionchange-attribute.https.html new file mode 100644 index 0000000000..43ea5dcce8 --- /dev/null +++ b/testing/web-platform/tests/payment-request/payment-request-onshippingoptionchange-attribute.https.html @@ -0,0 +1,79 @@ +<!DOCTYPE html> +<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). --> +<meta charset="utf-8"> +<title>Test for onshippingoptionchange attribute</title> +<link rel="help" href="https://w3c.github.io/browser-payment-api/#onshippingoptionchange-attribute"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +"use strict"; +const applePay = Object.freeze({ + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + } +}); +const basicCard = Object.freeze({ supportedMethods: "basic-card" }); +const defaultMethods = Object.freeze([basicCard, applePay]); +const defaultDetails = Object.freeze({ + total: { + label: "Total", + amount: { + currency: "USD", + value: "1.00", + }, + }, +}); + +test(() => { + const request = new PaymentRequest(defaultMethods, defaultDetails); + assert_idl_attribute(request, "onshippingoptionchange"); +}, "Must have a onshippingoptionchange IDL attribute"); + +test(() => { + const request = new PaymentRequest(defaultMethods, defaultDetails); + const ev = new Event("shippingoptionchange"); + let didHandle = false; + request.onshippingoptionchange = evt => { + assert_equals(ev, evt, "must be same event"); + didHandle = true; + }; + request.dispatchEvent(ev); + assert_true(didHandle, "event did not fire"); +}, `onshippingoptionchange attribute is a generic handler for "shippingoptionchange"`); + +test(() => { + const request = new PaymentRequest(defaultMethods, defaultDetails); + const ev = new PaymentRequestUpdateEvent("shippingoptionchange"); + let didHandle = false; + request.onshippingoptionchange = evt => { + assert_equals(ev, evt, "must be same event"); + didHandle = true; + }; + request.dispatchEvent(ev); + assert_true(didHandle, "event did not fire"); +}, `onshippingoptionchange attribute is a handler for PaymentRequestUpdateEvent`); + +test(() => { + const request = new PaymentRequest(defaultMethods, defaultDetails); + const ev = new PaymentRequestUpdateEvent("shippingoptionchange"); + let didHandle = false; + let didListen = false; + request.onshippingoptionchange = evt => { + assert_equals(ev, evt, "must be same event"); + didHandle = true; + }; + request.addEventListener("shippingoptionchange", evt => { + assert_equals(ev, evt, "must be same event"); + didListen = true; + }); + request.dispatchEvent(ev); + assert_true(didHandle, "onshippingoptionchange did not receive the event"); + assert_true(didListen, "addEventListener did not receive the event"); +}, `onshippingoptionchange attribute and listeners both work`); + +</script> diff --git a/testing/web-platform/tests/payment-request/payment-request-shippingAddress-attribute.https.html b/testing/web-platform/tests/payment-request/payment-request-shippingAddress-attribute.https.html new file mode 100644 index 0000000000..08918356b6 --- /dev/null +++ b/testing/web-platform/tests/payment-request/payment-request-shippingAddress-attribute.https.html @@ -0,0 +1,28 @@ +<!DOCTYPE html> +<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). --> +<meta charset="utf-8"> +<title>Test for PaymentRequest shippingAddress attribute</title> +<link rel="help" href="https://w3c.github.io/payment-request/#shippingaddress-attribute"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +const validMethod = Object.freeze({ supportedMethods: "foo" }); +const validMethods = Object.freeze([validMethod]); +const validAmount = Object.freeze({ currency: "USD", value: "5.00" }); +const validTotal = Object.freeze({ + label: "label", + amount: validAmount, +}); +const validDetails = Object.freeze({ total: validTotal }); + +test(() => { + const request = new PaymentRequest(validMethods, validDetails); + assert_idl_attribute(request, "shippingAddress"); +}, "Must have a .shippingAddress IDL attribute."); + +test(() => { + const request = new PaymentRequest(validMethods, validDetails); + assert_equals(request.shippingAddress, null, "expected null"); +}, ".shippingAddress attribute must default to null."); + +</script> diff --git a/testing/web-platform/tests/payment-request/payment-request-shippingOption-attribute.https.html b/testing/web-platform/tests/payment-request/payment-request-shippingOption-attribute.https.html new file mode 100644 index 0000000000..b5f9ea65c6 --- /dev/null +++ b/testing/web-platform/tests/payment-request/payment-request-shippingOption-attribute.https.html @@ -0,0 +1,100 @@ +<!DOCTYPE html> +<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). --> +<meta charset="utf-8"> +<title>Test for PaymentRequest shippingOption attribute</title> +<link rel="help" href="https://w3c.github.io/payment-request/#shippingoption-attribute"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +const validMethod = Object.freeze({ supportedMethods: "foo" }); +const validMethods = Object.freeze([validMethod]); +const validAmount = Object.freeze({ currency: "USD", value: "5.00" }); +const validTotal = Object.freeze({ + label: "label", + amount: validAmount, +}); +const validDetails = Object.freeze({ total: validTotal }); +const validShippingOption = Object.freeze({ + id: "valid", + label: "Valid shipping option", + amount: validAmount, + selected: false, +}); + +const requestShipping = Object.freeze({ + requestShipping: true, +}); + +test(() => { + const request = new PaymentRequest(validMethods, validDetails); + assert_idl_attribute(request, "shippingOption"); +}, "Must have a .shippingOption IDL attribute."); + +test(() => { + const request = new PaymentRequest(validMethods, validDetails); + assert_equals(request.shippingOption, null, "expected null"); +}, ".shippingOption attribute must default to null."); + +test(() => { + const detailsWithShippingOptions = Object.assign({}, validDetails, { + shippingOptions: [validShippingOption], + }); + const request = new PaymentRequest( + validMethods, + detailsWithShippingOptions, + requestShipping + ); + assert_equals(request.shippingOption, null, "expected null"); +}, "If there is a single shipping option, but selected is false, then .shippingOption must be null."); + +test(() => { + const shippingOption2 = Object.assign({}, validShippingOption, { + id: "valid2", + }); + const detailsWithShippingOptions = Object.assign({}, validDetails, { + shippingOptions: [validShippingOption, shippingOption2], + }); + const request = new PaymentRequest( + validMethods, + detailsWithShippingOptions, + requestShipping + ); + assert_equals(request.shippingOption, null, "expected null"); +}, "If there are multiple shipping options all with `selected` set to false, then .shippingOption is null."); + +test(() => { + const shippingOption2 = Object.assign({}, validShippingOption, { + id: "pass", + selected: true, + }); + const detailsWithShippingOptions = Object.assign({}, validDetails, { + shippingOptions: [shippingOption2, validShippingOption], + }); + const request = new PaymentRequest( + validMethods, + detailsWithShippingOptions, + requestShipping + ); + assert_equals(request.shippingOption, "pass", "expected 'pass'"); +}, "Given multiple shipping options, it must use the selected shipping option for .shippingOption value."); + +test(() => { + const shippingOption1 = Object.assign({}, validShippingOption, { + id: "fail", + selected: true, + }); + const shippingOption2 = Object.assign({}, validShippingOption, { + id: "pass", + selected: true, + }); + const detailsWithShippingOptions = Object.assign({}, validDetails, { + shippingOptions: [shippingOption1, shippingOption2, validShippingOption], + }); + const request = new PaymentRequest( + validMethods, + detailsWithShippingOptions, + requestShipping + ); + assert_equals(request.shippingOption, "pass", "expected 'pass'"); +}, "If there are multiple of the shipping options with selected true, then .shippingOption is the last selected shipping option in order."); +</script> diff --git a/testing/web-platform/tests/payment-request/payment-request-shippingType-attribute.https.html b/testing/web-platform/tests/payment-request/payment-request-shippingType-attribute.https.html new file mode 100644 index 0000000000..11f75b1c86 --- /dev/null +++ b/testing/web-platform/tests/payment-request/payment-request-shippingType-attribute.https.html @@ -0,0 +1,72 @@ +<!DOCTYPE html> +<!-- Copyright © 2017 Mozilla and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). --> +<meta charset="utf-8"> +<title>Test for PaymentRequest's shippingType attribute</title> +<link rel="help" href="https://w3c.github.io/payment-request/#shippingtype-attribute"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +"use strict"; +const paymentShipingTypes = Object.freeze(["delivery", "pickup", "shipping"]); +const applePay = Object.freeze({ + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + } +}); +const basicCard = Object.freeze({ supportedMethods: "basic-card" }); +const defaultMethods = Object.freeze([basicCard, applePay]); +const defaultDetails = Object.freeze({ + total: { + label: "", + amount: { + currency: "USD", + value: "1.00", + }, + }, +}); + +test(() => { + const request = new PaymentRequest(defaultMethods, defaultDetails); + assert_idl_attribute(request, "shippingType"); +}, "Must have a shippingType IDL attribute"); + +test(() => { + const request1 = new PaymentRequest(defaultMethods, defaultDetails, {}); + assert_equals(request1.shippingType, null, "must be null"); + const request2 = new PaymentRequest(defaultMethods, defaultDetails, { + requestShipping: false, + }); + assert_equals(request2.shippingType, null, "must be null"); + for (const shippingType of paymentShipingTypes) { + const request = new PaymentRequest(defaultMethods, defaultDetails, { + requestShipping: false, + shippingType, + }); + assert_equals(request.shippingType, null, "must be null"); + } +}, "If options.requestShipping is false, then request.shippingType attribute is null."); + +test(() => { + // option.shippingType defaults to 'shipping' + const defaultRequest = new PaymentRequest(defaultMethods, defaultDetails, { + requestShipping: true, + }); + assert_equals(defaultRequest.shippingType, "shipping", "must be shipping"); + for (const shippingType of paymentShipingTypes) { + const request = new PaymentRequest(defaultMethods, defaultDetails, { + requestShipping: true, + shippingType, + }); + assert_equals( + request.shippingType, + shippingType, + `must be ${shippingType}` + ); + } +}, "If options.requestShipping is true, request.shippingType will be options.shippingType."); +</script> diff --git a/testing/web-platform/tests/payment-request/payment-response/helpers.js b/testing/web-platform/tests/payment-request/payment-response/helpers.js index 1242ecb743..3e4f5cfd36 100644 --- a/testing/web-platform/tests/payment-request/payment-response/helpers.js +++ b/testing/web-platform/tests/payment-request/payment-response/helpers.js @@ -65,8 +65,30 @@ async function getPaymentRequestResponse(options, id) { label: "Total due", amount: { currency: "USD", value: "1.0" }, }, + shippingOptions: [ + { + id: "fail1", + label: "Fail option 1", + amount: { currency: "USD", value: "5.00" }, + selected: false, + }, + { + id: "pass", + label: "Pass option", + amount: { currency: "USD", value: "5.00" }, + selected: true, + }, + { + id: "fail2", + label: "Fail option 2", + amount: { currency: "USD", value: "5.00" }, + selected: false, + }, + ], }; const request = new PaymentRequest(methods, details, options); + request.onshippingaddresschange = ev => ev.updateWith(details); + request.onshippingoptionchange = ev => ev.updateWith(details); const response = await request.show(); return { request, response }; } @@ -106,5 +128,23 @@ async function runManualTest(button, options, expected = {}, id = undefined) { assert_equals(typeof response.details, "object", "Expected an object"); // Testing that this does not throw: response.toJSON(); + if (options && options.requestShipping) { + assert_equals( + response.shippingOption, + "pass", + "request.shippingOption must be 'pass'" + ); + } else { + assert_equals( + request.shippingOption, + null, + "If requestShipping is falsy, request.shippingOption must be null" + ); + assert_equals( + response.shippingOption, + null, + "request.shippingOption must be null" + ); + } }, button.textContent.trim()); } diff --git a/testing/web-platform/tests/payment-request/payment-response/retry-method-manual.https.html b/testing/web-platform/tests/payment-request/payment-response/retry-method-manual.https.html new file mode 100644 index 0000000000..a5aab49e38 --- /dev/null +++ b/testing/web-platform/tests/payment-request/payment-response/retry-method-manual.https.html @@ -0,0 +1,296 @@ +<!doctype html> +<meta charset="utf8"> +<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-retry"> +<title> + PaymentResponse.prototype.retry() method +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="helpers.js"></script> +<script> +test(() => { + assert_true( + "retry" in PaymentResponse.prototype, + "retry must be in prototype" + ); + assert_true( + PaymentResponse.prototype.retry instanceof Function, + "retry must be a function" + ); +}, "PaymentResponse.prototype must have a retry() function (smoke test)."); + +function checkCompletedCantRetry(button) { + button.disabled = true; + promise_test(async t => { + const { response } = await getPaymentRequestResponse(); + // sets response.[[complete]] to true. + await response.complete("success"); + return promise_rejects_dom( + t, + "InvalidStateError", + response.retry(), + "response.[[complete]] is true, so rejects with InvalidStateError." + ); + }, button.textContent.trim()); +} + +function repeatedCallsToRetry(button) { + button.disabled = true; + promise_test(async t => { + const { response } = await getPaymentRequestResponse(); + const retryPromise = response.retry(); + await promise_rejects_dom( + t, + "InvalidStateError", + response.retry(), + "Calling retry() again rejects with an InvalidStateError" + ); + await retryPromise; + await response.complete("success"); + }, button.textContent.trim()); +} + +function callCompleteWhileRetrying(button) { + button.disabled = true; + promise_test(async t => { + const { response } = await getPaymentRequestResponse(); + const retryPromise = response.retry(); + const completePromise1 = response.complete("success"); + const completePromise2 = response.complete("fail"); + assert_not_equals( + completePromise1, + completePromise2, + "complete() must return unique promises" + ); + await promise_rejects_dom( + t, + "InvalidStateError", + completePromise1, + "Calling complete() while retrying rejects with an InvalidStateError" + ); + await promise_rejects_dom( + t, + "InvalidStateError", + completePromise2, + "Calling complete() while retrying rejects with an InvalidStateError" + ); + assert_not_equals( + completePromise1, + completePromise2, + "complete() must return unique promises" + ); + await retryPromise; + await response.complete("success"); + }, button.textContent.trim()); +} + +function callingRequestAbortMustNotAbort(button) { + button.disabled = true; + promise_test(async t => { + const { response, request } = await getPaymentRequestResponse(); + const retryPromise = response.retry(); + await promise_rejects_dom( + t, + "InvalidStateError", + request.abort(), + "Calling request.abort() while retrying rejects with an InvalidStateError" + ); + await retryPromise; + await response.complete("success"); + }, button.textContent.trim()); +} + +function canRetryMultipleTimes(button) { + button.disabled = true; + promise_test(async t => { + const { response } = await getPaymentRequestResponse(); + assert_equals( + await response.retry(), + undefined, + "Expected undefined as the resolve value" + ); + assert_equals( + await response.retry(), + undefined, + "Expected undefined as the resolve value" + ); + await response.complete("success"); + await promise_rejects_dom( + t, + "InvalidStateError", + response.retry(), + "Calling retry() after complete() rejects with a InvalidStateError" + ); + }, button.textContent.trim()); +} + +function userCanAbortARetry(button) { + button.disabled = true; + promise_test(async t => { + const { response } = await getPaymentRequestResponse(); + await promise_rejects_dom( + t, + "AbortError", + response.retry(), + "The user aborting a retry rejects with a AbortError" + ); + await promise_rejects_dom( + t, + "InvalidStateError", + response.retry(), + "After the user aborts, response [[complete]] is true so retry() must reject with InvalidStateError" + ); + await promise_rejects_dom( + t, + "InvalidStateError", + response.complete("success"), + "After the user aborts, response [[complete]] is true, so complete() rejects with a InvalidStateError" + ); + }, button.textContent.trim()); +} + +function userIsShownErrorsFields(button) { + button.disabled = true; + promise_test(async t => { + const { response, request } = await getPaymentRequestResponse({ requestShipping: true }); + const retryPromise = response.retry({ + shippingAddress: { city: "Invalid city", addressLine: "Invalid address line" }, + }); + await retryPromise; + await response.complete("success"); + }, button.textContent.trim()); +} + +function abortTheUpdate(button) { + button.disabled = true; + promise_test(async t => { + const { response, request } = await getPaymentRequestResponse({ + requestShipping: true, + }); + const shipOptionChangePromise = new Promise(resolve => { + request.onshippingoptionchange = event => { + // causes "abort the update" to run + event.updateWith({ total: "error!" }); + resolve(); + }; + }); + const retryPromise = response.retry(); + await shipOptionChangePromise; + await promise_rejects_js( + t, + TypeError, + retryPromise, + "retry() aborts with a TypeError, because totals' value is invalid" + ); + await promise_rejects_dom( + t, + "InvalidStateError", + response.complete("success"), + "After the user aborts, response [[complete]] is true, so complete() rejects with a InvalidStateError" + ); + }, button.textContent.trim()); +} + +function callingRetryReturnsUniquePromise(button){ + button.disabled = true; + promise_test(async t => { + const { response } = await getPaymentRequestResponse(); + const retryPromise = response.retry(); + const promises = new Set([ + retryPromise, + response.retry(), + response.retry(), + ]); + assert_equals(promises.size, 3, "Must have three unique objects"); + await retryPromise; + await response.complete(); + }, button.textContent.trim()); +}; + + +</script> +<h2> + Manual Tests for PaymentResponse.retry() - Please run in order! +</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + When presented with the payment sheet, use any credit card select to "Pay" multiple times. +</p> +<ol> + <li> + <button onclick="checkCompletedCantRetry(this);"> + A completed payment request cannot be retried. + </button> + </li> + <li> + <button onclick="repeatedCallsToRetry(this);"> + Calling retry() more than once yield a rejected promise, but the + retryPromise resolves independently. + </button> + </li> + <li> + <button onclick="callCompleteWhileRetrying(this);"> + Calling complete() while a retry() is in progress results in an InvalidStateError. + </button> + </li> + <li> + <button onclick="callingRequestAbortMustNotAbort(this);"> + Trying to abort() via PaymentRequest is not possible. + </button> + </li> + <li> + <button onclick="canRetryMultipleTimes(this);"> + It's possible to retry() multiple times and eventually complete(). + After complete(), however, retry() rejects with an InvalidStateError. + </button> + </li> + <li> + <p> + When shown the payment sheet, hit pay once, then abort retrying the payment. + </p> + <button onclick="userCanAbortARetry(this);"> + The user aborting retrying a payment causes the retryPromise to reject with AbortError. + Aborting a payment is causes it complete. + </button> + </li> + <li> + <p> + When shown the payment sheet, hit pay once. Check payment sheet for error fields. + Then hit escape or otherwise abort the payment. + </p> + <button onclick="userIsShownErrorsFields(this);"> + When retrying, the user is shown error fields to fix. + </button> + </li> + <li> + <p> + When shown the payment sheet, hit pay once. + Then, change the shipping option. + Select to pay again. + </p> + <button onclick="abortTheUpdate(this);"> + When "abort the update" occurs because of an update error, + the `retryPromise` is rejected and response.[[complete]] becomes true. + </button> + </li> + <li> + <p> + When shown the payment sheet, hit pay once. Then retry once. + </p> + <button onclick="callingRetryReturnsUniquePromise(this);"> + Calling retry() multiple times is always a new object. + </button> + </li> + <li> + <button onclick="done();"> + Done! + </button> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">owners</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/payment-response/retry-method-warnings-manual.https.html b/testing/web-platform/tests/payment-request/payment-response/retry-method-warnings-manual.https.html new file mode 100644 index 0000000000..b68bf18309 --- /dev/null +++ b/testing/web-platform/tests/payment-request/payment-response/retry-method-warnings-manual.https.html @@ -0,0 +1,158 @@ +<!DOCTYPE html> <meta charset="utf-8" /> +<title>Warn when errorFields don't match request[[options]]</title> +<link rel="help" href="https://github.com/w3c/payment-request/pull/807" /> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="helpers.js"></script> +<script> + test(() => { + assert_true( + "retry" in PaymentResponse.prototype, + "retry must be in prototype" + ); + assert_true( + PaymentResponse.prototype.retry instanceof Function, + "retry must be a function" + ); + }, "PaymentResponse.prototype must have a retry() function (smoke test)."); + + const defaultOptions = { + requestPayerName: false, + requestPayerEmail: false, + requestPayerPhone: false, + requestShipping: false, + }; + function testShowWarning(button, errorFields) { + button.disabled = true; + promise_test(async () => { + const { response } = await getPaymentRequestResponse(defaultOptions); + await response.retry(errorFields); + await response.complete(); + }); + } +</script> +<h2>Manual Tests - Please run in order!</h2> +<p> + Please open the developer console. Each of the tests below should generate a + warning in the developer console. +</p> +<p>When presented with the payment sheet, hit pay twice.</p> +<ol> + <li> + <button onclick="testShowWarning(this, {payer: {name: 'Dont show this'}});"> + Show warning if `requestPayerName` if false, and `errorFields.payer.name` is + present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {payer: {email: 'Dont show this'}});" + > + Show warning if `requestPayerEmail` if false, and `errorFields.payer.email` + is present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {payer: {phone: 'Dont show this'}});" + > + Show warning if `requestPayerPhone` if false, and `errorFields.payer.phone` + is present. + </button> + </li> + <li> + <button onclick="testShowWarning(this, {shippingAddress: {}});"> + Show warning if `requestShipping` if false, and + `errorFields.shippingAddress` member present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {shippingAddress: {addressLine: 'Dont show this'}});" + > + Show warning if `requestShipping` if false, and + `errorFields.shippingAddress.addressLine` member present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {shippingAddress: {city: 'Dont show this'}});" + > + Show warning if `requestShipping` if false, and + `errorFields.shippingAddress.city` member present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {shippingAddress: {country: 'Dont show this'}});" + > + Show warning if `requestShipping` if false, and + `errorFields.shippingAddress.country` member present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {shippingAddress: {dependentLocality: 'Dont show this'}});" + > + Show warning if `requestShipping` if false, and + `errorFields.shippingAddress.dependentLocality` member present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {shippingAddress: {organization: 'Dont show this'}});" + > + Show warning if `requestShipping` if false, and + `errorFields.shippingAddress.organization` member present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {shippingAddress: {phone: 'Dont show this'}});" + > + Show warning if `requestShipping` if false, and + `errorFields.shippingAddress.phone` member present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {shippingAddress: {postalCode: 'Dont show this'}});" + > + Show warning if `requestShipping` if false, and + `errorFields.shippingAddress.postalCode` member present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {shippingAddress: {recipient: 'Dont show this'}});" + > + Show warning if `requestShipping` if false, and + `errorFields.shippingAddress.recipient` member present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {shippingAddress: {region: 'Dont show this'}});" + > + Show warning if `requestShipping` if false, and + `errorFields.shippingAddress.region` member present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {shippingAddress: {regionCode: 'Dont show this'}});" + > + Show warning if `requestShipping` if false, and + `errorFields.shippingAddress.regionCode` member present. + </button> + </li> + <li> + <button + onclick="testShowWarning(this, {shippingAddress: {sortingCode: 'Dont show this'}});" + > + Show warning if `requestShipping` if false, and + `errorFields.shippingAddress.sortingCode` member present. + </button> + </li> + <li><button onclick="done()">Done!</button></li> +</ol> diff --git a/testing/web-platform/tests/payment-request/payment-response/shippingAddress-attribute-manual.https.html b/testing/web-platform/tests/payment-request/payment-response/shippingAddress-attribute-manual.https.html new file mode 100644 index 0000000000..f9f0a6e4fa --- /dev/null +++ b/testing/web-platform/tests/payment-request/payment-response/shippingAddress-attribute-manual.https.html @@ -0,0 +1,101 @@ +<!doctype html> +<meta charset="utf8"> +<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-shippingaddress"> +<title> + PaymentResponse.prototype.shippingAddress +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="helpers.js"></script> +<script> +async function checkNullShippingAddress(button, options) { + button.disabled = true; + const { request, response } = await getPaymentRequestResponse(options); + await response.complete(); + test(() => { + assert_idl_attribute(response, "shippingAddress"); + assert_equals( + response.shippingAddress, + null, + "Expected response.shippingAddress to be null" + ); + assert_equals( + request.shippingAddress, + null, + "Expected request.shippingAddress to be null" + ); + }, button.textContent.trim()); +} + +async function runManualTest(button, options = {}, expected = {}, id) { + button.disabled = true; + const { request, response } = await getPaymentRequestResponse(options, id); + await response.complete(); + test(() => { + assert_equals(response.requestId, request.id, `Expected ids to match`); + assert_idl_attribute(response, "shippingAddress"); + const { shippingAddress: addr } = request; + assert_true( + addr instanceof ContactAddress, + "Expect instance of ContactAddress" + ); + try { + addr.toJSON(); + } catch (err) { + assert_unreached( + `Unexpected exception calling ContactAddress.toJSON(): ${err}` + ); + } + if (expected.shippingAddress === null) { + return; + } + assert_equals(addr.country, "AF", "Expected AF for country"); + assert_true( + addr.addressLine instanceof Array, + "Expected addressLine to be an array" + ); + assert_equals( + addr.addressLine.toString(), + "1 wpt street", + "Expected '1 wpt street' for addressLine" + ); + assert_equals(addr.city, "Kabul", "Expected city to be Kabul"); + assert_equals(addr.postalCode, "1001", "Expect postalCode to be 1001"); + assert_equals(addr.recipient, "web platform test"); + }, button.textContent.trim()); +} +</script> +<h2>shippingAddress attribute</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + When prompted, please enter "web platform test" as recipient, at address "1 wpt street" in "Kabul, Afghanistan", zip/postal code 1001. +</p> +<ol> + <li> + <button onclick="checkNullShippingAddress(this, undefined)"> + If options is undefined, then shippingAddress must be null. + </button> + </li> + <li> + <button onclick="checkNullShippingAddress(this, {})"> + If the requestShipping member is missing, then shippingAddress must be null. + </button> + </li> + <li> + <button onclick="checkNullShippingAddress(this, {requestShipping: false})"> + If the requestShipping member is false, then shippingAddress must be null. + </button> + </li> + <li> + <button onclick="runManualTest(this, {requestShipping: true}).then(done)"> + If the requestShipping member is true, then shippingAddress must be "1 wpt street" in "Kabul, Afghanistan", zip code 1001. + </button> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/payment-response/shippingOption-attribute-manual.https.html b/testing/web-platform/tests/payment-request/payment-response/shippingOption-attribute-manual.https.html new file mode 100644 index 0000000000..687d3a52de --- /dev/null +++ b/testing/web-platform/tests/payment-request/payment-response/shippingOption-attribute-manual.https.html @@ -0,0 +1,43 @@ +<!doctype html> +<meta charset="utf8"> +<link rel="help" href="https://w3c.github.io/payment-request/#dom-paymentresponse-shippingoption"> +<title> + PaymentResponse.prototype.complete() method +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="helpers.js"></script> +<h2>shippingOption attribute</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + For the last test, please select the only available shipping option and select "Pay". +</p> +<ol> + <li> + <button onclick="runManualTest(this, undefined, { shippingOption: null })"> + If the options is undefined, then shippingOption must be null. + </button> + </li> + <li> + <button onclick="runManualTest(this, { requestShipping: undefined }, { shippingOption: null })"> + If the requestShipping member is missing, then shippingOption must be null. + </button> + </li> + <li> + <button onclick="runManualTest(this, { requestShipping: false }, { shippingOption: null })"> + If the requestShipping member is false, then shippingOption must be null. + </button> + </li> + <li> + <button onclick="runManualTest(this, { requestShipping: true }, { shippingOption: 'pass' }).then(done)"> + If the requestShipping member is true, then shippingOption must be the id of the selected shipping option ("pass"). + </button> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/shipping-address-changed-manual.https.html b/testing/web-platform/tests/payment-request/shipping-address-changed-manual.https.html new file mode 100644 index 0000000000..aad57cd724 --- /dev/null +++ b/testing/web-platform/tests/payment-request/shipping-address-changed-manual.https.html @@ -0,0 +1,99 @@ +<!DOCTYPE html> +<!-- Copyright © 2017 Chromium authors and World Wide Web Consortium, (Massachusetts Institute of Technology, ERCIM, Keio University, Beihang). --> +<meta charset="utf-8"> +<title>Test for PaymentRequest shippingAddress attribute</title> +<link rel="help" href="https://w3c.github.io/payment-request/#shippingaddress-attribute"> +<link rel="help" href="https://w3c.github.io/payment-request/#onshippingaddresschange-attribute"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ explicit_done: true, explicit_timeout: true }); +const applePay = Object.freeze({ + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + } +}); +const validMethod = Object.freeze({ supportedMethods: "basic-card" }); +const validMethods = Object.freeze([validMethod, applePay]); +const validAmount = Object.freeze({ currency: "USD", value: "5.00" }); +const validTotal = Object.freeze({ + label: "label", + amount: validAmount, +}); +const validShippingOption = Object.freeze({ + id: "valid", + label: "Shipping Option", + amount: validAmount, + selected: false, +}); +const validDetails = Object.freeze({ + total: validTotal, + shippingOptions: [validShippingOption], +}); +const requestShipping = Object.freeze({ + requestShipping: true, +}); + +function testShippingAddressChange() { + promise_test(async t => { + const request = new PaymentRequest( + validMethods, + validDetails, + requestShipping + ); + assert_equals( + request.shippingAddress, + null, + "request.shippingAddress must initially be null" + ); + const listenerPromise = new Promise(resolve => { + request.addEventListener("shippingaddresschange", () => { + resolve(request.shippingAddress); + }); + }); + const handlerPromise = new Promise(resolve => { + request.onshippingaddresschange = () => { + resolve(request.shippingAddress); + }; + }); + request.show().catch(err => err); + const results = await Promise.all([listenerPromise, handlerPromise]); + results.forEach(obj => { + assert_true(obj instanceof ContactAddress, + "Expected instance of ContactAddress"); + assert_equals(obj.organization, "", "organization should be redacted"); + assert_equals(obj.phone, "", "phone should be redacted"); + assert_equals(obj.recipient, "", "recipient should be redacted"); + assert_equals(obj.addressLine.length, 0, "addressLine should be redacted"); + }); + await request.abort(); + }); + done(); +} + +</script> + +<h2>PaymentRequest shippingAddress attribute</h2> +<p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. +</p> +<p> + When the payment sheet is presented, enter or select a shipping address. +</p> +<ol> + <li> + <button onclick="testShippingAddressChange()"> + When the shipping address is manually changed, request.shippingAddress is a ContactAddress. + </button> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/show-method-optional-promise-rejects.https.html b/testing/web-platform/tests/payment-request/show-method-optional-promise-rejects.https.html index 4a41f28fc9..3b42965503 100644 --- a/testing/web-platform/tests/payment-request/show-method-optional-promise-rejects.https.html +++ b/testing/web-platform/tests/payment-request/show-method-optional-promise-rejects.https.html @@ -84,6 +84,10 @@ total: invalidNegativeTotal, }); + // PaymentOptions + const validOptions = Object.freeze({ + requestShipping: true, + }); // PaymentItem const validPaymentItem = Object.freeze({ @@ -100,6 +104,24 @@ const validPaymentItems = Object.freeze([validPaymentItem]); const invalidPaymentItems = Object.freeze([invalidPaymentItem]); + // PaymentShippingOption + const invalidShippingOption = Object.freeze({ + id: "abc", + label: "Invalid shipping option", + amount: invalidAmount, + selected: true, + }); + + // PaymentShippingOptions + const validShippingOption = Object.freeze({ + id: "abc", + label: "valid shipping option", + amount: validAmount, + }); + + const validShippingOptions = Object.freeze([validShippingOption]); + const invalidShippingOptions = Object.freeze([invalidShippingOption]); + // PaymentDetailsModifier const validModifier = Object.freeze({ additionalDisplayItems: validPaymentItems, @@ -144,7 +166,8 @@ promise_test(async (t) => { const request = new PaymentRequest( validMethods, - validDetails + validDetails, + validOptions ); await test_driver.bless("Payment request"); const detailsPromise = Promise.resolve(badDetails); @@ -194,6 +217,21 @@ ); testBadUpdate( + "Updating with duplicate shippingOptions (same IDs) results in a TypeError.", + { + ...validDetails, + shippingOptions: [validShippingOption, validShippingOption], + }, + TypeError + ); + + testBadUpdate( + "Updating with a shippingOption with an invalid currency value results in a RangError.", + { ...validDetails, shippingOptions: invalidShippingOptions }, + RangeError + ); + + testBadUpdate( "Must throw a RangeError when a modifier's total item has an invalid currency.", { ...validDetails, modifiers: [modifierWithInvalidTotal, validModifier] }, RangeError diff --git a/testing/web-platform/tests/payment-request/show-method-optional-promise-resolves-manual.https.html b/testing/web-platform/tests/payment-request/show-method-optional-promise-resolves-manual.https.html new file mode 100644 index 0000000000..5360a9704a --- /dev/null +++ b/testing/web-platform/tests/payment-request/show-method-optional-promise-resolves-manual.https.html @@ -0,0 +1,339 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Test for PaymentRequest.show(optional promise) method</title> +<link rel="help" href="https://w3c.github.io/browser-payment-api/#show-method"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +"use strict"; +setup({ + allow_uncaught_exception: true, + explicit_done: true, + explicit_timeout: true, +}); + +// DATA USED BY TESTS +// PaymentMethods +const validMethods = Object.freeze([ + { + supportedMethods: "valid-but-wont-ever-match", + }, + { + supportedMethods: "basic-card", + }, + { + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + } + }, +]); + +// Amounts +const failAmount = Object.freeze({ + currency: "USD", + value: "1.00", +}); +const passAmount = Object.freeze({ + currency: "CAD", + value: "50.00", +}); +const neutralAmount = Object.freeze({ + currency: "AUD", + value: "0.00", +}); + +// Labels +const failLabel = "💥 TEST HAS FAILED 💥"; +const passLabel = "✅ TEST HAS PASSED ✅"; +const neutralLabel = "Ignore this label"; +// Totals +const failTotal = Object.freeze({ + label: failLabel, + amount: failAmount, +}); +const passTotal = Object.freeze({ + label: passLabel, + amount: passAmount, +}); +const neutralTotal = Object.freeze({ + label: neutralLabel, + amount: passAmount, +}); + +// PaymentItem +const failPaymentItem = Object.freeze({ + amount: failAmount, + label: failLabel, +}); +const failPaymentItems = Object.freeze([failPaymentItem]); + +const passPaymentItem = Object.freeze({ + amount: passAmount, + label: passLabel, +}); +const passPaymentItems = Object.freeze([passPaymentItem]); + +// PaymentShippingOptions +const failShippingOption = Object.freeze({ + id: "fail", + label: failLabel, + amount: failAmount, +}); +const failShippingOptions = Object.freeze([failShippingOption]); + +const neutralShippingOption = Object.freeze({ + id: "neutral", + label: neutralLabel, + amount: neutralAmount, +}); + +const updatedShippingOption1 = Object.freeze({ + id: "updated-1", + label: `${passLabel} - option 1`, + amount: passAmount, +}); +const updatedShippingOption2 = Object.freeze({ + id: "updated-2", + label: `${passLabel} - option 2 (MUST BE SELECTED!)`, + amount: passAmount, + selected: true, +}); +const passShippingOptions = Object.freeze([ + updatedShippingOption1, + updatedShippingOption2, +]); + +// Modifiers +// create a modifier objects for each validMethods +// and single additional display item +const failModifiers = validMethods.map(modifier => { + const label = `${failLabel} - (${modifier.supportedMethods})`; + return { + ...modifier, + total: { + ...failTotal, + label, + }, + additionalDisplayItems: [ + { + ...failPaymentItem, + label, + }, + ], + }; +}); +// Updates the total for each, and changes the additionalDisplayItems +const passModifiers = failModifiers.map(modifier => { + const label = `${passLabel} - (${modifier.supportedMethods})`; + return { + ...modifier, + total: { + ...passTotal, + label, + }, + additionalDisplayItems: [ + { + ...passPaymentItem, + label, + }, + ], + }; +}); + +// PaymentDetailsInit +const failDetails = Object.freeze({ + displayItems: failPaymentItems, + id: "this cannot be changed", + modifiers: failModifiers, + shippingOptions: failShippingOptions, + total: failTotal, +}); + +const neutralDetails = Object.freeze({ + displayItems: [], + modifiers: [], + shippingOptions: [neutralShippingOption], + total: neutralTotal, +}); + +function smokeTest() { + promise_test(async t => { + const request = new PaymentRequest(validMethods, failDetails); + await promise_rejects_js( + t, + TypeError, + request.show({ + total: "This throws a TypeError", + }), + "expected TypeError" + ); + }, "smoke test - checks if the optional details are supported on show() method"); +} + +function runUpdateDetailsAlgorithm( + buttonElement, + details, + options = { + requestShipping: true, + }, + initialDetails = failDetails, +) { + const testAssertion = buttonElement.textContent.trim(); + buttonElement.disabled = true; + promise_test(async t => { + const request = new PaymentRequest(validMethods, initialDetails, options); + const detailsPromise = Promise.resolve(details); + const acceptPromise = request.show(detailsPromise); + assert_equals(request.id, "this cannot be changed", "id must never change."); + await promise_rejects_dom( + t, + "AbortError", + acceptPromise, + "expected AbortError" + ); + }, testAssertion); +} +</script> +<h2> + PaymentRequest <code>.show(optional detailsPromise)</code> tests +</h2> +<p> + These test cause <code>detailsPromise</code> to resolve successfully with some updated value. As such, that will cause + something in the payment sheet to change. Each test describes what is expected to change - if anything. +</p> +<p> + <strong>Instructions:</strong> Click on each button in sequence from top to bottom without refreshing the page. The payment + sheet will be shown. If required, confirm that the expected value appears in the payment sheet. Finally, manually abort/cancel + the payment request by closing the payment sheet. +</p> +<ol> + <li><button onclick="smokeTest()">If the payment sheet is shown, the test has failed.</button></li> + <li><button onclick=" + const details = { + ...neutralDetails, + id: 'fail', + }; + runUpdateDetailsAlgorithm(this, details); + "> + When the payment sheet is shown, the provided `id` must have no effect on the payment request. + </button></li> + <li><button onclick=" + const details = { + ...neutralDetails, + total: passTotal + }; + runUpdateDetailsAlgorithm(this, details); + "> + When the payment sheet is shown, the total must be CAD$50 with the label "✅ TEST HAS PASSED ✅". + </button></li> + <li><button onclick=" + const details = { + ...neutralDetails, + displayItems: passPaymentItems, + }; + runUpdateDetailsAlgorithm(this, details); + "> + When the payment sheet is shown, there must be a one display item with a value of CAD$50 with the label "✅ TEST HAS PASSED ✅". + </button> + </li> + <li><button onclick=" + const auItem = { + ...passPaymentItem, + amount: { value: '40', currency: 'AUD'}, + pending: true + } + const details = { + ...neutralDetails, + displayItems: passPaymentItems.concat(auItem), + }; + runUpdateDetailsAlgorithm(this, details); + "> + When the payment sheet is shown, there must be + two display items: One with a value of CAD$50, another with + value AUD$40 that is pending. + </button> + </li> + <li><button onclick=" + const details = { + ...neutralDetails, + shippingOptions: [updatedShippingOption1], + }; + runUpdateDetailsAlgorithm(this, details); + "> + When the payment sheet is shown, there must be a one shipping option + with a value of CAD$50. + </button> + </li> + <li><button onclick=" + const details = { + ...neutralDetails, + shippingOptions: passShippingOptions, + }; + runUpdateDetailsAlgorithm(this, details); + "> + When the payment sheet is shown, there must be + two shipping options: One with a value of CAD$50, another with + value AUD$40 that is selected. + </button> + </li> + <li><button onclick=" + const details = { + ...neutralDetails, + modifiers: passModifiers, + }; + runUpdateDetailsAlgorithm(this, details); + "> + When the payment sheet is shown, the total should be CAD$50. + </button> + </li> + <li> + <button onclick=" + const details = { + ...neutralDetails, + shippingOptions: [], + error: passLabel, + }; + runUpdateDetailsAlgorithm(this, details); + "> + When the payment sheet is shown, the string "✅ TEST HAS PASSED ✅" should be shown + somewhere in the user interface. Alternatively, the payment sheet must indicate to the + end user that it's not possible to ship their order. + </button> + </li> + <li> + <button onclick=" + const details = { + ...neutralDetails, + error: failLabel, + }; + runUpdateDetailsAlgorithm(this, details, {requestShipping: false}); + "> + When the payment sheet is shown, there should not be any errors shown. + </button> + </li> + <li> + <button onclick=" + const initialDetails = {total: passTotal, id: 'this cannot be changed'}; + const updatedDetails = {}; + runUpdateDetailsAlgorithm( + this, updatedDetails, {requestShipping: false}, initialDetails); + "> + Resolving the show promise with empty details will preserve the details from + the constructor. When the payment sheet is shown, the string + "✅ TEST HAS PASSED ✅" should be shown. + </button> + </li> + <li> + <button onclick="done();">Done!</button> + </li> +</ol> + +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/updateWith-method-pmi-handling-manual.https.html b/testing/web-platform/tests/payment-request/updateWith-method-pmi-handling-manual.https.html new file mode 100644 index 0000000000..1a52978ca3 --- /dev/null +++ b/testing/web-platform/tests/payment-request/updateWith-method-pmi-handling-manual.https.html @@ -0,0 +1,140 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>Test for validity of payment method identifiers when calling updateWith() method</title> +<link rel="help" href="https://www.w3.org/TR/payment-request/#updatewith()-method"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +"use strict"; +setup({ + explicit_done: true, + explicit_timeout: true, + allow_uncaught_exception: true, +}); +const applePay = Object.freeze({ + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + }, +}); +const validMethod = Object.freeze({ + supportedMethods: "https://:@wpt.fyi:443/payment-request", +}); + +const validMethods = Object.freeze([ + validMethod, + applePay, + { supportedMethods: "basic-card" }, +]); + +const validAmount = Object.freeze({ + currency: "USD", + value: "1.0", +}); + +const validTotal = Object.freeze({ + label: "Default Total", + amount: validAmount, +}); + +const validShippingOption = Object.freeze({ + id: "standard", + label: "Shipping option", + amount: validAmount, + selected: true, +}); + +const validDetails = Object.freeze({ + total: validTotal, + shippingOptions: [validShippingOption], +}); + +const validModifier = Object.freeze({ + supportedMethods: "basic-card", + total: validTotal, +}); + +function manualTest(button, { invalidMethod }) { + button.disabled = true; + promise_test(async t => { + const request = new PaymentRequest(validMethods, validDetails, { + requestShipping: true, + }); + const listener = ev => { + const invalidModifier = Object.assign({}, validModifier, { + supportedMethods: invalidMethod, + }); + const invalidDetails = Object.assign({}, validDetails, { + modifiers: [validModifier, invalidModifier], + }); + ev.updateWith(invalidDetails); + }; + // We test against a valid and an invalid modifier + request.addEventListener("shippingaddresschange", listener, { once: true }); + const showPromise = request.show(); + await promise_rejects_js(t, RangeError, showPromise); + }, button.textContent.trim()); +} +</script> +<h2>updateWith() method: test validity of payment method identifiers.</h2> +<p> + When shown a payment sheet, select a different address. +</p> +<ol> + <li> + <button onclick="manualTest(this, {invalidMethod: 'https://:password@example.com'});"> + Must throw if the URL has a password. + </button> + </li> + <li> + <button onclick="manualTest(this, {invalidMethod: 'https://username@example.com'});"> + Must throw if the URL has a username. + </button> + </li> + <li> + <button onclick="manualTest(this, {invalidMethod: 'https://username:password@example.com/pay'});"> + Must throw if the URL has a username and a password. + </button> + </li> + <li> + <button onclick="manualTest(this, {invalidMethod: 'http://username:password@example.com/pay'});"> + Must throw if it's http, and has a username and password. + </button> + </li> + <li> + <button onclick="manualTest(this, {invalidMethod: 'http://foo.com:100000000/pay'});"> + Must throw if the URL is invalid (port range). + </button> + </li> + <li> + <button onclick="manualTest(this, {invalidMethod: 'basic-💳'});"> + Must throw if the PMI contains characters that are out of range. + </button> + </li> + <li> + <button onclick="manualTest(this, {invalidMethod: 'not-https://wpt.fyi/payment-request'});"> + Must throw if not https. + </button> + </li> + <li> + <button onclick="manualTest(this, {invalidMethod: '¡basic-*-card!'});"> + Must throw if the standardized PMI contains characters outside the ascii range. + </button> + </li> + <li> + <button onclick="manualTest(this, {invalidMethod: 'Basic-Card'});"> + Must throw if standardized PMI has uppercase characters. + </button> + </li> + <li> + <button onclick="done();">Done!</button> + </li> +</ol> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/payment-request/user-accepts-payment-request-algo-manual.https.html b/testing/web-platform/tests/payment-request/user-accepts-payment-request-algo-manual.https.html new file mode 100644 index 0000000000..300f04811f --- /dev/null +++ b/testing/web-platform/tests/payment-request/user-accepts-payment-request-algo-manual.https.html @@ -0,0 +1,230 @@ +<!doctype html> +<meta charset="utf8"> +<link rel="help" href="https://w3c.github.io/payment-request/#user-accepts-the-payment-request-algorithm"> +<title> + User accepts the payment request algorithm +</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +setup({ explicit_done: true, explicit_timeout: true }); +const applePay = Object.freeze({ + supportedMethods: "https://apple.com/apple-pay", + data: { + version: 3, + merchantIdentifier: "merchant.com.example", + countryCode: "US", + merchantCapabilities: ["supports3DS"], + supportedNetworks: ["visa"], + } +}); +const basicCardMethod = Object.freeze({ + supportedMethods: "basic-card", +}); +const validMethod = Object.freeze({ + supportedMethods: "this-is-just-for-testings-will-never-match", +}); +const methods = Object.freeze([basicCardMethod, validMethod, applePay]); +const validAmount = Object.freeze({ + currency: "USD", + value: "5.00", +}); +const shippingOptions = [ + Object.freeze({ + id: "option1", + label: "Option 1", + amount: validAmount, + selected: false, + }), + Object.freeze({ + id: "option 2", + label: "Option 2", + amount: validAmount, + selected: true, + }), +]; + +const detailsNoShippingOptions = Object.freeze({ + total: { + label: "Total due", + amount: validAmount, + }, +}); + +const detailsWithShippingOptions = Object.assign({}, detailsNoShippingOptions, { + shippingOptions, +}); + +const optionsRequestNothing = Object.freeze({ + requestShipping: false, + requestPayerEmail: false, + requestPayerName: false, + requestPayerPhone: false, +}); + +const optionsRequestEverything = Object.freeze({ + requestShipping: true, + requestPayerEmail: true, + requestPayerName: true, + requestPayerPhone: true, +}); + +test(() => { + // smoke test + try { + new PaymentRequest(methods, detailsNoShippingOptions); + } catch (err) { + done(); + throw err; + } +}, "Must be able to construct a payment request (smoke test)"); + +function testAcceptRequestAlgorithm( + button, + details, + options = {}, + expectedResponse = {} +) { + button.disabled = true; + promise_test(async t => { + const request = new PaymentRequest(methods, details, options); + const response = await request.show(); + assert_true( + response instanceof PaymentResponse, + "Expected an instance of PaymentResponse." + ); + // Response [[calledComplete]] is false, so this shouldn't throw + await response.complete("success"); + // For good measure, test that subsequent complete() + for (const state of [undefined, "success", "fail", "unknown"]) { + await promise_rejects_dom( + t, + "InvalidStateError", + response.complete(state), + "Response [[calledComplete]] is true, so InvalidStateError" + ); + } + assert_equals( + request.id, + response.requestId, + "Request and response ids must match." + ); + assert_true(response.details instanceof Object, "Expected an object"); + assert_equals( + response.shippingAddress, + request.shippingAddress, + "Request and response must reference same shippingAddress (or both null)." + ); + assert_equals( + response.shippingOption, + request.shippingOption, + "Request and response must be the same value (or both null)." + ); + if (options.requestShipping === true) { + assert_true( + response.shippingAddress instanceof ContactAddress, + "Expected an instance of ContactAddress." + ); + } + const expected = { + methodName: "basic-card", + payerEmail: options.requestPayerEmail + ? expectedResponse.payerEmail + : null, + payerName: options.requestPayerName ? expectedResponse.payerName : null, + payerPhone: options.requestPayerPhone + ? expectedResponse.payerPhone + : null, + }; + for (const [attr, expectedValue] of Object.entries(expected)) { + assert_equals( + response[attr], + expectedValue, + `response.${attr} must be ${expectedValue}` + ); + } + await promise_rejects_dom( + t, + "InvalidStateError", + request.show(), + "Request [[state]] is closed, so InvalidStateError" + ); + }, button.textContent.trim()); +} + +</script> + +<section> + <h2 id="user-accepts-payment-request">User accepts payment request</h2> + <p> + Click on each button in sequence from top to bottom without refreshing the page. + Each button will bring up the Payment Request UI window. + </p> + <p> + When shown the payment sheet, please input a credit card and select Pay. + </p> + <ol> + <li> + <button onclick=" + const detailsWithId = Object.assign({}, detailsNoShippingOptions, { id: 'pass' }); + testAcceptRequestAlgorithm(this, detailsWithId, optionsRequestNothing);"> + User accepts payment request, but not shipping is requested. + </button> Use any credit card to pay. + </li> + <li> + <button onclick=" + const requestShipping = Object.assign({}, optionsRequestNothing, {requestShipping: true}); + const expectedValues = { shippingOption: 'option 2' }; + testAcceptRequestAlgorithm(this, detailsWithShippingOptions, requestShipping, expectedValues);"> + User accepts payment request, merchant requests shipping. + </button> Select any shipping option, and use any credit card to pay. + </li> + <li> + <button onclick=" + const requestPayerEmail = Object.assign({}, optionsRequestNothing, {requestPayerEmail: true}); + const expectValues = { payerEmail: 'wpt@w3.org' }; + testAcceptRequestAlgorithm(this, detailsNoShippingOptions, requestPayerEmail, expectValues);"> + User accepts payment request, merchant requests email. + </button> + When prompted, please use "wpt@w3.org" as the email. + </li> + <li> + <button onclick=" + const requestPayerPhone = Object.assign({}, optionsRequestNothing, {requestPayerPhone: true}); + const expectValues = { payerPhone: '+12345678910' }; + testAcceptRequestAlgorithm(this, detailsNoShippingOptions, requestPayerPhone, expectValues);"> + User accepts payment request, merchant requests phone. + </button> + When prompted, please use "+12345678910" as the phone number. + </li> + <li> + <button onclick=" + const requestPayerName = Object.assign({}, optionsRequestNothing, {requestPayerName: true}); + const expectValues = { payerName: 'web platform test' }; + testAcceptRequestAlgorithm(this, detailsNoShippingOptions, requestPayerName, expectValues);"> + User accepts payment request, merchant requests payer's name. + </button> + When prompted, please use "web platform test" as the payer name. + </li> + <li> + <button onclick=" + const expectValues = { + payerEmail: 'wpt@w3.org', + payerName: 'web platform test', + payerPhone: '+12345678910', + shippingOption: 'option 2', + }; + testAcceptRequestAlgorithm(this, detailsWithShippingOptions, optionsRequestEverything, expectValues);"> + User accepts payment request, merchant requests everything. + </button> + When prompted, please use: "+12345678910" as the phone number, "web platform test" as the payer name, and "wpt@w3.org" as the email. Then press Pay. + </li> + <li> + <button onclick="done()">Done</button> + </li> + </ol> +</section> +<small> + If you find a buggy test, please <a href="https://github.com/web-platform-tests/wpt/issues">file a bug</a> + and tag one of the <a href="https://github.com/web-platform-tests/wpt/blob/master/payment-request/META.yml">suggested reviewers</a>. +</small> diff --git a/testing/web-platform/tests/pending-beacon/META.yml b/testing/web-platform/tests/pending-beacon/META.yml deleted file mode 100644 index 04b88b5f72..0000000000 --- a/testing/web-platform/tests/pending-beacon/META.yml +++ /dev/null @@ -1,4 +0,0 @@ -spec: https://wicg.github.io/pending-beacon/ -suggested_reviewers: - - mingyc - - clelland diff --git a/testing/web-platform/tests/pending-beacon/pending_beacon-basic.tentative.https.window.js b/testing/web-platform/tests/pending-beacon/pending_beacon-basic.tentative.https.window.js deleted file mode 100644 index c63ae4e39d..0000000000 --- a/testing/web-platform/tests/pending-beacon/pending_beacon-basic.tentative.https.window.js +++ /dev/null @@ -1,87 +0,0 @@ -// META: script=./resources/pending_beacon-helper.js - -'use strict'; - -test(() => { - assert_throws_js(TypeError, () => new PendingBeacon('/')); -}, `PendingBeacon cannot be constructed directly.`); - -for (const beaconType of BeaconTypes) { - test(() => { - assert_throws_js(TypeError, () => new beaconType.type()); - assert_throws_js(TypeError, () => new beaconType.type(undefined)); - assert_throws_js(TypeError, () => new beaconType.type(null)); - }, `${beaconType.name}: constructor throws TypeError if URL is missing.`); - - test(() => { - assert_throws_js( - TypeError, () => new beaconType.type('http://www.google.com')); - assert_throws_js(TypeError, () => new beaconType.type('file://tmp')); - assert_throws_js(TypeError, () => new beaconType.type('ssh://example.com')); - assert_throws_js(TypeError, () => new beaconType.type('wss://example.com')); - assert_throws_js(TypeError, () => new beaconType.type('about:blank')); - assert_throws_js( - TypeError, () => new beaconType.type(`javascript:alert('');`)); - }, `${beaconType.name}: constructor throws TypeError on non-HTTPS URL.`); - - test(() => { - const beacon = new beaconType.type('/'); - assert_equals(beacon.url, '/'); - assert_equals(beacon.method, beaconType.expectedMethod); - assert_equals(beacon.backgroundTimeout, -1); - assert_equals(beacon.timeout, -1); - assert_true(beacon.pending); - }, `${beaconType.name}: constructor sets default properties if missing.`); - - test(() => { - const beacon = new beaconType.type( - 'https://www.google.com', {backgroundTimeout: 200, timeout: 100}); - assert_equals(beacon.url, 'https://www.google.com'); - assert_equals(beacon.method, beaconType.expectedMethod); - assert_equals(beacon.backgroundTimeout, 200); - assert_equals(beacon.timeout, 100); - assert_true(beacon.pending); - }, `${beaconType.name}: constructor can set properties.`); - - test(() => { - let beacon = new beaconType.type( - 'https://www.google.com', - {method: 'GET', backgroundTimeout: 200, timeout: 100}); - - beacon.backgroundTimeout = 400; - assert_equals(beacon.backgroundTimeout, 400); - - beacon.timeout = 600; - assert_equals(beacon.timeout, 600); - }, `${beaconType.name}: 'backgroundTimeout' & 'timeout' can be mutated.`); - - test( - () => { - let beacon = new beaconType.type('https://www.google.com'); - - assert_throws_js(TypeError, () => beacon.url = '/'); - assert_throws_js(TypeError, () => beacon.method = 'FOO'); - assert_throws_js(TypeError, () => beacon.pending = false); - }, - `${beaconType.name}: throws TypeError when mutating ` + - `'url', 'method', 'pending'.`); -} - -test(() => { - let beacon = new PendingGetBeacon('/'); - - assert_throws_js(TypeError, () => new beacon.setURL()); - assert_throws_js(TypeError, () => new beacon.setURL(undefined)); - assert_throws_js(TypeError, () => new beacon.setURL(null)); -}, `PendingGetBeacon: setURL() throws TypeError if URL is missing.`); - -test(() => { - let beacon = new PendingGetBeacon('/'); - - assert_throws_js(TypeError, () => beacon.setURL('http://www.google.com')); - assert_throws_js(TypeError, () => beacon.setURL('file://tmp')); - assert_throws_js(TypeError, () => beacon.setURL('ssh://example.com')); - assert_throws_js(TypeError, () => beacon.setURL('wss://example.com')); - assert_throws_js(TypeError, () => beacon.setURL('about:blank')); - assert_throws_js(TypeError, () => beacon.setURL(`javascript:alert('');`)); -}, `PendingGetBeacon: setURL() throws TypeError on non-HTTPS URL.`); diff --git a/testing/web-platform/tests/pending-beacon/pending_beacon-basic.window.js b/testing/web-platform/tests/pending-beacon/pending_beacon-basic.window.js deleted file mode 100644 index d6afd9fb5e..0000000000 --- a/testing/web-platform/tests/pending-beacon/pending_beacon-basic.window.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -test(() => { - assert_false(window.hasOwnProperty('PendingGetBeacon')); -}, `PendingGetBeacon is not supported in non-secure context.`); - -test(() => { - assert_false(window.hasOwnProperty('PendingPostBeacon')); -}, `PendingPostBeacon is not supported in non-secure context.`); diff --git a/testing/web-platform/tests/pending-beacon/pending_beacon-deactivate.tentative.https.window.js b/testing/web-platform/tests/pending-beacon/pending_beacon-deactivate.tentative.https.window.js deleted file mode 100644 index 74c0852adc..0000000000 --- a/testing/web-platform/tests/pending-beacon/pending_beacon-deactivate.tentative.https.window.js +++ /dev/null @@ -1,12 +0,0 @@ -// META: script=./resources/pending_beacon-helper.js - -'use strict'; - -for (const beaconType of BeaconTypes) { - test(() => { - const beacon = new beaconType.type('https://www.google.com'); - assert_true(beacon.pending); - beacon.deactivate(); - assert_false(beacon.pending); - }, `${beaconType.name}: deactivate() changes 'pending' state.`); -} diff --git a/testing/web-platform/tests/pending-beacon/pending_beacon-sendnow.tentative.https.window.js b/testing/web-platform/tests/pending-beacon/pending_beacon-sendnow.tentative.https.window.js deleted file mode 100644 index 00baccd0c1..0000000000 --- a/testing/web-platform/tests/pending-beacon/pending_beacon-sendnow.tentative.https.window.js +++ /dev/null @@ -1,44 +0,0 @@ -// META: script=/common/utils.js -// META: script=./resources/pending_beacon-helper.js - -'use strict'; - -promise_test(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - - // Create and send a beacon. - const beacon = new PendingGetBeacon(url); - beacon.sendNow(); - - await expectBeacon(uuid, {count: 1}); -}, 'sendNow() sends a beacon immediately.'); - -promise_test(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - - // Create and send a beacon. - const beacon = new PendingGetBeacon(url); - beacon.sendNow(); - await expectBeacon(uuid, {count: 1}); - - // Try to send the beacon again, and verify no beacon arrives. - beacon.sendNow(); - await expectBeacon(uuid, {count: 1}); -}, 'sendNow() doesn\'t send the same beacon twice.'); - -promise_test(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - - // Create and send 1st beacon. - const beacon1 = new PendingGetBeacon(url); - beacon1.sendNow(); - - // Create and send 2st beacon. - const beacon2 = new PendingGetBeacon(url); - beacon2.sendNow(); - - await expectBeacon(uuid, {count: 2}); -}, 'sendNow() sends multiple beacons.'); diff --git a/testing/web-platform/tests/pending-beacon/pending_beacon-sendondiscard.tentative.https.window.js b/testing/web-platform/tests/pending-beacon/pending_beacon-sendondiscard.tentative.https.window.js deleted file mode 100644 index b4283cecef..0000000000 --- a/testing/web-platform/tests/pending-beacon/pending_beacon-sendondiscard.tentative.https.window.js +++ /dev/null @@ -1,95 +0,0 @@ -// META: script=/common/utils.js -// META: script=./resources/pending_beacon-helper.js - -'use strict'; - -parallelPromiseTest(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - const numPerMethod = 20; - const total = numPerMethod * 2; - - // Loads an iframe that creates `numPerMethod` GET & POST beacons. - const iframe = await loadScriptAsIframe(` - const url = "${url}"; - for (let i = 0; i < ${numPerMethod}; i++) { - let get = new PendingGetBeacon(url); - let post = new PendingPostBeacon(url); - } - `); - - // Delete the iframe to trigger beacon sending. - document.body.removeChild(iframe); - - // The iframe should have sent all beacons. - await expectBeacon(uuid, {count: total}); -}, 'A discarded document sends all its beacons with default config.'); - -parallelPromiseTest(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - - // Loads an iframe that creates a GET beacon, - // then sends it out with `sendNow()`. - const iframe = await loadScriptAsIframe(` - const url = "${url}"; - let beacon = new PendingGetBeacon(url); - beacon.sendNow(); - `); - - // Delete the document and verify no more beacons are sent. - document.body.removeChild(iframe); - - // The iframe should have sent only 1 beacon. - await expectBeacon(uuid, {count: 1}); -}, 'A discarded document does not send an already sent beacon.'); - -parallelPromiseTest(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - const numPerMethod = 20; - const total = numPerMethod * 2; - - // Loads an iframe that creates `numPerMethod` GET & POST beacons with - // different timeouts. - const iframe = await loadScriptAsIframe(` - const url = "${url}"; - for (let i = 0; i < ${numPerMethod}; i++) { - let get = new PendingGetBeacon(url, {timeout: 100*i}); - let post = new PendingPostBeacon(url, {timeout: 100*i}); - } - `); - - // Delete the iframe to trigger beacon sending. - document.body.removeChild(iframe); - - // Even beacons are configured with different timeouts, - // the iframe should have sent all beacons when it is discarded. - await expectBeacon(uuid, {count: total}); -}, `A discarded document sends all its beacons of which timeouts are not - default.`); - -parallelPromiseTest(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - const numPerMethod = 20; - const total = numPerMethod * 2; - - // Loads an iframe that creates `numPerMethod` GET & POST beacons with - // different backgroundTimeouts. - const iframe = await loadScriptAsIframe(` - const url = "${url}"; - for (let i = 0; i < ${numPerMethod}; i++) { - let get = new PendingGetBeacon(url, {backgroundTimeout: 100*i}); - let post = new PendingPostBeacon(url, {backgroundTimeout: 100*i}); - } - `); - - // Delete the iframe to trigger beacon sending. - document.body.removeChild(iframe); - - // Even beacons are configured with different backgroundTimeouts, - // the iframe should have sent all beacons when it is discarded. - await expectBeacon(uuid, {count: total}); -}, `A discarded document sends all its beacons of which backgroundTimeouts are - not default.`); diff --git a/testing/web-platform/tests/pending-beacon/pending_beacon-sendonhidden.tentative.https.window.js b/testing/web-platform/tests/pending-beacon/pending_beacon-sendonhidden.tentative.https.window.js deleted file mode 100644 index a0ede5dadd..0000000000 --- a/testing/web-platform/tests/pending-beacon/pending_beacon-sendonhidden.tentative.https.window.js +++ /dev/null @@ -1,81 +0,0 @@ -// META: script=/common/dispatcher/dispatcher.js -// META: script=/common/get-host-info.sub.js -// META: script=/common/utils.js -// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js -// META: script=/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js -// META: script=./resources/pending_beacon-helper.js - -'use strict'; - -for (const beaconType of BeaconTypes) { - const beaconName = beaconType.name; - - parallelPromiseTest(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - // backgroundTimeout = 0s means `beacon should be sent out right on - // entering `hidden` state after navigating away. - const options = {backgroundTimeout: 0}; - const helper = new RemoteContextHelper(); - // Opens a window with noopener so that BFCache will work. - const rc1 = await helper.addWindow( - /*config=*/ null, /*options=*/ {features: 'noopener'}); - - // Creates a PendingBeacon in remote which should only be sent on navigating - // away. - await rc1.executeScript((beaconName, url, options) => { - const beacon = beaconName == 'PendingGetBeacon' ? - new PendingGetBeacon(url, options) : - new PendingPostBeacon(url, options); - }, [beaconName, url, options]); - - await expectBeacon(uuid, {count: 0}); - }, `${beaconName}: does not send without page navigation.`); - - parallelPromiseTest(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - // backgroundTimeout = 0s means `beacon should be sent out right on - // entering `hidden` state after navigating away. - const options = {backgroundTimeout: 0}; - const helper = new RemoteContextHelper(); - // Opens a window with noopener so that BFCache will work. - const rc1 = await helper.addWindow( - /*config=*/ null, /*options=*/ {features: 'noopener'}); - - // Creates a PendingBeacon in remote which should only be sent on navigating - // away. - await rc1.executeScript((beaconName, url, options) => { - const beacon = beaconName == 'PendingGetBeacon' ? - new PendingGetBeacon(url, options) : - new PendingPostBeacon(url, options); - }, [beaconName, url, options]); - // Navigates away to trigger beacon sending. - rc1.navigateToNew(); - - await expectBeacon(uuid, {count: 1}); - }, `${beaconName}: sends on page entering hidden state (w/ BFCache).`); - - parallelPromiseTest(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - // backgroundTimeout = 0s means `beacon should be sent out right on - // entering `hidden` state after navigating away. - const options = {backgroundTimeout: 0}; - const helper = new RemoteContextHelper(); - // Opens a window without BFCache. - const rc1 = await helper.addWindow(); - - // Creates a PendingBeacon in remote which should only be sent on navigating - // away. - await rc1.executeScript((beaconName, url, options) => { - const beacon = beaconName == 'PendingGetBeacon' ? - new PendingGetBeacon(url, options) : - new PendingPostBeacon(url, options); - }, [beaconName, url, options]); - // Navigates away to trigger beacon sending. - rc1.navigateToNew(); - - await expectBeacon(uuid, {count: 1}); - }, `${beaconName}: sends on page entering hidden state (w/o BFCache).`); -} diff --git a/testing/web-platform/tests/pending-beacon/pending_get_beacon-cors.tentative.https.window.js b/testing/web-platform/tests/pending-beacon/pending_get_beacon-cors.tentative.https.window.js deleted file mode 100644 index 10bb3a0bed..0000000000 --- a/testing/web-platform/tests/pending-beacon/pending_get_beacon-cors.tentative.https.window.js +++ /dev/null @@ -1,28 +0,0 @@ -// META: script=/common/get-host-info.sub.js -// META: script=/common/utils.js -// META: script=./resources/pending_beacon-helper.js - -'use strict'; - -const {HTTPS_ORIGIN, HTTPS_NOTSAMESITE_ORIGIN} = get_host_info(); - -parallelPromiseTest(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid, {host: HTTPS_ORIGIN}); - - let beacon = new PendingGetBeacon(url); - beacon.sendNow(); - - await expectBeacon(uuid, {count: 1}); -}, 'PendingGetBeacon: same-origin'); - -parallelPromiseTest(async t => { - const uuid = token(); - const url = generateSetBeaconURL( - uuid, {host: HTTPS_NOTSAMESITE_ORIGIN, expectOrigin: HTTPS_ORIGIN}); - - let beacon = new PendingGetBeacon(url); - beacon.sendNow(); - - await expectBeacon(uuid, {count: 1}); -}, 'PendingGetBeacon: cross-origin'); diff --git a/testing/web-platform/tests/pending-beacon/pending_get_beacon-send.tentative.https.window.js b/testing/web-platform/tests/pending-beacon/pending_get_beacon-send.tentative.https.window.js deleted file mode 100644 index 8c6e826af5..0000000000 --- a/testing/web-platform/tests/pending-beacon/pending_get_beacon-send.tentative.https.window.js +++ /dev/null @@ -1,38 +0,0 @@ -// META: script=/common/utils.js -// META: script=./resources/pending_beacon-helper.js - -'use strict'; - -const baseUrl = `${location.protocol}//${location.host}`; - -parallelPromiseTest(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - - let beacon = new PendingGetBeacon('/'); - - beacon.setURL(url); - assert_equals(beacon.url, url); - beacon.sendNow(); - - await expectBeacon(uuid, {count: 1}); -}, 'PendingGetBeacon is sent to the updated URL'); - -parallelPromiseTest(async t => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - - let beacon = new PendingGetBeacon('/0'); - - for (let i = 0; i < 10; i++) { - const transientUrl = `/${i}`; - beacon.setURL(transientUrl); - assert_equals(beacon.url, transientUrl); - } - beacon.setURL(url); - assert_equals(beacon.url, url); - - beacon.sendNow(); - - await expectBeacon(uuid, {count: 1}); -}, 'PendingGetBeacon is sent to the last updated URL'); diff --git a/testing/web-platform/tests/pending-beacon/pending_post_beacon-cors.tentative.https.window.js b/testing/web-platform/tests/pending-beacon/pending_post_beacon-cors.tentative.https.window.js deleted file mode 100644 index 01511d22c1..0000000000 --- a/testing/web-platform/tests/pending-beacon/pending_post_beacon-cors.tentative.https.window.js +++ /dev/null @@ -1,66 +0,0 @@ -// META: script=/common/get-host-info.sub.js -// META: script=/common/utils.js -// META: script=./resources/pending_beacon-helper.js - -'use strict'; - -const {HTTPS_ORIGIN, HTTPS_NOTSAMESITE_ORIGIN} = get_host_info(); -const SMALL_SIZE = 500; - -for (const dataType in BeaconDataType) { - postBeaconSendDataTest( - dataType, generatePayload(SMALL_SIZE), - `PendingPostBeacon[${dataType}]: same-origin`, - {urlOptions: {host: HTTPS_ORIGIN, expectOrigin: HTTPS_ORIGIN}}); - - postBeaconSendDataTest( - dataType, generatePayload(SMALL_SIZE), - `PendingPostBeacon[${dataType}]: cross-origin, ` + - `CORS-safelisted Content-Type`, - { - urlOptions: { - host: HTTPS_NOTSAMESITE_ORIGIN, - expectOrigin: HTTPS_ORIGIN, - } - }); - - postBeaconSendDataTest( - dataType, generatePayload(SMALL_SIZE), - `PendingPostBeacon[${dataType}]: cross-origin, ` + - 'CORS-safelisted Content-Type => ' + - 'non-CORS response (from redirect handler) ' + - 'should be rejected by browser', - { - expectCount: 0, - urlOptions: { - useRedirectHandler: true, - host: HTTPS_NOTSAMESITE_ORIGIN, - } - }); - - postBeaconSendDataTest( - dataType, generatePayload(SMALL_SIZE), - `PendingPostBeacon[${dataType}]: cross-origin, ` + - 'CORS-safelisted Content-Type => no cookie expected', - { - setCookie: 'test_beacon_cookie', - urlOptions: { - host: HTTPS_NOTSAMESITE_ORIGIN, - expectOrigin: HTTPS_ORIGIN, - expectCredentials: false, - } - }); -} - -postBeaconSendDataTest( - BeaconDataType.Blob, generatePayload(SMALL_SIZE), - 'PendingPostBeacon[Blob]: cross-origin, non-CORS-safelisted Content-Type' + - ' => preflight expected', - { - urlOptions: { - host: HTTPS_NOTSAMESITE_ORIGIN, - contentType: 'application/octet-stream', - expectOrigin: HTTPS_ORIGIN, - expectPreflight: true, - } - }); diff --git a/testing/web-platform/tests/pending-beacon/pending_post_beacon-sendwithdata.tentative.https.window.js b/testing/web-platform/tests/pending-beacon/pending_post_beacon-sendwithdata.tentative.https.window.js deleted file mode 100644 index 77e91479e8..0000000000 --- a/testing/web-platform/tests/pending-beacon/pending_post_beacon-sendwithdata.tentative.https.window.js +++ /dev/null @@ -1,43 +0,0 @@ -// META: script=/common/utils.js -// META: script=./resources/pending_beacon-helper.js - -'use strict'; - -// Test empty data. -for (const dataType in BeaconDataType) { - postBeaconSendDataTest( - dataType, '', `Sent empty ${dataType}, and server got no data.`, { - expectNoData: true, - }); -} - -// Test small payload. -for (const [dataType, skipCharset] of Object.entries( - BeaconDataTypeToSkipCharset)) { - postBeaconSendDataTest( - dataType, generateSequentialData(0, 1024, skipCharset), - 'Encoded and sent in POST request.'); -} - -// Test large payload. -for (const dataType in BeaconDataType) { - postBeaconSendDataTest( - dataType, generatePayload(65536), 'Sent out big data.'); -} - -test(() => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - let beacon = new PendingPostBeacon(url); - assert_throws_js(TypeError, () => beacon.setData(new ReadableStream())); -}, 'setData() does not support ReadableStream.'); - -test(() => { - const uuid = token(); - const url = generateSetBeaconURL(uuid); - let beacon = new PendingPostBeacon(url); - const formData = new FormData(); - formData.append('part1', 'value1'); - formData.append('part2', new Blob(['value2']), 'file.txt'); - assert_throws_js(RangeError, () => beacon.setData(formData)); -}, 'setData() does not support multi-parts data.'); diff --git a/testing/web-platform/tests/pending-beacon/resources/pending_beacon-helper.js b/testing/web-platform/tests/pending-beacon/resources/pending_beacon-helper.js deleted file mode 100644 index e7b6ea5cb6..0000000000 --- a/testing/web-platform/tests/pending-beacon/resources/pending_beacon-helper.js +++ /dev/null @@ -1,242 +0,0 @@ -'use strict'; - -const ROOT_NAME = 'pending-beacon'; - -function parallelPromiseTest(func, description) { - async_test((t) => { - Promise.resolve(func(t)).then(() => t.done()).catch(t.step_func((e) => { - throw e; - })); - }, description); -} - -const BeaconTypes = [ - {type: PendingPostBeacon, name: 'PendingPostBeacon', expectedMethod: 'POST'}, - {type: PendingGetBeacon, name: 'PendingGetBeacon', expectedMethod: 'GET'}, -]; - -/** @enum {string} */ -const BeaconDataType = { - String: 'String', - ArrayBuffer: 'ArrayBuffer', - FormData: 'FormData', - URLSearchParams: 'URLSearchParams', - Blob: 'Blob', - File: 'File', -}; - -/** @enum {string} */ -const BeaconDataTypeToSkipCharset = { - String: '', - ArrayBuffer: '', - FormData: '\n\r', // CRLF characters will be normalized by FormData - URLSearchParams: ';,/?:@&=+$', // reserved URI characters - Blob: '', - File: '', -}; - -const BEACON_PAYLOAD_KEY = 'payload'; - -// Creates beacon data of the given `dataType` from `data`. -// @param {string} data - A string representation of the beacon data. Note that -// it cannot contain UTF-16 surrogates for all `BeaconDataType` except BLOB. -// @param {BeaconDataType} dataType - must be one of `BeaconDataType`. -// @param {string} contentType - Request Content-Type. -function makeBeaconData(data, dataType, contentType) { - switch (dataType) { - case BeaconDataType.String: - return data; - case BeaconDataType.ArrayBuffer: - return new TextEncoder().encode(data).buffer; - case BeaconDataType.FormData: - const formData = new FormData(); - if (data.length > 0) { - formData.append(BEACON_PAYLOAD_KEY, data); - } - return formData; - case BeaconDataType.URLSearchParams: - if (data.length > 0) { - return new URLSearchParams(`${BEACON_PAYLOAD_KEY}=${data}`); - } - return new URLSearchParams(); - case BeaconDataType.Blob: { - const options = {type: contentType || undefined}; - return new Blob([data], options); - } - case BeaconDataType.File: { - const options = {type: contentType || 'text/plain'}; - return new File([data], 'file.txt', options); - } - default: - throw Error(`Unsupported beacon dataType: ${dataType}`); - } -} - -// Create a string of `end`-`begin` characters, with characters starting from -// UTF-16 code unit `begin` to `end`-1. -function generateSequentialData(begin, end, skip) { - const codeUnits = Array(end - begin).fill().map((el, i) => i + begin); - if (skip) { - return String.fromCharCode( - ...codeUnits.filter(c => !skip.includes(String.fromCharCode(c)))); - } - return String.fromCharCode(...codeUnits); -} - -function generatePayload(size) { - if (size == 0) { - return ''; - } - const prefix = String(size) + ':'; - if (size < prefix.length) { - return Array(size).fill('*').join(''); - } - if (size == prefix.length) { - return prefix; - } - - return prefix + Array(size - prefix.length).fill('*').join(''); -} - -function generateSetBeaconURL(uuid, options) { - const host = (options && options.host) || ''; - let url = `${host}/${ROOT_NAME}/resources/set_beacon.py?uuid=${uuid}`; - if (options) { - if (options.expectOrigin !== undefined) { - url = `${url}&expectOrigin=${options.expectOrigin}`; - } - if (options.expectPreflight !== undefined) { - url = `${url}&expectPreflight=${options.expectPreflight}`; - } - if (options.expectCredentials !== undefined) { - url = `${url}&expectCredentials=${options.expectCredentials}`; - } - - if (options.useRedirectHandler) { - const redirect = `${host}/common/redirect.py` + - `?location=${encodeURIComponent(url)}`; - url = redirect; - } - } - return url; -} - -async function poll(asyncFunc, expected) { - const maxRetries = 30; - const waitInterval = 100; // milliseconds. - const delay = ms => new Promise(res => setTimeout(res, ms)); - - let result = {data: []}; - for (let i = 0; i < maxRetries; i++) { - result = await asyncFunc(); - if (!expected(result)) { - await delay(waitInterval); - continue; - } - return result; - } - return result; -} - -// Waits until the `options.count` number of beacon data available from the -// server. Defaults to 1. -// If `options.data` is set, it will be used to compare with the data from the -// response. -async function expectBeacon(uuid, options) { - const expectedCount = - (options && options.count !== undefined) ? options.count : 1; - - const res = await poll( - async () => { - const res = await fetch( - `/${ROOT_NAME}/resources/get_beacon.py?uuid=${uuid}`, - {cache: 'no-store'}); - return await res.json(); - }, - (res) => { - if (expectedCount == 0) { - // If expecting no beacon, we should try to wait as long as possible. - // So always returning false here until `poll()` decides to terminate - // itself. - return false; - } - return res.data.length == expectedCount; - }); - if (!options || !options.data) { - assert_equals( - res.data.length, expectedCount, - 'Number of sent beacons does not match expected count:'); - return; - } - - if (expectedCount == 0) { - assert_equals( - res.data.length, 0, - 'Number of sent beacons does not match expected count:'); - return; - } - - const decoder = options && options.percentDecoded ? (s) => { - // application/x-www-form-urlencoded serializer encodes space as '+' - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent - s = s.replace(/\+/g, '%20'); - return decodeURIComponent(s); - } : (s) => s; - - assert_equals( - res.data.length, options.data.length, - `The size of beacon data ${ - res.data.length} from server does not match expected value ${ - options.data.length}.`); - for (let i = 0; i < options.data.length; i++) { - assert_equals( - decoder(res.data[i]), options.data[i], - 'The beacon data does not match expected value.'); - } -} - -function postBeaconSendDataTest(dataType, testData, description, options) { - parallelPromiseTest(async t => { - const expectNoData = options && options.expectNoData; - const expectCount = (options && options.expectCount !== undefined) ? - options.expectCount : - 1; - const uuid = token(); - const url = - generateSetBeaconURL(uuid, (options && options.urlOptions) || {}); - const beacon = new PendingPostBeacon(url); - assert_equals(beacon.method, 'POST', 'must be POST to call setData().'); - - if (options && options.setCookie) { - document.cookie = options.setCookie; - } - - beacon.setData(makeBeaconData( - testData, dataType, (options && options.contentType) || {})); - beacon.sendNow(); - - const expectedData = expectNoData ? null : testData; - const percentDecoded = - !expectNoData && dataType === BeaconDataType.URLSearchParams; - await expectBeacon(uuid, { - count: expectCount, - data: [expectedData], - percentDecoded: percentDecoded - }); - }, `PendingPostBeacon(${dataType}): ${description}`); -} - -function generateHTML(script) { - return `<!DOCTYPE html><body><script>${script}</script></body>`; -} - -// Loads `script` into an iframe and appends it to the current document. -// Returns the loaded iframe element. -async function loadScriptAsIframe(script) { - const iframe = document.createElement('iframe'); - iframe.srcdoc = generateHTML(script); - const iframeLoaded = new Promise(resolve => iframe.onload = resolve); - document.body.appendChild(iframe); - await iframeLoaded; - return iframe; -} diff --git a/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js b/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js index 42bda12919..2a313fe7b1 100644 --- a/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js +++ b/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-cross-origin-bfcache.tentative.window.js @@ -53,8 +53,7 @@ promise_test(async t => { 'url': null, 'src': rc1_child_url, 'id': 'test-id', - // Iframes that are generated by addIframe have an empty name. - 'name': '', + 'name': null, 'reasons': null, 'children': null }]); diff --git a/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-iframes-without-attributes.tentative.window.js b/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-iframes-without-attributes.tentative.window.js new file mode 100644 index 0000000000..cda0ac4394 --- /dev/null +++ b/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-iframes-without-attributes.tentative.window.js @@ -0,0 +1,103 @@ +// META: title=RemoteContextHelper navigation using BFCache +// META: script=./test-helper.js +// META: script=/common/dispatcher/dispatcher.js +// META: script=/common/get-host-info.sub.js +// META: script=/common/utils.js +// META: script=/html/browsers/browsing-the-web/back-forward-cache/resources/rc-helper.js +// META: script=/html/browsers/browsing-the-web/remote-context-helper/resources/remote-context-helper.js +// META: script=/websockets/constants.sub.js +// META: timeout=long + +'use strict'; + +// Ensure that empty attributes are reported as empty strings and missing +// attributes are reported as null. +promise_test(async t => { + const rcHelper = new RemoteContextHelper(); + // Open a window with noopener so that BFCache will work. + const rc1 = await rcHelper.addWindow( + /*config=*/ null, /*options=*/ {features: 'noopener'}); + const rc1_url = await rc1.executeScript(() => { + return location.href; + }); + // Add a cross-origin iframe. + const rc1_child = await rc1.addIframe( + /*extraConfig=*/ { + origin: 'HTTP_REMOTE_ORIGIN', + scripts: [], + headers: [], + }, + /*attributes=*/ {id: '', name: ''}, + ); + const rc2_child = await rc1.addIframe( + /*extraConfig=*/ { + origin: 'HTTP_REMOTE_ORIGIN', + scripts: [], + headers: [], + }, + /*attributes=*/ {}, + ); + const rc3_child = await rc1.addIframe( + /*extraConfig=*/ {}, + /*attributes=*/ {}, + ); + const rc4_child = await rc1.addIframe( + /*extraConfig=*/ {}, + /*attributes=*/ {id: '', name: ''}, + ); + // Use WebSocket to block BFCache. + await useWebSocket(rc1); + const rc1_child_url = await rc1_child.executeScript(() => { + return location.href; + }); + const rc2_child_url = await rc2_child.executeScript(() => { + return location.href; + }); + const rc3_child_url = await rc3_child.executeScript(() => { + return location.href; + }); + const rc4_child_url = await rc4_child.executeScript(() => { + return location.href; + }); + // Check the BFCache result and the reported reasons. + await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); + await assertNotRestoredReasonsEquals( + rc1, + /*url=*/ rc1_url, + /*src=*/ null, + /*id=*/ null, + /*name=*/ null, + /*reasons=*/[{'reason': 'websocket'}], + /*children=*/[{ + 'url': null, + 'src': rc1_child_url, + // Id and name should be empty. + 'id': '', + 'name': '', + 'reasons': null, + 'children': null + }, { + 'url': null, + 'src': rc2_child_url, + // Id and name should be null. + 'id': null, + 'name': null, + 'reasons': null, + 'children': null + },{ + 'url': rc3_child_url, + 'src': rc3_child_url, + // Id and name should be null. + 'id': null, + 'name': null, + 'reasons': [], + 'children': [] + }, { + 'url': rc4_child_url, + 'src': rc4_child_url, + 'id': '', + 'name': '', + 'reasons': [], + 'children': [] + }]); +});
\ No newline at end of file diff --git a/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-navigation-failure.tentative.window.js b/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-navigation-failure.tentative.window.js index 5812ebb2b3..faa7649bc3 100644 --- a/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-navigation-failure.tentative.window.js +++ b/testing/web-platform/tests/performance-timeline/not-restored-reasons/performance-navigation-timing-navigation-failure.tentative.window.js @@ -22,5 +22,5 @@ promise_test(async t => { // Check the BFCache result and the reported reasons. await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); - await assertNotRestoredFromBFCache(rc1, ['navigation-failure']); + await assertNotRestoredFromBFCache(rc1, ['response-status-not-ok']); });
\ No newline at end of file diff --git a/testing/web-platform/tests/performance-timeline/not-restored-reasons/test-helper.js b/testing/web-platform/tests/performance-timeline/not-restored-reasons/test-helper.js index 826b0ccb2b..ba9a4c0342 100644 --- a/testing/web-platform/tests/performance-timeline/not-restored-reasons/test-helper.js +++ b/testing/web-platform/tests/performance-timeline/not-restored-reasons/test-helper.js @@ -27,9 +27,9 @@ function assertReasonsStructEquals( } else { for (let j = 0; j < children.length; j++) { assertReasonsStructEquals( - result.children[0], children[0].url, - children[0].src, children[0].id, children[0].name, children[0].reasons, - children[0].children); + result.children[j], children[j].url, + children[j].src, children[j].id, children[j].name, children[j].reasons, + children[j].children); } } } diff --git a/testing/web-platform/tests/permissions-policy/payment-extension-allowed-by-permissions-policy-attribute.https.sub.html b/testing/web-platform/tests/permissions-policy/payment-extension-allowed-by-permissions-policy-attribute.https.sub.html index ef36bf97f1..d96bc5a0d1 100644 --- a/testing/web-platform/tests/permissions-policy/payment-extension-allowed-by-permissions-policy-attribute.https.sub.html +++ b/testing/web-platform/tests/permissions-policy/payment-extension-allowed-by-permissions-policy-attribute.https.sub.html @@ -13,7 +13,7 @@ promise_test(t => { return test_feature_availability_with_post_message_result( - t, cross_origin_src, "NotSupportedError#The 'payment' feature is not " + + t, cross_origin_src, "NotSupportedError#The 'payment' or 'publickey-credentials-create' features are not " + "enabled in this document. Permissions Policy may be used to " + "delegate Web Payment capabilities to cross-origin child frames."); }, feature_name + ' is not supported in cross-origin iframe without ' + header); diff --git a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment-extension.html b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment-extension.html index 86f0ea3e48..3f628ae25d 100644 --- a/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment-extension.html +++ b/testing/web-platform/tests/permissions-policy/resources/permissions-policy-payment-extension.html @@ -16,7 +16,7 @@ let authenticatorArgs = { window.onload = async function() { await window.test_driver.add_virtual_authenticator(authenticatorArgs); let enabled = true; - let message = `OK`; + let name = `OK`; try { const publicKey = { rp: { @@ -53,8 +53,8 @@ window.onload = async function() { }); } catch (e) { enabled = false; - message = e.name + '#' + e.message; + name = e.name + '#' + e.message; } - parent.postMessage({ type: 'availability-result', enabled, message }, '*'); + parent.postMessage({ type: 'availability-result', enabled, name }, '*'); } </script> diff --git a/testing/web-platform/tests/pointerevents/pointerevent_after_target_appended.html b/testing/web-platform/tests/pointerevents/pointerevent_after_target_appended.html index 5d73702aca..712670d647 100644 --- a/testing/web-platform/tests/pointerevents/pointerevent_after_target_appended.html +++ b/testing/web-platform/tests/pointerevents/pointerevent_after_target_appended.html @@ -1,5 +1,6 @@ <!DOCTYPE HTML> <title>Enter/leave events fired to parent after child is added</title> +<link rel="help" href="https://w3c.github.io/pointerevents/#firing-events-using-the-pointerevent-interface"> <meta name="variant" content="?mouse"> <meta name="variant" content="?touch"> <meta name="variant" content="?pen"> diff --git a/testing/web-platform/tests/pointerevents/pointerevent_after_target_removed.html b/testing/web-platform/tests/pointerevents/pointerevent_after_target_removed.html index 7b2a4eeb80..b63e8b92d1 100644 --- a/testing/web-platform/tests/pointerevents/pointerevent_after_target_removed.html +++ b/testing/web-platform/tests/pointerevents/pointerevent_after_target_removed.html @@ -1,4 +1,5 @@ <!DOCTYPE HTML> +<link rel="help" href="https://w3c.github.io/pointerevents/#firing-events-using-the-pointerevent-interface"> <title>Enter/leave events fired to parent after child is removed</title> <meta name="variant" content="?mouse"> <meta name="variant" content="?touch"> @@ -104,24 +105,12 @@ setup(); // Tests for dispatched pointer events. - addPromiseTest("pointerdown", "pointer", - pointer_type == "mouse" - // `pointerup` after removing the child should not cause `pointerover` - // on the parent if the pointer type is hoverable because pointer boundary - // events should be fired only when the hoverable pointer is actually - // moved. - ? [ - "pointerover@child", "pointerenter@parent", "pointerenter@child", - "pointerdown@child", "(child-removed)", "pointerup@parent", - "pointerover@parent", "pointerdown@parent", "pointerup@parent", - "pointerout@parent", "pointerleave@parent" - ] - : [ - "pointerover@child", "pointerenter@parent", "pointerenter@child", - "pointerdown@child", "(child-removed)", "pointerover@parent", "pointerup@parent", - "pointerdown@parent", "pointerup@parent", - "pointerout@parent", "pointerleave@parent" - ]); + addPromiseTest("pointerdown", "pointer", [ + "pointerover@child", "pointerenter@parent", "pointerenter@child", + "pointerdown@child", "(child-removed)", "pointerover@parent", "pointerup@parent", + "pointerdown@parent", "pointerup@parent", + "pointerout@parent", "pointerleave@parent" + ]); addPromiseTest("pointerup", "pointer", [ "pointerover@child", "pointerenter@parent", "pointerenter@child", "pointerdown@child", "pointerup@child", "(child-removed)", diff --git a/testing/web-platform/tests/pointerevents/pointerevent_pointermove_isprimary_same_as_pointerdown.html b/testing/web-platform/tests/pointerevents/pointerevent_pointermove_isprimary_same_as_pointerdown.html index 3073076a49..c8a10e8bb8 100644 --- a/testing/web-platform/tests/pointerevents/pointerevent_pointermove_isprimary_same_as_pointerdown.html +++ b/testing/web-platform/tests/pointerevents/pointerevent_pointermove_isprimary_same_as_pointerdown.html @@ -6,6 +6,8 @@ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/> <meta name="assert" content="The isPrimary attribute of a pointermove event must have the same value as the isPrimary attribute of the last pointerdown event with the same pointerId attribute."/> <link rel="stylesheet" type="text/css" href="pointerevent_styles.css"> + <meta name="variant" content="?mouse"> + <meta name="variant" content="?touch"> <!-- /resources/testharness.js --> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> @@ -22,45 +24,82 @@ // will fail unless the async_test is created with the var name "test_pointerEvent". add_completion_callback(showPointerTypes); - var pointermove_event = null; - var pointerdown_event = null; + var pointermove_events = new Map(); + var pointerdown_events = new Map(); function run() { var target0 = document.getElementById("target0"); var actions_promise; on_event(target0, "pointermove", function (event) { - if (pointerdown_event != null) { + let pointerdown_event = pointerdown_events.get(event.pointerId); + if (pointerdown_event) { test_pointerEvent.step(function () { assert_equals(event.pointerId, pointerdown_event.pointerId, "pointermove.pointerId should be the same as pointerdown.pointerId."); assert_equals(event.isPrimary, pointerdown_event.isPrimary, "pointermove.isPrimary should be the same as pointerdown.isPrimary."); }); - pointermove_event = event; + pointermove_events.set(event.pointerId, event); } }); on_event(target0, "pointerdown", function (event) { - pointerdown_event = event; + assert_equals(event.isPrimary, !!(pointerdown_events.size == 0), "pointerdown.isPrimary should only be true for first pointer."); + pointerdown_events.set(event.pointerId, event); detected_pointertypes[event.pointerType] = true; }); on_event(target0, "pointerup", function (event) { + let pointerdown_event = pointerdown_events.get(event.pointerId); test_pointerEvent.step(function () { - assert_not_equals(pointermove_event, null, "pointermove event was never received: "); - }); - // Make sure the test finishes after all the input actions are completed. - actions_promise.then( () => { - test_pointerEvent.done(); + assert_true(!!pointerdown_event, "pointerdown event was received."); + assert_true(!!pointermove_events.size, "pointermove event was received."); + assert_equals(event.pointerId, pointerdown_event.pointerId, "pointerup.pointerId should be the same as pointerdown.pointerId."); + assert_equals(event.isPrimary, pointerdown_event.isPrimary, "pointerup.isPrimary should be the same as pointerdown.isPrimary."); }); + pointermove_events.delete(event.pointerId); + pointerdown_events.delete(event.pointerId); + if (pointerdown_events.size == 0) { + // Make sure the test finishes after all the input actions are completed. + actions_promise.then(() => { + test_pointerEvent.done(); + }); + } }); - // Dispatch a mouse drag in target0. - actions_promise = new test_driver.Actions() - .pointerMove(0, 0, {origin: target0}) - .pointerDown() - .pointerMove(3, 3, {origin: target0}) - .pointerUp() - .send(); + // Dispatch a mouse/touch drag in target0. + var pointerType = location.search.substring(1); + switch (pointerType) { + case "mouse": + actions_promise = new test_driver.Actions() + .pointerMove(0, 0, {origin: target0}) + .pointerDown() + .pointerMove(3, 3, {origin: target0}) + .pointerUp() + .send(); + break; + case "touch": + actions_promise = new test_driver.Actions() + .addPointer("touchPointer1", "touch") + .pointerMove(0, 0, {origin: target0, sourceName: "touchPointer1"}) + .pointerDown({sourceName: "touchPointer1"}) + .pointerMove(3, 3, {origin: target0, sourceName: "touchPointer1"}) + .addPointer("touchPointer2", "touch") + .pointerMove(0, 0, {origin: target0, sourceName: "touchPointer2"}) + .pointerDown({sourceName: "touchPointer2"}) + .pointerMove(5, 5, {origin: target0, sourceName: "touchPointer2"}) + .addPointer("touchPointer3", "touch") + .pointerMove(0, 0, {origin: target0, sourceName: "touchPointer3"}) + .pointerDown({sourceName: "touchPointer3"}) + .pointerMove(7, 7, {origin: target0, sourceName: "touchPointer3"}) + .pointerUp({sourceName: "touchPointer3"}) + .pointerUp({sourceName: "touchPointer1"}) + .pointerUp({sourceName: "touchPointer2"}) + .send(); + break; + default: + assert_true(false, `does support testing ${pointerType} input`); + break; + } } </script> </head> diff --git a/testing/web-platform/tests/pointerlock/WEB_FEATURES.yml b/testing/web-platform/tests/pointerlock/WEB_FEATURES.yml new file mode 100644 index 0000000000..61ab30b26b --- /dev/null +++ b/testing/web-platform/tests/pointerlock/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: pointer-lock + files: "**" diff --git a/testing/web-platform/tests/preload/resources/A4.ogv b/testing/web-platform/tests/preload/resources/A4.ogv Binary files differdeleted file mode 100644 index de99616ece..0000000000 --- a/testing/web-platform/tests/preload/resources/A4.ogv +++ /dev/null diff --git a/testing/web-platform/tests/preload/supported-as-values.html b/testing/web-platform/tests/preload/supported-as-values.html new file mode 100644 index 0000000000..34abf80410 --- /dev/null +++ b/testing/web-platform/tests/preload/supported-as-values.html @@ -0,0 +1,44 @@ +<!DOCTYPE html> +<html> +<title>Test the supported value for <link rel=preload as="..."></title> +<meta name="timeout" content="long"> +<meta name="variant" content="?as=image&expected=1"> +<meta name="variant" content="?as=fetch&expected=1"> +<meta name="variant" content="?as=font&expected=1"> +<meta name="variant" content="?as=script&expected=1"> +<meta name="variant" content="?as=style&expected=1"> +<meta name="variant" content="?as=track&expected=1"> + +<meta name="variant" content="?as=garbagefoobar&expected=0"> +<meta name="variant" content="?as=video&expected=0"> +<meta name="variant" content="?as=audio&expected=0"> +<meta name="variant" content="?as=object&expected=0"> +<meta name="variant" content="?as=iframe&expected=0"> +<meta name="variant" content="?as=worklet&expected=0"> +<meta name="variant" content="?as=json&expected=0"> + +<script src="/common/utils.js"></script> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<script> + const params = new URLSearchParams(location.search); + const as = params.get("as"); + const expected = Number(params.get("expected")); + promise_test(async t => { + const link = document.createElement("link"); + link.href = new URL("/common/echo.py?content=nothing", location.href).href; + link.rel = "preload"; + link.as = as; + document.head.append(link); + await new Promise(resolve => { + t.step_timeout(resolve, 1000); + link.addEventListener("load", resolve); + link.addEventListener("error", resolve); + }); + const resources = performance.getEntriesByName(link.href); + assert_equals(resources.length, expected); + }); +</script> +</body> +</html> diff --git a/testing/web-platform/tests/remote-playback/watch-availability-callback-parameter.html b/testing/web-platform/tests/remote-playback/watch-availability-callback-parameter.html index fe407a9c03..df95885393 100644 --- a/testing/web-platform/tests/remote-playback/watch-availability-callback-parameter.html +++ b/testing/web-platform/tests/remote-playback/watch-availability-callback-parameter.html @@ -14,7 +14,7 @@ v.remote .watchAvailability( - t.step_func_done(() => assert_true(typeof available === "boolean")) + t.step_func_done(available => assert_true(typeof available === "boolean")) ) .then( t.step_func(() => {}), diff --git a/testing/web-platform/tests/resource-timing/initiator-type/script.html b/testing/web-platform/tests/resource-timing/initiator-type/script.html index dbd6a131de..6e9e3ae7ee 100644 --- a/testing/web-platform/tests/resource-timing/initiator-type/script.html +++ b/testing/web-platform/tests/resource-timing/initiator-type/script.html @@ -12,6 +12,7 @@ </head> <body> <script src="/resource-timing/resources/empty_script.js"></script> +<script type="module" src="/resource-timing/resources/parent_script.js"></script> <script> const async_xhr = new XMLHttpRequest; async_xhr.open('GET', '/resource-timing/resources/blue.png?id=async_xhr', @@ -21,6 +22,7 @@ <script> initiator_type_test("empty_script.js", "script", "<script>"); initiator_type_test("blue.png?id=async_xhr", "xmlhttprequest", "an asynchronous XmlHTTPRequest"); + initiator_type_test("child.js", "script", "script imported from another script"); </script> </body> </html> diff --git a/testing/web-platform/tests/resource-timing/initiator-type/video.html b/testing/web-platform/tests/resource-timing/initiator-type/video.html index 16f3b3dea5..2d8c9dcc47 100644 --- a/testing/web-platform/tests/resource-timing/initiator-type/video.html +++ b/testing/web-platform/tests/resource-timing/initiator-type/video.html @@ -19,14 +19,14 @@ src="/resource-timing/resources/empty.py?id=track"> </video> <video autoplay="true"> - <source src="/media/test.ogv?id=source-ogv" type="video/ogg"> + <source src="/media/test.webm?id=source-webm" type="video/webm"> </video> <script> initiator_type_test("blue.png?id=poster", "video", "<video poster>"); initiator_type_test("media/test.mp4?id=src", "video", "<video src>"); initiator_type_test("media/test.mp4?id=source-mp4", "video", "<source src> with type=\"video/mp4\""); initiator_type_test("empty.py?id=track", "track", "<track src>"); - initiator_type_test("media/test.ogv?id=source-ogv", "video", "<source src> with type=\"video/ogg\""); + initiator_type_test("media/test.webm?id=source-webm", "video", "<source src> with type=\"video/webm\""); </script> </body> </html> diff --git a/testing/web-platform/tests/resource-timing/resources/child_script.js b/testing/web-platform/tests/resource-timing/resources/child_script.js new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/testing/web-platform/tests/resource-timing/resources/child_script.js @@ -0,0 +1 @@ + diff --git a/testing/web-platform/tests/resource-timing/resources/parent_script.js b/testing/web-platform/tests/resource-timing/resources/parent_script.js new file mode 100644 index 0000000000..01ec0c5823 --- /dev/null +++ b/testing/web-platform/tests/resource-timing/resources/parent_script.js @@ -0,0 +1 @@ +import './child.js'; diff --git a/testing/web-platform/tests/resources/chromium/mock-pressure-service.js b/testing/web-platform/tests/resources/chromium/mock-pressure-service.js index 02d10f856a..bd0e32c567 100644 --- a/testing/web-platform/tests/resources/chromium/mock-pressure-service.js +++ b/testing/web-platform/tests/resources/chromium/mock-pressure-service.js @@ -58,8 +58,8 @@ class MockPressureService { return {status: this.pressureStatus_}; } - startPlatformCollector(sampleRate) { - if (sampleRate === 0) + startPlatformCollector(sampleInterval) { + if (sampleInterval === 0) return; if (this.pressureServiceReadingTimerId_ != null) @@ -80,7 +80,6 @@ class MockPressureService { // |epochDeltaInMs| equals to base::Time::kTimeTToMicrosecondsOffset. const epochDeltaInMs = unixEpoch - windowsEpoch; - const timeout = (1 / sampleRate) * 1000; this.pressureServiceReadingTimerId_ = self.setInterval(() => { if (this.pressureUpdate_ === null || this.observers_.length === 0) return; @@ -90,7 +89,7 @@ class MockPressureService { for (let observer of this.observers_) observer.onPressureUpdated(this.pressureUpdate_); this.updatesDelivered_++; - }, timeout); + }, sampleInterval); } stopPlatformCollector() { diff --git a/testing/web-platform/tests/screen-wake-lock/META.yml b/testing/web-platform/tests/screen-wake-lock/META.yml index b311993d82..78d6dfcbd4 100644 --- a/testing/web-platform/tests/screen-wake-lock/META.yml +++ b/testing/web-platform/tests/screen-wake-lock/META.yml @@ -1,6 +1,5 @@ spec: https://w3c.github.io/screen-wake-lock/ suggested_reviewers: - - Honry - marcoscaceres - rakuco - reillyeon diff --git a/testing/web-platform/tests/screen-wake-lock/wakelock-disabled-by-permissions-policy.https.html b/testing/web-platform/tests/screen-wake-lock/wakelock-disabled-by-permissions-policy.https.html index 354b0a5e15..fd1e6db656 100644 --- a/testing/web-platform/tests/screen-wake-lock/wakelock-disabled-by-permissions-policy.https.html +++ b/testing/web-platform/tests/screen-wake-lock/wakelock-disabled-by-permissions-policy.https.html @@ -25,7 +25,8 @@ 'navigator.wakeLock.request("screen")', t, same_origin_src, - expect_feature_unavailable_default + expect_feature_unavailable_default, + 'screen-wake-lock', ); }, `${header} disallows same-origin iframes.`); @@ -34,7 +35,8 @@ 'navigator.wakeLock.request("screen")', t, cross_origin_src, - expect_feature_unavailable_default + expect_feature_unavailable_default, + 'screen-wake-lock' ); }, `${header} disallows cross-origin iframes.`); </script> diff --git a/testing/web-platform/tests/screen-wake-lock/wakelock-enabled-on-self-origin-by-permissions-policy.https.html b/testing/web-platform/tests/screen-wake-lock/wakelock-enabled-on-self-origin-by-permissions-policy.https.html index 5b90b4f4ce..82ed6d3f78 100644 --- a/testing/web-platform/tests/screen-wake-lock/wakelock-enabled-on-self-origin-by-permissions-policy.https.html +++ b/testing/web-platform/tests/screen-wake-lock/wakelock-enabled-on-self-origin-by-permissions-policy.https.html @@ -37,7 +37,8 @@ 'navigator.wakeLock.request("screen")', t, cross_origin_src, - expect_feature_unavailable_default + expect_feature_unavailable_default, + 'screen-wake-lock' ); }, `${header} disallows cross-origin iframes.`); </script> diff --git a/testing/web-platform/tests/scroll-animations/WEB_FEATURES.yml b/testing/web-platform/tests/scroll-animations/WEB_FEATURES.yml new file mode 100644 index 0000000000..adf4bf2ac1 --- /dev/null +++ b/testing/web-platform/tests/scroll-animations/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: scroll-driven-animations + files: "**" diff --git a/testing/web-platform/tests/scroll-animations/scroll-timelines/setting-timeline.tentative.html b/testing/web-platform/tests/scroll-animations/scroll-timelines/setting-timeline.tentative.html index 5813de60fa..5502e13367 100644 --- a/testing/web-platform/tests/scroll-animations/scroll-timelines/setting-timeline.tentative.html +++ b/testing/web-platform/tests/scroll-animations/scroll-timelines/setting-timeline.tentative.html @@ -98,17 +98,6 @@ function assert_paused_times(animation, timeline_current_time, } } -function createViewTimeline(t) { - const parent = document.querySelector('.scroller'); - const elem = document.createElement('div'); - elem.id = 'target'; - t.add_cleanup(() => { - elem.remove(); - }); - parent.appendChild(elem); - return new ViewTimeline({ subject: elem }); -} - promise_test(async t => { const scrollTimeline = createScrollTimeline(t); await updateScrollPosition(scrollTimeline, 100); diff --git a/testing/web-platform/tests/scroll-animations/scroll-timelines/testcommon.js b/testing/web-platform/tests/scroll-animations/scroll-timelines/testcommon.js index 97e81f494c..88021409f1 100644 --- a/testing/web-platform/tests/scroll-animations/scroll-timelines/testcommon.js +++ b/testing/web-platform/tests/scroll-animations/scroll-timelines/testcommon.js @@ -85,6 +85,23 @@ function createScrollLinkedAnimationWithTiming(test, timing, timeline) { new KeyframeEffect(createDiv(test), KEYFRAMES, timing), timeline); } +function createViewTimeline(t) { + const parent = document.querySelector('.scroller'); + const elem = document.createElement('div'); + elem.id = 'target'; + t.add_cleanup(() => { + elem.remove(); + }); + parent.appendChild(elem); + return new ViewTimeline({ subject: elem }); +} + +function createAnimation(t) { + const elem = createDiv(t); + const animation = elem.animate({ opacity: [1, 0] }, 1000); + return animation; +} + function assert_approx_equals_or_null(actual, expected, tolerance, name) { if (actual === null || expected === null){ assert_equals(actual, expected, name); diff --git a/testing/web-platform/tests/selection/crashtests/selection-details-editor-ui.html b/testing/web-platform/tests/selection/crashtests/selection-details-editor-ui.html new file mode 100644 index 0000000000..444306207a --- /dev/null +++ b/testing/web-platform/tests/selection/crashtests/selection-details-editor-ui.html @@ -0,0 +1,19 @@ +<!doctype html> +<meta charset=utf-8> +<style> +*:nth-child(odd) { + display: table-header-group; +} +</style> +<script> +document.addEventListener("DOMContentLoaded", () => { + document.execCommand("enableObjectResizing", false) + document.addEventListener("selectionchange", () => { + document.execCommand("insertImage", false, "x") + document.execCommand("selectAll", false) + }, { once: true }); + document.getSelection().collapse(a) +}) +</script> +<details open="" contenteditable="true"> +<details id="a"> diff --git a/testing/web-platform/tests/selection/crashtests/selection-modify-line-next-to-input-and-make-it-invisible.html b/testing/web-platform/tests/selection/crashtests/selection-modify-line-next-to-input-and-make-it-invisible.html new file mode 100644 index 0000000000..09e018f581 --- /dev/null +++ b/testing/web-platform/tests/selection/crashtests/selection-modify-line-next-to-input-and-make-it-invisible.html @@ -0,0 +1,23 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<script> +document.addEventListener("DOMContentLoaded", () => { + getSelection().addRange(document.createRange()); + const range = document.createRange(); + range.selectNode(document.querySelector("meter")); + getSelection().addRange(range); + getSelection().modify('move', 'left', 'line'); + document.querySelector("input").style.display = "none"; + getSelection().modify('move', 'left', 'lineboundary'); +}) +</script> +</head> +<body> +<input> +<div contenteditable> +<meter> +</div> +</body> +</html> diff --git a/testing/web-platform/tests/selection/onselectionchange-on-distinct-text-controls.html b/testing/web-platform/tests/selection/onselectionchange-on-distinct-text-controls.html new file mode 100644 index 0000000000..ee26928699 --- /dev/null +++ b/testing/web-platform/tests/selection/onselectionchange-on-distinct-text-controls.html @@ -0,0 +1,47 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="help" href="https://w3c.github.io/selection-api/#selectionchange-event"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<input id="input1" value="hello"> +<input id="input2" value="world"> +<textarea id="textarea1">hello</textarea> +<textarea id="textarea2">world</textarea> +<script> + +promise_test(() => { + return (async function() { + let selectionChangeCount1 = 0; + let selectionChangeCount2 = 0; + input1.addEventListener("selectionchange", () => ++selectionChangeCount1); + input2.addEventListener("selectionchange", () => ++selectionChangeCount2); + input1.setSelectionRange(1, 2); + input1.setSelectionRange(2, 3); + input2.setSelectionRange(1, 3); + assert_equals(selectionChangeCount1, 0); + assert_equals(selectionChangeCount2, 0); + await new Promise(setTimeout); + assert_equals(selectionChangeCount1, 1); + assert_equals(selectionChangeCount2, 1); + })(); +}, "selectionchange event on each input element fires independently"); + +promise_test(() => { + return (async function() { + let selectionChangeCount1 = 0; + let selectionChangeCount2 = 0; + textarea1.addEventListener("selectionchange", () => ++selectionChangeCount1); + textarea2.addEventListener("selectionchange", () => ++selectionChangeCount2); + textarea1.setSelectionRange(1, 2); + textarea1.setSelectionRange(2, 3); + textarea2.setSelectionRange(1, 3); + assert_equals(selectionChangeCount1, 0); + assert_equals(selectionChangeCount2, 0); + await new Promise(setTimeout); + assert_equals(selectionChangeCount1, 1); + assert_equals(selectionChangeCount2, 1); + })(); +}, "selectionchange event on each textarea element fires independently"); + +</script> diff --git a/testing/web-platform/tests/selection/onselectionchange-on-document.html b/testing/web-platform/tests/selection/onselectionchange-on-document.html new file mode 100644 index 0000000000..4e06165377 --- /dev/null +++ b/testing/web-platform/tests/selection/onselectionchange-on-document.html @@ -0,0 +1,73 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<link rel="help" href="https://w3c.github.io/selection-api/#selectionchange-event"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<body> +<div id="container"><br><br></div> +<script> + +promise_test(() => { + return new Promise(resolve => { + let didFireSelectionChangeEvent = false; + document.addEventListener("selectionchange", () => { didFireSelectionChangeEvent = true; resolve(); }, {once: true}); + getSelection().setPosition(container, 0); + assert_false(didFireSelectionChangeEvent); + }); +}, "selectionchange event on document fires"); + +promise_test(() => { + return (async function() { + let selectionChangeCount = 0; + document.addEventListener("selectionchange", () => ++selectionChangeCount); + container.innerHTML = '<span><br></span><span><br></span>'; + getSelection().setPosition(container, 0); + assert_equals(selectionChangeCount, 0); + getSelection().setPosition(container, 2); + assert_equals(selectionChangeCount, 0); + await new Promise(setTimeout); + assert_equals(selectionChangeCount, 1); + })(); +}, "selectionchange event on document fires once"); + +promise_test(() => { + return (async function() { + let selectionChangeCount = 0; + document.addEventListener("selectionchange", () => ++selectionChangeCount); + container.innerHTML = '<span><br></span><span><br></span>'; + getSelection().setPosition(container, 0); + assert_equals(selectionChangeCount, 0); + getSelection().setPosition(container, 2); + assert_equals(selectionChangeCount, 0); + await new Promise(setTimeout); + assert_equals(selectionChangeCount, 1); + getSelection().setPosition(container, 0); + assert_equals(selectionChangeCount, 1); + getSelection().setPosition(container, 2); + assert_equals(selectionChangeCount, 1); + await new Promise(setTimeout); + assert_equals(selectionChangeCount, 2); + })(); +}, "task to fire selectionchange event gets queued each time selection is mutated"); + +promise_test(() => { + return (async function() { + let selectionChangeCount = 0; + document.addEventListener("selectionchange", () => { + if (!selectionChangeCount) { + getSelection().setPosition(container, 2); + getSelection().setPosition(container, 0); + } + ++selectionChangeCount; + }); + container.innerHTML = '<b><br></b><b><br></b>'; + getSelection().setPosition(container, 0); + assert_equals(selectionChangeCount, 0); + await new Promise(setTimeout); + assert_equals(selectionChangeCount, 1); + await new Promise(setTimeout); + assert_equals(selectionChangeCount, 2); + })(); +}, "has scheduled selectionchange event is set to false at the beginning of a task to fire selectionchange event"); + +</script> diff --git a/testing/web-platform/tests/selection/selection-nested-video.html b/testing/web-platform/tests/selection/selection-nested-video.html new file mode 100644 index 0000000000..9777d7d992 --- /dev/null +++ b/testing/web-platform/tests/selection/selection-nested-video.html @@ -0,0 +1,25 @@ +<!doctype html> +<meta charset=utf-8> +<title>Selection with nested videos doesn't crash</title> +<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1887963"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> +async_test(t => { + document.addEventListener("DOMContentLoaded", () => { + let c = a.attachShadow({mode: "open"}); + const sel = window.getSelection(); + + sel.setBaseAndExtent(b, 0, c, 0); + + assert_equals(sel.anchorNode, b); + assert_equals(sel.anchorOffset, 0); + assert_equals(sel.focusNode, b); + assert_equals(sel.focusOffset, 0); + t.done(); + }) +}); +</script> +<div id="a">A</div> +<video> +<video id="b"> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-1-ref.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-1-ref.html new file mode 100644 index 0000000000..9cc7affd67 --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-1-ref.html @@ -0,0 +1,6 @@ +<!doctype html> +OuterText +<div>innerText</div> +<script> + getSelection().selectAllChildren(document.body); +</script> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-1.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-1.html new file mode 100644 index 0000000000..58d7e9f8e4 --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-1.html @@ -0,0 +1,10 @@ +<!doctype html> +<head> +<link rel="match" href="cross-shadow-boundary-1-ref.html"/> +</head> +OuterText +<div id="host"></div> +<script> + document.getElementById("host").attachShadow({mode: "open"}).innerHTML = "innerText"; + getSelection().selectAllChildren(document.body); +</script> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-2-ref.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-2-ref.html new file mode 100644 index 0000000000..c9e0068c51 --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-2-ref.html @@ -0,0 +1,7 @@ +<!doctype html> +OuterText +<div>innerText</div> +OuterText +<script> + getSelection().selectAllChildren(document.body); +</script> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-2.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-2.html new file mode 100644 index 0000000000..e0d3a14f48 --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-2.html @@ -0,0 +1,11 @@ +<!doctype html> +<head> +<link rel="match" href="cross-shadow-boundary-2-ref.html" /> +</head> +OuterText +<div id="host"></div> +OuterText +<script> + document.getElementById("host").attachShadow({ mode: "open" }).innerHTML = "innerText"; + getSelection().selectAllChildren(document.body); +</script> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-3-ref.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-3-ref.html new file mode 100644 index 0000000000..189ab35c41 --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-3-ref.html @@ -0,0 +1,12 @@ +<!doctype html> +OuterText +<div id="host1">innerText1</div> +OuterText +<div id="host2">innerText2</div> +<script> + const host1 = document.getElementById("host1"); + const host2 = document.getElementById("host2"); + + getSelection().setBaseAndExtent( + host1.firstChild, 3, host2.firstChild, 3); +</script> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-3.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-3.html new file mode 100644 index 0000000000..3eb2ab37b1 --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-3.html @@ -0,0 +1,17 @@ +<!doctype html> +<head> +<link rel="match" href="cross-shadow-boundary-3-ref.html" /> +</head> +OuterText +<div id="host1"></div> +OuterText +<div id="host2"></div> +<script> + const root1 = document.getElementById("host1").attachShadow({ mode: "open" }); + root1.innerHTML = "innerText1"; + + const root2 = document.getElementById("host2").attachShadow({ mode: "open" }); + root2.innerHTML = "innerText2"; + + getSelection().setBaseAndExtent(root1.firstChild, 3, root2.firstChild, 3); +</script> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-4.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-4.html new file mode 100644 index 0000000000..a93bf77aaa --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-4.html @@ -0,0 +1,21 @@ +<!doctype html> +<head> +<!--Intentionally to use cross-shadow-boundary-3-ref.html here--> +<link rel=match href="cross-shadow-boundary-3-ref.html"> +</head> +OuterText +<div id="host1"></div> +OuterText +<div id="host2"></div> +<script> + const root1 = document.getElementById("host1").attachShadow({ mode: "open" }); + root1.innerHTML = "innerText1"; + + const root2 = document.getElementById("host2").attachShadow({ mode: "open" }); + root2.innerHTML = "<div></div>"; + + const root3 = root2.querySelector("div").attachShadow({ mode: "open" }); + root3.innerHTML = "innerText2"; + + getSelection().setBaseAndExtent(root1.firstChild, 3, root3.firstChild, 3); +</script> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-5-ref.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-5-ref.html new file mode 100644 index 0000000000..2abfd911ec --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-5-ref.html @@ -0,0 +1,13 @@ +<!doctype html> +OuterText1 +<div>innerText1</div> +OuterText2 +<div>innerText2</div> +OuterText3 +<script> + getSelection().setBaseAndExtent( + document.body.firstChild, + 3, + document.body.childNodes[4], + 3); +</script> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-5.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-5.html new file mode 100644 index 0000000000..75c0525ade --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-5.html @@ -0,0 +1,20 @@ +<!doctype html> +<head> +<link rel=match href="cross-shadow-boundary-5-ref.html"> +</head> +OuterText1 +<div id="host1"></div> +OuterText2 +<div id="host2"></div> +OuterText3 +<script> + const root1 = document.getElementById("host1").attachShadow({ mode: "open" }); + root1.innerHTML = "innerText1"; + + const root2 = document.getElementById("host2").attachShadow({ mode: "open" }); + root2.innerHTML = "<div></div>"; + + const root3 = root2.querySelector("div").attachShadow({ mode: "open" }); + root3.innerHTML = "innerText2"; + getSelection().setBaseAndExtent(document.body.firstChild, 3, document.body.childNodes[4], 3); +</script> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-6-ref.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-6-ref.html new file mode 100644 index 0000000000..b7441c7bbc --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-6-ref.html @@ -0,0 +1,12 @@ +<!doctype html> +<span id="span">Start +<div> + <span id="inner1">inner1</p> + <span id="inner2">inner2</p> +</div> +</span> +<script> + const start = document.getElementById("span").firstChild; + const end = document.getElementById("inner2"); + window.getSelection().setBaseAndExtent(start, 3, end.firstChild, 3); +</script> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-6.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-6.html new file mode 100644 index 0000000000..cc264a6668 --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-6.html @@ -0,0 +1,44 @@ +<!doctype html> +<html class="reftest-wait"> +<head> +<link rel=match href="cross-shadow-boundary-6-ref.html"> +</head> +<span id="span">Start +<div> + <template shadowrootmode="open"> + <span id="inner1">inner1</p> + <span id="inner2">inner2</p> + </template> +</div> +</span> +<script> + const start = document.getElementById("span").firstChild; + const end = document.querySelector('div').shadowRoot.getElementById("inner2"); + + async function waitForRAFs() { + return new Promise(resolve => { + window.requestAnimationFrame(() => { + window.requestAnimationFrame(() => { + window.requestAnimationFrame(() => { + resolve(); + }); + }); + }); + }); + } + + async function runTest() { + window.getSelection().setBaseAndExtent(start, 3, end.firstChild, 3); + await waitForRAFs(); + + window.getSelection().removeAllRanges(); + await waitForRAFs(); + + window.getSelection().setBaseAndExtent(start, 3, end.firstChild, 3); + await waitForRAFs(); + document.documentElement.className = ""; + } + + runTest(); +</script> +</html> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-img-ref.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-img-ref.html new file mode 100644 index 0000000000..7f3b03ace6 --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-img-ref.html @@ -0,0 +1,9 @@ +<!doctype html> +OuterText1 +<div>innerText1</div> +OuterText2 +<div><img style="width: 10px; height: 10px; background-color: black"></img></div> +OuterText3 +<script> + getSelection().setBaseAndExtent(document.body.firstChild, 3, document.body.childNodes[4], 3); +</script> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-img.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-img.html new file mode 100644 index 0000000000..46e8d70833 --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-img.html @@ -0,0 +1,26 @@ +<!doctype html> +<head> +<link rel=match href="cross-shadow-boundary-img-ref.html"> +</head> +OuterText1 +<div id="host1"></div> +OuterText2 +<div id="host2"></div> +OuterText3 +<script> + const root1 = document.getElementById("host1").attachShadow({ mode: "open" }); + root1.innerHTML = "innerText1"; + + const root2 = document.getElementById("host2").attachShadow({ mode: "open" }); + root2.innerHTML = "<div></div>"; + + const root3 = root2.querySelector("div").attachShadow({ mode: "open" }); + root3.innerHTML = "<img>"; + + const img = root3.querySelector("img"); + img.style.width = "10px"; + img.style.height = "10px"; + img.style.backgroundColor = "black"; + + getSelection().setBaseAndExtent(document.body.firstChild, 3, document.body.childNodes[4], 3); +</script> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-select-document-ref.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-select-document-ref.html new file mode 100644 index 0000000000..0312000bc0 --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-select-document-ref.html @@ -0,0 +1,9 @@ +<!doctype html> +<html> + <div>CONTENT</div> + <script> + const div = document.querySelector("div"); + getSelection().setBaseAndExtent(div.firstChild, 0, div.firstChild, 2); + </script> +</html> + diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-select-document.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-select-document.html new file mode 100644 index 0000000000..9eb298abbf --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-select-document.html @@ -0,0 +1,12 @@ +<!doctype html> +<html> + <head> + <link rel=match href="cross-shadow-boundary-select-document-ref.html"> + </head> + <div></div> + <script> + const root = document.querySelector("div").attachShadow({mode: "open"}); + root.innerHTML = "CONTENT"; + getSelection().setBaseAndExtent(document, 0, root.firstChild, 2); + </script> +</html> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-select-root-ref.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-select-root-ref.html new file mode 100644 index 0000000000..fe74406acd --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-select-root-ref.html @@ -0,0 +1,11 @@ +<!doctype html> +<html> + <body> + <div id="outerText">OuterText1</div> + <div>InnerText1</div> + <div>OuterText2</div> + <div id="host">InnerText2</div> + <script> + window.getSelection().setBaseAndExtent(outerText, 0, host, host.childNodes.length); + </script> +</body></html> diff --git a/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-select-root.html b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-select-root.html new file mode 100644 index 0000000000..f64dd0f3b0 --- /dev/null +++ b/testing/web-platform/tests/selection/shadow-dom/cross-shadow-boundary-select-root.html @@ -0,0 +1,24 @@ +<!doctype html> +<head> +<link rel=match href="cross-shadow-boundary-select-root-ref.html"> +</head> +<div id="outerText1">OuterText1</div> +<div id="host1"></div> +<div id="outerText2">OuterText2</div> +<div id="host2"></div> +<div id="host3"></div> +<script> + const outerText1 = document.getElementById("outerText1"); + const outerText2 = document.getElementById("outerText2"); + + const host1 = document.getElementById("host1"); + const root1 = host1.attachShadow({mode: "open"}); + root1.innerHTML = "InnerText1"; + + const host2 = document.getElementById("host2"); + const root2 = host2.attachShadow({mode: "open"}); + root2.innerHTML = "InnerText2"; + + getSelection().setBaseAndExtent(outerText1, 0, root2, root2.childNodes.length); +</script> + diff --git a/testing/web-platform/tests/selection/textcontrols/selectionchange.html b/testing/web-platform/tests/selection/textcontrols/selectionchange.html index 2b43cfe44b..1c4ddf2b9b 100644 --- a/testing/web-platform/tests/selection/textcontrols/selectionchange.html +++ b/testing/web-platform/tests/selection/textcontrols/selectionchange.html @@ -184,7 +184,7 @@ target.setRangeText("foo", 2, 6); await data.assert_empty_spin(); - assert_equals(collector.events.length, 2); + assert_equals(collector.events.length, 1); }, `Calling setRangeText() after select() on ${name}`); promise_test(async () => { @@ -196,7 +196,7 @@ target.setRangeText("", 10, 12); await data.assert_empty_spin(); - assert_equals(collector.events.length, 4); + assert_equals(collector.events.length, 1); }, `Calling setRangeText() repeatedly on ${name}`); promise_test(async () => { diff --git a/testing/web-platform/tests/server-timing/WEB_FEATURES.yml b/testing/web-platform/tests/server-timing/WEB_FEATURES.yml new file mode 100644 index 0000000000..8b00e934ea --- /dev/null +++ b/testing/web-platform/tests/server-timing/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: server-timing + files: "**" diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/router-rules.js b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/router-rules.js index c3aef4272f..014cd2ec95 100644 --- a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/router-rules.js +++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/router-rules.js @@ -1,9 +1,14 @@ +const TEST_CACHE_NAME = 'v1'; + const routerRules = { 'condition-urlpattern-constructed-source-network': [{ condition: {urlPattern: new URLPattern({pathname: '/**/direct.txt'})}, source: 'network' }], - 'condition-urlpattern-urlpatterninit-source-network': [ + 'condition-urlpattern-constructed-match-all-source-cache': [ + {condition: {urlPattern: new URLPattern({})}, source: 'cache'}, + ], + 'condition-urlpattern-urlpatterncompatible-source-network': [ {condition: {urlPattern: {pathname: '/**/direct.txt'}}, source: 'network'}, ], 'condition-urlpattern-string-source-network': [ @@ -12,6 +17,9 @@ const routerRules = { 'condition-urlpattern-string-source-cache': [ {condition: {urlPattern: '/**/cache.txt'}, source: 'cache'}, ], + 'condition-urlpattern-string-source-cache-with-name': [ + {condition: {urlPattern: '/**/cache.txt'}, source: {cacheName: TEST_CACHE_NAME}}, + ], 'condition-urlpattern-constructed-ignore-case-source-network': [{ condition: { urlPattern: @@ -90,6 +98,21 @@ const routerRules = { source: 'race-network-and-fetch-handler' }, ], + 'multiple-conditions-network': { + condition: { + urlPattern: new URLPattern({search: 'test'}), + requestMode: 'cors', + requestMethod: 'post', + }, + source: 'network' + }, + 'multiple-conditions-with-destination-network' : { + condition: { + urlPattern: new URLPattern({search: 'test'}), + requestDestination: 'style' + }, + source: 'network' + } }; -export {routerRules}; +export {routerRules, TEST_CACHE_NAME as cacheName}; diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.js b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.js index 07409ec42c..c0bd683f91 100644 --- a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.js +++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/resources/static-router-sw.js @@ -1,6 +1,6 @@ 'use strict'; -import {routerRules} from './router-rules.js'; +import {routerRules, cacheName} from './router-rules.js'; import { recordRequest, recordError, @@ -10,7 +10,7 @@ import { import './imported-sw.js'; self.addEventListener('install', async e => { - e.waitUntil(caches.open('v1').then( + e.waitUntil(caches.open(cacheName).then( cache => {cache.put('cache.txt', new Response('From cache'))})); const params = new URLSearchParams(location.search); diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-main-resource.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-main-resource.https.html index fc93a4f7c9..7998af3f99 100644 --- a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-main-resource.https.html +++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-main-resource.https.html @@ -18,6 +18,8 @@ const ROUTER_RULE_KEY_RESPECT_CASE = const ROUTER_RULE_KEY_URLPATTERN_CACHE = 'condition-urlpattern-string-source-cache'; const ROUTER_RULE_KEY_REQUEST_CACHE = 'condition-request-navigate-source-cache'; +const ROUTER_RULE_KEY_URLPATTERN_CACHE_WITH_NAME = + 'condition-urlpattern-string-source-cache-with-name'; const REGISTERED_ROUTE = 'resources/direct.txt'; const CACHED_ROUTE = 'resources/cache.txt'; const NON_REGISTERED_ROUTE = 'resources/simple.html'; @@ -65,5 +67,10 @@ iframeTest(NON_REGISTERED_ROUTE, ROUTER_RULE_KEY_REQUEST_CACHE, async (t, iwin, assert_equals(iwin.document.body.innerText, "Here's a simple html file."); }, 'Main resource fallback to the network when there is no cache entry'); +iframeTest(CACHED_ROUTE, ROUTER_RULE_KEY_URLPATTERN_CACHE_WITH_NAME, async (t, iwin, worker) => { + const {requests} = await get_info_from_worker(worker); + assert_equals(requests.length, 0); + assert_equals(iwin.document.body.innerText, "From cache"); +}, 'Main resource load matched with the cache source, with specifying the cache name'); </script> </body> diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-mutiple-conditions.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-mutiple-conditions.https.html new file mode 100644 index 0000000000..3d69411843 --- /dev/null +++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-mutiple-conditions.https.html @@ -0,0 +1,112 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title> + Static Router: routers are evaluated with the request method condition. +</title> +<script src="/common/get-host-info.sub.js"></script> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/service-workers/service-worker/resources/test-helpers.sub.js"> +</script> +<script src="resources/static-router-helpers.sub.js"> +</script> +<body> +<script> +const ROUTER_KEY = 'multiple-conditions-network'; +const ROUTER_KEY_WITH_DESTINATION = + 'multiple-conditions-with-destination-network'; +const HTML_FILE = 'resources/simple.html'; +const REQUEST_SRC = 'resources/direct.py'; + +const is_matched = async (worker) => { + const {requests} = await get_info_from_worker(worker); + return requests.length == 0; +} + +const appendCSS = async (iwin, src) => { + const promise = new Promise(resolve => { + const link = iwin.document.createElement('link'); + link.rel = 'stylesheet'; + link.href = src; + iwin.document.head.appendChild(link); + link.onload = () => { + resolve(link); + }; + }); + + return promise; +}; + +const appendScript = async (iwin, src) => { + const promise = new Promise(resolve => { + const script = iwin.document.createElement('script'); + script.src = src; + iwin.document.body.appendChild(script); + script.onload = () => { + resolve(script); + }; + }); + + return promise; +}; + +iframeTest(HTML_FILE, ROUTER_KEY, async (t, iwin, worker) => { + // Reset the fetch count created by the setup process. + await reset_info_in_worker(worker); + const {requests} = await get_info_from_worker(worker); + assert_equals(requests.length, 0); + + // Expected condtion: + // - urlPattern: { search: 'test' } + // - mode: 'cors' + // - method: POST + + // Expect match. + let response = await iwin.fetch(`../${REQUEST_SRC}?test`, {mode: 'cors', method: 'post'}); + assert_equals(response.status, 200); + assert_true(await is_matched(worker)); + await reset_info_in_worker(worker); + + // mode: 'no-cors' won't match. + response = await iwin.fetch(`../${REQUEST_SRC}?test`, {mode: 'no-cors', method: 'post'}); + assert_false(await is_matched(worker)); + await reset_info_in_worker(worker); + + // method: GET won't match. + response = await iwin.fetch(`../${REQUEST_SRC}?test`, {mode: 'cors', method: 'get'}); + assert_false(await is_matched(worker)); + await reset_info_in_worker(worker); + + // No seqarch query won't match. + response = await iwin.fetch(`../${REQUEST_SRC}`, {mode: 'cors', method: 'post'}); + assert_false(await is_matched(worker)); + await reset_info_in_worker(worker); +}, 'Multiple conditions work with `and` operation'); + +iframeTest(HTML_FILE, ROUTER_KEY_WITH_DESTINATION, async (t, iwin, worker) => { + // Reset the fetch count created by the setup process. + await reset_info_in_worker(worker); + const {requests} = await get_info_from_worker(worker); + assert_equals(requests.length, 0); + + // Expected condtion: + // - urlPattern: { search: 'test' } + // - destination: style + + // Expect match. + await appendCSS(iwin, `../${REQUEST_SRC}?test`); + assert_true(await is_matched(worker)); + await reset_info_in_worker(worker); + + // Other request destination won't match. + await appendScript(iwin, `../${REQUEST_SRC}?test`); + assert_false(await is_matched(worker)); + await reset_info_in_worker(worker); + + // No seqarch query won't match. + await appendCSS(iwin, `../${REQUEST_SRC}`); + assert_false(await is_matched(worker)); + await reset_info_in_worker(worker); +}, 'Multiple conditions including requestDestination work with `and` operation'); +</script> +</body> diff --git a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-subresource.https.html b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-subresource.https.html index 3f7902a872..00b9070bf1 100644 --- a/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-subresource.https.html +++ b/testing/web-platform/tests/service-workers/service-worker/tentative/static-router/static-router-subresource.https.html @@ -15,13 +15,17 @@ const ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED_IGNORE_CASE = 'condition-urlpattern-constructed-ignore-case-source-network'; const ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED_RESPECT_CASE = 'condition-urlpattern-constructed-respect-case-source-network'; -const ROUTER_RULE_KEY_URL_PATTERN_URLPATTERNINIT = - 'condition-urlpattern-urlpatterninit-source-network'; +const ROUTER_RULE_KEY_URL_PATTERN_URLPATTERNCOMPATIBLE = + 'condition-urlpattern-urlpatterncompatible-source-network'; const ROUTER_RULE_KEY_URL_PATTERN_STRING = 'condition-urlpattern-string-source-network'; const ROUTER_RULE_KEY_REQUEST = 'condition-request-source-network' const ROUTER_RULE_KEY_URL_PATTERN_STRING_CACHE = 'condition-urlpattern-string-source-cache'; +const ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED_MATCH_ALL_CACHE = + 'condition-urlpattern-constructed-match-all-source-cache'; +const ROUTER_RULE_KEY_URLPATTERN_CACHE_WITH_NAME = + 'condition-urlpattern-string-source-cache-with-name'; const ROUTER_RULE_KEY_OR = 'condition-or-source-network' const SCOPE = 'resources/'; const HTML_FILE = 'resources/simple.html'; @@ -49,7 +53,7 @@ iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED, async (t, iwin) => iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED, async (t, iwin, worker) => { const rnd = randomString(); - // Confirm that the given URLPatternInit has a wildcard pattern for the + // Confirm that the given URLPatternCompatible has a wildcard pattern for the // hostname. Also, if |urlPattern| is a consutructed URLPattern object, // baseURL won't be set while adding router rules, thus it matches the cross // origin request as far as other components matches. So expecting the direct @@ -75,24 +79,24 @@ iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED_RESPECT_CASE, async assert_equals(await response.text(), rnd); }, 'Subresource load matched without ignoreCase URLPattern condition'); -iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_URLPATTERNINIT, async (t, iwin) => { +iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_URLPATTERNCOMPATIBLE, async (t, iwin) => { const rnd = randomString(); const response = await iwin.fetch('?nonce=' + rnd); assert_equals(await response.text(), "Network\n"); -}, 'Subresource load matched with URLPattern condition via URLPatternInit'); +}, 'Subresource load matched with URLPattern condition via URLPatternCompatible'); -iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_URLPATTERNINIT, async (t, iwin, worker) => { +iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_URLPATTERNCOMPATIBLE, async (t, iwin, worker) => { // The SW script URL is added as a baseURL when |urlPattern| is passed via - // URLPatternInit, and there is not |baseURL| in it. Cross origin request will - // go through the fetch handler because |baseURL| info complements hostname - // with the hostname of the SW script. + // URLPatternCompatible, and there is not |baseURL| in it. Cross + // origin request will go through the fetch handler because |baseURL| info + // complements hostname with the hostname of the SW script. const rnd = randomString(); const origin = get_host_info().HTTPS_REMOTE_ORIGIN; const response = await iwin.fetch(`${origin}/${TXT_FILE}?nonce=${rnd}`); const {requests} = await get_info_from_worker(worker); assert_equals(requests.length, 1); assert_equals(await response.text(), rnd); -}, 'Subresource cross origin load not matched with URLPattern condition via URLPatternInit'); +}, 'Subresource cross origin load not matched with URLPattern condition via URLPatternCompatible'); iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_STRING, async (t, iwin) => { const rnd = randomString(); @@ -150,5 +154,35 @@ iframeTest(HTML_FILE, ROUTER_RULE_KEY_URL_PATTERN_STRING_CACHE, async (t, iwin) assert_equals(response_with_param.status, 404); }, 'Subresource load matched with the cache source rule'); +iframeTest(TXT_FILE, ROUTER_RULE_KEY_URL_PATTERN_CONSTRUCTED_MATCH_ALL_CACHE, async (t, iwin, worker) => { + // Send a request, which is not stored in the cache, but it exists over the network. + const rnd = randomString(); + let response = await iwin.fetch(`?nonce=${rnd}`); + assert_equals(await response.text(), "Network\n"); + assert_equals(response.status, 200); + + // Send a request, which is not stored in the cache, and does not exist over the network. + const NON_EXISTING_FILE = 'not-found.txt'; + response = await iwin.fetch(`${NON_EXISTING_FILE}?nonce=${randomString()}`); + assert_equals(response.status, 404); + + // Both requests are not handled by ServiceWorker. + const {requests} = await get_info_from_worker(worker); + assert_equals(requests.length, 0); +}, 'Subresource load did not match with the cache and fallback to the network'); + +iframeTest(HTML_FILE, ROUTER_RULE_KEY_URLPATTERN_CACHE_WITH_NAME, async (t, iwin, worker) => { + // No need to set `resources/` because the request is dispatched from iframe. + const CACHED_FILE = 'cache.txt'; + const response = await iwin.fetch(CACHED_FILE); + assert_equals(response.status, 200); + assert_equals(await response.text(), "From cache"); + + // This doesn't match because the cache key is wrong. + const rnd = randomString(); + const response_with_param = await iwin.fetch(`${CACHED_FILE}?nonce=${rnd}`); + assert_equals(response_with_param.status, 404); +}, 'Subresource load matched with the cache source, with specifying the cache name'); + </script> </body> diff --git a/testing/web-platform/tests/shadow-dom/declarative/declarative-shadow-dom-repeats.html b/testing/web-platform/tests/shadow-dom/declarative/declarative-shadow-dom-repeats.html index 9cee41f2f3..a2bfa488fb 100644 --- a/testing/web-platform/tests/shadow-dom/declarative/declarative-shadow-dom-repeats.html +++ b/testing/web-platform/tests/shadow-dom/declarative/declarative-shadow-dom-repeats.html @@ -55,7 +55,7 @@ test((t) => { </script> <div id=open2> - <template shadowrootmode=open shadowrootdelegatesfocus shadowrootclonable serializable> + <template shadowrootmode=open shadowrootdelegatesfocus shadowrootclonable shadowrootserializable> Open, delegates focus (not the default), clonable (not the default) serializable (not the default), named slot assignment (the default) </template> diff --git a/testing/web-platform/tests/shadow-dom/declarative/gethtml-ordering.html b/testing/web-platform/tests/shadow-dom/declarative/gethtml-ordering.html new file mode 100644 index 0000000000..6ac32e2902 --- /dev/null +++ b/testing/web-platform/tests/shadow-dom/declarative/gethtml-ordering.html @@ -0,0 +1,69 @@ +<!DOCTYPE html> +<title>getHTML ordering behavior</title> +<link rel='author' href='mailto:masonf@chromium.org'> +<link rel='help' href='https://github.com/whatwg/html/pull/10139'> +<script src='/resources/testharness.js'></script> +<script src='/resources/testharnessreport.js'></script> + + +<div id=tests> + <div data-name="base"> + <template shadowrootmode=open shadowrootdelegatesfocus shadowrootserializable shadowrootclonable> + <slot></slot> + </template> + <span class=content>Content 1</span> + <span class=content>Content 2</span> + </div> + + <div data-name="template position"> + <span class=content>Content 1</span> + <template shadowrootmode=open shadowrootdelegatesfocus shadowrootserializable shadowrootclonable> + <slot></slot> + </template> + <span class=content>Content 2</span> + </div> + + <div data-name="attribute position"> + <template shadowrootclonable shadowrootserializable shadowrootdelegatesfocus shadowrootmode=open> + <slot></slot> + </template> + <span class=content>Content 1</span> + <span class=content>Content 2</span> + </div> + + <div data-name="both template and attribute position"> + <span class=content>Content 1</span> + <span class=content>Content 2</span> + <template shadowrootclonable shadowrootserializable shadowrootdelegatesfocus shadowrootmode=open> + <slot></slot> + </template> + </div> +</div> + +<script> + function removeWhitespaceNodes(el) { + el.shadowRoot && removeWhitespaceNodes(el.shadowRoot); + var iter = document.createNodeIterator(el, NodeFilter.SHOW_TEXT, + (node) => (node.data.replace(/\s/g,'').length === 0) ? + NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT); + let node; + while (node = iter.nextNode()) { + node.remove(); + } + return el; + } + const serialize = (host) => host.getHTML({shadowRoots: [host.shadowRoot]}); + + const testCases = Array.from(document.querySelectorAll('#tests>div')); + assert_true(testCases.length > 1); + const baseHost = removeWhitespaceNodes(testCases[0]); + const correctSerialization = serialize(baseHost); + baseHost.remove(); + for(let i=1;i<testCases.length;++i) { + const thisHost = removeWhitespaceNodes(testCases[i]); + test(t => { + assert_equals(serialize(thisHost),correctSerialization,'Serialization should be identical'); + thisHost.remove(); + },thisHost.dataset.name); + } +</script> diff --git a/testing/web-platform/tests/shadow-dom/declarative/gethtml.html b/testing/web-platform/tests/shadow-dom/declarative/gethtml.html new file mode 100644 index 0000000000..d950ca7734 --- /dev/null +++ b/testing/web-platform/tests/shadow-dom/declarative/gethtml.html @@ -0,0 +1,155 @@ +<!DOCTYPE html> +<title>getHTML behavior</title> +<meta name="timeout" content="long"> +<link rel='author' href='mailto:masonf@chromium.org'> +<link rel='help' href='https://github.com/whatwg/html/issues/8867'> +<script src='/resources/testharness.js'></script> +<script src='/resources/testharnessreport.js'></script> +<script src='../../html/resources/common.js'></script> + +<body> + +<script> +function testElementType(allowsShadowDom, elementType, runGetHTMLOnShadowRoot, + lightDOMContent, declarativeShadowDom, mode, delegatesFocus, + serializable, clonable) { + const t = test(t => { + // Create and attach element + let wrapper; + if (runGetHTMLOnShadowRoot) { + // This ensures we're testing both Element.getHTML() and ShadowRoot.getHTML(). + const host = document.createElement('div'); + t.add_cleanup(function() { host.remove(); }); + document.body.appendChild(host); + wrapper = host.attachShadow({mode: 'open'}); + } else { + wrapper = document.createElement('div'); + t.add_cleanup(function() { wrapper.remove(); }); + document.body.appendChild(wrapper); + } + + let shadowRoot; + const isOpen = mode === 'open'; + let initDict = {mode: mode, delegatesFocus: delegatesFocus, clonable}; + let expectedSerializable = null; + switch (serializable) { + case undefined: expectedSerializable = false; break; + case "true": initDict.serializable = expectedSerializable = true; break; + case "false": initDict.serializable = expectedSerializable = false; break; + default: throw new Error(`Invalid serializable ${serializable}`); + } + const delegatesAttr = delegatesFocus ? ' shadowrootdelegatesfocus=""' : ''; + const serializableAttr = expectedSerializable ? ' shadowrootserializable=""' : ''; + const clonableAttr = clonable ? ' shadowrootclonable=""' : ''; + + if (allowsShadowDom && declarativeShadowDom) { + const html = `<${elementType}>${lightDOMContent}<template ` + + `shadowrootmode=${mode}${delegatesAttr}${serializableAttr}` + + `${clonableAttr}>`; + wrapper.setHTMLUnsafe(html); + if (isOpen) { + shadowRoot = wrapper.firstElementChild.shadowRoot; + } else { + // For closed shadow root, we rely on the behavior of attachShadow to return it to us + shadowRoot = wrapper.firstElementChild.attachShadow(initDict); + } + } else { + // Imperative shadow dom + const element = document.createElement(elementType); + wrapper.appendChild(element); + const temp = document.createElement('div'); + temp.innerHTML = lightDOMContent; + element.append(...temp.childNodes); + if (allowsShadowDom) { + shadowRoot = element.attachShadow(initDict); + } + } + assert_true(!allowsShadowDom || !!shadowRoot); + + if (allowsShadowDom) { + const correctShadowHtml = `<template shadowrootmode="${mode}"` + + `${delegatesAttr}${serializableAttr}${clonableAttr}><slot></slot>` + + `</template>`; + const correctHtml = `<${elementType}>${correctShadowHtml}` + + `${lightDOMContent}</${elementType}>`; + assert_equals(shadowRoot.mode,mode); + assert_equals(shadowRoot.delegatesFocus,delegatesFocus); + assert_equals(shadowRoot.serializable,expectedSerializable); + assert_equals(shadowRoot.clonable,clonable); + shadowRoot.appendChild(document.createElement('slot')); + const emptyElement = `<${elementType}>${lightDOMContent}</${elementType}>`; + if (isOpen) { + if (expectedSerializable) { + assert_equals(wrapper.getHTML({serializableShadowRoots: true}), + correctHtml); + assert_equals(wrapper.firstElementChild.getHTML({ + serializableShadowRoots: true}), + `${correctShadowHtml}${lightDOMContent}`); + } else { + assert_equals(wrapper.getHTML({serializableShadowRoots: true}), emptyElement); + } + } else { + // Closed shadow roots should not be returned unless shadowRoots specifically contains the shadow root: + assert_equals(wrapper.getHTML({serializableShadowRoots: true}), + emptyElement); + assert_equals(wrapper.getHTML({serializableShadowRoots: true, + shadowRoots: []}), emptyElement); + } + // If we provide the shadow root, serialize it, regardless of serializableShadowRoots. + assert_equals(wrapper.getHTML({serializableShadowRoots: true, shadowRoots: + [shadowRoot]}),correctHtml); + assert_equals(wrapper.getHTML({serializableShadowRoots: false, shadowRoots: + [shadowRoot]}),correctHtml); + assert_equals(wrapper.getHTML({shadowRoots: [shadowRoot]}),correctHtml); + } else { + // For non-shadow hosts, getHTML() should also match .innerHTML + assert_equals(wrapper.getHTML({serializableShadowRoots: true}),wrapper.innerHTML); + } + + // Either way, make sure getHTML({serializableShadowRoots: false}) matches .innerHTML + assert_equals(wrapper.getHTML({serializableShadowRoots: false}),wrapper.innerHTML, + 'getHTML() with serializableShadowRoots false should return the same as .innerHTML'); + // ...and that the default for serializableShadowRoots is false. + assert_equals(wrapper.getHTML(),wrapper.innerHTML, + 'The default for serializableShadowRoots should be false'); + + }, `${runGetHTMLOnShadowRoot ? 'ShadowRoot' : 'Element'}.getHTML() on ` + + `<${elementType}>${lightDOMContent}${allowsShadowDom ? + `, with ${declarativeShadowDom ? 'declarative' : 'imperative'} shadow, ` + + `mode=${mode}, delegatesFocus=${delegatesFocus}, ` + + `serializable=${serializable}, clonable=${clonable}.` : ''}`); +} + +function runAllTests() { + const allElements = [...HTML5_ELEMENTS, 'htmlunknown']; + const safelisted = HTML5_SHADOW_ALLOWED_ELEMENTS.filter(el => el != 'body'); + for (const elementName of allElements) { + const allowsShadowDom = safelisted.includes(elementName); + for (const runGetHTMLOnShadowRoot of [false, true]) { + for (const lightDOMContent of ['','<span>light</span>']) { + if (allowsShadowDom) { + for (const declarativeShadowDom of [false, true]) { + for (const delegatesFocus of [false, true]) { + for (const clonable of [false, true]) { + for (const mode of ['open', 'closed']) { + for (const serializable of [undefined, 'false', 'true']) { + testElementType(true, elementName, runGetHTMLOnShadowRoot, + lightDOMContent, declarativeShadowDom, mode, + delegatesFocus, serializable, clonable); + } + } + } + } + } + } else { + testElementType(false, elementName, runGetHTMLOnShadowRoot, + lightDOMContent); + } + } + } + } +} + +runAllTests(); + +</script> diff --git a/testing/web-platform/tests/shadow-dom/declarative/gethtml.tentative.html b/testing/web-platform/tests/shadow-dom/declarative/gethtml.tentative.html deleted file mode 100644 index eabd39131b..0000000000 --- a/testing/web-platform/tests/shadow-dom/declarative/gethtml.tentative.html +++ /dev/null @@ -1,125 +0,0 @@ -<!DOCTYPE html> -<title>getHTML behavior</title> -<link rel='author' href='mailto:masonf@chromium.org'> -<link rel='help' href='https://github.com/whatwg/html/issues/8867'> -<script src='/resources/testharness.js'></script> -<script src='/resources/testharnessreport.js'></script> -<script src='../../html/resources/common.js'></script> - -<body> - -<script> -function testElementType(allowsShadowDom, elementType, runGetHTMLOnShadowRoot, declarativeShadowDom, mode, delegatesFocus, serializable, clonable) { - const t = test(t => { - // Create and attach element - let wrapper; - if (runGetHTMLOnShadowRoot) { - // This ensures we're testing both Element.getHTML() and ShadowRoot.getHTML(). - const host = document.createElement('div'); - t.add_cleanup(function() { host.remove(); }); - document.body.appendChild(host); - wrapper = host.attachShadow({mode: 'open'}); - } else { - wrapper = document.createElement('div'); - t.add_cleanup(function() { wrapper.remove(); }); - document.body.appendChild(wrapper); - } - - let shadowRoot; - const isOpen = mode === 'open'; - let initDict = {mode: mode, delegatesFocus: delegatesFocus, clonable}; - let expectedSerializable = null; - switch (serializable) { - case undefined: expectedSerializable = false; break; - case "true": initDict.serializable = expectedSerializable = true; break; - case "false": initDict.serializable = expectedSerializable = false; break; - default: throw new Error(`Invalid serializable ${serializable}`); - } - const delegatesAttr = delegatesFocus ? ' shadowrootdelegatesfocus=""' : ''; - const serializableAttr = expectedSerializable ? ' serializable=""' : ''; - const clonableAttr = clonable ? ' shadowrootclonable=""' : ''; - - if (allowsShadowDom && declarativeShadowDom) { - const html = `<${elementType}><template shadowrootmode=${mode}${delegatesAttr}${serializableAttr}${clonableAttr}>`; - wrapper.setHTMLUnsafe(html); - if (isOpen) { - shadowRoot = wrapper.firstElementChild.shadowRoot; - } else { - // For closed shadow root, we rely on the behavior of attachShadow to return it to us - shadowRoot = wrapper.firstElementChild.attachShadow(initDict); - } - } else { - // Imperative shadow dom - const element = document.createElement(elementType); - wrapper.appendChild(element); - if (allowsShadowDom) { - shadowRoot = element.attachShadow(initDict); - } - } - assert_true(!allowsShadowDom || !!shadowRoot); - - if (allowsShadowDom) { - const correctShadowHtml = `<template shadowrootmode="${mode}"${delegatesAttr}${serializableAttr}${clonableAttr}><slot></slot></template>`; - const correctHtml = `<${elementType}>${correctShadowHtml}</${elementType}>`; - assert_equals(shadowRoot.mode,mode); - assert_equals(shadowRoot.delegatesFocus,delegatesFocus); - assert_equals(shadowRoot.serializable,expectedSerializable); - assert_equals(shadowRoot.clonable,clonable); - shadowRoot.appendChild(document.createElement('slot')); - const emptyElement = `<${elementType}></${elementType}>`; - if (isOpen) { - if (expectedSerializable) { - assert_equals(wrapper.getHTML({serializableShadowRoots: true}), correctHtml); - } else { - assert_equals(wrapper.getHTML({serializableShadowRoots: true}), emptyElement); - } - } else { - // Closed shadow roots should not be returned unless shadowRoots specifically contains the shadow root: - assert_equals(wrapper.getHTML({serializableShadowRoots: true}), emptyElement); - assert_equals(wrapper.getHTML({serializableShadowRoots: true, shadowRoots: []}), emptyElement); - } - // If we provide the shadow root, serialize it, regardless of serializableShadowRoots. - assert_equals(wrapper.getHTML({serializableShadowRoots: true, shadowRoots: [shadowRoot]}),correctHtml); - assert_equals(wrapper.getHTML({serializableShadowRoots: false, shadowRoots: [shadowRoot]}),correctHtml); - assert_equals(wrapper.getHTML({shadowRoots: [shadowRoot]}),correctHtml); - } else { - // For non-shadow hosts, getHTML() should also match .innerHTML - assert_equals(wrapper.getHTML({serializableShadowRoots: true}),wrapper.innerHTML); - } - - // Either way, make sure getHTML({serializableShadowRoots: false}) matches .innerHTML - assert_equals(wrapper.getHTML({serializableShadowRoots: false}),wrapper.innerHTML,'getHTML() with serializableShadowRoots false should return the same as .innerHTML'); - // ...and that the default for serializableShadowRoots is false. - assert_equals(wrapper.getHTML(),wrapper.innerHTML,'The default for serializableShadowRoots should be false'); - - }, `${runGetHTMLOnShadowRoot ? 'ShadowRoot' : 'Element'}.getHTML() on <${elementType}>${allowsShadowDom ? `, with ${declarativeShadowDom ? 'declarative' : 'imperative'} shadow, mode=${mode}, delegatesFocus=${delegatesFocus}, serializable=${serializable}, clonable=${clonable}.` : ''}`); -} - -function runAllTests() { - const allElements = [...HTML5_ELEMENTS, 'htmlunknown']; - const safelisted = HTML5_SHADOW_ALLOWED_ELEMENTS.filter(el => el != 'body'); - for (const elementName of allElements) { - const allowsShadowDom = safelisted.includes(elementName); - for (const runGetHTMLOnShadowRoot of [false, true]) { - if (allowsShadowDom) { - for (const declarativeShadowDom of [false, true]) { - for (const delegatesFocus of [false, true]) { - for (const clonable of [false, true]) { - for (const mode of ['open', 'closed']) { - for (const serializable of [undefined, 'false', 'true']) { - testElementType(true, elementName, runGetHTMLOnShadowRoot, declarativeShadowDom, mode, delegatesFocus, serializable, clonable); - } - } - } - } - } - } else { - testElementType(false, elementName, runGetHTMLOnShadowRoot); - } - } - } -} - -runAllTests(); - -</script> diff --git a/testing/web-platform/tests/shadow-dom/host-with-namespace.xhtml b/testing/web-platform/tests/shadow-dom/host-with-namespace.xhtml new file mode 100644 index 0000000000..243728a015 --- /dev/null +++ b/testing/web-platform/tests/shadow-dom/host-with-namespace.xhtml @@ -0,0 +1,27 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xhtml="http://www.w3.org/1999/xhtml" lang="en" > +<head> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> + <div id="default-namespace"></div> + <xhtml:div id="explicit-namespace"></xhtml:div> + + <script> + <![CDATA[ + test(function() { + const defaultNamespaceHost = document.getElementById('default-namespace'); + defaultNamespaceHost.attachShadow({mode: 'open'}); + assert_not_equals(defaultNamespaceHost.shadowRoot, null, "attachShadow should work on node with default namespace"); + }, 'attachShadow with a default-namespaced element'); + + test(function() { + const explicitNamespaceHost = document.getElementById('explicit-namespace'); + explicitNamespaceHost.attachShadow({mode: 'open'}); + assert_not_equals(explicitNamespaceHost.shadowRoot, null, "attachShadow should work on node with explicit namespace"); + }, 'Test attachShadow with a namespaced element'); + ]]> + </script> +</body> +</html> diff --git a/testing/web-platform/tests/shadow-dom/selection-direction.tentative.html b/testing/web-platform/tests/shadow-dom/selection-direction.tentative.html index 3a2512dcc7..b5b3f6044c 100644 --- a/testing/web-platform/tests/shadow-dom/selection-direction.tentative.html +++ b/testing/web-platform/tests/shadow-dom/selection-direction.tentative.html @@ -6,6 +6,9 @@ <link rel="help" href="https://w3c.github.io/selection-api/#dom-selection-getcomposedrange"> <script src="/resources/testharness.js"></script> <script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-actions.js"></script> +<script src='/resources/testdriver-vendor.js'></script> <div id="container"></div> <script> @@ -58,6 +61,45 @@ test(() => { assert_equals(getSelection().direction, 'backward'); }, 'direction returns "backward" when there is a forward selection that crosses shadow boundaries'); +promise_test(async () => { + container.innerHTML = 'hello, world'; + const doubleClick = new test_driver.Actions() + .pointerMove(0, 0, container.firstChild) + .pointerDown() + .pointerUp() + .pointerDown() + .pointerUp() + .send(); + await doubleClick; + + const sel = getSelection(); + assert_equals(sel.anchorNode, container.firstChild); + assert_equals(sel.anchorOffset, 0); + assert_equals(sel.focusNode, container.firstChild); + assert_equals(sel.focusOffset, 5); // hello + assert_equals(sel.direction, 'none'); +}, 'direction returns "none" when there is a double click selection(directionless)'); + +promise_test(async () => { + container.innerHTML = 'hello, world'; + const tripleClick = new test_driver.Actions() + .pointerMove(0, 0, container.firstChild) + .pointerDown() + .pointerUp() + .pointerDown() + .pointerUp() + .pointerDown() + .pointerUp() + .send(); + await tripleClick; + + const sel = getSelection(); + assert_equals(sel.anchorNode, container); + assert_equals(sel.anchorOffset, 0); + assert_equals(sel.focusNode, container); + assert_equals(sel.focusOffset, 1); + assert_equals(sel.direction, 'none'); +}, 'direction returns "none" when there is a triple click selection(directionless)'); </script> </body> </html> diff --git a/testing/web-platform/tests/speculation-rules/prerender/resources/request-picture-in-picture.html b/testing/web-platform/tests/speculation-rules/prerender/resources/request-picture-in-picture.html index 9aea3d33d5..360cf3b200 100644 --- a/testing/web-platform/tests/speculation-rules/prerender/resources/request-picture-in-picture.html +++ b/testing/web-platform/tests/speculation-rules/prerender/resources/request-picture-in-picture.html @@ -3,7 +3,7 @@ <script src="/resources/testharnessreport.js"></script> <script src="/speculation-rules/prerender/resources/utils.js"></script> <video id="target" - onloadstart="loadstart()" src="/media/test.ogv"></video> + onloadstart="loadstart()" src="/media/test.webm"></video> <script> assert_true(document.prerendering); diff --git a/testing/web-platform/tests/storage-access-api/resources/storage-access-beyond-cookies-iframe-iframe.html b/testing/web-platform/tests/storage-access-api/resources/storage-access-beyond-cookies-iframe-iframe.html index 2d5e22fa71..77f1163f36 100644 --- a/testing/web-platform/tests/storage-access-api/resources/storage-access-beyond-cookies-iframe-iframe.html +++ b/testing/web-platform/tests/storage-access-api/resources/storage-access-beyond-cookies-iframe-iframe.html @@ -149,6 +149,53 @@ if (local_has) { message = "Handle should not override window Cache Storage"; } + document.cookie = "partitioned=test; SameSite=None; Secure; Partitioned;"; + const cache = await handle.caches.open(id); + await cache.add("/storage-access-api/resources/get_cookies.py?2"); + await test_driver.bless("fake user interaction", () => document.requestStorageAccess()); + await cache.add("/storage-access-api/resources/get_cookies.py?3"); + let req = await cache.match("/storage-access-api/resources/get_cookies.py?1"); + let req_json = await req.json(); + if (!req_json.hasOwnProperty("samesite_strict")) { + message = "Top-level cache fetch should have SameSite=Strict cookies."; + } + if (!req_json.hasOwnProperty("samesite_lax")) { + message = "Top-level cache fetch should have SameSite=Lax cookies."; + } + if (!req_json.hasOwnProperty("samesite_none")) { + message = "Top-level cache fetch should have SameSite=None cookies."; + } + if (req_json.hasOwnProperty("partitioned")) { + message = "Top-level cache fetch should not have partitioned cookies."; + } + req = await cache.match("/storage-access-api/resources/get_cookies.py?2"); + req_json = await req.json(); + if (req_json.hasOwnProperty("samesite_strict")) { + message = "SAA cache fetch should not have SameSite=Strict cookies."; + } + if (req_json.hasOwnProperty("samesite_lax")) { + message = "SAA cache fetch should not have SameSite=Lax cookies."; + } + if (req_json.hasOwnProperty("samesite_none")) { + message = "SAA cache fetch should not have SameSite=None cookies."; + } + if (!req_json.hasOwnProperty("partitioned")) { + message = "SAA cache fetch should have partitioned cookies."; + } + req = await cache.match("/storage-access-api/resources/get_cookies.py?3"); + req_json = await req.json(); + if (req_json.hasOwnProperty("samesite_strict")) { + message = "SAA cache + cookie fetch should not have SameSite=Strict cookies."; + } + if (req_json.hasOwnProperty("samesite_lax")) { + message = "SAA cache + cookie fetch should not have SameSite=Lax cookies."; + } + if (!req_json.hasOwnProperty("samesite_none")) { + message = "SAA cache + cookie fetch should have SameSite=None cookies."; + } + if (!req_json.hasOwnProperty("partitioned")) { + message = "SAA cache + cookie fetch should have partitioned cookies."; + } await handle.caches.delete(id); break; } diff --git a/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.SharedWorker.tentative.sub.https.window.js b/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.SharedWorker.tentative.sub.https.window.js index ed4f25517f..613a47ba1b 100644 --- a/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.SharedWorker.tentative.sub.https.window.js +++ b/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.SharedWorker.tentative.sub.https.window.js @@ -30,11 +30,10 @@ async_test(t => { assert_equals(e.data, "Same-origin handle access", "Relay worker should divert messages here"); // Step 8 const cookie_worker = new SharedWorker("/storage-access-api/resources/shared-worker-cookies.py", {name: id, sameSiteCookies: 'none'}); - cookie_worker.port.onmessage = t.step_func(e => { + cookie_worker.port.onmessage = t.step_func(async (e) => { assert_equals(e.data, "ReadOnLoad:None,ReadOnFetch:None,ConnectionsMade:2", "Worker should already have been opened and only see SameSite=None cookies"); - test_driver.delete_all_cookies().then(t.step_func(() => { - t.done(); - })); + await test_driver.delete_all_cookies(); + t.done(); }); }); diff --git a/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.caches.tentative.sub.https.window.js b/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.caches.tentative.sub.https.window.js index 7907084e63..51e5c648a6 100644 --- a/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.caches.tentative.sub.https.window.js +++ b/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.caches.tentative.sub.https.window.js @@ -15,18 +15,24 @@ async_test(t => { // Step 1 - window.addEventListener("message", t.step_func(e => { + window.addEventListener("message", t.step_func((e) => { if (e.data.type != "result") { return; } // Step 8 assert_equals(e.data.message, "HasAccess for caches", "Storage Access API should be accessible and return first-party data"); + t.add_cleanup(() => {test_driver.delete_all_cookies();}); t.done(); })); // Step 2 const id = Date.now(); - window.caches.open(id).then(() => { + document.cookie = "samesite_strict=test; SameSite=Strict; Secure"; + document.cookie = "samesite_lax=test; SameSite=Lax; Secure"; + document.cookie = "samesite_none=test; SameSite=None; Secure"; + + window.caches.open(id).then(async (cache) => { + await cache.add("https://{{hosts[][]}}:{{ports[https][0]}}/storage-access-api/resources/get_cookies.py?1"); // Step 3 let iframe = document.createElement("iframe"); iframe.src = "https://{{hosts[alt][]}}:{{ports[https][0]}}/storage-access-api/resources/storage-access-beyond-cookies-iframe.sub.html?type=caches&id="+id; diff --git a/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.cookies.tentative.sub.https.window.js b/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.cookies.tentative.sub.https.window.js index 1ff00fa919..ad760cfda7 100644 --- a/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.cookies.tentative.sub.https.window.js +++ b/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.cookies.tentative.sub.https.window.js @@ -15,15 +15,14 @@ async_test(t => { // Step 1 - window.addEventListener("message", t.step_func(e => { + window.addEventListener("message", t.step_func((e) => { if (e.data.type != "result") { return; } // Step 8 assert_equals(e.data.message, "HasAccess for cookies", "Storage Access API should be accessible and return first-party data"); - test_driver.delete_all_cookies().then(t.step_func(() => { - t.done(); - })); + t.add_cleanup(() => {test_driver.delete_all_cookies();}); + t.done(); })); // Step 2 diff --git a/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.estimate.tentative.sub.https.window.js b/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.estimate.tentative.sub.https.window.js index fb15dfee09..eb8cb0c68d 100644 --- a/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.estimate.tentative.sub.https.window.js +++ b/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.estimate.tentative.sub.https.window.js @@ -17,24 +17,22 @@ async_test(t => { const id = "test"; // Step 1 - window.addEventListener("message", t.step_func(e => { + window.addEventListener("message", t.step_func((e) => { if (e.data.type != "result") { return; } // Step 8 assert_equals(e.data.message, "HasAccess for estimate", "Storage Access API should be accessible and return first-party data"); - caches.delete(id).then(() => { - t.done(); - }); + t.add_cleanup(() => {caches.delete(id);}); + t.done(); })); // Step 2 - window.caches.open(id).then((cache) => { - cache.put('/test.json', new Response('x'.repeat(1024*1024))).then(() => { - // Step 3 - let iframe = document.createElement("iframe"); - iframe.src = "https://{{hosts[alt][]}}:{{ports[https][0]}}/storage-access-api/resources/storage-access-beyond-cookies-iframe.sub.html?type=estimate&id="+id; - document.body.appendChild(iframe); - }); + window.caches.open(id).then(async (cache) => { + await cache.put('/test.json', new Response('x'.repeat(1024*1024))); + // Step 3 + let iframe = document.createElement("iframe"); + iframe.src = "https://{{hosts[alt][]}}:{{ports[https][0]}}/storage-access-api/resources/storage-access-beyond-cookies-iframe.sub.html?type=estimate&id="+id; + document.body.appendChild(iframe); }); }, "Verify StorageAccessAPIBeyondCookies for Quota"); diff --git a/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.getDirectory.tentative.sub.https.window.js b/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.getDirectory.tentative.sub.https.window.js index b3b8f7e8e2..d59caa93cd 100644 --- a/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.getDirectory.tentative.sub.https.window.js +++ b/testing/web-platform/tests/storage-access-api/storage-access-beyond-cookies.getDirectory.tentative.sub.https.window.js @@ -26,12 +26,11 @@ async_test(t => { // Step 2 const id = Date.now(); - window.navigator.storage.getDirectory().then((root) => { - root.getFileHandle(id, {create: true}).then(() => { - // Step 3 - let iframe = document.createElement("iframe"); - iframe.src = "https://{{hosts[alt][]}}:{{ports[https][0]}}/storage-access-api/resources/storage-access-beyond-cookies-iframe.sub.html?type=getDirectory&id="+id; - document.body.appendChild(iframe); - }); + window.navigator.storage.getDirectory().then(async (root) => { + await root.getFileHandle(id, {create: true}); + // Step 3 + let iframe = document.createElement("iframe"); + iframe.src = "https://{{hosts[alt][]}}:{{ports[https][0]}}/storage-access-api/resources/storage-access-beyond-cookies-iframe.sub.html?type=getDirectory&id="+id; + document.body.appendChild(iframe); }); }, "Verify StorageAccessAPIBeyondCookies for Origin Private File System"); diff --git a/testing/web-platform/tests/streams/piping/crashtests/cross-piping2.https.html b/testing/web-platform/tests/streams/piping/crashtests/cross-piping2.https.html new file mode 100644 index 0000000000..4616aef479 --- /dev/null +++ b/testing/web-platform/tests/streams/piping/crashtests/cross-piping2.https.html @@ -0,0 +1,14 @@ +<!DOCTYPE html> +<script type="module"> + let a = self.open() + let d = await a.navigator.storage.getDirectory() + let h = await d.getFileHandle("c5c9960b-a637-4232-be3d-3ccc5704852f", {"create": true}) + let r = new ReadableStream({ + start(c) { + c.enqueue(h) + c.close(); + } + }); + let w = await h.createWritable({ }) + r.pipeThrough({"readable": r, "writable": w}, {}) +</script> diff --git a/testing/web-platform/tests/svg-aam/role/roles-generic.html b/testing/web-platform/tests/svg-aam/role/roles-generic.html index d8ad4f9ea1..d5d46b792c 100644 --- a/testing/web-platform/tests/svg-aam/role/roles-generic.html +++ b/testing/web-platform/tests/svg-aam/role/roles-generic.html @@ -36,8 +36,7 @@ <script> AriaUtils.verifyGenericRolesBySelector(".ex-generic"); -AriaUtils.verifyRolesBySelector(".ex"); </script> </body> -</html>
\ No newline at end of file +</html> diff --git a/testing/web-platform/tests/svg/linking/reftests/url-processing-invalid-base.svg b/testing/web-platform/tests/svg/linking/reftests/url-processing-invalid-base.svg deleted file mode 100644 index fc5d7c6b06..0000000000 --- a/testing/web-platform/tests/svg/linking/reftests/url-processing-invalid-base.svg +++ /dev/null @@ -1,15 +0,0 @@ -<svg xmlns="http://www.w3.org/2000/svg" - xmlns:html="http://www.w3.org/1999/xhtml"> - <title>An invalid base URL makes all internal references invalid</title> - <html:link rel="help" href="https://svgwg.org/svg2-draft/linking.html#processingURL"/> - <html:link rel="help" href="https://svgwg.org/svg2-draft/painting.html#SpecifyingPaint"/> - <html:link rel="help" href="https://svgwg.org/svg2-draft/pservers.html#PaintServerTemplates"/> - <html:link rel="match" href="reference/green-100x100.svg"/> - <html:base href="invalid:"/> - <linearGradient id="p2"> - <stop stop-color="orange"/> - </linearGradient> - <linearGradient id="p" href="#p2"/> - <rect width="100" height="100" fill="red"/> - <rect width="100" height="100" fill="url(#p) green"/> -</svg> diff --git a/testing/web-platform/tests/svg/linking/scripted/testcommon.js b/testing/web-platform/tests/svg/linking/scripted/testcommon.js index 7d87923f59..7177dd801f 100644 --- a/testing/web-platform/tests/svg/linking/scripted/testcommon.js +++ b/testing/web-platform/tests/svg/linking/scripted/testcommon.js @@ -23,9 +23,11 @@ function createSVGElement(test, tag, parent, attrs) { } } parent.appendChild(elem); - test.add_cleanup(function() { - elem.remove(); - }); + if (test) { + test.add_cleanup(function() { + elem.remove(); + }); + } return elem; } diff --git a/testing/web-platform/tests/svg/painting/reftests/paint-context-006-ref.svg b/testing/web-platform/tests/svg/painting/reftests/paint-context-006-ref.svg new file mode 100644 index 0000000000..6aa42374c5 --- /dev/null +++ b/testing/web-platform/tests/svg/painting/reftests/paint-context-006-ref.svg @@ -0,0 +1,21 @@ +<svg id="svg-root" viewBox="0 0 800 600" + xmlns="http://www.w3.org/2000/svg" + xmlns:html="http://www.w3.org/1999/xhtml"> + <g id="testmeta"> + <title>Paint: 'context-fill' for markers with gradient</title> + <html:link rel="author" + title="Stefan Zager" + href="mailto:szager@chromium.org"/> + </g> + + <defs> + <linearGradient id="lg" x1="10%"> + <stop offset="0" stop-color="red"/> + <stop offset="1" stop-color="blue"/> + </linearGradient> + </defs> + + <path id="test-reference" fill="url(#lg)" + d="M 90 75 h 400 v 50 h 200 v 400 h -540 v -350 h -60 Z"/> + <text x="290" y="105" style="font-size:96px" stroke="black" fill="none" rotate="45">A</text> +</svg> diff --git a/testing/web-platform/tests/svg/painting/reftests/paint-context-006.svg b/testing/web-platform/tests/svg/painting/reftests/paint-context-006.svg new file mode 100644 index 0000000000..95bd2740fa --- /dev/null +++ b/testing/web-platform/tests/svg/painting/reftests/paint-context-006.svg @@ -0,0 +1,36 @@ +<svg id="svg-root" viewBox="0 0 800 600" + xmlns="http://www.w3.org/2000/svg" + xmlns:html="http://www.w3.org/1999/xhtml"> + <g id="testmeta"> + <title>Paint: 'context-fill' for markers with gradient</title> + <html:link rel="author" + title="Stefan Zager" + href="mailto:szager@chromium.org"/> + <html:link rel="help" + href="https://www.w3.org/TR/SVG2/painting.html#SpecifyingPaint"/> + <html:link rel="match" href="paint-context-006-ref.svg" /> + </g> + + <defs> + <linearGradient id="lg"> + <stop offset="0" stop-color="red"/> + <stop offset="1" stop-color="blue"/> + </linearGradient> + + <marker id="inner-marker" refX="50" refY="20" markerWidth="100" markerHeight="50"> + <rect width="100" height="50" fill="context-fill"/> + </marker> + + <marker id="marker" refX="50" refY="340" markerWidth="100" markerHeight="400" orient="0.25turn"> + <g fill="context-fill"> + <rect y="40" width="100" height="360"/> + <text x="30" y="200" style="font-size:96px" stroke="black" rotate="-45">A</text> + <path d="M 50 40 v -20" marker-end="url(#inner-marker)"/> + </g> + </marker> + </defs> + + <path id="test-body-content" fill="url(#lg)" + d="M 150 125 h 540 v 400 h -540 Z" + marker-start="url(#marker)"/> +</svg> diff --git a/testing/web-platform/tests/svg/types/scripted/SVGGraphicsElement.getScreenCTM.html b/testing/web-platform/tests/svg/types/scripted/SVGGraphicsElement.getScreenCTM.html new file mode 100644 index 0000000000..7e5dfc649f --- /dev/null +++ b/testing/web-platform/tests/svg/types/scripted/SVGGraphicsElement.getScreenCTM.html @@ -0,0 +1,245 @@ +<!DOCTYPE html> +<html> +<head> + <title>SVGGraphicsElement.getScreenCTM</title> + <metadata> + <link rel="help" href="https://svgwg.org/svg2-draft/types.html#InterfaceSVGGraphicsElement"/> + </metadata> + <script src="../../linking/scripted/testcommon.js"></script> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> +</head> +<body> +<style> + body { + margin: 8px; + } + .absPosAtTopLeft { + position: absolute; + top: 0; + left: 0; + } + .absPosMegaOffset { + position: absolute; + left: 1px; + top: 2px; + margin-left: 3px; + margin-top: 4px; + padding-left: 5px; + padding-top: 6px; + } + .svgMegaOffset { + margin-left: 7px; + margin-top: 8px; + padding-left: 9px; + padding-top: 10px; + } +</style> +<div style="transform: translate(50px, 50px)"> + <svg id="svg1" width="150" height="150"> + <rect id="rect1" fill="lime" x="50" y="50" width="100" height="100"/> + </svg> +</div> +<script> +/* Utility function to append an unstyled div to the body, with the + * div containing a 200x120 SVG element. */ +function make_subtree() { + let container = document.createElement("div"); + // Pass in "null" for test param, since we don't want this <svg> element + // to get autoremoved at cleanup time. (It's easier to debug test failures + // if we leave it in the DOM, for manual inspection in devtools.) + let svg = createSVGElement(null, "svg", container, + {width: 200, height: 120}); + document.body.appendChild(container); + return container; +} + +/* Utility function for checking a CTM matrix's components: */ +function assert_ctm_equals(actualCTM, expectedCTM) { + // Check every component for which an expected value was provided: + for (let key in expectedCTM) { + let actualVal = actualCTM[key]; + let expectedVal = expectedCTM[key]; + + // Special-case: browsers sometimes get `-0` in CTMs which + // is ~fine since it's numerically equivalent; but assert_equals + // treats it as different, which is why it needs special handling here + // (pretending the -0 was in fact 0), to avoid a spurious test-failure. + if (Object.is(expectedVal, 0) && Object.is(actualVal, -0)) { + actualVal = 0; + } + assert_equals(actualVal, expectedVal, `CTM component ${key}`); + } +} + +test(function() { + let pt = DOMPoint.fromPoint({x: 58, y: 58}); + let screenCTM = svg1.getScreenCTM(); + assert_ctm_equals(screenCTM, { a: 1, b: 0, + c: 0, d: 1, + e: 58, f: 58 }); + + let transformedPoint = pt.matrixTransform(screenCTM.inverse()); + assert_equals(transformedPoint.x, 0); + assert_equals(transformedPoint.y, 0); +}, "<svg> should give correct screen CTM for transform:translate(...) on " + + "ancestor elem, combined with margin on <body>"); + +test(function() { + let screenCTM = document.getElementById("rect1").getScreenCTM(); + assert_ctm_equals(screenCTM, { a: 1, b: 0, + c: 0, d: 1, + e: 58, f: 58 }); +}, "<rect> should give correct screen CTM for transform:translate(...) on " + + "ancestor elem, combined with margin on <body>"); + +test(function() { + let container = make_subtree(); + let svg = container.firstChild; + container.style.transform = "scale(2,1)"; + + // First four CTM components should encode the scaleX=2 and scaleY=1. + // (The other two depend on where the SVG ended up on the page and + // are dependent on its in-flow position, so we're not bothering to + // check them here, but we will in the next part.) + let screenCTM = svg.getScreenCTM(); + assert_ctm_equals(screenCTM, { a: 2, b: 0, + c: 0, d: 1 }); + + // Now we make the container abspos so that we can reliably predict the + // position: + container.classList.add("absPosAtTopLeft"); + + // First four CTM components should still encode those same scale factors. + // The last two components should now encode a -100px translateX, since + // (starting with the SVG positioned at the screen's origin) we've scaled up + // the container by 2x in the horizontal axis, about its own center point, + // which shifts half of its original width (100px) off the left side of the + // screen. + screenCTM = svg.getScreenCTM(); + assert_ctm_equals(screenCTM, { a: 2, b: 0, + c: 0, d: 1, + e: -100, f: 0 }); +}, "<svg> should give correct screen CTM for transform:scale(2,1) on ancestor"); + +test(function() { + let container = make_subtree(); + let svg = container.firstChild; + + container.classList.add("absPosAtTopLeft"); + svg.style.transform = "scale(0.5)"; + + // First four CTM components should encode the scale(0.5). The last two + // components should encode a translation inwards by 1/4 of the <svg>'s + // size in each axis, since we're shrinking by 1/2 about the center point. + // (1/4 of the 200px width is 50px, and 1/4 of the 120px height is 30px) + let screenCTM = svg.getScreenCTM(); + assert_ctm_equals(screenCTM, { a: 0.5, b: 0, + c: 0, d: 0.5, + e: 50, f: 30 }); +}, "<svg> should give correct screen CTM for transform:scale(0.5) on self"); + +test(function() { + let container = make_subtree(); + let svg = container.firstChild; + + container.classList.add("absPosMegaOffset"); + container.style.transform = "translate(20px, 30px)"; + svg.classList.add("svgMegaOffset"); + svg.style.transform = "translate(40px, 50px)"; + + // Expecting CTM to have: + // * identity matrix for scale components + // * translateX component of 1+3+5+7+9+20+40 = 85 + // * translateY component of 2+4+6+8+10+30+50 = 110 + let screenCTM = svg.getScreenCTM(); + assert_ctm_equals(screenCTM, { a: 1, b: 0, + c: 0, d: 1, + e: 85, f: 110 }); +}, "<svg> should give correct screen CTM with many forms of offsets on " + + "ancestor and self"); + +test(function() { + let container = make_subtree(); + let svg = container.firstChild; + // Note: in cases where the SVG is rotated, we have to override + // the default 'vertical-align:baseline' to avoid having our flipped + // y-position be influenced by baseline-related offsets that come from + // the line box. + svg.style.verticalAlign = "top"; + container.classList.add("absPosAtTopLeft"); + + container.style.transform = "rotate(90deg)"; + svg.style.transform = "rotate(270deg)"; + let screenCTM = svg.getScreenCTM(); + assert_ctm_equals(screenCTM, { a: 1, b: 0, + c: 0, d: 1, + e: 0, f: 0 }); + + container.style.transform = "rotate(270deg)"; + svg.style.transform = "rotate(90deg)"; + screenCTM = svg.getScreenCTM(); + assert_ctm_equals(screenCTM, { a: 1, b: 0, + c: 0, d: 1, + e: 0, f: 0 }); + + container.style.transform = "rotate(180deg)"; + svg.style.transform = "rotate(180deg)"; + screenCTM = svg.getScreenCTM(); + assert_ctm_equals(screenCTM, { a: 1, b: 0, + c: 0, d: 1, + e: 0, f: 0 }); +}, "<svg> should give identity screen CTM with friendly rotations on " + + "ancestor and self that add up to 360deg"); + +test(function() { + let container = make_subtree(); + let svg = container.firstChild; + container.classList.add("absPosAtTopLeft"); + svg.classList.add("svgMegaOffset"); + container.style.transform = "rotate(180deg)"; + svg.style.verticalAlign = "top"; + + // Expecting CTM to have: + // * "flip" matrix for scale components (-1 instead of 1) + // * translateX component of 200px, which comes from our 200px width. + // * translateY component of 70px, which comes from our 120px height. + // Note that our "svgMegaOffset" offsets have no effect on our CTM, because + // they end up on the right side when the SVG rotates about its border-box's + // center point, so they don't influence where the SVG geometry actually ends + // up in the screen's coordinate space and are hence irrelevant to the CTM. + let screenCTM = svg.getScreenCTM(); + assert_ctm_equals(screenCTM, { a: -1, b: 0, + c: 0, d: -1, + e: 200, f: 120 }); +}, "<svg> should give correct screen CTM with many forms of offsets on " + + "self, and rotated self"); + +test(function() { + let container = make_subtree(); + let svg = container.firstChild; + container.classList.add("absPosAtTopLeft"); + container.style.transform = "rotate(180deg)"; + svg.classList.add("svgMegaOffset"); + svg.style.transform = "translate(40px, 50px)"; + svg.style.verticalAlign = "top"; + + // Expecting CTM to have: + // * "flip" matrix for scale components (-1 instead of 1) + // * translateX component of 160px, which comes from the 40px in our + // "translate(40px,50px), taken as a reduction from our 200px width. + // * translateY component of 70px, which comes from the 50px in our + // "translate(40px,50px), taken as a reduction from our 120px height. + // Note that our "svgMegaOffset" offsets have no effect on our CTM, because + // they end up on the right side when our ancestor rotates about its center + // point, so they end up irrelevant to our CTM. + let screenCTM = svg.getScreenCTM(); + assert_ctm_equals(screenCTM, { a: -1, b: 0, + c: 0, d: -1, + e: 160, f: 70 }); +}, "<svg> should give correct screen CTM with many forms of offsets on " + + "self, and rotated ancestor"); + +</script> +</body> +</html> diff --git a/testing/web-platform/tests/tools/web_features/manifest.py b/testing/web-platform/tests/tools/web_features/manifest.py index 0f442fe7f6..15ec5362b4 100644 --- a/testing/web-platform/tests/tools/web_features/manifest.py +++ b/testing/web-platform/tests/tools/web_features/manifest.py @@ -187,7 +187,7 @@ def write_manifest_file(path: str, web_features_map: WebFeaturesMap) -> None: { "version": 1, "data": web_features_map - }, cls=WebFeatureManifestEncoder)) + }, cls=WebFeatureManifestEncoder, sort_keys=True)) def main(venv: Any = None, **kwargs: Any) -> int: diff --git a/testing/web-platform/tests/tools/web_features/tests/test_manifest.py b/testing/web-platform/tests/tools/web_features/tests/test_manifest.py index 8b656876ff..b83829308a 100644 --- a/testing/web-platform/tests/tools/web_features/tests/test_manifest.py +++ b/testing/web-platform/tests/tools/web_features/tests/test_manifest.py @@ -202,8 +202,7 @@ def test_valid_schema(): write_manifest_file("test_file.json", web_features_map) mock_file.assert_called_once_with("test_file.json", "w") mock_file.return_value.write.assert_called_once_with( - ('{"version": 1,' - ' "data": {"grid": ["/grid_test1.js", "/grid_test2.js"], "avif": ["/avif_test1.js"]}}')) + '{"data": {"avif": ["/avif_test1.js"], "grid": ["/grid_test1.js", "/grid_test2.js"]}, "version": 1}') args = mock_file.return_value.write.call_args file_dict = json.loads(args[0][0]) # Should not throw an exception diff --git a/testing/web-platform/tests/tools/webdriver/webdriver/bidi/modules/permissions.py b/testing/web-platform/tests/tools/webdriver/webdriver/bidi/modules/permissions.py index 3062260b34..a081e060e9 100644 --- a/testing/web-platform/tests/tools/webdriver/webdriver/bidi/modules/permissions.py +++ b/testing/web-platform/tests/tools/webdriver/webdriver/bidi/modules/permissions.py @@ -9,10 +9,12 @@ class Permissions(BidiModule): def set_permission(self, descriptor: Union[Optional[Mapping[str, Any]], Undefined] = UNDEFINED, state: Union[Optional[str], Undefined] = UNDEFINED, - origin: Union[Optional[str], Undefined] = UNDEFINED) -> Mapping[str, Any]: + origin: Union[Optional[str], Undefined] = UNDEFINED, + user_context: Union[Optional[str], Undefined] = UNDEFINED) -> Mapping[str, Any]: params: MutableMapping[str, Any] = { "descriptor": descriptor, "state": state, - "origin": origin + "origin": origin, + "userContext": user_context, } return params diff --git a/testing/web-platform/tests/tools/wpt/browser.py b/testing/web-platform/tests/tools/wpt/browser.py index 2f9c453131..ea71499ec4 100644 --- a/testing/web-platform/tests/tools/wpt/browser.py +++ b/testing/web-platform/tests/tools/wpt/browser.py @@ -1559,7 +1559,23 @@ class ChromeiOS(Browser): raise NotImplementedError def version(self, binary=None, webdriver_binary=None): - return None + if webdriver_binary is None: + self.logger.warning( + "Cannot find ChromeiOS version without CWTChromeDriver") + return None + # Use `chrome iOS driver --version` to get the version. Example output: + # "125.0.6378.0" + try: + version_string = call(webdriver_binary, "--version").strip() + except subprocess.CalledProcessError as e: + self.logger.warning(f"Failed to call {webdriver_binary}: {e}") + return None + m = re.match(r"[\d][\d\.]*", version_string) + if not m: + self.logger.warning( + f"Failed to extract version from: {version_string}") + return None + return m.group(0) class Opera(Browser): diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/base.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/base.py index 180e3fb959..6b1465cde8 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/base.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/base.py @@ -1,5 +1,3 @@ -# mypy: allow-untyped-defs - import enum import errno import os @@ -8,16 +6,21 @@ import socket import time import traceback from abc import ABCMeta, abstractmethod +from typing import cast, Any, List, Mapping, Optional, Tuple, Type import mozprocess +from mozdebug import DebuggerInfo +from mozlog.structuredlog import StructuredLogger from ..environment import wait_for_service +from ..testloader import GroupMetadata from ..wptcommandline import require_arg # noqa: F401 +from ..wpttest import Test here = os.path.dirname(__file__) -def cmd_arg(name, value=None): +def cmd_arg(name: str, value: Optional[str] = None) -> str: prefix = "-" if platform.system() == "Windows" else "--" rv = prefix + name if value is not None: @@ -25,7 +28,7 @@ def cmd_arg(name, value=None): return rv -def maybe_add_args(required_args, current_args): +def maybe_add_args(required_args: List[str], current_args: List[str]) -> List[str]: for required_arg in required_args: # If the arg is in the form of "variable=value", only add it if # no arg with another value for "variable" is already there. @@ -39,15 +42,16 @@ def maybe_add_args(required_args, current_args): return current_args -def certificate_domain_list(list_of_domains, certificate_file): +def certificate_domain_list(list_of_domains: List[str], + certificate_file: str) -> List[Mapping[str, Any]]: """Build a list of domains where certificate_file should be used""" - cert_list = [] + cert_list: List[Mapping[str, Any]] = [] for domain in list_of_domains: cert_list.append({"host": domain, "certificateFile": certificate_file}) return cert_list -def get_free_port(): +def get_free_port() -> int: """Get a random unbound port""" while True: s = socket.socket() @@ -56,27 +60,27 @@ def get_free_port(): except OSError: continue else: - return s.getsockname()[1] + return cast(int, s.getsockname()[1]) finally: s.close() -def get_timeout_multiplier(test_type, run_info_data, **kwargs): +def get_timeout_multiplier(test_type: str, run_info_data: Mapping[str, Any], **kwargs: Any) -> float: if kwargs["timeout_multiplier"] is not None: - return kwargs["timeout_multiplier"] + return cast(float, kwargs["timeout_multiplier"]) return 1 -def browser_command(binary, args, debug_info): +def browser_command(binary: str, + args: List[str], + debug_info: DebuggerInfo) -> Tuple[List[str], List[str]]: if debug_info: if debug_info.requiresEscapedArgs: args = [item.replace("&", "\\&") for item in args] debug_args = [debug_info.path] + debug_info.args else: debug_args = [] - command = [binary] + args - return debug_args, command @@ -84,6 +88,9 @@ class BrowserError(Exception): pass +BrowserSettings = Mapping[str, Any] + + class Browser: """Abstract class serving as the basis for Browser implementations. @@ -94,17 +101,16 @@ class Browser: """ __metaclass__ = ABCMeta - process_cls = None - init_timeout = 30 + init_timeout: float = 30 - def __init__(self, logger): + def __init__(self, logger: StructuredLogger): self.logger = logger - def setup(self): + def setup(self) -> None: """Used for browser-specific setup that happens at the start of a test run""" pass - def settings(self, test): + def settings(self, test: Test) -> BrowserSettings: """Dictionary of metadata that is constant for a specific launch of a browser. This is used to determine when the browser instance configuration changes, requiring @@ -114,64 +120,66 @@ class Browser: return {} @abstractmethod - def start(self, group_metadata, **kwargs): + def start(self, group_metadata: GroupMetadata, **kwargs: Any) -> None: """Launch the browser object and get it into a state where is is ready to run tests""" pass @abstractmethod - def stop(self, force=False): - """Stop the running browser process.""" + def stop(self, force: bool = False) -> bool: + """Stop the running browser process. + + Return True iff the browser was successfully stopped. + """ pass + @property @abstractmethod - def pid(self): + def pid(self) -> Optional[int]: """pid of the browser process or None if there is no pid""" pass @abstractmethod - def is_alive(self): + def is_alive(self) -> bool: """Boolean indicating whether the browser process is still running""" pass - def cleanup(self): + def cleanup(self) -> None: """Browser-specific cleanup that is run after the testrun is finished""" pass - def executor_browser(self): + def executor_browser(self) -> Tuple[Type['ExecutorBrowser'], Mapping[str, Any]]: """Returns the ExecutorBrowser subclass for this Browser subclass and the keyword arguments with which it should be instantiated""" return ExecutorBrowser, {} - def maybe_parse_tombstone(self): - """Possibly parse tombstones on Android device for Android target""" - pass - - def check_crash(self, process, test): + def check_crash(self, process: int, test: str) -> bool: """Check if a crash occured and output any useful information to the log. Returns a boolean indicating whether a crash occured.""" return False @property - def pac(self): + def pac(self) -> Optional[str]: return None + class NullBrowser(Browser): - def __init__(self, logger, **kwargs): + def __init__(self, logger: StructuredLogger, **kwargs: Any): super().__init__(logger) - def start(self, **kwargs): + def start(self, group_metadata: GroupMetadata, **kwargs: Any) -> None: """No-op browser to use in scenarios where the TestRunnerManager shouldn't actually own the browser process (e.g. Servo where we start one browser per test)""" pass - def stop(self, force=False): - pass + def stop(self, force: bool = False) -> bool: + return True - def pid(self): + @property + def pid(self) -> Optional[int]: return None - def is_alive(self): + def is_alive(self) -> bool: return True @@ -184,7 +192,7 @@ class ExecutorBrowser: but in some cases it may have more elaborate methods for setting up the browser from the runner process. """ - def __init__(self, **kwargs): + def __init__(self, **kwargs: Any): for k, v in kwargs.items(): setattr(self, k, v) @@ -233,20 +241,20 @@ class OutputHandler: but sometimes use a wrapper e.g. mozrunner. """ - def __init__(self, logger, command, **kwargs): + def __init__(self, logger: StructuredLogger, command: List[str], **kwargs: Any): self.logger = logger self.command = command - self.pid = None + self.pid: Optional[int] = None self.state = OutputHandlerState.BEFORE_PROCESS_START - self.line_buffer = [] + self.line_buffer: List[bytes] = [] - def after_process_start(self, pid): + def after_process_start(self, pid: int) -> None: assert self.state == OutputHandlerState.BEFORE_PROCESS_START self.logger.debug("OutputHandler.after_process_start") self.pid = pid self.state = OutputHandlerState.AFTER_PROCESS_START - def start(self, **kwargs): + def start(self, **kwargs: Any) -> None: assert self.state == OutputHandlerState.AFTER_PROCESS_START self.logger.debug("OutputHandler.start") # Need to change the state here before we try to empty the buffer @@ -254,9 +262,9 @@ class OutputHandler: self.state = OutputHandlerState.AFTER_HANDLER_START for item in self.line_buffer: self(item) - self.line_buffer = None + self.line_buffer.clear() - def after_process_stop(self, clean_shutdown=True): + def after_process_stop(self, clean_shutdown: bool = True) -> None: # If we didn't get as far as configure, just # dump all logs with no configuration self.logger.debug("OutputHandler.after_process_stop") @@ -264,7 +272,7 @@ class OutputHandler: self.start() self.state = OutputHandlerState.AFTER_PROCESS_STOP - def __call__(self, line): + def __call__(self, line: bytes) -> None: if self.state < OutputHandlerState.AFTER_HANDLER_START: self.line_buffer.append(line) return @@ -282,9 +290,17 @@ class OutputHandler: class WebDriverBrowser(Browser): __metaclass__ = ABCMeta - def __init__(self, logger, binary=None, webdriver_binary=None, - webdriver_args=None, host="127.0.0.1", port=None, base_path="/", - env=None, supports_pac=True, **kwargs): + def __init__(self, + logger: StructuredLogger, + binary: Optional[str] = None, + webdriver_binary: Optional[str] = None, + webdriver_args: Optional[List[str]] = None, + host: str = "127.0.0.1", + port: Optional[int] = None, + base_path: str = "/", + env: Optional[Mapping[str, str]] = None, + supports_pac: bool = True, + **kwargs: Any): super().__init__(logger) if webdriver_binary is None: @@ -303,17 +319,17 @@ class WebDriverBrowser(Browser): self.env = os.environ.copy() if env is None else env self.webdriver_args = webdriver_args if webdriver_args is not None else [] - self.init_deadline = None - self._output_handler = None + self.init_deadline: Optional[float] = None + self._output_handler: Optional[OutputHandler] = None self._cmd = None - self._proc = None + self._proc: Optional[mozprocess.ProcessHandler] = None self._pac = None - def make_command(self): + def make_command(self) -> List[str]: """Returns the full command for starting the server process as a list.""" return [self.webdriver_binary] + self.webdriver_args - def start(self, group_metadata, **kwargs): + def start(self, group_metadata: GroupMetadata, **kwargs: Any) -> None: self.init_deadline = time.time() + self.init_timeout try: self._run_server(group_metadata, **kwargs) @@ -321,14 +337,15 @@ class WebDriverBrowser(Browser): self.stop() raise - def create_output_handler(self, cmd): + def create_output_handler(self, cmd: List[str]) -> OutputHandler: """Return an instance of the class used to handle application output. This can be overridden by subclasses which have particular requirements for parsing, or otherwise using, the output.""" return OutputHandler(self.logger, cmd) - def _run_server(self, group_metadata, **kwargs): + def _run_server(self, group_metadata: GroupMetadata, **kwargs: Any) -> None: + assert self.init_deadline is not None cmd = self.make_command() self._output_handler = self.create_output_handler(cmd) @@ -365,17 +382,18 @@ class WebDriverBrowser(Browser): self._output_handler.start(group_metadata=group_metadata, **kwargs) self.logger.debug("_run complete") - def stop(self, force=False): + def stop(self, force: bool = False) -> bool: self.logger.debug("Stopping WebDriver") clean = True if self.is_alive(): + proc = cast(mozprocess.ProcessHandler, self._proc) # Pass a timeout value to mozprocess Processhandler.kill() # to ensure it always returns within it. # See https://bugzilla.mozilla.org/show_bug.cgi?id=1760080 - kill_result = self._proc.kill(timeout=5) + kill_result = proc.kill(timeout=5) if force and kill_result != 0: clean = False - self._proc.kill(9, timeout=5) + proc.kill(9, timeout=5) success = not self.is_alive() if success and self._output_handler is not None: # Only try to do output post-processing if we managed to shut down @@ -383,42 +401,41 @@ class WebDriverBrowser(Browser): self._output_handler = None return success - def is_alive(self): - return hasattr(self._proc, "proc") and self._proc.poll() is None + def is_alive(self) -> bool: + return self._proc is not None and hasattr(self._proc, "proc") and self._proc.poll() is None @property - def url(self): + def url(self) -> str: if self.port is not None: return f"http://{self.host}:{self.port}{self.base_path}" raise ValueError("Can't get WebDriver URL before port is assigned") @property - def pid(self): - if self._proc is not None: - return self._proc.pid + def pid(self) -> Optional[int]: + return self._proc.pid if self._proc is not None else None @property - def port(self): + def port(self) -> int: # If no port is supplied, we'll get a free port right before we use it. # Nothing guarantees an absence of race conditions here. if self._port is None: self._port = get_free_port() return self._port - def cleanup(self): + def cleanup(self) -> None: self.stop() - def executor_browser(self): + def executor_browser(self) -> Tuple[Type[ExecutorBrowser], Mapping[str, Any]]: return ExecutorBrowser, {"webdriver_url": self.url, "host": self.host, "port": self.port, "pac": self.pac, "env": self.env} - def settings(self, test): + def settings(self, test: Test) -> BrowserSettings: self._pac = test.environment.get("pac", None) if self._supports_pac else None return {"pac": self._pac} @property - def pac(self): + def pac(self) -> Optional[str]: return self._pac diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome.py index 7cb46783fc..8198bfe11d 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome.py @@ -97,6 +97,9 @@ def executor_kwargs(logger, test_type, test_environment, run_info_data, chrome_options["args"].append("--use-fake-ui-for-media-stream") # Use a fake UI for FedCM to allow testing it. chrome_options["args"].append("--use-fake-ui-for-fedcm") + # This is needed until https://github.com/web-platform-tests/wpt/pull/40709 + # is merged. + chrome_options["args"].append("--enable-features=FedCmWithoutWellKnownEnforcement") # Use a fake UI for digital identity to allow testing it. chrome_options["args"].append("--use-fake-ui-for-digital-identity") # Shorten delay for Reporting <https://w3c.github.io/reporting/>. diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome_ios.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome_ios.py index 85c98f2994..d3beb449e2 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome_ios.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/chrome_ios.py @@ -4,7 +4,9 @@ from .base import WebDriverBrowser, require_arg from .base import get_timeout_multiplier # noqa: F401 from ..executors import executor_kwargs as base_executor_kwargs from ..executors.base import WdspecExecutor # noqa: F401 -from ..executors.executorwebdriver import (WebDriverTestharnessExecutor, # noqa: F401 +from ..executors.executorchrome import ChromeDriverPrintRefTestExecutor # noqa: F401 +from ..executors.executorwebdriver import (WebDriverCrashtestExecutor, # noqa: F401 + WebDriverTestharnessExecutor, # noqa: F401 WebDriverRefTestExecutor) # noqa: F401 @@ -12,7 +14,9 @@ __wptrunner__ = {"product": "chrome_ios", "check_args": "check_args", "browser": "ChromeiOSBrowser", "executor": {"testharness": "WebDriverTestharnessExecutor", - "reftest": "WebDriverRefTestExecutor"}, + "reftest": "WebDriverRefTestExecutor", + "print-reftest": "ChromeDriverPrintRefTestExecutor", + "crashtest": "WebDriverCrashtestExecutor"}, "browser_kwargs": "browser_kwargs", "executor_kwargs": "executor_kwargs", "env_extras": "env_extras", diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox.py index 6bcbef7c47..814b8b8d75 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox.py @@ -864,6 +864,7 @@ class FirefoxBrowser(Browser): self.instance_manager.stop_current(force) self.logger.debug("stopped") + @property def pid(self): return self.instance.pid() diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox_android.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox_android.py index 0e90c8a6e4..7c158902e1 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox_android.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/firefox_android.py @@ -323,6 +323,7 @@ class FirefoxAndroidBrowser(Browser): self.runner.cleanup() self.logger.debug("stopped") + @property def pid(self): if self.runner.process_handler is None: return None diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/sauce.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/sauce.py index 0f21afd38f..465aac6e49 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/sauce.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/sauce.py @@ -231,6 +231,7 @@ class SauceBrowser(Browser): def stop(self, force=False): pass + @property def pid(self): return None diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/servodriver.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/servodriver.py index 5195fa6442..2cb638be15 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/servodriver.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/servodriver.py @@ -161,6 +161,7 @@ class ServoWebDriverBrowser(Browser): if self.output_handler is not None: self.output_handler.after_process_stop() + @property def pid(self): if self.proc is None: return None diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/wktr.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/wktr.py index 8d429f357b..d65f35ccb3 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/wktr.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/browsers/wktr.py @@ -186,6 +186,7 @@ class WKTRBrowser(Browser): def is_alive(self): return self._proc is not None and self._proc.poll() is None + @property def pid(self): return self._proc.pid if self._proc else None diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/environment.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/environment.py index e206e42754..37951f41d2 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/environment.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/environment.py @@ -1,5 +1,6 @@ # mypy: allow-untyped-defs +import contextlib import errno import json import os @@ -7,8 +8,11 @@ import signal import socket import sys import time +from typing import Optional +import mozprocess from mozlog import get_default_logger, handlers +from mozlog.structuredlog import StructuredLogger from . import mpcontext from .wptlogging import LogLevelRewriter, QueueHandler, LogQueueThread @@ -110,6 +114,7 @@ class TestEnvironment: self.options = options if options is not None else {} mp_context = mpcontext.get_context() + self._stack = contextlib.ExitStack() self.cache_manager = mp_context.Manager() self.stash = serve.stash.StashServer(mp_context=mp_context) self.env_extras = env_extras @@ -121,13 +126,13 @@ class TestEnvironment: self.suppress_handler_traceback = suppress_handler_traceback def __enter__(self): - server_log_handler = self.server_logging_ctx.__enter__() + server_log_handler = self._stack.enter_context(self.server_logging_ctx) self.config_ctx = self.build_config() - self.config = self.config_ctx.__enter__() + self.config = self._stack.enter_context(self.config_ctx) - self.stash.__enter__() - self.cache_manager.__enter__() + self._stack.enter_context(self.stash) + self._stack.enter_context(self.cache_manager) assert self.env_extras_cms is None, ( "A TestEnvironment object cannot be nested") @@ -136,7 +141,7 @@ class TestEnvironment: for env in self.env_extras: cm = env(self.options, self.config) - cm.__enter__() + self._stack.enter_context(cm) self.env_extras_cms.append(cm) self.servers = serve.start(self.server_logger, @@ -147,33 +152,27 @@ class TestEnvironment: webtransport_h3=self.enable_webtransport) if self.options.get("supports_debugger") and self.debug_info and self.debug_info.interactive: - self.ignore_interrupts() + self._stack.enter_context(self.ignore_interrupts()) return self def __exit__(self, exc_type, exc_val, exc_tb): - self.process_interrupts() - for servers in self.servers.values(): for _, server in servers: server.request_shutdown() for servers in self.servers.values(): for _, server in servers: server.wait() - for cm in self.env_extras_cms: - cm.__exit__(exc_type, exc_val, exc_tb) + self._stack.__exit__(exc_type, exc_val, exc_tb) self.env_extras_cms = None - self.cache_manager.__exit__(exc_type, exc_val, exc_tb) - self.stash.__exit__() - self.config_ctx.__exit__(exc_type, exc_val, exc_tb) - self.server_logging_ctx.__exit__(exc_type, exc_val, exc_tb) - + @contextlib.contextmanager def ignore_interrupts(self): - signal.signal(signal.SIGINT, signal.SIG_IGN) - - def process_interrupts(self): - signal.signal(signal.SIGINT, signal.SIG_DFL) + prev_handler = signal.signal(signal.SIGINT, signal.SIG_IGN) + try: + yield + finally: + signal.signal(signal.SIGINT, prev_handler) def build_config(self): override_path = os.path.join(serve_path(self.test_paths), "config.json") @@ -331,7 +330,11 @@ class TestdriverLoader: return self._handler(request, response) -def wait_for_service(logger, host, port, timeout=60, server_process=None): +def wait_for_service(logger: StructuredLogger, + host: str, + port: int, + timeout: float = 60, + server_process: Optional[mozprocess.ProcessHandler] = None) -> bool: """Waits until network service given as a tuple of (host, port) becomes available, `timeout` duration is reached, or the `server_process` exits at which point ``socket.error`` is raised.""" diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/actions.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/actions.py index 6e671f4cfd..cb9c1a1508 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/actions.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/executors/actions.py @@ -319,7 +319,7 @@ class ClickFedCMDialogButtonAction: def __call__(self, payload): dialog_button = payload["dialog_button"] self.logger.debug(f"Clicking FedCM dialog button: {dialog_button}") - return self.protocol.fedcm.click_fedcm_dialog_button() + return self.protocol.fedcm.click_fedcm_dialog_button(dialog_button) class SelectFedCMAccountAction: name = "select_fedcm_account" diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/testharnessreport-servo.js b/testing/web-platform/tests/tools/wptrunner/wptrunner/testharnessreport-servo.js index 4a27dc27ef..d6616739e6 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/testharnessreport-servo.js +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/testharnessreport-servo.js @@ -1,4 +1,9 @@ -var props = {output:%(output)d, debug: %(debug)s}; +var props = { + output:%(output)d, + timeout_multiplier: %(timeout_multiplier)s, + explicit_timeout: %(explicit_timeout)s, + debug: %(debug)s +}; var start_loc = document.createElement('a'); start_loc.href = location.href; setup(props); diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/testloader.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/testloader.py index aa266548d7..098e443b5c 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/testloader.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/testloader.py @@ -530,6 +530,7 @@ def get_test_queue_builder(**kwargs: Any) -> Tuple[TestQueueBuilder, Mapping[str TestGroup = namedtuple("TestGroup", ["group", "subsuite", "test_type", "metadata"]) +GroupMetadata = Mapping[str, Any] class TestQueueBuilder: @@ -573,7 +574,7 @@ class TestQueueBuilder: def tests_by_group(self, tests_by_type: TestsByType) -> Mapping[str, List[str]]: pass - def group_metadata(self, state: Mapping[str, Any]) -> Mapping[str, Any]: + def group_metadata(self, state: Mapping[str, Any]) -> GroupMetadata: return {"scope": "/"} def process_count(self, requested_processes: int, num_test_groups: int) -> int: @@ -654,7 +655,7 @@ class PathGroupedSource(TestQueueBuilder): groups[group_name].append(test.id) return groups - def group_metadata(self, state: Mapping[str, Any]) -> Mapping[str, Any]: + def group_metadata(self, state: Mapping[str, Any]) -> GroupMetadata: return {"scope": "/%s" % "/".join(state["prev_group_key"][2])} diff --git a/testing/web-platform/tests/tools/wptrunner/wptrunner/testrunner.py b/testing/web-platform/tests/tools/wptrunner/wptrunner/testrunner.py index 28d06f88ee..70da22f5b7 100644 --- a/testing/web-platform/tests/tools/wptrunner/wptrunner/testrunner.py +++ b/testing/web-platform/tests/tools/wptrunner/wptrunner/testrunner.py @@ -773,6 +773,8 @@ class TestRunnerManager(threading.Thread): test.max_assertion_count) file_result.extra["test_timeout"] = test.timeout * self.executor_kwargs['timeout_multiplier'] + if self.browser.browser_pid: + file_result.extra["browser_pid"] = self.browser.browser_pid self.logger.test_end(test.id, status, diff --git a/testing/web-platform/tests/tools/wptserve/wptserve/constants.py b/testing/web-platform/tests/tools/wptserve/wptserve/constants.py index 584f2cc1c7..292f0085dc 100644 --- a/testing/web-platform/tests/tools/wptserve/wptserve/constants.py +++ b/testing/web-platform/tests/tools/wptserve/wptserve/constants.py @@ -25,7 +25,6 @@ content_types = utils.invert_dict({ "text/plain": ["txt", "md"], "text/vtt": ["vtt"], "video/mp4": ["mp4", "m4v"], - "video/ogg": ["ogg", "ogv"], "video/webm": ["webm"], }) diff --git a/testing/web-platform/tests/tools/wptserve/wptserve/utils.py b/testing/web-platform/tests/tools/wptserve/wptserve/utils.py index e711e40725..403c359d27 100644 --- a/testing/web-platform/tests/tools/wptserve/wptserve/utils.py +++ b/testing/web-platform/tests/tools/wptserve/wptserve/utils.py @@ -141,6 +141,7 @@ def is_bad_port(port: int) -> bool: 2049, # nfs 3659, # apple-sasl 4045, # lockd + 4190, # sieve 5060, # sip 5061, # sips 6000, # x11 @@ -150,6 +151,7 @@ def is_bad_port(port: int) -> bool: 6667, # irc (default) 6668, # irc (alternate) 6669, # irc (alternate) + 6679, # osaut 6697, # irc+tls 10080, # amanda ] diff --git a/testing/web-platform/tests/trusted-types/DOMWindowTimers-setTimeout-setInterval.html b/testing/web-platform/tests/trusted-types/DOMWindowTimers-setTimeout-setInterval.html index 2ad47555a9..40310771e4 100644 --- a/testing/web-platform/tests/trusted-types/DOMWindowTimers-setTimeout-setInterval.html +++ b/testing/web-platform/tests/trusted-types/DOMWindowTimers-setTimeout-setInterval.html @@ -19,7 +19,14 @@ setInterval(script); }, "window.setInterval assigned via policy (successful Script transformation)."); - trustedTypes.createPolicy("default", {createScript: s => "window." + s + ".done()"}); + trustedTypes.createPolicy("default", {createScript: (s, _, sink) => { + if (s === "timeoutStringTest") { + assert_equals(sink, 'Window setTimeout'); + } else if (s === "intervalStringTest") { + assert_equals(sink, 'Window setInterval'); + } + return "window." + s + ".done()"; + }}); async_test(t => { window.timeoutStringTest = t; diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getAttributeType-svg.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getAttributeType-svg.html new file mode 100644 index 0000000000..e6f6cbdb1d --- /dev/null +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getAttributeType-svg.html @@ -0,0 +1,22 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js" ></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support/helper.sub.js"></script> + +<body> +<div id="target"></div> +<script> + test(t => { + assert_equals(trustedTypes.getAttributeType("script", "href"), null); + }, "trustedTypes.getAttributeType html script[href]"); + test(t => { + assert_equals(trustedTypes.getAttributeType("script", "href", "http://www.w3.org/2000/svg"), "TrustedScriptURL"); + }, "trustedTypes.getAttributeType svg script[href]"); + test(t => { + assert_equals(trustedTypes.getAttributeType("script", "href", "http://www.w3.org/2000/svg", "http://www.w3.org/1999/xlink"), "TrustedScriptURL"); + }, "trustedTypes.getAttributeType svg script[href] xlink href"); + test(t => { + assert_equals(trustedTypes.getAttributeType("script", "href", "http://www.w3.org/2000/svg", "http://www.w3.org/other"), null); + }, "trustedTypes.getAttributeType svg script[href] other href"); +</script> +</body> diff --git a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getPropertyType.html b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getPropertyType.html index 21aba668cc..84bcb8d839 100644 --- a/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getPropertyType.html +++ b/testing/web-platform/tests/trusted-types/TrustedTypePolicyFactory-getPropertyType.html @@ -104,5 +104,10 @@ }, `${elem}.${attr} is maybe defined`); } } + + test(t => { + assert_equals(trustedTypes.getPropertyType("img", "onerror"), null); + assert_equals(trustedTypes.getAttributeType("img", "onerror"), "TrustedScript"); + }, "getPropertyType vs getAttributeType for event handler."); </script> </body> diff --git a/testing/web-platform/tests/trusted-types/WEB_FEATURES.yml b/testing/web-platform/tests/trusted-types/WEB_FEATURES.yml new file mode 100644 index 0000000000..6ea31f1ef8 --- /dev/null +++ b/testing/web-platform/tests/trusted-types/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: trusted-types + files: "**" diff --git a/testing/web-platform/tests/uievents/mouse/mouse_boundary_events_after_reappending_last_over_target.tentative.html b/testing/web-platform/tests/uievents/mouse/mouse_boundary_events_after_reappending_last_over_target.tentative.html new file mode 100644 index 0000000000..f75404ad78 --- /dev/null +++ b/testing/web-platform/tests/uievents/mouse/mouse_boundary_events_after_reappending_last_over_target.tentative.html @@ -0,0 +1,152 @@ +<!doctype html> +<html> +<head> +<meta charset="utf-8"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> +<title>Temporary removal of "mouseover" target should not be considered as the last over element is changed</title> +<script src=/resources/testharness.js></script> +<script src=/resources/testharnessreport.js></script> +<script src=/resources/testdriver.js></script> +<script src=/resources/testdriver-actions.js></script> +<script src=/resources/testdriver-vendor.js></script> +<script> +"use strict"; + +function stringifyEvents(eventArray) { + if (!eventArray.length) { + return "[]"; + } + let result = ""; + eventArray.forEach(event => { + if (result != "") { + result += ", "; + } + result += `${event.type}@${ + event.target?.nodeType == Node.ELEMENT_NODE + ? `${event.target.localName}${ + event.target.id ? `#${event.target.id}` : "" + }` + : event.target?.localName + }`; + }); + return result; +} + +addEventListener("load", () => { + function promiseTick() { + return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); + } + + function append3NestedDivElementsToBody() { + const div1 = document.createElement("div"); + div1.setAttribute("id", "grandparent"); + div1.setAttribute("style", "width: 32px; height: 32px; margin: 32px"); + const div2 = document.createElement("div"); + div2.setAttribute("id", "parent"); + div2.setAttribute("style", "width: 32px; height: 32px"); + const div3 = document.createElement("div"); + div3.setAttribute("id", "child"); + div3.setAttribute("style", "width: 32px; height: 32px"); + div1.appendChild(div2); + div2.appendChild(div3); + document.body.appendChild(div1); + return { div1, div2, div3 }; + } + + promise_test(async () => { + const {div1, div2, div3} = append3NestedDivElementsToBody(); + const bodyRect = document.body.getBoundingClientRect(); + const div3Rect = div3.getBoundingClientRect(); + let events = []; + for (const type of ["mouseenter", "mouseleave", "mouseover", "mouseout", "mousemove"]) { + for (const node of [document.body, div1, div2, div3]) { + node.addEventListener(type, event => { + if (event.target == node) { + events.push({type: event.type, target: event.target}); + } + }, {capture: true}); + } + } + let firstMouseOver = true; + div3.addEventListener("mouseover", event => { + // Temporarily remove div3 from div2, but append to the same position. + div2.appendChild(div3); + events = []; + events.push({type: event.type, target: event.target}); + }, {once: true}); + await new test_driver.Actions() + .pointerMove(1, 1, {}) + .pointerMove(div3Rect.x + 10, div3Rect.y + 10, {}) + .send(); + const expectedEvents = [ + { type: "mouseover", target: div3 }, + { type: "mouseenter", target: document.body }, + { type: "mouseenter", target: div1 }, + { type: "mouseenter", target: div2 }, + { type: "mouseenter", target: div3 }, + { type: "mousemove", target: div3 }, + // Removing the node temporarily should not cause mouse boundary events. + ]; + assert_equals( + stringifyEvents(events), + stringifyEvents(expectedEvents), + ); + div1.remove(); + await new test_driver.Actions() + .pointerMove(1, 1, {}) + .send(); + }, + "After re-appending the last over element at mouseover, " + + "mouse boundary events should be fired as just over on the target"); + + promise_test(async () => { + const {div1, div2, div3} = append3NestedDivElementsToBody(); + const bodyRect = document.body.getBoundingClientRect(); + const div3Rect = div3.getBoundingClientRect(); + let events = []; + for (const type of ["mouseenter", "mouseleave", "mouseover", "mouseout", "mousemove"]) { + for (const node of [document.body, div1, div2, div3]) { + node.addEventListener(type, event => { + if (event.target == node) { + events.push({type: event.type, target: event.target}); + } + }, {capture: true}); + } + } + div3.addEventListener("mouseover", event => { + div3.addEventListener("mouseenter", () => { + // Temporarily remove div3 from div2, but append to the same position. + div2.appendChild(div3); + }, {once: true}); + events = []; + events.push({type: event.type, target: event.target}); + }, {once: true}); + await new test_driver.Actions() + .pointerMove(1, 1, {}) + .pointerMove(div3Rect.x + 10, div3Rect.y + 10, {}) + .send(); + const expectedEvents = [ + { type: "mouseover", target: div3 }, + { type: "mouseenter", target: document.body }, + { type: "mouseenter", target: div1 }, + { type: "mouseenter", target: div2 }, + { type: "mouseenter", target: div3 }, + { type: "mousemove", target: div3 }, + // Removing the node temporarily should not cause mouse boundary events. + ]; + assert_equals( + stringifyEvents(events), + stringifyEvents(expectedEvents), + ); + div1.remove(); + await new test_driver.Actions() + .pointerMove(1, 1, {}) + .send(); + }, + "After re-appending the last over element at mouseenter, " + + "mouse boundary events should not be fired"); +}, {once: true}); +</script> +</head> +<body style="padding-top: 32px"></body> +</html> diff --git a/testing/web-platform/tests/uievents/mouse/mousemove_prevent_default_action.tentative.html b/testing/web-platform/tests/uievents/mouse/mousemove_prevent_default_action.html index 4caf98087f..4caf98087f 100644 --- a/testing/web-platform/tests/uievents/mouse/mousemove_prevent_default_action.tentative.html +++ b/testing/web-platform/tests/uievents/mouse/mousemove_prevent_default_action.html diff --git a/testing/web-platform/tests/url/resources/urltestdata.json b/testing/web-platform/tests/url/resources/urltestdata.json index 9f1be0449c..b9a199daab 100644 --- a/testing/web-platform/tests/url/resources/urltestdata.json +++ b/testing/web-platform/tests/url/resources/urltestdata.json @@ -3590,6 +3590,34 @@ "search": "", "hash": "" }, + { + "input": "file:.", + "base": null, + "href": "file:///", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/", + "search": "", + "hash": "" + }, + { + "input": "file:.", + "base": "http://www.example.com/test", + "href": "file:///", + "protocol": "file:", + "username": "", + "password": "", + "host": "", + "hostname": "", + "port": "", + "pathname": "/", + "search": "", + "hash": "" + }, "# Based on http://trac.webkit.org/browser/trunk/LayoutTests/fast/url/host.html", "Basic canonicalization, uppercase should be converted to lowercase", { diff --git a/testing/web-platform/tests/url/url-statics-parse.any.js b/testing/web-platform/tests/url/url-statics-parse.any.js new file mode 100644 index 0000000000..0822e9da07 --- /dev/null +++ b/testing/web-platform/tests/url/url-statics-parse.any.js @@ -0,0 +1,50 @@ +// This intentionally does not use resources/urltestdata.json to preserve resources. +[ + { + "url": undefined, + "base": undefined, + "expected": false + }, + { + "url": "aaa:b", + "base": undefined, + "expected": true + }, + { + "url": undefined, + "base": "aaa:b", + "expected": false + }, + { + "url": "aaa:/b", + "base": undefined, + "expected": true + }, + { + "url": undefined, + "base": "aaa:/b", + "expected": true + }, + { + "url": "https://test:test", + "base": undefined, + "expected": false + }, + { + "url": "a", + "base": "https://b/", + "expected": true + } +].forEach(({ url, base, expected }) => { + test(() => { + if (expected == false) { + assert_equals(URL.parse(url, base), null); + } else { + assert_equals(URL.parse(url, base).href, new URL(url, base).href); + } + }, `URL.parse(${url}, ${base})`); +}); + +test(() => { + assert_not_equals(URL.parse("https://example/"), URL.parse("https://example/")); +}, `URL.parse() should return a unique object`); diff --git a/testing/web-platform/tests/visual-viewport/WEB_FEATURES.yml b/testing/web-platform/tests/visual-viewport/WEB_FEATURES.yml new file mode 100644 index 0000000000..4eecec544c --- /dev/null +++ b/testing/web-platform/tests/visual-viewport/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: visual-viewport + files: "**" diff --git a/testing/web-platform/tests/wai-aria/role/grid-roles.html b/testing/web-platform/tests/wai-aria/role/grid-roles.html index c40771ba9f..2ca3705cf0 100644 --- a/testing/web-platform/tests/wai-aria/role/grid-roles.html +++ b/testing/web-platform/tests/wai-aria/role/grid-roles.html @@ -43,6 +43,12 @@ </div> </div> + <!-- + CORE-AAM requires that, for elements with roles not contained in the + required context, user agents must ignore the role token and return the + computed role as if the ignored role token had not been included. + See https://w3c.github.io/core-aam/#roleMappingComputedRole + --> <span role="row" data-testname="orphaned row outside the context of table" class="ex-generic">x</span> <span role="rowgroup" data-testname="orphaned rowgroup outside the context of row" class="ex-generic">x</span> <div role="gridcell" data-testname="orphaned div with gridcell role outside the context of row" class="ex-generic">x</div> diff --git a/testing/web-platform/tests/wai-aria/role/list-roles.html b/testing/web-platform/tests/wai-aria/role/list-roles.html index 71c672c601..3fd3bfa0e6 100644 --- a/testing/web-platform/tests/wai-aria/role/list-roles.html +++ b/testing/web-platform/tests/wai-aria/role/list-roles.html @@ -18,11 +18,18 @@ <div role="listitem" data-testname="last simple listitem" data-expectedrole="listitem" class="ex">x</div> </div> +<!-- + CORE-AAM requires that, for elements with roles not contained in the + required context, user agents must ignore the role token and return the + computed role as if the ignored role token had not been included. + See https://w3c.github.io/core-aam/#roleMappingComputedRole +--> <div role="listitem" data-testname="orphan div with listitem role" class="ex-generic">x</div> <p role="listitem" data-testname="orphan p with listitem role" data-expectedrole="paragraph" class="ex">x</p> <script> AriaUtils.verifyRolesBySelector(".ex"); +AriaUtils.verifyGenericRolesBySelector(".ex-generic"); </script> </body> diff --git a/testing/web-platform/tests/wai-aria/role/listbox-roles.html b/testing/web-platform/tests/wai-aria/role/listbox-roles.html index 5020822927..3d26f04d51 100644 --- a/testing/web-platform/tests/wai-aria/role/listbox-roles.html +++ b/testing/web-platform/tests/wai-aria/role/listbox-roles.html @@ -31,6 +31,12 @@ </li> </ul> +<!-- + CORE-AAM requires that, for elements with roles not contained in the + required context, user agents must ignore the role token and return the + computed role as if the ignored role token had not been included. + See https://w3c.github.io/core-aam/#roleMappingComputedRole +--> <nav role="option" data-testname="orphaned option outside the context of listbox" data-expectedrole="navigation" class="ex">x </nav> diff --git a/testing/web-platform/tests/wai-aria/role/menu-roles.html b/testing/web-platform/tests/wai-aria/role/menu-roles.html index 4af4d8d2ff..fed15769ab 100644 --- a/testing/web-platform/tests/wai-aria/role/menu-roles.html +++ b/testing/web-platform/tests/wai-aria/role/menu-roles.html @@ -55,6 +55,12 @@ </div> </div> +<!-- + CORE-AAM requires that, for elements with roles not contained in the + required context, user agents must ignore the role token and return the + computed role as if the ignored role token had not been included. + See https://w3c.github.io/core-aam/#roleMappingComputedRole +--> <nav role="menuitem" data-testname="orphaned menuitem outside the context of menu/menubar" data-expectedrole="navigation" class="ex">x </nav> @@ -76,6 +82,7 @@ <script> AriaUtils.verifyRolesBySelector(".ex"); + AriaUtils.verifyGenericRolesBySelector(".ex-generic"); </script> </body> diff --git a/testing/web-platform/tests/wai-aria/role/tab-roles.html b/testing/web-platform/tests/wai-aria/role/tab-roles.html index e3d14741c8..6561614dd8 100644 --- a/testing/web-platform/tests/wai-aria/role/tab-roles.html +++ b/testing/web-platform/tests/wai-aria/role/tab-roles.html @@ -82,12 +82,18 @@ <div role="tabpanel" data-testname="role is tabpanel as sibling to ul with child role none li elements" data-expectedrole="tabpanel" class="ex">Tab one's stuff</div> <div role="tabpanel" data-testname="role is tabpanel as sibling to ul with child role none li elements (duplicate)" data-expectedrole="tabpanel" class="ex">Tab two's stuff</div> -<!--orphan tab semantics --> +<!-- + CORE-AAM requires that, for elements with roles not contained in the + required context, user agents must ignore the role token and return the + computed role as if the ignored role token had not been included. + See https://w3c.github.io/core-aam/#roleMappingComputedRole +--> <button role="tab" data-testname="orphan button with tab role" data-expectedrole="button" class="ex">x</button> <span role="tab" data-testname="orphan span with tab role" class="ex-generic">x</span> <script> AriaUtils.verifyRolesBySelector(".ex"); + AriaUtils.verifyGenericRolesBySelector(".ex-generic"); </script> </body> diff --git a/testing/web-platform/tests/wai-aria/role/tree-roles.html b/testing/web-platform/tests/wai-aria/role/tree-roles.html index eb1084040d..5d179fbcbf 100644 --- a/testing/web-platform/tests/wai-aria/role/tree-roles.html +++ b/testing/web-platform/tests/wai-aria/role/tree-roles.html @@ -102,6 +102,12 @@ </tbody> </table> +<!-- + CORE-AAM requires that, for elements with roles not contained in the + required context, user agents must ignore the role token and return the + computed role as if the ignored role token had not been included. + See https://w3c.github.io/core-aam/#roleMappingComputedRole +--> <nav role="treeitem" data-testname="orphaned treeitem outside the context of tree" data-expectedrole="navigation" class="ex">x</nav> <button role="treeitem" data-testname="orphaned button with treeitem role outside tree context" data-expectedrole="button" class="ex">x</button> diff --git a/testing/web-platform/tests/wasm/jsapi/module/moduleSource.tentative.any.js b/testing/web-platform/tests/wasm/jsapi/module/moduleSource.tentative.any.js index a3d09d55d6..8a94cdd48c 100644 --- a/testing/web-platform/tests/wasm/jsapi/module/moduleSource.tentative.any.js +++ b/testing/web-platform/tests/wasm/jsapi/module/moduleSource.tentative.any.js @@ -9,6 +9,9 @@ setup(() => { test(() => { assert_equals(typeof AbstractModuleSource, "undefined"); +}, "AbstractModuleSource is not a global"); + +test(() => { const AbstractModuleSource = Object.getPrototypeOf(WebAssembly.Module); assert_equals(AbstractModuleSource.name, "AbstractModuleSource"); assert_not_equals(AbstractModuleSource, Function); @@ -16,22 +19,13 @@ test(() => { test(() => { const AbstractModuleSourceProto = Object.getPrototypeOf(WebAssembly.Module.prototype); - assert_not_equals(AbstractModuleSourceProto, Object); + assert_not_equals(AbstractModuleSourceProto, Object.prototype); const AbstractModuleSource = Object.getPrototypeOf(WebAssembly.Module); assert_equals(AbstractModuleSource.prototype, AbstractModuleSourceProto); }, "AbstractModuleSourceProto intrinsic"); test(() => { - const builder = new WasmModuleBuilder(); - - builder - .addFunction("fn", kSig_v_v) - .addBody([]) - .exportFunc(); - builder.addMemory(0, 256, true); - - const buffer = builder.toBuffer() - const module = new WebAssembly.Module(buffer); + const module = new WebAssembly.Module(emptyModuleBinary); const AbstractModuleSource = Object.getPrototypeOf(WebAssembly.Module); const toStringTag = Object.getOwnPropertyDescriptor(AbstractModuleSource.prototype, Symbol.toStringTag).get; diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js index c7a4f0d437..e1c2703899 100644 --- a/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js +++ b/testing/web-platform/tests/wasm/webapi/esm-integration/resources/worker-source-phase.js @@ -1,3 +1,10 @@ import source modSource from "./worker.wasm"; +import { pm } from "./worker-helper.js"; assert_true(modSource instanceof WebAssembly.Module); assert_true(await import.source("./worker.wasm") === modSource); + +await WebAssembly.instantiate(modSource, { + "./worker-helper.js": { + "pm": pm + } +});
\ No newline at end of file diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/script-src-allows-wasm.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/script-src-allows-wasm.tentative.html new file mode 100644 index 0000000000..07faf9a5a1 --- /dev/null +++ b/testing/web-platform/tests/wasm/webapi/esm-integration/script-src-allows-wasm.tentative.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<title>script-src allows Wasm execution</title> +<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + setup({allow_uncaught_exception: true}); + + const test_load = async_test( + "Importing a WebAssembly module should be allowed by script-src CSP."); + + window.log = []; + document.addEventListener("securitypolicyviolation", (e) => { + window.log.push(e.violatedDirective); + }); + + window.addEventListener("load", test_load.step_func_done(ev => { + assert_array_equals(log, ["executed"]); + })); +</script> +<script type="module" src="./resources/execute-start.wasm"></script> diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.html new file mode 100644 index 0000000000..c1232730ea --- /dev/null +++ b/testing/web-platform/tests/wasm/webapi/esm-integration/script-src-blocks-wasm.tentative.html @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<title>script-src blocks Wasm execution</title> +<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline';"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script> + setup({allow_uncaught_exception: true}); + + const test_load = async_test( + "Importing a WebAssembly module should be guarded by script-src CSP."); + + window.log = []; + document.addEventListener("securitypolicyviolation", (e) => { + window.log.push(e.violatedDirective); + }); + + window.addEventListener("load", test_load.step_func_done(ev => { + assert_array_equals(log, ["script-src-elem"]); + })); +</script> +<script type="module" src="./resources/execute-start.wasm"></script> diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/source-phase.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/source-phase.tentative.html index eb415a4c6f..870b16bd0a 100644 --- a/testing/web-platform/tests/wasm/webapi/esm-integration/source-phase.tentative.html +++ b/testing/web-platform/tests/wasm/webapi/esm-integration/source-phase.tentative.html @@ -13,7 +13,7 @@ const AbstractModuleSource = Object.getPrototypeOf(WebAssembly.Module); assert_equals(AbstractModuleSource.name, "AbstractModuleSource"); assert_true(exportedNamesSource instanceof AbstractModuleSource); -assert_array_equals(WebAssembly.Module.exports(exportedNamesSource).sort(), +assert_array_equals(WebAssembly.Module.exports(exportedNamesSource).map(({ name }) => name).sort(), ["func", "glob", "mem", "tab"]); const wasmImportFromWasmSource = await import.source("./resources/wasm-import-from-wasm.wasm"); diff --git a/testing/web-platform/tests/wasm/webapi/esm-integration/worker-import-source-phase.tentative.html b/testing/web-platform/tests/wasm/webapi/esm-integration/worker-import-source-phase.tentative.html new file mode 100644 index 0000000000..e193f3efc6 --- /dev/null +++ b/testing/web-platform/tests/wasm/webapi/esm-integration/worker-import-source-phase.tentative.html @@ -0,0 +1,13 @@ +<!DOCTYPE html> +<title>Testing import of WebAssembly source phase from JavaScript worker</title> + +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script type=module> +setup({ single_test: true }); +const worker = new Worker("resources/worker-source-phase.js", { type: "module" }); +worker.onmessage = (msg) => { + assert_equals(msg.data, 42); + done(); +} +</script> diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/progress.tentative.html b/testing/web-platform/tests/web-animations/interfaces/Animation/progress.tentative.html new file mode 100644 index 0000000000..38f37cbfeb --- /dev/null +++ b/testing/web-platform/tests/web-animations/interfaces/Animation/progress.tentative.html @@ -0,0 +1,81 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Animation.progress</title> +<link rel="help" +href="https://drafts.csswg.org/web-animations-2/#the-progress-of-an-animation"> +<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(t => { + const animation = new Animation(null); + animation.startTime = document.timeline.currentTime; + assert_time_equals_literal(animation.currentTime, 0, 'currentTime is zero'); + assert_equals(animation.progress, null, 'progress is unresolved'); +}, 'progress of a newly created animation without an effect is unresolved'); + +test(t => { + // currentTime should be unresolved because the animation has no associated + // timeline. + const animation = new Animation(new KeyframeEffect(createDiv(t), null), null); + assert_equals(animation.currentTime, null, 'currentTime is unresolved'); + assert_equals(animation.progress, null, 'progress is unresolved'); +}, 'progress of an animation whose currentTime is unresolved is unresolved.'); + +test(t => { + const animation = new Animation(new KeyframeEffect(createDiv(t), null, + { duration: 0 }), document.timeline); + // Set the startTime to 20 seconds into the future. + animation.startTime = document.timeline.currentTime + 20 * MS_PER_SEC; + assert_less_than(animation.currentTime, 0, 'currentTime is negative'); + assert_approx_equals(animation.progress, 0, 0.01, 'progress is zero'); +}, "progress of an animation whose effect's endTime is zero is zero if its " + + "currentTime is negative."); + +test(t => { + const animation = new Animation(new KeyframeEffect(createDiv(t), null, + { duration: 0 }), document.timeline); + animation.startTime = document.timeline.currentTime; + assert_time_equals_literal(animation.currentTime, 0, 'currentTime is zero'); + assert_approx_equals(animation.progress, 1, 0.01, 'progress is one'); + + animation.startTime = document.timeline.currentTime - 20 * MS_PER_SEC; + assert_greater_than(animation.currentTime, 0, 'currentTime greater than zero'); + assert_approx_equals(animation.progress, 1, 0.01, 'progress is one'); +}, "progress of an animation whose effect's endTime is zero is one if its " + + "currentTime is non-negative."); + +test(t => { + const animation = new Animation(new KeyframeEffect(createDiv(t), null, + { duration: Infinity }), document.timeline); + + animation.startTime = document.timeline.currentTime - 20 * MS_PER_SEC; + assert_greater_than(animation.currentTime, 0, 'currentTime is positive'); + assert_approx_equals(animation.progress, 0, 0.01, 'progress is zero'); +}, "progress of an animation whose effect's endTime is infinity is zero."); + +test(t => { + const animation = new Animation(new KeyframeEffect(createDiv(t), null, + 200 * MS_PER_SEC), document.timeline); + animation.playbackRate = 2; + + animation.startTime = document.timeline.currentTime - 50 * MS_PER_SEC; + assert_time_equals_literal(animation.currentTime, 100 * MS_PER_SEC, 'currentTime is 100s'); + assert_approx_equals(animation.progress, 0.5, 0.01, 'progress is zero'); + + animation.startTime = document.timeline.currentTime - 100 * MS_PER_SEC; + assert_time_equals_literal(animation.currentTime, 200 * MS_PER_SEC, 'currentTime is 200s'); + assert_approx_equals(animation.progress, 1, 0.01, 'progress is one'); + + animation.startTime = document.timeline.currentTime - 150 * MS_PER_SEC; + assert_time_equals_literal(animation.currentTime, 300 * MS_PER_SEC, 'currentTime is 300s'); + assert_approx_equals(animation.progress, 1, 0.01, 'progress is still one'); +}, "progress of an animation is calculated by currentTime / effect endTime."); + +</script> +</body> diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/scroll-timeline-progress.tentative.html b/testing/web-platform/tests/web-animations/interfaces/Animation/scroll-timeline-progress.tentative.html new file mode 100644 index 0000000000..b40e72482b --- /dev/null +++ b/testing/web-platform/tests/web-animations/interfaces/Animation/scroll-timeline-progress.tentative.html @@ -0,0 +1,166 @@ +<!DOCTYPE html> +<meta charset=utf-8> +<title>Animation.progress</title> +<link rel="help" +href="https://drafts.csswg.org/web-animations-2/#the-progress-of-an-animation"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="../../testcommon.js"></script> +<script src="../../../scroll-animations/scroll-timelines/testcommon.js"></script> +<body> + <style> + .scroller { + overflow-x: hidden; + overflow-y: auto; + height: 200px; + width: 100px; + will-change: transform; + } + .contents { + height: 1200px; + width: 100%; + } + #target { + height: 100px; + width: 100px; + background-color: green; + margin-top: -1000px; + } + </style> +<div id="log"></div> +<script> +'use strict'; + +promise_test(async t => { + const animation = createScrollLinkedAnimation(t); + const scroller = animation.timeline.source; + const maxScroll = scroller.scrollHeight - scroller.clientHeight; + + assert_equals(animation.currentTime, null, + "The current time is null in Idle state."); + assert_equals(animation.progress, null, + "The progress is null since the currentTime is unresolved."); + + animation.play(); + assert_true(animation.pending, "Animation is in the pending state."); + assert_equals(animation.currentTime, null, + "The current time remains null while in the pending state."); + assert_equals(animation.progress, null, + "The progress is null since the currentTime is unresolved."); + + await animation.ready; + // Verify initial start and current times once ready. + assert_percents_equal(animation.currentTime, 0, + "The current time is resolved when ready."); + assert_equals(animation.progress, 0, "The progress is should be zero."); + + scroller.scrollTop = 0.4 * maxScroll; + + await waitForNextFrame(); + assert_percents_equal(animation.currentTime, 40, + "currentTime reflects progress as a percentage"); + assert_approx_equals(animation.progress, 0.4, 0.01, + "The progress is should match the scroll progress."); +}, "animation.progress reflects the progress of a scroll animation as a "+ + "number between 0 and 1"); + +promise_test(async t => { + const animation = createScrollLinkedAnimation(t); + const scroller = animation.timeline.source; + const maxScroll = scroller.scrollHeight - scroller.clientHeight; + + animation.play(); + await animation.ready; + // Verify initial start and current times once ready. + assert_percents_equal(animation.currentTime, 0, + "The current time is resolved when ready."); + assert_equals(animation.progress, 0, "The progress is should be zero."); + + scroller.scrollTop = 0.4 * maxScroll; + await waitForNextFrame(); + + let timing = animation.effect.getComputedTiming(); + // iteration duration should be 100%, progress should reflect 40%. + assert_percents_equal(timing.duration, 100); + assert_percents_equal(animation.currentTime, 40, + "currentTime reflects progress as a percentage"); + assert_approx_equals(animation.progress, 0.4, 0.01, + "The progress is should match the scroll progress."); + timing = animation.effect.getComputedTiming(); + + animation.effect.updateTiming({ iterations: 2 }); + + timing = animation.effect.getComputedTiming(); + // iteration duration should be 50%, progress should still reflect 40% + // as it measures currentTime / effect endTime. + assert_percents_equal(timing.duration, 50); + assert_percents_equal(animation.currentTime, 40, + "currentTime reflects progress as a percentage"); + assert_approx_equals(animation.progress, 0.4, 0.01, + "The progress is should match the scroll progress."); +}, "animation.progress reflects the overall progress of a scroll animation " + + "with multiple iterations."); + +promise_test(async t => { + const scroller = createScroller(t); + const view_timeline = createViewTimeline(t); + + const animation = createAnimation(t); + animation.rangeStart = { rangeName: 'contain', offset: CSS.percent(10) }; + animation.rangeEnd = { rangeName: 'contain', offset: CSS.percent(90) }; + animation.timeline = view_timeline; + + await animation.ready; + + // Cover range is [0, 300] + // Contain range is [100, 200] + // rangeStart offset of 10% => 110px + // rangeEnd offset of 90% => 190px + const target_pct = 40; + const target_scroll_top = 110 + (target_pct / 100) * (190 - 110); + scroller.scrollTop = target_scroll_top; + await waitForNextFrame(); + + const start_time = 110 / 300 * 100; + const timeline_time = target_scroll_top / 300 * 100; + const expected_current_time = timeline_time - start_time; + assert_percents_equal(animation.currentTime, expected_current_time, + "currentTime reflects progress as a percentage"); + assert_approx_equals(animation.progress, target_pct / 100, 0.01, + "progress should reflect fraction of view timeline range scroll through."); +}, "animation.progress reflects the overall progress of a scroll animation " + + "that uses a view-timeline."); + +promise_test(async t => { + const scroller = createScroller(t); + const view_timeline = createViewTimeline(t); + + const animation = createAnimation(t); + animation.rangeStart = { rangeName: 'contain', offset: CSS.percent(10) }; + animation.rangeEnd = { rangeName: 'contain', offset: CSS.percent(90) }; + animation.timeline = view_timeline; + + await animation.ready; + + // Cover range is [0, 300] + // Contain range is [100, 200] + // rangeStart offset of 10% => 110px + // rangeEnd offset of 10% => 190px + + scroller.scrollTop = 100; + await waitForNextFrame(); + let timing = animation.effect.getComputedTiming(); + assert_less_than(animation.currentTime.value, 0, "currentTime is negative"); + assert_approx_equals(animation.progress, 0, 0.01, "progress is zero when " + + "scroll offset is less than range start."); + + scroller.scrollTop = 200; + await waitForNextFrame(); + assert_approx_equals(animation.currentTime.value, timing.endTime.value, 0.01, + "currentTime has reached endTime"); + assert_approx_equals(animation.progress, 1, 0.01, "progress is one when " + + "scroll offset goes past than range end."); +}, "progresss of a view-timeline is bounded between 0 and 1."); + +</script> +</body> diff --git a/testing/web-platform/tests/web-animations/interfaces/Animation/style-change-events.html b/testing/web-platform/tests/web-animations/interfaces/Animation/style-change-events.html index 0ec21657e3..d2d6e9d4c9 100644 --- a/testing/web-platform/tests/web-animations/interfaces/Animation/style-change-events.html +++ b/testing/web-platform/tests/web-animations/interfaces/Animation/style-change-events.html @@ -169,6 +169,7 @@ const tests = { // no effect. rangeStart: UsePropertyTest(animation => animation.rangeStart), rangeEnd: UsePropertyTest(animation => animation.rangeEnd), + progress: UsePropertyTest(animation => animation.progress), replaceState: UsePropertyTest(animation => animation.replaceState), ready: UsePropertyTest(animation => animation.ready), finished: UsePropertyTest(animation => { diff --git a/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001.html b/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001.html index 271a47b301..2252248e23 100644 --- a/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001.html +++ b/testing/web-platform/tests/web-animations/interfaces/KeyframeEffect/processing-a-keyframes-argument-001.html @@ -37,7 +37,6 @@ const gNonAnimatableProps = [ 'transitionTimingFunction', 'contain', 'direction', - 'display', 'textCombineUpright', 'textOrientation', 'unicodeBidi', diff --git a/testing/web-platform/tests/web-animations/responsive/background-position-responsive.html b/testing/web-platform/tests/web-animations/responsive/background-position-responsive.html new file mode 100644 index 0000000000..7b1a6cd4db --- /dev/null +++ b/testing/web-platform/tests/web-animations/responsive/background-position-responsive.html @@ -0,0 +1,53 @@ +<!DOCTYPE html> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Animation with neutral keyframe is responsive to change in underlying style</title> + <link rel="help" href="https://www.w3.org/TR/web-animations-1/#the-effect-value-of-a-keyframe-animation-effect"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="responsive-test.js"></script> +</head> +<body></body> +<script type="text/javascript"> + promise_test(async t => { + const responsiveTest = + createResponsiveTest(t, { + property: 'backgroundPosition', + to: '100px 100px' + }); + await responsiveTest.ready; + responsiveTest.underlyingValue = '20px 60px'; + responsiveTest.assertResponsive([ + {at: 0.25, is: '40px 70px'}, + {at: 0.75, is: '80px 90px'}, + ]); + responsiveTest.underlyingValue = '60px 20px'; + responsiveTest.assertResponsive([ + {at: 0.25, is: '70px 40px'}, + {at: 0.75, is: '90px 80px'}, + ]); + }, 'Animating from a neutral keyframe when the underlying style is ' + + 'explicitly set'); + + promise_test(async t => { + const responsiveTest = + createResponsiveTest(t, { + property: 'backgroundPosition', + from: 'inherit', + to: '100px 100px' + }); + await responsiveTest.ready; + responsiveTest.inheritedValue = '20px 60px'; + responsiveTest.assertResponsive([ + {at: 0.25, is: '40px 70px'}, + {at: 0.75, is: '80px 90px'}, + ]); + responsiveTest.inheritedValue = '60px 20px'; + responsiveTest.assertResponsive([ + {at: 0.25, is: '70px 40px'}, + {at: 0.75, is: '90px 80px'}, + ]); + }, 'Animating from a neutral keyframe when the underlying style is ' + + 'inherited from the parent'); +</script> diff --git a/testing/web-platform/tests/web-animations/responsive/box-shadow-responsive.html b/testing/web-platform/tests/web-animations/responsive/box-shadow-responsive.html new file mode 100644 index 0000000000..4f5325dca6 --- /dev/null +++ b/testing/web-platform/tests/web-animations/responsive/box-shadow-responsive.html @@ -0,0 +1,61 @@ +<!DOCTYPE html> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Animation with neutral keyframe is responsive to change in underlying style</title> + <link rel="help" href="https://www.w3.org/TR/web-animations-1/#the-effect-value-of-a-keyframe-animation-effect"> + <script src="/resources/testharness.js"></script> + <script src="/resources/testharnessreport.js"></script> + <script src="responsive-test.js"></script> +</head> +<body></body> +<script type="text/javascript"> + promise_test(async t => { + const responsiveTest = + createResponsiveTest(t, { + property: 'boxShadow', + from: 'green 20px 20px 20px 20px', + to: 'inherit', + }); + await responsiveTest.ready; + responsiveTest.inheritedValue = 'blue 0px 0px 0px 0px'; + responsiveTest.assertResponsive([ + {at: 0.25, is: 'rgb(0, 96, 64) 15px 15px 15px 15px'}, + {at: 0.75, is: 'rgb(0, 32, 191) 5px 5px 5px 5px'}, + ]); + responsiveTest.inheritedValue = 'yellow 100px 100px 100px 100px'; + responsiveTest.assertResponsive([ + {at: 0.25, is: 'rgb(64, 160, 0) 40px 40px 40px 40px'}, + {at: 0.75, is: 'rgb(191, 223, 0) 80px 80px 80px 80px'}, + ]); + }, 'Animating to inherit responsive to change in style'); + + promise_test(async t => { + const responsiveTest = + createResponsiveTest(t, { + property: 'boxShadow', + from: 'inherit', + to: 'green 20px 20px 20px 20px', + }); + await responsiveTest.ready; + responsiveTest.inheritedValue = + 'blue 0px 0px 0px 0px, yellow 100px 100px 100px 100px'; + responsiveTest.assertResponsive([ + { + at: 0.25, + is: 'rgb(0, 32, 191) 5px 5px 5px 5px, ' + + 'rgba(255, 255, 0, 0.75) 75px 75px 75px 75px' + }, + { + at: 0.75, + is: 'rgb(0, 96, 64) 15px 15px 15px 15px, ' + + 'rgba(255, 255, 0, 0.25) 25px 25px 25px 25px' + }, + ]); + responsiveTest.inheritedValue = 'yellow 100px 100px 100px 100px'; + responsiveTest.assertResponsive([ + {at: 0.25, is: 'rgb(191, 223, 0) 80px 80px 80px 80px'}, + {at: 0.75, is: 'rgb(64, 160, 0) 40px 40px 40px 40px'}, + ]); + }, 'Animating from inherit responsive to change in style'); +</script> diff --git a/testing/web-platform/tests/web-animations/responsive/neutral-keyframe-ref.html b/testing/web-platform/tests/web-animations/responsive/neutral-keyframe-ref.html new file mode 100644 index 0000000000..3893330fb9 --- /dev/null +++ b/testing/web-platform/tests/web-animations/responsive/neutral-keyframe-ref.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<html> +<head> + <style type="text/css"> + #block { + background-color: green; + height: 100px; + width: 100px; + transform: translate(100px); + } + </style> +</head> +<body> + <div id="block"></div> +</body> +</html> diff --git a/testing/web-platform/tests/web-animations/responsive/neutral-keyframe.html b/testing/web-platform/tests/web-animations/responsive/neutral-keyframe.html new file mode 100644 index 0000000000..813aa7c04c --- /dev/null +++ b/testing/web-platform/tests/web-animations/responsive/neutral-keyframe.html @@ -0,0 +1,41 @@ +<!DOCTYPE html> +<html class="reftest-wait"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>Animation with neutral keyframe is responsive to change in underlying style</title> + <link rel="help" href="https://www.w3.org/TR/web-animations-1/#the-effect-value-of-a-keyframe-animation-effect"> + <link rel="match" href="neutral-keyframe-ref.html"> + <script src="/common/reftest-wait.js"></script> + <script src="/web-animations/testcommon.js"></script> + <style type="text/css"> + #block { + background-color: green; + height: 100px; + width: 100px; + } + </style> +</head> +<body> + <div id="block"></div> +</body> +<script> + window.onload = async () => { + const target = document.getElementById('block'); + const anim = target.animate({ translate: '200px' }, + { + duration: 10000, + easing: 'steps(1,jump-end)' + }); + await anim.ready; + await waitForNextFrame(); + // The neutral keyframe value changes from transform 'none' to '100px'. + // Since using jump-end for the easing function, the animated value is the + // underlying (neutral) value. If the box is not translated, then the change + // to the underlying value is not taking effect. + target.style.translate = '100px'; + await waitForNextFrame(); + takeScreenshot(); + }; +</script> +</html> diff --git a/testing/web-platform/tests/web-animations/responsive/responsive-test.js b/testing/web-platform/tests/web-animations/responsive/responsive-test.js new file mode 100644 index 0000000000..feca53259c --- /dev/null +++ b/testing/web-platform/tests/web-animations/responsive/responsive-test.js @@ -0,0 +1,65 @@ +class ResponsiveTest { + constructor(target, property, keyframes) { + this.property = property; + this.target = target; + this.duration = 1000; + this.anim = target.animate(keyframes, this.duration); + this.anim.pause(); + } + + get ready() { + return new Promise(resolve => { + this.anim.ready.then(resolve); + }); + } + + set underlyingValue(value) { + this.target.style[this.property] = value; + } + + set inheritedValue(value) { + this.target.parentElement.style[this.property] = value; + } + + // The testCases are of the form: + // [{at: <fractional_progress>, is: <computed style> }, ...] + assertResponsive(testCases) { + for (let i = 0; i < testCases.length; i++) { + const testCase = testCases[i]; + this.anim.currentTime = this.duration * testCase.at; + assert_equals(getComputedStyle(this.target)[this.property], testCase.is, + `${this.property} at ${testCase.at}`); + } + } +} + +// Creates a test that allows setting the underlying style of the target +// element or its parent. +// Options are of the form: +// property: required property in camelcase form as used in the +// web animation API. +// from: optional starting keyframe as a string. +// to: optional ending keyframe as a string. +function createResponsiveTest(test, options) { + const parent = document.createElement('div'); + const target = document.createElement('div'); + document.body.appendChild(parent); + parent.appendChild(target); + const property = options.property; + const keyframes = []; + const createKeyframe = (value) => { + const keyframe = {}; + keyframe[property] = value; + return keyframe; + } + if (options.from) { + keyframes.push(createKeyframe(options.from)); + } + if (options.to) { + keyframes.push(createKeyframe(options.to)); + } + test.add_cleanup(() => { + parent.remove(); + }); + return new ResponsiveTest(target, property, keyframes); +} diff --git a/testing/web-platform/tests/web-animations/responsive/width.html b/testing/web-platform/tests/web-animations/responsive/width.html new file mode 100644 index 0000000000..9f5bf5a29d --- /dev/null +++ b/testing/web-platform/tests/web-animations/responsive/width.html @@ -0,0 +1,88 @@ +<!DOCTYPE html> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<style> + +body { width: 500px } + +#child { width: 20px } + +</style> + +<div id='container'> + <div id='element'> + <div id='child'> + </div> + </div> +</div> + +<script> +var element = document.getElementById('element'); +var container = document.getElementById('container'); + +test(function() { + element.style.fontSize = '10px'; + var player = element.animate([{width: '3em'}, {width: '5em'}], 10); + player.pause(); + player.currentTime = 5; + element.style.fontSize = '20px'; + assert_equals(getComputedStyle(element).width, '80px'); +}, 'width responsive to style changes'); + +test(function() { + element.style.fontSize = '10px'; + var player = element.animate([{width: '40px'}, {width: 'calc(40px - 2em)'}], 10); + player.pause(); + + player.currentTime = 5; + element.style.fontSize = '40px'; + assert_equals(getComputedStyle(element).width, '20px'); + + player.currentTime = 7.5; + assert_equals(getComputedStyle(element).width, '10px'); +}, 'width clamped to 0px on keyframes'); + +test(function() { + container.style.width = 'auto'; + var player = element.animate([{width: 'inherit'}, {width: '20px'}], 4000); + player.pause(); + + player.currentTime = 1000; + assert_equals(getComputedStyle(element).width, '500px'); + + container.style.width = '100px'; + assert_equals(getComputedStyle(element).width, '80px'); + + container.style.width = 'min-content'; + assert_equals(getComputedStyle(element).width, '20px'); + + container.style.width = '100px'; + assert_equals(getComputedStyle(element).width, '80px'); + + container.style.width = 'max-content'; + assert_equals(getComputedStyle(element).width, '20px'); + + container.style.width = '100px'; + assert_equals(getComputedStyle(element).width, '80px'); + + container.style.width = 'fit-content'; + assert_equals(getComputedStyle(element).width, '20px'); +}, 'width responsive to inherited changes from keyword'); + +test(function() { + container.style.fontSize = '10px'; + container.style.width = '100px'; + var player = element.animate([{width: 'inherit'}, {width: '200px'}], 4000); + player.pause(); + + player.currentTime = 1000; + assert_equals(getComputedStyle(element).width, '125px'); + + container.style.width = '120px'; + assert_equals(getComputedStyle(element).width, '140px'); + + container.style.width = '100px'; + assert_equals(getComputedStyle(element).width, '125px'); +}, 'width responsive to inherited changes from length'); + +</script> diff --git a/testing/web-platform/tests/webaudio/the-audio-api/the-audionode-interface/audionode.html b/testing/web-platform/tests/webaudio/the-audio-api/the-audionode-interface/audionode.html index 0b57d27e8e..3d14cd5b01 100644 --- a/testing/web-platform/tests/webaudio/the-audio-api/the-audionode-interface/audionode.html +++ b/testing/web-platform/tests/webaudio/the-audio-api/the-audionode-interface/audionode.html @@ -41,7 +41,7 @@ should( context.destination.numberOfOutputs, 'AudioContext.destination.numberOfOutputs') - .beEqualTo(0); + .beEqualTo(1); // Try calling connect() method with illegal values. should( diff --git a/testing/web-platform/tests/webcodecs/audio-encoder-config.https.any.js b/testing/web-platform/tests/webcodecs/audio-encoder-config.https.any.js index 3be8eb3f6d..559ff3203b 100644 --- a/testing/web-platform/tests/webcodecs/audio-encoder-config.https.any.js +++ b/testing/web-platform/tests/webcodecs/audio-encoder-config.https.any.js @@ -111,6 +111,15 @@ const invalidConfigs = [ }, }, }, + { + comment: 'Bitrate is too low for Opus', + config: { + codec: 'opus', + sampleRate: 48000, + numberOfChannels: 2, + bitrate: 1, + }, + }, ]; invalidConfigs.forEach(entry => { @@ -138,15 +147,6 @@ invalidConfigs.forEach(entry => { const validButUnsupportedConfigs = [ { - comment: 'Bitrate is too low', - config: { - codec: 'opus', - sampleRate: 48000, - numberOfChannels: 2, - bitrate: 1, - }, - }, - { comment: 'Unrecognized codec', config: { codec: 'bogus', diff --git a/testing/web-platform/tests/webcodecs/videoFrame-serialization.crossAgentCluster.https.html b/testing/web-platform/tests/webcodecs/videoFrame-serialization.crossAgentCluster.https.html index 967f02ec2c..e96725bb28 100644 --- a/testing/web-platform/tests/webcodecs/videoFrame-serialization.crossAgentCluster.https.html +++ b/testing/web-platform/tests/webcodecs/videoFrame-serialization.crossAgentCluster.https.html @@ -12,15 +12,15 @@ const HELPER = '/webcodecs/videoFrame-serialization.crossAgentCluster.helper.htm const CROSSORIGIN_BASE = get_host_info().HTTPS_NOTSAMESITE_ORIGIN; const CROSSORIGIN_HELPER = CROSSORIGIN_BASE + HELPER; -promise_test(async () => { +promise_test(async (t) => { const target = (await appendIframe(CROSSORIGIN_HELPER)).contentWindow; - let frame = createVideoFrame(20); + let frame = createVideoFrame(t, 20); assert_false(await canSerializeVideoFrame(target, frame)); }, 'Verify frames cannot be passed accross the different agent clusters'); -promise_test(async () => { +promise_test(async (t) => { const target = (await appendIframe(CROSSORIGIN_HELPER)).contentWindow; - let frame = createVideoFrame(80); + let frame = createVideoFrame(t, 80); assert_false(await canTransferVideoFrame(target, frame)); }, 'Verify frames cannot be transferred accross the different agent clusters'); @@ -31,17 +31,19 @@ function appendIframe(src) { return new Promise(resolve => frame.onload = () => resolve(frame)); }; -function createVideoFrame(ts) { +function createVideoFrame(test, timestamp) { let data = new Uint8Array([ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, ]); - return new VideoFrame(data, { - timestamp: ts, + const frame = new VideoFrame(data, { + timestamp, codedWidth: 2, codedHeight: 2, format: 'RGBA', }); + test.add_cleanup(() => frame.close()); + return frame; } function canSerializeVideoFrame(target, vf) { diff --git a/testing/web-platform/tests/webdriver/tests/bidi/__init__.py b/testing/web-platform/tests/webdriver/tests/bidi/__init__.py index 98b670f89f..c8715183b0 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/__init__.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/__init__.py @@ -71,6 +71,15 @@ def int_interval(start: int, end: int) -> Callable[[Any], None]: return _ +def assert_cookies(cookies, expected_cookies): + assert len(cookies) == len(expected_cookies) + + expected = sorted(expected_cookies, key=lambda cookie: cookie["name"]) + actual = sorted(cookies, key=lambda cookie: cookie["name"]) + + recursive_compare(expected, actual) + + def assert_handle(obj: Mapping[str, Any], should_contain_handle: bool) -> None: if should_contain_handle: assert "handle" in obj, f"Result should contain `handle`. Actual: {obj}" @@ -128,13 +137,21 @@ async def get_element_dimensions(bidi_session, context, element): return remote_mapping_to_dict(result["value"]) -async def get_viewport_dimensions(bidi_session, context: str): - expression = """ - ({ - height: window.innerHeight || document.documentElement.clientHeight, - width: window.innerWidth || document.documentElement.clientWidth, - }); - """ +async def get_viewport_dimensions(bidi_session, context: str, with_scrollbar: bool = True): + if with_scrollbar == True: + expression = """ + ({ + height: window.innerHeight, + width: window.innerWidth, + }); + """ + else: + expression = """ + ({ + height: document.documentElement.clientHeight, + width: document.documentElement.clientWidth, + }); + """ result = await bidi_session.script.evaluate( expression=expression, target=ContextTarget(context["context"]), diff --git a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/classic_interop/window_handle.py b/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/classic_interop/window_handle.py deleted file mode 100644 index 4f36fba197..0000000000 --- a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/classic_interop/window_handle.py +++ /dev/null @@ -1,7 +0,0 @@ -import pytest - -pytestmark = pytest.mark.asyncio - - -async def test_top_level_context_id_equals_window_handle(top_context, current_session): - assert top_context["context"] == current_session.window_handle diff --git a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/locate_nodes/locator.py b/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/locate_nodes/locator.py index 656eaddc1f..e560fa9239 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/locate_nodes/locator.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/locate_nodes/locator.py @@ -48,134 +48,88 @@ async def test_find_by_locator(bidi_session, inline, top_context, type, value): recursive_compare(expected, result["nodes"]) -@pytest.mark.parametrize("ignore_case,match_type,max_depth,value,expected", [ - (True, "full", None, "bar", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 1, - "children": [], - "localName": "strong", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }, - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 1, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (False, "full", None, "BAR", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 1, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (True, "partial", None, "ba", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 1, - "localName": "strong", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }, - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 1, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (False, "partial", None, "ba", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 1, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (True, "full", 0, "foobarbarbaz", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 4, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (False, "full", 0, "foobarBARbaz", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 4, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (True, "partial", 0, "bar", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 4, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ), - (False, "partial", 0, "BAR", [ - { - "type": "node", - "sharedId": any_string, - "value": { - "attributes": {}, - "childNodeCount": 4, - "localName": "span", - "namespaceURI": "http://www.w3.org/1999/xhtml", - "nodeType": 1, - } - }] - ) +@pytest.mark.parametrize("locator,expected_nodes_values", [ + ({ + "type": "innerText", + "ignoreCase": True, + "matchType": "full", + "value": "bar" + }, ["strong", "span"]), + ({ + "type": "innerText", + "ignoreCase": False, + "matchType": "full", + "value": "BAR" + }, ["span"]), + ({ + "type": "innerText", + "ignoreCase": True, + "matchType": "partial", + "value": "ba" + }, ["strong", "span"]), + ({ + "type": "innerText", + "ignoreCase": False, + "matchType": "partial", + "value": "ba" + }, ["strong"]), + ({ + "type": "innerText", + "ignoreCase": True, + "matchType": "full", + "maxDepth": 0, + "value": "foobarbarbaz" + }, ["body"]), + ({ + "type": "innerText", + "ignoreCase": False, + "matchType": "full", + "maxDepth": 0, + "value": "foobarBARbaz" + }, ["body"]), + ({ + "type": "innerText", + "ignoreCase": True, + "matchType": "partial", + "maxDepth": 0, + "value": "bar" + }, ["body"]), + ({ + "type": "innerText", + "ignoreCase": False, + "matchType": "partial", + "maxDepth": 0, + "value": "BAR" + }, ["body"]), + ({ + + "type": "innerText", + "ignoreCase": True, + "matchType": "full", + "maxDepth": 1, + "value": "foobarbarbaz" + }, ["div"]), + ({ + "type": "innerText", + "ignoreCase": False, + "matchType": "full", + "maxDepth": 1, + "value": "foobarBARbaz" + }, ["div"]), + ({ + "type": "innerText", + "ignoreCase": True, + "matchType": "partial", + "maxDepth": 1, + "value": "bar" + }, ["div"]), + ({ + "type": "innerText", + "ignoreCase": False, + "matchType": "partial", + "maxDepth": 1, + "value": "BAR" + }, ["div"]), ], ids=[ "ignore_case_true_full_match_no_max_depth", "ignore_case_false_full_match_no_max_depth", @@ -185,23 +139,29 @@ async def test_find_by_locator(bidi_session, inline, top_context, type, value): "ignore_case_false_full_match_max_depth_zero", "ignore_case_true_partial_match_max_depth_zero", "ignore_case_false_partial_match_max_depth_zero", + "ignore_case_true_full_match_max_depth_one", + "ignore_case_false_full_match_max_depth_one", + "ignore_case_true_partial_match_max_depth_one", + "ignore_case_false_partial_match_max_depth_one", ]) @pytest.mark.asyncio -async def test_find_by_inner_text(bidi_session, inline, top_context, ignore_case, match_type, max_depth, value, expected): +async def test_find_by_inner_text(bidi_session, inline, top_context, locator, expected_nodes_values): url = inline("""<div>foo<span><strong>bar</strong></span><span>BAR</span>baz</div>""") await bidi_session.browsing_context.navigate( context=top_context["context"], url=url, wait="complete" ) + # Construct expected nodes list with the expected nodes values emitting other fields. + expected = [{ + "type": "node", + "value": { + "localName": node_value, + } + } for node_value in expected_nodes_values] + result = await bidi_session.browsing_context.locate_nodes( context=top_context["context"], - locator={ - "type": "innerText", - "value": value, - "ignoreCase": ignore_case, - "matchType": match_type, - "maxDepth": max_depth - } + locator=locator ) recursive_compare(expected, result["nodes"]) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/set_viewport/viewport.py b/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/set_viewport/viewport.py index 60f9e47040..2e8126b1f8 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/set_viewport/viewport.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/browsing_context/set_viewport/viewport.py @@ -184,3 +184,65 @@ async def test_persists_on_reload(bidi_session, inline, new_tab): ) assert await get_viewport_dimensions(bidi_session, new_tab) == test_viewport + + +@pytest.mark.asyncio +@pytest.mark.parametrize( + "use_horizontal_scrollbar, use_vertical_scrollbar", + [ + (True, False), + (False, True), + (True, True), + ], + ids=["horizontal", "vertical", "both"], +) +@pytest.mark.parametrize( + "doctype", + ["html", "html_quirks"], + ids=["standard", "quirks"], +) +async def test_with_scrollbars( + bidi_session, + inline, + new_tab, + use_horizontal_scrollbar, + use_vertical_scrollbar, + doctype, +): + viewport_dimensions = await get_viewport_dimensions(bidi_session, new_tab) + + width = 100 + if use_horizontal_scrollbar: + width = viewport_dimensions["width"] + 100 + + height = 100 + if use_vertical_scrollbar: + height = viewport_dimensions["height"] + 100 + + html = f"""<div style="width: {width}px; height: {height}px;">foo</div>""" + page_url = inline(html, doctype=doctype) + + await bidi_session.browsing_context.navigate( + context=new_tab["context"], url=page_url, wait="complete" + ) + + test_viewport = {"width": 499, "height": 599} + + assert await get_viewport_dimensions(bidi_session, new_tab) != test_viewport + + await bidi_session.browsing_context.set_viewport( + context=new_tab["context"], viewport=test_viewport + ) + + assert await get_viewport_dimensions(bidi_session, new_tab) == test_viewport + + viewport_without_scrollbar = await get_viewport_dimensions( + bidi_session, new_tab, with_scrollbar=False + ) + + # The side which has scrollbar takes up space on the other side + # (e.g. if we have a horizontal scroll height is going to be smaller than viewport height) + if use_horizontal_scrollbar: + assert viewport_without_scrollbar["height"] < test_viewport["height"] + if use_vertical_scrollbar: + assert viewport_without_scrollbar["width"] < test_viewport["width"] diff --git a/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/invalid.py b/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/invalid.py index 0ace04e8bc..5397dc7b62 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/invalid.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/invalid.py @@ -52,3 +52,14 @@ async def test_params_origin_invalid_type(bidi_session, origin): state="granted", origin=origin, ) + + +@pytest.mark.parametrize("user_context", [False, 42, {}, [], None]) +async def test_params_origin_invalid_type(bidi_session, user_context): + with pytest.raises(error.InvalidArgumentException): + await bidi_session.permissions.set_permission( + descriptor={"name": "geolocation"}, + state="granted", + origin="https://example.com", + user_context=user_context, + ) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/set_permission.py b/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/set_permission.py index 92ebed1e63..45c50dbf88 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/set_permission.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/external/permissions/set_permission/set_permission.py @@ -16,7 +16,7 @@ async def test_set_permission(bidi_session, new_tab, url): origin = await get_context_origin(bidi_session, new_tab) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" await bidi_session.permissions.set_permission( descriptor={"name": "geolocation"}, @@ -24,7 +24,7 @@ async def test_set_permission(bidi_session, new_tab, url): origin=origin, ) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "granted" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "granted" await bidi_session.permissions.set_permission( descriptor={"name": "geolocation"}, @@ -32,7 +32,7 @@ async def test_set_permission(bidi_session, new_tab, url): origin=origin, ) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "denied" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "denied" await bidi_session.permissions.set_permission( descriptor={"name": "geolocation"}, @@ -40,7 +40,7 @@ async def test_set_permission(bidi_session, new_tab, url): origin=origin, ) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" @pytest.mark.asyncio @@ -73,7 +73,7 @@ async def test_set_permission_new_context(bidi_session, new_tab, url): origin = await get_context_origin(bidi_session, new_tab) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" await bidi_session.permissions.set_permission( descriptor={"name": "geolocation"}, @@ -81,7 +81,7 @@ async def test_set_permission_new_context(bidi_session, new_tab, url): origin=origin, ) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "granted" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "granted" new_context = await bidi_session.browsing_context.create(type_hint="tab") assert new_tab["context"] != new_context["context"] @@ -92,7 +92,7 @@ async def test_set_permission_new_context(bidi_session, new_tab, url): ) # See https://github.com/w3c/permissions/issues/437. - assert await get_permission_state(bidi_session, new_context, "geolocation") == "granted" + assert await get_permission_state(bidi_session, new_context, "geolocation") == "granted" @pytest.mark.parametrize("origin", ['UNKNOWN', '']) @@ -117,4 +117,42 @@ async def test_set_permission_origin_unknown(bidi_session, new_tab, origin, url) state="granted", origin=origin, ) - assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + + +@pytest.mark.asyncio +async def test_set_permission_user_context(bidi_session, new_tab, url, create_user_context): + test_url = url("/common/blank.html", protocol="https") + + user_context = await create_user_context() + # new_tab is in the default user context. new_tab2 is in the non-default user context. + new_tab2 = await bidi_session.browsing_context.create(type_hint="tab", user_context=user_context) + + # Navigate a context in the default user context. + await bidi_session.browsing_context.navigate( + context=new_tab["context"], + url=test_url, + wait="complete", + ) + + # Navigate a context in the non-default user context. + await bidi_session.browsing_context.navigate( + context=new_tab2["context"], + url=test_url, + wait="complete", + ) + + origin = await get_context_origin(bidi_session, new_tab) + + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + assert await get_permission_state(bidi_session, new_tab2, "geolocation") == "prompt" + + await bidi_session.permissions.set_permission( + descriptor={"name": "geolocation"}, + state="granted", + origin=origin, + user_context=user_context, + ) + + assert await get_permission_state(bidi_session, new_tab, "geolocation") == "prompt" + assert await get_permission_state(bidi_session, new_tab2, "geolocation") == "granted" diff --git a/testing/web-platform/tests/webdriver/tests/bidi/integration/__init__.py b/testing/web-platform/tests/webdriver/tests/bidi/integration/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/testing/web-platform/tests/webdriver/tests/bidi/integration/__init__.py diff --git a/testing/web-platform/tests/webdriver/tests/bidi/integration/cookies_with_network_events.py b/testing/web-platform/tests/webdriver/tests/bidi/integration/cookies_with_network_events.py new file mode 100644 index 0000000000..e7fddbb1c4 --- /dev/null +++ b/testing/web-platform/tests/webdriver/tests/bidi/integration/cookies_with_network_events.py @@ -0,0 +1,201 @@ +import pytest + +from webdriver.bidi.modules.script import ContextTarget +from webdriver.bidi.modules.storage import BrowsingContextPartitionDescriptor + +from .. import assert_cookies + +pytestmark = pytest.mark.asyncio + +PNG_BLACK_DOT = "/webdriver/tests/bidi/storage/get_cookies/support/black_dot.png" + + +async def test_top_context( + bidi_session, + new_tab, + inline, + setup_network_test, + wait_for_event, + wait_for_future_safe, +): + cookie_name = "foo" + cookie_value = "bar" + url = inline( + "<div>with cookies</div>", + parameters={"pipe": f"header(Set-Cookie, {cookie_name}={cookie_value})"}, + ) + + await bidi_session.browsing_context.navigate( + context=new_tab["context"], url=url, wait="complete" + ) + + BEFORE_REQUEST_SENT_EVENT = "network.beforeRequestSent" + network_events = await setup_network_test(events=[BEFORE_REQUEST_SENT_EVENT]) + events = network_events[BEFORE_REQUEST_SENT_EVENT] + on_before_request_sent = wait_for_event(BEFORE_REQUEST_SENT_EVENT) + + await bidi_session.browsing_context.reload( + context=new_tab["context"], wait="complete" + ) + + await wait_for_future_safe(on_before_request_sent) + + result = await bidi_session.storage.get_cookies( + partition=BrowsingContextPartitionDescriptor(new_tab["context"]) + ) + + assert_cookies(result["cookies"], events[0]["request"]["cookies"]) + + await bidi_session.storage.delete_cookies() + + +@pytest.mark.parametrize("domain_1", ["", "alt"], ids=["same_origin", "cross_origin"]) +async def test_iframe( + bidi_session, + new_tab, + inline, + setup_network_test, + wait_for_event, + wait_for_future_safe, + domain_1, +): + cookie_name = "bar" + cookie_value = "foo" + iframe_url = inline( + "<div id='in-iframe'>with cookies</div>", + domain=domain_1, + parameters={"pipe": f"header(Set-Cookie, {cookie_name}={cookie_value})"}, + ) + + await bidi_session.browsing_context.navigate( + context=new_tab["context"], url=iframe_url, wait="complete" + ) + + BEFORE_REQUEST_SENT_EVENT = "network.beforeRequestSent" + network_events = await setup_network_test(events=[BEFORE_REQUEST_SENT_EVENT]) + events = network_events[BEFORE_REQUEST_SENT_EVENT] + on_before_request_sent = wait_for_event(BEFORE_REQUEST_SENT_EVENT) + + page_url = inline(f"<iframe src='{iframe_url}'></iframe>") + await bidi_session.browsing_context.navigate( + context=new_tab["context"], url=page_url, wait="complete" + ) + + await wait_for_future_safe(on_before_request_sent) + + all_contexts = await bidi_session.browsing_context.get_tree(root=new_tab["context"]) + iframe_context = all_contexts[0]["children"][0]["context"] + + result = await bidi_session.storage.get_cookies( + partition=BrowsingContextPartitionDescriptor(iframe_context) + ) + + # Find the network event which belongs to the iframe. + event_for_iframe = next( + event for event in events if event["context"] == iframe_context + ) + + assert_cookies(result["cookies"], event_for_iframe["request"]["cookies"]) + + # Remove the coookie. + await bidi_session.storage.delete_cookies() + + +@pytest.mark.parametrize("domain_1", ["", "alt"], ids=["same_origin", "cross_origin"]) +async def test_fetch( + bidi_session, + new_tab, + setup_network_test, + wait_for_event, + fetch, + wait_for_future_safe, + url, + domain_1, +): + # Clean up cookies in case some other tests failed before cleaning up. + await bidi_session.storage.delete_cookies() + + cookie_name = "foo" + cookie_value = "bar" + # Add `Access-Control-Allow-Origin` header for cross-origin request to work. + request_url = url( + "/webdriver/tests/support/http_handlers/headers.py?header=Access-Control-Allow-Origin:*", + domain=domain_1, + ) + + await bidi_session.script.evaluate( + expression=f"document.cookie = '{cookie_name}={cookie_value}';", + target=ContextTarget(new_tab["context"]), + await_promise=False, + ) + + BEFORE_REQUEST_SENT_EVENT = "network.beforeRequestSent" + network_events = await setup_network_test(events=[BEFORE_REQUEST_SENT_EVENT]) + events = network_events[BEFORE_REQUEST_SENT_EVENT] + + on_before_request_sent = wait_for_event(BEFORE_REQUEST_SENT_EVENT) + await fetch(request_url, method="GET") + await wait_for_future_safe(on_before_request_sent) + + result = await bidi_session.storage.get_cookies( + partition=BrowsingContextPartitionDescriptor(new_tab["context"]) + ) + assert_cookies(result["cookies"], events[0]["request"]["cookies"]) + + # Remove the coookie. + await bidi_session.storage.delete_cookies() + + +@pytest.mark.parametrize("domain_1", ["", "alt"], ids=["same_origin", "cross_origin"]) +async def test_image( + bidi_session, + new_tab, + setup_network_test, + wait_for_event, + wait_for_future_safe, + url, + inline, + domain_1, +): + # Clean up cookies in case some other tests failed before cleaning up. + await bidi_session.storage.delete_cookies() + + cookie_name = "bar" + cookie_value = "foo" + + image_url = url(PNG_BLACK_DOT) + + await bidi_session.browsing_context.navigate( + context=new_tab["context"], url=image_url, wait="complete" + ) + + await bidi_session.script.evaluate( + expression=f"document.cookie = '{cookie_name}={cookie_value}';", + target=ContextTarget(new_tab["context"]), + await_promise=False, + ) + + BEFORE_REQUEST_SENT_EVENT = "network.beforeRequestSent" + network_events = await setup_network_test(events=[BEFORE_REQUEST_SENT_EVENT]) + events = network_events[BEFORE_REQUEST_SENT_EVENT] + + page_with_image = inline(f"<img src='{image_url}'>", domain=domain_1) + + on_before_request_sent = wait_for_event(BEFORE_REQUEST_SENT_EVENT) + await bidi_session.browsing_context.navigate( + context=new_tab["context"], url=page_with_image, wait="complete" + ) + await wait_for_future_safe(on_before_request_sent) + + result = await bidi_session.storage.get_cookies( + partition=BrowsingContextPartitionDescriptor(new_tab["context"]) + ) + + # Find the network event which belongs to the image. + event_for_image = next( + event for event in events if event["request"]["url"] == image_url + ) + assert_cookies(result["cookies"], event_for_image["request"]["cookies"]) + + # Remove the coookie. + await bidi_session.storage.delete_cookies() diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/__init__.py b/testing/web-platform/tests/webdriver/tests/bidi/network/__init__.py index 9bbc6f5daf..2e6376287b 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/__init__.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/__init__.py @@ -6,6 +6,7 @@ from .. import ( any_list, any_string, any_string_or_null, + assert_cookies, recursive_compare, ) @@ -15,21 +16,6 @@ def assert_bytes_value(bytes_value): any_string(bytes_value["value"]) -def assert_cookies(event_cookies, expected_cookies): - assert len(event_cookies) == len(expected_cookies) - - # Simple helper to find a cookie by key and value only. - def match_cookie(cookie, expected): - for key in expected: - if cookie[key] != expected[key]: - return False - - return True - - for cookie in expected_cookies: - assert next(c for c in event_cookies if match_cookie(c, cookie)) is not None - - def assert_headers(event_headers, expected_headers): # The browser sets request headers, only assert that the expected headers # are included in the request's headers. @@ -349,3 +335,9 @@ BEFORE_REQUEST_SENT_EVENT = "network.beforeRequestSent" FETCH_ERROR_EVENT = "network.fetchError" RESPONSE_COMPLETED_EVENT = "network.responseCompleted" RESPONSE_STARTED_EVENT = "network.responseStarted" + +PHASE_TO_EVENT_MAP = { + "authRequired": [AUTH_REQUIRED_EVENT, assert_response_event], + "beforeRequestSent": [BEFORE_REQUEST_SENT_EVENT, assert_before_request_sent_event], + "responseStarted": [RESPONSE_STARTED_EVENT, assert_response_event], +} diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/add_intercept/contexts.py b/testing/web-platform/tests/webdriver/tests/bidi/network/add_intercept/contexts.py index 83dfa5560f..7606b2368b 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/add_intercept/contexts.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/add_intercept/contexts.py @@ -5,15 +5,69 @@ from webdriver.bidi.modules.script import ScriptEvaluateResultException from .. import ( assert_before_request_sent_event, + assert_response_event, PAGE_EMPTY_HTML, PAGE_EMPTY_TEXT, BEFORE_REQUEST_SENT_EVENT, RESPONSE_COMPLETED_EVENT, RESPONSE_STARTED_EVENT, + PHASE_TO_EVENT_MAP, ) @pytest.mark.asyncio +@pytest.mark.parametrize("domain", ["", "alt"], ids=["same_origin", "cross_origin"]) +@pytest.mark.parametrize("phase", ["beforeRequestSent", "responseStarted"]) +async def test_frame_context( + bidi_session, + url, + inline, + top_context, + add_intercept, + fetch, + setup_network_test, + wait_for_event, + wait_for_future_safe, + domain, + phase +): + await setup_network_test( + events=[ + BEFORE_REQUEST_SENT_EVENT, + RESPONSE_STARTED_EVENT, + RESPONSE_COMPLETED_EVENT, + ], + contexts=[top_context["context"]], + ) + + frame_url = inline("<div>foo</div>") + test_url = inline(f"<iframe src='{frame_url}'></iframe>", domain=domain) + await bidi_session.browsing_context.navigate( + url=test_url, context=top_context["context"], wait="complete" + ) + + # Retrieve the context for the iframe. + contexts = await bidi_session.browsing_context.get_tree(root=top_context["context"]) + assert len(contexts[0]["children"]) == 1 + frame = contexts[0]["children"][0] + + # Add an intercept. + text_url = url(PAGE_EMPTY_TEXT) + await add_intercept( + phases=[phase], + url_patterns=[{"type": "string", "pattern": text_url}], + contexts=[top_context["context"]], + ) + + # Request in the iframe context should be blocked. + [event_name, assert_network_event] = PHASE_TO_EVENT_MAP[phase] + on_network_event = wait_for_event(event_name) + asyncio.ensure_future(fetch(text_url, context=frame)) + event = await wait_for_future_safe(on_network_event) + assert_network_event(event, is_blocked=True) + + +@pytest.mark.asyncio @pytest.mark.parametrize("phase", ["beforeRequestSent", "responseStarted"]) async def test_other_context( bidi_session, @@ -22,7 +76,9 @@ async def test_other_context( add_intercept, fetch, setup_network_test, - phase, + wait_for_event, + wait_for_future_safe, + phase ): # Subscribe to network events only in top_context await setup_network_test( @@ -47,13 +103,17 @@ async def test_other_context( url_patterns=[{"type": "string", "pattern": text_url}], ) - # Request to top_context should be blocked and throw a ScriptEvaluateResultException - # from the AbortController. - with pytest.raises(ScriptEvaluateResultException): - await fetch(text_url, context=top_context) - # Request to other_context should not be blocked. - await fetch(text_url, context=other_context) + # Request to top_context should be blocked. + [event_name, assert_network_event] = PHASE_TO_EVENT_MAP[phase] + on_network_event = wait_for_event(event_name) + asyncio.ensure_future(fetch(text_url, context=top_context)) + event = await wait_for_future_safe(on_network_event) + assert_network_event(event, is_blocked=True) + + # Request to other_context should not be blocked because we are not + # subscribed to network events. Wait for fetch to resolve successfully. + await asyncio.ensure_future(fetch(text_url, context=other_context)) @pytest.mark.asyncio diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/before_request_sent/before_request_sent.py b/testing/web-platform/tests/webdriver/tests/bidi/network/before_request_sent/before_request_sent.py index c92337e507..95a790e37c 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/before_request_sent/before_request_sent.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/before_request_sent/before_request_sent.py @@ -393,3 +393,26 @@ async def test_redirect_navigation( # Check that both requests share the same requestId assert events[0]["request"]["request"] == events[1]["request"]["request"] + + +@pytest.mark.asyncio +async def test_url_with_fragment( + url, wait_for_event, wait_for_future_safe, fetch, setup_network_test +): + fragment_url = url(f"{PAGE_EMPTY_HTML}#foo") + + network_events = await setup_network_test(events=[BEFORE_REQUEST_SENT_EVENT]) + events = network_events[BEFORE_REQUEST_SENT_EVENT] + + on_before_request_sent = wait_for_event(BEFORE_REQUEST_SENT_EVENT) + await fetch(fragment_url, method="GET") + await wait_for_future_safe(on_before_request_sent) + + assert len(events) == 1 + + # Assert that the event contains the full fragment URL in requestData. + assert_before_request_sent_event( + events[0], + expected_request={"method": "GET", "url": fragment_url}, + redirect_count=0, + ) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/conftest.py b/testing/web-platform/tests/webdriver/tests/bidi/network/conftest.py index 7813530c4c..424fa8b5c7 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/conftest.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/conftest.py @@ -1,13 +1,9 @@ -import json - import asyncio -import pytest import pytest_asyncio from webdriver.bidi.error import NoSuchInterceptException -from webdriver.bidi.modules.script import ContextTarget -from . import PAGE_EMPTY_HTML, PAGE_EMPTY_TEXT, RESPONSE_COMPLETED_EVENT +from . import PAGE_EMPTY_TEXT @pytest_asyncio.fixture @@ -40,102 +36,6 @@ async def add_intercept(bidi_session): pass -@pytest.fixture -def fetch(bidi_session, top_context, configuration): - """Perform a fetch from the page of the provided context, default to the - top context. - """ - - async def fetch( - url, method="GET", headers=None, context=top_context, timeout_in_seconds=3 - ): - method_arg = f"method: '{method}'," - - headers_arg = "" - if headers is not None: - headers_arg = f"headers: {json.dumps(headers)}," - - timeout_in_seconds = timeout_in_seconds * configuration["timeout_multiplier"] - - # Wait for fetch() to resolve a response and for response.text() to - # resolve as well to make sure the request/response is completed when - # the helper returns. - await bidi_session.script.evaluate( - expression=f""" - {{ - const controller = new AbortController(); - setTimeout(() => controller.abort(), {timeout_in_seconds * 1000}); - fetch("{url}", {{ - {method_arg} - {headers_arg} - signal: controller.signal - }}).then(response => response.text()); - }}""", - target=ContextTarget(context["context"]), - await_promise=True, - ) - - return fetch - - -@pytest_asyncio.fixture -async def setup_network_test( - bidi_session, - subscribe_events, - wait_for_event, - wait_for_future_safe, - top_context, - url, -): - """Navigate the current top level context to the provided url and subscribe - to network.beforeRequestSent. - - Returns an `events` dictionary in which the captured network events will be added. - The keys of the dictionary are network event names (eg. "network.beforeRequestSent"), - and the value is an array of collected events. - """ - listeners = [] - - async def _setup_network_test(events, test_url=url(PAGE_EMPTY_HTML), contexts=None): - nonlocal listeners - - # Listen for network.responseCompleted for the initial navigation to - # make sure this event will not be captured unexpectedly by the tests. - await bidi_session.session.subscribe( - events=[RESPONSE_COMPLETED_EVENT], contexts=[top_context["context"]] - ) - on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT) - - await bidi_session.browsing_context.navigate( - context=top_context["context"], - url=test_url, - wait="complete", - ) - await wait_for_future_safe(on_response_completed) - await bidi_session.session.unsubscribe( - events=[RESPONSE_COMPLETED_EVENT], contexts=[top_context["context"]] - ) - - await subscribe_events(events, contexts) - - network_events = {} - for event in events: - network_events[event] = [] - - async def on_event(method, data, event=event): - network_events[event].append(data) - - listeners.append(bidi_session.add_event_listener(event, on_event)) - - return network_events - - yield _setup_network_test - - # cleanup - for remove_listener in listeners: - remove_listener() - - @pytest_asyncio.fixture async def setup_blocked_request( bidi_session, diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/provide_response/request.py b/testing/web-platform/tests/webdriver/tests/bidi/network/provide_response/request.py index de9492f0a5..f8cc3fb676 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/provide_response/request.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/provide_response/request.py @@ -11,7 +11,7 @@ pytestmark = pytest.mark.asyncio @pytest.mark.parametrize("navigate", [False, True], ids=["fetch", "navigate"]) async def test_provide_response_auth_required( - setup_blocked_request, subscribe_events, wait_for_event, bidi_session, navigate + setup_blocked_request, subscribe_events, wait_for_event, bidi_session, navigate, wait_for_future_safe ): request = await setup_blocked_request("authRequired", navigate=navigate) @@ -28,13 +28,13 @@ async def test_provide_response_auth_required( await bidi_session.network.provide_response(request=request) - await on_auth_required + await wait_for_future_safe(on_auth_required) @pytest.mark.parametrize("phase", ["beforeRequestSent", "responseStarted"]) @pytest.mark.parametrize("navigate", [False, True], ids=["fetch", "navigate"]) async def test_provide_response_phase( - setup_blocked_request, subscribe_events, wait_for_event, bidi_session, phase, navigate + setup_blocked_request, subscribe_events, wait_for_event, bidi_session, phase, navigate, wait_for_future_safe ): request = await setup_blocked_request(phase, navigate=navigate) @@ -58,10 +58,10 @@ async def test_provide_response_phase( await bidi_session.network.provide_response(request=request) - await on_response_completed + await wait_for_future_safe(on_response_completed) if phase == "beforeRequestSent": - await on_response_started + await wait_for_future_safe(on_response_started) if navigate: - await on_load + await wait_for_future_safe(on_load) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/response_completed/response_completed.py b/testing/web-platform/tests/webdriver/tests/bidi/network/response_completed/response_completed.py index b9b4ae727e..56b9461642 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/response_completed/response_completed.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/response_completed/response_completed.py @@ -368,3 +368,28 @@ async def test_redirect_document( # Check that the last 2 requests share the same request id assert events[1]["request"]["request"] == events[2]["request"]["request"] + + +@pytest.mark.asyncio +async def test_url_with_fragment( + url, wait_for_event, wait_for_future_safe, fetch, setup_network_test +): + fragment_url = url(f"{PAGE_EMPTY_HTML}#foo") + + network_events = await setup_network_test(events=[RESPONSE_COMPLETED_EVENT]) + events = network_events[RESPONSE_COMPLETED_EVENT] + + on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT) + await fetch(fragment_url, method="GET") + await wait_for_future_safe(on_response_completed) + + assert len(events) == 1 + + # Assert that the event contains the full fragment URL both in requestData + # and responseData + assert_response_event( + events[0], + expected_request={"method": "GET", "url": fragment_url}, + expected_response={"url": fragment_url}, + redirect_count=0, + ) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/network/response_started/response_started.py b/testing/web-platform/tests/webdriver/tests/bidi/network/response_started/response_started.py index fb99073fb3..6c10714ca8 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/network/response_started/response_started.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/network/response_started/response_started.py @@ -311,3 +311,28 @@ async def test_redirect(bidi_session, url, fetch, setup_network_test): # Check that both requests share the same requestId assert events[0]["request"]["request"] == events[1]["request"]["request"] + + +@pytest.mark.asyncio +async def test_url_with_fragment( + url, wait_for_event, wait_for_future_safe, fetch, setup_network_test +): + fragment_url = url(f"{PAGE_EMPTY_HTML}#foo") + + network_events = await setup_network_test(events=[RESPONSE_STARTED_EVENT]) + events = network_events[RESPONSE_STARTED_EVENT] + + on_response_started = wait_for_event(RESPONSE_STARTED_EVENT) + await fetch(fragment_url, method="GET") + await wait_for_future_safe(on_response_started) + + assert len(events) == 1 + + # Assert that the event contains the full fragment URL both in requestData + # and responseData + assert_response_event( + events[0], + expected_request={"method": "GET", "url": fragment_url}, + expected_response={"url": fragment_url}, + redirect_count=0, + ) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/script/__init__.py b/testing/web-platform/tests/webdriver/tests/bidi/script/__init__.py index 7feae91f27..4971737f8c 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/script/__init__.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/script/__init__.py @@ -155,16 +155,16 @@ REMOTE_VALUES: list[tuple[str, dict]] = [ ("new WeakSet()", {"type": "weakset", },), ("new Error('SOME_ERROR_TEXT')", {"type": "error"},), ("[1, 2][Symbol.iterator]()", { - "type": "iterator", + "type": "object", }), ("'mystring'[Symbol.iterator]()", { - "type": "iterator", + "type": "object", }), ("(new Set([1,2]))[Symbol.iterator]()", { - "type": "iterator", + "type": "object", }), ("(new Map([[1,2]]))[Symbol.iterator]()", { - "type": "iterator", + "type": "object", }), ("new Proxy({}, {})", { "type": "proxy", diff --git a/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/window_reference.py b/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/window_reference.py deleted file mode 100644 index 1588303be0..0000000000 --- a/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/window_reference.py +++ /dev/null @@ -1,124 +0,0 @@ -import pytest - -from webdriver import WebFrame, WebWindow -from webdriver.bidi.modules.script import ContextTarget - -pytestmark = pytest.mark.asyncio - - -async def test_web_window_reference_created_in_classic( - bidi_session, - current_session, - get_test_page, -): - handle = current_session.new_window(type_hint="tab") - current_session.window_handle = handle - current_session.url = get_test_page() - - expected_test_value = "bar" - window = current_session.execute_script( - f"window.foo = '{expected_test_value}'; return window;" - ) - - contexts = await bidi_session.browsing_context.get_tree() - assert len(contexts) == 2 - - assert window.id == contexts[1]["context"] - - result = await bidi_session.script.evaluate( - expression="window.foo", - target=ContextTarget(window.id), - await_promise=False, - ) - - assert result["value"] == expected_test_value - - -async def test_web_frame_reference_created_in_classic( - bidi_session, - current_session, - get_test_page, -): - handle = current_session.new_window(type_hint="tab") - current_session.window_handle = handle - current_session.url = get_test_page() - - expected_test_value = "foo" - frame = current_session.execute_script( - f"window.frames[0].bar='{expected_test_value}'; return window.frames[0]" - ) - - contexts = await bidi_session.browsing_context.get_tree() - assert len(contexts) == 2 - - assert frame.id == contexts[1]["children"][0]["context"] - - result = await bidi_session.script.evaluate( - expression="window.bar", - target=ContextTarget(frame.id), - await_promise=False, - ) - - assert result["value"] == expected_test_value - - -async def test_web_window_reference_created_in_bidi( - bidi_session, - current_session, - get_test_page, - new_tab -): - await bidi_session.browsing_context.navigate( - context=new_tab["context"], - url=get_test_page(), - wait="complete" - ) - - expected_test_value = "bar" - result = await bidi_session.script.evaluate( - expression=f"window.xyz = '{expected_test_value}'; window;", - target=ContextTarget(new_tab["context"]), - await_promise=False, - ) - - context_id = result["value"]["context"] - - # Use window reference from WebDriver BiDi in WebDriver classic - current_session.window_handle = new_tab["context"] - window = WebWindow(current_session, context_id) - test_value = current_session.execute_script( - """return arguments[0].xyz""", args=(window,) - ) - - assert test_value == expected_test_value - - -async def test_web_frame_reference_created_in_bidi( - bidi_session, - current_session, - get_test_page, - new_tab -): - await bidi_session.browsing_context.navigate( - context=new_tab["context"], - url=get_test_page(), - wait="complete" - ) - - expected_test_value = "foo" - result = await bidi_session.script.evaluate( - expression=f"window.frames[0].baz='{expected_test_value}'; window.frames[0];", - target=ContextTarget(new_tab["context"]), - await_promise=False, - ) - - context_id = result["value"]["context"] - - # Use window reference from WebDriver BiDi in WebDriver classic - current_session.window_handle = new_tab["context"] - window = WebFrame(current_session, context_id) - test_value = current_session.execute_script( - """return arguments[0].baz""", args=(window,) - ) - - assert test_value == expected_test_value diff --git a/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/partition.py b/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/partition.py index 632e7ffa26..dbe882b8cf 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/partition.py +++ b/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/partition.py @@ -159,7 +159,7 @@ async def test_partition_context_with_different_domain( partition=BrowsingContextPartitionDescriptor(new_tab["context"]) ) - assert result["cookies"] == [ + recursive_compare([ { "domain": cookie_domain, "httpOnly": False, @@ -170,7 +170,7 @@ async def test_partition_context_with_different_domain( "size": 6, "value": {"type": "string", "value": cookie_value}, } - ] + ], result["cookies"]) @pytest.mark.parametrize("domain", ["", "alt"], ids=["same_origin", "cross_origin"]) diff --git a/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/support/black_dot.png b/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/support/black_dot.png Binary files differnew file mode 100644 index 0000000000..613754cfaf --- /dev/null +++ b/testing/web-platform/tests/webdriver/tests/bidi/storage/get_cookies/support/black_dot.png diff --git a/testing/web-platform/tests/webdriver/tests/classic/element_click/interactability.py b/testing/web-platform/tests/webdriver/tests/classic/element_click/interactability.py index d55860c874..65f8a9015e 100644 --- a/testing/web-platform/tests/webdriver/tests/classic/element_click/interactability.py +++ b/testing/web-platform/tests/webdriver/tests/classic/element_click/interactability.py @@ -42,8 +42,22 @@ def test_disabled(session, inline): assert_success(response) +@pytest.mark.parametrize("transform", ["translate(100px, 100px)", "rotate(50deg)"]) +def test_element_interactable_css_transform(session, inline, transform): + # The button is transformed within the viewport. + session.url = inline(""" + <div style="width: 500px; height: 100px; position: absolute; left: 50px; top: 200px; + background-color: blue; transform: {transform};"> + <input type=button> + </div>""".format(transform=transform)) + element = session.find.css("input", all=False) + response = element_click(session, element) + assert_success(response) + + @pytest.mark.parametrize("transform", ["translate(-100px, -100px)", "rotate(50deg)"]) def test_element_not_interactable_css_transform(session, inline, transform): + # The button is transformed outside of the viewport. session.url = inline(""" <div style="width: 500px; height: 100px; background-color: blue; transform: {transform};"> diff --git a/testing/web-platform/tests/webdriver/tests/classic/element_click/scroll_into_view.py b/testing/web-platform/tests/webdriver/tests/classic/element_click/scroll_into_view.py index 591847e881..041f0dee6a 100644 --- a/testing/web-platform/tests/webdriver/tests/classic/element_click/scroll_into_view.py +++ b/testing/web-platform/tests/webdriver/tests/classic/element_click/scroll_into_view.py @@ -31,9 +31,9 @@ def test_scroll_into_view(session, inline): assert session.execute_script(""" let input = arguments[0]; rect = input.getBoundingClientRect(); - return rect["top"] >= 0 && rect["left"] >= 0 && - (rect["top"] + rect["height"]) <= window.innerHeight && - (rect["left"] + rect["width"]) <= window.innerWidth; + return rect.top >= 0 && rect.left >= 0 && + Math.floor(rect.bottom) <= window.innerHeight && + Math.floor(rect.right) <= window.innerWidth; """, args=(element,)) is True @@ -69,4 +69,4 @@ def test_partially_visible_does_not_scroll(session, offset, inline): assert_success(response) assert session.execute_script("return window.scrollY || document.documentElement.scrollTop") == 0 click_point = assert_one_click(session) - assert click_point == center_point(target) + assert click_point == pytest.approx(center_point(target), abs=1.0) diff --git a/testing/web-platform/tests/webdriver/tests/classic/switch_to_parent_frame/switch.py b/testing/web-platform/tests/webdriver/tests/classic/switch_to_parent_frame/switch.py index f777d6a767..184dc4234e 100644 --- a/testing/web-platform/tests/webdriver/tests/classic/switch_to_parent_frame/switch.py +++ b/testing/web-platform/tests/webdriver/tests/classic/switch_to_parent_frame/switch.py @@ -1,9 +1,8 @@ import pytest -from webdriver import NoSuchElementException, NoSuchWindowException +from webdriver import NoSuchElementException from tests.support.asserts import assert_error, assert_success -from tests.support.sync import Poll def switch_to_parent_frame(session): @@ -35,37 +34,6 @@ def test_no_top_browsing_context(session, url): assert_error(response, "no such window") -def test_no_parent_browsing_context(session, url): - session.url = url("/webdriver/tests/support/html/frames.html") - - subframe = session.find.css("#sub-frame", all=False) - session.switch_frame(subframe) - - deleteframe = session.find.css("#delete-frame", all=False) - session.switch_frame(deleteframe) - - button = session.find.css("#remove-top", all=False) - button.click() - - def is_window_closed(s): - try: - s.find.css("#remove-top", all=False) - return False - except NoSuchWindowException: - return True - - # Wait until iframe is gone. - wait = Poll( - session, - timeout=5, - message="Iframe is still present", - ) - wait.until(lambda s: is_window_closed(s)) - - response = switch_to_parent_frame(session) - assert_error(response, "no such window") - - def test_no_browsing_context(session, closed_frame): response = switch_to_parent_frame(session) assert_success(response) diff --git a/testing/web-platform/tests/webdriver/tests/interop/__init__.py b/testing/web-platform/tests/webdriver/tests/interop/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/testing/web-platform/tests/webdriver/tests/interop/__init__.py diff --git a/testing/web-platform/tests/webdriver/tests/interop/frames.py b/testing/web-platform/tests/webdriver/tests/interop/frames.py new file mode 100644 index 0000000000..b2cafb4987 --- /dev/null +++ b/testing/web-platform/tests/webdriver/tests/interop/frames.py @@ -0,0 +1,37 @@ +import pytest +from webdriver.error import NoSuchWindowException + +from tests.support.sync import AsyncPoll + +pytestmark = pytest.mark.asyncio + + +async def test_classic_switch_to_parent_no_browsing_context(bidi_session, current_session, url): + # With WebDriver classic it cannot be checked if the parent frame is already + # gone before switching to it. To prevent race conditions such a check needs + # to be done via WebDriver BiDi. + current_session.url = url("/webdriver/tests/support/html/frames.html") + + subframe = current_session.find.css("#sub-frame", all=False) + current_session.switch_frame(subframe) + + deleteframe = current_session.find.css("#delete-frame", all=False) + current_session.switch_frame(deleteframe) + + button = current_session.find.css("#remove-top", all=False) + button.click() + + async def is_frame_removed(_): + contexts = await bidi_session.browsing_context.get_tree(root=current_session.window_handle) + return not contexts[0]["children"] + + # Wait until IFrame is gone. + wait = AsyncPoll( + current_session, + timeout=5, + message="IFrame that should be closed is still open", + ) + await wait.until(is_frame_removed) + + with pytest.raises(NoSuchWindowException): + current_session.switch_frame("parent") diff --git a/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/node_shared_id.py b/testing/web-platform/tests/webdriver/tests/interop/shared_id_node.py index aeb2bc4597..aeb2bc4597 100644 --- a/testing/web-platform/tests/webdriver/tests/bidi/script/classic_interop/node_shared_id.py +++ b/testing/web-platform/tests/webdriver/tests/interop/shared_id_node.py diff --git a/testing/web-platform/tests/webdriver/tests/interop/shared_id_window.py b/testing/web-platform/tests/webdriver/tests/interop/shared_id_window.py new file mode 100644 index 0000000000..d13262b4e5 --- /dev/null +++ b/testing/web-platform/tests/webdriver/tests/interop/shared_id_window.py @@ -0,0 +1,128 @@ +import pytest + +from webdriver import WebFrame, WebWindow +from webdriver.bidi.modules.script import ContextTarget + +pytestmark = pytest.mark.asyncio + + +async def test_top_level_context_id_equals_window_handle(top_context, current_session): + assert top_context["context"] == current_session.window_handle + + +async def test_web_window_reference_created_in_classic( + bidi_session, + current_session, + get_test_page, +): + handle = current_session.new_window(type_hint="tab") + current_session.window_handle = handle + current_session.url = get_test_page() + + expected_test_value = "bar" + window = current_session.execute_script( + f"window.foo = '{expected_test_value}'; return window;" + ) + + contexts = await bidi_session.browsing_context.get_tree() + assert len(contexts) == 2 + + assert window.id == contexts[1]["context"] + + result = await bidi_session.script.evaluate( + expression="window.foo", + target=ContextTarget(window.id), + await_promise=False, + ) + + assert result["value"] == expected_test_value + + +async def test_web_frame_reference_created_in_classic( + bidi_session, + current_session, + get_test_page, +): + handle = current_session.new_window(type_hint="tab") + current_session.window_handle = handle + current_session.url = get_test_page() + + expected_test_value = "foo" + frame = current_session.execute_script( + f"window.frames[0].bar='{expected_test_value}'; return window.frames[0]" + ) + + contexts = await bidi_session.browsing_context.get_tree() + assert len(contexts) == 2 + + assert frame.id == contexts[1]["children"][0]["context"] + + result = await bidi_session.script.evaluate( + expression="window.bar", + target=ContextTarget(frame.id), + await_promise=False, + ) + + assert result["value"] == expected_test_value + + +async def test_web_window_reference_created_in_bidi( + bidi_session, + current_session, + get_test_page, + new_tab +): + await bidi_session.browsing_context.navigate( + context=new_tab["context"], + url=get_test_page(), + wait="complete" + ) + + expected_test_value = "bar" + result = await bidi_session.script.evaluate( + expression=f"window.xyz = '{expected_test_value}'; window;", + target=ContextTarget(new_tab["context"]), + await_promise=False, + ) + + context_id = result["value"]["context"] + + # Use window reference from WebDriver BiDi in WebDriver classic + current_session.window_handle = new_tab["context"] + window = WebWindow(current_session, context_id) + test_value = current_session.execute_script( + """return arguments[0].xyz""", args=(window,) + ) + + assert test_value == expected_test_value + + +async def test_web_frame_reference_created_in_bidi( + bidi_session, + current_session, + get_test_page, + new_tab +): + await bidi_session.browsing_context.navigate( + context=new_tab["context"], + url=get_test_page(), + wait="complete" + ) + + expected_test_value = "foo" + result = await bidi_session.script.evaluate( + expression=f"window.frames[0].baz='{expected_test_value}'; window.frames[0];", + target=ContextTarget(new_tab["context"]), + await_promise=False, + ) + + context_id = result["value"]["context"] + + # Use window reference from WebDriver BiDi in WebDriver classic + current_session.window_handle = new_tab["context"] + window = WebFrame(current_session, context_id) + test_value = current_session.execute_script( + """return arguments[0].baz""", args=(window,) + ) + + assert test_value == expected_test_value diff --git a/testing/web-platform/tests/webdriver/tests/support/fixtures_bidi.py b/testing/web-platform/tests/webdriver/tests/support/fixtures_bidi.py index 7f3e4f9a9a..32919210bf 100644 --- a/testing/web-platform/tests/webdriver/tests/support/fixtures_bidi.py +++ b/testing/web-platform/tests/webdriver/tests/support/fixtures_bidi.py @@ -1,15 +1,16 @@ -import base64 - -from tests.support.asserts import assert_pdf -from tests.support.image import cm_to_px, png_dimensions, ImageDifference -from typing import Any, Coroutine, Mapping - import asyncio +import base64 import copy +import json +import time from datetime import datetime, timedelta +from typing import Any, Coroutine, Mapping + import pytest import pytest_asyncio -import time + +from tests.support.asserts import assert_pdf +from tests.support.image import cm_to_px, png_dimensions, ImageDifference from webdriver.bidi.error import ( InvalidArgumentException, NoSuchFrameException, @@ -528,3 +529,98 @@ def domain_value(server_config): return server_config["domains"][domain][subdomain] return domain_value + + +@pytest.fixture +def fetch(bidi_session, top_context, configuration): + """Perform a fetch from the page of the provided context, default to the + top context. + """ + + async def fetch( + url, method="GET", headers=None, context=top_context, timeout_in_seconds=3 + ): + method_arg = f"method: '{method}'," + + headers_arg = "" + if headers is not None: + headers_arg = f"headers: {json.dumps(headers)}," + + timeout_in_seconds = timeout_in_seconds * configuration["timeout_multiplier"] + # Wait for fetch() to resolve a response and for response.text() to + # resolve as well to make sure the request/response is completed when + # the helper returns. + await bidi_session.script.evaluate( + expression=f""" + {{ + const controller = new AbortController(); + setTimeout(() => controller.abort(), {timeout_in_seconds * 1000}); + fetch("{url}", {{ + {method_arg} + {headers_arg} + signal: controller.signal, + }}).then(response => response.text()); + }}""", + target=ContextTarget(context["context"]), + await_promise=True, + ) + + return fetch + + +@pytest_asyncio.fixture +async def setup_network_test( + bidi_session, + subscribe_events, + wait_for_event, + wait_for_future_safe, + top_context, + url, +): + """Navigate the current top level context to the provided url and subscribe + to network.beforeRequestSent. + + Returns an `events` dictionary in which the captured network events will be added. + The keys of the dictionary are network event names (eg. "network.beforeRequestSent"), + and the value is an array of collected events. + """ + listeners = [] + + async def _setup_network_test(events, test_url=url("/webdriver/tests/bidi/network/support/empty.html"), contexts=None): + nonlocal listeners + + # Listen for network.responseCompleted for the initial navigation to + # make sure this event will not be captured unexpectedly by the tests. + await bidi_session.session.subscribe( + events=["network.responseCompleted"], contexts=[top_context["context"]] + ) + on_response_completed = wait_for_event("network.responseCompleted") + + await bidi_session.browsing_context.navigate( + context=top_context["context"], + url=test_url, + wait="complete", + ) + await wait_for_future_safe(on_response_completed) + await bidi_session.session.unsubscribe( + events=["network.responseCompleted"], contexts=[top_context["context"]] + ) + + await subscribe_events(events, contexts) + + network_events = {} + for event in events: + network_events[event] = [] + + async def on_event(method, data, event=event): + network_events[event].append(data) + + listeners.append(bidi_session.add_event_listener(event, on_event)) + + return network_events + + yield _setup_network_test + + # cleanup + for remove_listener in listeners: + remove_listener() diff --git a/testing/web-platform/tests/webdriver/tests/support/inline.py b/testing/web-platform/tests/webdriver/tests/support/inline.py index ecb2a2587b..8364e0590e 100644 --- a/testing/web-platform/tests/webdriver/tests/support/inline.py +++ b/testing/web-platform/tests/webdriver/tests/support/inline.py @@ -6,6 +6,7 @@ from urllib.parse import urlencode BOILERPLATES = { "html": "<!doctype html>\n<meta charset={charset}>\n{src}", + "html_quirks": "{src}", "xhtml": """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> @@ -22,6 +23,7 @@ BOILERPLATES = { } MIME_TYPES = { "html": "text/html", + "html_quirks": "text/html", "xhtml": "application/xhtml+xml", "xml": "text/xml", "js": "text/javascript", diff --git a/testing/web-platform/tests/webmidi/WEB_FEATURES.yml b/testing/web-platform/tests/webmidi/WEB_FEATURES.yml new file mode 100644 index 0000000000..76ddf996a2 --- /dev/null +++ b/testing/web-platform/tests/webmidi/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: web-midi + files: "**" diff --git a/testing/web-platform/tests/webnn/conformance_tests/buffer.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/buffer.https.any.js index 5b0f46dae0..9391be8dbf 100644 --- a/testing/web-platform/tests/webnn/conformance_tests/buffer.https.any.js +++ b/testing/web-platform/tests/webnn/conformance_tests/buffer.https.any.js @@ -9,4 +9,8 @@ testCreateWebNNBuffer("create", 4); -testDestroyWebNNBuffer("destroyTwice");
\ No newline at end of file +testDestroyWebNNBuffer('destroyTwice'); + +testReadWebNNBuffer('read'); + +testWriteWebNNBuffer('write'); diff --git a/testing/web-platform/tests/webnn/conformance_tests/gpu/buffer.https.any.js b/testing/web-platform/tests/webnn/conformance_tests/gpu/buffer.https.any.js index 66bba9ef4a..225bc40185 100644 --- a/testing/web-platform/tests/webnn/conformance_tests/gpu/buffer.https.any.js +++ b/testing/web-platform/tests/webnn/conformance_tests/gpu/buffer.https.any.js @@ -9,4 +9,8 @@ testCreateWebNNBuffer("create", 4, 'gpu'); -testDestroyWebNNBuffer("destroyTwice", 'gpu');
\ No newline at end of file +testDestroyWebNNBuffer('destroyTwice', 'gpu'); + +testReadWebNNBuffer('read', 'gpu'); + +testWriteWebNNBuffer('write', 'gpu'); diff --git a/testing/web-platform/tests/webnn/resources/test_data/add.json b/testing/web-platform/tests/webnn/resources/test_data/add.json index dba361228b..28e04a6127 100644 --- a/testing/web-platform/tests/webnn/resources/test_data/add.json +++ b/testing/web-platform/tests/webnn/resources/test_data/add.json @@ -877,6 +877,29 @@ ], "type": "float32" } + }, + { + "name": "add float32 large inputs", + "inputs": { + "a": { + "shape": [6000, 6000], + "data": 89.32998288116718, + "type": "float32", + "constant": true + }, + "b": { + "shape": [6000, 6000], + "data": 77.24720464493949, + "type": "float32", + "constant": false + } + }, + "expected": { + "name": "output", + "shape": [6000, 6000], + "data": 166.57718752610668, + "type": "float32" + } } ] }
\ No newline at end of file diff --git a/testing/web-platform/tests/webnn/resources/test_data/average_pool2d.json b/testing/web-platform/tests/webnn/resources/test_data/average_pool2d.json index 3d0c432273..b95e9395e7 100644 --- a/testing/web-platform/tests/webnn/resources/test_data/average_pool2d.json +++ b/testing/web-platform/tests/webnn/resources/test_data/average_pool2d.json @@ -301,6 +301,79 @@ } }, { + "name": "global averagePool2d float32 4D tensor all positive options.windowDimensions", + "inputs": { + "input": { + "shape": [1, 2, 5, 5], + "data": [ + 22.975555502750634, + 78.15438048012338, + 9.68611138116071, + 51.29803808129347, + 32.19308601456918, + 87.65037289600019, + 87.25082191311348, + 39.49793996935087, + 80.09963591169489, + 10.220142557736978, + 52.60270021646585, + 1.4128639882603933, + 11.954064466077474, + 85.0007506374375, + 64.7837446465813, + 88.03128735720126, + 11.333851214909307, + 70.61659435728073, + 84.90442561999888, + 79.06688041781518, + 7.328724951604215, + 35.97796581186121, + 10.17730631094398, + 1.4140757517112412, + 78.10038172113374, + 91.59549689157087, + 65.64701225681809, + 55.14215004436653, + 18.432438840756184, + 49.34624267439973, + 15.648024969290454, + 68.02723372727797, + 20.342549040418124, + 26.72794900604616, + 64.87446829774323, + 46.56714896227794, + 79.57832937136276, + 4.338463748959498, + 38.18383968382213, + 45.253981324455175, + 80.9717996657439, + 67.58124910163149, + 6.026499585657263, + 29.77881349289366, + 58.58993337807239, + 2.2384984647495054, + 14.505490166700486, + 68.72449589246624, + 76.45657404642184, + 23.53263275794233 + ], + "type": "float32" + } + }, + "options": { + "windowDimensions": [5, 5] + }, + "expected": { + "name": "output", + "shape": [1, 2, 1, 1], + "data": [ + 47.26926803588867, + 44.72445297241211 + ], + "type": "float32" + } + }, + { "name": "averagePool2d float32 4D tensor options.padding", "inputs": { "input": { @@ -680,6 +753,80 @@ } }, { + "name": "global averagePool2d float32 4D tensor options.layout=nhwc and options.windowDimensions", + "inputs": { + "input": { + "shape": [1, 5, 5, 2], + "data": [ + 22.975555502750634, + 91.59549689157087, + 78.15438048012338, + 65.64701225681809, + 9.68611138116071, + 55.14215004436653, + 51.29803808129347, + 18.432438840756184, + 32.19308601456918, + 49.34624267439973, + 87.65037289600019, + 15.648024969290454, + 87.25082191311348, + 68.02723372727797, + 39.49793996935087, + 20.342549040418124, + 80.09963591169489, + 26.72794900604616, + 10.220142557736978, + 64.87446829774323, + 52.60270021646585, + 46.56714896227794, + 1.4128639882603933, + 79.57832937136276, + 11.954064466077474, + 4.338463748959498, + 85.0007506374375, + 38.18383968382213, + 64.7837446465813, + 45.253981324455175, + 88.03128735720126, + 80.9717996657439, + 11.333851214909307, + 67.58124910163149, + 70.61659435728073, + 6.026499585657263, + 84.90442561999888, + 29.77881349289366, + 79.06688041781518, + 58.58993337807239, + 7.328724951604215, + 2.2384984647495054, + 35.97796581186121, + 14.505490166700486, + 10.17730631094398, + 68.72449589246624, + 1.4140757517112412, + 76.45657404642184, + 78.10038172113374, + 23.53263275794233 + ], + "type": "float32" + } + }, + "options": { + "layout": "nhwc", + "windowDimensions": [5, 5] + }, + "expected": { + "name": "output", + "shape": [1, 1, 1, 2], + "data": [ + 47.26926803588867, + 44.72445297241211 + ], + "type": "float32" + } + }, + { "name": "averagePool2d float32 4D tensor options.roundingType=floor", "inputs": { "input": { diff --git a/testing/web-platform/tests/webnn/resources/test_data/batch_normalization.json b/testing/web-platform/tests/webnn/resources/test_data/batch_normalization.json index 04ab0d0d6f..192985f5f4 100644 --- a/testing/web-platform/tests/webnn/resources/test_data/batch_normalization.json +++ b/testing/web-platform/tests/webnn/resources/test_data/batch_normalization.json @@ -1147,7 +1147,7 @@ "activation": "relu" }, "expected": { - "shape": [2, 3, 2, 2], + "shape": [2, 2, 2, 3], "data": [ 0, 200.12615966796875, diff --git a/testing/web-platform/tests/webnn/resources/test_data/clamp.json b/testing/web-platform/tests/webnn/resources/test_data/clamp.json index 0e948f9931..f25019e4d9 100644 --- a/testing/web-platform/tests/webnn/resources/test_data/clamp.json +++ b/testing/web-platform/tests/webnn/resources/test_data/clamp.json @@ -643,7 +643,7 @@ }, "expected": { "name": "output", - "shape": [2, 1, 4, 3], + "shape": [2, 2, 1, 2, 3], "data": [ -9.817828178405762, -6.024064064025879, diff --git a/testing/web-platform/tests/webnn/resources/utils.js b/testing/web-platform/tests/webnn/resources/utils.js index 375c71174a..d1dc0675a7 100644 --- a/testing/web-platform/tests/webnn/resources/utils.js +++ b/testing/web-platform/tests/webnn/resources/utils.js @@ -13,20 +13,33 @@ const TypedArrayDict = { int64: BigInt64Array, }; -const getTypedArrayData = (type, data) => { +// The maximum index to validate for the output's expected value. +const kMaximumIndexToValidate = 1000; + +const getTypedArrayData = (type, size, data) => { let outData; + if (type === 'float16') { + if (typeof (data) === 'number' && size > 1) { + return new TypedArrayDict[type](size).fill(toHalf(data)); + } // workaround to convert Float16 to Uint16 outData = new TypedArrayDict[type](data.length); for (let i = 0; i < data.length; i++) { outData[i] = toHalf(data[i]); } } else if (type === 'int64') { + if (typeof (data) === 'number' && size > 1) { + return new TypedArrayDict[type](size).fill(BigInt(data)); + } outData = new TypedArrayDict[type](data.length); for (let i = 0; i < data.length; i++) { outData[i] = BigInt(data[i]); } } else { + if (typeof (data) === 'number' && size > 1) { + return new TypedArrayDict[type](size).fill(data); + } outData = new TypedArrayDict[type](data); } return outData; @@ -67,25 +80,26 @@ const loadTests = (operationName) => { }; /** - * Get exptected data and data type from given resources with output name. - * @param {Array} resources - An array of expected resources + * Get expected resource from given resources with output name. + * @param {Array} resources - An array of given resources * @param {String} outputName - An output name - * @returns {Array.<[Number[], String]>} An array of expected data array and data type + * @returns {Object} An object of expected resource */ -const getExpectedDataAndType = (resources, outputName) => { +const getNamedResource = (resources, outputName) => { let ret; - for (let subResources of resources) { - if (subResources.name === outputName) { - ret = [subResources.data, subResources.type]; + for (let resource of resources) { + if (resource.name === outputName) { + ret = resource; break; } } if (ret === undefined) { - throw new Error(`Failed to get expected data sources and type by ${outputName}`); + throw new Error(`Failed to get expected resource by ${outputName}`); } return ret; }; + /** * Get ULP tolerance of conv2d/convTranspose2d operation. * @param {Object} resources - Resources used for building a graph @@ -521,13 +535,14 @@ const checkResults = (operationName, namedOutputOperands, outputs, resources) => if (Array.isArray(expected)) { // the outputs of split() or gru() is a sequence for (let operandName in namedOutputOperands) { + const suboutputResource = getNamedResource(expected, operandName); + assert_array_equals(namedOutputOperands[operandName].shape(), suboutputResource.shape ?? []); outputData = outputs[operandName]; - // for some operations which may have multi outputs of different types - [expectedData, operandType] = getExpectedDataAndType(expected, operandName); tolerance = getPrecisonTolerance(operationName, metricType, resources); - doAssert(operationName, outputData, expectedData, tolerance, operandType, metricType) + doAssert(operationName, outputData, suboutputResource.data, tolerance, suboutputResource.type, metricType) } } else { + assert_array_equals(namedOutputOperands[expected.name].shape(), expected.shape ?? []); outputData = outputs[expected.name]; expectedData = expected.data; operandType = expected.type; @@ -543,7 +558,11 @@ const checkResults = (operationName, namedOutputOperands, outputs, resources) => * @returns {MLOperand} A constant operand */ const createConstantOperand = (builder, resources) => { - const bufferView = new TypedArrayDict[resources.type](resources.data); + const bufferView = (typeof (resources.data) === 'number' && + sizeOfShape(resources.shape) > 1) ? + new TypedArrayDict[resources.type](sizeOfShape(resources.shape)) + .fill(resources.data) : + new TypedArrayDict[resources.type](resources.data); return builder.constant({dataType: resources.type, type: resources.type, dimensions: resources.shape}, bufferView); }; @@ -801,14 +820,17 @@ const buildGraph = (operationName, builder, resources, buildFunc) => { // the inputs of concat() is a sequence for (let subInput of resources.inputs) { if (!subInput.hasOwnProperty('constant') || !subInput.constant) { - inputs[subInput.name] = getTypedArrayData(subInput.type, subInput.data); + inputs[subInput.name] = getTypedArrayData( + subInput.type, sizeOfShape(subInput.shape), subInput.data); } } } else { for (let inputName in resources.inputs) { const subTestByName = resources.inputs[inputName]; if (!subTestByName.hasOwnProperty('constant') || !subTestByName.constant) { - inputs[inputName] = getTypedArrayData(subTestByName.type, subTestByName.data); + inputs[inputName] = getTypedArrayData( + subTestByName.type, sizeOfShape(subTestByName.shape), + subTestByName.data); } } } @@ -931,6 +953,7 @@ const toHalf = (value) => { * WebNN buffer creation. * @param {MLContext} context - the context used to create the buffer. * @param {Number} bufferSize - Size of the buffer to create, in bytes. + * @returns {MLBuffer} the created buffer. */ const createBuffer = (context, bufferSize) => { let buffer; @@ -980,4 +1003,286 @@ const testCreateWebNNBuffer = (testName, bufferSize, deviceType = 'cpu') => { promise_test(async () => { createBuffer(context, bufferSize); }, `${testName} / ${bufferSize}`); -};
\ No newline at end of file +}; + +/** + * Asserts the buffer data in MLBuffer matches expected. + * @param {MLContext} ml_context - The context used to create the buffer. + * @param {MLBuffer} ml_buffer - The buffer to read and compare data. + * @param {Array} expected - Array of the expected data in the buffer. + */ +const assert_buffer_data_equals = async (ml_context, ml_buffer, expected) => { + const actual = await ml_context.readBuffer(ml_buffer); + assert_array_equals( + new expected.constructor(actual), expected, + 'Read buffer data equals expected data.'); +}; + +/** + * WebNN write buffer operation test. + * @param {String} testName - The name of the test operation. + * @param {String} deviceType - The execution device type for this test. + */ +const testWriteWebNNBuffer = (testName, deviceType = 'cpu') => { + let ml_context; + promise_setup(async () => { + ml_context = await navigator.ml.createContext({deviceType}); + }); + + promise_test(async () => { + let ml_buffer = createBuffer(ml_context, 4); + + // MLBuffer was unsupported for the deviceType. + if (ml_buffer === undefined) { + return; + } + + let array_buffer = new ArrayBuffer(ml_buffer.size); + + // Writing with a size that goes past that source buffer length. + assert_throws_js( + TypeError, + () => ml_context.writeBuffer( + ml_buffer, new Uint8Array(array_buffer), /*srcOffset=*/ 0, + /*srcSize=*/ ml_buffer.size + 1)); + assert_throws_js( + TypeError, + () => ml_context.writeBuffer( + ml_buffer, new Uint8Array(array_buffer), /*srcOffset=*/ 3, + /*srcSize=*/ 4)); + + // Writing with a source offset that is out of range of the source size. + assert_throws_js( + TypeError, + () => ml_context.writeBuffer( + ml_buffer, new Uint8Array(array_buffer), + /*srcOffset=*/ ml_buffer.size + 1)); + + // Writing with a source offset that is out of range of implicit copy size. + assert_throws_js( + TypeError, + () => ml_context.writeBuffer( + ml_buffer, new Uint8Array(array_buffer), + /*srcOffset=*/ ml_buffer.size + 1, /*srcSize=*/ undefined)); + + assert_throws_js( + TypeError, + () => ml_context.writeBuffer( + ml_buffer, new Uint8Array(array_buffer), /*srcOffset=*/ undefined, + /*srcSize=*/ ml_buffer.size + 1)); + + assert_throws_js( + TypeError, + () => ml_context.writeBuffer( + ml_buffer, Uint8Array.from([0xEE, 0xEE, 0xEE, 0xEE, 0xEE]))); + }, `${testName} / error`); + + promise_test(async () => { + let ml_buffer = createBuffer(ml_context, 4); + + // MLBuffer was unsupported for the deviceType. + if (ml_buffer === undefined) { + return; + } + + // Writing data to a destroyed MLBuffer should throw. + ml_buffer.destroy(); + + assert_throws_dom( + 'InvalidStateError', + () => + ml_context.writeBuffer(ml_buffer, new Uint8Array(ml_buffer.size))); + }, `${testName} / destroy`); + + promise_test(async () => { + let ml_buffer = createBuffer(ml_context, 4); + + // MLBuffer was unsupported for the deviceType. + if (ml_buffer === undefined) { + return; + } + + const array_buffer = new ArrayBuffer(ml_buffer.size); + const detached_buffer = array_buffer.transfer(); + assert_true(array_buffer.detached, 'array buffer should be detached.'); + + ml_context.writeBuffer(ml_buffer, array_buffer); + }, `${testName} / detached`); + + promise_test(async () => { + let ml_buffer = createBuffer(ml_context, 4); + + // MLBuffer was unsupported for the deviceType. + if (ml_buffer === undefined) { + return; + } + + let another_ml_context = await navigator.ml.createContext({deviceType}); + let another_ml_buffer = createBuffer(another_ml_context, ml_buffer.size); + + let input_data = new Uint8Array(ml_buffer.size).fill(0xAA); + assert_throws_js( + TypeError, () => ml_context.writeBuffer(another_ml_buffer, input_data)); + assert_throws_js( + TypeError, () => another_ml_context.writeBuffer(ml_buffer, input_data)); + }, `${testName} / context_mismatch`); +}; + +/** + * WebNN read buffer operation test. + * @param {String} testName - The name of the test operation. + * @param {String} deviceType - The execution device type for this test. + */ +const testReadWebNNBuffer = (testName, deviceType = 'cpu') => { + let ml_context; + promise_setup(async () => { + ml_context = await navigator.ml.createContext({deviceType}); + }); + + promise_test(async t => { + let ml_buffer = createBuffer(ml_context, 4); + + // MLBuffer was unsupported for the deviceType. + if (ml_buffer === undefined) { + return; + } + + // Reading a destroyed MLBuffer should reject. + ml_buffer.destroy(); + + await promise_rejects_dom( + t, 'InvalidStateError', ml_context.readBuffer(ml_buffer)); + }, `${testName} / destroy`); + + promise_test(async () => { + let ml_buffer = createBuffer(ml_context, 4); + + // MLBuffer was unsupported for the deviceType. + if (ml_buffer === undefined) { + return; + } + + // Initialize the buffer. + ml_context.writeBuffer( + ml_buffer, Uint8Array.from([0xAA, 0xAA, 0xAA, 0xAA])); + + ml_context.writeBuffer(ml_buffer, Uint32Array.from([0xBBBBBBBB])); + await assert_buffer_data_equals( + ml_context, ml_buffer, Uint32Array.from([0xBBBBBBBB])); + ; + }, `${testName} / full_size`); + + promise_test(async () => { + let ml_buffer = createBuffer(ml_context, 4); + + // MLBuffer was unsupported for the deviceType. + if (ml_buffer === undefined) { + return; + } + + // Initialize the buffer. + ml_context.writeBuffer( + ml_buffer, Uint8Array.from([0xAA, 0xAA, 0xAA, 0xAA])); + + // Writing to the remainder of the buffer from source offset. + ml_context.writeBuffer( + ml_buffer, Uint8Array.from([0xCC, 0xCC, 0xBB, 0xBB]), + /*srcOffset=*/ 2); + await assert_buffer_data_equals( + ml_context, ml_buffer, Uint8Array.from([0xBB, 0xBB, 0xAA, 0xAA])); + }, `${testName} / src_offset_only`); + + promise_test(async () => { + let ml_buffer = createBuffer(ml_context, 4); + + // MLBuffer was unsupported for the deviceType. + if (ml_buffer === undefined) { + return; + } + + // Initialize the buffer. + const input_data = [0xAA, 0xAA, 0xAA, 0xAA]; + ml_context.writeBuffer(ml_buffer, Uint8Array.from(input_data)); + + // Writing zero bytes at the end of the buffer. + ml_context.writeBuffer( + ml_buffer, Uint32Array.from([0xBBBBBBBB]), /*srcOffset=*/ 1); + await assert_buffer_data_equals( + ml_context, ml_buffer, Uint8Array.from(input_data)); + }, `${testName} / zero_write`); + + promise_test(async () => { + let ml_buffer = createBuffer(ml_context, 4); + + // MLBuffer was unsupported for the deviceType. + if (ml_buffer === undefined) { + return; + } + + // Initialize the buffer. + ml_context.writeBuffer( + ml_buffer, Uint8Array.from([0xAA, 0xAA, 0xAA, 0xAA])); + + // Writing with both a source offset and size. + ml_context.writeBuffer( + ml_buffer, Uint8Array.from([0xDD, 0xDD, 0xCC, 0xDD]), + /*srcOffset=*/ 2, /*srcSize=*/ 1); + await assert_buffer_data_equals( + ml_context, ml_buffer, Uint8Array.from([0xCC, 0xAA, 0xAA, 0xAA])); + }, `${testName} / src_offset_and_size`); + + promise_test(async () => { + let ml_buffer = createBuffer(ml_context, 4); + + // MLBuffer was unsupported for the deviceType. + if (ml_buffer === undefined) { + return; + } + + // Initialize the buffer. + ml_context.writeBuffer( + ml_buffer, Uint8Array.from([0xAA, 0xAA, 0xAA, 0xAA])); + + // Using an offset allows a larger source buffer to fit. + ml_context.writeBuffer( + ml_buffer, Uint8Array.from([0xEE, 0xEE, 0xEE, 0xEE, 0xEE]), + /*srcOffset=*/ 1); + await assert_buffer_data_equals( + ml_context, ml_buffer, Uint8Array.from([0xEE, 0xEE, 0xEE, 0xEE])); + }, `${testName} / larger_src_data`); + + promise_test(async () => { + let ml_buffer = createBuffer(ml_context, 4); + + // MLBuffer was unsupported for the deviceType. + if (ml_buffer === undefined) { + return; + } + + const input_data = [0xAA, 0xAA, 0xAA, 0xAA]; + + // Writing with a source offset of undefined should be treated as 0. + ml_context.writeBuffer( + ml_buffer, Uint8Array.from(input_data), /*srcOffset=*/ undefined, + /*srcSize=*/ input_data.length); + await assert_buffer_data_equals( + ml_context, ml_buffer, Uint8Array.from(input_data)); + }, `${testName} / no_src_offset`); + + promise_test(async t => { + let ml_buffer = createBuffer(ml_context, 4); + + // MLBuffer was unsupported for the deviceType. + if (ml_buffer === undefined) { + return; + } + + let another_ml_context = await navigator.ml.createContext({deviceType}); + let another_ml_buffer = createBuffer(another_ml_context, ml_buffer.size); + + await promise_rejects_js( + t, TypeError, ml_context.readBuffer(another_ml_buffer)); + await promise_rejects_js( + t, TypeError, another_ml_context.readBuffer(ml_buffer)); + }, `${testName} / context_mismatch`); +}; diff --git a/testing/web-platform/tests/webnn/resources/utils_validation.js b/testing/web-platform/tests/webnn/resources/utils_validation.js index 7f1d4a4a94..5c4eb08761 100644 --- a/testing/web-platform/tests/webnn/resources/utils_validation.js +++ b/testing/web-platform/tests/webnn/resources/utils_validation.js @@ -12,6 +12,16 @@ const allWebNNOperandDataTypes = [ 'uint8' ]; +// https://webidl.spec.whatwg.org/#idl-unsigned-long +// The unsigned long type is an unsigned integer type that has values in the +// range [0, 4294967295]. +// 4294967295 = 2 ** 32 - 1 +const kMaxUnsignedLong = 2 ** 32 - 1; + +const floatingPointTypes = ['float32', 'float16']; + +const signedIntegerTypes = ['int32', 'int64', 'int8']; + const unsignedLongType = 'unsigned long'; const dimensions0D = []; @@ -173,9 +183,7 @@ function generateOutOfRangeValuesArray(type) { let range, outsideValueArray; switch (type) { case 'unsigned long': - // https://webidl.spec.whatwg.org/#idl-unsigned-long - // The unsigned long type is an unsigned integer type that has values in the range [0, 4294967295]. - range = [0, 4294967295]; + range = [0, kMaxUnsignedLong]; break; default: throw new Error(`Unsupport ${type}`); @@ -251,11 +259,11 @@ function validateTwoInputsOfSameDataType(operationName) { /** * Validate options.axes by given operation and input rank for - * argMin/Max / layerNormalization / Reduction operations / resample2d operations - * @param {(String[]|String)} operationName - An operation name array or an operation name - * @param {Number} [inputRank] + * argMin/Max / layerNormalization / Reduction operations operations + * @param {(String[]|String)} operationName - An operation name array or an + * operation name */ -function validateOptionsAxes(operationName, inputRank) { +function validateOptionsAxes(operationName) { if (navigator.ml === undefined) { return; } @@ -271,33 +279,25 @@ function validateOptionsAxes(operationName, inputRank) { for (let subOperationName of operationNameArray) { // TypeError is expected if any of options.axes elements is not an unsigned long interger promise_test(async t => { - if (inputRank === undefined) { - // argMin/Max / layerNormalization / Reduction operations - for (let dataType of allWebNNOperandDataTypes) { - for (let dimensions of allWebNNDimensionsArray) { - const rank = getRank(dimensions); - if (rank >= 1) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions}); - for (let invalidAxis of invalidAxisArray) { - assert_throws_js(TypeError, () => builder[subOperationName](input, {axes: invalidAxis})); - } - for (let axis of notUnsignedLongAxisArray) { - assert_false(typeof axis === 'number' && Number.isInteger(axis), `[${subOperationName}] any of options.axes elements should be of 'unsigned long'`); - assert_throws_js(TypeError, () => builder[subOperationName](input, {axes: [axis]})); - } + for (let dataType of allWebNNOperandDataTypes) { + for (let dimensions of allWebNNDimensionsArray) { + const rank = getRank(dimensions); + if (rank >= 1) { + const input = + builder.input(`input${++inputIndex}`, {dataType, dimensions}); + for (let invalidAxis of invalidAxisArray) { + assert_throws_js( + TypeError, + () => builder[subOperationName](input, {axes: invalidAxis})); + } + for (let axis of notUnsignedLongAxisArray) { + assert_false( + typeof axis === 'number' && Number.isInteger(axis), + `[${subOperationName}] any of options.axes elements should be of 'unsigned long'`); + assert_throws_js( + TypeError, + () => builder[subOperationName](input, {axes: [axis]})); } - } - } - } else { - // resample2d - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: allWebNNDimensionsArray[inputRank]}); - for (let invalidAxis of invalidAxisArray) { - assert_throws_js(TypeError, () => builder[subOperationName](input, {axes: invalidAxis})); - } - for (let axis of notUnsignedLongAxisArray) { - assert_false(typeof axis === 'number' && Number.isInteger(axis), `[${subOperationName}] any of options.axes elements should be of 'unsigned long'`); - assert_throws_js(TypeError, () => builder[subOperationName](input, {axes: [axis]})); } } } @@ -305,55 +305,141 @@ function validateOptionsAxes(operationName, inputRank) { // DataError is expected if any of options.axes elements is greater or equal to the size of input promise_test(async t => { - if (inputRank === undefined) { - // argMin/Max / layerNormalization / Reduction operations - for (let dataType of allWebNNOperandDataTypes) { - for (let dimensions of allWebNNDimensionsArray) { - const rank = getRank(dimensions); - if (rank >= 1) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions}); - assert_throws_dom('DataError', () => builder[subOperationName](input, {axes: [rank]})); - assert_throws_dom('DataError', () => builder[subOperationName](input, {axes: [rank + 1]})); - } + for (let dataType of allWebNNOperandDataTypes) { + for (let dimensions of allWebNNDimensionsArray) { + const rank = getRank(dimensions); + if (rank >= 1) { + const input = + builder.input(`input${++inputIndex}`, {dataType, dimensions}); + assert_throws_dom( + 'DataError', + () => builder[subOperationName](input, {axes: [rank]})); + assert_throws_dom( + 'DataError', + () => builder[subOperationName](input, {axes: [rank + 1]})); } } - } else { - // resample2d - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: allWebNNDimensionsArray[inputRank]}); - assert_throws_dom('DataError', () => builder[subOperationName](input, {axes: [inputRank]})); - assert_throws_dom('DataError', () => builder[subOperationName](input, {axes: [inputRank + 1]})); - } } }, `[${subOperationName}] DataError is expected if any of options.axes elements is greater or equal to the size of input`); // DataError is expected if two or more values are same in the axes sequence promise_test(async t => { - if (inputRank === undefined) { - // argMin/Max / layerNormalization / Reduction operations - for (let dataType of allWebNNOperandDataTypes) { - for (let dimensions of allWebNNDimensionsArray) { - const rank = getRank(dimensions); - if (rank >= 2) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions}); - const axesArrayContainSameValues = getAxesArrayContainSameValues(dimensions); - for (let axes of axesArrayContainSameValues) { - assert_throws_dom('DataError', () => builder[subOperationName](input, {axes})); - } + for (let dataType of allWebNNOperandDataTypes) { + for (let dimensions of allWebNNDimensionsArray) { + const rank = getRank(dimensions); + if (rank >= 2) { + const input = + builder.input(`input${++inputIndex}`, {dataType, dimensions}); + const axesArrayContainSameValues = + getAxesArrayContainSameValues(dimensions); + for (let axes of axesArrayContainSameValues) { + assert_throws_dom( + 'DataError', () => builder[subOperationName](input, {axes})); } } } - } else { - // resample2d - for (let dataType of allWebNNOperandDataTypes) { - const dimensions = allWebNNDimensionsArray[inputRank]; - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions}); - const axesArrayContainSameValues = getAxesArrayContainSameValues(dimensions); - for (let axes of axesArrayContainSameValues) { - assert_throws_dom('DataError', () => builder[subOperationName](input, {axes})); - } - } } }, `[${subOperationName}] DataError is expected if two or more values are same in the axes sequence`); } } + +/** + * Validate a unary operation + * @param {String} operationName - An operation name + * @param {Array} supportedDataTypes - Test building with these data types + * succeeds and test building with all other data types fails + * @param {Boolean} alsoBuildActivation - If test building this operation as an + * activation + */ +function validateUnaryOperation( + operationName, supportedDataTypes, alsoBuildActivation = false) { + for (let dataType of supportedDataTypes) { + for (let dimensions of allWebNNDimensionsArray) { + promise_test( + async t => { + const input = builder.input(`input`, {dataType, dimensions}); + const output = builder[operationName](input); + assert_equals(output.dataType(), dataType); + assert_array_equals(output.shape(), dimensions); + }, + `[${operationName}] Test building an operator, dataType = ${ + dataType}, dimensions = [${dimensions}]`); + } + } + + const unsupportedDataTypes = + new Set(allWebNNOperandDataTypes).difference(new Set(supportedDataTypes)); + for (let dataType of unsupportedDataTypes) { + for (let dimensions of allWebNNDimensionsArray) { + promise_test( + async t => { + const input = builder.input(`input`, {dataType, dimensions}); + assert_throws_js(TypeError, () => builder[operationName](input)); + }, + `[${operationName}] Throw if the dataType is not supported, dataType = ${ + dataType}, dimensions = [${dimensions}]`); + } + } + + if (alsoBuildActivation) { + promise_test(async t => { + builder[operationName](); + }, `[${operationName}] Test building an activation`); + } +} + +/** + * Basic test that the builder method specified by `operationName` throws if + * given an input from another builder. Operands which do not accept a float32 + * square 2D input should pass their own `operatorDescriptor`. + * @param {String} operationName + * @param {String} operatorDescriptor + */ +function validateInputFromAnotherBuilder(operatorName, operatorDescriptor = { + dataType: 'float32', + dimensions: [2, 2] +}) { + multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', operatorDescriptor); + assert_throws_js( + TypeError, () => builder[operatorName](inputFromOtherBuilder)); + }, `[${operatorName}] throw if input is from another builder`); +}; + +/** + * Basic test that the builder method specified by `operationName` throws if one + * of its inputs is from another builder. This helper may only be used by + * operands which accept float32 square 2D inputs. + * @param {String} operationName + */ +function validateTwoInputsFromMultipleBuilders(operatorName) { + const opDescriptor = {dataType: 'float32', dimensions: [2, 2]}; + + multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = otherBuilder.input('other', opDescriptor); + + const input = builder.input('input', opDescriptor); + assert_throws_js( + TypeError, () => builder[operatorName](inputFromOtherBuilder, input)); + }, `[${operatorName}] throw if first input is from another builder`); + + multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = otherBuilder.input('other', opDescriptor); + + const input = builder.input('input', opDescriptor); + assert_throws_js( + TypeError, () => builder[operatorName](input, inputFromOtherBuilder)); + }, `[${operatorName}] throw if second input is from another builder`); +}; + +function multi_builder_test(func, description) { + promise_test(async t => { + const context = await navigator.ml.createContext(); + + const builder = new MLGraphBuilder(context); + const otherBuilder = new MLGraphBuilder(context); + + await func(t, builder, otherBuilder); + }, description); +} diff --git a/testing/web-platform/tests/webnn/validation_tests/argMinMax.https.any.js b/testing/web-platform/tests/webnn/validation_tests/argMinMax.https.any.js new file mode 100644 index 0000000000..6e8d558306 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/argMinMax.https.any.js @@ -0,0 +1,15 @@ +// META: title=validation tests for WebNN API argMin/Max operations +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const kArgMinMaxOperators = [ + 'argMin', + 'argMax', +]; + +kArgMinMaxOperators.forEach((operatorName) => { + validateOptionsAxes(operatorName); + validateInputFromAnotherBuilder(operatorName); +}); diff --git a/testing/web-platform/tests/webnn/validation_tests/arg_min_max.https.any.js b/testing/web-platform/tests/webnn/validation_tests/arg_min_max.https.any.js deleted file mode 100644 index 700be83b04..0000000000 --- a/testing/web-platform/tests/webnn/validation_tests/arg_min_max.https.any.js +++ /dev/null @@ -1,8 +0,0 @@ -// META: title=validation tests for WebNN API argMin/Max operations -// META: global=window,dedicatedworker -// META: script=../resources/utils_validation.js -// META: timeout=long - -'use strict'; - -validateOptionsAxes(['argMin', 'argMax']); diff --git a/testing/web-platform/tests/webnn/validation_tests/batchNormalization.https.any.js b/testing/web-platform/tests/webnn/validation_tests/batchNormalization.https.any.js new file mode 100644 index 0000000000..cafd9f9cd5 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/batchNormalization.https.any.js @@ -0,0 +1,270 @@ +// META: title=validation tests for WebNN API batchNormalization operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +let meanIndex = 0; +let varianceIndex = 0; + +const kExampleInputDescriptor = { + dataType: 'float32', + dimensions: [2, 2] +}; +// 1D tensor descriptor which may be used for `mean`, `variance`, `scale`, or +// `bias` inputs. +const kExample1DTensorDescriptor = { + dataType: 'float32', + dimensions: [kExampleInputDescriptor.dimensions[/* axis */ 1]] +}; + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + const invalidAxisArray = generateOutOfRangeValuesArray(unsignedLongType); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); + for (let invalidAxis of invalidAxisArray) { + assert_throws_js(TypeError, () => builder.batchNormalization(input, mean, variance, {axis: invalidAxis})); + } + } + } +}, "[batchNormalization] TypeError is expected if options.axis is outside the 'unsigned long' value range"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis: getRank(dimensions2D)})); + } + } +}, "[batchNormalization] DataError is expected if options.axis is 'unsigned long' and it's not in the range 0 to the rank of input, exclusive"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); + for (let axis of notUnsignedLongAxisArray) { + assert_false(typeof axis === 'number' && Number.isInteger(axis), "[batchNormalization] options.axis should be of 'unsigned long'"); + assert_throws_js(TypeError, () => builder.batchNormalization(input, mean, variance, {axis})); + } + } + } +}, '[batchNormalization] TypeError is expected if options.axis is not an unsigned long interger'); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); + for (let dimensions of allWebNNDimensionsArray) { + if (dimensions.length !== 1) { + // set mean not be 1D tensor + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance)); + } + } + } + } +}, "[batchNormalization] DataError is expected if the size of mean.dimensions is not 1"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); + for (let offset of adjustOffsetsArray) { + const adjustedSize = size + offset; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [adjustedSize]}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis})); + } + } + } +}, "[batchNormalization] DataError is expected if mean.dimensions[0] is not equal to input.dimensions[options.axis]"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); + for (let dimensions of allWebNNDimensionsArray) { + if (dimensions.length !== 1) { + // set variance not be 1D tensor + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance)); + } + } + } + } +}, "[batchNormalization] DataError is expected if the size of variance.dimensions is not 1"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); + for (let offset of adjustOffsetsArray) { + const adjustedSize = size + offset; + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [adjustedSize]}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis})); + } + } + } +}, "[batchNormalization] DataError is expected if variance.dimensions[0] is not equal to input.dimensions[options.axis]"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); + for (let dimensions of allWebNNDimensionsArray) { + if (dimensions.length !== 1) { + // set scale not be 1D tensor + const scale = builder.input('scale', {dataType, dimensions}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis, scale})); + } + } + } + } +}, "[batchNormalization] DataError is expected if the size of scale.dimensions is not 1"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); + for (let offset of adjustOffsetsArray) { + const adjustedSize = size + offset; + const scale = builder.input('scale', {dataType, dimensions: [adjustedSize]}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis, scale})); + } + } + } +}, "[batchNormalization] DataError is expected if scale.dimensions[0] is not equal to input.dimensions[options.axis]"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); + for (let dimensions of allWebNNDimensionsArray) { + if (dimensions.length !== 1) { + // set bias not be 1D tensor + const bias = builder.input('bias', {dataType, dimensions}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis, bias})); + } + } + } + } +}, "[batchNormalization] DataError is expected if the size of bias.dimensions is not 1"); + +promise_test(async t => { + for (let dataType of allWebNNOperandDataTypes) { + const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); + const validAxisArray = getAxisArray(dimensions2D); + for (let axis of validAxisArray) { + let size = dimensions2D[axis]; + const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); + const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); + for (let offset of adjustOffsetsArray) { + const adjustedSize = size + offset; + const bias = builder.input('bias', {dataType, dimensions: [adjustedSize]}); + assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis, bias})); + } + } + } +}, "[batchNormalization] DataError is expected if bias.dimensions[0] is not equal to input.dimensions[options.axis]"); + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', kExampleInputDescriptor); + + const mean = builder.input('mean', kExample1DTensorDescriptor); + const variance = builder.input('variance', kExample1DTensorDescriptor); + assert_throws_js( + TypeError, () => builder.batchNormalization(inputFromOtherBuilder, mean, variance)); +}, '[batchNormalization] throw if input is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const meanFromOtherBuilder = + otherBuilder.input('mean', kExample1DTensorDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + const variance = builder.input('variance', kExample1DTensorDescriptor); + assert_throws_js( + TypeError, + () => builder.batchNormalization(input, meanFromOtherBuilder, variance)); +}, '[batchNormalization] throw if mean is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const varianceFromOtherBuilder = + otherBuilder.input('variance', kExample1DTensorDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + const mean = builder.input('mean', kExample1DTensorDescriptor); + assert_throws_js( + TypeError, + () => builder.batchNormalization(input, mean, varianceFromOtherBuilder)); +}, '[batchNormalization] throw if variance is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const scaleFromOtherBuilder = + otherBuilder.input('scale', kExample1DTensorDescriptor); + const options = {scale: scaleFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const mean = builder.input('mean', kExample1DTensorDescriptor); + const variance = builder.input('variance', kExample1DTensorDescriptor); + assert_throws_js( + TypeError, + () => builder.batchNormalization(input, mean, variance, options)); +}, '[batchNormalization] throw if scale option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const biasFromOtherBuilder = + otherBuilder.input('bias', kExample1DTensorDescriptor); + const options = {scale: biasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const mean = builder.input('mean', kExample1DTensorDescriptor); + const variance = builder.input('variance', kExample1DTensorDescriptor); + assert_throws_js( + TypeError, + () => builder.batchNormalization(input, mean, variance, options)); +}, '[batchNormalization] throw if bias option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const activationFromOtherBuilder = otherBuilder.clamp(); + const options = {activation: activationFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const mean = builder.input('mean', kExample1DTensorDescriptor); + const variance = builder.input('variance', kExample1DTensorDescriptor); + assert_throws_js( + TypeError, + () => builder.batchNormalization(input, mean, variance, options)); +}, '[batchNormalization] throw if activation option is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/batch_normalization.https.any.js b/testing/web-platform/tests/webnn/validation_tests/batch_normalization.https.any.js deleted file mode 100644 index 25b542d34e..0000000000 --- a/testing/web-platform/tests/webnn/validation_tests/batch_normalization.https.any.js +++ /dev/null @@ -1,190 +0,0 @@ -// META: title=validation tests for WebNN API batchNormalization operation -// META: global=window,dedicatedworker -// META: script=../resources/utils_validation.js -// META: timeout=long - -'use strict'; - -let meanIndex = 0; -let varianceIndex = 0; - -promise_test(async t => { - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); - const validAxisArray = getAxisArray(dimensions2D); - const invalidAxisArray = generateOutOfRangeValuesArray(unsignedLongType); - for (let axis of validAxisArray) { - let size = dimensions2D[axis]; - const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); - const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); - for (let invalidAxis of invalidAxisArray) { - assert_throws_js(TypeError, () => builder.batchNormalization(input, mean, variance, {axis: invalidAxis})); - } - } - } -}, "[batchNormalization] TypeError is expected if options.axis is outside the 'unsigned long' value range"); - -promise_test(async t => { - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); - const validAxisArray = getAxisArray(dimensions2D); - for (let axis of validAxisArray) { - let size = dimensions2D[axis]; - const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); - const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); - assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis: getRank(dimensions2D)})); - } - } -}, "[batchNormalization] DataError is expected if options.axis is 'unsigned long' and it's not in the range 0 to the rank of input, exclusive"); - -promise_test(async t => { - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); - const validAxisArray = getAxisArray(dimensions2D); - for (let axis of validAxisArray) { - let size = dimensions2D[axis]; - const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); - const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); - for (let axis of notUnsignedLongAxisArray) { - assert_false(typeof axis === 'number' && Number.isInteger(axis), "[batchNormalization] options.axis should be of 'unsigned long'"); - assert_throws_js(TypeError, () => builder.batchNormalization(input, mean, variance, {axis})); - } - } - } -}, '[batchNormalization] TypeError is expected if options.axis is not an unsigned long interger'); - -promise_test(async t => { - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); - const validAxisArray = getAxisArray(dimensions2D); - for (let axis of validAxisArray) { - const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); - for (let dimensions of allWebNNDimensionsArray) { - if (dimensions.length !== 1) { - // set mean not be 1D tensor - const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions}); - assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance)); - } - } - } - } -}, "[batchNormalization] DataError is expected if the size of mean.dimensions is not 1"); - -promise_test(async t => { - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); - const validAxisArray = getAxisArray(dimensions2D); - for (let axis of validAxisArray) { - let size = dimensions2D[axis]; - const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); - for (let offset of adjustOffsetsArray) { - const adjustedSize = size + offset; - const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [adjustedSize]}); - assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis})); - } - } - } -}, "[batchNormalization] DataError is expected if mean.dimensions[0] is not equal to input.dimensions[options.axis]"); - -promise_test(async t => { - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); - const validAxisArray = getAxisArray(dimensions2D); - for (let axis of validAxisArray) { - const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); - for (let dimensions of allWebNNDimensionsArray) { - if (dimensions.length !== 1) { - // set variance not be 1D tensor - const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions}); - assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance)); - } - } - } - } -}, "[batchNormalization] DataError is expected if the size of variance.dimensions is not 1"); - -promise_test(async t => { - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); - const validAxisArray = getAxisArray(dimensions2D); - for (let axis of validAxisArray) { - let size = dimensions2D[axis]; - const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); - for (let offset of adjustOffsetsArray) { - const adjustedSize = size + offset; - const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [adjustedSize]}); - assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis})); - } - } - } -}, "[batchNormalization] DataError is expected if variance.dimensions[0] is not equal to input.dimensions[options.axis]"); - -promise_test(async t => { - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); - const validAxisArray = getAxisArray(dimensions2D); - for (let axis of validAxisArray) { - const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); - const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); - for (let dimensions of allWebNNDimensionsArray) { - if (dimensions.length !== 1) { - // set scale not be 1D tensor - const scale = builder.input('scale', {dataType, dimensions}); - assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis, scale})); - } - } - } - } -}, "[batchNormalization] DataError is expected if the size of scale.dimensions is not 1"); - -promise_test(async t => { - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); - const validAxisArray = getAxisArray(dimensions2D); - for (let axis of validAxisArray) { - let size = dimensions2D[axis]; - const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); - const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); - for (let offset of adjustOffsetsArray) { - const adjustedSize = size + offset; - const scale = builder.input('scale', {dataType, dimensions: [adjustedSize]}); - assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis, scale})); - } - } - } -}, "[batchNormalization] DataError is expected if scale.dimensions[0] is not equal to input.dimensions[options.axis]"); - -promise_test(async t => { - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); - const validAxisArray = getAxisArray(dimensions2D); - for (let axis of validAxisArray) { - const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); - const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [dimensions2D[axis]]}); - for (let dimensions of allWebNNDimensionsArray) { - if (dimensions.length !== 1) { - // set bias not be 1D tensor - const bias = builder.input('bias', {dataType, dimensions}); - assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis, bias})); - } - } - } - } -}, "[batchNormalization] DataError is expected if the size of bias.dimensions is not 1"); - -promise_test(async t => { - for (let dataType of allWebNNOperandDataTypes) { - const input = builder.input(`input${++inputIndex}`, {dataType, dimensions: dimensions2D}); - const validAxisArray = getAxisArray(dimensions2D); - for (let axis of validAxisArray) { - let size = dimensions2D[axis]; - const mean = builder.input(`mean${++meanIndex}`, {dataType, dimensions: [size]}); - const variance = builder.input(`variance${++varianceIndex}`, {dataType, dimensions: [size]}); - for (let offset of adjustOffsetsArray) { - const adjustedSize = size + offset; - const bias = builder.input('bias', {dataType, dimensions: [adjustedSize]}); - assert_throws_dom('DataError', () => builder.batchNormalization(input, mean, variance, {axis, bias})); - } - } - } -}, "[batchNormalization] DataError is expected if bias.dimensions[0] is not equal to input.dimensions[options.axis]"); diff --git a/testing/web-platform/tests/webnn/validation_tests/cast.https.any.js b/testing/web-platform/tests/webnn/validation_tests/cast.https.any.js new file mode 100644 index 0000000000..f616203a88 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/cast.https.any.js @@ -0,0 +1,13 @@ +// META: title=validation tests for WebNN API cast operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', {dataType: 'int32', dimensions: [2, 2]}); + + assert_throws_js( + TypeError, () => builder.cast(inputFromOtherBuilder, 'int64')); +}, '[cast] throw if input is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js b/testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js new file mode 100644 index 0000000000..85cd19a566 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/clamp.https.any.js @@ -0,0 +1,7 @@ +// META: title=validation tests for WebNN API clamp operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('clamp'); diff --git a/testing/web-platform/tests/webnn/validation_tests/compute-multiple-arraybufferviews-sharing-same-arraybuffer.https.any.js b/testing/web-platform/tests/webnn/validation_tests/compute-multiple-arraybufferviews-sharing-same-arraybuffer.https.any.js new file mode 100644 index 0000000000..42b123a97e --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/compute-multiple-arraybufferviews-sharing-same-arraybuffer.https.any.js @@ -0,0 +1,50 @@ +// META: title=ensure WebNN MLContext.compute() rejecting detached buffers +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +// These tests are used to reproduce the Chromium issue: +// https://issues.chromium.org/issues/332002364 +promise_test(async t => { + const a = builder.input('a', {dataType: 'float32', dimensions: [2]}); + const b = builder.input('b', {dataType: 'float32', dimensions: [2]}); + const c = builder.add(a, b); + const graph = await builder.build({c}); + const arraybuffer = new ArrayBuffer(100); + const aBuffer = new Float32Array(arraybuffer, 0, 2); + const bBuffer = new Float32Array(arraybuffer, 8, 2); + const cBuffer = new Float32Array(2); + const promise = context.compute(graph, {'a': aBuffer, 'b': bBuffer}, {'c': cBuffer}); + promise_rejects_js(t, TypeError, promise); + }, 'Throw if two input ArrayBufferViews sharing the same ArrayBuffer'); + +promise_test(async t => { + const a = builder.input('a', {dataType: 'float32', dimensions: [2]}); + const [b, c] = builder.split(a, 2); + const graph = await builder.build({b, c}); + const aBuffer = new Float32Array(2); + const arraybuffer = new ArrayBuffer(100); + const bBuffer = new Float32Array(arraybuffer, 0, 1); + const cBuffer = new Float32Array(arraybuffer, 4, 1); + const promise = context.compute(graph, {'a': aBuffer}, {'b': bBuffer, 'c': cBuffer}); + promise_rejects_js(t, TypeError, promise); +}, 'Throw if two output ArrayBufferViews sharing the same ArrayBuffer'); + +promise_test(async t => { + const a = builder.input('a', {dataType: 'float32', dimensions: [2]}); + const b = builder.relu(a); + const graph = await builder.build({b}); + const arraybuffer = new ArrayBuffer(100); + const aBuffer = new Float32Array(arraybuffer, 0, 2); + const bBuffer = new Float32Array(arraybuffer, 8, 2); + const promise = context.compute(graph, {'a': aBuffer}, {'b': bBuffer}); + promise_rejects_js(t, TypeError, promise); +}, 'Throw if input and output ArrayBufferViews sharing the same ArrayBuffer'); + +promise_test(async t => { + const a = builder.input('a', {dataType: 'float32', dimensions: [2]}); + const b = builder.relu(a); + const graph = await builder.build({b}); + const buffer = new Float32Array(2); + const promise = context.compute(graph, {'a': buffer}, {'b': buffer}); + promise_rejects_js(t, TypeError, promise); +}, 'Throw if input and output are the same ArrayBufferView'); diff --git a/testing/web-platform/tests/webnn/validation_tests/concat.https.any.js b/testing/web-platform/tests/webnn/validation_tests/concat.https.any.js new file mode 100644 index 0000000000..b61f2d2bc7 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/concat.https.any.js @@ -0,0 +1,106 @@ +// META: title=validation tests for WebNN API concat operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const tests = [ + { + name: '[concat] Test building Concat with one input.', + inputs: [{dataType: 'float32', dimensions: [4,4,3]}], + axis: 2, + output: {dataType: 'float32', dimensions: [4,4,3]} + }, + { + name: '[concat] Test building Concat with two inputs', + inputs: [{dataType: 'float32', dimensions: [3,1,5]}, + {dataType: 'float32', dimensions: [3,2,5]}], + axis: 1, + output: {dataType: 'float32', dimensions: [3,3,5]} + }, + { + name: '[concat] Test building Concat with three inputs', + inputs: [{dataType: 'float32', dimensions: [3,5,1]}, + {dataType: 'float32', dimensions: [3,5,2]}, + {dataType: 'float32', dimensions: [3,5,3]}], + axis: 2, + output: {dataType: 'float32', dimensions: [3,5,6]} + }, + { + name: '[concat] Test building Concat with two 1D inputs.', + inputs: [{dataType: 'float32', dimensions: [1]}, + {dataType: 'float32', dimensions: [1]}], + axis: 0, + output: {dataType: 'float32', dimensions: [2]} + }, + { + name: '[concat] Throw if the inputs are empty.', + axis: 0, + }, + { + name: '[concat] Throw if the argument types are inconsistent.', + inputs: [{dataType: 'float32', dimensions: [1,1]}, + {dataType: 'int32', dimensions: [1,1]}], + axis: 0, + }, + { + name: '[concat] Throw if the inputs have different ranks.', + inputs: [{dataType: 'float32', dimensions: [1,1]}, + {dataType: 'float32', dimensions: [1,1,1]}], + axis: 0, + }, + { + name: '[concat] Throw if the axis is equal to or greater than the size of ranks', + inputs: [{dataType: 'float32', dimensions: [1,1]}, + {dataType: 'float32', dimensions: [1,1]}], + axis: 2, + }, + { + name: '[concat] Throw if concat with two 0-D scalars.', + inputs: [{dataType: 'float32', dimensions: []}, + {dataType: 'float32', dimensions: []}], + axis: 0, + }, + { + name: '[concat] Throw if the inputs have other axes with different sizes except on the axis.', + inputs: [{dataType: 'float32', dimensions: [1,1,1]}, + {dataType: 'float32', dimensions: [1,2,3]}], + axis: 1, + }, + +]; + +tests.forEach(test => + promise_test(async t => { + let inputs = []; + if (test.inputs) { + for (let i = 0; i < test.inputs.length; ++i) { + inputs[i] = builder.input( + `inputs[${i}]`, + { dataType: test.inputs[i].dataType, dimensions: test.inputs[i].dimensions } + ); + } + } + if (test.output) { + const output = builder.concat(inputs, test.axis); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js(TypeError, () => builder.concat(inputs, test.axis)); + } + }, test.name) + ); + +multi_builder_test(async (t, builder, otherBuilder) => { + const operandDescriptor = {dataType: 'float32', dimensions: [2, 2]}; + + const inputFromOtherBuilder = otherBuilder.input('input', operandDescriptor); + + const input1 = builder.input('input', operandDescriptor); + const input2 = builder.input('input', operandDescriptor); + const input3 = builder.input('input', operandDescriptor); + + assert_throws_js( + TypeError, + () => builder.concat([input1, input2, inputFromOtherBuilder, input3])); +}, '[concat] throw if any input is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/constant.https.any.js b/testing/web-platform/tests/webnn/validation_tests/constant.https.any.js new file mode 100644 index 0000000000..86a60ee209 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/constant.https.any.js @@ -0,0 +1,141 @@ +// META: title=validation tests for WebNN API constant interface +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const tests = [ + // Tests for constant(descriptor, bufferView) + { + name: + '[constant] Test building a 0-D scalar constant without presenting dimensions', + descriptor: {dataType: 'float32'}, + bufferView: {type: Float32Array, byteLength: 1 * 4}, + output: {dataType: 'float32', dimensions: []} + }, + { + name: + '[constant] Test building a 0-D scalar constant with empty dimensions', + descriptor: {dataType: 'float32', dimensions: []}, + bufferView: {type: Float32Array, byteLength: 1 * 4}, + output: {dataType: 'float32', dimensions: []} + }, + { + name: '[constant] Test building a constant with float32 data type', + descriptor: {dataType: 'float32', dimensions: [2, 3]}, + bufferView: {type: Float32Array, byteLength: 6 * 4}, + output: {dataType: 'float32', dimensions: [2, 3]} + }, + { + name: + '[constant] Throw if byte length of bufferView for float32 doesn\'t match the given dimensions', + descriptor: {dataType: 'float32', dimensions: [2, 3]}, + bufferView: { + type: Float32Array, + byteLength: 6 * 4 - 4 // The bufferView's byte length is less than the + // one by given dimensions + } + }, + // TODO (crbug.com/329702838): Test building a constant with float16 data type + { + name: '[constant] Test building a constant with int32 data type', + descriptor: {dataType: 'int32', dimensions: [2, 3]}, + bufferView: {type: Int32Array, byteLength: 6 * 4}, + output: {dataType: 'int32', dimensions: [2, 3]} + }, + { + name: + '[constant] Throw if byte length of bufferView for int32 doesn\'t match the given dimensions', + descriptor: {dataType: 'int32', dimensions: [2, 3]}, + bufferView: { + type: Int32Array, + byteLength: 6 * 4 + 4 // The bufferView's byte length is greater than the + // one by given dimensions + } + }, + { + name: '[constant] Test building a constant with uint32 data type', + descriptor: {dataType: 'uint32', dimensions: [2, 3]}, + bufferView: {type: Uint32Array, byteLength: 6 * 4}, + output: {dataType: 'uint32', dimensions: [2, 3]} + }, + { + name: + '[constant] Throw if byte length of bufferView for uint32 doesn\'t match the given dimensions', + descriptor: {dataType: 'uint32', dimensions: [2, 3]}, + bufferView: {type: Uint32Array, byteLength: 6 * 4 + 4} + }, + { + name: '[constant] Test building a constant with int64 data type', + descriptor: {dataType: 'int64', dimensions: [2, 3]}, + bufferView: {type: BigInt64Array, byteLength: 6 * 8}, + output: {dataType: 'int64', dimensions: [2, 3]} + }, + { + name: + '[constant] Throw if byte length of bufferView for int64 doesn\'t match the given dimensions', + descriptor: {dataType: 'int64', dimensions: [2, 3]}, + bufferView: {type: BigInt64Array, byteLength: 6 * 8 + 8} + }, + { + name: '[constant] Test building a constant with uint64 data type', + descriptor: {dataType: 'uint64', dimensions: [2, 3]}, + bufferView: {type: BigUint64Array, byteLength: 6 * 8}, + output: {dataType: 'uint64', dimensions: [2, 3]} + }, + { + name: + '[constant] Throw if byte length of bufferView for uint64 doesn\'t match the given dimensions', + descriptor: {dataType: 'uint64', dimensions: [2, 3]}, + bufferView: {type: BigUint64Array, byteLength: 6 * 8 + 8} + }, + { + name: '[constant] Test building a constant with int8 data type', + descriptor: {dataType: 'int8', dimensions: [2, 3]}, + bufferView: {type: Int8Array, byteLength: 6 * 1}, + output: {dataType: 'int8', dimensions: [2, 3]} + }, + { + name: + '[constant] Throw if byte length of bufferView for int8 doesn\'t match the given dimensions', + descriptor: {dataType: 'int8', dimensions: [2, 3]}, + bufferView: {type: Int8Array, byteLength: 6 * 4 - 4} + }, + { + name: '[constant] Test building a constant with uint8 data type', + descriptor: {dataType: 'uint8', dimensions: [2, 3]}, + bufferView: {type: Uint8Array, byteLength: 6 * 1}, + output: {dataType: 'uint8', dimensions: [2, 3]} + }, + { + name: + '[constant] Throw if byte length of bufferView for uint8 doesn\'t match the given dimensions', + descriptor: {dataType: 'uint8', dimensions: [2, 3]}, + bufferView: {type: Uint8Array, byteLength: 6 * 4 - 4} + }, + { + name: '[constant] Throw if a dimension is 0', + descriptor: {dataType: 'float32', dimensions: [2, 0]}, + bufferView: {type: Float32Array, byteLength: 2 * 4} + }, + { + name: + '[constant] Throw if bufferView type doesn\'t match the operand data type', + descriptor: {dataType: 'float32', dimensions: [2, 3]}, + bufferView: {type: Int32Array, byteLength: 6 * 4} + } +]; + +tests.forEach( + test => promise_test(async t => { + const buffer = new ArrayBuffer(test.bufferView.byteLength); + const bufferView = new test.bufferView.type(buffer); + if (test.output) { + const constantOperand = builder.constant(test.descriptor, bufferView); + assert_equals(constantOperand.dataType(), test.output.dataType); + assert_array_equals(constantOperand.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, () => builder.constant(test.descriptor, bufferView)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js b/testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js new file mode 100644 index 0000000000..ffc9c2c65d --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/conv2d.https.any.js @@ -0,0 +1,57 @@ +// META: title=validation tests for WebNN API conv2d operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +// Example input in NCHW layout. +const kExampleInputDescriptor = { + dataType: 'float32', + dimensions: [1, 1, 5, 5] +}; +// Example filter in OIHW layout. +const kExampleFilterDescriptor = { + dataType: 'float32', + dimensions: [1, 1, 3, 3] +}; +const kExampleBiasDescriptor = { + dataType: 'float32', + dimensions: [/* output channels */ 1] +}; + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', kExampleInputDescriptor); + + const filter = builder.input('filter', kExampleFilterDescriptor); + assert_throws_js( + TypeError, () => builder.conv2d(inputFromOtherBuilder, filter)); +}, '[conv2d] throw if input is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const filterFromOtherBuilder = + otherBuilder.input('filter', kExampleFilterDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + assert_throws_js( + TypeError, () => builder.conv2d(input, filterFromOtherBuilder)); +}, '[conv2d] throw if filter is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const biasFromOtherBuilder = + otherBuilder.input('bias', kExampleBiasDescriptor); + const options = {inputLayout: 'nchw', bias: biasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const filter = builder.input('filter', kExampleFilterDescriptor); + assert_throws_js(TypeError, () => builder.conv2d(input, filter, options)); +}, '[conv2d] throw if bias option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const activationFromOtherBuilder = otherBuilder.clamp(); + const options = {activation: activationFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const filter = builder.input('filter', kExampleFilterDescriptor); + assert_throws_js(TypeError, () => builder.conv2d(input, filter, options)); +}, '[conv2d] throw if activation option is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js b/testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js new file mode 100644 index 0000000000..c14f445bf3 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/convTranspose2d.https.any.js @@ -0,0 +1,59 @@ +// META: title=validation tests for WebNN API convTranspose2d operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +// Example input in NCHW layout. +const kExampleInputDescriptor = { + dataType: 'float32', + dimensions: [1, 1, 5, 5] +}; +// Example filter in OIHW layout. +const kExampleFilterDescriptor = { + dataType: 'float32', + dimensions: [1, 1, 3, 3] +}; +const kExampleBiasDescriptor = { + dataType: 'float32', + dimensions: [/* output channels */ 1] +}; + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', kExampleInputDescriptor); + + const filter = builder.input('filter', kExampleFilterDescriptor); + assert_throws_js( + TypeError, () => builder.convTranspose2d(inputFromOtherBuilder, filter)); +}, '[convTranspose2d] throw if input is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const filterFromOtherBuilder = + otherBuilder.input('filter', kExampleFilterDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + assert_throws_js( + TypeError, () => builder.convTranspose2d(input, filterFromOtherBuilder)); +}, '[convTranspose2d] throw if filter is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const biasFromOtherBuilder = + otherBuilder.input('bias', kExampleBiasDescriptor); + const options = {inputLayout: 'nchw', bias: biasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const filter = builder.input('filter', kExampleFilterDescriptor); + assert_throws_js( + TypeError, () => builder.convTranspose2d(input, filter, options)); +}, '[convTranspose2d] throw if bias option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const activationFromOtherBuilder = otherBuilder.clamp(); + const options = {activation: activationFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const filter = builder.input('filter', kExampleFilterDescriptor); + assert_throws_js( + TypeError, () => builder.convTranspose2d(input, filter, options)); +}, '[convTranspose2d] throw if activation option is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/elementwise-binary.https.any.js b/testing/web-platform/tests/webnn/validation_tests/elementwise-binary.https.any.js new file mode 100644 index 0000000000..f54626b50d --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/elementwise-binary.https.any.js @@ -0,0 +1,21 @@ +// META: title=validation tests for WebNN API element-wise binary operations +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const kElementwiseBinaryOperators = [ + 'add', + 'sub', + 'mul', + 'div', + 'max', + 'min', + 'pow', +]; + +kElementwiseBinaryOperators.forEach((operatorName) => { + validateTwoInputsOfSameDataType(operatorName); + validateTwoInputsBroadcastable(operatorName); + validateTwoInputsFromMultipleBuilders(operatorName); +}); diff --git a/testing/web-platform/tests/webnn/validation_tests/elementwise-logical.https.any copy.js b/testing/web-platform/tests/webnn/validation_tests/elementwise-logical.https.any copy.js new file mode 100644 index 0000000000..86183b074a --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/elementwise-logical.https.any copy.js @@ -0,0 +1,20 @@ +// META: title=validation tests for WebNN API element-wise logical operations +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const kElementwiseLogicalBinaryOperators = [ + 'equal', + 'greater', + 'greaterOrEqual', + 'lesser', + 'lesserOrEqual', +]; + +kElementwiseLogicalBinaryOperators.forEach((operatorName) => { + validateTwoInputsFromMultipleBuilders(operatorName); +}); + +// The `not()` operator is unary. +validateInputFromOtherBuilder('not'); diff --git a/testing/web-platform/tests/webnn/validation_tests/elementwise-unary.https.any.js b/testing/web-platform/tests/webnn/validation_tests/elementwise-unary.https.any.js new file mode 100644 index 0000000000..f87c61b4e4 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/elementwise-unary.https.any.js @@ -0,0 +1,39 @@ +// META: title=validation tests for WebNN API element-wise unary operations +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const kElementwiseUnaryOperators = [ + 'abs', 'ceil', 'cos', 'erf', 'exp', 'floor', 'identity', 'log', 'neg', + 'reciprocal', 'sin', 'sqrt', 'tan' +]; + +kElementwiseUnaryOperators.forEach((operatorName) => { + validateInputFromAnotherBuilder(operatorName); +}); + +const kElementwiseUnaryOperations = [ + { + name: 'abs', + supportedDataTypes: [...floatingPointTypes, ...signedIntegerTypes] + }, + {name: 'ceil', supportedDataTypes: floatingPointTypes}, + {name: 'exp', supportedDataTypes: floatingPointTypes}, + {name: 'floor', supportedDataTypes: floatingPointTypes}, + {name: 'log', supportedDataTypes: floatingPointTypes}, { + name: 'neg', + supportedDataTypes: [...floatingPointTypes, ...signedIntegerTypes] + }, + {name: 'sin', supportedDataTypes: floatingPointTypes}, + {name: 'tan', supportedDataTypes: floatingPointTypes}, + {name: 'erf', supportedDataTypes: floatingPointTypes}, + {name: 'identity', supportedDataTypes: allWebNNOperandDataTypes}, + {name: 'logicalNot', supportedDataTypes: ['uint8']}, + {name: 'reciprocal', supportedDataTypes: floatingPointTypes}, + {name: 'sqrt', supportedDataTypes: floatingPointTypes} +]; + +kElementwiseUnaryOperations.forEach((operation) => { + validateUnaryOperation(operation.name, operation.supportedDataTypes); +}); diff --git a/testing/web-platform/tests/webnn/validation_tests/elementwise_binary.https.any.js b/testing/web-platform/tests/webnn/validation_tests/elementwise_binary.https.any.js deleted file mode 100644 index 97a1a2b93c..0000000000 --- a/testing/web-platform/tests/webnn/validation_tests/elementwise_binary.https.any.js +++ /dev/null @@ -1,11 +0,0 @@ -// META: title=validation tests for WebNN API element-wise binary operations -// META: global=window,dedicatedworker -// META: script=../resources/utils_validation.js -// META: timeout=long - -'use strict'; - -['add', 'sub', 'mul', 'div', 'max', 'min', 'pow'].forEach((operationName) => { - validateTwoInputsOfSameDataType(operationName); - validateTwoInputsBroadcastable(operationName); -}); diff --git a/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js new file mode 100644 index 0000000000..6e842cb691 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/elu.https.any.js @@ -0,0 +1,7 @@ +// META: title=validation tests for WebNN API elu operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('elu'); diff --git a/testing/web-platform/tests/webnn/validation_tests/expand.https.any.js b/testing/web-platform/tests/webnn/validation_tests/expand.https.any.js new file mode 100644 index 0000000000..d90ab89468 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/expand.https.any.js @@ -0,0 +1,14 @@ +// META: title=validation tests for WebNN API expand operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', {dataType: 'float32', dimensions: [2, 1, 2]}); + + const newShape = [2, 2, 2]; + assert_throws_js( + TypeError, () => builder.expand(inputFromOtherBuilder, newShape)); +}, '[expand] throw if input is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/gather.https.any.js b/testing/web-platform/tests/webnn/validation_tests/gather.https.any.js index 67ac7d7be3..184e8033e6 100644 --- a/testing/web-platform/tests/webnn/validation_tests/gather.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/gather.https.any.js @@ -1,7 +1,6 @@ // META: title=validation tests for WebNN API gather operation // META: global=window,dedicatedworker // META: script=../resources/utils_validation.js -// META: timeout=long 'use strict'; @@ -60,3 +59,23 @@ tests.forEach( TypeError, () => builder.gather(input, indices, options)); } }, test.name)); + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', {dataType: 'float32', dimensions: [2, 2]}); + + const indices = + builder.input('indices', {dataType: 'int64', dimensions: [2, 2]}); + assert_throws_js( + TypeError, () => builder.gather(inputFromOtherBuilder, indices)); +}, '[gather] throw if input is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const indicesFromOtherBuilder = + otherBuilder.input('indices', {dataType: 'int64', dimensions: [2, 2]}); + + const input = + builder.input('input', {dataType: 'float32', dimensions: [2, 2]}); + assert_throws_js( + TypeError, () => builder.gather(input, indicesFromOtherBuilder)); +}, '[gather] throw if indices is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js b/testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js new file mode 100644 index 0000000000..77ce6383cc --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/gemm.https.any.js @@ -0,0 +1,21 @@ +// META: title=validation tests for WebNN API gemm operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const kExampleInputDescriptor = { + dataType: 'float32', + dimensions: [2, 2] +}; + +validateTwoInputsFromMultipleBuilders('gemm'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const cFromOtherBuilder = otherBuilder.input('c', kExampleInputDescriptor); + const options = {c: cFromOtherBuilder}; + + const a = builder.input('a', kExampleInputDescriptor); + const b = builder.input('b', kExampleInputDescriptor); + assert_throws_js(TypeError, () => builder.gemm(a, b, options)); +}, '[gemm] throw if c option is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/gru.https.any.js b/testing/web-platform/tests/webnn/validation_tests/gru.https.any.js index 295baab9c2..50d39d297a 100644 --- a/testing/web-platform/tests/webnn/validation_tests/gru.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/gru.https.any.js @@ -1,398 +1,445 @@ // META: title=validation tests for WebNN API gru operation // META: global=window,dedicatedworker // META: script=../resources/utils_validation.js -// META: timeout=long 'use strict'; -const steps = 2, batchSize = 3, inputSize = 4, hiddenSize = 5, - numDirections = 1; +const steps = 2, batchSize = 3, inputSize = 4, hiddenSize = 5, oneDirection = 1, + bothDirections = 2; + +// Dimensions required of required inputs. +const kValidInputDimensions = [steps, batchSize, inputSize]; +const kValidWeightDimensions = [oneDirection, 3 * hiddenSize, inputSize]; +const kValidRecurrentWeightDimensions = + [oneDirection, 3 * hiddenSize, hiddenSize]; +// Dimensions required of optional inputs. +const kValidBiasDimensions = [oneDirection, 3 * hiddenSize]; +const kValidRecurrentBiasDimensions = [oneDirection, 3 * hiddenSize]; +const kValidInitialHiddenStateDimensions = + [oneDirection, batchSize, hiddenSize]; + +// Example descriptors which are valid according to the above dimensions. +const kExampleInputDescriptor = { + dataType: 'float32', + dimensions: kValidInputDimensions +}; +const kExampleWeightDescriptor = { + dataType: 'float32', + dimensions: kValidWeightDimensions +}; +const kExampleRecurrentWeightDescriptor = { + dataType: 'float32', + dimensions: kValidRecurrentWeightDimensions +}; +const kExampleBiasDescriptor = { + dataType: 'float32', + dimensions: kValidBiasDimensions +}; +const kExampleRecurrentBiasDescriptor = { + dataType: 'float32', + dimensions: kValidRecurrentBiasDimensions +}; +const kExampleInitialHiddenStateDescriptor = { + dataType: 'float32', + dimensions: kValidInitialHiddenStateDimensions +}; const tests = [ - { - name: '[gru] Test with default options', - input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize, - outputs: [ - { dataType: 'float32', dimensions: [numDirections, batchSize, hiddenSize] } - ] - }, - { - name: '[gru] Test with given options', - input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float32', - dimensions: [/*numDirections=*/ 2, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [/*numDirections=*/ 2, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize, - options: { - bias: { - dataType: 'float32', - dimensions: [/*numDirections=*/ 2, 3 * hiddenSize] - }, - recurrentBias: { - dataType: 'float32', - dimensions: [/*numDirections=*/ 2, 3 * hiddenSize] - }, - initialHiddenState: { - dataType: 'float32', - dimensions: [/*numDirections=*/ 2, batchSize, hiddenSize] - }, - restAfter: true, - returnSequence: true, - direction: 'both', - layout: 'rzn', - activations: ['sigmoid', 'relu'] - }, - outputs: [ - { - dataType: 'float32', - dimensions: [/*numDirections=*/ 2, batchSize, hiddenSize] - }, - { - dataType: 'float32', - dimensions: [steps, /*numDirections=*/ 2, batchSize, hiddenSize] - } - ] - }, - { - name: '[gru] TypeError is expected if steps equals to zero', - input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, - steps: 0, - hiddenSize: hiddenSize, - }, - { - name: '[gru] TypeError is expected if hiddenSize equals to zero', - input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: 0 - }, - { - name: '[gru] TypeError is expected if hiddenSize is too large', - input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: 4294967295, + { + name: '[gru] Test with default options', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + steps: steps, + hiddenSize: hiddenSize, + outputs: [ + {dataType: 'float32', dimensions: [oneDirection, batchSize, hiddenSize]} + ] + }, + { + name: '[gru] Test with given options', + input: kExampleInputDescriptor, + weight: { + dataType: 'float32', + dimensions: [bothDirections, 3 * hiddenSize, inputSize] }, - { - name: - '[gru] TypeError is expected if the data type of the inputs is not one of the floating point types', - input: { dataType: 'uint32', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'uint32', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'uint32', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize + recurrentWeight: { + dataType: 'float32', + dimensions: [bothDirections, 3 * hiddenSize, hiddenSize] }, - { - name: - '[gru] TypeError is expected if the rank of input is not 3', - input: { dataType: 'float32', dimensions: [steps, batchSize] }, - weight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize + steps: steps, + hiddenSize: hiddenSize, + options: { + bias: {dataType: 'float32', dimensions: [bothDirections, 3 * hiddenSize]}, + recurrentBias: + {dataType: 'float32', dimensions: [bothDirections, 3 * hiddenSize]}, + initialHiddenState: { + dataType: 'float32', + dimensions: [bothDirections, batchSize, hiddenSize] + }, + restAfter: true, + returnSequence: true, + direction: 'both', + layout: 'rzn', + activations: ['sigmoid', 'relu'] }, - { - name: - '[gru] TypeError is expected if input.dimensions[0] is not equal to steps', - input: { dataType: 'float32', dimensions: [1000, batchSize, inputSize] }, - weight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize + outputs: [ + { + dataType: 'float32', + dimensions: [bothDirections, batchSize, hiddenSize] + }, + { + dataType: 'float32', + dimensions: [steps, bothDirections, batchSize, hiddenSize] + } + ] + }, + { + name: '[gru] TypeError is expected if steps equals to zero', + input: kExampleInputDescriptor, + weight: { + dataType: 'float32', + dimensions: [oneDirection, 4 * hiddenSize, inputSize] }, - { - name: '[gru] TypeError is expected if weight.dimensions[1] is not 3 * hiddenSize', - input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize + recurrentWeight: { + dataType: 'float32', + dimensions: [oneDirection, 4 * hiddenSize, hiddenSize] }, - { - name: - '[gru] TypeError is expected if the rank of recurrentWeight is not 3', - input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: - { dataType: 'float32', dimensions: [numDirections, 3 * hiddenSize] }, - steps: steps, - hiddenSize: hiddenSize + steps: 0, + hiddenSize: hiddenSize, + }, + { + name: '[gru] TypeError is expected if hiddenSize equals to zero', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + steps: steps, + hiddenSize: 0 + }, + { + name: '[gru] TypeError is expected if hiddenSize is too large', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + steps: steps, + hiddenSize: 4294967295, + }, + { + name: + '[gru] TypeError is expected if the data type of the inputs is not one of the floating point types', + input: {dataType: 'uint32', dimensions: kValidInputDimensions}, + weight: {dataType: 'uint32', dimensions: kValidWeightDimensions}, + recurrentWeight: + {dataType: 'uint32', dimensions: kValidRecurrentWeightDimensions}, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: '[gru] TypeError is expected if the rank of input is not 3', + input: {dataType: 'float32', dimensions: [steps, batchSize]}, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[gru] TypeError is expected if input.dimensions[0] is not equal to steps', + input: {dataType: 'float32', dimensions: [1000, batchSize, inputSize]}, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[gru] TypeError is expected if weight.dimensions[1] is not 3 * hiddenSize', + input: kExampleInputDescriptor, + weight: { + dataType: 'float32', + dimensions: [oneDirection, 4 * hiddenSize, inputSize] }, - { - name: - '[gru] TypeError is expected if the recurrentWeight.dimensions is invalid', - input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: - { dataType: 'float32', dimensions: [numDirections, 4 * hiddenSize, inputSize] }, - steps: steps, - hiddenSize: hiddenSize - }, - { - name: - '[gru] TypeError is expected if the size of options.activations is not 2', - input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize, - options: { activations: ['sigmoid', 'tanh', 'relu'] } - }, - { - name: - '[gru] TypeError is expected if the rank of options.bias is not 2', - input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize, - options: { bias: { dataType: 'float32', dimensions: [numDirections] } } - }, - { - name: - '[gru] TypeError is expected if options.bias.dimensions[1] is not 3 * hiddenSize', - input: { dataType: 'float32', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize, - options: { bias: { dataType: 'float32', dimensions: [numDirections, hiddenSize] } } - }, - { - name: - '[gru] TypeError is expected if options.recurrentBias.dimensions[1] is not 3 * hiddenSize', - input: { dataType: 'float16', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float16', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float16', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize, - options: { - recurrentBias: { dataType: 'float16', dimensions: [numDirections, 4 * hiddenSize] } - } + recurrentWeight: kExampleRecurrentWeightDescriptor, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: '[gru] TypeError is expected if the rank of recurrentWeight is not 3', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: + {dataType: 'float32', dimensions: [oneDirection, 3 * hiddenSize]}, + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[gru] TypeError is expected if the recurrentWeight.dimensions is invalid', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: { + dataType: 'float32', + dimensions: [oneDirection, 4 * hiddenSize, inputSize] }, - { - name: - '[gru] TypeError is expected if the rank of options.initialHiddenState is not 3', - input: { dataType: 'float16', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float16', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float16', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize, - options: { - initialHiddenState: { - dataType: 'float16', - dimensions: [numDirections, batchSize] - } - } - }, - { - name: - '[gru] TypeError is expected if options.initialHiddenState.dimensions[2] is not inputSize', - input: { dataType: 'float16', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float16', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float16', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize, - options: { - initialHiddenState: { - dataType: 'float16', - dimensions: [numDirections, batchSize, 3 * hiddenSize] - } - } - }, - { - name: - '[gru] TypeError is expected if the dataType of options.initialHiddenState is incorrect', - input: { dataType: 'float16', dimensions: [steps, batchSize, inputSize] }, - weight: { - dataType: 'float16', - dimensions: [numDirections, 3 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float16', - dimensions: [numDirections, 3 * hiddenSize, hiddenSize] - }, - steps: steps, - hiddenSize: hiddenSize, - options: { - initialHiddenState: { - dataType: 'uint64', - dimensions: [numDirections, batchSize, hiddenSize] - } - } + steps: steps, + hiddenSize: hiddenSize + }, + { + name: + '[gru] TypeError is expected if the size of options.activations is not 2', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + steps: steps, + hiddenSize: hiddenSize, + options: {activations: ['sigmoid', 'tanh', 'relu']} + }, + { + name: '[gru] TypeError is expected if the rank of options.bias is not 2', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + steps: steps, + hiddenSize: hiddenSize, + options: {bias: {dataType: 'float32', dimensions: [oneDirection]}} + }, + { + name: + '[gru] TypeError is expected if options.bias.dimensions[1] is not 3 * hiddenSize', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + steps: steps, + hiddenSize: hiddenSize, + options: + {bias: {dataType: 'float32', dimensions: [oneDirection, hiddenSize]}} + }, + { + name: + '[gru] TypeError is expected if options.recurrentBias.dimensions[1] is not 3 * hiddenSize', + input: {dataType: 'float16', dimensions: kValidInputDimensions}, + weight: {dataType: 'float16', dimensions: kValidWeightDimensions}, + recurrentWeight: + {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions}, + steps: steps, + hiddenSize: hiddenSize, + options: { + recurrentBias: + {dataType: 'float16', dimensions: [oneDirection, 4 * hiddenSize]} } + }, + { + name: + '[gru] TypeError is expected if the rank of options.initialHiddenState is not 3', + input: {dataType: 'float16', dimensions: kValidInputDimensions}, + weight: {dataType: 'float16', dimensions: kValidWeightDimensions}, + recurrentWeight: + {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions}, + steps: steps, + hiddenSize: hiddenSize, + options: { + initialHiddenState: + {dataType: 'float16', dimensions: [oneDirection, batchSize]} + } + }, + { + name: + '[gru] TypeError is expected if options.initialHiddenState.dimensions[2] is not inputSize', + input: {dataType: 'float16', dimensions: kValidInputDimensions}, + weight: {dataType: 'float16', dimensions: kValidWeightDimensions}, + recurrentWeight: + {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions}, + steps: steps, + hiddenSize: hiddenSize, + options: { + initialHiddenState: { + dataType: 'float16', + dimensions: [oneDirection, batchSize, 3 * hiddenSize] + } + } + }, + { + name: + '[gru] TypeError is expected if the dataType of options.initialHiddenState is incorrect', + input: {dataType: 'float16', dimensions: kValidInputDimensions}, + weight: {dataType: 'float16', dimensions: kValidWeightDimensions}, + recurrentWeight: + {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions}, + steps: steps, + hiddenSize: hiddenSize, + options: { + initialHiddenState: { + dataType: 'uint64', + dimensions: [oneDirection, batchSize, hiddenSize] + } + } + } ]; tests.forEach( test => promise_test(async t => { - const input = builder.input( - 'input', - { dataType: test.input.dataType, dimensions: test.input.dimensions }); - const weight = builder.input( - 'weight', - { dataType: test.weight.dataType, dimensions: test.weight.dimensions }); - const recurrentWeight = builder.input('recurrentWeight', { - dataType: test.recurrentWeight.dataType, - dimensions: test.recurrentWeight.dimensions - }); + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + const weight = builder.input( + 'weight', + {dataType: test.weight.dataType, dimensions: test.weight.dimensions}); + const recurrentWeight = builder.input('recurrentWeight', { + dataType: test.recurrentWeight.dataType, + dimensions: test.recurrentWeight.dimensions + }); - const options = {}; - if (test.options) { - if (test.options.bias) { - options.bias = builder.input('bias', { - dataType: test.options.bias.dataType, - dimensions: test.options.bias.dimensions - }); - } - if (test.options.recurrentBias) { - options.bias = builder.input('recurrentBias', { - dataType: test.options.recurrentBias.dataType, - dimensions: test.options.recurrentBias.dimensions - }); - } - if (test.options.initialHiddenState) { - options.initialHiddenState = builder.input('initialHiddenState', { - dataType: test.options.initialHiddenState.dataType, - dimensions: test.options.initialHiddenState.dimensions - }); - } - if (test.options.resetAfter) { - options.resetAfter = test.options.resetAfter; - } - if (test.options.returnSequence) { - options.returnSequence = test.options.returnSequence; - } - if (test.options.direction) { - options.direction = test.options.direction; - } - if (test.options.layout) { - options.layout = test.options.layout; - } - if (test.options.activations) { - options.activations = []; - test.options.activations.forEach( - activation => options.activations.push(builder[activation]())); - } + const options = {}; + if (test.options) { + if (test.options.bias) { + options.bias = builder.input('bias', { + dataType: test.options.bias.dataType, + dimensions: test.options.bias.dimensions + }); + } + if (test.options.recurrentBias) { + options.bias = builder.input('recurrentBias', { + dataType: test.options.recurrentBias.dataType, + dimensions: test.options.recurrentBias.dimensions + }); + } + if (test.options.initialHiddenState) { + options.initialHiddenState = builder.input('initialHiddenState', { + dataType: test.options.initialHiddenState.dataType, + dimensions: test.options.initialHiddenState.dimensions + }); } + if (test.options.resetAfter) { + options.resetAfter = test.options.resetAfter; + } + if (test.options.returnSequence) { + options.returnSequence = test.options.returnSequence; + } + if (test.options.direction) { + options.direction = test.options.direction; + } + if (test.options.layout) { + options.layout = test.options.layout; + } + if (test.options.activations) { + options.activations = []; + test.options.activations.forEach( + activation => options.activations.push(builder[activation]())); + } + } - if (test.outputs) { - const outputs = builder.gru( - input, weight, recurrentWeight, test.steps, test.hiddenSize, - options); - assert_equals(outputs.length, test.outputs.length); - for (let i = 0; i < outputs.length; ++i) { - assert_equals(outputs[i].dataType(), test.outputs[i].dataType); - assert_array_equals(outputs[i].shape(), test.outputs[i].dimensions); - } - } else { - assert_throws_js( - TypeError, - () => builder.gru( - input, weight, recurrentWeight, test.steps, test.hiddenSize, - options)); + if (test.outputs) { + const outputs = builder.gru( + input, weight, recurrentWeight, test.steps, test.hiddenSize, + options); + assert_equals(outputs.length, test.outputs.length); + for (let i = 0; i < outputs.length; ++i) { + assert_equals(outputs[i].dataType(), test.outputs[i].dataType); + assert_array_equals(outputs[i].shape(), test.outputs[i].dimensions); } - }, test.name));
\ No newline at end of file + } else { + assert_throws_js( + TypeError, + () => builder.gru( + input, weight, recurrentWeight, test.steps, test.hiddenSize, + options)); + } + }, test.name)); + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', kExampleInputDescriptor); + + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.gru( + inputFromOtherBuilder, weight, recurrentWeight, steps, hiddenSize)); +}, '[gru] throw if input is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const weightFromOtherBuilder = + otherBuilder.input('weight', kExampleWeightDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.gru( + input, weightFromOtherBuilder, recurrentWeight, steps, hiddenSize)); +}, '[gru] throw if weight is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const recurrentWeightFromOtherBuilder = + otherBuilder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.gru( + input, weight, recurrentWeightFromOtherBuilder, steps, hiddenSize)); +}, '[gru] throw if recurrentWeight is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const biasFromOtherBuilder = + otherBuilder.input('bias', kExampleBiasDescriptor); + const options = {bias: biasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.gru( + input, weight, recurrentWeight, steps, hiddenSize, options)); +}, '[gru] throw if bias option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const recurrentBiasFromOtherBuilder = + otherBuilder.input('recurrentBias', kExampleRecurrentBiasDescriptor); + const options = {recurrentBias: recurrentBiasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.gru( + input, weight, recurrentWeight, steps, hiddenSize, options)); +}, '[gru] throw if recurrentBias option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const initialHiddenStateFromOtherBuilder = otherBuilder.input( + 'initialHiddenState', kExampleInitialHiddenStateDescriptor); + const options = {initialHiddenState: initialHiddenStateFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.gru( + input, weight, recurrentWeight, steps, hiddenSize, options)); +}, '[gru] throw if initialHiddenState option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const activation = builder.clamp(); + const activationFromOtherBuilder = otherBuilder.clamp(); + const options = {activations: [activation, activationFromOtherBuilder]}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.gru( + input, weight, recurrentWeight, steps, hiddenSize, options)); +}, '[gru] throw if any activation option is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/gruCell.https.any.js b/testing/web-platform/tests/webnn/validation_tests/gruCell.https.any.js new file mode 100644 index 0000000000..3cd9d32b07 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/gruCell.https.any.js @@ -0,0 +1,471 @@ +// META: title=validation tests for WebNN API gruCell operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const batchSize = 3, inputSize = 4, hiddenSize = 5; + +// Dimensions required of required inputs. +const kValidInputDimensions = [batchSize, inputSize]; +const kValidWeightDimensions = [3 * hiddenSize, inputSize]; +const kValidRecurrentWeightDimensions = [3 * hiddenSize, hiddenSize]; +const kValidHiddenStateDimensions = [batchSize, hiddenSize]; +// Dimensions required of optional inputs. +const kValidBiasDimensions = [3 * hiddenSize]; +const kValidRecurrentBiasDimensions = [3 * hiddenSize]; +// Dimensions required of required output. +const kValidOutputDimensions = [batchSize, hiddenSize]; + +// Example descriptors which are valid according to the above dimensions. +const kExampleInputDescriptor = { + dataType: 'float32', + dimensions: kValidInputDimensions +}; +const kExampleWeightDescriptor = { + dataType: 'float32', + dimensions: kValidWeightDimensions +}; +const kExampleRecurrentWeightDescriptor = { + dataType: 'float32', + dimensions: kValidRecurrentWeightDimensions +}; +const kExampleHiddenStateDescriptor = { + dataType: 'float32', + dimensions: kValidHiddenStateDimensions +}; +const kExampleBiasDescriptor = { + dataType: 'float32', + dimensions: kValidBiasDimensions +}; +const kExampleRecurrentBiasDescriptor = { + dataType: 'float32', + dimensions: kValidRecurrentBiasDimensions +}; +const kExampleOutputDescriptor = { + dataType: 'float32', + dimensions: kValidOutputDimensions + }; + +const tests = [ + { + name: '[gruCell] Test with default options', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize, + output: kExampleOutputDescriptor + }, + { + name: '[gruCell] Test with given options', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize, + options: { + bias: kExampleBiasDescriptor, + recurrentBias: kExampleRecurrentBiasDescriptor, + restAfter: true, + layout: 'rzn', + activations: ['sigmoid', 'relu'] + }, + output: kExampleOutputDescriptor + }, + { + name: '[gruCell] Throw if hiddenSize equals to zero', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: 0 + }, + { + name: '[gruCell] Throw if hiddenSize is too large', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: 4294967295, + }, + { + name: + '[gruCell] Throw if the data type of the inputs is not one of the floating point types', + input: { dataType: 'uint32', dimensions: kValidInputDimensions }, + weight: { dataType: 'uint32', dimensions: kValidWeightDimensions }, + recurrentWeight: { + dataType: 'uint32', + dimensions: kValidRecurrentWeightDimensions + }, + hiddenState: { + dataType: 'uint32', + dimensions: kValidHiddenStateDimensions + }, + hiddenSize: hiddenSize + }, + { + name: + '[gruCell] Throw if the rank of input is not 2', + input: { dataType: 'float32', dimensions: [batchSize] }, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize + }, + { + name: + '[gruCell] Throw if the input.dimensions[1] is incorrect', + input: { dataType: 'float32', dimensions: [inputSize, inputSize] }, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize + }, + { + name: '[gruCell] Throw if data type of weight is not one of the floating point types', + input: kExampleInputDescriptor, + weight: { + dataType: 'int8', + dimensions: [3 * hiddenSize, inputSize] + }, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize + }, + { + name: '[gruCell] Throw if rank of weight is not 2', + input: kExampleInputDescriptor, + weight: { + dataType: 'float32', + dimensions: [3 * hiddenSize] + }, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize + }, + { + name: '[gruCell] Throw if weight.dimensions[0] is not 3 * hiddenSize', + input: kExampleInputDescriptor, + weight: { + dataType: 'float32', + dimensions: [4 * hiddenSize, inputSize] + }, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize + }, + { + name: '[gruCell] Throw if data type of recurrentWeight is not one of the floating point types', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: { + dataType: 'int32', + dimensions: [3 * hiddenSize, hiddenSize] + }, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize + }, + { + name: + '[gruCell] Throw if the rank of recurrentWeight is not 2', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: + { dataType: 'float32', dimensions: [3 * hiddenSize] }, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize + }, + { + name: + '[gruCell] Throw if the recurrentWeight.dimensions is invalid', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: + { dataType: 'float32', dimensions: [4 * hiddenSize, inputSize] }, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize + }, + { + name: + '[gruCell] Throw if data type of hiddenState is not one of the floating point types', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: + kExampleRecurrentWeightDescriptor, + hiddenState: { + dataType: 'uint32', + dimensions: [batchSize, hiddenSize] + }, + hiddenSize: hiddenSize + }, + { + name: + '[gruCell] Throw if the rank of hiddenState is not 2', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: + kExampleRecurrentWeightDescriptor, + hiddenState: { + dataType: 'float32', + dimensions: [hiddenSize] + }, + hiddenSize: hiddenSize + }, + { + name: + '[gruCell] Throw if the hiddenState.dimensions is invalid', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: { + dataType: 'float32', + dimensions: [batchSize, 3 * hiddenSize] + }, + hiddenSize: hiddenSize + }, + { + name: + '[gruCell] Throw if the size of options.activations is not 2', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize, + options: { activations: ['sigmoid', 'tanh', 'relu'] } + }, + { + name: + '[gruCell] Throw if data type of options.bias is not one of the floating point types', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize, + options: { bias: { dataType: 'uint8', dimensions: [3 * hiddenSize] } } + }, + { + name: + '[gruCell] Throw if the rank of options.bias is not 1', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize, + options: { bias: { dataType: 'float32', dimensions: [batchSize, 3 * hiddenSize] } } + }, + { + name: + '[gruCell] Throw if options.bias.dimensions[0] is not 3 * hiddenSize', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize, + options: { bias: { dataType: 'float32', dimensions: [2 * hiddenSize] } } + }, + { + name: + '[gruCell] Throw if data type of options.recurrentBias is not one of the floating point types', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize, + options: { recurrentBias: { dataType: 'int8', dimensions: [3 * hiddenSize] } } + }, + { + name: + '[gruCell] Throw if the rank of options.recurrentBias is not 1', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize, + options: { recurrentBias: { dataType: 'float32', dimensions: [batchSize, 3 * hiddenSize] } } + }, + { + name: + '[gruCell] Throw if options.recurrentBias.dimensions[0] is not 3 * hiddenSize', + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, + hiddenState: kExampleHiddenStateDescriptor, + hiddenSize: hiddenSize, + options: { + recurrentBias: { dataType: 'float16', dimensions: [4 * hiddenSize] } + } + } +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + { dataType: test.input.dataType, dimensions: test.input.dimensions }); + const weight = builder.input( + 'weight', + { dataType: test.weight.dataType, dimensions: test.weight.dimensions }); + const recurrentWeight = builder.input('recurrentWeight', { + dataType: test.recurrentWeight.dataType, + dimensions: test.recurrentWeight.dimensions + }); + const hiddenState = builder.input('hiddenState', { + dataType: test.hiddenState.dataType, + dimensions: test.hiddenState.dimensions + }); + + const options = {}; + if (test.options) { + if (test.options.bias) { + options.bias = builder.input('bias', { + dataType: test.options.bias.dataType, + dimensions: test.options.bias.dimensions + }); + } + if (test.options.recurrentBias) { + options.bias = builder.input('recurrentBias', { + dataType: test.options.recurrentBias.dataType, + dimensions: test.options.recurrentBias.dimensions + }); + } + if (test.options.resetAfter) { + options.resetAfter = test.options.resetAfter; + } + if (test.options.layout) { + options.layout = test.options.layout; + } + if (test.options.activations) { + options.activations = []; + test.options.activations.forEach( + activation => options.activations.push(builder[activation]())); + } + } + + if (test.output) { + const output = builder.gruCell( + input, weight, recurrentWeight, hiddenState, test.hiddenSize, + options); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, + () => builder.gruCell( + input, weight, recurrentWeight, hiddenState, test.hiddenSize, + options)); + } + }, test.name)); + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', kExampleInputDescriptor); + + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + assert_throws_js( + TypeError, + () => builder.gruCell( + inputFromOtherBuilder, weight, recurrentWeight, hiddenState, + hiddenSize)); +}, '[gruCell] throw if input is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const weightFromOtherBuilder = + otherBuilder.input('weight', kExampleWeightDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + assert_throws_js( + TypeError, + () => builder.gruCell( + input, weightFromOtherBuilder, recurrentWeight, hiddenState, + hiddenSize)); +}, '[gruCell] throw if weight is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const recurrentWeightFromOtherBuilder = + otherBuilder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + assert_throws_js( + TypeError, + () => builder.gruCell( + input, weight, recurrentWeightFromOtherBuilder, hiddenState, + hiddenSize)); +}, '[gruCell] throw if recurrentWeight is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const hiddenStateFromOtherBuilder = + otherBuilder.input('hiddenState', kExampleHiddenStateDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.gruCell( + input, weight, recurrentWeight, hiddenStateFromOtherBuilder, + hiddenSize)); +}, '[gruCell] throw if hiddenState is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const biasFromOtherBuilder = + otherBuilder.input('bias', kExampleBiasDescriptor); + const options = {bias: biasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + assert_throws_js( + TypeError, + () => builder.gruCell( + input, weight, recurrentWeight, hiddenState, hiddenSize, options)); +}, '[gruCell] throw if bias option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const recurrentBiasFromOtherBuilder = + otherBuilder.input('recurrentBias', kExampleRecurrentBiasDescriptor); + const options = {recurrentBias: recurrentBiasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + assert_throws_js( + TypeError, + () => builder.gruCell( + input, weight, recurrentWeight, hiddenState, hiddenSize, options)); +}, '[gruCell] throw if recurrentBias option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const activation = builder.clamp(); + const activationFromOtherBuilder = otherBuilder.clamp(); + const options = {activations: [activation, activationFromOtherBuilder]}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + assert_throws_js( + TypeError, + () => builder.gruCell( + input, weight, recurrentWeight, hiddenState, hiddenSize, options)); +}, '[gruCell] throw if any activation option is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js b/testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js new file mode 100644 index 0000000000..01b24dbc7c --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/hardSigmoid.https.any.js @@ -0,0 +1,7 @@ +// META: title=validation tests for WebNN API hardSigmoid operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('hardSigmoid'); diff --git a/testing/web-platform/tests/webnn/validation_tests/hardSwish.https.any.js b/testing/web-platform/tests/webnn/validation_tests/hardSwish.https.any.js new file mode 100644 index 0000000000..97ecfb4142 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/hardSwish.https.any.js @@ -0,0 +1,10 @@ +// META: title=validation tests for WebNN API hardSwish operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('hardSwish'); + +validateUnaryOperation( + 'hardSwish', floatingPointTypes, /*alsoBuildActivation=*/ true); diff --git a/testing/web-platform/tests/webnn/validation_tests/input.https.any.js b/testing/web-platform/tests/webnn/validation_tests/input.https.any.js new file mode 100644 index 0000000000..a7561bf628 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/input.https.any.js @@ -0,0 +1,70 @@ +// META: title=validation tests for WebNN API input interface +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +// Tests for input(name, descriptor) +const tests = [ + { + testName: + '[input] Test building a 0-D scalar input without presenting dimensions', + name: 'input', + descriptor: {dataType: 'float32'}, + output: {dataType: 'float32', dimensions: []}, + }, + { + testName: '[input] Test building a 0-D scalar input with empty dimensions', + name: 'input', + descriptor: {dataType: 'float32', dimensions: []}, + output: {dataType: 'float32', dimensions: []}, + }, + { + testName: '[input] Test building a 1-D input with int64 data type', + name: 'input', + descriptor: {dataType: 'int64', dimensions: [3]}, + output: {dataType: 'int64', dimensions: [3]}, + }, + { + testName: '[input] Test building a 2-D input without errors', + name: 'input', + descriptor: {dataType: 'float32', dimensions: [3, 4]}, + output: {dataType: 'float32', dimensions: [3, 4]}, + }, + { + testName: '[input] Throw if the name is empty', + name: '', + descriptor: {dataType: 'float32', dimensions: [3, 4]} + }, + { + testName: '[input] Throw if a dimension size is 0', + name: 'input', + descriptor: {dataType: 'float32', dimensions: [3, 0]} + }, + { + testName: + '[input] Throw if the value of any element in dimensions is outside the \'unsigned long\' value range', + name: 'input', + descriptor: {dataType: 'float32', dimensions: [kMaxUnsignedLong + 1]} + }, + { + testName: '[input] Throw if the number of elements is too large', + name: 'input', + descriptor: { + dataType: 'float32', + dimensions: [kMaxUnsignedLong, kMaxUnsignedLong, kMaxUnsignedLong] + } + } +]; + +tests.forEach( + test => promise_test(async t => { + if (test.output) { + const inputOperand = builder.input(test.name, test.descriptor); + assert_equals(inputOperand.dataType(), test.output.dataType); + assert_array_equals(inputOperand.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, () => builder.input(test.name, test.descriptor)); + } + }, test.testName)); diff --git a/testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js b/testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js new file mode 100644 index 0000000000..bdd338588f --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/instanceNormalization.https.any.js @@ -0,0 +1,43 @@ +// META: title=validation tests for WebNN API instanceNormalization operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const kExampleInputDescriptor = { + dataType: 'float32', + dimensions: [2, 2, 2, 2] +}; +// 1D tensor descriptor which may be used for `scale`, or `bias` inputs. +const kExample1DTensorDescriptor = { + dataType: 'float32', + dimensions: [2] +}; + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', kExampleInputDescriptor); + + assert_throws_js( + TypeError, () => builder.instanceNormalization(inputFromOtherBuilder)); +}, '[instanceNormalization] throw if input is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const scaleFromOtherBuilder = + otherBuilder.input('scale', kExample1DTensorDescriptor); + const options = {scale: scaleFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + assert_throws_js( + TypeError, () => builder.instanceNormalization(input, options)); +}, '[instanceNormalization] throw if scale option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const biasFromOtherBuilder = + otherBuilder.input('bias', kExample1DTensorDescriptor); + const options = {bias: biasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + assert_throws_js( + TypeError, () => builder.instanceNormalization(input, options)); +}, '[instanceNormalization] throw if bias option is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js b/testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js new file mode 100644 index 0000000000..e9e9141aa6 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/layerNormalization.https.any.js @@ -0,0 +1,32 @@ +// META: title=validation tests for WebNN API layerNormalization operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const kExampleInputDescriptor = { + dataType: 'float32', + dimensions: [2, 2] +}; + +validateOptionsAxes('layerNormalization', 4); + +validateInputFromAnotherBuilder('layerNormalization'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const scaleFromOtherBuilder = + otherBuilder.input('scale', kExampleInputDescriptor); + const options = {scale: scaleFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + assert_throws_js(TypeError, () => builder.layerNormalization(input, options)); +}, '[layerNormalization] throw if scale option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const biasFromOtherBuilder = + otherBuilder.input('bias', kExampleInputDescriptor); + const options = {bias: biasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + assert_throws_js(TypeError, () => builder.layerNormalization(input, options)); +}, '[layerNormalization] throw if bias option is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/layer_normalization.https.any.js b/testing/web-platform/tests/webnn/validation_tests/layer_normalization.https.any.js deleted file mode 100644 index 7dbcf5c74a..0000000000 --- a/testing/web-platform/tests/webnn/validation_tests/layer_normalization.https.any.js +++ /dev/null @@ -1,8 +0,0 @@ -// META: title=validation tests for WebNN API -// META: global=window,dedicatedworker -// META: script=../resources/utils_validation.js -// META: timeout=long - -'use strict'; - -validateOptionsAxes('layerNormalization', 4); diff --git a/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js new file mode 100644 index 0000000000..6fc19b1f0d --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/leakyRelu.https.any.js @@ -0,0 +1,7 @@ +// META: title=validation tests for WebNN API leakyRelu operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('leakyRelu'); diff --git a/testing/web-platform/tests/webnn/validation_tests/linear.https.any.js b/testing/web-platform/tests/webnn/validation_tests/linear.https.any.js new file mode 100644 index 0000000000..99c1daad3f --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/linear.https.any.js @@ -0,0 +1,7 @@ +// META: title=validation tests for WebNN API linear operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('linear'); diff --git a/testing/web-platform/tests/webnn/validation_tests/lstm.https.any.js b/testing/web-platform/tests/webnn/validation_tests/lstm.https.any.js index efa05090ca..18d609798c 100644 --- a/testing/web-platform/tests/webnn/validation_tests/lstm.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/lstm.https.any.js @@ -1,25 +1,56 @@ // META: title=validation tests for WebNN API lstm operation // META: global=window,dedicatedworker // META: script=../resources/utils_validation.js -// META: timeout=long 'use strict'; const steps = 10, batchSize = 5, inputSize = 3, hiddenSize = 8, numDirections = 1; +// Dimensions required of required inputs. +const kValidInputDimensions = [steps, batchSize, inputSize]; +const kValidWeightDimensions = [numDirections, 4 * hiddenSize, inputSize]; +const kValidRecurrentWeightDimensions = + [numDirections, 4 * hiddenSize, hiddenSize]; +// Dimensions required of optional inputs. +const kValidBiasDimensions = [numDirections, 4 * hiddenSize]; +const kValidPeepholeWeightDimensions = [numDirections, 3 * hiddenSize]; +const kValidInitialHiddenStateDimensions = + [numDirections, batchSize, hiddenSize]; + +// Example descriptors which are valid according to the above dimensions. +const kExampleInputDescriptor = { + dataType: 'float32', + dimensions: kValidInputDimensions +}; +const kExampleWeightDescriptor = { + dataType: 'float32', + dimensions: kValidWeightDimensions +}; +const kExampleRecurrentWeightDescriptor = { + dataType: 'float32', + dimensions: kValidRecurrentWeightDimensions +}; +const kExampleBiasDescriptor = { + dataType: 'float32', + dimensions: kValidBiasDimensions +}; +const kExamplePeepholeWeightDescriptor = { + dataType: 'float32', + dimensions: kValidPeepholeWeightDimensions +}; +const kExampleInitialHiddenStateDescriptor = { + dataType: 'float32', + dimensions: kValidInitialHiddenStateDimensions +}; + const tests = [ { name: '[lstm] Test with default options', - input: {dataType: 'float16', dimensions: [steps, batchSize, inputSize]}, - weight: { - dataType: 'float16', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float16', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + input: {dataType: 'float16', dimensions: kValidInputDimensions}, + weight: {dataType: 'float16', dimensions: kValidWeightDimensions}, + recurrentWeight: + {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions}, steps: steps, hiddenSize: hiddenSize, outputs: [ @@ -29,7 +60,7 @@ const tests = [ }, { name: '[lstm] Test with given options', - input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, + input: kExampleInputDescriptor, weight: { dataType: 'float32', dimensions: [/*numDirections=*/ 2, 4 * hiddenSize, inputSize] @@ -83,73 +114,43 @@ const tests = [ }, { name: '[lstm] DataError is expected if hiddenSize equals to zero', - input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, - weight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, steps: steps, hiddenSize: 0 }, { name: '[lstm] DataError is expected if hiddenSize is too large', - input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, - weight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, steps: steps, hiddenSize: 4294967295, }, { name: '[lstm] DataError is expected if steps equals to zero', - input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, - weight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, steps: 0, hiddenSize: hiddenSize, }, { name: '[lstm] DataError is expected if the data type is not one of the floating point types', - input: {dataType: 'uint32', dimensions: [steps, batchSize, inputSize]}, - weight: { - dataType: 'uint32', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'uint32', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + input: {dataType: 'uint32', dimensions: kValidInputDimensions}, + weight: {dataType: 'uint32', dimensions: kValidWeightDimensions}, + recurrentWeight: + {dataType: 'uint32', dimensions: kValidRecurrentWeightDimensions}, steps: steps, hiddenSize: hiddenSize }, { - name: - '[lstm] DataError is expected if the rank of input is not 3', + name: '[lstm] DataError is expected if the rank of input is not 3', input: {dataType: 'float32', dimensions: [steps, batchSize]}, - weight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, steps: steps, hiddenSize: hiddenSize }, @@ -157,39 +158,27 @@ const tests = [ name: '[lstm] DataError is expected if input.dimensions[0] is not equal to steps', input: {dataType: 'float32', dimensions: [1000, batchSize, inputSize]}, - weight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, steps: steps, hiddenSize: hiddenSize }, { name: '[lstm] DataError is expected if the shape of weight is incorrect', - input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, + input: kExampleInputDescriptor, weight: { dataType: 'float32', dimensions: [numDirections, 4 * hiddenSize, 1000] }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + recurrentWeight: kExampleRecurrentWeightDescriptor, steps: steps, hiddenSize: hiddenSize }, { name: '[lstm] DataError is expected if the rank of recurrentWeight is not 3', - input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, - weight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, recurrentWeight: {dataType: 'float32', dimensions: [numDirections, 4 * hiddenSize]}, steps: steps, @@ -198,31 +187,19 @@ const tests = [ { name: '[lstm] DataError is expected if the size of options.activations is not 3', - input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, - weight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, steps: steps, hiddenSize: hiddenSize, options: {activations: ['sigmoid', 'tanh']} }, { - name: - '[lstm] DataError is expected if the rank of options.bias is not 2', - input: {dataType: 'float16', dimensions: [steps, batchSize, inputSize]}, - weight: { - dataType: 'float16', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float16', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + name: '[lstm] DataError is expected if the rank of options.bias is not 2', + input: {dataType: 'float16', dimensions: kValidInputDimensions}, + weight: {dataType: 'float16', dimensions: kValidWeightDimensions}, + recurrentWeight: + {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions}, steps: steps, hiddenSize: hiddenSize, options: {bias: {dataType: 'float16', dimensions: [numDirections]}} @@ -230,15 +207,10 @@ const tests = [ { name: '[lstm] DataError is expected if the shape of options.recurrentBias.dimensions is incorrect', - input: {dataType: 'float16', dimensions: [steps, batchSize, inputSize]}, - weight: { - dataType: 'float16', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float16', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + input: {dataType: 'float16', dimensions: kValidInputDimensions}, + weight: {dataType: 'float16', dimensions: kValidWeightDimensions}, + recurrentWeight: + {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions}, steps: steps, hiddenSize: hiddenSize, options: { @@ -248,15 +220,10 @@ const tests = [ { name: '[lstm] DataError is expected if the dataType of options.peepholeWeight is incorrect', - input: {dataType: 'float16', dimensions: [steps, batchSize, inputSize]}, - weight: { - dataType: 'float16', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float16', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + input: {dataType: 'float16', dimensions: kValidInputDimensions}, + weight: {dataType: 'float16', dimensions: kValidWeightDimensions}, + recurrentWeight: + {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions}, steps: steps, hiddenSize: hiddenSize, options: { @@ -267,15 +234,10 @@ const tests = [ { name: '[lstm] DataError is expected if the dataType of options.initialHiddenState is incorrect', - input: {dataType: 'float16', dimensions: [steps, batchSize, inputSize]}, - weight: { - dataType: 'float16', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float16', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + input: {dataType: 'float16', dimensions: kValidInputDimensions}, + weight: {dataType: 'float16', dimensions: kValidWeightDimensions}, + recurrentWeight: + {dataType: 'float16', dimensions: kValidRecurrentWeightDimensions}, steps: steps, hiddenSize: hiddenSize, options: { @@ -288,15 +250,9 @@ const tests = [ { name: '[lstm] DataError is expected if the shape of options.initialCellState is incorrect', - input: {dataType: 'float32', dimensions: [steps, batchSize, inputSize]}, - weight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, inputSize] - }, - recurrentWeight: { - dataType: 'float32', - dimensions: [numDirections, 4 * hiddenSize, hiddenSize] - }, + input: kExampleInputDescriptor, + weight: kExampleWeightDescriptor, + recurrentWeight: kExampleRecurrentWeightDescriptor, steps: steps, hiddenSize: hiddenSize, options: { @@ -384,3 +340,132 @@ tests.forEach( options)); } }, test.name)); + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + + assert_throws_js( + TypeError, + () => builder.lstm( + inputFromOtherBuilder, weight, recurrentWeight, steps, hiddenSize)); +}, '[lstm] throw if input is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const input = builder.input('input', kExampleInputDescriptor); + const weightFromOtherBuilder = + otherBuilder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + + assert_throws_js( + TypeError, + () => builder.lstm( + input, weightFromOtherBuilder, recurrentWeight, steps, hiddenSize)); +}, '[lstm] throw if weight is from another builder'); + + +multi_builder_test(async (t, builder, otherBuilder) => { + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeightFromOtherBuilder = + otherBuilder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + + assert_throws_js( + TypeError, + () => builder.lstm( + input, weight, recurrentWeightFromOtherBuilder, steps, hiddenSize)); +}, '[lstm] throw if recurrentWeight is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const biasFromOtherBuilder = + otherBuilder.input('bias', kExampleBiasDescriptor); + const options = {bias: biasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.lstm( + input, weight, recurrentWeight, steps, hiddenSize, options)); +}, '[lstm] throw if bias option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const recurrentBiasFromOtherBuilder = + otherBuilder.input('bias', kExampleBiasDescriptor); + const options = {recurrentBias: recurrentBiasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.lstm( + input, weight, recurrentWeight, steps, hiddenSize, options)); +}, '[lstm] throw if recurrentBias option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const peepholeWeightFromOtherBuilder = + otherBuilder.input('peepholeWeight', kExamplePeepholeWeightDescriptor); + const options = {peepholeWeight: peepholeWeightFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.lstm( + input, weight, recurrentWeight, steps, hiddenSize, options)); +}, '[lstm] throw if peepholeWeight option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const initialHiddenStateFromOtherBuilder = otherBuilder.input( + 'initialHiddenState', kExampleInitialHiddenStateDescriptor); + const options = {initialHiddenState: initialHiddenStateFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.lstm( + input, weight, recurrentWeight, steps, hiddenSize, options)); +}, '[lstm] throw if initialHiddenState option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const initialCellStateFromOtherBuilder = otherBuilder.input( + 'initialCellState', kExampleInitialHiddenStateDescriptor); + const options = {initialCellState: initialCellStateFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.lstm( + input, weight, recurrentWeight, steps, hiddenSize, options)); +}, '[lstm] throw if initialCellState option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const activation = builder.clamp(); + const activationFromOtherBuilder = otherBuilder.clamp(); + const options = {activations: [activation, activationFromOtherBuilder]}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + assert_throws_js( + TypeError, + () => builder.lstm( + input, weight, recurrentWeight, steps, hiddenSize, options)); +}, '[lstm] throw if any activation option is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/lstmCell.https.any.js b/testing/web-platform/tests/webnn/validation_tests/lstmCell.https.any.js new file mode 100644 index 0000000000..c3769c828d --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/lstmCell.https.any.js @@ -0,0 +1,600 @@ +// META: title=validation tests for WebNN API lstmCell operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const batchSize = 3, inputSize = 4, hiddenSize = 5; + +// Dimensions required of required inputs. +const kValidInputDimensions = [batchSize, inputSize]; +const kValidWeightDimensions = [4 * hiddenSize, inputSize]; +const kValidRecurrentWeightDimensions = [4 * hiddenSize, hiddenSize]; +const kValidHiddenStateDimensions = [batchSize, hiddenSize]; +const kValidCellStateDimensions = [batchSize, hiddenSize]; +// Dimensions required of optional inputs. +const kValidBiasDimensions = [4 * hiddenSize]; +const kValidPeepholeWeightDimensions = [3 * hiddenSize]; + +// Example descriptors which are valid according to the above dimensions. +const kExampleInputDescriptor = { + dataType: 'float32', + dimensions: kValidInputDimensions +}; +const kExampleWeightDescriptor = { + dataType: 'float32', + dimensions: kValidWeightDimensions +}; +const kExampleRecurrentWeightDescriptor = { + dataType: 'float32', + dimensions: kValidRecurrentWeightDimensions +}; +const kExampleHiddenStateDescriptor = { + dataType: 'float32', + dimensions: kValidHiddenStateDimensions +}; +const kExampleCellStateDescriptor = { + dataType: 'float32', + dimensions: kValidCellStateDimensions +}; +const kExampleBiasDescriptor = { + dataType: 'float32', + dimensions: kValidBiasDimensions +}; +const kExamplePeepholeWeightDescriptor = { + dataType: 'float32', + dimensions: kValidPeepholeWeightDimensions +}; + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', kExampleInputDescriptor); + + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + const cellState = builder.input('cellState', kExampleCellStateDescriptor); + assert_throws_js( + TypeError, + () => builder.lstmCell( + inputFromOtherBuilder, weight, recurrentWeight, hiddenState, + cellState, hiddenSize)); +}, '[lstmCell] throw if input is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const weightFromOtherBuilder = + otherBuilder.input('weight', kExampleWeightDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + const cellState = builder.input('cellState', kExampleCellStateDescriptor); + assert_throws_js( + TypeError, + () => builder.lstmCell( + input, weightFromOtherBuilder, recurrentWeight, hiddenState, + cellState, hiddenSize)); +}, '[lstmCell] throw if weight is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const recurrentWeightFromOtherBuilder = + otherBuilder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + const cellState = builder.input('cellState', kExampleCellStateDescriptor); + assert_throws_js( + TypeError, + () => builder.lstmCell( + input, weight, recurrentWeightFromOtherBuilder, hiddenState, + cellState, hiddenSize)); +}, '[lstmCell] throw if recurrentWeight is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const hiddenStateFromOtherBuilder = + otherBuilder.input('hiddenState', kExampleHiddenStateDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const cellState = builder.input('cellState', kExampleCellStateDescriptor); + assert_throws_js( + TypeError, + () => builder.lstmCell( + input, weight, recurrentWeight, hiddenStateFromOtherBuilder, + cellState, hiddenSize)); +}, '[lstmCell] throw if hiddenState is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const cellStateFromOtherBuilder = + otherBuilder.input('cellState', kExampleCellStateDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + assert_throws_js( + TypeError, + () => builder.lstmCell( + input, weight, recurrentWeight, hiddenState, + cellStateFromOtherBuilder, hiddenSize)); +}, '[lstmCell] throw if cellState is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const biasFromOtherBuilder = + otherBuilder.input('bias', kExampleBiasDescriptor); + const options = {bias: biasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + const cellState = builder.input('cellState', kExampleCellStateDescriptor); + assert_throws_js( + TypeError, + () => builder.lstmCell( + input, weight, recurrentWeight, hiddenState, cellState, hiddenSize, + options)); +}, '[lstmCell] throw if bias option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const recurrentBiasFromOtherBuilder = + otherBuilder.input('bias', kExampleBiasDescriptor); + const options = {recurrentBias: recurrentBiasFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + const cellState = builder.input('cellState', kExampleCellStateDescriptor); + assert_throws_js( + TypeError, + () => builder.lstmCell( + input, weight, recurrentWeight, hiddenState, cellState, hiddenSize, + options)); +}, '[lstmCell] throw if recurrentBias option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const peepholeWeightFromOtherBuilder = + otherBuilder.input('peepholeWeight', kExamplePeepholeWeightDescriptor); + const options = {peepholeWeight: peepholeWeightFromOtherBuilder}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + const cellState = builder.input('cellState', kExampleCellStateDescriptor); + assert_throws_js( + TypeError, + () => builder.lstmCell( + input, weight, recurrentWeight, hiddenState, cellState, hiddenSize, + options)); +}, '[lstmCell] throw if peepholeWeight option is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const activation = builder.clamp(); + const activationFromOtherBuilder = otherBuilder.clamp(); + const options = {activations: [activation, activationFromOtherBuilder]}; + + const input = builder.input('input', kExampleInputDescriptor); + const weight = builder.input('weight', kExampleWeightDescriptor); + const recurrentWeight = + builder.input('recurrentWeight', kExampleRecurrentWeightDescriptor); + const hiddenState = + builder.input('hiddenState', kExampleHiddenStateDescriptor); + const cellState = builder.input('cellState', kExampleCellStateDescriptor); + assert_throws_js( + TypeError, + () => builder.lstmCell( + input, weight, recurrentWeight, hiddenState, cellState, hiddenSize, + options)); +}, '[lstmCell] throw if activation option is from another builder'); + +const tests = [ + { + name: '[lstmCell] Test with default options', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize, + outputs: [ + {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + {dataType: 'float16', dimensions: [batchSize, hiddenSize]} + ] + }, + { + name: '[lstmCell] Test with given options', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize, + options: { + bias: {dataType: 'float32', dimensions: [4 * hiddenSize]}, + recurrentBias: {dataType: 'float32', dimensions: [4 * hiddenSize]}, + peepholeWeight: {dataType: 'float32', dimensions: [3 * hiddenSize]}, + layout: 'ifgo', + activations: ['sigmoid', 'relu', 'tanh'] + }, + outputs: [ + {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + {dataType: 'float32', dimensions: [batchSize, hiddenSize]} + ] + }, + { + name: '[lstmCell] Throw if hiddenSize is equal to zero', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: 0 + }, + { + name: '[lstmCell] Throw if hiddenSize is too large', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: 4294967295 + }, + { + name: + '[lstmCell] Throw if the input data type is not one of the floating point types', + input: {dataType: 'uint32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the rank of input is not 2', + input: {dataType: 'float32', dimensions: [batchSize]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the shape of input is incorrect', + input: {dataType: 'float32', dimensions: [batchSize, 1000]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the data type of weight is incorrect', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the rank of weight is not 2', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: + {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize, 1000]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the shape of weight is incorrect', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float32', dimensions: [1000, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the data type of recurrentWeight is incorrect', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the rank of recurrentWeight is not 2', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [1000, 4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the shape of recurrentWeight is incorrect', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: {dataType: 'float32', dimensions: [1000, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the data type of hiddenState is incorrect', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'int64', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the rank of hiddenState is not 2', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the shape of hiddenState is incorrect', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, 1000]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the data type of cellState is incorrect', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the rank of cellState is not 2', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the shape of cellState is incorrect', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float16', dimensions: [batchSize, 1000]}, + hiddenSize: hiddenSize + }, + { + name: '[lstmCell] Throw if the data type of options.bias is incorrect', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize, + options: {bias: {dataType: 'int8', dimensions: [4 * hiddenSize]}} + }, + { + name: '[lstmCell] Throw if the rank of options.bias is not 1', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize, + options: {bias: {dataType: 'float16', dimensions: [4 * hiddenSize, 1000]}} + }, + { + name: '[lstmCell] Throw if the shape of options.bias is incorrect', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize, + options: {bias: {dataType: 'float16', dimensions: [1000]}} + }, + { + name: + '[lstmCell] Throw if the data type of options.recurrentBias is incorrect', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize, + options: {recurrentBias: {dataType: 'uint8', dimensions: [4 * hiddenSize]}} + }, + { + name: '[lstmCell] Throw if the rank of options.recurrentBias is not 1', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize, + options: { + recurrentBias: {dataType: 'float16', dimensions: [4 * hiddenSize, 1000]} + } + }, + { + name: '[lstmCell] Throw if the shape of options.recurrentBias is incorrect', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize, + options: {recurrentBias: {dataType: 'float16', dimensions: [1000]}} + }, + { + name: + '[lstmCell] Throw if the data type of options.peepholeWeight is incorrect', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize, + options: + {peepholeWeight: {dataType: 'float32', dimensions: [3 * hiddenSize]}} + }, + { + name: '[lstmCell] Throw if the rank of options.peepholeWeight is not 1', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize, + options: {peepholeWeight: {dataType: 'float16', dimensions: []}} + }, + { + name: + '[lstmCell] Throw if the shape of options.peepholeWeight is incorrect', + input: {dataType: 'float16', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float16', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float16', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float16', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize, + options: {peepholeWeight: {dataType: 'float16', dimensions: [1000]}} + }, + { + name: '[lstmCell] Throw if the size of options.activations is not 3', + input: {dataType: 'float32', dimensions: [batchSize, inputSize]}, + weight: {dataType: 'float32', dimensions: [4 * hiddenSize, inputSize]}, + recurrentWeight: + {dataType: 'float32', dimensions: [4 * hiddenSize, hiddenSize]}, + hiddenState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + cellState: {dataType: 'float32', dimensions: [batchSize, hiddenSize]}, + hiddenSize: hiddenSize, + options: {activations: ['sigmoid', 'tanh', 'sigmoid', 'tanh']} + } +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + const weight = builder.input( + 'weight', + {dataType: test.weight.dataType, dimensions: test.weight.dimensions}); + const recurrentWeight = builder.input('recurrentWeight', { + dataType: test.recurrentWeight.dataType, + dimensions: test.recurrentWeight.dimensions + }); + const hiddenState = builder.input('hiddenState', { + dataType: test.hiddenState.dataType, + dimensions: test.hiddenState.dimensions + }); + const cellState = builder.input('cellState', { + dataType: test.cellState.dataType, + dimensions: test.cellState.dimensions + }); + + const options = {}; + if (test.options) { + if (test.options.bias) { + options.bias = builder.input('bias', { + dataType: test.options.bias.dataType, + dimensions: test.options.bias.dimensions + }); + } + if (test.options.recurrentBias) { + options.bias = builder.input('recurrentBias', { + dataType: test.options.recurrentBias.dataType, + dimensions: test.options.recurrentBias.dimensions + }); + } + if (test.options.peepholeWeight) { + options.peepholeWeight = builder.input('peepholeWeight', { + dataType: test.options.peepholeWeight.dataType, + dimensions: test.options.peepholeWeight.dimensions + }); + } + if (test.options.layout) { + options.layout = test.options.layout; + } + if (test.options.activations) { + options.activations = []; + test.options.activations.forEach( + activation => options.activations.push(builder[activation]())); + } + } + + if (test.outputs) { + const outputs = builder.lstmCell( + input, weight, recurrentWeight, hiddenState, cellState, + test.hiddenSize, options); + assert_equals(outputs.length, test.outputs.length); + for (let i = 0; i < outputs.length; ++i) { + assert_equals(outputs[i].dataType(), test.outputs[i].dataType); + assert_array_equals(outputs[i].shape(), test.outputs[i].dimensions); + } + } else { + assert_throws_js( + TypeError, + () => builder.lstmCell( + input, weight, recurrentWeight, hiddenState, cellState, + test.hiddenSize, options)); + } + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js b/testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js new file mode 100644 index 0000000000..03616ddb01 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/matmul.https.any.js @@ -0,0 +1,7 @@ +// META: title=validation tests for WebNN API matmul operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateTwoInputsFromMultipleBuilders('matmul'); diff --git a/testing/web-platform/tests/webnn/validation_tests/pad.https.any.js b/testing/web-platform/tests/webnn/validation_tests/pad.https.any.js new file mode 100644 index 0000000000..11c6a8f7ef --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/pad.https.any.js @@ -0,0 +1,17 @@ +// META: title=validation tests for WebNN API pad operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', {dataType: 'float32', dimensions: [2, 2]}); + + const beginningPadding = [1, 1]; + const endingPadding = [1, 1]; + assert_throws_js( + TypeError, + () => + builder.pad(inputFromOtherBuilder, beginningPadding, endingPadding)); +}, '[pad] throw if input is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/pooling.https.any.js b/testing/web-platform/tests/webnn/validation_tests/pooling.https.any.js new file mode 100644 index 0000000000..e8add0511f --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/pooling.https.any.js @@ -0,0 +1,275 @@ +// META: title=validation tests for WebNN API pooling operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const kPoolingOperators = ['averagePool2d', 'l2Pool2d', 'maxPool2d']; + +kPoolingOperators.forEach((operatorName) => { + validateInputFromAnotherBuilder( + operatorName, {dataType: 'float32', dimensions: [2, 2, 2, 2]}); +}); + + +const tests = [ + { + name: 'Test pool2d with default options.', + input: {dataType: 'float32', dimensions: [1, 3, 4, 4]}, + output: {dataType: 'float32', dimensions: [1, 3, 1, 1]} + }, + { + name: 'Test pool2d with windowDimensions', + input: {dataType: 'float32', dimensions: [1, 3, 4, 4]}, + options: { + windowDimensions: [3, 3], + }, + output: {dataType: 'float32', dimensions: [1, 3, 2, 2]} + }, + { + name: 'Test pool2d with padding.', + input: {dataType: 'float32', dimensions: [1, 3, 5, 5]}, + options: { + windowDimensions: [5, 5], + padding: [2, 2, 2, 2], + }, + output: {dataType: 'float32', dimensions: [1, 3, 5, 5]} + }, + { + name: 'Test pool2d with strides.', + input: {dataType: 'float32', dimensions: [1, 3, 5, 5]}, + options: { + windowDimensions: [2, 2], + strides: [2, 2], + }, + output: {dataType: 'float32', dimensions: [1, 3, 2, 2]} + }, + { + name: 'Test pool2d with strides and padding.', + input: {dataType: 'float32', dimensions: [1, 3, 5, 5]}, + options: { + windowDimensions: [3, 3], + padding: [1, 1, 1, 1], + strides: [2, 2], + }, + output: {dataType: 'float32', dimensions: [1, 3, 3, 3]} + }, + { + name: 'Test pool2d with strides and asymmetric padding.', + input: {dataType: 'float32', dimensions: [1, 3, 7, 7]}, + options: { + windowDimensions: [4, 4], + padding: [2, 1, 2, 1], + strides: [2, 2], + }, + output: {dataType: 'float32', dimensions: [1, 3, 4, 4]} + }, + { + name: 'Test pool2d with strides, padding and roundingType="floor".', + input: {dataType: 'float32', dimensions: [1, 3, 7, 7]}, + options: { + windowDimensions: [4, 4], + padding: [1, 1, 1, 1], + strides: [2, 2], + roundingType: 'floor', + }, + output: {dataType: 'float32', dimensions: [1, 3, 3, 3]} + }, + { + name: 'Test pool2d with strides, padding and roundingType="ceil".', + input: {dataType: 'float32', dimensions: [1, 3, 7, 7]}, + options: { + windowDimensions: [4, 4], + padding: [1, 1, 1, 1], + strides: [2, 2], + roundingType: 'ceil', + }, + output: {dataType: 'float32', dimensions: [1, 3, 4, 4]} + }, + { + name: 'Test pool2d with explicit outputSizes ignored roundingType', + input: {dataType: 'float32', dimensions: [1, 3, 7, 7]}, + options: { + windowDimensions: [4, 4], + padding: [1, 1, 1, 1], + strides: [2, 2], + roundingType: 'ceil', + outputSizes: [3, 3], + }, + output: {dataType: 'float32', dimensions: [1, 3, 3, 3]} + }, + { + name: 'Test pool2d with strides, padding and outputSizes=[3, 3].', + input: {dataType: 'float32', dimensions: [1, 3, 7, 7]}, + options: { + windowDimensions: [4, 4], + padding: [1, 1, 1, 1], + strides: [2, 2], + outputSizes: [3, 3], + }, + output: {dataType: 'float32', dimensions: [1, 3, 3, 3]} + }, + { + name: 'Test pool2d with strides, padding and outputSizes=[4, 4].', + input: {dataType: 'float32', dimensions: [1, 3, 7, 7]}, + options: { + windowDimensions: [4, 4], + padding: [1, 1, 1, 1], + strides: [2, 2], + outputSizes: [4, 4], + }, + output: {dataType: 'float32', dimensions: [1, 3, 4, 4]} + }, + { + name: 'Test pool2d with layout="nchw".', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + windowDimensions: [3, 3], + layout: 'nchw', + }, + output: {dataType: 'float32', dimensions: [1, 2, 3, 3]} + }, + { + name: 'Test pool2d with layout="nhwc".', + input: {dataType: 'float32', dimensions: [1, 5, 5, 2]}, + options: { + windowDimensions: [3, 3], + layout: 'nhwc', + }, + output: {dataType: 'float32', dimensions: [1, 3, 3, 2]} + }, + { + name: 'Throw if the input is not a 4-D tensor.', + input: {dataType: 'float32', dimensions: [1, 5, 5]}, + }, + { + name: 'Throw if the output sizes is incorrect.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + windowDimensions: [2, 2], + padding: [2, 2, 2, 2], + strides: [2, 2], + outputSizes: [3, 3], + }, + }, + { + name: 'Throw if the length of output sizes is not 2.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + windowDimensions: [2, 2], + padding: [2, 2, 2, 2], + strides: [2, 2], + outputSizes: [1, 2, 4, 4], + }, + }, + { + name: 'Throw if the length of window dimensions is not 2.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + windowDimensions: [1, 1, 1, 1], + }, + }, + { + name: 'Throw if any window dimension is lesser than 1.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + windowDimensions: [0, 2], + }, + }, + { + name: + 'Throw if the input height is too small to fill the pool window height.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + windowDimensions: [8, 2], + }, + }, + { + name: + 'Throw if the input width is too small to fill the pool window width.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + windowDimensions: [2, 8], + }, + }, + { + name: 'Throw if the calculated output height is equal to 0.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + windowDimensions: [6, 3], + }, + }, + { + name: 'Throw if the calculated output width is equal to 0.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + windowDimensions: [3, 6], + }, + }, + { + name: 'Throw if the length of padding is not 4.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + padding: [2, 2], + }, + }, + { + name: 'Throw if the length of strides is not 2.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + strides: [2], + }, + }, + { + name: 'Throw if one stride value is smaller than 1.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + strides: [0, 2], + }, + }, + { + name: 'Throw if the length of dilations is not 2.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + dilations: [1, 1, 2], + }, + }, + { + name: 'Throw if one dilation value is smaller than 1.', + input: {dataType: 'float32', dimensions: [1, 2, 5, 5]}, + options: { + dilations: [1, 0], + }, + }, + { + name: 'Throw if the padding height value is too large', + input: {dataType: 'float32', dimensions: [1, 3, 5, 5]}, + options: { + padding: [kMaxUnsignedLong, kMaxUnsignedLong, 0, 0], + }, + }, + { + name: 'Throw if the padding width value is too large', + input: {dataType: 'float32', dimensions: [1, 3, 5, 5]}, + options: { + padding: [0, 0, kMaxUnsignedLong, kMaxUnsignedLong], + }, + }, +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + kPoolingOperators.forEach((operatorName) => { + if (test.output) { + const output = builder[operatorName](input, test.options); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, () => builder[operatorName](input, test.options)); + } + }); + }, test.name)); diff --git a/testing/web-platform/tests/webnn/validation_tests/prelu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/prelu.https.any.js new file mode 100644 index 0000000000..865f9f684c --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/prelu.https.any.js @@ -0,0 +1,7 @@ +// META: title=validation tests for WebNN API prelu operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateTwoInputsFromMultipleBuilders('prelu'); diff --git a/testing/web-platform/tests/webnn/validation_tests/reduction.https.any.js b/testing/web-platform/tests/webnn/validation_tests/reduction.https.any.js index 65b71239b9..60f0978678 100644 --- a/testing/web-platform/tests/webnn/validation_tests/reduction.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/reduction.https.any.js @@ -1,11 +1,10 @@ -// META: title=validation tests for WebNN API reduction operation +// META: title=validation tests for WebNN API reduction operation // META: global=window,dedicatedworker // META: script=../resources/utils_validation.js -// META: timeout=long 'use strict'; -[ +const kReductionOperators = [ 'reduceL1', 'reduceL2', 'reduceLogSum', @@ -16,6 +15,9 @@ 'reduceProduct', 'reduceSum', 'reduceSumSquare', -].forEach((operationName) => { - validateOptionsAxes(operationName); +]; + +kReductionOperators.forEach((operatorName) => { + validateOptionsAxes(operatorName); + validateInputFromAnotherBuilder(operatorName); }); diff --git a/testing/web-platform/tests/webnn/validation_tests/relu.https.any.js b/testing/web-platform/tests/webnn/validation_tests/relu.https.any.js new file mode 100644 index 0000000000..237c1c3eda --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/relu.https.any.js @@ -0,0 +1,10 @@ +// META: title=validation tests for WebNN API relu operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('relu'); + +validateUnaryOperation( + 'relu', allWebNNOperandDataTypes, /*alsoBuildActivation=*/ true); diff --git a/testing/web-platform/tests/webnn/validation_tests/resample2d.https.any.js b/testing/web-platform/tests/webnn/validation_tests/resample2d.https.any.js index 2e00cf297c..de44c6a333 100644 --- a/testing/web-platform/tests/webnn/validation_tests/resample2d.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/resample2d.https.any.js @@ -1,8 +1,158 @@ // META: title=validation tests for WebNN API resample2d operation // META: global=window,dedicatedworker // META: script=../resources/utils_validation.js -// META: timeout=long 'use strict'; -validateOptionsAxes('resample2d', 4); +// Tests for resample2d(input, options) +const tests = [ + { + name: '[resample2d] Test building resample2d with default options', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + output: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + }, + { + name: '[resample2d] Test building resample2d with scales=[2.0, 2.0]', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {scales: [2.0, 2.0]}, + output: {dataType: 'float32', dimensions: [1, 1, 4, 8]}, + }, + { + name: '[resample2d] Test building resample2d with scales=[0.5, 0.5]', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + options: {scales: [0.5, 0.5]}, + output: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + }, + { + name: + '[resample2d] Test building resample2d with scales=[0.5, 0.5] and explicit axes=[2, 3]', + input: {dataType: 'float32', dimensions: [1, 1, 5, 5]}, + options: {scales: [0.5, 0.5], axes: [2, 3]}, + output: {dataType: 'float32', dimensions: [1, 1, 2, 2]}, + }, + { + name: + '[resample2d] Test building resample2d with scales=[1.0, 2.0] and axes=[0, 1]', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {scales: [1.0, 2.0], axes: [0, 1]}, + output: {dataType: 'float32', dimensions: [1, 2, 2, 4]}, + }, + { + name: + '[resample2d] Test building resample2d with scales=[2.0, 2.0] and axes=[1, 2]', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {scales: [2.0, 2.0], axes: [1, 2]}, + output: {dataType: 'float32', dimensions: [1, 2, 4, 4]}, + }, + { + name: + '[resample2d] Test building resample2d with sizes=[3, 6] ignored scales', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {scales: [2.0, 2.0], sizes: [3, 6]}, + output: {dataType: 'float32', dimensions: [1, 1, 3, 6]}, + }, + { + name: '[resample2d] Throw if the rank of input is not 4', + input: {dataType: 'float32', dimensions: [2, 4]}, + }, + { + name: '[resample2d] Throw if the length of scales is not 2', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {scales: [1.0, 1.0, 2.0, 2.0]}, + }, + { + name: '[resample2d] Throw if any scale value is negative', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {scales: [1.0, -2.0]}, + }, + { + name: '[resample2d] Throw if any scale value is 0', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {scales: [0, 2.0]}, + }, + { + name: '[resample2d] Throw if the length of sizes is not 2', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {sizes: [1, 1, 4, 6]}, + }, + { + name: + '[resample2d] Throw if any size value is out of \'unsigned long\' value range', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {sizes: [kMaxUnsignedLong + 1, kMaxUnsignedLong + 1]}, + }, + { + name: + '[resample2d] Throw if outputHeight being floor(scaleHeight*inputHeight) is too large', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + // The maximum dimension size is kMaxUnsignedLong (2 ** 32 - 1). + // Here scaleHeight=kMaxUnsignedLong and inputHeight=2, + // so outputHeight being kMaxUnsignedLong*2 > kMaxUnsignedLong . + options: {scales: /*[scaleHeight, scaleWidth]*/[kMaxUnsignedLong, 1]}, + }, + { + name: '[resample2d] Throw if scaleHeight is too small', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + // Here scaleHeight=0.02 and inputHeight=2, + // so outputHeight would be 0. + // Link to https://github.com/webmachinelearning/webnn/issues/391. + options: {scales: /*[scaleHeight, scaleWidth]*/[0.02, 0.8]}, + }, + { + name: + '[resample2d] Throw if outputWidth being floor(scaleWidth*inputWidth) is too large', + input: {dataType: 'float32', dimensions: [1, 1, 4, 2]}, + // The maximum dimension size is kMaxUnsignedLong (2 ** 32 - 1). + // Here scaleWidth=kMaxUnsignedLong and inputWidth=2, + // so outputWidth being kMaxUnsignedLong*2 > kMaxUnsignedLong . + options: {scales: /*[scaleHeight, scaleWidth]*/[1, kMaxUnsignedLong]}, + }, + { + name: '[resample2d] Throw if scaleWidth is too small', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + // Here scaleWidth=0.1 and inputWidth=4, + // so outputWidth would be 0. + // Link to https://github.com/webmachinelearning/webnn/issues/391. + options: {scales: /*[scaleHeight, scaleWidth]*/[0.7, 0.1]}, + }, + { + name: '[resample2d] Throw if the length of axes is not 2', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {axes: [0, 1, 2]}, + }, + { + name: + '[resample2d] Throw if any axis value is greater than or equal to the input rank', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {axes: [3, 4]}, + }, + { + // The valid values in the axes sequence are [0, 1], [1, 2] or [2, 3] + name: '[resample2d] Throw if the values of axes are inconsecutive', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {axes: [0, 2]}, + }, + { + name: '[resample2d] Throw if the values of axes are same', + input: {dataType: 'float32', dimensions: [1, 1, 2, 4]}, + options: {axes: [0, 0]}, + }, +]; + +tests.forEach( + test => promise_test(async t => { + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + const options = test.options ?? {}; + if (test.output) { + const output = builder.resample2d(input, options); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js(TypeError, () => builder.resample2d(input, options)); + } + }, test.name)); + +validateInputFromAnotherBuilder( + 'resample2d', {dataType: 'float32', dimensions: [2, 2, 2, 2]}); diff --git a/testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js b/testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js new file mode 100644 index 0000000000..435551b716 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/reshape.https.any.js @@ -0,0 +1,14 @@ +// META: title=validation tests for WebNN API reshape operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', {dataType: 'float32', dimensions: [1, 2, 3]}); + + const newShape = [3, 2, 1]; + assert_throws_js( + TypeError, () => builder.reshape(inputFromOtherBuilder, newShape)); +}, '[reshape] throw if input is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/sigmoid.https.any.js b/testing/web-platform/tests/webnn/validation_tests/sigmoid.https.any.js new file mode 100644 index 0000000000..b40ddc3fd4 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/sigmoid.https.any.js @@ -0,0 +1,10 @@ +// META: title=validation tests for WebNN API sigmoid operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('sigmoid'); + +validateUnaryOperation( + 'sigmoid', floatingPointTypes, /*alsoBuildActivation=*/ true); diff --git a/testing/web-platform/tests/webnn/validation_tests/slice.https.any.js b/testing/web-platform/tests/webnn/validation_tests/slice.https.any.js new file mode 100644 index 0000000000..a45ecd3fcb --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/slice.https.any.js @@ -0,0 +1,15 @@ +// META: title=validation tests for WebNN API slice operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', {dataType: 'float32', dimensions: [2, 2]}); + + const starts = [1, 1]; + const sizes = [1, 1]; + assert_throws_js( + TypeError, () => builder.slice(inputFromOtherBuilder, starts, sizes)); +}, '[slice] throw if input is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/softmax.https.any.js b/testing/web-platform/tests/webnn/validation_tests/softmax.https.any.js new file mode 100644 index 0000000000..68891b27d8 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/softmax.https.any.js @@ -0,0 +1,7 @@ +// META: title=validation tests for WebNN API softmax operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('softmax'); diff --git a/testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js b/testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js new file mode 100644 index 0000000000..347dfcd938 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/softplus.https.any.js @@ -0,0 +1,7 @@ +// META: title=validation tests for WebNN API softplus operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('softplus'); diff --git a/testing/web-platform/tests/webnn/validation_tests/softsign.https.any.js b/testing/web-platform/tests/webnn/validation_tests/softsign.https.any.js new file mode 100644 index 0000000000..58ec487159 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/softsign.https.any.js @@ -0,0 +1,10 @@ +// META: title=validation tests for WebNN API softsign operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('softsign'); + +validateUnaryOperation( + 'softsign', floatingPointTypes, /*alsoBuildActivation=*/ true); diff --git a/testing/web-platform/tests/webnn/validation_tests/split.https.any.js b/testing/web-platform/tests/webnn/validation_tests/split.https.any.js new file mode 100644 index 0000000000..38f3126603 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/split.https.any.js @@ -0,0 +1,14 @@ +// META: title=validation tests for WebNN API split operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', {dataType: 'float32', dimensions: [4, 4]}); + + const splits = 2; + assert_throws_js( + TypeError, () => builder.split(inputFromOtherBuilder, splits)); +}, '[split] throw if input is from another builder'); diff --git a/testing/web-platform/tests/webnn/validation_tests/tanh.https.any.js b/testing/web-platform/tests/webnn/validation_tests/tanh.https.any.js new file mode 100644 index 0000000000..4f9de919f6 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/tanh.https.any.js @@ -0,0 +1,10 @@ +// META: title=validation tests for WebNN API tanh operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('tanh'); + +validateUnaryOperation( + 'tanh', floatingPointTypes, /*alsoBuildActivation=*/ true); diff --git a/testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js b/testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js new file mode 100644 index 0000000000..9ea5a5dcf8 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/transpose.https.any.js @@ -0,0 +1,7 @@ +// META: title=validation tests for WebNN API transpose operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +validateInputFromAnotherBuilder('transpose'); diff --git a/testing/web-platform/tests/webnn/validation_tests/triangular.https.any.js b/testing/web-platform/tests/webnn/validation_tests/triangular.https.any.js index 4e4c368f82..ee8958659c 100644 --- a/testing/web-platform/tests/webnn/validation_tests/triangular.https.any.js +++ b/testing/web-platform/tests/webnn/validation_tests/triangular.https.any.js @@ -1,7 +1,6 @@ // META: title=validation tests for WebNN API triangular operation // META: global=window,dedicatedworker // META: script=../resources/utils_validation.js -// META: timeout=long 'use strict'; @@ -14,3 +13,5 @@ promise_test(async t => { } } }, "[triangular] DataError is expected if input's rank is less than 2"); + +validateInputFromAnotherBuilder('triangular'); diff --git a/testing/web-platform/tests/webnn/validation_tests/where.https.any.js b/testing/web-platform/tests/webnn/validation_tests/where.https.any.js new file mode 100644 index 0000000000..a26fa24931 --- /dev/null +++ b/testing/web-platform/tests/webnn/validation_tests/where.https.any.js @@ -0,0 +1,129 @@ +// META: title=validation tests for WebNN API where operation +// META: global=window,dedicatedworker +// META: script=../resources/utils_validation.js + +'use strict'; + +const kExampleConditionDescriptor = { + dataType: 'uint8', + dimensions: [2, 4] +}; +const kExampleInputDescriptor = { + dataType: 'float32', + dimensions: [2, 4] +}; + +const tests = [ + { + name: + '[where] Throw if the condition data type is not uint8.', + condition: {dataType: 'float32', dimensions: [2, 4]}, + input: {dataType: 'float32', dimensions: [2, 4]}, + other: {dataType: 'float32', dimensions: [2, 4]}, + }, + { + name: + '[where] Throw if the data types of input and other do not match', + condition: {dataType: 'uint8', dimensions: [2, 4]}, + input: {dataType: 'float16', dimensions: [2, 4]}, + other: {dataType: 'float32', dimensions: [2, 4]}, + }, + { + name: + '[where] Throw if the shapes of input and other are not broadcastable', + condition: {dataType: 'uint8', dimensions: [2, 4]}, + input: {dataType: 'float32', dimensions: [2, 4]}, + other: {dataType: 'float32', dimensions: [2, 3]}, + }, + { + name: + '[where] Throw if the condition shape is not broadcastable', + condition: {dataType: 'uint8', dimensions: [2, 4]}, + input: {dataType: 'float32', dimensions: [2, 3]}, + other: {dataType: 'float32', dimensions: [2, 1]}, + }, + { + name: + '[where] Test building where with 2-D condition, 2-D input and 2-D other using broadcast', + condition: {dataType: 'uint8', dimensions: [2, 1]}, + input: {dataType: 'float32', dimensions: [2, 4]}, + other: {dataType: 'float32', dimensions: [2, 4]}, + output: {dataType: 'float32', dimensions: [2, 4]}, + }, + { + name: + '[where] Test building where with 2-D condition, 2-D input and 3-D other using broadcast', + condition: {dataType: 'uint8', dimensions: [1, 4]}, + input: {dataType: 'float32', dimensions: [3, 4]}, + other: {dataType: 'float32', dimensions: [2, 3, 4]}, + output: {dataType: 'float32', dimensions: [2, 3, 4]}, + }, + { + name: + '[where] Test building where with 3-D condition, 3-D input and 2-D other using broadcast', + condition: {dataType: 'uint8', dimensions: [2, 1, 4]}, + input: {dataType: 'float32', dimensions: [2, 3, 4]}, + other: {dataType: 'float32', dimensions: [1, 4]}, + output: {dataType: 'float32', dimensions: [2, 3, 4]}, + }, + { + name: + '[where] Test building where with 4-D condition, 3-D input and 2-D other using broadcast', + condition: {dataType: 'uint8', dimensions: [2, 3, 4, 5]}, + input: {dataType: 'float32', dimensions: [3, 4, 5]}, + other: {dataType: 'float32', dimensions: [4, 5]}, + output: {dataType: 'float32', dimensions: [2, 3, 4, 5]}, + } +]; + +tests.forEach( + test => promise_test(async t => { + const condition = builder.input('condition', { + dataType: test.condition.dataType, + dimensions: test.condition.dimensions + }); + const input = builder.input( + 'input', + {dataType: test.input.dataType, dimensions: test.input.dimensions}); + const other = builder.input( + 'other', + {dataType: test.other.dataType, dimensions: test.other.dimensions}); + if (test.output) { + const output = builder.where(condition, input, other); + assert_equals(output.dataType(), test.output.dataType); + assert_array_equals(output.shape(), test.output.dimensions); + } else { + assert_throws_js( + TypeError, () => builder.where(condition, input, other)); + } + }, test.name)); + +multi_builder_test(async (t, builder, otherBuilder) => { + const conditionFromOtherBuilder = + otherBuilder.input('condition', kExampleConditionDescriptor); + + const input = builder.input('input', kExampleInputDescriptor); + const other = builder.input('other', kExampleInputDescriptor); + assert_throws_js( + TypeError, () => builder.where(conditionFromOtherBuilder, input, other)); +}, '[where] throw if condition is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const inputFromOtherBuilder = + otherBuilder.input('input', kExampleInputDescriptor); + + const condition = builder.input('condition', kExampleConditionDescriptor); + const other = builder.input('other', kExampleInputDescriptor); + assert_throws_js( + TypeError, () => builder.where(condition, inputFromOtherBuilder, other)); +}, '[where] throw if input is from another builder'); + +multi_builder_test(async (t, builder, otherBuilder) => { + const otherFromOtherBuilder = + otherBuilder.input('other', kExampleInputDescriptor); + + const condition = builder.input('condition', kExampleConditionDescriptor); + const input = builder.input('input', kExampleInputDescriptor); + assert_throws_js( + TypeError, () => builder.where(condition, input, otherFromOtherBuilder)); +}, '[where] throw if other is from another builder'); diff --git a/testing/web-platform/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk-worker.js b/testing/web-platform/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk-worker.js new file mode 100644 index 0000000000..cc9dee42ac --- /dev/null +++ b/testing/web-platform/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk-worker.js @@ -0,0 +1,13 @@ +onrtctransform = async (event) => { + const { port } = event.transformer.options; + port.postMessage("started"); + + const reader = event.transformer.readable.getReader(); + const writer = event.transformer.writable.getWriter(); + + const { done, value } = await reader.read(); + + writer.write(null).catch(err => port.postMessage([err.name, 'null'])); + writer.write(value).catch(err => port.postMessage([err.name, value.constructor.name])); +}; +self.postMessage('registered'); diff --git a/testing/web-platform/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk.https.html b/testing/web-platform/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk.https.html new file mode 100644 index 0000000000..a837f627f2 --- /dev/null +++ b/testing/web-platform/tests/webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk.https.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src ="routines.js"></script> + +<video id="video1" autoplay></video> + +<script> +promise_test(async (test) => { + const {sender, receiver, senderPc, receiverPc} = await createConnectionWithTransform(test, 'RTCRtpScriptTransform-bad-chunk-worker.js', {audio: true}); + + assert_array_equals(await getNextMessage(sender.transform.port), ["TypeError", "null"]); + assert_array_equals(await getNextMessage(sender.transform.port), ["TypeError", "RTCEncodedAudioFrame"]); +}, "Writing bad chunks should error the stream"); +</script> diff --git a/testing/web-platform/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-metadata.https.html b/testing/web-platform/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-metadata.https.html new file mode 100644 index 0000000000..1e420e6f72 --- /dev/null +++ b/testing/web-platform/tests/webrtc-encoded-transform/tentative/RTCEncodedAudioFrame-metadata.https.html @@ -0,0 +1,129 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>RTCEncodedAudioFrame can be cloned and distributed</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src=/resources/testdriver.js></script> +<script src=/resources/testdriver-vendor.js></script> +<script src='../../mediacapture-streams/permission-helper.js'></script> +<script src="../../webrtc/RTCPeerConnection-helper.js"></script> +<script src="../../service-workers/service-worker/resources/test-helpers.sub.js"></script> + +<script> +"use strict"; +promise_test(async t => { + const caller1 = new RTCPeerConnection(); + t.add_cleanup(() => caller1.close()); + const callee1 = new RTCPeerConnection({encodedInsertableStreams:true}); + t.add_cleanup(() => callee1.close()); + await setMediaPermission("granted", ["microphone"]); + const inputStream = await navigator.mediaDevices.getUserMedia({audio:true}); + const inputTrack = inputStream.getAudioTracks()[0]; + t.add_cleanup(() => inputTrack.stop()); + caller1.addTrack(inputTrack) + exchangeIceCandidates(caller1, callee1); + + const caller2 = new RTCPeerConnection({encodedInsertableStreams:true}); + t.add_cleanup(() => caller2.close()); + const sender2 = caller2.addTransceiver("audio").sender; + const writer2 = sender2.createEncodedStreams().writable.getWriter(); + sender2.replaceTrack(new MediaStreamTrackGenerator({ kind: 'audio' })); + + const framesReceivedCorrectly = new Promise((resolve, reject) => { + callee1.ontrack = async e => { + const receiverStreams = e.receiver.createEncodedStreams(); + const receiverReader = receiverStreams.readable.getReader(); + const result = await receiverReader.read(); + const original = result.value; + let newFrame = new RTCEncodedAudioFrame(original); + assert_equals(original.getMetadata().rtpTimestamp, newFrame.getMetadata().rtpTimestamp); + assert_equals(original.getMetadata().absCaptureTime, newFrame.getMetadata().absCaptureTime); + assert_array_equals(Array.from(original.data), Array.from(newFrame.data)); + await writer2.write(newFrame); + resolve(); + } + }); + + await exchangeOfferAnswer(caller1, callee1); + + return framesReceivedCorrectly; +}, "Constructing audio frame before sending works"); + +promise_test(async t => { + const caller1 = new RTCPeerConnection(); + + t.add_cleanup(() => caller1.close()); + const callee1 = new RTCPeerConnection({encodedInsertableStreams:true}); + t.add_cleanup(() => callee1.close()); + await setMediaPermission("granted", ["microphone"]); + const inputStream = await navigator.mediaDevices.getUserMedia({audio:true}); + const inputTrack = inputStream.getAudioTracks()[0]; + t.add_cleanup(() => inputTrack.stop()); + caller1.addTrack(inputTrack) + exchangeIceCandidates(caller1, callee1); + + const caller2 = new RTCPeerConnection({encodedInsertableStreams:true}); + t.add_cleanup(() => caller2.close()); + const sender2 = caller2.addTransceiver("audio").sender; + const writer2 = sender2.createEncodedStreams().writable.getWriter(); + sender2.replaceTrack(new MediaStreamTrackGenerator({ kind: 'audio' })); + + const framesReceivedCorrectly = new Promise((resolve, reject) => { + callee1.ontrack = async e => { + const receiverStreams = e.receiver.createEncodedStreams(); + const receiverReader = receiverStreams.readable.getReader(); + const result = await receiverReader.read(); + const original = result.value; + let newMetadata = original.getMetadata(); + newMetadata.rtpTimestamp = newMetadata.rtpTimestamp + 1; + let newFrame = new RTCEncodedAudioFrame(original, newMetadata); + assert_not_equals(original.getMetadata().rtpTimestamp, newFrame.getMetadata().rtpTimestamp); + assert_equals(newMetadata.rtpTimestamp, newFrame.getMetadata().rtpTimestamp); + assert_equals(original.getMetadata().absCaptureTime, newFrame.getMetadata().absCaptureTime); + assert_array_equals(Array.from(original.data), Array.from(newFrame.data)); + await writer2.write(newFrame); + resolve(); + } + }); + + await exchangeOfferAnswer(caller1, callee1); + + return framesReceivedCorrectly; +}, "Constructing audio frame with metadata argument before sending works"); + +promise_test(async t => { + const caller1 = new RTCPeerConnection(); + t.add_cleanup(() => caller1.close()); + const callee1 = new RTCPeerConnection({encodedInsertableStreams:true}); + t.add_cleanup(() => callee1.close()); + await setMediaPermission("granted", ["microphone"]); + const inputStream = await navigator.mediaDevices.getUserMedia({audio:true}); + const inputTrack = inputStream.getAudioTracks()[0]; + t.add_cleanup(() => inputTrack.stop()); + caller1.addTrack(inputTrack) + exchangeIceCandidates(caller1, callee1); + + const caller2 = new RTCPeerConnection({encodedInsertableStreams:true}); + t.add_cleanup(() => caller2.close()); + const sender2 = caller2.addTransceiver("audio").sender; + const writer2 = sender2.createEncodedStreams().writable.getWriter(); + sender2.replaceTrack(new MediaStreamTrackGenerator({ kind: 'audio' })); + + const framesReceivedCorrectly = new Promise((resolve, reject) => { + callee1.ontrack = async e => { + const receiverStreams = e.receiver.createEncodedStreams(); + const receiverReader = receiverStreams.readable.getReader(); + const result = await receiverReader.read(); + const original = result.value; + let newMetadata = original.getMetadata(); + newMetadata.synchronizationSource = newMetadata.synchronizationSource + 1; + assert_throws_dom("InvalidModificationError", () => new RTCEncodedAudioFrame(original, newMetadata)); + resolve(); + } + }); + + await exchangeOfferAnswer(caller1, callee1); + + return framesReceivedCorrectly; +}, "Constructing audio frame with bad metadata argument before sending does not work"); +</script> diff --git a/testing/web-platform/tests/webrtc/RTCDataChannel-send-close.html b/testing/web-platform/tests/webrtc/RTCDataChannel-send-close.html new file mode 100644 index 0000000000..1bcc96790d --- /dev/null +++ b/testing/web-platform/tests/webrtc/RTCDataChannel-send-close.html @@ -0,0 +1,123 @@ +<!doctype html> +<meta charset=utf-8> +<meta name="timeout" content="long"> +<title>RTCDataChannel.prototype.send</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="RTCPeerConnection-helper.js"></script> +<script> +'use strict'; + +const largeString = " ".repeat(64 * 1024); +const largeArrayBuffer = new TextEncoder("utf-8").encode(largeString); +const largeBlob = new Blob([" ".repeat(64 * 1024)]); + +for (const options of [{}, {negotiated: true, id: 0}]) { + const mode = `${options.negotiated? "Negotiated d" : "D"}atachannel`; + + promise_test(async t => { + let [channel1, channel2] = await createDataChannelPair(t, options); + let receivedSize = 0, sentSize = 0; + + channel2.binaryType = 'arraybuffer'; + channel2.onmessage = e => { + if (typeof e.data === 'string') + receivedSize += e.data.length; + else + receivedSize += e.data.byteLength; + } + + let closePromiseResolve; + let closePromise = new Promise((resolve, reject) => { + closePromiseResolve = resolve; + }); + channel2.onclose = e => { + closePromiseResolve(); + } + + try { + while(true) { + channel1.send(largeString); + sentSize += largeString.length; + } + } catch(error) { + assert_true(error instanceof DOMException); + assert_equals(error.name, 'OperationError'); + } + channel1.close(); + + await closePromise; + assert_equals(receivedSize, sentSize, 'All the pending sent messages are received after calling close()'); + }, `${mode} should be able to send and receive all string messages on close`); + + promise_test(async t => { + let [channel1, channel2] = await createDataChannelPair(t, options); + let receivedSize = 0, sentSize = 0; + + channel2.binaryType = 'arraybuffer'; + channel2.onmessage = e => { + if (typeof e.data === 'string') + receivedSize += e.data.length; + else + receivedSize += e.data.byteLength; + } + + let closePromiseResolve; + let closePromise = new Promise((resolve, reject) => { + closePromiseResolve = resolve; + }); + channel2.onclose = e => { + closePromiseResolve(); + } + + try { + while(true) { + channel1.send(largeArrayBuffer); + sentSize += largeArrayBuffer.length; + } + } catch(error) { + assert_true(error instanceof DOMException); + assert_equals(error.name, 'OperationError'); + } + channel1.close(); + + await closePromise; + assert_equals(receivedSize, sentSize, 'All the pending sent messages are received after calling close()'); + }, `${mode} should be able to send and receive all arraybuffer messages on close`); + + promise_test(async t => { + let [channel1, channel2] = await createDataChannelPair(t, options); + let receivedSize = 0, sentSize = 0; + + channel2.binaryType = 'arraybuffer'; + channel2.onmessage = e => { + if (typeof e.data === 'string') + receivedSize += e.data.length; + else + receivedSize += e.data.byteLength; + } + + let closePromiseResolve; + let closePromise = new Promise((resolve, reject) => { + closePromiseResolve = resolve; + }); + channel2.onclose = e => { + closePromiseResolve(); + } + + try { + while(true) { + channel1.send(largeBlob); + sentSize += largeBlob.size; + } + } catch(error) { + assert_true(error instanceof DOMException); + assert_equals(error.name, 'OperationError'); + } + channel1.close(); + + await closePromise; + assert_equals(receivedSize, sentSize, 'All the pending sent messages are received after calling close()'); + }, `${mode} should be able to send and receive all blob messages on close`); +} +</script> diff --git a/testing/web-platform/tests/webrtc/RTCIceCandidate-constructor.html b/testing/web-platform/tests/webrtc/RTCIceCandidate-constructor.html index 66d6962079..b760c7b05a 100644 --- a/testing/web-platform/tests/webrtc/RTCIceCandidate-constructor.html +++ b/testing/web-platform/tests/webrtc/RTCIceCandidate-constructor.html @@ -57,7 +57,7 @@ }, `new RTCIceCandidate({ candidate: '' })`); test(t => { - // Throws because the candidate field is not nullable + // Throws because both sdpMid and sdpMLineIndex are null by default assert_throws_js(TypeError, () => new RTCIceCandidate({ candidate: null @@ -116,15 +116,15 @@ test(t => { const candidate = new RTCIceCandidate({ - candidate: '', + candidate: null, sdpMLineIndex: 0 }); - assert_equals(candidate.candidate, '', 'candidate'); + assert_equals(candidate.candidate, 'null', 'candidate'); assert_equals(candidate.sdpMid, null, 'sdpMid', 'sdpMid'); assert_equals(candidate.sdpMLineIndex, 0, 'sdpMLineIndex'); assert_equals(candidate.usernameFragment, null, 'usernameFragment'); - }, `new RTCIceCandidate({ candidate: '', sdpMLineIndex: 0 }`); + }, `new RTCIceCandidate({ candidate: null, sdpMLineIndex: 0 }`); test(t => { const candidate = new RTCIceCandidate({ @@ -138,7 +138,7 @@ assert_equals(candidate.usernameFragment, null, 'usernameFragment'); }, 'new RTCIceCandidate({ ... }) with valid candidate string and sdpMid'); - test(t =>{ + test(t => { // candidate string is not validated in RTCIceCandidate const candidate = new RTCIceCandidate({ candidate: arbitraryString, diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-GC.https.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-GC.https.html index 156a2e1f09..282c362a2d 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-GC.https.html +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-GC.https.html @@ -85,6 +85,36 @@ promise_test(async t => { await onVideoChange(); assert_not_equals(color, getVideoSignal(destVideo)); }, "GC does not collect a peer connection pipe rendering to a video element"); + +promise_test(async t => { + const pc1 = new RTCPeerConnection(); + t.add_cleanup(() => pc1.close()); + const pc2 = new RTCPeerConnection(); + t.add_cleanup(() => pc2.close()); + + const [track, stream] = await createTrackAndStreamWithCleanup(t, "video"); + pc1.addTrack(track, stream); + exchangeIceCandidates(pc1, pc2); + + const metadataToBeLoaded = []; + pc2.ontrack = (e) => { + const stream = e.streams[0]; + const v = document.createElement('video'); + v.autoplay = true; + v.srcObject = stream; + v.id = stream.id + metadataToBeLoaded.push(new Promise((resolve) => { + v.addEventListener('loadedmetadata', () => { + resolve(); + }); + })); + }; + await exchangeOfferAnswer(pc1, pc2); + + garbageCollect(); + + await Promise.all(metadataToBeLoaded); +}, "GC does not collect an HTMLMediaElement playing a video track"); </script> </body> </html> diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-addTcpIceCandidate.html b/testing/web-platform/tests/webrtc/RTCPeerConnection-addTcpIceCandidate.html new file mode 100644 index 0000000000..1e7fc3ba95 --- /dev/null +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-addTcpIceCandidate.html @@ -0,0 +1,95 @@ +<!doctype html> +<title>Test RTCPeerConnection.prototype.addIceCandidate with TCP candidates</title> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="RTCPeerConnection-helper.js"></script> +<script> +'use strict'; + +const sdp = `v=0 +o=- 166855176514521964 2 IN IP4 127.0.0.1 +s=- +t=0 0 +a=msid-semantic:WMS * +m=audio 9 UDP/TLS/RTP/SAVPF 111 +c=IN IP4 0.0.0.0 +a=rtcp:9 IN IP4 0.0.0.0 +a=ice-ufrag:655Y +a=ice-pwd:somelongpwdwithenoughrandomness +a=fingerprint:sha-256 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00 +a=setup:actpass +a=mid:audio1 +a=sendonly +a=rtcp-mux +a=rtcp-rsize +a=rtpmap:111 opus/48000/2 +a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level +a=ssrc:1001 cname:some +`; + +const candidate_8001 = 'a=candidate:2983561038 1 tcp 1518214911 127.0.0.1 8001 typ host tcptype passive generation 0 ufrag 655Y network-id 1 network-cost 10'; + +const candidate_2049 = 'a=candidate:2983561038 1 tcp 1518214911 127.0.0.1 2049 typ host tcptype passive generation 0 ufrag 655Y network-id 1 network-cost 10'; + +const candidate_37 = 'a=candidate:2983561038 1 tcp 1518214911 127.0.0.1 37 typ host tcptype passive generation 0 ufrag 655Y network-id 1 network-cost 10'; + +promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + await pc.setRemoteDescription({type: 'offer', sdp: sdp + candidate_8001 + '\n'}) + const answer = await pc.createAnswer(); + await pc.setLocalDescription(answer); + await waitForConnectionStateChangeWithTimeout(t, pc, + ['failed', 'disconnected'], 1000); +}, 'TCP candidate aimed at port 8001 accepted'); + +promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + await pc.setRemoteDescription({type: 'offer', sdp: sdp + candidate_2049 + '\n'}) + const answer = await pc.createAnswer(); + await pc.setLocalDescription(answer); + pc.onicestatechange = t.unreached_func(); + await new Promise(resolve => t.step_timeout(resolve, 500)); +}, 'TCP candidate aimed at port 2049 ignored'); + +promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + await pc.setRemoteDescription({type: 'offer', sdp: sdp + candidate_37 + '\n'}) + const answer = await pc.createAnswer(); + await pc.setLocalDescription(answer); + pc.onicestatechange = t.unreached_func(); + await new Promise(resolve => t.step_timeout(resolve, 500)); +}, 'TCP candidate aimed at port 37 ignored'); + +promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + await pc.setRemoteDescription({type: 'offer', sdp: sdp}) + const answer = await pc.createAnswer(); + await pc.setLocalDescription(answer); + await pc.addIceCandidate(new RTCIceCandidate({ + candidate: candidate_8001, + sdpMid: 'audio1' + })); + await waitForConnectionStateChangeWithTimeout( + t, pc, ['failed', 'disconnected'], 1000); +}, 'TCP addIceCandidate aimed at port 8001 accepted'); + +promise_test(async t => { + const pc = new RTCPeerConnection(); + t.add_cleanup(() => pc.close()); + await pc.setRemoteDescription({type: 'offer', sdp: sdp}) + const answer = await pc.createAnswer(); + await pc.setLocalDescription(answer); + await pc.addIceCandidate(new RTCIceCandidate({ + candidate: candidate_2049, + sdpMid: 'audio1' + })); + pc.onicestatechange = t.unreached_func(); + await new Promise(resolve => t.step_timeout(resolve, 500)); +}, 'TCP addIceCandidate aimed at port 2049 ignored'); + + +</script> diff --git a/testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js b/testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js index 6f35fde76c..cd27b033d3 100644 --- a/testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js +++ b/testing/web-platform/tests/webrtc/RTCPeerConnection-helper.js @@ -230,6 +230,20 @@ async function waitForConnectionStateChange(pc, wantedStates) { } } +function waitForConnectionStateChangeWithTimeout(t, pc, wantedStates, timeout) { + return new Promise((resolve, reject) => { + if (wantedStates.includes(pc.connectionState)) { + resolve(); + return; + } + pc.addEventListener('connectionstatechange', () => { + if (wantedStates.includes(pc.connectionState)) + resolve(); + }); + t.step_timeout(reject, timeout); + }); +} + async function waitForIceGatheringState(pc, wantedStates) { while (!wantedStates.includes(pc.iceGatheringState)) { await waitUntilEvent(pc, 'icegatheringstatechange'); diff --git a/testing/web-platform/tests/webrtc/back-forward-cache-with-open-webrtc-connection.https.window.js b/testing/web-platform/tests/webrtc/back-forward-cache-with-open-webrtc-connection.https.window.js index 5cc3b745b3..fe41a9cfd5 100644 --- a/testing/web-platform/tests/webrtc/back-forward-cache-with-open-webrtc-connection.https.window.js +++ b/testing/web-platform/tests/webrtc/back-forward-cache-with-open-webrtc-connection.https.window.js @@ -16,5 +16,5 @@ promise_test(async t => { await openWebRTC(rc1); // The page should not be eligible for BFCache because of open WebRTC connection and live MediaStreamTrack. await assertBFCacheEligibility(rc1, /*shouldRestoreFromBFCache=*/ false); - await assertNotRestoredFromBFCache(rc1, ['webrtc', 'media-stream']); + await assertNotRestoredFromBFCache(rc1, ['rtc', 'mediastream']); }); diff --git a/testing/web-platform/tests/websockets/Create-blocked-port.any.js b/testing/web-platform/tests/websockets/Create-blocked-port.any.js index 2962312ff5..b8e3e26445 100644 --- a/testing/web-platform/tests/websockets/Create-blocked-port.any.js +++ b/testing/web-platform/tests/websockets/Create-blocked-port.any.js @@ -79,6 +79,7 @@ async_test(t => { 2049, // nfs 3659, // apple-sasl 4045, // lockd + 4190, // sieve 6000, // x11 6566, // sane-port 6665, // irc (alternate) @@ -86,6 +87,7 @@ async_test(t => { 6667, // irc (default) 6668, // irc (alternate) 6669, // irc (alternate) + 6679, // osaut 6697, // irc+tls 10080, // amanda ].forEach(blockedPort => { diff --git a/testing/web-platform/tests/webtransport/datagram-bad-chunk.https.any.js b/testing/web-platform/tests/webtransport/datagram-bad-chunk.https.any.js new file mode 100644 index 0000000000..92601dec9a --- /dev/null +++ b/testing/web-platform/tests/webtransport/datagram-bad-chunk.https.any.js @@ -0,0 +1,15 @@ +// META: global=window,worker +// META: script=/common/get-host-info.sub.js +// META: script=resources/webtransport-test-helpers.sub.js +// META: script=/common/utils.js + +promise_test(async t => { + // Establish a WebTransport session. + const wt = new WebTransport(webtransport_url('echo.py')); + t.add_cleanup(() => wt.close()); + await wt.ready; + + const writer = wt.datagrams.writable.getWriter(); + await promise_rejects_js(t, TypeError, writer.write("foo")); + await promise_rejects_js(t, TypeError, writer.write(new Uint8Array(0))); +}, 'Datagram should reject when non-buffer-source data is written'); diff --git a/testing/web-platform/tests/webtransport/sendstream-bad-chunk.https.any.js b/testing/web-platform/tests/webtransport/sendstream-bad-chunk.https.any.js new file mode 100644 index 0000000000..013390879b --- /dev/null +++ b/testing/web-platform/tests/webtransport/sendstream-bad-chunk.https.any.js @@ -0,0 +1,18 @@ +// META: global=window,worker +// META: script=/common/get-host-info.sub.js +// META: script=resources/webtransport-test-helpers.sub.js +// META: script=/common/utils.js + +promise_test(async t => { + // Establish a WebTransport session. + const wt = new WebTransport(webtransport_url('echo.py')); + t.add_cleanup(() => wt.close()); + await wt.ready; + + // Create a bidirectional stream + const {writable} = await wt.createBidirectionalStream(); + + const writer = writable.getWriter(); + await promise_rejects_js(t, TypeError, writer.write("foo")); + await promise_rejects_js(t, TypeError, writer.write(new Uint8Array(0))); +}, 'WebTransportSendStream should reject when non-buffer-source data is written'); |