mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Auto merge of #29869 - servo:wpt_update_11-06-2023, r=servo-wpt-sync
Sync WPT with upstream (11-06-2023) Automated downstream sync of changes from upstream as of 11-06-2023 [no-wpt-sync] r? @servo-wpt-sync
This commit is contained in:
commit
21c199072f
403 changed files with 9670 additions and 1715 deletions
|
@ -0,0 +1,2 @@
|
|||
[malformed-decl-block-001.xht]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[opacity-animation-ending-correctly-002.html]
|
||||
expected: TIMEOUT
|
|
@ -0,0 +1,2 @@
|
|||
[gap-019.html]
|
||||
expected: FAIL
|
|
@ -19,3 +19,9 @@
|
|||
|
||||
[.floating-flexbox 7]
|
||||
expected: FAIL
|
||||
|
||||
[.floating-flexbox 8]
|
||||
expected: FAIL
|
||||
|
||||
[.floating-flexbox 9]
|
||||
expected: FAIL
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[row-compat-001.html]
|
||||
[.flex 1]
|
||||
expected: FAIL
|
|
@ -0,0 +1,6 @@
|
|||
[row-use-cases-001.html]
|
||||
[left < right]
|
||||
expected: FAIL
|
||||
|
||||
[no overflow]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[row-wrap-002.tentative.html]
|
||||
expected: FAIL
|
|
@ -7,3 +7,12 @@
|
|||
|
||||
[e.style['flex-flow'\] = "nowrap" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['flex-flow'\] = "row nowrap" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['flex-flow'\] = "wrap" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['flex-flow'\] = "row wrap" should set the property value]
|
||||
expected: FAIL
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
[text-combine-emphasis.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,3 @@
|
|||
[MediaQueryListEvent.html]
|
||||
[constructor of "change" event]
|
||||
expected: FAIL
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,2 @@
|
|||
[malformed-decl-block-001.xht]
|
||||
expected: FAIL
|
2
tests/wpt/metadata/css/css-flexbox/gap-019.html.ini
Normal file
2
tests/wpt/metadata/css/css-flexbox/gap-019.html.ini
Normal file
|
@ -0,0 +1,2 @@
|
|||
[gap-019.html]
|
||||
expected: FAIL
|
|
@ -19,3 +19,6 @@
|
|||
|
||||
[.floating-flexbox 7]
|
||||
expected: FAIL
|
||||
|
||||
[.floating-flexbox 8]
|
||||
expected: FAIL
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[row-compat-001.html]
|
||||
[.flex 1]
|
||||
expected: FAIL
|
|
@ -0,0 +1,12 @@
|
|||
[row-use-cases-001.html]
|
||||
[left < right]
|
||||
expected: FAIL
|
||||
|
||||
[no overflow]
|
||||
expected: FAIL
|
||||
|
||||
[same widths]
|
||||
expected: FAIL
|
||||
|
||||
[zero width]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[row-wrap-002.tentative.html]
|
||||
expected: FAIL
|
|
@ -7,3 +7,12 @@
|
|||
|
||||
[e.style['flex-flow'\] = "nowrap" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['flex-flow'\] = "row nowrap" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['flex-flow'\] = "wrap" should set the property value]
|
||||
expected: FAIL
|
||||
|
||||
[e.style['flex-flow'\] = "row wrap" should set the property value]
|
||||
expected: FAIL
|
||||
|
|
2
tests/wpt/metadata/css/css-fonts/separators.html.ini
Normal file
2
tests/wpt/metadata/css/css-fonts/separators.html.ini
Normal file
|
@ -0,0 +1,2 @@
|
|||
[separators.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[text-combine-emphasis.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[object-replacement-1.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,2 @@
|
|||
[object-replacement-2.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,93 @@
|
|||
[calc-infinity-nan-serialize-number.html]
|
||||
['calc(NaN)' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(infinity)' as a specified value should serialize as 'calc(infinity)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(-infinity)' as a specified value should serialize as 'calc(-infinity)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * NaN)' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * nan)' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * infinity / infinity)' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * 0 * infinity)' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * (infinity + -infinity))' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * (-infinity + infinity))' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * (infinity - infinity))' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * infinity)' as a specified value should serialize as 'calc(infinity)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * -infinity)' as a specified value should serialize as 'calc(-infinity)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * iNFinIty)' as a specified value should serialize as 'calc(infinity)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * (infinity + infinity))' as a specified value should serialize as 'calc(infinity)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * (-infinity + -infinity))' as a specified value should serialize as 'calc(-infinity)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * 1/infinity)' as a specified value should serialize as 'calc(0)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * infinity * infinity)' as a specified value should serialize as 'calc(infinity)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * -infinity * -infinity)' as a specified value should serialize as 'calc(infinity)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * max(INFinity*3, 0))' as a specified value should serialize as 'calc(infinity)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * min(inFInity*4, 0))' as a specified value should serialize as 'calc(0)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * max(nAn*2, 0))' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * min(nan*3, 0))' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * clamp(-INFINITY*20, 0, infiniTY*10))' as a specified value should serialize as 'calc(0)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * max(NaN, min(0,10)))' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * clamp(NaN, 0, 10))' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * max(0, min(10, NaN)))' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * clamp(0, 10, NaN))' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * max(0, min(NaN, 10)))' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * clamp(0, NaN, 10))' as a specified value should serialize as 'calc(NaN)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * clamp(-Infinity, 0, infinity))' as a specified value should serialize as 'calc(0)'.]
|
||||
expected: FAIL
|
||||
|
||||
['calc(1 * clamp(-inFinity, infinity, 10))' as a specified value should serialize as 'calc(10)'.]
|
||||
expected: FAIL
|
|
@ -0,0 +1,3 @@
|
|||
[calc-linear-radial-conic-gradient-001.html]
|
||||
[testing background-image: conic-gradient(rgb(0, 128, 0) calc(50% + 10%), rgb(0, 0, 255) calc(60% + 20%))]
|
||||
expected: FAIL
|
|
@ -0,0 +1,6 @@
|
|||
[getComputedStyle-calc-mixed-units-002.html]
|
||||
[testing width: calc(5% + 4rem)]
|
||||
expected: FAIL
|
||||
|
||||
[testing width: calc(8lh + 7px)]
|
||||
expected: FAIL
|
|
@ -32,3 +32,12 @@
|
|||
|
||||
[customElements.define must not throw when defining another custom element in a different global object during Get(constructor, "prototype")]
|
||||
expected: FAIL
|
||||
|
||||
[customElements.getName must return null when the registry does not contain an entry with the given constructor]
|
||||
expected: FAIL
|
||||
|
||||
[customElements.getName returns the name of the entry with the given constructor when there is a matching entry.]
|
||||
expected: FAIL
|
||||
|
||||
[customElements.getName returns the name of the entry with the given customized built in constructor when there is a matching entry.]
|
||||
expected: FAIL
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[basic-dom-part-objects.tentative.html]
|
||||
[Basic imperative DOM Parts object construction]
|
||||
expected: FAIL
|
|
@ -1,3 +1,4 @@
|
|||
[keepalive.any.html]
|
||||
expected: TIMEOUT
|
||||
[keepalive in onunload in nested frame in another window]
|
||||
expected: FAIL
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
[cors-keepalive.any.html]
|
||||
expected: TIMEOUT
|
|
@ -1,4 +1,5 @@
|
|||
[redirect-keepalive.any.html]
|
||||
expected: TIMEOUT
|
||||
[[keepalive\][new window\][unload\] same-origin redirect]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
[fenced-frame-subresource-fetch.tentative.https.window.html?include=from-private]
|
||||
[private to local: failed preflight.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: missing CORS headers on preflight response.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: missing PNA header on preflight response.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: missing CORS headers on final response.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: success.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: PUT success.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: no-CORS mode failed preflight.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: no-CORS mode missing CORS headers on preflight response.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: no-CORS mode missing PNA header on preflight response.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: no-CORS mode success.]
|
||||
expected: FAIL
|
||||
|
||||
[private to private: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
[private to public: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[fenced-frame-subresource-fetch.tentative.https.window.html?include=from-local]
|
||||
[local to local: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
[local to private: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
[local to public: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[fenced-frame-subresource-fetch.tentative.https.window.html?include=from-public]
|
||||
[public to local: failed preflight.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: missing CORS headers on preflight response.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: missing PNA header on preflight response.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: missing CORS headers on final response.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: success.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: PUT success.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: no-CORS mode failed preflight.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: no-CORS mode missing CORS headers on preflight response.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: no-CORS mode missing PNA header on preflight response.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: no-CORS mode success.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: failed preflight.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: missing CORS headers on preflight response.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: missing PNA header on preflight response.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: missing CORS headers on final response.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: success.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: PUT success.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: no-CORS mode failed preflight.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: no-CORS mode missing CORS headers on preflight response.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: no-CORS mode missing PNA header on preflight response.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: no-CORS mode success.]
|
||||
expected: FAIL
|
||||
|
||||
[public to public: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[fenced-frame-subresource-fetch.tentative.https.window.html?include=baseline]
|
||||
[local to public: PUT preflight failure.]
|
||||
expected: FAIL
|
||||
|
||||
[local to public: PUT preflight success.]
|
||||
expected: FAIL
|
|
@ -0,0 +1,87 @@
|
|||
[fenced-frame.tentative.https.window.html]
|
||||
[local to local: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
[local to private: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
[local to public: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: failed preflight.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: missing CORS headers.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: missing PNA header.]
|
||||
expected: FAIL
|
||||
|
||||
[private to local: failed because fenced frames are incompatible with PNA.]
|
||||
expected: FAIL
|
||||
|
||||
[private to private: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
[private to public: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: failed preflight.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: missing CORS headers.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: missing PNA header.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: failed because fenced frames are incompatible with PNA.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: failed preflight.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: missing CORS headers.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: missing PNA header.]
|
||||
expected: FAIL
|
||||
|
||||
[public to private: failed because fenced frames are incompatible with PNA.]
|
||||
expected: FAIL
|
||||
|
||||
[public to public: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
[treat-as-public-address to local: failed preflight.]
|
||||
expected: FAIL
|
||||
|
||||
[treat-as-public-address to local: missing CORS headers.]
|
||||
expected: FAIL
|
||||
|
||||
[treat-as-public-address to local: missing PNA header.]
|
||||
expected: FAIL
|
||||
|
||||
[treat-as-public-address to local: failed because fenced frames are incompatible with PNA.]
|
||||
expected: FAIL
|
||||
|
||||
[treat-as-public-address to local (same-origin): fenced frame embedder initiated navigation has opaque origin.]
|
||||
expected: FAIL
|
||||
|
||||
[treat-as-public-address to private: failed preflight.]
|
||||
expected: FAIL
|
||||
|
||||
[treat-as-public-address to private: missing CORS headers.]
|
||||
expected: FAIL
|
||||
|
||||
[treat-as-public-address to private: missing PNA header.]
|
||||
expected: FAIL
|
||||
|
||||
[treat-as-public-address to private: failed because fenced frames are incompatible with PNA.]
|
||||
expected: FAIL
|
||||
|
||||
[treat-as-public-address to public: no preflight required.]
|
||||
expected: FAIL
|
||||
|
||||
[treat-as-public-address to local: optional preflight]
|
||||
expected: FAIL
|
|
@ -44,3 +44,54 @@
|
|||
|
||||
[treat-as-public to local (same-origin): no preflight required.]
|
||||
expected: NOTRUN
|
||||
|
||||
|
||||
[service-worker-fetch.https.window.html?9-last]
|
||||
expected: TIMEOUT
|
||||
[public to public: success.]
|
||||
expected: TIMEOUT
|
||||
|
||||
[treat-as-public to local: failed preflight.]
|
||||
expected: NOTRUN
|
||||
|
||||
[treat-as-public to local: success.]
|
||||
expected: NOTRUN
|
||||
|
||||
[treat-as-public to local (same-origin): no preflight required.]
|
||||
expected: NOTRUN
|
||||
|
||||
[treat-as-public to private: failed preflight.]
|
||||
expected: NOTRUN
|
||||
|
||||
[treat-as-public to private: success.]
|
||||
expected: NOTRUN
|
||||
|
||||
[treat-as-public to public: success.]
|
||||
expected: NOTRUN
|
||||
|
||||
|
||||
[service-worker-fetch.https.window.html?1-8]
|
||||
expected: TIMEOUT
|
||||
[local to local: success.]
|
||||
expected: TIMEOUT
|
||||
|
||||
[private to local: failed preflight.]
|
||||
expected: NOTRUN
|
||||
|
||||
[private to local: success.]
|
||||
expected: NOTRUN
|
||||
|
||||
[private to private: success.]
|
||||
expected: NOTRUN
|
||||
|
||||
[public to local: failed preflight.]
|
||||
expected: NOTRUN
|
||||
|
||||
[public to local: success.]
|
||||
expected: NOTRUN
|
||||
|
||||
[public to private: failed preflight.]
|
||||
expected: NOTRUN
|
||||
|
||||
[public to private: success.]
|
||||
expected: NOTRUN
|
||||
|
|
|
@ -5,9 +5,6 @@
|
|||
[private to local: failure.]
|
||||
expected: FAIL
|
||||
|
||||
[private to private: success.]
|
||||
expected: FAIL
|
||||
|
||||
[public to local: failure.]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
[consecutive-srcdoc.html]
|
||||
expected: TIMEOUT
|
||||
[changing srcdoc does a replace navigation since the URL is still about:srcdoc]
|
||||
expected: FAIL
|
||||
|
||||
[changing srcdoc to about:srcdoc#yo then another srcdoc does two push navigations and we can navigate back]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
[failure-check-sequence.https.html]
|
||||
expected: TIMEOUT
|
||||
[CSP check precedes COEP check - CSP header first]
|
||||
expected: NOTRUN
|
||||
expected: FAIL
|
||||
|
||||
[COEP check precedes X-Frame-Options check]
|
||||
expected: NOTRUN
|
||||
expected: FAIL
|
||||
|
||||
[CSP check precedes COEP check - COEP header first]
|
||||
expected: NOTRUN
|
||||
expected: FAIL
|
||||
|
||||
[CSP check precedes X-Frame-Options check]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
[iframe-src-aboutblank-navigate-immediately.html]
|
||||
expected: TIMEOUT
|
||||
[Navigating to a different document with window.open]
|
||||
expected: FAIL
|
||||
|
||||
[Navigating to a different document with link click]
|
||||
expected: FAIL
|
||||
expected: TIMEOUT
|
||||
|
||||
[Navigating to a different document with form submission]
|
||||
expected: FAIL
|
||||
expected: NOTRUN
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
[navigate-to-unparseable-url.html]
|
||||
[location.href setter throws a SyntaxError DOMException for unparseable URLs]
|
||||
expected: FAIL
|
||||
|
||||
[<a> tag navigate fails for unparseable URLs]
|
||||
expected: FAIL
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[navigation-unload-cross-origin.sub.window.html]
|
||||
[Cross-origin navigation started from unload handler must be ignored]
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[navigation-unload-same-origin-fragment.html]
|
||||
[Tests that a fragment navigation in the unload handler will not block the initial navigation]
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[navigation-unload-same-origin.window.html]
|
||||
[Same-origin navigation started from unload handler must be ignored]
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[a-click.html]
|
||||
[aElement.click() before the load event must NOT replace]
|
||||
expected: FAIL
|
|
@ -1,4 +0,0 @@
|
|||
[traverse-during-unload.html]
|
||||
expected: TIMEOUT
|
||||
[Traversing the history during unload]
|
||||
expected: TIMEOUT
|
|
@ -1,3 +0,0 @@
|
|||
[navigate-to-about-blank-while-initial-load-pending.html]
|
||||
[Navigating to about:blank while window.open initial load pending.]
|
||||
expected: FAIL
|
|
@ -1,7 +1,6 @@
|
|||
[update-the-rendering.html]
|
||||
expected: TIMEOUT
|
||||
["Flush autofocus candidates" should be happen after the first animation frame callbacks, and before a resize event in the next iteration of window event loop.]
|
||||
expected: TIMEOUT
|
||||
|
||||
["Flush autofocus candidates" should be happen before a scroll event and animation frame callbacks]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
|
|
@ -18,3 +18,41 @@
|
|||
[redirected to cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclear pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
||||
[cross-origin SVGImageElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
|
||||
expected: TIMEOUT
|
||||
|
||||
[cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
||||
[redirected to cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
||||
[redirected to same-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
||||
[unclean HTMLCanvasElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
||||
[unclean ImageBitmap: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
||||
[cross-origin HTMLImageElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
||||
[cross-origin SVGImageElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
||||
[cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
||||
[redirected to cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
||||
[redirected to same-origin HTMLVideoElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
||||
[unclean HTMLCanvasElement: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
||||
[unclean ImageBitmap: Setting fillStyle to an origin-unclean offscreen canvas pattern makes the canvas origin-unclean]
|
||||
expected: NOTRUN
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
[iframe_sandbox_popups_nonescaping-2.html]
|
||||
type: testharness
|
||||
expected: TIMEOUT
|
||||
[Check that popups from a sandboxed iframe do not escape the sandbox]
|
||||
expected: NOTRUN
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
[reparent-form-during-planned-navigation-task.html]
|
||||
expected: TIMEOUT
|
||||
[reparent-form-during-planned-navigation-task]
|
||||
expected: TIMEOUT
|
|
@ -0,0 +1,9 @@
|
|||
[pattern_attribute_v_flag.html]
|
||||
[<input pattern> supports set difference syntax]
|
||||
expected: FAIL
|
||||
|
||||
[<input pattern> supports string literal syntax]
|
||||
expected: FAIL
|
||||
|
||||
[<input pattern> enables the RegExp v flag]
|
||||
expected: FAIL
|
|
@ -7,3 +7,6 @@
|
|||
|
||||
[mutually exclusive details across multiple names and multiple tree scopes]
|
||||
expected: FAIL
|
||||
|
||||
[mutation event and toggle event order matches order of insertion in set of named elements]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[DOMContentLoaded-defer.html]
|
||||
[The end: DOMContentLoaded and defer scripts]
|
||||
expected: FAIL
|
|
@ -3,6 +3,3 @@
|
|||
expected: TIMEOUT
|
||||
[The incumbent settings object while executing the compiled callback via Web IDL's invoke must be that of the node document]
|
||||
expected: TIMEOUT
|
||||
|
||||
[The entry settings object while executing the compiled callback via Web IDL's invoke must be that of the node document]
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
[promise-job-entry-different-function-realm.html]
|
||||
expected: TIMEOUT
|
||||
[Fulfillment handler on fulfilled promise]
|
||||
expected: FAIL
|
||||
|
||||
[Rejection handler on pending-then-rejected promise]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
[Thenable resolution]
|
||||
expected: FAIL
|
||||
|
@ -13,4 +12,4 @@
|
|||
expected: FAIL
|
||||
|
||||
[Fulfillment handler on pending-then-fulfilled promise]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
[promise-job-entry.html]
|
||||
expected: TIMEOUT
|
||||
[Fulfillment handler on fulfilled promise]
|
||||
expected: FAIL
|
||||
|
||||
|
@ -7,7 +6,7 @@
|
|||
expected: FAIL
|
||||
|
||||
[Sanity check: this all works as expected with no promises involved]
|
||||
expected: TIMEOUT
|
||||
expected: FAIL
|
||||
|
||||
[Thenable resolution]
|
||||
expected: FAIL
|
||||
|
|
171
tests/wpt/metadata/streams/readable-streams/from.any.js.ini
Normal file
171
tests/wpt/metadata/streams/readable-streams/from.any.js.ini
Normal file
|
@ -0,0 +1,171 @@
|
|||
[from.any.serviceworker.html]
|
||||
expected: ERROR
|
||||
|
||||
[from.any.html]
|
||||
[ReadableStream.from accepts an array of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a string]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream async iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@iterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@asyncIterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from ignores @@iterator if @@asyncIterator exists]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an empty iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream errors when next() rejects]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream stalls when next() never settles]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: calls next() after first read()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancelling the returned stream calls and awaits return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: return() is not called when iterator completes normally]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancel() rejects when return() fulfills with a non-object]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.read() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from(array), push() to array while reading]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[from.any.worker.html]
|
||||
[ReadableStream.from accepts an array of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an array iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a string]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a Set iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async generator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of values]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a sync iterable of promises]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an async iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts a ReadableStream async iterator]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@iterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from re-throws errors from calling the @@asyncIterator method]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from ignores @@iterator if @@asyncIterator exists]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from accepts an empty iterable]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream errors when next() rejects]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: stream stalls when next() never settles]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: calls next() after first read()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancelling the returned stream calls and awaits return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: return() is not called when iterator completes normally]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: cancel() rejects when return() fulfills with a non-object]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.read() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside next()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from: reader.cancel() inside return()]
|
||||
expected: FAIL
|
||||
|
||||
[ReadableStream.from(array), push() to array while reading]
|
||||
expected: FAIL
|
||||
|
||||
|
||||
[from.any.sharedworker.html]
|
||||
expected: ERROR
|
|
@ -1,5 +1,4 @@
|
|||
[audiocontext-not-fully-active.html]
|
||||
expected: TIMEOUT
|
||||
[frame in navigated remote-site frame]
|
||||
expected: FAIL
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[audioworklet-denormals.https.window.html]
|
||||
[Test denormal behavior in AudioWorkletGlobalScope]
|
||||
expected: FAIL
|
4
tests/wpt/metadata/webmessaging/with-ports/017.html.ini
Normal file
4
tests/wpt/metadata/webmessaging/with-ports/017.html.ini
Normal file
|
@ -0,0 +1,4 @@
|
|||
[017.html]
|
||||
expected: TIMEOUT
|
||||
[origin of the script that invoked the method, about:blank]
|
||||
expected: TIMEOUT
|
4
tests/wpt/metadata/webmessaging/with-ports/018.html.ini
Normal file
4
tests/wpt/metadata/webmessaging/with-ports/018.html.ini
Normal file
|
@ -0,0 +1,4 @@
|
|||
[018.html]
|
||||
expected: TIMEOUT
|
||||
[origin of the script that invoked the method, javascript:]
|
||||
expected: TIMEOUT
|
|
@ -0,0 +1,4 @@
|
|||
[017.html]
|
||||
expected: TIMEOUT
|
||||
[origin of the script that invoked the method, about:blank]
|
||||
expected: TIMEOUT
|
|
@ -0,0 +1,4 @@
|
|||
[018.html]
|
||||
expected: TIMEOUT
|
||||
[origin of the script that invoked the method, javascript:]
|
||||
expected: TIMEOUT
|
|
@ -0,0 +1,4 @@
|
|||
[localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.tentative.html]
|
||||
expected: TIMEOUT
|
||||
[StorageKey: test 3P about:blank window opened from a 3P iframe]
|
||||
expected: TIMEOUT
|
|
@ -0,0 +1,2 @@
|
|||
[Worker-constructor.html]
|
||||
expected: ERROR
|
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html>
|
||||
<div>
|
||||
<div role="combobox">
|
||||
<input id="textfield" role="textbox" aria-hidden="true" tabindex="-1" aria-readonly="true" aria-label="foo" aria-autocomplete="both">
|
||||
<div role="listbox">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,18 @@
|
|||
<!doctype HTML>
|
||||
<style>
|
||||
article::after {
|
||||
content: url(data:text/plain,test);}
|
||||
</style>
|
||||
This test passes if it does not crash.
|
||||
<img usemap="#map2">
|
||||
<map id=map name="map2">
|
||||
<command id=command>
|
||||
<article id=article ></article>
|
||||
</command>
|
||||
</map>
|
||||
<script>
|
||||
command.appendChild(article);
|
||||
requestAnimationFrame(() => requestAnimationFrame(() => {
|
||||
map.style.content = "none";
|
||||
}));
|
||||
</script>
|
|
@ -0,0 +1,51 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<meta name=timeout content=long>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/attribution-reporting/resources/helpers.js"></script>
|
||||
<script>
|
||||
attribution_reporting_promise_test(async t => {
|
||||
const host = 'https://{{host}}';
|
||||
|
||||
const expectedSourceEventId = generateSourceEventId();
|
||||
const expectedSourceDebugKey = '456';
|
||||
const expectedTriggerDebugKey = '654';
|
||||
|
||||
registerAttributionSrcByImg(createRedirectChain([
|
||||
{
|
||||
cookie: attributionDebugCookie,
|
||||
source: {
|
||||
aggregation_keys: {
|
||||
campaignCounts: '0x159',
|
||||
},
|
||||
debug_key: expectedSourceDebugKey,
|
||||
destination: host,
|
||||
source_event_id: expectedSourceEventId,
|
||||
},
|
||||
},
|
||||
{
|
||||
trigger : {
|
||||
aggregatable_values: {
|
||||
geoValue: 32768,
|
||||
},
|
||||
debug_key: expectedTriggerDebugKey,
|
||||
debug_reporting: true,
|
||||
},
|
||||
},
|
||||
]));
|
||||
|
||||
const debugPayload = await pollVerboseDebugReports();
|
||||
assert_equals(debugPayload.reports.length, 1);
|
||||
const debugReport = JSON.parse(debugPayload.reports[0].body);
|
||||
assert_equals(debugReport.length, 1);
|
||||
assert_equals(debugReport[0].type, 'trigger-aggregate-no-contributions');
|
||||
assert_own_property(debugReport[0], 'body');
|
||||
const debugReportBody = debugReport[0].body;
|
||||
assert_equals(debugReportBody.attribution_destination, host);
|
||||
assert_equals(debugReportBody.source_event_id, expectedSourceEventId);
|
||||
assert_equals(debugReportBody.source_site, host);
|
||||
assert_equals(debugReportBody.source_debug_key, expectedSourceDebugKey);
|
||||
assert_equals(debugReportBody.trigger_debug_key, expectedTriggerDebugKey);
|
||||
}, 'Aggregatable report is not created due to no contributions.');
|
||||
</script>
|
|
@ -0,0 +1,66 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<meta name=timeout content=long>
|
||||
<meta name=variant content="?method=a&expected-eligible=navigation-source">
|
||||
<meta name=variant content="?method=img&expected-eligible=event-source, trigger">
|
||||
<meta name=variant content="?method=img&eligible&expected-eligible=event-source, trigger">
|
||||
<meta name=variant content="?method=open&expected-eligible=navigation-source">
|
||||
<meta name=variant content="?method=script&expected-eligible=event-source, trigger">
|
||||
<meta name=variant content="?method=script&eligible&expected-eligible=event-source, trigger">
|
||||
<meta name=variant content="?method=fetch">
|
||||
<meta name=variant content='?method=fetch&eligible={"eventSourceEligible":true,"triggerEligible":false}&expected-eligible=event-source'>
|
||||
<meta name=variant content="?method=xhr">
|
||||
<meta name=variant content='?method=xhr&eligible={"eventSourceEligible":true,"triggerEligible":false}&expected-eligible=event-source'>
|
||||
<script src="/resources/testdriver.js"></script>
|
||||
<script src="/resources/testdriver-vendor.js"></script>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="resources/helpers.js"></script>
|
||||
<body>
|
||||
<script>
|
||||
const waitForRequest = async () => {
|
||||
const url = blankURL();
|
||||
url.searchParams.set('get-requests', 'true');
|
||||
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const resp = await fetch(url);
|
||||
const payload = await resp.json();
|
||||
if (payload !== null && payload.length > 0) {
|
||||
return payload;
|
||||
}
|
||||
await delay(100);
|
||||
}
|
||||
throw new Error('Timeout polling requests');
|
||||
};
|
||||
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const expected_eligible =
|
||||
searchParams.get('expected-eligible') === null ? undefined : searchParams.get('expected-eligible');
|
||||
|
||||
promise_test(async t => {
|
||||
// Set mixed-case query params to ensure that they are propagated correctly.
|
||||
await registerAttributionSrc({
|
||||
method: 'variant',
|
||||
extraQueryParams: {'aB': 'Cd', 'store-request': 'true'},
|
||||
});
|
||||
|
||||
const requests = await waitForRequest();
|
||||
assert_equals(requests.length, 1);
|
||||
assert_equals(requests[0].method, 'GET');
|
||||
// TODO(apaseltiner): Check header values once WPT can parse structured dictionaries.
|
||||
if (expected_eligible) {
|
||||
assert_own_property(requests[0], 'attribution-reporting-eligible');
|
||||
} else {
|
||||
assert_not_own_property(requests[0], 'attribution-reporting-eligible');
|
||||
}
|
||||
assert_equals(requests[0].referer, location.toString());
|
||||
|
||||
// TODO(apaseltiner): Test various referrer policies.
|
||||
// TODO(apaseltiner): Test cookie propagation.
|
||||
|
||||
const expectedURL = blankURL();
|
||||
expectedURL.searchParams.set('aB', 'Cd');
|
||||
expectedURL.searchParams.set('store-request', 'true');
|
||||
assert_equals(requests[0].url, expectedURL.toString());
|
||||
}, 'attributionsrc request has the proper format.');
|
||||
</script>
|
|
@ -0,0 +1,360 @@
|
|||
/**
|
||||
* Helper functions for attribution reporting API tests.
|
||||
*/
|
||||
|
||||
const blankURL = (base = location.origin) => new URL('/attribution-reporting/resources/reporting_origin.py', base);
|
||||
|
||||
const attribution_reporting_promise_test = (f, name) =>
|
||||
promise_test(async t => {
|
||||
await resetWptServer();
|
||||
return f(t);
|
||||
}, name);
|
||||
|
||||
const resetWptServer = () =>
|
||||
Promise
|
||||
.all([
|
||||
resetAttributionReports(eventLevelReportsUrl),
|
||||
resetAttributionReports(aggregatableReportsUrl),
|
||||
resetAttributionReports(eventLevelDebugReportsUrl),
|
||||
resetAttributionReports(aggregatableDebugReportsUrl),
|
||||
resetAttributionReports(verboseDebugReportsUrl),
|
||||
resetRegisteredSources(),
|
||||
]);
|
||||
|
||||
const eventLevelReportsUrl =
|
||||
'/.well-known/attribution-reporting/report-event-attribution';
|
||||
const eventLevelDebugReportsUrl =
|
||||
'/.well-known/attribution-reporting/debug/report-event-attribution';
|
||||
const aggregatableReportsUrl =
|
||||
'/.well-known/attribution-reporting/report-aggregate-attribution';
|
||||
const aggregatableDebugReportsUrl =
|
||||
'/.well-known/attribution-reporting/debug/report-aggregate-attribution';
|
||||
const verboseDebugReportsUrl =
|
||||
'/.well-known/attribution-reporting/debug/verbose';
|
||||
|
||||
const attributionDebugCookie = 'ar_debug=1;Secure;HttpOnly;SameSite=None;Path=/';
|
||||
|
||||
const pipeHeaderPattern = /[,)]/g;
|
||||
|
||||
// , and ) in pipe values must be escaped with \
|
||||
const encodeForPipe = urlString => urlString.replace(pipeHeaderPattern, '\\$&');
|
||||
|
||||
const blankURLWithHeaders = (headers, origin, status) => {
|
||||
const url = blankURL(origin);
|
||||
|
||||
const parts = headers.map(h => `header(${h.name},${encodeForPipe(h.value)})`);
|
||||
|
||||
if (status !== undefined) {
|
||||
parts.push(`status(${encodeForPipe(status)})`);
|
||||
}
|
||||
|
||||
if (parts.length > 0) {
|
||||
url.searchParams.set('pipe', parts.join('|'));
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the source registration stash.
|
||||
*/
|
||||
const resetRegisteredSources = () => {
|
||||
return fetch(`${blankURL()}?clear-stash=true`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to clear the stash. Takes the URL as parameter. This could be for
|
||||
* event-level or aggregatable reports.
|
||||
*/
|
||||
const resetAttributionReports = url => {
|
||||
// The view of the stash is path-specific (https://web-platform-tests.org/tools/wptserve/docs/stash.html),
|
||||
// therefore the origin doesn't need to be specified.
|
||||
url = `${url}?clear_stash=true`;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
};
|
||||
return fetch(url, options);
|
||||
};
|
||||
|
||||
const redirectReportsTo = origin => {
|
||||
return Promise.all([
|
||||
fetch(`${eventLevelReportsUrl}?redirect_to=${origin}`, {method: 'POST'}),
|
||||
fetch(`${aggregatableReportsUrl}?redirect_to=${origin}`, {method: 'POST'})
|
||||
]);
|
||||
};
|
||||
|
||||
const getFetchParams = (origin, cookie) => {
|
||||
let credentials;
|
||||
const headers = [];
|
||||
|
||||
if (!origin || origin === location.origin) {
|
||||
return {credentials, headers};
|
||||
}
|
||||
|
||||
// https://fetch.spec.whatwg.org/#http-cors-protocol
|
||||
|
||||
const allowOriginHeader = 'Access-Control-Allow-Origin';
|
||||
const allowHeadersHeader = 'Access-Control-Allow-Headers';
|
||||
|
||||
if (cookie) {
|
||||
credentials = 'include';
|
||||
headers.push({
|
||||
name: 'Access-Control-Allow-Credentials',
|
||||
value: 'true',
|
||||
});
|
||||
headers.push({
|
||||
name: allowOriginHeader,
|
||||
value: `${location.origin}`,
|
||||
});
|
||||
} else {
|
||||
headers.push({
|
||||
name: allowOriginHeader,
|
||||
value: '*',
|
||||
});
|
||||
headers.push({
|
||||
name: allowHeadersHeader,
|
||||
value: '*',
|
||||
})
|
||||
}
|
||||
return {credentials, headers};
|
||||
};
|
||||
|
||||
const getDefaultReportingOrigin = () => {
|
||||
// cross-origin means that the reporting origin differs from the source/destination origin.
|
||||
const crossOrigin = new URLSearchParams(location.search).get('cross-origin');
|
||||
return crossOrigin === null ? location.origin : get_host_info().HTTPS_REMOTE_ORIGIN;
|
||||
};
|
||||
|
||||
const createRedirectChain = (redirects) => {
|
||||
let redirectTo;
|
||||
|
||||
for (let i = redirects.length - 1; i >= 0; i--) {
|
||||
const {source, trigger, cookie, reportingOrigin} = redirects[i];
|
||||
const headers = [];
|
||||
|
||||
if (source) {
|
||||
headers.push({
|
||||
name: 'Attribution-Reporting-Register-Source',
|
||||
value: JSON.stringify(source),
|
||||
});
|
||||
}
|
||||
|
||||
if (trigger) {
|
||||
headers.push({
|
||||
name: 'Attribution-Reporting-Register-Trigger',
|
||||
value: JSON.stringify(trigger),
|
||||
});
|
||||
}
|
||||
|
||||
if (cookie) {
|
||||
headers.push({name: 'Set-Cookie', value: cookie});
|
||||
}
|
||||
|
||||
let status;
|
||||
if (redirectTo) {
|
||||
headers.push({name: 'Location', value: redirectTo.toString()});
|
||||
status = '302';
|
||||
}
|
||||
|
||||
redirectTo = blankURLWithHeaders(
|
||||
headers, reportingOrigin || getDefaultReportingOrigin(), status);
|
||||
}
|
||||
|
||||
return redirectTo;
|
||||
};
|
||||
|
||||
const registerAttributionSrcByImg = (attributionSrc) => {
|
||||
const element = document.createElement('img');
|
||||
element.attributionSrc = attributionSrc;
|
||||
};
|
||||
|
||||
const registerAttributionSrc = async ({
|
||||
source,
|
||||
trigger,
|
||||
cookie,
|
||||
method = 'img',
|
||||
extraQueryParams = {},
|
||||
reportingOrigin,
|
||||
}) => {
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
|
||||
if (method === 'variant') {
|
||||
method = searchParams.get('method');
|
||||
}
|
||||
|
||||
const eligible = searchParams.get('eligible');
|
||||
|
||||
let headers = [];
|
||||
|
||||
if (source) {
|
||||
headers.push({
|
||||
name: 'Attribution-Reporting-Register-Source',
|
||||
value: JSON.stringify(source),
|
||||
});
|
||||
}
|
||||
|
||||
if (trigger) {
|
||||
headers.push({
|
||||
name: 'Attribution-Reporting-Register-Trigger',
|
||||
value: JSON.stringify(trigger),
|
||||
});
|
||||
}
|
||||
|
||||
if (cookie) {
|
||||
const name = 'Set-Cookie';
|
||||
headers.push({name, value: cookie});
|
||||
}
|
||||
|
||||
|
||||
let credentials;
|
||||
if (method === 'fetch') {
|
||||
const params = getFetchParams(reportingOrigin, cookie);
|
||||
credentials = params.credentials;
|
||||
headers = headers.concat(params.headers);
|
||||
}
|
||||
|
||||
const url = blankURLWithHeaders(headers, reportingOrigin);
|
||||
|
||||
Object.entries(extraQueryParams)
|
||||
.forEach(([key, value]) => url.searchParams.set(key, value));
|
||||
|
||||
switch (method) {
|
||||
case 'img':
|
||||
const img = document.createElement('img');
|
||||
if (eligible === null) {
|
||||
img.attributionSrc = url;
|
||||
} else {
|
||||
await new Promise(resolve => {
|
||||
img.onload = resolve;
|
||||
// Since the resource being fetched isn't a valid image, onerror will
|
||||
// be fired, but the browser will still process the
|
||||
// attribution-related headers, so resolve the promise instead of
|
||||
// rejecting.
|
||||
img.onerror = resolve;
|
||||
img.attributionSrc = '';
|
||||
img.src = url;
|
||||
});
|
||||
}
|
||||
return 'event';
|
||||
case 'script':
|
||||
const script = document.createElement('script');
|
||||
if (eligible === null) {
|
||||
script.attributionSrc = url;
|
||||
} else {
|
||||
await new Promise(resolve => {
|
||||
script.onload = resolve;
|
||||
script.attributionSrc = '';
|
||||
script.src = url;
|
||||
document.body.appendChild(script);
|
||||
});
|
||||
}
|
||||
return 'event';
|
||||
case 'a':
|
||||
const a = document.createElement('a');
|
||||
a.target = '_blank';
|
||||
a.textContent = 'link';
|
||||
if (eligible === null) {
|
||||
a.attributionSrc = url;
|
||||
a.href = blankURL();
|
||||
} else {
|
||||
a.attributionSrc = '';
|
||||
a.href = url;
|
||||
}
|
||||
document.body.appendChild(a);
|
||||
await test_driver.click(a);
|
||||
return 'navigation';
|
||||
case 'open':
|
||||
await test_driver.bless('open window', () => {
|
||||
if (eligible === null) {
|
||||
open(
|
||||
blankURL(), '_blank',
|
||||
`attributionsrc=${encodeURIComponent(url)}`);
|
||||
} else {
|
||||
open(url, '_blank', 'attributionsrc');
|
||||
}
|
||||
});
|
||||
return 'navigation';
|
||||
case 'fetch': {
|
||||
let attributionReporting;
|
||||
if (eligible !== null) {
|
||||
attributionReporting = JSON.parse(eligible);
|
||||
}
|
||||
await fetch(url, {credentials, attributionReporting});
|
||||
return 'event';
|
||||
}
|
||||
case 'xhr':
|
||||
await new Promise((resolve, reject) => {
|
||||
const req = new XMLHttpRequest();
|
||||
req.open('GET', url);
|
||||
if (eligible !== null) {
|
||||
req.setAttributionReporting(JSON.parse(eligible));
|
||||
}
|
||||
req.onload = resolve;
|
||||
req.onerror = () => reject(req.statusText);
|
||||
req.send();
|
||||
});
|
||||
return 'event';
|
||||
default:
|
||||
throw `unknown method "${method}"`;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generates a random pseudo-unique source event id.
|
||||
*/
|
||||
const generateSourceEventId = () => {
|
||||
return `${Math.round(Math.random() * 10000000000000)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delay method that waits for prescribed number of milliseconds.
|
||||
*/
|
||||
const delay = ms => new Promise(resolve => step_timeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* Method that polls a particular URL for reports. Once reports
|
||||
* are received, returns the payload as promise. Returns null if the
|
||||
* timeout is reached before a report is available.
|
||||
*/
|
||||
const pollAttributionReports = async (url, origin = location.origin, timeout = 60 * 1000 /*ms*/) => {
|
||||
let startTime = performance.now();
|
||||
while (performance.now() - startTime < timeout) {
|
||||
const resp = await fetch(new URL(url, origin));
|
||||
const payload = await resp.json();
|
||||
if (payload.reports.length > 0) {
|
||||
return payload;
|
||||
}
|
||||
await delay(/*ms=*/ 100);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
// Verbose debug reporting must have been enabled on the source registration for this to work.
|
||||
const waitForSourceToBeRegistered = async (sourceId, reportingOrigin) => {
|
||||
const debugReportPayload = await pollVerboseDebugReports(reportingOrigin);
|
||||
assert_equals(debugReportPayload.reports.length, 1);
|
||||
const debugReport = JSON.parse(debugReportPayload.reports[0].body);
|
||||
assert_equals(debugReport.length, 1);
|
||||
assert_equals(debugReport[0].type, 'source-success');
|
||||
assert_equals(debugReport[0].body.source_event_id, sourceId);
|
||||
};
|
||||
|
||||
const pollEventLevelReports = (origin) =>
|
||||
pollAttributionReports(eventLevelReportsUrl, origin);
|
||||
const pollEventLevelDebugReports = (origin) =>
|
||||
pollAttributionReports(eventLevelDebugReportsUrl, origin);
|
||||
const pollAggregatableReports = (origin) =>
|
||||
pollAttributionReports(aggregatableReportsUrl, origin);
|
||||
const pollAggregatableDebugReports = (origin) =>
|
||||
pollAttributionReports(aggregatableDebugReportsUrl, origin);
|
||||
const pollVerboseDebugReports = (origin) =>
|
||||
pollAttributionReports(verboseDebugReportsUrl, origin);
|
||||
|
||||
const validateReportHeaders = headers => {
|
||||
assert_array_equals(headers['content-type'], ['application/json']);
|
||||
assert_array_equals(headers['cache-control'], ['no-cache']);
|
||||
assert_own_property(headers, 'user-agent');
|
||||
assert_not_own_property(headers, 'cookie');
|
||||
assert_not_own_property(headers, 'referer');
|
||||
};
|
|
@ -0,0 +1,65 @@
|
|||
"""Test reporting origin server used for two reasons:
|
||||
|
||||
1. It is a workaround for lack of preflight support in the test server.
|
||||
2. Stashes requests so they can be inspected by tests.
|
||||
"""
|
||||
|
||||
from wptserve.stash import Stash
|
||||
import json
|
||||
|
||||
REQUESTS = "9250f93f-2c05-4aae-83b9-2817b0e18b4d"
|
||||
|
||||
|
||||
headers = [
|
||||
b"attribution-reporting-eligible",
|
||||
b"attribution-reporting-support",
|
||||
b"referer",
|
||||
]
|
||||
|
||||
|
||||
def store_request(request) -> None:
|
||||
obj = {
|
||||
"method": request.method,
|
||||
"url": request.url,
|
||||
}
|
||||
for header in headers:
|
||||
value = request.headers.get(header)
|
||||
if value is not None:
|
||||
obj[str(header, "utf-8")] = str(value, "utf-8")
|
||||
with request.server.stash.lock:
|
||||
requests = request.server.stash.take(REQUESTS)
|
||||
if not requests:
|
||||
requests = []
|
||||
requests.append(obj)
|
||||
request.server.stash.put(REQUESTS, requests)
|
||||
return None
|
||||
|
||||
|
||||
def get_requests(request) -> str:
|
||||
with request.server.stash.lock:
|
||||
return json.dumps(request.server.stash.take(REQUESTS))
|
||||
|
||||
|
||||
def main(request, response):
|
||||
"""
|
||||
For most requests, simply returns a 200. Actual source/trigger registration
|
||||
headers are piped using the `pipe` query param.
|
||||
|
||||
If a `clear-stash` param is set, it will clear the stash.
|
||||
"""
|
||||
if request.GET.get(b"clear-stash"):
|
||||
request.stash.take(REQUESTS)
|
||||
return
|
||||
|
||||
# We dont want to redirect preflight requests. The cors headers are piped
|
||||
# so we can simply return a 200 and redirect the following request
|
||||
if request.method == "OPTIONS":
|
||||
response.status = 200
|
||||
return
|
||||
|
||||
if request.GET.get(b"get-requests"):
|
||||
return get_requests(request)
|
||||
|
||||
if request.GET.get(b"store-request"):
|
||||
store_request(request)
|
||||
return ""
|
|
@ -0,0 +1,32 @@
|
|||
<!doctype html>
|
||||
<meta charset=utf-8>
|
||||
<meta name=timeout content=long>
|
||||
<script src="/common/get-host-info.sub.js"></script>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/attribution-reporting/resources/helpers.js"></script>
|
||||
<script>
|
||||
attribution_reporting_promise_test(async t => {
|
||||
const expectedTriggerDebugKey = '456';
|
||||
|
||||
registerAttributionSrcByImg(createRedirectChain([
|
||||
{
|
||||
cookie: attributionDebugCookie,
|
||||
trigger: {
|
||||
debug_reporting: true,
|
||||
debug_key: expectedTriggerDebugKey,
|
||||
event_trigger_data: [{}],
|
||||
},
|
||||
},
|
||||
]));
|
||||
|
||||
const payload = await pollVerboseDebugReports();
|
||||
assert_equals(payload.reports.length, 1);
|
||||
const report = JSON.parse(payload.reports[0].body);
|
||||
assert_equals(report.length, 1);
|
||||
assert_equals(report[0].type, 'trigger-no-matching-source');
|
||||
assert_own_property(report[0], 'body');
|
||||
assert_equals(report[0].body.attribution_destination, 'https://{{host}}');
|
||||
assert_equals(report[0].body.trigger_debug_key, expectedTriggerDebugKey);
|
||||
}, 'Verbose debug report is received.');
|
||||
</script>
|
|
@ -9,10 +9,15 @@ function getVideoURI(base)
|
|||
|
||||
var videotag = document.createElement("video");
|
||||
|
||||
if ( videotag.canPlayType &&
|
||||
videotag.canPlayType('video/ogg; codecs="theora, vorbis"') )
|
||||
if ( videotag.canPlayType )
|
||||
{
|
||||
extension = '.ogv';
|
||||
if (videotag.canPlayType('video/webm; codecs="vp9, opus"') )
|
||||
{
|
||||
extension = '.webm';
|
||||
} else if ( videotag.canPlayType('video/ogg; codecs="theora, vorbis"') )
|
||||
{
|
||||
extension = '.ogv';
|
||||
}
|
||||
}
|
||||
|
||||
return base + extension;
|
||||
|
@ -46,10 +51,11 @@ function getAudioURI(base)
|
|||
function getMediaContentType(url) {
|
||||
var extension = new URL(url, location).pathname.split(".").pop();
|
||||
var map = {
|
||||
"mp4": "video/mp4",
|
||||
"ogv": "application/ogg",
|
||||
"mp3": "audio/mp3",
|
||||
"oga": "application/ogg",
|
||||
"mp4" : "video/mp4",
|
||||
"ogv" : "application/ogg",
|
||||
"webm": "video/webm",
|
||||
"mp3" : "audio/mp3",
|
||||
"oga" : "application/ogg",
|
||||
};
|
||||
return map[extension];
|
||||
}
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
<body>
|
||||
|
||||
<script type="module">
|
||||
import {default_request_options,
|
||||
import {request_options_with_mediation_required,
|
||||
fedcm_test} from './support/fedcm-helper.sub.js';
|
||||
|
||||
fedcm_test(async t => {
|
||||
const cred = await navigator.credentials.get(default_request_options());
|
||||
const cred = await navigator.credentials.get(request_options_with_mediation_required());
|
||||
assert_equals(cred.token, 'token');
|
||||
}, 'Test that COEP policy do not apply to FedCM requests');
|
||||
|
||||
|
|
|
@ -7,10 +7,12 @@
|
|||
<body>
|
||||
|
||||
<script type="module">
|
||||
import {default_request_options, fedcm_test, set_fedcm_cookie} from './support/fedcm-helper.sub.js';
|
||||
import {request_options_with_mediation_required,
|
||||
fedcm_test,
|
||||
set_fedcm_cookie} from './support/fedcm-helper.sub.js';
|
||||
|
||||
fedcm_test(async t => {
|
||||
const cred = navigator.credentials.get(default_request_options());
|
||||
const cred = navigator.credentials.get(request_options_with_mediation_required());
|
||||
return promise_rejects_dom(t, "NetworkError", cred);
|
||||
}, "Provider configURL should honor Content-Security-Policy.");
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<title>Federated Credential Management API login hint 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="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
|
||||
<body>
|
||||
|
||||
<script type="module">
|
||||
import {fedcm_test,
|
||||
request_options_with_login_hint,
|
||||
select_manifest} from './support/fedcm-helper.sub.js';
|
||||
|
||||
fedcm_test(async t => {
|
||||
let options = request_options_with_login_hint('manifest.py', 'nomatch');
|
||||
const cred = navigator.credentials.get(options);
|
||||
return promise_rejects_dom(t, "NetworkError", cred);
|
||||
}, "No login hint matches an account.");
|
||||
|
||||
fedcm_test(async t => {
|
||||
let options = request_options_with_login_hint('manifest.py', 'john_doe');
|
||||
const cred = await navigator.credentials.get(options);
|
||||
assert_equals(cred.token, 'token');
|
||||
}, "Login hint matches an account.");
|
||||
|
||||
fedcm_test(async t => {
|
||||
let options = request_options_with_login_hint('manifest_with_two_accounts.json', 'john_doe');
|
||||
await select_manifest(t, options);
|
||||
|
||||
const cred = await navigator.credentials.get(options);
|
||||
assert_equals(cred.token, 'account_id=john_doe');
|
||||
}, "Login hint matches an account from two accounts.");
|
||||
</script>
|
|
@ -8,10 +8,9 @@
|
|||
<body>
|
||||
|
||||
<script type="module">
|
||||
import {alt_manifest_origin,
|
||||
default_request_options,
|
||||
default_alt_request_options,
|
||||
request_options_with_auto_reauthn,
|
||||
import {request_options_with_mediation_required,
|
||||
alt_request_options_with_mediation_required,
|
||||
request_options_with_mediation_optional,
|
||||
fedcm_test,
|
||||
select_manifest,
|
||||
set_fedcm_cookie} from './support/fedcm-helper.sub.js';
|
||||
|
@ -25,24 +24,14 @@ function loadUrlInIframe(url) {
|
|||
});
|
||||
}
|
||||
|
||||
async function createIframeWithPermissionPolicyAndWaitForMessage(test, iframeUrl) {
|
||||
const messageWatcher = new EventWatcher(test, window, "message");
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.src = iframeUrl;
|
||||
iframe.allow = "identity-credentials-get";
|
||||
document.body.appendChild(iframe);
|
||||
const message = await messageWatcher.wait_for("message");
|
||||
return message.data;
|
||||
}
|
||||
|
||||
fedcm_test(async t => {
|
||||
const cred = await navigator.credentials.get(default_request_options());
|
||||
const cred = await navigator.credentials.get(request_options_with_mediation_required());
|
||||
assert_equals(cred.token, "token");
|
||||
}, "Successfully obtaining token should resolve the promise.");
|
||||
|
||||
fedcm_test(async t => {
|
||||
const first = navigator.credentials.get(default_request_options());
|
||||
const second = navigator.credentials.get(default_alt_request_options());
|
||||
const first = navigator.credentials.get(request_options_with_mediation_required());
|
||||
const second = navigator.credentials.get(alt_request_options_with_mediation_required());
|
||||
|
||||
// We have to call promise_rejects_dom here, because if we call it after
|
||||
// the promise gets rejected, the unhandled rejection event handler is called
|
||||
|
@ -57,35 +46,35 @@ fedcm_test(async t => {
|
|||
"When there's a pending request, a second `get` call should be rejected. ");
|
||||
|
||||
fedcm_test(async t => {
|
||||
let test_options = default_request_options();
|
||||
let test_options = request_options_with_mediation_required();
|
||||
test_options.identity.providers = [];
|
||||
const cred = navigator.credentials.get(test_options);
|
||||
return promise_rejects_js(t, TypeError, cred);
|
||||
}, "Reject when provider list is empty");
|
||||
|
||||
fedcm_test(async t => {
|
||||
let test_options = default_request_options();
|
||||
let test_options = request_options_with_mediation_required();
|
||||
delete test_options.identity.providers[0].configURL;
|
||||
const cred = navigator.credentials.get(test_options);
|
||||
return promise_rejects_js(t, TypeError, cred);
|
||||
}, "Reject when configURL is missing" );
|
||||
|
||||
fedcm_test(async t => {
|
||||
let test_options = default_request_options();
|
||||
let test_options = request_options_with_mediation_required();
|
||||
test_options.identity.providers[0].configURL = 'test';
|
||||
const cred = navigator.credentials.get(test_options);
|
||||
return promise_rejects_dom(t, "InvalidStateError", cred);
|
||||
}, "Reject when configURL is invalid");
|
||||
|
||||
fedcm_test(async t => {
|
||||
let test_options = default_request_options();
|
||||
let test_options = request_options_with_mediation_required();
|
||||
test_options.identity.providers[0].clientId = '';
|
||||
const cred = navigator.credentials.get(test_options);
|
||||
return promise_rejects_dom(t, "InvalidStateError", cred);
|
||||
}, "Reject when clientId is empty");
|
||||
|
||||
fedcm_test(async t => {
|
||||
let test_options = default_request_options();
|
||||
let test_options = request_options_with_mediation_required();
|
||||
assert_true("nonce" in test_options.identity.providers[0]);
|
||||
delete test_options.identity.providers[0].nonce;
|
||||
const cred = await navigator.credentials.get(test_options);
|
||||
|
@ -93,7 +82,7 @@ fedcm_test(async t => {
|
|||
}, "nonce is not required in FederatedIdentityProvider.");
|
||||
|
||||
fedcm_test(async t => {
|
||||
let test_options = default_request_options();
|
||||
let test_options = request_options_with_mediation_required();
|
||||
delete test_options.identity.providers[0].clientId;
|
||||
const cred = navigator.credentials.get(test_options);
|
||||
return promise_rejects_js(t, TypeError, cred);
|
||||
|
@ -101,7 +90,7 @@ fedcm_test(async t => {
|
|||
|
||||
fedcm_test(async t => {
|
||||
let controller = new AbortController();
|
||||
let test_options = default_request_options();
|
||||
let test_options = request_options_with_mediation_required();
|
||||
test_options.signal = controller.signal;
|
||||
const cred = navigator.credentials.get(test_options);
|
||||
controller.abort();
|
||||
|
@ -110,24 +99,24 @@ fedcm_test(async t => {
|
|||
|
||||
fedcm_test(async t => {
|
||||
let controller = new AbortController();
|
||||
let test_options = default_request_options();
|
||||
let test_options = request_options_with_mediation_required();
|
||||
test_options.signal = controller.signal;
|
||||
const first_cred = navigator.credentials.get(test_options);
|
||||
controller.abort();
|
||||
await promise_rejects_dom(t, 'AbortError', first_cred);
|
||||
|
||||
const second_cred = await navigator.credentials.get(default_request_options());
|
||||
const second_cred = await navigator.credentials.get(request_options_with_mediation_required());
|
||||
assert_equals(second_cred.token, "token");
|
||||
}, "Get after abort should work");
|
||||
|
||||
fedcm_test(async t => {
|
||||
let test_options = default_request_options('manifest-not-in-list.json');
|
||||
let test_options = request_options_with_mediation_required('manifest-not-in-list.json');
|
||||
const cred = navigator.credentials.get(test_options);
|
||||
return promise_rejects_dom(t, 'NetworkError', cred);
|
||||
}, 'Test that the promise is rejected if the manifest is not in the manifest list');
|
||||
|
||||
fedcm_test(async t => {
|
||||
let test_options = default_request_options("manifest_redirect_accounts.json");
|
||||
let test_options = request_options_with_mediation_required("manifest_redirect_accounts.json");
|
||||
await select_manifest(t, test_options);
|
||||
|
||||
const cred = navigator.credentials.get(test_options);
|
||||
|
@ -137,7 +126,7 @@ fedcm_test(async t => {
|
|||
// legitimate IDP in order to get the list of user accounts.
|
||||
|
||||
fedcm_test(async t => {
|
||||
let test_options = default_request_options("manifest_redirect_token.json");
|
||||
let test_options = request_options_with_mediation_required("manifest_redirect_token.json");
|
||||
await select_manifest(t, test_options);
|
||||
|
||||
const cred = navigator.credentials.get(test_options);
|
||||
|
@ -151,7 +140,7 @@ fedcm_test(async t => {
|
|||
const clear_metadata_count_path = `support/fedcm/client_metadata_clear_count.py`;
|
||||
await fetch(clear_metadata_count_path);
|
||||
|
||||
const cred = await navigator.credentials.get(default_request_options());
|
||||
const cred = await navigator.credentials.get(request_options_with_mediation_required());
|
||||
assert_equals(cred.token, "token");
|
||||
|
||||
await new Promise(resolve => {
|
||||
|
@ -193,7 +182,7 @@ fedcm_test(async t => {
|
|||
assert_equals(query_sw_iframe.contentDocument.body.textContent, "1");
|
||||
|
||||
await set_fedcm_cookie();
|
||||
const cred = await navigator.credentials.get(default_request_options());
|
||||
const cred = await navigator.credentials.get(request_options_with_mediation_required());
|
||||
assert_equals(cred.token, "token");
|
||||
|
||||
// Use cache buster query parameter to avoid cached response.
|
||||
|
@ -202,57 +191,14 @@ fedcm_test(async t => {
|
|||
}, 'Test that service worker cannot observe fetches performed by FedCM API');
|
||||
|
||||
fedcm_test(async t => {
|
||||
const cred = await navigator.credentials.get(default_alt_request_options());
|
||||
assert_equals(cred.token, "token");
|
||||
|
||||
const iframe_in_idp_scope = `${alt_manifest_origin}/\
|
||||
credential-management/support/fedcm/userinfo-iframe.html`;
|
||||
const message = await createIframeWithPermissionPolicyAndWaitForMessage(t, iframe_in_idp_scope);
|
||||
assert_equals(message.result, "Pass");
|
||||
assert_equals(message.numAccounts, 1);
|
||||
assert_equals(message.firstAccountEmail, "john_doe@idp.example");
|
||||
assert_equals(message.firstAccountName, "John Doe");
|
||||
assert_equals(message.firstAccountGivenName, "John");
|
||||
assert_equals(message.firstAccountPicture, "https://idp.example/profile/123");
|
||||
}, 'Test basic User InFo API flow');
|
||||
|
||||
fedcm_test(async t => {
|
||||
const cred = await navigator.credentials.get(default_alt_request_options());
|
||||
assert_equals(cred.token, "token");
|
||||
|
||||
const iframe_in_idp_scope = `support/fedcm/userinfo-iframe.html`;
|
||||
const message = await createIframeWithPermissionPolicyAndWaitForMessage(t, iframe_in_idp_scope);
|
||||
assert_equals(message.result, "Fail");
|
||||
}, 'Test that User Info API only works when invoked from iframe that is same origin as the IDP');
|
||||
|
||||
fedcm_test(async t => {
|
||||
const cred = await navigator.credentials.get(default_alt_request_options());
|
||||
assert_equals(cred.token, "token");
|
||||
|
||||
try {
|
||||
const manifest_path = `${alt_manifest_origin}/\
|
||||
credential-management/support/fedcm/manifest.py`;
|
||||
const user_info = await IdentityProvider.getUserInfo({
|
||||
configURL: manifest_path,
|
||||
// Approved client
|
||||
clientId: '123',
|
||||
});
|
||||
assert_unreached("Failure message");
|
||||
} catch (error) {
|
||||
assert_equals(error.message, "UserInfo request must be initiated from a frame that is the same origin with the provider.");
|
||||
// Expect failure
|
||||
}
|
||||
}, 'Test that User Info API does not work in the top frame');
|
||||
|
||||
fedcm_test(async t => {
|
||||
let test_options = request_options_with_auto_reauthn("manifest_with_single_account.json");
|
||||
let test_options = request_options_with_mediation_optional("manifest_with_single_account.json");
|
||||
await select_manifest(t, test_options);
|
||||
|
||||
// Signs in john_doe so that they will be a returning user
|
||||
let cred = await navigator.credentials.get(test_options);
|
||||
assert_equals(cred.token, "account_id=john_doe");
|
||||
|
||||
test_options = request_options_with_auto_reauthn("manifest_with_two_accounts.json");
|
||||
test_options = request_options_with_mediation_optional("manifest_with_two_accounts.json");
|
||||
await select_manifest(t, test_options);
|
||||
|
||||
// There are two accounts "Jane" and "John" returned in that order. Without
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<title>Federated Credential Management API getUserInfo() tests.</title>
|
||||
<link rel="help" href="https://fedidcg.github.io/FedCM">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<body>
|
||||
|
||||
<script type="module">
|
||||
import {alt_manifest_origin,
|
||||
alt_request_options_with_mediation_required,
|
||||
fedcm_test} from './support/fedcm-helper.sub.js';
|
||||
|
||||
async function createIframeWithPermissionPolicyAndWaitForMessage(test, iframeUrl) {
|
||||
const messageWatcher = new EventWatcher(test, window, "message");
|
||||
let iframe = document.createElement("iframe");
|
||||
iframe.src = iframeUrl;
|
||||
iframe.allow = "identity-credentials-get";
|
||||
document.body.appendChild(iframe);
|
||||
const message = await messageWatcher.wait_for("message");
|
||||
return message.data;
|
||||
}
|
||||
|
||||
fedcm_test(async t => {
|
||||
const cred = await navigator.credentials.get(alt_request_options_with_mediation_required());
|
||||
assert_equals(cred.token, "token");
|
||||
|
||||
const iframe_in_idp_scope = `${alt_manifest_origin}/\
|
||||
credential-management/support/fedcm/userinfo-iframe.html`;
|
||||
const message = await createIframeWithPermissionPolicyAndWaitForMessage(t, iframe_in_idp_scope);
|
||||
assert_equals(message.result, "Pass");
|
||||
assert_equals(message.numAccounts, 1);
|
||||
assert_equals(message.firstAccountEmail, "john_doe@idp.example");
|
||||
assert_equals(message.firstAccountName, "John Doe");
|
||||
assert_equals(message.firstAccountGivenName, "John");
|
||||
assert_equals(message.firstAccountPicture, "https://idp.example/profile/123");
|
||||
}, 'Test basic User InFo API flow');
|
||||
|
||||
fedcm_test(async t => {
|
||||
const cred = await navigator.credentials.get(alt_request_options_with_mediation_required());
|
||||
assert_equals(cred.token, "token");
|
||||
|
||||
const iframe_in_idp_scope = `support/fedcm/userinfo-iframe.html`;
|
||||
const message = await createIframeWithPermissionPolicyAndWaitForMessage(t, iframe_in_idp_scope);
|
||||
assert_equals(message.result, "Fail");
|
||||
}, 'Test that User Info API only works when invoked from iframe that is same origin as the IDP');
|
||||
|
||||
fedcm_test(async t => {
|
||||
const cred = await navigator.credentials.get(alt_request_options_with_mediation_required());
|
||||
assert_equals(cred.token, "token");
|
||||
|
||||
try {
|
||||
const manifest_path = `${alt_manifest_origin}/\
|
||||
credential-management/support/fedcm/manifest.py`;
|
||||
const user_info = await IdentityProvider.getUserInfo({
|
||||
configURL: manifest_path,
|
||||
// Approved client
|
||||
clientId: '123',
|
||||
});
|
||||
assert_unreached("Failure message");
|
||||
} catch (error) {
|
||||
assert_equals(error.message, "UserInfo request must be initiated from a frame that is the same origin with the provider.");
|
||||
// Expect failure
|
||||
}
|
||||
}, 'Test that User Info API does not work in the top frame');
|
||||
|
||||
</script>
|
|
@ -29,7 +29,7 @@ export function set_alt_fedcm_cookie() {
|
|||
|
||||
// Returns FedCM CredentialRequestOptions for which navigator.credentials.get()
|
||||
// succeeds.
|
||||
export function default_request_options(manifest_filename) {
|
||||
export function request_options_with_mediation_required(manifest_filename) {
|
||||
if (manifest_filename === undefined) {
|
||||
manifest_filename = "manifest.py";
|
||||
}
|
||||
|
@ -40,15 +40,16 @@ credential-management/support/fedcm/${manifest_filename}`;
|
|||
providers: [{
|
||||
configURL: manifest_path,
|
||||
clientId: '1',
|
||||
nonce: '2',
|
||||
nonce: '2'
|
||||
}]
|
||||
}
|
||||
},
|
||||
mediation: 'required'
|
||||
};
|
||||
}
|
||||
|
||||
// Returns alternate FedCM CredentialRequestOptions for which navigator.credentials.get()
|
||||
// succeeds.
|
||||
export function default_alt_request_options(manifest_filename) {
|
||||
export function alt_request_options_with_mediation_required(manifest_filename) {
|
||||
if (manifest_filename === undefined) {
|
||||
manifest_filename = "manifest.py";
|
||||
}
|
||||
|
@ -59,19 +60,20 @@ credential-management/support/fedcm/${manifest_filename}`;
|
|||
providers: [{
|
||||
configURL: manifest_path,
|
||||
clientId: '1',
|
||||
nonce: '2',
|
||||
nonce: '2'
|
||||
}]
|
||||
}
|
||||
},
|
||||
mediation: 'required'
|
||||
};
|
||||
}
|
||||
|
||||
// Returns FedCM CredentialRequestOptions with auto re-authentication.
|
||||
// succeeds.
|
||||
export function request_options_with_auto_reauthn(manifest_filename) {
|
||||
let options = default_request_options(manifest_filename);
|
||||
export function request_options_with_mediation_optional(manifest_filename) {
|
||||
let options = alt_request_options_with_mediation_required(manifest_filename);
|
||||
// Approved client
|
||||
options.identity.providers[0].clientId = '123';
|
||||
options.identity.autoReauthn = true;
|
||||
options.mediation = 'optional';
|
||||
|
||||
return options;
|
||||
}
|
||||
|
@ -108,3 +110,10 @@ export function select_manifest(test, test_options) {
|
|||
const manifest_url = test_options.identity.providers[0].configURL;
|
||||
return select_manifest_impl(manifest_url);
|
||||
}
|
||||
|
||||
export function request_options_with_login_hint(manifest_filename, login_hint) {
|
||||
let options = request_options_with_mediation_required(manifest_filename);
|
||||
options.identity.providers[0].loginHint = login_hint;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<!doctype html>
|
||||
<script type="module">
|
||||
import {default_request_options} from './fedcm-helper.sub.js';
|
||||
import {request_options_with_mediation_required} from './fedcm-helper.sub.js';
|
||||
|
||||
// Loading fedcm-iframe.html in the test will make a FedCM call on load, and
|
||||
// trigger a postMessage upon completion.
|
||||
|
@ -13,7 +13,7 @@ import {default_request_options} from './fedcm-helper.sub.js';
|
|||
|
||||
window.onload = async () => {
|
||||
try {
|
||||
const cred = await navigator.credentials.get(default_request_options());
|
||||
const cred = await navigator.credentials.get(request_options_with_mediation_required());
|
||||
window.top.postMessage({result: "Pass", token: cred.token}, '*');
|
||||
} catch (error) {
|
||||
window.top.postMessage({result: "Fail", errorType: error.name}, '*');
|
||||
|
|
|
@ -20,7 +20,8 @@ def main(request, response):
|
|||
"name": "John Doe",
|
||||
"email": "john_doe@idp.example",
|
||||
"picture": "https://idp.example/profile/123",
|
||||
"approved_clients": ["123", "456", "789"]
|
||||
"approved_clients": ["123", "456", "789"],
|
||||
"login_hints": ["john_doe"]
|
||||
}]
|
||||
}
|
||||
"""
|
||||
|
|
|
@ -29,7 +29,8 @@ def main(request, response):
|
|||
"name": "John Doe",
|
||||
"email": "john_doe@idp.example",
|
||||
"picture": "https://idp.example/profile/123",
|
||||
"approved_clients": ["123", "456", "789"]
|
||||
"approved_clients": ["123", "456", "789"],
|
||||
"login_hints": ["john_doe"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
<!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">
|
||||
<head>
|
||||
<title>CSS Reftest Reference</title>
|
||||
<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com"/>
|
||||
<style type="text/css">
|
||||
p.test {
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>There must be no red.</p>
|
||||
<p class="test">This sentence must be green.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -1,24 +0,0 @@
|
|||
<!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" lang="en">
|
||||
<head>
|
||||
<title>CSS Test: Nested blocks in unexpected places</title>
|
||||
<link rel="author" title="Ian Hickson" href="mailto:ian@hixie.ch"/>
|
||||
<link rel="alternate" href="http://www.hixie.ch/tests/adhoc/css/parsing/core-syntax/002.html" type="text/html"/>
|
||||
<link rel="help" href="http://www.w3.org/TR/CSS21/syndata.html#parsing-errors" />
|
||||
<link rel="match" href="core-syntax-002-ref.xht"/>
|
||||
<meta name="flags" content="invalid" />
|
||||
<style type="text/css">
|
||||
.instruct { color: black; background: white; }
|
||||
p {
|
||||
color: green;
|
||||
/* no "}" */
|
||||
span { color: yellow; background: red; }
|
||||
.test { color: maroon; background: yellow; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p class="instruct">There must be no red.</p>
|
||||
<p>This sentence <span>must</span> be <span class="test">green</span>.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -1,15 +0,0 @@
|
|||
<!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" lang="en">
|
||||
<head>
|
||||
<title>CSS Test: Nested blocks in unexpected places (with no whitespace)</title>
|
||||
<link rel="author" title="Ian Hickson" href="mailto:ian@hixie.ch"/>
|
||||
<link rel="alternate" href="http://www.hixie.ch/tests/adhoc/css/parsing/core-syntax/003.html" type="text/html"/>
|
||||
<link rel="help" href="http://www.w3.org/TR/CSS21/syndata.html#parsing-errors" />
|
||||
<link rel="match" href="../reference/ref-this-text-should-be-green.xht"/>
|
||||
<meta name="flags" content="invalid" />
|
||||
<style type="text/css">p{color:green;/*no"}"*/span{color:yellow;background:red;}.test{color:maroon;background:yellow;}}</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>This text <span>should</span> be <span class="test">green</span>.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -1,18 +0,0 @@
|
|||
<!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" lang="en">
|
||||
<head>
|
||||
<title>CSS Test: Nested blocks in unexpected places (with no whitespace)</title>
|
||||
<link rel="author" title="Ian Hickson" href="mailto:ian@hixie.ch"/>
|
||||
<link rel="alternate" href="http://www.hixie.ch/tests/adhoc/css/parsing/core-syntax/002-demo.html" type="text/html"/>
|
||||
<link rel="help" href="http://www.w3.org/TR/CSS21/syndata.html#parsing-errors" />
|
||||
<link rel="match" href="../reference/ref-this-text-should-be-green.xht"/>
|
||||
<meta name="flags" content="invalid" />
|
||||
<style type="text/css">
|
||||
body { color : green ; p{color : red ; }
|
||||
span { color : red ; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>This <span>text</span> should be <span>green</span>.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -17,7 +17,7 @@
|
|||
#p3 {@foo {color: red} color: green}
|
||||
#p4 {12; color: green}
|
||||
#p5 {color: green; 12 color: red}
|
||||
#p6 {color: green; 12 @page {color: red} color: red}
|
||||
#p6 {color: orange; 12 @page {color: red} color: green}
|
||||
#p7 {@foo {color: red}; color: green}
|
||||
</style>
|
||||
</head>
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,71 @@
|
|||
<!DOCTYPE html>
|
||||
<title>Tests automatic anchor positioning without fallbacks</title>
|
||||
<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#anchor-auto">
|
||||
<link rel="auto" 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;
|
||||
}
|
||||
|
||||
#cb {
|
||||
position: absolute;
|
||||
width: 500px;
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#anchor {
|
||||
margin-left: 150px;
|
||||
margin-top: 250px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: orange;
|
||||
anchor-name: --a;
|
||||
}
|
||||
|
||||
.target {
|
||||
position: absolute;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
background: lime;
|
||||
}
|
||||
|
||||
#target1 {
|
||||
top: anchor(--a auto); /* should evaluate to `bottom` */
|
||||
left: anchor(--a auto-same); /* should evaluate to `left` */
|
||||
}
|
||||
|
||||
#target2 {
|
||||
bottom: anchor(--a auto); /* should evaluate to `top` */
|
||||
right: anchor(--a auto-same); /* should evaluate to `right` */
|
||||
}
|
||||
|
||||
#target3 {
|
||||
top: anchor(--a auto-same); /* should evaluate to `top` */
|
||||
left: anchor(--a auto); /* should evaluate to `right` */
|
||||
}
|
||||
|
||||
#target4 {
|
||||
bottom: anchor(--a auto-same); /* should evaluate to `bottom` */
|
||||
right: anchor(--a auto); /* should evaluate to `left` */
|
||||
}
|
||||
</style>
|
||||
|
||||
<body onload="checkLayoutForAnchorPos('.target')">
|
||||
<div id="cb">
|
||||
<div id="anchor"></div>
|
||||
|
||||
<div id="target1" class="target"
|
||||
data-offset-x="150" data-offset-y="350"></div>
|
||||
<div id="target2" class="target"
|
||||
data-offset-x="150" data-offset-y="150"></div>
|
||||
<div id="target3" class="target"
|
||||
data-offset-x="250" data-offset-y="250"></div>
|
||||
<div id="target4" class="target"
|
||||
data-offset-x="50" data-offset-y="250"></div>
|
||||
</div>
|
||||
</body>
|
|
@ -73,10 +73,10 @@ for (const colorSpace of [ "xyz", "xyz-d50", "xyz-d65" ]) {
|
|||
test_valid_value("color", `color(${colorSpace} none none none / 0.5)`, `color(${resultColorSpace} none none none / 0.5)`);
|
||||
test_valid_value("color", `color(${colorSpace} 0 0 0 / none)`, `color(${resultColorSpace} 0 0 0 / none)`);
|
||||
|
||||
test_valid_value("color", `color(${colorSpace} 0 calc(infinity) 0)`, `color(${colorSpace} 0 calc(infinity) 0)`);
|
||||
test_valid_value("color", `color(${colorSpace} 0 calc(-infinity) 0)`, `color(${colorSpace} 0 calc(-infinity) 0)`);
|
||||
test_valid_value("color", `color(${colorSpace} calc(NaN) 0 0)`, `color(${colorSpace} calc(NaN) 0 0)`);
|
||||
test_valid_value("color", `color(${colorSpace} calc(0 / 0) 0 0)`, `color(${colorSpace} calc(NaN) 0 0)`);
|
||||
test_valid_value("color", `color(${colorSpace} 0 calc(infinity) 0)`, `color(${resultColorSpace} 0 calc(infinity) 0)`);
|
||||
test_valid_value("color", `color(${colorSpace} 0 calc(-infinity) 0)`, `color(${resultColorSpace} 0 calc(-infinity) 0)`);
|
||||
test_valid_value("color", `color(${colorSpace} calc(NaN) 0 0)`, `color(${resultColorSpace} calc(NaN) 0 0)`);
|
||||
test_valid_value("color", `color(${colorSpace} calc(0 / 0) 0 0)`, `color(${resultColorSpace} calc(NaN) 0 0)`);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="reftest-wait">
|
||||
<meta charset="utf-8">
|
||||
<title>Container Queries - Size change during transitions crash</title>
|
||||
<script src="/common/reftest-wait.js"></script>
|
||||
<link rel="help" href="https://crbug.com/1451359">
|
||||
<style>
|
||||
#outer {
|
||||
container-type: inline-size;
|
||||
width: 100px;
|
||||
}
|
||||
#inner {
|
||||
background-color: black;
|
||||
transition: background-color 60s;
|
||||
}
|
||||
#inner.target {
|
||||
background-color: white;
|
||||
}
|
||||
@container (width > 200px) {
|
||||
#inner.target {
|
||||
background-color: lime;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<p>Pass if no crash.</p>
|
||||
<div id="outer">
|
||||
<div id="inner">Look at my background</div>
|
||||
</div>
|
||||
<script>
|
||||
inner.offsetTop;
|
||||
inner.className = "target";
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
outer.style.width = "300px";
|
||||
takeScreenshot();
|
||||
});
|
||||
});
|
||||
</script>
|
|
@ -87,13 +87,13 @@ async_test((t) => {
|
|||
end.focus();
|
||||
requestAnimationFrame(step5);
|
||||
}
|
||||
// After blurring the focused element, we should go back to the contained
|
||||
// height of 100px.
|
||||
// After blurring the focused element, we keep the last rendered size, see
|
||||
// https://github.com/w3c/csswg-drafts/issues/8407.
|
||||
function step5() {
|
||||
const r = container.getBoundingClientRect();
|
||||
t.step(() => {
|
||||
assert_equals(r.y, 3000, "step5 offset");
|
||||
assert_equals(r.height, 100, "step5 height");
|
||||
assert_equals(r.height, 10, "step5 height");
|
||||
});
|
||||
t.done();
|
||||
}
|
||||
|
|
|
@ -96,13 +96,13 @@ async_test((t) => {
|
|||
|
||||
requestAnimationFrame(step6);
|
||||
}
|
||||
// After removing the selection we should go back to the contained
|
||||
// height of 100px.
|
||||
// After removing the selection, we keep the last rendered size, see
|
||||
// https://github.com/w3c/csswg-drafts/issues/8407.
|
||||
function step6() {
|
||||
const r = container.getBoundingClientRect();
|
||||
t.step(() => {
|
||||
assert_equals(r.y, 3000, "step5 offset");
|
||||
assert_equals(r.height, 100, "step5 height");
|
||||
assert_equals(r.y, 3000, "step6 offset");
|
||||
assert_equals(r.height, 10, "step6 height");
|
||||
});
|
||||
t.done();
|
||||
}
|
||||
|
|
33
tests/wpt/web-platform-tests/css/css-flexbox/gap-019.html
Normal file
33
tests/wpt/web-platform-tests/css/css-flexbox/gap-019.html
Normal file
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html>
|
||||
<title>Flex gaps</title>
|
||||
<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-flexbox/#intrinsic-sizes">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-align/#gaps">
|
||||
<link rel="match" href="../reference/ref-filled-green-100px-square.xht">
|
||||
<meta name="assert"
|
||||
content="Intrinsic max size of multiline row flex containers includes gaps" />
|
||||
|
||||
<style>
|
||||
.item {
|
||||
flex: 0 0 10px;
|
||||
}
|
||||
|
||||
#reference-overlapped-red {
|
||||
position: absolute;
|
||||
background-color: red;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
z-index: -1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<p>Test passes if there is a filled green square and <strong>no red</strong>.
|
||||
</p>
|
||||
|
||||
<div id=reference-overlapped-red></div>
|
||||
|
||||
<div
|
||||
style="display: flex; column-gap: 80px; background: green; height: 100px; width: max-content; flex-wrap: wrap;">
|
||||
<div class=item></div>
|
||||
<div class=item></div>
|
||||
</div>
|
|
@ -4,7 +4,8 @@
|
|||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resources/check-layout-th.js"></script>
|
||||
<meta name="assert" content="min-content width is calculated correctly in a variety of scenarios with two flex items" />
|
||||
<meta name="assert"
|
||||
content="min-content width is calculated correctly in a variety of scenarios with multiple flex items" />
|
||||
|
||||
<style>
|
||||
.zero-width {
|
||||
|
@ -28,23 +29,27 @@
|
|||
background: orange;
|
||||
}
|
||||
|
||||
.floating-flexbox>div:nth-child(3) {
|
||||
background: lightblue;
|
||||
}
|
||||
|
||||
.floating-flexbox>div>div {
|
||||
width: 100px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body onload="checkLayout('.floating-flexbox')">
|
||||
<div id="log"></div>
|
||||
|
||||
<div class="zero-width">
|
||||
<div class="floating-flexbox" data-expected-width="300">
|
||||
<div class="floating-flexbox" data-expected-width="200">
|
||||
<!-- min contribution: 100 -->
|
||||
<!-- fraction: -0.5 -->
|
||||
<!-- flex base size + product: 200px + -0.5*200px = 100px -->
|
||||
<div style="flex: 1 1 200px; width:50px;">
|
||||
<!-- desired fraction: -0.5 -->
|
||||
<!-- chosen fraction <=0 and desired fraction <=0 and item can shrink, so it contributes its min-content contribution -->
|
||||
<div style="flex: 1 1 200px; width:50px; min-width:0px;">
|
||||
<div></div>
|
||||
</div>
|
||||
<!-- min contribution: 100 -->
|
||||
<!-- fraction: -0.75 -->
|
||||
<!-- flex base size + product: 400px + -0.5*400px = 200px -->
|
||||
<!-- chosen fraction <=0 and desired fraction <=0 and item can shrink, so it contributes its min-content contribution -->
|
||||
<div style="flex: 1 1 400px; width:50px;">
|
||||
<div></div>
|
||||
</div>
|
||||
|
@ -52,10 +57,18 @@
|
|||
</div>
|
||||
|
||||
<div class="zero-width">
|
||||
<div class="floating-flexbox" data-expected-width="225">
|
||||
<div class="floating-flexbox" data-expected-width="200">
|
||||
<!-- min contribution: 100 -->
|
||||
<!-- min contribution - flex basis: -100 -->
|
||||
<!-- desired fraction: -0.5 -->
|
||||
<!-- chosen fraction <=0 and desired fraction <=0 and item can shrink, so it contributes its min-content contribution -->
|
||||
<div style="flex: 1 1 200px; width:50px;">
|
||||
<div></div>
|
||||
</div>
|
||||
<!-- min contribution: 100 -->
|
||||
<!-- min contribution - flex basis: -300 -->
|
||||
<!-- desired fraction: -300/(2*400) = -0.375 -->
|
||||
<!-- chosen fraction <=0 and desired fraction <=0 and item can shrink, so it contributes its min-content contribution -->
|
||||
<div style="flex: 1 2 400px; width:50px;">
|
||||
<div></div>
|
||||
</div>
|
||||
|
@ -65,7 +78,7 @@
|
|||
<!-- This is same as above except for min-width auto is no longer in
|
||||
effect. EdgeHTML renders it differently than the above. -->
|
||||
<div class="zero-width">
|
||||
<div class="floating-flexbox" data-expected-width="225">
|
||||
<div class="floating-flexbox" data-expected-width="200">
|
||||
<div style="flex: 1 1 200px; width:50px; min-width: 0px;">
|
||||
<div></div>
|
||||
</div>
|
||||
|
@ -76,16 +89,17 @@
|
|||
</div>
|
||||
|
||||
<div class="zero-width">
|
||||
<!-- first item contributes 200px. second item contributes 100px -->
|
||||
<div class="floating-flexbox" data-expected-width="300">
|
||||
<!-- min contribution: 100 -->
|
||||
<!-- fraction: -inf -->
|
||||
<!-- flex base size + product: 200px + -0.75*0px = 200px -->
|
||||
<!-- desired fraction: -inf -->
|
||||
<!-- chosen fraction <= 0 and item can't shrink, so contribute flex basis -->
|
||||
<div style="flex: 1 0 200px; width:50px;">
|
||||
<div></div>
|
||||
</div>
|
||||
<!-- min contribution: 100 -->
|
||||
<!-- fraction: -0.75 -->
|
||||
<!-- flex base size + product: 400px + -0.75*400px = 100px -->
|
||||
<!-- desired fraction: -0.75 -->
|
||||
<!-- chosen fraction <= 0 and item CAN shrink, so contribute min contribution -->
|
||||
<div style="flex: 1 1 400px; width:50px;">
|
||||
<div></div>
|
||||
</div>
|
||||
|
@ -94,9 +108,15 @@
|
|||
|
||||
<div class="zero-width">
|
||||
<div class="floating-flexbox" data-expected-width="200">
|
||||
<!-- min contribution: 200 -->
|
||||
<!-- min contribution - flex basis: 150 -->
|
||||
<!-- desired fraction: 0 (because flex grow factor is 0) -->
|
||||
<!-- chosen fraction <= 0 so contribute flex basis 50px -->
|
||||
<!-- except flex basis is outside of used min/max, so contribute clamped flex basis = 100px -->
|
||||
<div style="flex: 0 0 50px; width: 200px;">
|
||||
<div></div>
|
||||
</div>
|
||||
<!-- identical to above -->
|
||||
<div style="flex: 0 0 50px; width: 200px;">
|
||||
<div></div>
|
||||
</div>
|
||||
|
@ -106,14 +126,14 @@
|
|||
<div class="zero-width">
|
||||
<!-- 200 + 400 = 600 -->
|
||||
<div class="floating-flexbox" data-expected-width="600">
|
||||
<!-- contribution: 200 -->
|
||||
<!-- fraction: 150 -->
|
||||
<!-- min contribution: 200 -->
|
||||
<!-- desired fraction: 150px -->
|
||||
<!-- 50 + 1*150 = 200 -->
|
||||
<div style="flex: 1 0 50px; width: 200px;">
|
||||
<div></div>
|
||||
</div>
|
||||
<!-- contribution: 200 -->
|
||||
<!-- fraction: 100 -->
|
||||
<!-- min contribution: 200 -->
|
||||
<!-- desired fraction: 100px -->
|
||||
<!-- 100 + 2*150 = 400 -->
|
||||
<div style="flex: 2 0 100px; width: 200px;">
|
||||
<div></div>
|
||||
|
@ -122,19 +142,60 @@
|
|||
</div>
|
||||
|
||||
<div class="zero-width">
|
||||
<!-- chosen fraction: 50px -->
|
||||
<div class="floating-flexbox" data-expected-width="400">
|
||||
<!-- min contribution: 100 -->
|
||||
<!-- fraction: -0.5 -->
|
||||
<!-- flex base size + product: = 200px + 50px*0 = 200px -->
|
||||
<!-- desired fraction: -0.5 -->
|
||||
<!-- final contribution = flex base size + product = 200px + 50px*0 = 200px -->
|
||||
<div style="flex: 0 1 200px; width: 50px;">
|
||||
<div></div>
|
||||
</div>
|
||||
<!-- min contribution: 200 -->
|
||||
<!-- fraction: (200px - 100px) / 2 = 50px -->
|
||||
<!-- flex base size + product: = 100px + 50px*2 = 200px -->
|
||||
<!-- desired fraction: (200px - 100px) / 2 = 50px -->
|
||||
<!-- final contribution = flex base size + product = 100px + 50px*2 = 200px -->
|
||||
<div style="flex: 2 0 100px; width: 200px;">
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="zero-width">
|
||||
<!-- chosen fraction: 0 -->
|
||||
<div class="floating-flexbox" data-expected-width="600">
|
||||
<!-- min contribution: 250 -->
|
||||
<!-- wants to grow, but can't -->
|
||||
<!-- desired fraction: 0 -->
|
||||
<!-- final contribution: 100 -->
|
||||
<div style="flex: 0 1 100px; width: 250px;"></div>
|
||||
<!-- min contribution: 100 -->
|
||||
<!-- wants to shrink, but can't -->
|
||||
<!-- desired fraction: -inf -->
|
||||
<!-- final contribution: 200 -->
|
||||
<div style="flex: 1 0 200px; width: 100px;"></div>
|
||||
<!-- min contribution: -->
|
||||
<!-- doesn't have to change --->
|
||||
<!-- desired fraction: 0 -->
|
||||
<!-- final contribution: 300 -->
|
||||
<div style="flex: 1 1 300px; width: 300px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="zero-width">
|
||||
<!-- chosen fraction: 0 -->
|
||||
<div class="floating-flexbox" data-expected-width="700">
|
||||
<!-- min contribution: 100 -->
|
||||
<!-- wants to shrink, but can't -->
|
||||
<!-- desired fraction: -inf -->
|
||||
<!-- final contribution: 200 -->
|
||||
<!-- In legacy and V2, this item gets 0 width. -->
|
||||
<div style="flex: 0 10 300px; width: 200px;"></div>
|
||||
<!-- min contribution: -->
|
||||
<!-- doesn't have to change --->
|
||||
<!-- desired fraction: 0 -->
|
||||
<!-- final contribution: 300 -->
|
||||
<!-- In legacy and V2, this item gets all the width. -->
|
||||
<div style="flex: 0 1 1000px; width: 500px;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<link rel="author" title="David Grogan" href="mailto:dgrogan@chromium.org">
|
||||
<link rel="help" href="https://drafts.csswg.org/css-flexbox/#intrinsic-sizes">
|
||||
<link rel="help" href="https://crbug.com/1445937">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resources/check-layout-th.js"></script>
|
||||
<meta name="assert"
|
||||
content="Virtually all of the compat problems reduced to this case" />
|
||||
|
||||
<style>
|
||||
section.bugreport {
|
||||
outline: 1px solid grey;
|
||||
margin-bottom: 25px;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
#gmail .flex {
|
||||
display: flex;
|
||||
width: min-content;
|
||||
outline: 2px solid;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#gmail span.orange100 {
|
||||
float: left;
|
||||
height: 25px;
|
||||
width: 100px;
|
||||
background: orange;
|
||||
}
|
||||
</style>
|
||||
|
||||
<section id="gmail" class="bugreport">
|
||||
<p>https://crbug.com/1445937 We need to see a 100x100 orange square, not a
|
||||
400x25 orange rectangle.</p>
|
||||
<div class="flex" data-expected-width="100">
|
||||
<!-- one item with very negative desired flex fraction -->
|
||||
<div>
|
||||
<span class="orange100"></span>
|
||||
<span class="orange100"></span>
|
||||
<span class="orange100"></span>
|
||||
<span class="orange100"></span>
|
||||
</div>
|
||||
<!-- second item with desired flex fraction 0 -->
|
||||
<div></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
checkLayout('.flex');
|
||||
</script>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue