mirror of
https://github.com/servo/servo
synced 2026-04-25 17:15:48 +02:00
Update web-platform-tests to revision b'0132c620b681ab085bffb45ad74040e61090e1ba'
Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com>
This commit is contained in:
3177
tests/wpt/meta/MANIFEST.json
vendored
3177
tests/wpt/meta/MANIFEST.json
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,3 +0,0 @@
|
||||
[scope-import-implicit.tentative.html]
|
||||
[Scope-imported rule applies within implicit scope]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[scope-import-inner-scope.tentative.html]
|
||||
[@scope within scope-imported stylesheet matches]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[scope-import-multiple.tentative.html]
|
||||
[A stylesheet may be imported multiple times, and scoped differently]
|
||||
expected: FAIL
|
||||
@@ -1,6 +0,0 @@
|
||||
[scope-import-parent-pseudo.tentative.html]
|
||||
[The & selector matches the scoping root]
|
||||
expected: FAIL
|
||||
|
||||
[The & selector behaves like :scope]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[scope-import-scope-end.tentative.html]
|
||||
[Scope-imported rule applies within scope, above limit]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[scope-import-scope-pseudo.tentative.html]
|
||||
[The :scope pseudo-class works in imported stylesheets]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[scope-import-scope-start.tentative.html]
|
||||
[Scope-imported rule applies within scope]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[grid-lanes-subgrid.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[marker-content-007.tentative.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[marker-content-009.tentative.html]
|
||||
expected: FAIL
|
||||
@@ -1,2 +0,0 @@
|
||||
[marker-content-011.tentative.html]
|
||||
expected: FAIL
|
||||
@@ -1,24 +0,0 @@
|
||||
[marker-attribute.html]
|
||||
[Access to .marker returns an empty DOMTokenList.]
|
||||
expected: FAIL
|
||||
|
||||
[Multiple names give a DOMTokenList with multiple entries.]
|
||||
expected: FAIL
|
||||
|
||||
[DOMTokenList created by access is persisted.]
|
||||
expected: FAIL
|
||||
|
||||
[Changes in DOMTokenList are refected in attribute.]
|
||||
expected: FAIL
|
||||
|
||||
[Access to .marker returns an empty string]
|
||||
expected: FAIL
|
||||
|
||||
[Multiple names stay a single string]
|
||||
expected: FAIL
|
||||
|
||||
[Setting .marker updates the attribute.]
|
||||
expected: FAIL
|
||||
|
||||
[marker retains whitespace]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[template-for-prepend-invalid-ref.html]
|
||||
[Prepending works even when element after the range is removed]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[template-for-replace-invalid-ref.html]
|
||||
[<template for> replace]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[audio-loading-attr-default.tentative.html]
|
||||
[Audio loading attribute default should be eager when not set]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[audio-loading-attr-lazy.tentative.html]
|
||||
[Audio loading attribute should reflect the loading content attribute]
|
||||
expected: FAIL
|
||||
@@ -1,30 +0,0 @@
|
||||
[audio-loading-attr-reflect.tentative.html]
|
||||
[Audio loading attribute default value is 'eager']
|
||||
expected: FAIL
|
||||
|
||||
[Audio loading attribute reflects 'lazy']
|
||||
expected: FAIL
|
||||
|
||||
[Audio loading attribute reflects 'eager']
|
||||
expected: FAIL
|
||||
|
||||
[Audio loading attribute with invalid value reflects as 'eager']
|
||||
expected: FAIL
|
||||
|
||||
[Audio loading attribute with empty string reflects as 'eager']
|
||||
expected: FAIL
|
||||
|
||||
[Audio loading attribute is case-insensitive for 'lazy']
|
||||
expected: FAIL
|
||||
|
||||
[Audio loading attribute is case-insensitive for 'eager']
|
||||
expected: FAIL
|
||||
|
||||
[Setting audio.loading IDL attribute to 'lazy']
|
||||
expected: FAIL
|
||||
|
||||
[Setting audio.loading IDL attribute to 'eager']
|
||||
expected: FAIL
|
||||
|
||||
[Setting audio.loading IDL attribute to invalid value]
|
||||
expected: FAIL
|
||||
@@ -1,4 +0,0 @@
|
||||
[audio-loading-autoplay-deferred.tentative.html]
|
||||
expected: ERROR
|
||||
[Audio with loading=lazy and autoplay that is not visible in viewport does not load audio data or autoplay]
|
||||
expected: FAIL
|
||||
@@ -1,4 +0,0 @@
|
||||
[audio-loading-lazy-autoplay-when-visible.tentative.html]
|
||||
expected: ERROR
|
||||
[Audio with loading=lazy and autoplay does not play while not visible, then plays once scrolled into view]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[audio-loading-lazy-disconnected.tentative.html]
|
||||
[Audio with loading=lazy does not load when not connected to document]
|
||||
expected: FAIL
|
||||
@@ -1,4 +0,0 @@
|
||||
[audio-loading-lazy-in-viewport.tentative.html]
|
||||
expected: ERROR
|
||||
[In-viewport audio with loading='lazy' fires window load before loadstart]
|
||||
expected: TIMEOUT
|
||||
@@ -1,3 +0,0 @@
|
||||
[audio-loading-lazy-not-rendered.tentative.html]
|
||||
[In-viewport audio with loading=lazy and no controls attribute, or display:none or hidden attribute should never load]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[audio-loading-lazy-to-eager.tentative.html]
|
||||
[Below-viewport audio with loading='lazy' loads when changed to loading='eager' or when loading attribute is removed]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[audio-loading-lazy-window-onload.tentative.html]
|
||||
[Audio with loading=lazy does not delay window onload event]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[audio-loading-load-deferred.tentative.html]
|
||||
[Audio with loading=lazy that is not visible in viewport does not load audio data]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[audio-loading-load-preload-auto-deferred.tentative.html]
|
||||
[Audio with loading=lazy and preload=auto that is not visible in viewport does not load audio data]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[audio-loading-load-preload-metadata-deferred.tentative.html]
|
||||
[Audio with loading=lazy and preload=metadata that is not visible in viewport does not load audio data]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[video-loading-attr-lazy.tentative.html]
|
||||
[Video loading attribute should reflect the loading content attribute]
|
||||
expected: FAIL
|
||||
@@ -1,30 +0,0 @@
|
||||
[video-loading-attr-reflect.tentative.html]
|
||||
[Video loading attribute default value is 'eager']
|
||||
expected: FAIL
|
||||
|
||||
[Video loading attribute reflects 'lazy']
|
||||
expected: FAIL
|
||||
|
||||
[Video loading attribute reflects 'eager']
|
||||
expected: FAIL
|
||||
|
||||
[Video loading attribute with invalid value reflects as 'eager']
|
||||
expected: FAIL
|
||||
|
||||
[Video loading attribute with empty string reflects as 'eager']
|
||||
expected: FAIL
|
||||
|
||||
[Video loading attribute is case-insensitive for 'lazy']
|
||||
expected: FAIL
|
||||
|
||||
[Video loading attribute is case-insensitive for 'eager']
|
||||
expected: FAIL
|
||||
|
||||
[Setting video.loading IDL attribute to 'lazy']
|
||||
expected: FAIL
|
||||
|
||||
[Setting video.loading IDL attribute to 'eager']
|
||||
expected: FAIL
|
||||
|
||||
[Setting video.loading IDL attribute to invalid value]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[video-loading-attr-default.tentative.html]
|
||||
[Video loading attribute default should be eager when not set]
|
||||
expected: FAIL
|
||||
@@ -1,4 +0,0 @@
|
||||
[video-loading-autoplay-deferred.tentative.html]
|
||||
expected: ERROR
|
||||
[Video with loading=lazy and autoplay that is not visible in viewport does not load video data or autoplay]
|
||||
expected: FAIL
|
||||
@@ -1,4 +0,0 @@
|
||||
[video-loading-lazy-autoplay-when-visible.tentative.html]
|
||||
expected: ERROR
|
||||
[Video with loading=lazy and autoplay does not play while not visible, then plays once scrolled into view]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[video-loading-lazy-disconnected.tentative.html]
|
||||
[Video with loading=lazy does not load when not connected to document]
|
||||
expected: FAIL
|
||||
@@ -1,4 +0,0 @@
|
||||
[video-loading-lazy-in-viewport.tentative.html]
|
||||
expected: ERROR
|
||||
[In-viewport video with loading='lazy' fires window load before loadstart]
|
||||
expected: TIMEOUT
|
||||
@@ -1,3 +0,0 @@
|
||||
[video-loading-lazy-load-when-visible.tentative.html]
|
||||
[Video with loading=lazy does not fetch video while not visible, then fetches video once scrolled into view]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[video-loading-lazy-not-rendered.tentative.html]
|
||||
[In-viewport video with loading=lazy and no controls attribute, or display:none or hidden attribute should never load]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[video-loading-lazy-poster-when-visible.tentative.html]
|
||||
[Video with loading=lazy does not fetch poster while not visible, then fetches poster once scrolled into view]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[video-loading-lazy-to-eager.tentative.html]
|
||||
[Below-viewport video with loading='lazy' loads when changed to loading='eager' or when loading attribute is removed]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[video-loading-lazy-window-onload.tentative.html]
|
||||
[Video with loading=lazy does not delay window onload event]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[video-loading-load-deferred.tentative.html]
|
||||
[Video with loading=lazy that is not visible in viewport does not load video data]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[video-loading-load-preload-auto-deferred.tentative.html]
|
||||
[Video with loading=lazy and preload=auto that is not visible in viewport does not load video data]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[video-loading-load-preload-metadata-deferred.tentative.html]
|
||||
[Video with loading=lazy and preload=metadata that is not visible in viewport does not load video data]
|
||||
expected: FAIL
|
||||
@@ -1,3 +0,0 @@
|
||||
[video-loading-poster-deferred.tentative.html]
|
||||
[Video with loading=lazy that is not visible in viewport does not load poster image]
|
||||
expected: FAIL
|
||||
3
tests/wpt/tests/CODEOWNERS
vendored
3
tests/wpt/tests/CODEOWNERS
vendored
@@ -1647,11 +1647,14 @@ webtransport/back-forward-cache-with-open-webtransport-connection-ccns.https.ten
|
||||
webtransport/back-forward-cache-with-open-webtransport-connection.https.window.js @web-platform-tests/interop
|
||||
webtransport/close.https.any.js @web-platform-tests/interop
|
||||
webtransport/connect.https.any.js @web-platform-tests/interop
|
||||
webtransport/constructor.https.sub.any.js @web-platform-tests/interop
|
||||
webtransport/csp-fail.https.window.js @web-platform-tests/interop
|
||||
webtransport/csp-pass.https.window.js @web-platform-tests/interop
|
||||
webtransport/datagram-bad-chunk.https.any.js @web-platform-tests/interop
|
||||
webtransport/datagram-cancel-crash.https.sub.window.js @web-platform-tests/interop
|
||||
webtransport/datagrams.https.any.js @web-platform-tests/interop
|
||||
webtransport/echo-large-bidirectional-streams.https.any.js @web-platform-tests/interop
|
||||
webtransport/idlharness.https.sub.any.js @web-platform-tests/interop
|
||||
webtransport/in-removed-iframe.https.html @web-platform-tests/interop
|
||||
webtransport/sendorder.https.any.js @web-platform-tests/interop
|
||||
webtransport/sendstream-bad-chunk.https.any.js @web-platform-tests/interop
|
||||
|
||||
72
tests/wpt/tests/WebCryptoAPI/import_export/raw_format_aliases.tentative.https.any.js
vendored
Normal file
72
tests/wpt/tests/WebCryptoAPI/import_export/raw_format_aliases.tentative.https.any.js
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// META: title=WebCryptoAPI: raw-secret and raw-public importKey() format aliases
|
||||
// META: timeout=long
|
||||
// META: script=../util/helpers.js
|
||||
|
||||
// For all existing symmetric algorithms in WebCrypto, "raw-secret" acts as an
|
||||
// alias of "raw". For all existing asymmetric algorithms in WebCrypto,
|
||||
// "raw-public" acts as an alias of "raw".
|
||||
|
||||
"use strict";
|
||||
|
||||
const rawKeyData16 = crypto.getRandomValues(new Uint8Array(16));
|
||||
const rawKeyData32 = crypto.getRandomValues(new Uint8Array(32));
|
||||
const wrapAlgorithm = { name: "AES-GCM", iv: new Uint8Array(12) };
|
||||
|
||||
const symmetricAlgorithms = [
|
||||
{ algorithm: { name: "AES-CTR", length: 128 }, keyData: rawKeyData16, usages: ["encrypt", "decrypt"] },
|
||||
{ algorithm: { name: "AES-CBC", length: 128 }, keyData: rawKeyData16, usages: ["encrypt", "decrypt"] },
|
||||
{ algorithm: { name: "AES-GCM", length: 128 }, keyData: rawKeyData16, usages: ["encrypt", "decrypt"] },
|
||||
{ algorithm: { name: "AES-KW", length: 128 }, keyData: rawKeyData16, usages: ["wrapKey", "unwrapKey"] },
|
||||
{ algorithm: { name: "HMAC", hash: "SHA-256", length: 256 }, keyData: rawKeyData32, usages: ["sign", "verify"] },
|
||||
{ algorithm: { name: "HKDF" }, keyData: rawKeyData32, usages: ["deriveBits", "deriveKey"], extractable: false },
|
||||
{ algorithm: { name: "PBKDF2" }, keyData: rawKeyData32, usages: ["deriveBits", "deriveKey"], extractable: false },
|
||||
];
|
||||
|
||||
for (const { algorithm, keyData, usages, extractable = true } of symmetricAlgorithms) {
|
||||
promise_test(async () => {
|
||||
const key = await crypto.subtle.importKey("raw-secret", keyData, algorithm, extractable, usages);
|
||||
assert_goodCryptoKey(key, algorithm, extractable, usages, "secret");
|
||||
if (extractable) {
|
||||
await crypto.subtle.exportKey("raw-secret", key);
|
||||
}
|
||||
}, `importKey/exportKey with raw-secret: ${algorithm.name}`);
|
||||
|
||||
if (extractable) {
|
||||
promise_test(async () => {
|
||||
const wrappingKey = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, ["wrapKey", "unwrapKey"]);
|
||||
const key = await crypto.subtle.importKey("raw-secret", keyData, algorithm, true, usages);
|
||||
const wrapped = await crypto.subtle.wrapKey("raw-secret", key, wrappingKey, wrapAlgorithm);
|
||||
const unwrapped = await crypto.subtle.unwrapKey("raw-secret", wrapped, wrappingKey, wrapAlgorithm, algorithm, true, usages);
|
||||
assert_goodCryptoKey(unwrapped, algorithm, true, usages, "secret");
|
||||
}, `wrapKey/unwrapKey with raw-secret: ${algorithm.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
const asymmetricAlgorithms = [
|
||||
{ algorithm: { name: "ECDSA", namedCurve: "P-256" }, usages: ["verify"] },
|
||||
{ algorithm: { name: "ECDH", namedCurve: "P-256" }, usages: [] },
|
||||
{ algorithm: { name: "Ed25519" }, usages: ["verify"] },
|
||||
{ algorithm: { name: "X25519" }, usages: [] },
|
||||
];
|
||||
|
||||
for (const { algorithm, usages } of asymmetricAlgorithms) {
|
||||
const generateKeyUsages = usages.length ? usages.concat("sign") : ["deriveBits"];
|
||||
|
||||
promise_test(async () => {
|
||||
const keyPair = await crypto.subtle.generateKey(algorithm, true, generateKeyUsages);
|
||||
const keyData = await crypto.subtle.exportKey("raw-public", keyPair.publicKey);
|
||||
|
||||
const key = await crypto.subtle.importKey("raw-public", keyData, algorithm, true, usages);
|
||||
assert_goodCryptoKey(key, algorithm, true, usages, "public");
|
||||
await crypto.subtle.exportKey("raw-public", key);
|
||||
}, `importKey/exportKey with raw-public: ${algorithm.name}`);
|
||||
|
||||
promise_test(async () => {
|
||||
const keyPair = await crypto.subtle.generateKey(algorithm, true, generateKeyUsages);
|
||||
|
||||
const wrappingKey = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, false, ["wrapKey", "unwrapKey"]);
|
||||
const wrapped = await crypto.subtle.wrapKey("raw-public", keyPair.publicKey, wrappingKey, wrapAlgorithm);
|
||||
const unwrapped = await crypto.subtle.unwrapKey("raw-public", wrapped, wrappingKey, wrapAlgorithm, algorithm, true, usages);
|
||||
assert_goodCryptoKey(unwrapped, algorithm, true, usages, "public");
|
||||
}, `wrapKey/unwrapKey with raw-public: ${algorithm.name}`);
|
||||
}
|
||||
@@ -23,17 +23,6 @@ promise_test(async () => {
|
||||
assert_equals(session.contextUsage, promptUsage);
|
||||
}, 'Check contextUsage increases from a simple LanguageModel.append() call');
|
||||
|
||||
promise_test(async (t) => {
|
||||
await ensureLanguageModel();
|
||||
const session = await createLanguageModel();
|
||||
assert_equals(session.contextUsage, 0);
|
||||
await session.append([]);
|
||||
assert_equals(session.contextUsage, 0);
|
||||
// Invalid input should be stringified.
|
||||
await session.append({});
|
||||
assert_greater_than(session.contextUsage, 0);
|
||||
}, 'Check empty Object input for LanguageModel.append()');
|
||||
|
||||
promise_test(async t => {
|
||||
await ensureLanguageModel();
|
||||
const session = await createLanguageModel();
|
||||
@@ -42,3 +31,38 @@ promise_test(async t => {
|
||||
await promise_rejects_quotaexceedederror(
|
||||
t, session.append(promptString), usage, session.contextWindow);
|
||||
}, 'Test that append input exceeding the total context window rejects');
|
||||
|
||||
promise_test(async t => {
|
||||
await ensureLanguageModel();
|
||||
const session = await createLanguageModel();
|
||||
const result1 = session.append([
|
||||
{role: 'user', content: 'foo'},
|
||||
{role: 'system', content: 'bar'},
|
||||
]);
|
||||
await promise_rejects_js(t, TypeError, result1);
|
||||
|
||||
const result2 = session.append([
|
||||
{role: 'system', content: 'foo'},
|
||||
{role: 'system', content: 'bar'},
|
||||
]);
|
||||
await promise_rejects_js(t, TypeError, result2);
|
||||
|
||||
const result3 = session.append({role: 'system', content: 'foo'});
|
||||
await promise_rejects_js(
|
||||
t, TypeError, session.append([{role: 'system', content: 'bar'}]));
|
||||
await result3;
|
||||
}, 'append() should reject system role messages after other messages');
|
||||
|
||||
promise_test(async (t) => {
|
||||
await ensureLanguageModel();
|
||||
const model = await createLanguageModel();
|
||||
|
||||
// null, undefined, and objects are coerced to strings.
|
||||
await model.append(null);
|
||||
await model.append(undefined);
|
||||
await model.append({});
|
||||
await model.append('');
|
||||
await model.append([]);
|
||||
await model.append([{ role: 'user', content: [] }]);
|
||||
await model.append([{role: 'user', content: [{type: 'text', value: ''}]}]);
|
||||
}, 'LanguageModel.append() allows empty and coerced inputs');
|
||||
|
||||
@@ -70,13 +70,19 @@ promise_test(async t => {
|
||||
}, 'Create with initialPrompts without system role');
|
||||
|
||||
promise_test(async t => {
|
||||
let result = createLanguageModel({
|
||||
let result1 = createLanguageModel({
|
||||
initialPrompts: [
|
||||
{role: 'user', content: 'hello'}, {role: 'assistant', content: 'hello'},
|
||||
{role: 'system', content: 'you are a robot'}
|
||||
]
|
||||
});
|
||||
await promise_rejects_js(t, TypeError, result);
|
||||
await promise_rejects_js(t, TypeError, result1);
|
||||
|
||||
let result2 = createLanguageModel({
|
||||
initialPrompts:
|
||||
[{role: 'system', content: 'foo'}, {role: 'system', content: 'bar'}]
|
||||
});
|
||||
await promise_rejects_js(t, TypeError, result2);
|
||||
}, 'Create with system role not ordered first should fail');
|
||||
|
||||
promise_test(async t => {
|
||||
|
||||
@@ -8,14 +8,21 @@
|
||||
|
||||
promise_test(async t => {
|
||||
await ensureLanguageModel();
|
||||
|
||||
// Start a new session.
|
||||
const session = await createLanguageModel();
|
||||
const result = await session.measureContextUsage('This is a prompt.');
|
||||
assert_equals(typeof result, 'number');
|
||||
assert_greater_than(result, 0);
|
||||
}, 'measureContextUsage returns a number greater than zero for text');
|
||||
|
||||
// Test the measureContextUsage() API.
|
||||
let result = await session.measureContextUsage('This is a prompt.');
|
||||
assert_true(
|
||||
typeof result === "number" && result > 0,
|
||||
"The counting result should be a positive number."
|
||||
);
|
||||
});
|
||||
promise_test(async t => {
|
||||
const prompts = [
|
||||
{role: 'system', content: 'foo'},
|
||||
{role: 'user', content: 'bar'},
|
||||
{role: 'assistant', content: 'baz'},
|
||||
];
|
||||
await ensureLanguageModel();
|
||||
const session = await createLanguageModel({initialPrompts: prompts});
|
||||
const result = await session.measureContextUsage(prompts);
|
||||
assert_equals(typeof result, 'number');
|
||||
assert_greater_than(result, 0);
|
||||
}, 'measure message sequences of various roles, even after adding prompts');
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
// META: title=Language Model Prompt Empty Input
|
||||
// META: script=/resources/testdriver.js
|
||||
// META: script=/resources/testdriver-vendor.js
|
||||
// META: script=../resources/util.js
|
||||
// META: timeout=long
|
||||
|
||||
'use strict';
|
||||
|
||||
promise_test(async (t) => {
|
||||
await ensureLanguageModel();
|
||||
const model = await createLanguageModel();
|
||||
|
||||
// null and undefined are coerced to the strings "null" and "undefined" by the
|
||||
// IDL bindings.
|
||||
assert_regexp_match(await model.prompt(null), /null/);
|
||||
assert_regexp_match(await model.prompt(undefined), /undefined/);
|
||||
|
||||
// Empty string is allowed even when context is empty.
|
||||
assert_equals(typeof await model.prompt(""), "string");
|
||||
|
||||
// Empty sequence [] is allowed even when context is empty.
|
||||
assert_equals(typeof await model.prompt([]), "string");
|
||||
|
||||
// Nested empty content sequence is allowed.
|
||||
assert_equals(typeof await model.prompt([{ role: 'user', content: [] }]), "string");
|
||||
|
||||
// Nested structured message with empty text is allowed.
|
||||
assert_equals(typeof await model.prompt([{ role: 'user', content: [{ type: 'text', value: '' }] }]), "string");
|
||||
}, "LanguageModel.prompt() allows empty or coerced inputs");
|
||||
|
||||
promise_test(async (t) => {
|
||||
await ensureLanguageModel();
|
||||
const model = await createLanguageModel();
|
||||
|
||||
assert_equals(await model.append(null), undefined);
|
||||
assert_equals(await model.append(undefined), undefined);
|
||||
assert_equals(await model.append(""), undefined);
|
||||
assert_equals(await model.append([]), undefined);
|
||||
assert_equals(await model.append([{ role: 'user', content: [] }]), undefined);
|
||||
assert_equals(await model.append([{ role: 'user', content: [{ type: 'text', value: '' }] }]), undefined);
|
||||
}, "LanguageModel.append() allows empty or coerced inputs");
|
||||
|
||||
promise_test(async (t) => {
|
||||
await ensureLanguageModel();
|
||||
const model = await createLanguageModel();
|
||||
|
||||
assert_true(model.promptStreaming(null) instanceof ReadableStream);
|
||||
assert_true(model.promptStreaming(undefined) instanceof ReadableStream);
|
||||
assert_true(model.promptStreaming("") instanceof ReadableStream);
|
||||
assert_true(model.promptStreaming([]) instanceof ReadableStream);
|
||||
assert_true(model.promptStreaming([{ role: 'user', content: [] }]) instanceof ReadableStream);
|
||||
assert_true(model.promptStreaming([{ role: 'user', content: [{ type: 'text', value: '' }] }]) instanceof ReadableStream);
|
||||
}, "LanguageModel.promptStreaming() allows empty or coerced inputs");
|
||||
@@ -16,10 +16,7 @@ promise_test(async t => {
|
||||
session.promptStreaming(kTestPrompt);
|
||||
// Run GC.
|
||||
gc();
|
||||
assert_equals(
|
||||
Object.prototype.toString.call(streamingResponse),
|
||||
"[object ReadableStream]"
|
||||
);
|
||||
assert_true(streamingResponse instanceof ReadableStream);
|
||||
let result = "";
|
||||
for await (const value of streamingResponse) {
|
||||
result += value;
|
||||
|
||||
@@ -8,26 +8,23 @@
|
||||
|
||||
promise_test(async t => {
|
||||
await ensureLanguageModel();
|
||||
|
||||
// Start a new session.
|
||||
const session = await createLanguageModel();
|
||||
// Test the streaming prompt API.
|
||||
const streamingResponse =
|
||||
session.promptStreaming(kTestPrompt);
|
||||
assert_equals(
|
||||
Object.prototype.toString.call(streamingResponse),
|
||||
"[object ReadableStream]"
|
||||
);
|
||||
const reader = streamingResponse.getReader();
|
||||
let result = "";
|
||||
while (true) {
|
||||
const { value, done } = await reader.read();
|
||||
if (done) {
|
||||
break;
|
||||
}
|
||||
if (value) {
|
||||
result += value;
|
||||
}
|
||||
}
|
||||
assert_greater_than(result.length, 0, "The result should not be empty.");
|
||||
const streamingResponse = session.promptStreaming(kTestPrompt);
|
||||
assert_true(streamingResponse instanceof ReadableStream);
|
||||
const result = (await Array.fromAsync(streamingResponse)).join('');
|
||||
assert_greater_than(result.length, 0, 'The result should not be empty.');
|
||||
});
|
||||
|
||||
promise_test(async (t) => {
|
||||
await ensureLanguageModel();
|
||||
const model = await createLanguageModel();
|
||||
|
||||
// null, undefined, and objects are coerced to strings.
|
||||
for await (const _ of model.promptStreaming(null)) { }
|
||||
for await (const _ of model.promptStreaming(undefined)) { }
|
||||
for await (const _ of model.promptStreaming({})) { }
|
||||
for await (const _ of model.promptStreaming('')) { }
|
||||
for await (const _ of model.promptStreaming([])) { }
|
||||
for await (const _ of model.promptStreaming([{ role: 'user', content: [] }])) { }
|
||||
for await (const _ of model.promptStreaming([{ role: 'user', content: [{ type: 'text', value: '' }] }])) { }
|
||||
}, 'LanguageModel.promptStreaming() allows empty and coerced inputs');
|
||||
|
||||
@@ -33,6 +33,24 @@ promise_test(async (t) => {
|
||||
assert_regexp_match(await session.prompt({}), /\[object Object\]/);
|
||||
}, 'Check empty Object input');
|
||||
|
||||
promise_test(async (t) => {
|
||||
await ensureLanguageModel();
|
||||
const model = await createLanguageModel();
|
||||
|
||||
// null, undefined, and objects are coerced to strings.
|
||||
assert_regexp_match(await model.prompt(null), /null/);
|
||||
assert_regexp_match(await model.prompt(undefined), /undefined/);
|
||||
assert_equals(typeof await model.prompt({}), 'string');
|
||||
assert_equals(typeof await model.prompt(''), 'string');
|
||||
assert_equals(typeof await model.prompt([]), 'string');
|
||||
assert_equals(
|
||||
typeof await model.prompt([{role: 'user', content: []}]), 'string');
|
||||
assert_equals(
|
||||
typeof await model.prompt(
|
||||
[{role: 'user', content: [{type: 'text', value: ''}]}]),
|
||||
'string');
|
||||
}, 'LanguageModel.prompt() allows empty and coerced inputs');
|
||||
|
||||
promise_test(async (t) => {
|
||||
await ensureLanguageModel();
|
||||
const session = await createLanguageModel();
|
||||
@@ -42,15 +60,15 @@ promise_test(async (t) => {
|
||||
promise_test(async () => {
|
||||
const options = {
|
||||
initialPrompts:
|
||||
[{role: 'system', content: [{type: 'text', value: 'The word of the day is regurgitation.'}]}]
|
||||
[{role: 'system', content: 'The word of the day is regurgitation.'}]
|
||||
};
|
||||
await ensureLanguageModel(options);
|
||||
const session = await LanguageModel.create(options);
|
||||
const usage = await session.measureContextUsage(options.initialPrompts);
|
||||
assert_greater_than(usage, 0);
|
||||
assert_equals(session.contextUsage, usage);
|
||||
assert_regexp_match(await session.prompt('What is the word of the day?'),
|
||||
/regurgitation/i);
|
||||
assert_regexp_match(
|
||||
await session.prompt('What is the word of the day?'), /regurgitation/i);
|
||||
}, 'Test that initialPrompt counts towards session contextUsage');
|
||||
|
||||
promise_test(async () => {
|
||||
@@ -80,3 +98,24 @@ promise_test(async t => {
|
||||
await promise_rejects_quotaexceedederror(
|
||||
t, session.prompt(promptString), usage, session.contextWindow);
|
||||
}, 'Test that prompt input exceeding the total context window rejects');
|
||||
|
||||
promise_test(async t => {
|
||||
await ensureLanguageModel();
|
||||
const session = await createLanguageModel();
|
||||
const result1 = session.prompt([
|
||||
{role: 'user', content: 'foo'},
|
||||
{role: 'system', content: 'bar'},
|
||||
]);
|
||||
await promise_rejects_js(t, TypeError, result1);
|
||||
|
||||
const result2 = session.prompt([
|
||||
{role: 'system', content: 'foo'},
|
||||
{role: 'system', content: 'bar'},
|
||||
]);
|
||||
await promise_rejects_js(t, TypeError, result2);
|
||||
|
||||
const result3 = session.prompt({role: 'system', content: 'foo'});
|
||||
await promise_rejects_js(
|
||||
t, TypeError, session.prompt([{role: 'system', content: 'bar'}]));
|
||||
await result3;
|
||||
}, 'prompt() should reject system role messages after other messages');
|
||||
|
||||
3
tests/wpt/tests/browsing-topics/WEB_FEATURES.yml
vendored
Normal file
3
tests/wpt/tests/browsing-topics/WEB_FEATURES.yml
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
features:
|
||||
- name: topics
|
||||
files: "**"
|
||||
34
tests/wpt/tests/connection-allowlist/tentative/dedicated-worker-redirect-allow.sub.window.js
vendored
Normal file
34
tests/wpt/tests/connection-allowlist/tentative/dedicated-worker-redirect-allow.sub.window.js
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
// META: script=resources/worker_redirect_test.js
|
||||
//
|
||||
// The following tests assume the policy `Connection-Allowlist:
|
||||
// (response-origin);redirects=allow` has been set.
|
||||
|
||||
// 1. Same-origin worker script redirect:
|
||||
// origin: http://{{hosts[][]}} (allowed by allowlist)
|
||||
// target: http://{{hosts[][]}} (also allowed)
|
||||
// This should SUCCEED because redirects are unconditionally enabled via
|
||||
// redirects=allow.
|
||||
worker_script_redirect_test(
|
||||
get_host_info().HTTP_ORIGIN, get_host_info().HTTP_ORIGIN, SUCCESS,
|
||||
'Same-origin dedicated worker main script fetch with redirect succeeds due to redirects=allow.');
|
||||
|
||||
// 2. Same-origin subresource fetch from same-origin worker with same-origin
|
||||
// redirect:
|
||||
// worker: local scheme (data: URL inherits policy)
|
||||
// fetch origin: same-origin
|
||||
// fetch target: same-origin
|
||||
// This should SUCCEED.
|
||||
worker_subresource_redirect_test(
|
||||
get_host_info().HTTP_ORIGIN, get_host_info().HTTP_ORIGIN, SUCCESS,
|
||||
'Same-origin subresource fetch from dedicated worker with same-origin redirect succeeds due to redirects=allow.');
|
||||
|
||||
// 3. Same-origin subresource fetch from same-origin worker with cross-origin
|
||||
// redirect:
|
||||
// worker: local scheme (data: URL inherits policy)
|
||||
// fetch origin: same-origin
|
||||
// fetch target: cross-origin
|
||||
// This should SUCCEED.
|
||||
worker_subresource_redirect_test(
|
||||
get_host_info().HTTP_ORIGIN, get_host_info().HTTP_REMOTE_ORIGIN, SUCCESS,
|
||||
'Same-origin subresource fetch from dedicated worker with cross-origin redirect succeeds due to redirects=allow.');
|
||||
@@ -0,0 +1 @@
|
||||
Connection-Allowlist: (response-origin);redirects=allow
|
||||
33
tests/wpt/tests/connection-allowlist/tentative/dedicated-worker-redirect-block.sub.window.js
vendored
Normal file
33
tests/wpt/tests/connection-allowlist/tentative/dedicated-worker-redirect-block.sub.window.js
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
// META: script=resources/worker_redirect_test.js
|
||||
//
|
||||
// The following tests assume the policy `Connection-Allowlist:
|
||||
// (response-origin);redirects=block` has been set.
|
||||
|
||||
// 1. Same-origin worker script redirect:
|
||||
// origin: http://{{hosts[][]}} (allowed by allowlist)
|
||||
// target: http://{{hosts[][]}} (also allowed)
|
||||
// This should FAIL because redirects are explicitly blocked.
|
||||
worker_script_redirect_test(
|
||||
get_host_info().HTTP_ORIGIN, get_host_info().HTTP_ORIGIN, FAILURE,
|
||||
'Same-origin dedicated worker main script fetch with redirect fails due to redirects=block.');
|
||||
|
||||
// 2. Same-origin subresource fetch from same-origin worker with same-origin
|
||||
// redirect:
|
||||
// worker: local scheme (data: URL inherits policy)
|
||||
// fetch origin: same-origin
|
||||
// fetch target: same-origin
|
||||
// This should FAIL.
|
||||
worker_subresource_redirect_test(
|
||||
get_host_info().HTTP_ORIGIN, get_host_info().HTTP_ORIGIN, FAILURE,
|
||||
'Same-origin subresource fetch from dedicated worker with same-origin redirect fails due to redirects=block.');
|
||||
|
||||
// 3. Same-origin subresource fetch from same-origin worker with cross-origin
|
||||
// redirect:
|
||||
// worker: local scheme (data: URL inherits policy)
|
||||
// fetch origin: same-origin
|
||||
// fetch target: cross-origin
|
||||
// This should FAIL.
|
||||
worker_subresource_redirect_test(
|
||||
get_host_info().HTTP_ORIGIN, get_host_info().HTTP_REMOTE_ORIGIN, FAILURE,
|
||||
'Same-origin subresource fetch from dedicated worker with cross-origin redirect fails due to redirects=block.');
|
||||
@@ -0,0 +1 @@
|
||||
Connection-Allowlist: (response-origin);redirects=block
|
||||
34
tests/wpt/tests/connection-allowlist/tentative/dedicated-worker-redirect-default.sub.window.js
vendored
Normal file
34
tests/wpt/tests/connection-allowlist/tentative/dedicated-worker-redirect-default.sub.window.js
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
// META: script=resources/worker_redirect_test.js
|
||||
//
|
||||
// The following tests assume the policy `Connection-Allowlist:
|
||||
// (response-origin)` has been set.
|
||||
|
||||
// 1. Same-origin worker script redirect:
|
||||
// origin: http://{{hosts[][]}} (allowed by allowlist)
|
||||
// target: http://{{hosts[][]}} (also allowed)
|
||||
// This should FAIL because redirects are default-blocked for allowlisted
|
||||
// fetches.
|
||||
worker_script_redirect_test(
|
||||
get_host_info().HTTP_ORIGIN, get_host_info().HTTP_ORIGIN, FAILURE,
|
||||
'Same-origin dedicated worker main script fetch with redirect fails by default.');
|
||||
|
||||
// 2. Same-origin subresource fetch from same-origin worker with same-origin
|
||||
// redirect:
|
||||
// worker: local scheme (data: URL inherits policy)
|
||||
// fetch origin: same-origin
|
||||
// fetch target: same-origin
|
||||
// This should FAIL.
|
||||
worker_subresource_redirect_test(
|
||||
get_host_info().HTTP_ORIGIN, get_host_info().HTTP_ORIGIN, FAILURE,
|
||||
'Same-origin subresource fetch from dedicated worker with same-origin redirect fails by default.');
|
||||
|
||||
// 3. Same-origin subresource fetch from same-origin worker with cross-origin
|
||||
// redirect:
|
||||
// worker: local scheme (data: URL inherits policy)
|
||||
// fetch origin: same-origin
|
||||
// fetch target: cross-origin
|
||||
// This should FAIL.
|
||||
worker_subresource_redirect_test(
|
||||
get_host_info().HTTP_ORIGIN, get_host_info().HTTP_REMOTE_ORIGIN, FAILURE,
|
||||
'Same-origin subresource fetch from dedicated worker with cross-origin redirect fails by default.');
|
||||
@@ -1,112 +0,0 @@
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
//
|
||||
// The following tests assume the policy `Connection-Allowlist: (response-origin)` has been set.
|
||||
|
||||
const port = get_host_info().HTTP_PORT_ELIDED;
|
||||
const SUCCESS = true;
|
||||
const FAILURE = false;
|
||||
|
||||
function worker_script_redirect_test(origin, target_origin, expectation, description) {
|
||||
promise_test(async t => {
|
||||
const target_url = target_origin + "/connection-allowlist/tentative/resources/worker-fetch-script.js";
|
||||
const url = origin + "/common/redirect.py?status=302&location=" + encodeURIComponent(target_url);
|
||||
|
||||
let worker;
|
||||
try {
|
||||
worker = new Worker(url);
|
||||
} catch (e) {
|
||||
// Some implementations might throw synchronously for some origins, but usually it's an error event.
|
||||
assert_equals(expectation, FAILURE, "Worker constructor threw unexpectedly");
|
||||
return;
|
||||
}
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
worker.onmessage = () => resolve(SUCCESS);
|
||||
worker.onerror = (e) => {
|
||||
e.preventDefault();
|
||||
reject(new Error("Worker Load Error"));
|
||||
};
|
||||
// Send a message to the worker. If it loaded successfully, it will
|
||||
// respond and onmessage will fire. If it failed to load, onerror
|
||||
// should fire.
|
||||
worker.postMessage(`${get_host_info().HTTP_ORIGIN}/common/blank-with-cors.html`);
|
||||
});
|
||||
|
||||
if (expectation === SUCCESS) {
|
||||
const result = await promise;
|
||||
assert_equals(result, expectation, description);
|
||||
} else {
|
||||
await promise_rejects_js(t, Error, promise, description);
|
||||
}
|
||||
}, description);
|
||||
}
|
||||
|
||||
// 1. Same-origin worker script redirect:
|
||||
// origin: http://{{hosts[][]}} (allowed by allowlist)
|
||||
// target: http://{{hosts[][]}} (also allowed)
|
||||
// This should FAIL because redirects are default-blocked for allowlisted fetches.
|
||||
worker_script_redirect_test(
|
||||
get_host_info().HTTP_ORIGIN,
|
||||
get_host_info().HTTP_ORIGIN,
|
||||
FAILURE,
|
||||
"Same-origin dedicated worker main script fetch with redirect fails."
|
||||
);
|
||||
|
||||
const worker_content = `
|
||||
onmessage = async (e) => {
|
||||
const url = e.data;
|
||||
try {
|
||||
const r = await fetch(url, { mode: 'cors', credentials: 'omit' });
|
||||
postMessage({ url: url, success: r.ok });
|
||||
} catch (err) {
|
||||
postMessage({ url: url, success: false, error: err.name });
|
||||
}
|
||||
};
|
||||
`;
|
||||
const dataUrl = "data:text/javascript," + encodeURIComponent(worker_content);
|
||||
|
||||
function worker_subresource_redirect_test(fetch_origin, fetch_target_origin, expectation, description) {
|
||||
promise_test(async t => {
|
||||
const worker = new Worker(dataUrl, { type: 'module' });
|
||||
|
||||
const target_url = fetch_target_origin + "/common/blank-with-cors.html";
|
||||
const fetch_url = fetch_origin + "/common/redirect.py?status=302&location=" + encodeURIComponent(target_url);
|
||||
|
||||
worker.postMessage(fetch_url);
|
||||
|
||||
const msgEvent = await new Promise((resolve, reject) => {
|
||||
worker.onmessage = resolve;
|
||||
worker.onerror = (e) => reject(new Error("Worker Error"));
|
||||
});
|
||||
|
||||
if (expectation === SUCCESS) {
|
||||
assert_true(msgEvent.data.success, `Fetch to ${fetch_url} should succeed.`);
|
||||
} else {
|
||||
assert_false(msgEvent.data.success, `Fetch to ${fetch_url} should be blocked.`);
|
||||
}
|
||||
}, description);
|
||||
}
|
||||
|
||||
// 2. Same-origin subresource fetch from same-origin worker with same-origin redirect:
|
||||
// worker: same-origin (data: URL inherits policy)
|
||||
// fetch origin: same-origin
|
||||
// fetch target: same-origin
|
||||
// This should FAIL.
|
||||
worker_subresource_redirect_test(
|
||||
get_host_info().HTTP_ORIGIN,
|
||||
get_host_info().HTTP_ORIGIN,
|
||||
FAILURE,
|
||||
"Same-origin subresource fetch from dedicated worker with same-origin redirect fails."
|
||||
);
|
||||
|
||||
// 3. Same-origin subresource fetch from same-origin worker with cross-origin redirect:
|
||||
// worker: same-origin (data: URL inherits policy)
|
||||
// fetch origin: same-origin
|
||||
// fetch target: cross-origin
|
||||
// This should FAIL.
|
||||
worker_subresource_redirect_test(
|
||||
get_host_info().HTTP_ORIGIN,
|
||||
get_host_info().HTTP_REMOTE_ORIGIN,
|
||||
FAILURE,
|
||||
"Same-origin subresource fetch from dedicated worker with cross-origin redirect fails."
|
||||
);
|
||||
31
tests/wpt/tests/connection-allowlist/tentative/fetch-redirect-allow.sub.window.js
vendored
Normal file
31
tests/wpt/tests/connection-allowlist/tentative/fetch-redirect-allow.sub.window.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
// META: script=resources/fetch_redirect_test.js
|
||||
//
|
||||
// The following tests assume the policy `Connection-Allowlist:
|
||||
// (response-origin);redirects=allow` has been set. Redirects for fetches
|
||||
// allowed through connection allowlists should be allowed.
|
||||
|
||||
// We're loading this page from `http://{{hosts[][]}}`.
|
||||
// The connection allowlist header is `Connection-Allowlist:
|
||||
// (response-origin);redirects=allow`. Thus, only `http://{{hosts[][]}}` is
|
||||
// allowlisted for fetches.
|
||||
|
||||
// Same-origin redirect:
|
||||
// origin: http://{{hosts[][]}} (allowed by allowlist)
|
||||
// target: http://{{hosts[][]}} (also allowed)
|
||||
// This should SUCCEED, because of the "allow" header param.
|
||||
fetch_redirect_test(
|
||||
'http://{{hosts[][]}}' + port, 'http://{{hosts[][]}}' + port, SUCCESS);
|
||||
|
||||
// Redirect from an allowlisted origin to a different origin:
|
||||
// origin: http://{{hosts[][]}} (allowed by allowlist)
|
||||
// target: http://{{hosts[alt][]}} (not allowed)
|
||||
// This should SUCCEED, because of the "allow" header param.
|
||||
fetch_redirect_test(
|
||||
'http://{{hosts[][]}}' + port, 'http://{{hosts[alt][]}}' + port, SUCCESS);
|
||||
|
||||
// Fetch to a non-allowlisted origin:
|
||||
// origin: http://{{hosts[alt][]}} (not allowed)
|
||||
// This is blocked before the redirect even happens.
|
||||
fetch_redirect_test(
|
||||
'http://{{hosts[alt][]}}' + port, 'http://{{hosts[][]}}' + port, FAILURE);
|
||||
1
tests/wpt/tests/connection-allowlist/tentative/fetch-redirect-allow.sub.window.js.headers
vendored
Normal file
1
tests/wpt/tests/connection-allowlist/tentative/fetch-redirect-allow.sub.window.js.headers
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Connection-Allowlist: (response-origin);redirects=allow
|
||||
31
tests/wpt/tests/connection-allowlist/tentative/fetch-redirect-block.sub.window.js
vendored
Normal file
31
tests/wpt/tests/connection-allowlist/tentative/fetch-redirect-block.sub.window.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
// META: script=resources/fetch_redirect_test.js
|
||||
//
|
||||
// The following tests assume the policy `Connection-Allowlist:
|
||||
// (response-origin);redirects=block` has been set. Redirects for fetches
|
||||
// allowed through connection allowlists should be blocked.
|
||||
|
||||
// We're loading this page from `http://{{hosts[][]}}`.
|
||||
// The connection allowlist header is `Connection-Allowlist:
|
||||
// (response-origin);redirects=block`. Thus, only `http://{{hosts[][]}}` is
|
||||
// allowlisted for fetches.
|
||||
|
||||
// Same-origin redirect:
|
||||
// origin: http://{{hosts[][]}} (allowed by allowlist)
|
||||
// target: http://{{hosts[][]}} (also allowed)
|
||||
// This should FAIL.
|
||||
fetch_redirect_test(
|
||||
'http://{{hosts[][]}}' + port, 'http://{{hosts[][]}}' + port, FAILURE);
|
||||
|
||||
// Redirect from an allowlisted origin to a different origin:
|
||||
// origin: http://{{hosts[][]}} (allowed by allowlist)
|
||||
// target: http://{{hosts[alt][]}} (not allowed)
|
||||
// This should FAIL.
|
||||
fetch_redirect_test(
|
||||
'http://{{hosts[][]}}' + port, 'http://{{hosts[alt][]}}' + port, FAILURE);
|
||||
|
||||
// Fetch to a non-allowlisted origin:
|
||||
// origin: http://{{hosts[alt][]}} (not allowed)
|
||||
// This is blocked before the redirect even happens.
|
||||
fetch_redirect_test(
|
||||
'http://{{hosts[alt][]}}' + port, 'http://{{hosts[][]}}' + port, FAILURE);
|
||||
1
tests/wpt/tests/connection-allowlist/tentative/fetch-redirect-block.sub.window.js.headers
vendored
Normal file
1
tests/wpt/tests/connection-allowlist/tentative/fetch-redirect-block.sub.window.js.headers
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Connection-Allowlist: (response-origin);redirects=block
|
||||
31
tests/wpt/tests/connection-allowlist/tentative/fetch-redirect-default.sub.window.js
vendored
Normal file
31
tests/wpt/tests/connection-allowlist/tentative/fetch-redirect-default.sub.window.js
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
// META: script=resources/fetch_redirect_test.js
|
||||
//
|
||||
// The following tests assume the policy `Connection-Allowlist:
|
||||
// (response-origin)` has been set. Redirects for fetches allowed through
|
||||
// connection allowlists should be blocked by default.
|
||||
|
||||
// We're loading this page from `http://{{hosts[][]}}`.
|
||||
// The connection allowlist header is `Connection-Allowlist: (response-origin)`.
|
||||
// Thus, only `http://{{hosts[][]}}` is allowlisted for fetches.
|
||||
|
||||
// Same-origin redirect:
|
||||
// origin: http://{{hosts[][]}} (allowed by allowlist)
|
||||
// target: http://{{hosts[][]}} (also allowed)
|
||||
// This should FAIL because redirects are default-blocked for allowlisted
|
||||
// fetches.
|
||||
fetch_redirect_test(
|
||||
'http://{{hosts[][]}}' + port, 'http://{{hosts[][]}}' + port, FAILURE);
|
||||
|
||||
// Redirect from an allowlisted origin to a different origin:
|
||||
// origin: http://{{hosts[][]}} (allowed by allowlist)
|
||||
// target: http://{{hosts[alt][]}} (not allowed)
|
||||
// This should FAIL.
|
||||
fetch_redirect_test(
|
||||
'http://{{hosts[][]}}' + port, 'http://{{hosts[alt][]}}' + port, FAILURE);
|
||||
|
||||
// Fetch to a non-allowlisted origin:
|
||||
// origin: http://{{hosts[alt][]}} (not allowed)
|
||||
// This is blocked before the redirect even happens.
|
||||
fetch_redirect_test(
|
||||
'http://{{hosts[alt][]}}' + port, 'http://{{hosts[][]}}' + port, FAILURE);
|
||||
@@ -1,46 +0,0 @@
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
//
|
||||
// The following tests assume the policy `Connection-Allowlist: (response-origin)` has been set.
|
||||
// Redirects for fetches allowed through connection allowlists should be blocked by default.
|
||||
|
||||
const port = get_host_info().HTTP_PORT_ELIDED;
|
||||
const SUCCESS = true;
|
||||
const FAILURE = false;
|
||||
|
||||
function fetch_redirect_test(origin, target_origin, expectation) {
|
||||
const target_url = target_origin + "/common/blank-with-cors.html";
|
||||
const url = origin + "/common/redirect.py?status=302&location=" + encodeURIComponent(target_url);
|
||||
|
||||
if (expectation === FAILURE) {
|
||||
return promise_test(async t => {
|
||||
const fetcher = fetch(url, { mode: "cors", credentials: "omit" });
|
||||
return promise_rejects_js(t, TypeError, fetcher);
|
||||
}, `Fetch redirect from ${origin} to ${target_origin} fails.`);
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
const r = await fetch(url, { mode: "cors", credentials: "omit" });
|
||||
assert_equals(r.status, 200);
|
||||
}, `Fetch redirect from ${origin} to ${target_origin} succeeds.`);
|
||||
}
|
||||
|
||||
// We're loading this page from `http://{{hosts[][]}}`.
|
||||
// The connection allowlist header is `Connection-Allowlist: (response-origin)`.
|
||||
// Thus, only `http://{{hosts[][]}}` is allowlisted for fetches.
|
||||
|
||||
// Same-origin redirect:
|
||||
// origin: http://{{hosts[][]}} (allowed by allowlist)
|
||||
// target: http://{{hosts[][]}} (also allowed)
|
||||
// This should FAIL because redirects are default-blocked for allowlisted fetches.
|
||||
fetch_redirect_test("http://{{hosts[][]}}" + port, "http://{{hosts[][]}}" + port, FAILURE);
|
||||
|
||||
// Redirect from an allowlisted origin to a different origin:
|
||||
// origin: http://{{hosts[][]}} (allowed by allowlist)
|
||||
// target: http://{{hosts[alt][]}} (not allowed)
|
||||
// This should FAIL.
|
||||
fetch_redirect_test("http://{{hosts[][]}}" + port, "http://{{hosts[alt][]}}" + port, FAILURE);
|
||||
|
||||
// Fetch to a non-allowlisted origin:
|
||||
// origin: http://{{hosts[alt][]}} (not allowed)
|
||||
// This is blocked before the redirect even happens.
|
||||
fetch_redirect_test("http://{{hosts[alt][]}}" + port, "http://{{hosts[][]}}" + port, FAILURE);
|
||||
23
tests/wpt/tests/connection-allowlist/tentative/resources/fetch_redirect_test.js
vendored
Normal file
23
tests/wpt/tests/connection-allowlist/tentative/resources/fetch_redirect_test.js
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
|
||||
const port = get_host_info().HTTP_PORT_ELIDED;
|
||||
const SUCCESS = true;
|
||||
const FAILURE = false;
|
||||
|
||||
function fetch_redirect_test(origin, target_origin, expectation) {
|
||||
const target_url = target_origin + '/common/blank-with-cors.html';
|
||||
const url = origin + '/common/redirect.py?status=302&location=' +
|
||||
encodeURIComponent(target_url);
|
||||
|
||||
if (expectation === FAILURE) {
|
||||
return promise_test(async t => {
|
||||
const fetcher = fetch(url, {mode: 'cors', credentials: 'omit'});
|
||||
return promise_rejects_js(t, TypeError, fetcher);
|
||||
}, `Fetch redirect from ${origin} to ${target_origin} fails.`);
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
const r = await fetch(url, {mode: 'cors', credentials: 'omit'});
|
||||
assert_equals(r.status, 200);
|
||||
}, `Fetch redirect from ${origin} to ${target_origin} succeeds.`);
|
||||
}
|
||||
88
tests/wpt/tests/connection-allowlist/tentative/resources/worker_redirect_test.js
vendored
Normal file
88
tests/wpt/tests/connection-allowlist/tentative/resources/worker_redirect_test.js
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
|
||||
const port = get_host_info().HTTP_PORT_ELIDED;
|
||||
const SUCCESS = true;
|
||||
const FAILURE = false;
|
||||
|
||||
function worker_script_redirect_test(
|
||||
origin, target_origin, expectation, description) {
|
||||
promise_test(async t => {
|
||||
const target_url = target_origin +
|
||||
'/connection-allowlist/tentative/resources/worker-fetch-script.js';
|
||||
const url = origin + '/common/redirect.py?status=302&location=' +
|
||||
encodeURIComponent(target_url);
|
||||
|
||||
let worker;
|
||||
try {
|
||||
worker = new Worker(url);
|
||||
} catch (e) {
|
||||
// Some implementations might throw synchronously for some origins, but
|
||||
// usually it's an error event.
|
||||
assert_equals(
|
||||
expectation, FAILURE, 'Worker constructor threw unexpectedly');
|
||||
return;
|
||||
}
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
worker.onmessage = () => resolve(SUCCESS);
|
||||
worker.onerror = (e) => {
|
||||
e.preventDefault();
|
||||
reject(new Error('Worker Load Error'));
|
||||
};
|
||||
// Send a message to the worker. If it loaded successfully, it will
|
||||
// respond and onmessage will fire. If it failed to load, onerror
|
||||
// should fire.
|
||||
worker.postMessage(
|
||||
`${get_host_info().HTTP_ORIGIN}/common/blank-with-cors.html`);
|
||||
});
|
||||
|
||||
if (expectation === SUCCESS) {
|
||||
const result = await promise;
|
||||
assert_equals(result, expectation, description);
|
||||
} else {
|
||||
await promise_rejects_js(t, Error, promise, description);
|
||||
}
|
||||
}, description);
|
||||
}
|
||||
|
||||
const worker_content = `
|
||||
onmessage = async (e) => {
|
||||
const url = e.data;
|
||||
try {
|
||||
const r = await fetch(url, { mode: 'cors', credentials: 'omit' });
|
||||
postMessage({ url: url, success: r.ok });
|
||||
} catch (err) {
|
||||
postMessage({ url: url, success: false, error: err.name });
|
||||
}
|
||||
};
|
||||
`;
|
||||
const dataUrl = 'data:text/javascript,' + encodeURIComponent(worker_content);
|
||||
|
||||
function worker_subresource_redirect_test(
|
||||
fetch_origin, fetch_target_origin, expectation, description) {
|
||||
promise_test(async t => {
|
||||
const worker = new Worker(dataUrl, {type: 'module'});
|
||||
|
||||
const target_url = fetch_target_origin + '/common/blank-with-cors.html';
|
||||
// We need to pass `enable-cors` because the worker request origin will be
|
||||
// `null`.
|
||||
const fetch_url = fetch_origin +
|
||||
'/common/redirect.py?status=302&enable-cors&location=' +
|
||||
encodeURIComponent(target_url);
|
||||
|
||||
worker.postMessage(fetch_url);
|
||||
|
||||
const msgEvent = await new Promise((resolve, reject) => {
|
||||
worker.onmessage = resolve;
|
||||
worker.onerror = (e) => reject(new Error('Worker Error'));
|
||||
});
|
||||
|
||||
if (expectation === SUCCESS) {
|
||||
assert_true(
|
||||
msgEvent.data.success, `Fetch to ${fetch_url} should succeed.`);
|
||||
} else {
|
||||
assert_false(
|
||||
msgEvent.data.success, `Fetch to ${fetch_url} should be blocked.`);
|
||||
}
|
||||
}, description);
|
||||
}
|
||||
56
tests/wpt/tests/connection-allowlist/tentative/websocket.sub.window.js
vendored
Normal file
56
tests/wpt/tests/connection-allowlist/tentative/websocket.sub.window.js
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
// META: script=/common/get-host-info.sub.js
|
||||
//
|
||||
// The following tests assume the policy
|
||||
// `Connection-Allowlist: (response-origin "http://{{host}}:{{ports[ws][0]}}")`
|
||||
// has been set. The WPT WebSocket server runs on a different port than HTTP,
|
||||
// so the allowlist explicitly includes the same-host WebSocket origin.
|
||||
|
||||
const ws_port = "{{ports[ws][0]}}";
|
||||
|
||||
function websocket_test(host, expectation, description) {
|
||||
promise_test(async t => {
|
||||
const url = `ws://${host}:${ws_port}/echo`;
|
||||
const ws = new WebSocket(url);
|
||||
|
||||
const result = await new Promise(resolve => {
|
||||
ws.onopen = () => { ws.close(); resolve('open'); };
|
||||
ws.onerror = () => resolve('error');
|
||||
});
|
||||
|
||||
assert_equals(result, expectation,
|
||||
`WebSocket to ${host} should ${expectation === 'open' ? 'connect' : 'be blocked'}.`);
|
||||
}, description);
|
||||
}
|
||||
|
||||
// Same-origin WebSocket should succeed (allowlisted via explicit pattern).
|
||||
websocket_test(
|
||||
"{{hosts[][]}}",
|
||||
"open",
|
||||
"Same-origin WebSocket succeeds."
|
||||
);
|
||||
|
||||
// Same-site but cross-origin subdomains should fail.
|
||||
websocket_test(
|
||||
"{{hosts[][www]}}",
|
||||
"error",
|
||||
"Cross-origin same-site WebSocket (www) is blocked."
|
||||
);
|
||||
|
||||
websocket_test(
|
||||
"{{hosts[][www1]}}",
|
||||
"error",
|
||||
"Cross-origin same-site WebSocket (www1) is blocked."
|
||||
);
|
||||
|
||||
// Cross-site origins should fail.
|
||||
websocket_test(
|
||||
"{{hosts[alt][]}}",
|
||||
"error",
|
||||
"Cross-site WebSocket is blocked."
|
||||
);
|
||||
|
||||
websocket_test(
|
||||
"{{hosts[alt][www]}}",
|
||||
"error",
|
||||
"Cross-site WebSocket (www subdomain) is blocked."
|
||||
);
|
||||
1
tests/wpt/tests/connection-allowlist/tentative/websocket.sub.window.js.sub.headers
vendored
Normal file
1
tests/wpt/tests/connection-allowlist/tentative/websocket.sub.window.js.sub.headers
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Connection-Allowlist: (response-origin "http://{{host}}:{{ports[ws][0]}}")
|
||||
@@ -78,12 +78,11 @@ promise_test(t => {
|
||||
}, "Same-origin => cross-origin 'fetch()'.");
|
||||
|
||||
let websocket_url = "wss://{{host}}:{{ports[wss][0]}}/echo";
|
||||
let expected_websocket_csp_url = websocket_url.replace('wss://', 'https://');
|
||||
|
||||
// The WebSocket URL is not the same as 'self'
|
||||
promise_test(t => {
|
||||
return Promise.all([
|
||||
waitUntilCSPEventForURL(t, expected_websocket_csp_url),
|
||||
waitUntilCSPEventForURL(t, websocket_url),
|
||||
new Promise(resolve => {
|
||||
let ws = new WebSocket(websocket_url);
|
||||
ws.onopen = resolve;
|
||||
@@ -92,8 +91,8 @@ promise_test(t => {
|
||||
}, "WebSocket.");
|
||||
|
||||
let expected_blocked_urls = self.XMLHttpRequest
|
||||
? [ fetch_cross_origin_url, xhr_cross_origin_url, redirect_url, expected_websocket_csp_url ]
|
||||
: [ fetch_cross_origin_url, redirect_url, expected_websocket_csp_url ];
|
||||
? [ fetch_cross_origin_url, xhr_cross_origin_url, redirect_url, websocket_url ]
|
||||
: [ fetch_cross_origin_url, redirect_url, websocket_url ];
|
||||
|
||||
promise_test(async t => {
|
||||
let report_url = `{{location[server]}}/reporting/resources/report.py?` +
|
||||
|
||||
@@ -90,12 +90,11 @@ promise_test(t => {
|
||||
|
||||
|
||||
let websocket_url = "wss://{{host}}:{{ports[wss][0]}}/echo";
|
||||
let expected_websocket_csp_url = websocket_url.replace('wss://', 'https://');
|
||||
|
||||
// The WebSocket URL is not the same as 'self'
|
||||
promise_test(t => {
|
||||
return Promise.all([
|
||||
waitUntilCSPEventForURL(t, expected_websocket_csp_url),
|
||||
waitUntilCSPEventForURL(t, websocket_url),
|
||||
new Promise((resolve, reject) => {
|
||||
// Firefox throws in the constructor, Chrome triggers the error event.
|
||||
try {
|
||||
@@ -110,8 +109,8 @@ promise_test(t => {
|
||||
}, "WebSocket in " + self.location.protocol + " with {{GET[test-name]}}");
|
||||
|
||||
let expected_blocked_urls = self.XMLHttpRequest
|
||||
? [ fetch_cross_origin_url, xhr_cross_origin_url, redirect_url, expected_websocket_csp_url ]
|
||||
: [ fetch_cross_origin_url, redirect_url, expected_websocket_csp_url ];
|
||||
? [ fetch_cross_origin_url, xhr_cross_origin_url, redirect_url, websocket_url ]
|
||||
: [ fetch_cross_origin_url, redirect_url, websocket_url ];
|
||||
|
||||
promise_test(async t => {
|
||||
let report_url = `{{location[server]}}/reporting/resources/report.py` +
|
||||
|
||||
@@ -26,28 +26,28 @@ promise_test(async test => {
|
||||
const url = get_host_info().HTTP_ORIGIN.replace("http", "ws") + "/path";
|
||||
const violation = nextCSPViolation(test);
|
||||
try { new WebSocket(url); } catch (e) {}
|
||||
assert_equals((await violation).blockedURI, url.replace('ws://', 'http://'));
|
||||
assert_equals((await violation).blockedURI, url);
|
||||
}, "ws");
|
||||
|
||||
promise_test(async test => {
|
||||
const url = get_host_info().HTTP_ORIGIN.replace("http", "wss") + "/path";
|
||||
const violation = nextCSPViolation(test);
|
||||
try { new WebSocket(url); } catch (e) {}
|
||||
assert_equals((await violation).blockedURI, url.replace('wss://', 'https://'));
|
||||
assert_equals((await violation).blockedURI, url);
|
||||
}, "wss");
|
||||
|
||||
promise_test(async test => {
|
||||
const url = get_host_info().HTTP_REMOTE_ORIGIN.replace("http", "wss") + "/path";
|
||||
const violation = nextCSPViolation(test);
|
||||
try { new WebSocket(url); } catch (e) {}
|
||||
assert_equals((await violation).blockedURI, url.replace('wss://', 'https://'));
|
||||
assert_equals((await violation).blockedURI, url);
|
||||
}, "cross-origin");
|
||||
|
||||
promise_test(async test => {
|
||||
const url = get_host_info().HTTP_ORIGIN.replace("http", "wss") + "/path";
|
||||
const violation = nextCSPViolation(test);
|
||||
try {new WebSocket(redirector + "?location=" + url); } catch (e) {}
|
||||
assert_equals((await violation).blockedURI, url.replace('wss://', 'https://'));
|
||||
assert_equals((await violation).blockedURI, url);
|
||||
}, "redirect");
|
||||
|
||||
</script>
|
||||
|
||||
@@ -55,6 +55,42 @@
|
||||
);
|
||||
}, "Calling navigator.credentials.get() without a valid matching interface.");
|
||||
|
||||
promise_test(async (t) => {
|
||||
const invalidCombinations = [
|
||||
{ password: true, publicKey: { challenge: new Uint8Array() } },
|
||||
{ password: true, otp: { transport: ["sms"] } },
|
||||
{ password: true, identity: { providers: [] } },
|
||||
{ federated: { providers: ['https://idp.example.com'] }, publicKey: { challenge: new Uint8Array() } },
|
||||
{ federated: { providers: ['https://idp.example.com'] }, otp: { transport: ["sms"] } },
|
||||
{ federated: { providers: ['https://idp.example.com'] }, identity: { providers: [] } },
|
||||
{ publicKey: { challenge: new Uint8Array() }, otp: { transport: ["sms"] } },
|
||||
{ publicKey: { challenge: new Uint8Array() }, identity: { providers: [] } },
|
||||
{ otp: { transport: ["sms"] }, identity: { providers: [] } },
|
||||
];
|
||||
|
||||
for (const options of invalidCombinations) {
|
||||
await promise_rejects_dom(
|
||||
t,
|
||||
"NotSupportedError",
|
||||
navigator.credentials.get(options)
|
||||
);
|
||||
}
|
||||
}, "Calling navigator.credentials.get() with invalid combinations of credential types.");
|
||||
|
||||
promise_test(async (t) => {
|
||||
// Valid combination (password + federated) is allowed by the spec. It should reach the
|
||||
// fetching phase, where it will fail with NotAllowedError due to lack of user activation,
|
||||
// but crucially NOT NotSupportedError.
|
||||
await promise_rejects_dom(
|
||||
t,
|
||||
"NotAllowedError",
|
||||
navigator.credentials.get({
|
||||
password: true,
|
||||
federated: { providers: ['https://idp.example.com'] }
|
||||
})
|
||||
);
|
||||
}, "Calling navigator.credentials.get() with valid combination (password + federated).");
|
||||
|
||||
promise_test(function(t) {
|
||||
const controller = new AbortController();
|
||||
controller.abort("custom reason");
|
||||
|
||||
10
tests/wpt/tests/css/CSS2/floats/crashtests/float-dynamic-change-8.html
vendored
Normal file
10
tests/wpt/tests/css/CSS2/floats/crashtests/float-dynamic-change-8.html
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
<!DOCTYPE html>
|
||||
<link rel="help" href="https://crbug.com/495571483">
|
||||
<style>
|
||||
.float::backdrop { float: left; }
|
||||
</style>
|
||||
<dialog id="dialog"></dialog>
|
||||
<script>
|
||||
dialog.showModal();
|
||||
dialog.classList.add('float');
|
||||
</script>
|
||||
49
tests/wpt/tests/css/css-anchor-position/anchor-function-chain.html
vendored
Normal file
49
tests/wpt/tests/css/css-anchor-position/anchor-function-chain.html
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<title>Tests that chain of elements that anchor to each other using anchor() works.</title>
|
||||
|
||||
<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#anchor-pos">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#determining">
|
||||
<link rel="author" title="Kiet Ho" href="mailto:kiet.ho@apple.com">
|
||||
|
||||
<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>
|
||||
.containing-block {
|
||||
border: 1px solid black;
|
||||
|
||||
position: relative;
|
||||
width: 500px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.box {
|
||||
position: absolute;
|
||||
left: calc(anchor(--box right) + 10px);
|
||||
anchor-name: --box;
|
||||
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-color: green;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body onload="checkLayoutForAnchorPos('.target')">
|
||||
<p>You should see a row of five boxes in order.</p>
|
||||
|
||||
<div class="containing-block">
|
||||
<div class="box target" data-offset-x="0" data-offset-y="0">1</div>
|
||||
<div class="box target" data-offset-x="60" data-offset-y="0">2</div>
|
||||
<div class="box target" data-offset-x="120" data-offset-y="0">3</div>
|
||||
<div class="box target" data-offset-x="180" data-offset-y="0">4</div>
|
||||
<div class="box target" data-offset-x="240" data-offset-y="0">5</div>
|
||||
</div>
|
||||
</body>
|
||||
@@ -83,4 +83,8 @@ test_valid_value('top', 'calc((anchor(--foo top) + anchor(--bar bottom)) / 2)',
|
||||
test_valid_value('top', 'calc(0.5 * (anchor(--foo top) + anchor(--bar bottom)))');
|
||||
test_valid_value('top', 'anchor(--foo top, calc(0.5 * anchor(--bar bottom)))');
|
||||
test_valid_value('top', 'min(100px, 10%, anchor(--foo top), anchor(--bar bottom))');
|
||||
|
||||
// Should accept unitless zero
|
||||
test_valid_value('top', "anchor(--foo left, 0)", "anchor(--foo left, 0px)");
|
||||
test_valid_value('top', "calc(anchor(--foo left, 0))", "anchor(--foo left, 0px)");
|
||||
</script>
|
||||
|
||||
@@ -20,7 +20,11 @@ div {
|
||||
}
|
||||
|
||||
#scroller1,#scroller2 {
|
||||
overflow: scroll;
|
||||
/* Using visible scrollbars here tests scrollbar painting order as well, which
|
||||
isn't related to anchor positioning. See:
|
||||
https://github.com/web-platform-tests/wpt/pull/55022#issuecomment-3499478504 */
|
||||
overflow: scroll;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
#anchor {
|
||||
|
||||
65
tests/wpt/tests/css/css-anchor-position/anchor-size-function-chain.html
vendored
Normal file
65
tests/wpt/tests/css/css-anchor-position/anchor-size-function-chain.html
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<title>Tests that chain of elements that anchor to each other using anchor-size() works.</title>
|
||||
|
||||
<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#anchor-size-fn">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#determining">
|
||||
<link rel="author" title="Kiet Ho" href="mailto:kiet.ho@apple.com">
|
||||
|
||||
<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>
|
||||
.containing-block {
|
||||
border: 1px solid black;
|
||||
|
||||
position: relative;
|
||||
width: 500px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.box {
|
||||
/* Can only use anchor-size() with position: absolute */
|
||||
position: absolute;
|
||||
background-color: green;
|
||||
anchor-name: --box;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#box1 {
|
||||
width: 50px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
#box2, #box3, #box4, #box5 {
|
||||
width: calc(anchor-size(--box width) + 10px);
|
||||
height: calc(anchor-size(--box height) + 20px);
|
||||
}
|
||||
|
||||
#box2 { left: 60px; }
|
||||
#box3 { left: 130px; }
|
||||
#box4 { left: 210px; }
|
||||
#box5 { left: 300px; }
|
||||
</style>
|
||||
|
||||
<body onload="checkLayoutForAnchorPos('.target')">
|
||||
<p>You should see a row of five rectangles growing in size in order.</p>
|
||||
|
||||
<div class="containing-block">
|
||||
<!--
|
||||
The increasing size is to check that the boxes are anchoring to each other
|
||||
in the correct order i.e box2 chains to box1, box3 chains to box2, ...
|
||||
-->
|
||||
<div class="box target" id="box1" data-expected-width="50" data-expected-height="60">1</div>
|
||||
<div class="box target" id="box2" data-expected-width="60" data-expected-height="80">2</div>
|
||||
<div class="box target" id="box3" data-expected-width="70" data-expected-height="100">3</div>
|
||||
<div class="box target" id="box4" data-expected-width="80" data-expected-height="120">4</div>
|
||||
<div class="box target" id="box5" data-expected-width="90" data-expected-height="140">5</div>
|
||||
</div>
|
||||
</body>
|
||||
@@ -105,4 +105,8 @@ for (const prop of ['width', 'max-width', 'margin-left']) {
|
||||
test_valid_value(prop, 'anchor-size(--foo width, calc(0.5 * anchor-size(--bar height)))');
|
||||
test_valid_value(prop, 'min(100px, 10%, anchor-size(--foo width), anchor-size(--bar height))');
|
||||
}
|
||||
|
||||
// Should accept unitless zero
|
||||
test_valid_value('width', "anchor-size(--foo width, 0)", "anchor-size(--foo width, 0px)");
|
||||
test_valid_value('width', "calc(anchor-size(--foo width, 0))", "anchor-size(--foo width, 0px)");
|
||||
</script>
|
||||
|
||||
51
tests/wpt/tests/css/css-anchor-position/position-area-chain.html
vendored
Normal file
51
tests/wpt/tests/css/css-anchor-position/position-area-chain.html
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<title>Tests that chain of elements that anchor to each other using position-area works.</title>
|
||||
|
||||
<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#position-area">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#determining">
|
||||
<link rel="author" title="Kiet Ho" href="mailto:kiet.ho@apple.com">
|
||||
|
||||
<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>
|
||||
.containing-block {
|
||||
border: 1px solid black;
|
||||
|
||||
position: relative;
|
||||
width: 500px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.box {
|
||||
position: absolute;
|
||||
anchor-name: --box;
|
||||
position-area: center right;
|
||||
position-anchor: --box;
|
||||
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-right: 10px white solid;
|
||||
background-color: green;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body onload="checkLayoutForAnchorPos('.target')">
|
||||
<p>You should see a row of five boxes in order.</p>
|
||||
|
||||
<div class="containing-block">
|
||||
<div class="box target" data-offset-x="0" data-offset-y="0">1</div>
|
||||
<div class="box target" data-offset-x="60" data-offset-y="0">2</div>
|
||||
<div class="box target" data-offset-x="120" data-offset-y="0">3</div>
|
||||
<div class="box target" data-offset-x="180" data-offset-y="0">4</div>
|
||||
<div class="box target" data-offset-x="240" data-offset-y="0">5</div>
|
||||
</div>
|
||||
</body>
|
||||
17
tests/wpt/tests/css/css-anchor-position/position-area-no-default-anchor.html
vendored
Normal file
17
tests/wpt/tests/css/css-anchor-position/position-area-no-default-anchor.html
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
<!DOCTYPE html>
|
||||
<link rel="help" href="https://crbug.com/388575663">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#typedef-position-area">
|
||||
<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
|
||||
<title>If the box does not have a default anchor box, or is not an absolutely positioned box, position-area has no effect.</title>
|
||||
<style>
|
||||
.abspos {
|
||||
position: absolute;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
</style>
|
||||
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
|
||||
<div style="position: relative; width: 100px; height: 200px;">
|
||||
<div class="abspos" style="background: red;"></div>
|
||||
<div class="abspos" style="position-area: left; background: green;"></div>
|
||||
</div>
|
||||
@@ -17,6 +17,7 @@ div {
|
||||
|
||||
#scroller1,#scroller2 {
|
||||
overflow: scroll;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
#anchor {
|
||||
|
||||
83
tests/wpt/tests/css/css-animations/responsive/fill-forwards-viewport-units.html
vendored
Normal file
83
tests/wpt/tests/css/css-animations/responsive/fill-forwards-viewport-units.html
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Animations: fill-forwards with viewport units responds to viewport resize</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-animations-1/#animation-fill-mode">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../support/testcommon.js"></script>
|
||||
<style>
|
||||
iframe {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
function createIframe(test) {
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.srcdoc = `
|
||||
<style>
|
||||
@keyframes grow {
|
||||
from { width: 10vw; }
|
||||
to { width: 50vw; }
|
||||
}
|
||||
#target {
|
||||
width: 0px;
|
||||
height: 100px;
|
||||
background: green;
|
||||
animation: grow 100s forwards linear;
|
||||
}
|
||||
</style>
|
||||
<div id="target"></div>
|
||||
`;
|
||||
document.body.appendChild(iframe);
|
||||
test.add_cleanup(() => iframe.remove());
|
||||
return new Promise(resolve => iframe.addEventListener('load', () => resolve(iframe)));
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
const iframe = await createIframe(t);
|
||||
const doc = iframe.contentDocument;
|
||||
const win = iframe.contentWindow;
|
||||
const target = doc.getElementById('target');
|
||||
const anim = target.getAnimations()[0];
|
||||
|
||||
// Jump to end so fill-mode: forwards takes effect.
|
||||
anim.currentTime = 100 * 1000;
|
||||
await anim.finished;
|
||||
await waitForAnimationFrames(2);
|
||||
|
||||
// iframe is 200px wide, so 50vw = 100px.
|
||||
assert_approx_equals(
|
||||
parseFloat(win.getComputedStyle(target).width), 100, 1,
|
||||
'Filled width should be 50vw of 200px viewport'
|
||||
);
|
||||
|
||||
// Resize iframe to 400px wide.
|
||||
iframe.style.width = '400px';
|
||||
target.offsetHeight;
|
||||
await waitForAnimationFrames(3);
|
||||
|
||||
// 50vw of 400px = 200px.
|
||||
assert_approx_equals(
|
||||
parseFloat(win.getComputedStyle(target).width), 200, 1,
|
||||
'Filled width should update to 50vw of 400px viewport after resize'
|
||||
);
|
||||
|
||||
// Resize iframe to 600px wide.
|
||||
iframe.style.width = '600px';
|
||||
target.offsetHeight;
|
||||
await waitForAnimationFrames(3);
|
||||
|
||||
// 50vw of 600px = 300px.
|
||||
assert_approx_equals(
|
||||
parseFloat(win.getComputedStyle(target).width), 300, 1,
|
||||
'Filled width should update to 50vw of 600px viewport after second resize'
|
||||
);
|
||||
}, 'fill: forwards with viewport units updates on viewport resize');
|
||||
</script>
|
||||
</body>
|
||||
@@ -1,41 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 230px;
|
||||
height: 240px;
|
||||
overflow: visible;
|
||||
}
|
||||
.outer-shadow {
|
||||
fill: blue;
|
||||
stroke: blue;
|
||||
stroke-width: 20px;
|
||||
transform: translate(10px, 20px);
|
||||
}
|
||||
.border {
|
||||
fill: green;
|
||||
stroke: black;
|
||||
stroke-width: 10px;
|
||||
}
|
||||
.inset-shadow {
|
||||
fill: none;
|
||||
stroke: purple;
|
||||
stroke-width: 10px;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
svg {
|
||||
display: block;
|
||||
width: 240px;
|
||||
height: 250px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<svg viewBox="0 0 230 240" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg viewBox="0 0 240 250" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<clipPath id="inner-clip">
|
||||
<rect x="10" y="10" width="190" height="190" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<rect x="0" y="0" width="210" height="210" class="outer-shadow" />
|
||||
<rect x="5" y="5" width="200" height="200" class="border" />
|
||||
<rect x="10" y="10" width="190" height="190" class="inset-shadow" clip-path="url(#inner-clip)" />
|
||||
<!-- Outer shadow: 230x230 at (0,10) -->
|
||||
<rect x="0" y="10" width="230" height="230" fill="blue" />
|
||||
|
||||
<!-- Background: 190x190 at (10,10) -->
|
||||
<rect x="10" y="10" width="190" height="190" fill="green" />
|
||||
|
||||
<!-- Inset shadow: 10px stroke inside the 190x190 rect. Center at (15,15), size 180x180 -->
|
||||
<rect x="10" y="10" width="190" height="190" fill="none" stroke="purple" stroke-width="20" clip-path="url(#inner-clip)" />
|
||||
|
||||
<!-- Border: 10px stroke centered on base path (5,5) 200x200 rect -->
|
||||
<rect x="5" y="5" width="200" height="200" fill="none" stroke="black" stroke-width="10" />
|
||||
</svg>
|
||||
|
||||
@@ -1,36 +1,27 @@
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
svg {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
overflow: visible;
|
||||
border: 5px solid transparent;
|
||||
}
|
||||
.background {
|
||||
fill: green;
|
||||
}
|
||||
.inset-shadow {
|
||||
fill: none;
|
||||
stroke: purple;
|
||||
stroke-width: 20px;
|
||||
}
|
||||
.border {
|
||||
fill: none;
|
||||
stroke: black;
|
||||
stroke-width: 10px;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
svg {
|
||||
display: block;
|
||||
width: 210px;
|
||||
height: 210px;
|
||||
overflow: visible;
|
||||
}
|
||||
</style>
|
||||
|
||||
<svg viewBox="0 0 200 200" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg viewBox="0 0 210 210" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<clipPath id="inner-clip">
|
||||
<polygon points="100,0 200,100, 100,200, 0,100" />
|
||||
<polygon points="105,0 210,105 105,210 0,105" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<polygon points="100,0 200,100, 100,200, 0,100" class="background" />
|
||||
<polygon points="100,0 200,100, 100,200, 0,100" class="inset-shadow" clip-path="url(#inner-clip)" />
|
||||
<polygon points="100,0 200,100, 100,200, 0,100" class="border" />
|
||||
<!-- Background: clipped to the inner edge -->
|
||||
<polygon points="105,10 200,105 105,200 10,105" fill="green" />
|
||||
|
||||
<!-- Inset shadow -->
|
||||
<polygon points="105,5 205,105 105,205 5,105" fill="none" stroke="purple" stroke-width="30" clip-path="url(#inner-clip)" />
|
||||
|
||||
<!-- Border -->
|
||||
<polygon points="105,5 205,105 105,205 5,105" fill="none" stroke="black" stroke-width="10" />
|
||||
</svg>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-borders-4/#border-shape">
|
||||
<link rel="match" href="border-shape-inset-shadow-ref.html">
|
||||
<meta name="fuzzy" content="maxDifference=0-255;totalPixels=0-2000">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
|
||||
21
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-blur-ref.html
vendored
Normal file
21
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-blur-ref.html
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>CSS Borders: border-shape with box-shadow reference</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 50px;
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: green;
|
||||
/* Use border-radius as the canonical circle reference */
|
||||
border-radius: 50px;
|
||||
box-shadow: 0 0 10px 0 black;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
</html>
|
||||
24
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-blur.html
vendored
Normal file
24
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-blur.html
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>CSS Borders: border-shape with box-shadow (blur > 0)</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-borders-4/#border-shape">
|
||||
<link rel="match" href="border-shape-shadow-blur-ref.html">
|
||||
<meta name="fuzzy" content="maxDifference=0-32;totalPixels=0-500">
|
||||
<meta name="assert" content="box-shadow blur on a circle border-shape should exactly match the blur on a border-radius circle.">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 50px;
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: green;
|
||||
border-shape: circle(50px at 50% 50%);
|
||||
box-shadow: 0 0 10px 0 black;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
</html>
|
||||
13
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-circle-ref.html
vendored
Normal file
13
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-circle-ref.html
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<style>
|
||||
#ref1, #ref2 {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50px;
|
||||
margin: 100px;
|
||||
box-shadow: 0 0 10px 0 black;
|
||||
}
|
||||
</style>
|
||||
<p>The following two should look the same:</p>
|
||||
<div id="ref1"></div>
|
||||
<div id="ref2"></div>
|
||||
22
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-circle.html
vendored
Normal file
22
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-circle.html
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<!doctype html>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-borders-4/#border-shape">
|
||||
<link rel="match" href="border-shape-shadow-circle-ref.html">
|
||||
<style>
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-shape: circle(50px at 50% 50%);
|
||||
}
|
||||
#ref {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border-radius: 50px;
|
||||
}
|
||||
#target, #ref {
|
||||
margin: 100px;
|
||||
box-shadow: 0 0 10px 0 black;
|
||||
}
|
||||
</style>
|
||||
<p>The following two should look the same:</p>
|
||||
<div id="target"></div>
|
||||
<div id="ref"></div>
|
||||
21
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-inset-blur-ref.html
vendored
Normal file
21
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-inset-blur-ref.html
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>CSS Borders: border-shape with inset box-shadow reference</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 50px;
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: green;
|
||||
/* Use border-radius as the canonical circle reference */
|
||||
border-radius: 50px;
|
||||
box-shadow: inset 0 0 10px 0 black;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
</html>
|
||||
24
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-inset-blur.html
vendored
Normal file
24
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-inset-blur.html
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>CSS Borders: border-shape with inset box-shadow (blur > 0)</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-borders-4/#border-shape">
|
||||
<link rel="match" href="border-shape-shadow-inset-blur-ref.html">
|
||||
<meta name="fuzzy" content="maxDifference=0-2;totalPixels=0-150">
|
||||
<meta name="assert" content="inset box-shadow blur on a circle border-shape should exactly match the blur on a border-radius circle.">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 50px;
|
||||
}
|
||||
#target {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: green;
|
||||
border-shape: circle(50px at 50% 50%);
|
||||
box-shadow: inset 0 0 10px 0 black;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
</html>
|
||||
16
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-transparent-ref.html
vendored
Normal file
16
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-transparent-ref.html
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>CSS Borders: border-shape with transparent background ref</title>
|
||||
<style>
|
||||
body { padding: 50px; }
|
||||
#target {
|
||||
width: 100px; height: 100px;
|
||||
background: transparent;
|
||||
border-radius: 50px;
|
||||
box-shadow: 0 0 0 20px black;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
</html>
|
||||
19
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-transparent.html
vendored
Normal file
19
tests/wpt/tests/css/css-borders/border-shape/border-shape-shadow-transparent.html
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<title>CSS Borders: border-shape with transparent background</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-borders-4/#border-shape">
|
||||
<link rel="match" href="border-shape-shadow-transparent-ref.html">
|
||||
<meta name="fuzzy" content="maxDifference=0-72;totalPixels=0-500">
|
||||
<style>
|
||||
body { padding: 50px; }
|
||||
#target {
|
||||
width: 100px; height: 100px;
|
||||
background: transparent;
|
||||
border-shape: circle(50px at 50% 50%);
|
||||
box-shadow: 0 0 0 20px black;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<div id="target"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,43 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
svg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 220px;
|
||||
height: 220px;
|
||||
overflow: visible;
|
||||
}
|
||||
.outer-shadow {
|
||||
fill: blue;
|
||||
stroke: blue;
|
||||
stroke-width: 10px;
|
||||
transform: translate(10px, 10px);
|
||||
}
|
||||
.outer-border {
|
||||
fill: yellow;
|
||||
}
|
||||
.inner-fill {
|
||||
fill: green;
|
||||
}
|
||||
.inset-shadow {
|
||||
fill: none;
|
||||
stroke: purple;
|
||||
stroke-width: 10px;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
}
|
||||
svg {
|
||||
display: block;
|
||||
width: 220px;
|
||||
height: 220px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<svg viewBox="0 0 220 220" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<clipPath id="inner-clip">
|
||||
<circle cx="100" cy="100" r="50" />
|
||||
</clipPath>
|
||||
</defs>
|
||||
<circle cx="100" cy="100" r="100" class="outer-shadow" />
|
||||
<circle cx="100" cy="100" r="100" class="outer-border" />
|
||||
<circle cx="100" cy="100" r="50" class="inner-fill" />
|
||||
<circle cx="100" cy="100" r="50" class="inset-shadow" clip-path="url(#inner-clip)" />
|
||||
<!-- Outer shadow: r=105 at (110,110) -->
|
||||
<circle cx="110" cy="110" r="105" fill="blue" />
|
||||
|
||||
<!-- Outer border shape (filled with border color) -->
|
||||
<circle cx="100" cy="100" r="100" fill="yellow" />
|
||||
|
||||
<!-- Background (filled inside inner shape) -->
|
||||
<circle cx="100" cy="100" r="50" fill="green" />
|
||||
|
||||
<!-- Inset shadow: 10px stroke on r=50 circle, clipped to inside -->
|
||||
<circle cx="100" cy="100" r="50" fill="none" stroke="purple" stroke-width="10" clip-path="url(#inner-clip)" />
|
||||
</svg>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-borders-4/#border-shape">
|
||||
<link rel="match" href="border-shape-two-shapes-shadow-ref.html">
|
||||
<meta name="fuzzy" content="maxDifference=0-110;totalPixels=0-1400">
|
||||
<meta name="fuzzy" content="maxDifference=0-91;totalPixels=0-2000">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
|
||||
29
tests/wpt/tests/css/css-cascade/revert-rule-custom-property.html
vendored
Normal file
29
tests/wpt/tests/css/css-cascade/revert-rule-custom-property.html
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE html>
|
||||
<title>The revert-rule keyword: custom properties</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-cascade-5/#revert-rule-keyword">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<style>
|
||||
#test1 {
|
||||
--a: red;
|
||||
--b: green;
|
||||
}
|
||||
#test1 {
|
||||
--a: green;
|
||||
--b: revert-rule;
|
||||
}
|
||||
#test1 {
|
||||
--a: revert-rule;
|
||||
--b: revert-rule;
|
||||
}
|
||||
</style>
|
||||
<div id=test1></div>
|
||||
<script>
|
||||
test(() => {
|
||||
assert_true(CSS.supports('color:revert-rule'));
|
||||
assert_equals(getComputedStyle(test1).getPropertyValue("--a"), 'green')
|
||||
assert_equals(getComputedStyle(test1).getPropertyValue("--b"), 'green')
|
||||
|
||||
}, 'revert-rule in a custom property');
|
||||
</script>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user