Sync WPT with upstream (13-06-2025) (#37436)

Automated downstream sync of changes from upstream as of 13-06-2025
[no-wpt-sync]

---------

Signed-off-by: WPT Sync Bot <ghbot+wpt-sync@servo.org>
Signed-off-by: Oriol Brufau <obrufau@igalia.com>
Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
Servo WPT Sync 2025-06-13 16:15:36 +02:00 committed by GitHub
parent e4da496e2d
commit a7e9df0fef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
339 changed files with 7696 additions and 1370 deletions

File diff suppressed because it is too large Load diff

View file

@ -81,3 +81,6 @@
[corner-shape-render-fuzzy.html?corner-shape=superellipse(0.8)&border-radius=40px&border-width=10px]
expected: FAIL
[corner-shape-render-fuzzy.html?border-radius=50%&corner-shape=bevel&box-shadow=10px%2010px%200%2010px%20black]
expected: FAIL

View file

@ -37,3 +37,15 @@
[@font-face matching for quoted and unquoted math]
expected: [FAIL, PASS]
[@font-face matching for quoted and unquoted generic(fangsong)]
expected: FAIL
[@font-face matching for quoted and unquoted generic(kai)]
expected: FAIL
[@font-face matching for quoted and unquoted generic(khmer-mul)]
expected: FAIL
[@font-face matching for quoted and unquoted generic(nastaliq)]
expected: FAIL

View file

@ -0,0 +1,12 @@
[generic-family-keywords-002.html]
[font-family: -webkit-generic(fangsong) treated as <font-family>, not <generic-name>]
expected: FAIL
[font-family: -webkit-generic(kai) treated as <font-family>, not <generic-name>]
expected: FAIL
[font-family: -webkit-generic(khmer-mul) treated as <font-family>, not <generic-name>]
expected: FAIL
[font-family: -webkit-generic(nastaliq) treated as <font-family>, not <generic-name>]
expected: FAIL

View file

@ -0,0 +1,2 @@
[grid-gap-decorations-047.html]
expected: FAIL

View file

@ -1,3 +0,0 @@
[grid-content-alignment-with-abspos-001.html]
[.grid 1]
expected: FAIL

View file

@ -0,0 +1,18 @@
[item-tolerance-computed.html]
[Property item-tolerance value 'normal']
expected: FAIL
[Property item-tolerance value '10px']
expected: FAIL
[Property item-tolerance value '20%']
expected: FAIL
[Property item-tolerance value 'calc(20% + 10px)']
expected: FAIL
[Property item-tolerance value 'calc(-0.5em + 10px)']
expected: FAIL
[Property item-tolerance value 'calc(0.5em + 10px)']
expected: FAIL

View file

@ -0,0 +1,18 @@
[item-tolerance-valid.html]
[e.style['item-tolerance'\] = "normal" should set the property value]
expected: FAIL
[e.style['item-tolerance'\] = "0" should set the property value]
expected: FAIL
[e.style['item-tolerance'\] = "1px" should set the property value]
expected: FAIL
[e.style['item-tolerance'\] = "calc(2em + 3ex)" should set the property value]
expected: FAIL
[e.style['item-tolerance'\] = "4%" should set the property value]
expected: FAIL
[e.style['item-tolerance'\] = "5vmin" should set the property value]
expected: FAIL

View file

@ -1,36 +0,0 @@
[masonry-slack-computed.html]
[Property masonry-slack value 'normal']
expected: FAIL
[Property masonry-slack value '10px']
expected: FAIL
[Property masonry-slack value '20%']
expected: FAIL
[Property masonry-slack value 'calc(20% + 10px)']
expected: FAIL
[Property masonry-slack value 'calc(-0.5em + 10px)']
expected: FAIL
[Property masonry-slack value 'calc(0.5em + 10px)']
expected: FAIL
[Property item-tolerance value 'normal']
expected: FAIL
[Property item-tolerance value '10px']
expected: FAIL
[Property item-tolerance value '20%']
expected: FAIL
[Property item-tolerance value 'calc(20% + 10px)']
expected: FAIL
[Property item-tolerance value 'calc(-0.5em + 10px)']
expected: FAIL
[Property item-tolerance value 'calc(0.5em + 10px)']
expected: FAIL

View file

@ -1,36 +0,0 @@
[masonry-slack-valid.html]
[e.style['masonry-slack'\] = "normal" should set the property value]
expected: FAIL
[e.style['masonry-slack'\] = "0" should set the property value]
expected: FAIL
[e.style['masonry-slack'\] = "1px" should set the property value]
expected: FAIL
[e.style['masonry-slack'\] = "calc(2em + 3ex)" should set the property value]
expected: FAIL
[e.style['masonry-slack'\] = "4%" should set the property value]
expected: FAIL
[e.style['masonry-slack'\] = "5vmin" should set the property value]
expected: FAIL
[e.style['item-tolerance'\] = "normal" should set the property value]
expected: FAIL
[e.style['item-tolerance'\] = "0" should set the property value]
expected: FAIL
[e.style['item-tolerance'\] = "1px" should set the property value]
expected: FAIL
[e.style['item-tolerance'\] = "calc(2em + 3ex)" should set the property value]
expected: FAIL
[e.style['item-tolerance'\] = "4%" should set the property value]
expected: FAIL
[e.style['item-tolerance'\] = "5vmin" should set the property value]
expected: FAIL

View file

@ -142,3 +142,6 @@
[@function --foo(--x:1px, --y, --z:2px) is valid]
expected: FAIL
[@function --foo(--x type(*): 10px) is valid]
expected: FAIL

View file

@ -0,0 +1,2 @@
[line-clamp-021.tentative.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[line-clamp-with-floats-010.tentative.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[margin-block-end-scroll-area-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[position-sticky-left-002.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[position-sticky-top-002.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[first-line-child-display-none-dynamic.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[first-line-child-display-none.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[dynamic-available-size-iframe.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[intrinsic-percent-non-replaced-001.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[intrinsic-percent-non-replaced-004.html]
expected: FAIL

View file

@ -0,0 +1,24 @@
[keyword-sizes-on-replaced-element.html]
[.test 68]
expected: FAIL
[.test 69]
expected: FAIL
[.test 71]
expected: FAIL
[.test 75]
expected: FAIL
[.test 77]
expected: FAIL
[.test 78]
expected: FAIL
[.test 80]
expected: FAIL
[.test 84]
expected: FAIL

View file

@ -0,0 +1,2 @@
[cache-miss-001.html]
expected: FAIL

View file

@ -0,0 +1,2 @@
[cache-miss-002.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[table-cell-overflow-auto-scrolled.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[calc-min-height-block-1.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[calc-min-width-block-intrinsic-1.html]
expected: FAIL

View file

@ -13,3 +13,6 @@
[testing width: calc(20% / 0.5em * 1px)]
expected: FAIL
[testing width: calc(52px * 1px / 10%)]
expected: FAIL

View file

@ -565,3 +565,12 @@
[CSS Values and Units Test: CSS inline if() function 188]
expected: FAIL
[CSS Values and Units Test: CSS inline if() function 189]
expected: FAIL
[CSS Values and Units Test: CSS inline if() function 190]
expected: FAIL
[CSS Values and Units Test: CSS inline if() function 191]
expected: FAIL

View file

@ -0,0 +1,3 @@
[scrollIntoView-scrolling-container.html]
[scrollIntoView on a scrolling container should scroll its outer scrollers into view but not scroll itself.]
expected: FAIL

View file

@ -0,0 +1,6 @@
[font-family-serialization-001.html]
[Serialization of <generic-family>]
expected: FAIL
[Serialization of prefixed -webkit-<generic-family>]
expected: FAIL

View file

@ -0,0 +1,15 @@
[dictionary-compressed.tentative.https.html]
[Decompresion using gzip-encoded dictionary works as expected]
expected: FAIL
[Decompresion using Brotli-encoded dictionary works as expected]
expected: FAIL
[Decompresion using Zstandard-encoded dictionary works as expected]
expected: FAIL
[A dcb dictionary-compressed dictionary can be used as a dictionary for future requests.]
expected: FAIL
[A dcz dictionary-compressed dictionary can be used as a dictionary for future requests.]
expected: FAIL

View file

@ -0,0 +1,3 @@
[dictionary-fetch-no-cors.tentative.https.html]
[Fetch cross-origin no-cors request does not include Available-Dictionary header]
expected: FAIL

View file

@ -7,3 +7,9 @@
[New dictionary registration overrides the existing one]
expected: FAIL
[Dictionary registration does not invalidate cache entry]
expected: FAIL
[Expired dictionary is not used]
expected: FAIL

View file

@ -0,0 +1,3 @@
[focus-sync-when-blur.html]
[Element.focus() in blur listener when focus has moved away]
expected: FAIL

View file

@ -1,6 +0,0 @@
[empty-iframe-load-event.html]
[Check execution order from nested timeout]
expected: FAIL
[Check execution order on load handler]
expected: FAIL

View file

@ -1,3 +1,6 @@
[iframe-src-aboutblank-navigate-immediately.html]
[Navigating to a different document with window.open]
expected: FAIL
[Navigating to a different document with form submission]
expected: FAIL

View file

@ -1,3 +0,0 @@
[same-document-refresh.html]
[Same-Document Referrer from Refresh]
expected: FAIL

View file

@ -1,3 +1,4 @@
[document-base-url-window-initiator-is-not-opener.https.window.html]
expected: TIMEOUT
[window.open() gets base url from initiator not opener.]
expected: [FAIL, PASS, TIMEOUT]

View file

@ -1,4 +1,3 @@
[update-the-rendering.html]
expected: TIMEOUT
["Flush autofocus candidates" should be happen before a scroll event and animation frame callbacks]
expected: TIMEOUT
expected: FAIL

View file

@ -1,4 +1,4 @@
[iframe_sandbox_popups_escaping-2.html]
expected: TIMEOUT
[Check that popups from a sandboxed iframe escape the sandbox if\n allow-popups-to-escape-sandbox is used]
expected: TIMEOUT
expected: FAIL

View file

@ -1,3 +1,4 @@
[iframe_sandbox_popups_nonescaping-2.html]
expected: TIMEOUT
[Check that popups from a sandboxed iframe do not escape the sandbox]
expected: FAIL
expected: NOTRUN

View file

@ -0,0 +1,6 @@
[button-invoke-menulist.html]
[Button with command=toggle-popover can invoke menulist popover.]
expected: FAIL
[Button with command=toggle-menu can invoke menulist popover.]
expected: FAIL

View file

@ -0,0 +1,24 @@
[checkable.html]
[menuitem is not checkable on its own]
expected: FAIL
[menuitem in menubar is not checkable]
expected: FAIL
[menuitem in menulist]
expected: FAIL
[checkable menuitem exclusivity]
expected: FAIL
[checkable multiple]
expected: FAIL
[checkable menuitem exclusivity when disconnected]
expected: FAIL
[when fieldset becomes uncheckable, so do its menuitems]
expected: FAIL
[fieldset multiple => single; all but the first checked menuitem gets reset]
expected: FAIL

View file

@ -0,0 +1,2 @@
[menu-elements-default-style.html]
expected: FAIL

View file

@ -0,0 +1,9 @@
[menubar-invoke-menulist.html]
[Menu elements are HTML elements.]
expected: FAIL
[Menuitem with valid command/commandfor can invoke menulist popover.]
expected: FAIL
[Checkable menuitems can still invoke menulist popovers]
expected: FAIL

View file

@ -0,0 +1,3 @@
[menulist-popover-attribute.html]
[menulist is a popover by default.]
expected: FAIL

View file

@ -7,3 +7,6 @@
[Reload navigationStart > Original navigationStart]
expected: FAIL
[Reload domInteractive > Original domInteractive]
expected: FAIL

View file

@ -62,9 +62,6 @@
[success (style): main]
expected: FAIL
[404 (style): main]
expected: FAIL
[CORS (style): main]
expected: FAIL

View file

@ -13,3 +13,24 @@
[Ensure that ReportingObserver gets called without endpoints]
expected: FAIL
[Ensure that a script with integrity runs]
expected: FAIL
[Ensure that a data URI script with no integrity runs]
expected: FAIL
[Ensure that a no-CORS data URI script with no integrity runs]
expected: FAIL
[Ensure that a blob URL script with no integrity runs]
expected: FAIL
[Ensure that a no-CORS blob URL script with no integrity runs]
expected: FAIL
[Ensure that an about:blank URL script with no integrity does not trigger a report]
expected: FAIL
[Ensure that a no-CORS about:blank URL script with no integrity does not trigger a report]
expected: FAIL

View file

@ -0,0 +1,6 @@
[script-enforcement-010.html]
[Changing script's type from classic to module in the default policy works.]
expected: FAIL
[Changing script's type from module to classic in the default policy works.]
expected: FAIL

View file

@ -0,0 +1,6 @@
[script-enforcement-011.html]
[Changing script's type from classic to module in the default policy works.]
expected: FAIL
[Changing script's type from module to classic in the default policy works.]
expected: FAIL

View file

@ -823,3 +823,12 @@
[X SNR (-105.9343049040375 dB) is not greater than or equal to 65.737. Got -105.9343049040375.]
expected: FAIL
[X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...\] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[14650\]\t2.9399906000000000e+7\t8.6956524848937988e-1\t2.9399905130434752e+7\t3.3809889690864086e+7\t3.8985999999999999e-3\n\t[14651\]\t3.0547976493835449e-1\t8.9879405498504639e-1\t5.9331429004669189e-1\t6.6012262403823208e-1\t3.8985999999999999e-3\n\tMax AbsError of 2.9399905130434752e+7 at index of 14650.\n\tMax RelError of 3.3809889690864086e+7 at index of 14650.\n]
expected: FAIL
[X SNR (-105.93283263603922 dB) is not greater than or equal to 65.737. Got -105.93283263603922.]
expected: FAIL
[X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...\] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}.\n\tIndex\tActual\t\t\tExpected\t\tAbsError\t\tRelError\t\tTest threshold\n\t[14650\]\t3.7113974459152624e-22\t8.6956524848937988e-1\t8.6956524848937988e-1\t1.0000000000000000e+0\t3.8985999999999999e-3\n\t[14651\]\t3.0547976493835449e-1\t8.9879405498504639e-1\t5.9331429004669189e-1\t6.6012262403823208e-1\t3.8985999999999999e-3\n\tMax AbsError of 8.6956524848937988e-1 at index of 14650.\n\tMax RelError of 1.0000000000000000e+0 at index of 14650.\n]
expected: FAIL

View file

@ -20,6 +20,9 @@
[writing a view on a shared buffer should be rejected]
expected: FAIL
[Garbage collecting a WebSocket stream doesn't crash while write promise is pending]
expected: FAIL
[write.any.sharedworker.html?wss]
expected: ERROR
@ -40,6 +43,9 @@
[writing a view on a shared buffer should be rejected]
expected: FAIL
[Garbage collecting a WebSocket stream doesn't crash while write promise is pending]
expected: FAIL
[write.any.worker.html?wss]
[a write that was incomplete at close time should reject]
@ -57,6 +63,9 @@
[writing a view on a shared buffer should be rejected]
expected: FAIL
[Garbage collecting a WebSocket stream doesn't crash while write promise is pending]
expected: FAIL
[write.any.serviceworker.html?wpt_flags=h2]
expected: ERROR
@ -77,6 +86,9 @@
[writing a view on a shared buffer should be rejected]
expected: FAIL
[Garbage collecting a WebSocket stream doesn't crash while write promise is pending]
expected: FAIL
[write.any.serviceworker.html?wss]
expected: ERROR
@ -100,6 +112,9 @@
[writing a view on a shared buffer should be rejected]
expected: FAIL
[Garbage collecting a WebSocket stream doesn't crash while write promise is pending]
expected: FAIL
[write.any.html?wss]
[a write that was incomplete at close time should reject]
@ -116,3 +131,6 @@
[writing a view on a shared buffer should be rejected]
expected: FAIL
[Garbage collecting a WebSocket stream doesn't crash while write promise is pending]
expected: FAIL

View file

@ -27,44 +27,6 @@ variables:
safaridriver_diagnose: false
jobs:
# The affected tests jobs are unconditional for speed, as most PRs have one or
# more affected tests: https://github.com/web-platform-tests/wpt/issues/13936.
- job: affected_safari_preview
displayName: 'affected tests: Safari Technology Preview'
condition: eq(variables['Build.Reason'], 'PullRequest')
pool:
vmImage: 'macOS-14'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.12'
- template: tools/ci/azure/affected_tests.yml
parameters:
artifactName: 'safari-preview-affected-tests'
- template: tools/ci/azure/fyi_hook.yml
parameters:
dependsOn: affected_safari_preview
artifactName: safari-preview-affected-tests
- job: affected_without_changes_safari_preview
displayName: 'affected tests without changes: Safari Technology Preview'
condition: eq(variables['Build.Reason'], 'PullRequest')
pool:
vmImage: 'macOS-14'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.13'
- template: tools/ci/azure/affected_tests.yml
parameters:
checkoutCommit: 'HEAD^1'
affectedRange: 'HEAD@{1}'
artifactName: 'safari-preview-affected-tests-without-changes'
- template: tools/ci/azure/fyi_hook.yml
parameters:
dependsOn: affected_without_changes_safari_preview
artifactName: safari-preview-affected-tests-without-changes
# The decision jobs runs `./wpt test-jobs` to determine which jobs to run,
# and all following jobs wait for it to finish and depend on its output.
- job: decision
@ -87,37 +49,6 @@ jobs:
name: test_jobs
displayName: 'Run ./wpt test-jobs'
- job: infrastructure_mac
displayName: 'infrastructure/ tests: macOS'
dependsOn: decision
condition: dependencies.decision.outputs['test_jobs.wptrunner_infrastructure']
pool:
vmImage: 'macOS-14'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.13'
- template: tools/ci/azure/checkout.yml
- template: tools/ci/azure/install_fonts.yml
- template: tools/ci/azure/install_certs.yml
- template: tools/ci/azure/color_profile.yml
- template: tools/ci/azure/install_safari.yml
- template: tools/ci/azure/update_hosts.yml
- template: tools/ci/azure/update_manifest.yml
- script: |
set -eux -o pipefail
export SYSTEM_VERSION_COMPAT=0
./wpt run --yes --no-manifest-update --manifest MANIFEST.json --metadata infrastructure/metadata/ --log-mach - --log-mach-level info --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_macos_safari.json --channel preview safari infrastructure/
condition: succeededOrFailed()
displayName: 'Run tests (Safari Technology Preview)'
- task: PublishBuildArtifacts@1
condition: succeededOrFailed()
displayName: 'Publish results'
inputs:
artifactName: 'infrastructure-results'
- template: tools/ci/azure/publish_logs.yml
- template: tools/ci/azure/sysdiagnose.yml
- job: tools_unittest_mac_py38
displayName: 'tools/ unittests: macOS + Python 3.8'
dependsOn: decision

View file

@ -1,4 +1,4 @@
name: "test-jobs"
name: test-jobs
on:
pull_request:
@ -25,11 +25,12 @@ jobs:
if: contains(fromJSON(needs.decision.outputs.test_jobs), 'affected_tests')
uses: ./.github/workflows/safari-wptrunner.yml
with:
artifact-name: "safari-preview-affected-tests"
artifact-name: safari-preview-affected-tests-with-changes
merged-artifact-name: safari-preview-affected-tests
safari-technology-preview: true
safaridriver-diagnose: false
fetch-depth: 2
extra-options: "--affected ${{ github.sha }}^1"
extra-options: --affected ${{ github.sha }}^1
affected_without_changes_safari_preview:
name: "affected tests without changes: Safari Technology Preview"
@ -38,11 +39,12 @@ jobs:
uses: ./.github/workflows/safari-wptrunner.yml
with:
artifact-name: safari-preview-affected-tests-without-changes
merged-artifact-name: safari-preview-affected-tests-without-changes
safari-technology-preview: true
safaridriver-diagnose: false
fetch-depth: 2
test-rev: "HEAD^1"
extra-options: "--affected ${{ github.sha }}"
test-rev: HEAD^1
extra-options: --affected ${{ github.sha }}
infrastructure_mac:
name: "infrastructure/ tests: macOS"

View file

@ -4,35 +4,39 @@ on:
workflow_call:
inputs:
artifact-name:
description: "Prefix for the artifact uploaded"
description: Prefix for the artifact uploaded
required: true
type: string
merged-artifact-name:
description: Merge the uploaded artifacts into a singular artifact
required: false
type: string
safari-technology-preview:
description: "Run Safari Technology Preview rather than the system Safari"
description: Run Safari Technology Preview rather than the system Safari
required: true
type: boolean
safaridriver-diagnose:
description: "Run safaridriver capturing diagnostics"
description: Run safaridriver capturing diagnostics
required: true
type: boolean
fetch-ref:
description: "The ref to fetch and initially checkout"
description: The ref to fetch and initially checkout
required: false
type: string
fetch-depth:
description: "The fetch-depth to checkout"
description: The fetch-depth to checkout
required: false
type: number
test-rev:
description: "The rev to checkout before running the tests"
description: The rev to checkout before running the tests
required: false
type: string
matrix-include:
description: "Extra items to include in the matrix, to override test-type/current-chunk/total-chunks"
matrix:
description: Test matrix, to override test-type/current-chunk/total-chunks
required: false
type: string
extra-options:
description: "Extra options to pass to wpt run"
description: Extra options to pass to wpt run
required: false
type: string
@ -41,19 +45,17 @@ on:
permissions: {}
jobs:
safari-results:
name: ${{ matrix.current-chunk }} (of ${{ matrix.total-chunks }})
results:
name: ${{ matrix.current-chunk || 1 }} (of ${{ matrix.total-chunks || 1 }})
env:
CURRENT_CHUNK: ${{ matrix.current-chunk || 1 }}
TOTAL_CHUNKS: ${{ matrix.total-chunks || 1 }}
runs-on:
- self-hosted
- webkit-ews
timeout-minutes: 180
strategy:
matrix:
current-chunk:
- 1
total-chunks:
- 1
include: ${{ fromJSON(inputs.matrix-include || '[]') }}
matrix: "${{ fromJSON(inputs.matrix || '{\"pointless-matrix-item\": [0]}') }}"
steps:
- name: checkout
uses: actions/checkout@v4.1.0
@ -102,11 +104,11 @@ jobs:
--no-restart-on-unexpected \
--no-fail-on-unexpected \
--no-pause \
--this-chunk ${{ matrix.current-chunk }} \
--total-chunks ${{ matrix.total-chunks }} \
--this-chunk "$CURRENT_CHUNK" \
--total-chunks "$TOTAL_CHUNKS" \
--chunk-type hash \
--log-wptreport ${{ runner.temp }}/wpt_report_${{ matrix.current-chunk }}.json \
--log-wptscreenshot ${{ runner.temp }}/wpt_screenshot_${{ matrix.current-chunk }}.txt \
--log-wptreport ${{ runner.temp }}/wpt_report_"$CURRENT_CHUNK".json \
--log-wptscreenshot ${{ runner.temp }}/wpt_screenshot_"$CURRENT_CHUNK".txt \
--log-mach - \
--log-mach-level info \
--channel ${{ inputs.safari-technology-preview && 'preview' || 'stable' }} \
@ -118,16 +120,16 @@ jobs:
- name: Publish results
uses: actions/upload-artifact@v4.1.0
with:
name: ${{ inputs.artifact-name }}-${{ matrix.current-chunk }}
name: ${{ inputs.artifact-name }}-${{ env.CURRENT_CHUNK }}
path: |
${{ runner.temp }}/wpt_report_*.json
${{ runner.temp }}/wpt_screenshot_*.txt
if-no-files-found: "error"
if-no-files-found: error
- name: Publish safaridriver logs
if: inputs.safaridriver-diagnose
uses: actions/upload-artifact@v4.1.0
with:
name: ${{ inputs.artifact-name }}-safaridriver-logs-${{ matrix.current-chunk }}
name: ${{ inputs.artifact-name }}-safaridriver-logs-${{ env.CURRENT_CHUNK }}
path: ~/Library/Logs/com.apple.WebDriver/
if-no-files-found: warn
- name: Disable safaridriver diagnostics
@ -141,8 +143,30 @@ jobs:
set -ux
sudo sed -i '' '/^# Start web-platform-tests hosts$/,/^# End web-platform-tests hosts$/d' /etc/hosts
safari-notify:
needs: safari-results
notify-unmerged:
name: Notify (unmerged results)
needs: results
if: "! inputs.merged-artifact-name"
uses: ./.github/workflows/wpt_fyi_notify.yml
with:
artifact-name: "${{ inputs.artifact-name }}-*"
artifact-name: ${{ inputs.artifact-name }}-*
merge-results:
name: Merge results artifacts
runs-on: ubuntu-24.04
needs: results
if: inputs.merged-artifact-name
steps:
- name: Merge Artifacts
uses: actions/upload-artifact/merge@v4
with:
name: ${{ inputs.merged-artifact-name }}
pattern: ${{ inputs.artifact-name }}-*
notify-merged:
name: Notify (merged results)
needs: merge-results
if: inputs.merged-artifact-name
uses: ./.github/workflows/wpt_fyi_notify.yml
with:
artifact-name: ${{ inputs.merged-artifact-name }}

View file

@ -18,7 +18,7 @@ on:
jobs:
check-workflow-run:
name: "Check for appropriate epochs"
name: Check for appropriate epochs
uses: ./.github/workflows/check-workflow-run.yml
with:
check-refs: '["refs/heads/epochs/daily"]'
@ -32,15 +32,7 @@ jobs:
github.event_name != 'workflow_run' || fromJSON(needs.check-workflow-run.outputs.updated-refs)[0] != null
uses: ./.github/workflows/safari-wptrunner.yml
with:
artifact-name: "safari-results"
artifact-name: safari-results
safari-technology-preview: false
safaridriver-diagnose: false
matrix-include: >-
[{"current-chunk": 1, "total-chunks": 8},
{"current-chunk": 2, "total-chunks": 8},
{"current-chunk": 3, "total-chunks": 8},
{"current-chunk": 4, "total-chunks": 8},
{"current-chunk": 5, "total-chunks": 8},
{"current-chunk": 6, "total-chunks": 8},
{"current-chunk": 7, "total-chunks": 8},
{"current-chunk": 8, "total-chunks": 8}]
matrix: '{"current-chunk": [1, 2, 3, 4, 5, 6, 7, 8], "total-chunks": [8]}'

View file

@ -18,7 +18,7 @@ on:
jobs:
check-workflow-run:
name: "Check for appropriate epochs"
name: Check for appropriate epochs
uses: ./.github/workflows/check-workflow-run.yml
with:
check-refs: '["refs/heads/epochs/three_hourly"]'
@ -32,15 +32,7 @@ jobs:
github.event_name != 'workflow_run' || fromJSON(needs.check-workflow-run.outputs.updated-refs)[0] != null
uses: ./.github/workflows/safari-wptrunner.yml
with:
artifact-name: "safari-technology-preview-results"
artifact-name: safari-technology-preview-results
safari-technology-preview: true
safaridriver-diagnose: false
matrix-include: >-
[{"current-chunk": 1, "total-chunks": 8},
{"current-chunk": 2, "total-chunks": 8},
{"current-chunk": 3, "total-chunks": 8},
{"current-chunk": 4, "total-chunks": 8},
{"current-chunk": 5, "total-chunks": 8},
{"current-chunk": 6, "total-chunks": 8},
{"current-chunk": 7, "total-chunks": 8},
{"current-chunk": 8, "total-chunks": 8}]
matrix: '{"current-chunk": [1, 2, 3, 4, 5, 6, 7, 8], "total-chunks": [8]}'

View file

@ -11,6 +11,7 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: "wpt.fyi"
if: ${{ !cancelled() }}
uses: fjogeleit/http-request-action@v1
with:
url: 'https://wpt.fyi/api/checks/github-actions/'
@ -26,6 +27,7 @@ jobs:
) }}
- name: "staging.wpt.fyi"
if: ${{ !cancelled() }}
uses: fjogeleit/http-request-action@v1
with:
url: 'https://staging.wpt.fyi/api/checks/github-actions/'

View file

@ -0,0 +1,120 @@
// META: title=IndexedDB: Test IDBIndex.getAll with options dictionary.
// META: global=window,worker
// META: script=resources/nested-cloning-common.js
// META: script=resources/support.js
// META: script=resources/support-get-all.js
// META: script=resources/support-promises.js
// META: timeout=long
'use_strict';
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {query: 'C'}, 'Single item get');
index_get_all_values_with_options_test(
/*storeName=*/ 'empty', /*options=*/ {}, 'Empty object store');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {}, 'Get all');
index_get_all_values_with_options_test(
/*storeName=*/ 'generated', /*options=*/ {}, 'Get all with generated keys');
index_get_all_values_with_options_test(
/*storeName=*/ 'large-values', /*options=*/ {},
'Get all with large values');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {count: 10}, 'maxCount=10');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.bound('G', 'M')}, 'Get bound range');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.bound('G', 'M'), count: 3},
'Get bound range with maxCount');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {
query:
IDBKeyRange.bound('G', 'K', /*lowerOpen=*/ false, /*upperOpen=*/ true)
},
'Get upper excluded');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {
query:
IDBKeyRange.bound('G', 'K', /*lowerOpen=*/ true, /*upperOpen=*/ false)
},
'Get lower excluded');
index_get_all_values_with_options_test(
/*storeName=*/ 'generated',
/*options=*/ {query: IDBKeyRange.bound(4, 15), count: 3},
'Get bound range (generated) with maxCount');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {query: 'Doesn\'t exist'},
'Non existent key');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {count: 0}, 'maxCount=0');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {count: 4294967295},
'Max value count');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.upperBound('0')},
'Query with empty range where first key < upperBound');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.lowerBound('ZZ')},
'Query with empty range where lowerBound < last key');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line-not-unique', /*options=*/ {query: 'first'},
'Retrieve multiEntry key');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line-multi', /*options=*/ {query: 'vowel'},
'Retrieve one key multiple values');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'next'},
'Direction: next');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'prev'},
'Direction: prev');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'nextunique'},
'Direction: nextunique');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'prevunique'},
'Direction: prevunique');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {
direction: 'prev',
query: IDBKeyRange.bound('b', 'x'),
},
'Direction and query');
index_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {
direction: 'prev',
query: IDBKeyRange.bound('b', 'x'),
count: 4
},
'Direction, query and count');
get_all_with_options_and_count_test(
'getAll', /*storeName=*/ 'out-of-line', /*indexName=*/ 'test_idx',
'Get all values with both options and count');

View file

@ -0,0 +1,119 @@
// META: title=IndexedDB: Test IDBIndex.getAllKeys with options dictionary.
// META: global=window,worker
// META: script=resources/nested-cloning-common.js
// META: script=resources/support.js
// META: script=resources/support-get-all.js
// META: script=resources/support-promises.js
// META: timeout=long
'use_strict';
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {query: 'C'}, 'Single item get');
index_get_all_keys_with_options_test(
/*storeName=*/ 'empty', /*options=*/ {}, 'Empty object store');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {}, 'Get all keys');
index_get_all_keys_with_options_test(
/*storeName=*/ 'generated', /*options=*/ {}, 'Get all generated keys');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {count: 10}, 'maxCount=10');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.bound('G', 'M')}, 'Get bound range');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.bound('G', 'M'), count: 3},
'Get bound range with maxCount');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {
query:
IDBKeyRange.bound('G', 'K', /*lowerOpen=*/ false, /*upperOpen=*/ true)
},
'Get upper excluded');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {
query:
IDBKeyRange.bound('G', 'K', /*lowerOpen=*/ true, /*upperOpen=*/ false)
},
'Get lower excluded');
index_get_all_keys_with_options_test(
/*storeName=*/ 'generated',
/*options=*/ {query: IDBKeyRange.bound(4, 15), count: 3},
'Get bound range (generated) with maxCount');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: 'Doesn\'t exist'}, 'Non existent key');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {count: 0}, 'maxCount=0');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: 4294967295}, 'Max value count');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.upperBound('0')},
'Query with empty range where first key < upperBound');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.lowerBound('ZZ')},
'Query with empty range where lowerBound < last key');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line-not-unique', /*options=*/ {query: 'first'},
'Retrieve multiEntry key');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line-multi',
/*options=*/ {query: 'vowel'}, 'Retrieve one key multiple values');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'next'},
'Direction: next');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'prev'},
'Direction: prev');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'nextunique'},
'Direction: nextunique');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'prevunique'},
'Direction: prevunique');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {
direction: 'prev',
query: IDBKeyRange.bound('b', 'x'),
},
'Direction and query');
index_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {
direction: 'prev',
query: IDBKeyRange.bound('b', 'x'),
count: 4
},
'Direction, query and count');
get_all_with_options_and_count_test(
'getAllKeys', /*storeName=*/ 'out-of-line', /*indexName=*/ 'test_idx',
'Get all keys with both options and count');

View file

@ -0,0 +1,115 @@
// META: title=IndexedDB: Test IDBObjectStore.getAll with options dictionary.
// META: global=window,worker
// META: script=resources/nested-cloning-common.js
// META: script=resources/support.js
// META: script=resources/support-get-all.js
// META: script=resources/support-promises.js
// META: timeout=long
'use strict';
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {query: 'c'}, 'Single item get');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'generated', /*options=*/ {query: 3},
'Single item get (generated key)');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'empty', /*options=*/ {}, 'getAll on empty object store');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {}, 'Get all values');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'large-values', /*options=*/ {},
'Get all with large values');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {count: 10}, 'Test maxCount');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.bound('g', 'm')}, 'Get bound range');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.bound('g', 'm'), count: 3},
'Get bound range with maxCount');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {
query:
IDBKeyRange.bound('g', 'k', /*lowerOpen=*/ false, /*upperOpen=*/ true)
},
'Get upper excluded');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {
query:
IDBKeyRange.bound('g', 'k', /*lowerOpen=*/ true, /*upperOpen=*/ false)
},
'Get lower excluded');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'generated',
/*options=*/ {query: IDBKeyRange.bound(4, 15), count: 3},
'Get bound range (generated) with maxCount');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: 'Doesn\'t exist'}, 'Non existent key');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {count: 0}, 'zero maxCount');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {count: 4294967295},
'Max value count');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.upperBound('0')},
'Query with empty range where first key < upperBound');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.lowerBound('zz')},
'Query with empty range where lowerBound < last key');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'next'},
'Direction: next');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'prev'},
'Direction: prev');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'nextunique'},
'Direction: nextunique');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'prevunique'},
'Direction: prevunique');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {
direction: 'prev',
query: IDBKeyRange.bound('b', 'x'),
},
'Direction and query');
object_store_get_all_values_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {
direction: 'prev',
query: IDBKeyRange.bound('b', 'x'),
count: 4
},
'Direction, query and count');
get_all_with_options_and_count_test(
'getAll', /*storeName=*/ 'out-of-line', /*indexName=*/ undefined,
'Get all values with both options and count');

View file

@ -0,0 +1,111 @@
// META: title=IndexedDB: Test IDBObjectStore.getAllKeys with options dictionary.
// META: global=window,worker
// META: script=resources/nested-cloning-common.js
// META: script=resources/support.js
// META: script=resources/support-get-all.js
// META: script=resources/support-promises.js
// META: timeout=long
'use strict';
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {query: 'c'}, 'Single item get');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'generated', /*options=*/ {query: 3},
'Single item get (generated key)');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'empty', /*options=*/ undefined,
'getAllKeys on empty object store');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ undefined, 'Get all keys');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {count: 10}, 'Test maxCount');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.bound('g', 'm')}, 'Get bound range');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.bound('g', 'm'), count: 3},
'Get bound range with maxCount');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {
query:
IDBKeyRange.bound('g', 'k', /*lowerOpen=*/ false, /*upperOpen=*/ true)
},
'Get upper excluded');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {
query:
IDBKeyRange.bound('g', 'k', /*lowerOpen=*/ true, /*upperOpen=*/ false)
},
'Get lower excluded');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'generated',
/*options=*/ {query: IDBKeyRange.bound(4, 15), count: 3},
'Get bound range (generated) with maxCount');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: 'Doesn\'t exist'}, 'Non existent key');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {count: 0}, 'zero maxCount');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {count: 4294967295}, 'Max value count');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.upperBound('0')},
'Query with empty range where first key < upperBound');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line',
/*options=*/ {query: IDBKeyRange.lowerBound('zz')},
'Query with empty range where lowerBound < last key');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'next'},
'Direction: next');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'prev'},
'Direction: prev');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'nextunique'},
'Direction: nextunique');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {direction: 'prevunique'},
'Direction: prevunique');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {
direction: 'prev',
query: IDBKeyRange.bound('b', 'x'),
},
'Direction and query');
object_store_get_all_keys_with_options_test(
/*storeName=*/ 'out-of-line', /*options=*/ {
direction: 'prev',
query: IDBKeyRange.bound('b', 'x'),
count: 4
},
'Direction, query and count');
get_all_with_options_and_count_test(
'getAllKeys', /*storeName=*/ 'out-of-line', /*indexName=*/ undefined,
'Get all keys with both options and count');

View file

@ -65,6 +65,29 @@ function check_method(receiver, method, args) {
}
}
// Verifies that invalid keys throw when used with the `IDBGetAllOptions`
// dictionary. `getAllRecords()` added `IDBGetAllOptions`, which `getAll()` and
// `getAllKeys()` also support.
function check_method_with_get_all_options(receiver, method) {
assert_throws_dom('DataError', () => {
receiver[method]({query: invalid_key});
}, 'options query key conversion with invalid key should throw DataError');
const [key, err] = throwing_key('getter');
assert_throws_exactly(err, () => {
receiver[method]({query: key});
}, 'options query key conversion with throwing getter should rethrow');
// Verify `getAll()` and `getAllKeys()` throw when given an invalid key range
// directly without the options dictionary. `getAllRecords()` only supports
// the options dictionary.
if (method !== 'getAllRecords') {
assert_throws_exactly(err, () => {
receiver[method](key);
}, 'query key conversion with throwing getter should rethrow');
}
}
// Static key comparison utility on IDBFactory.
test(
t => check_method(indexedDB, 'cmp', 2),
@ -176,8 +199,6 @@ test(
['delete',
'get',
'getKey',
'getAll',
'getAllKeys',
'count',
'openCursor',
'openKeyCursor',
@ -192,8 +213,6 @@ test(
// Generic (key-or-key-path) methods on IDBIndex.
['get',
'getKey',
'getAll',
'getAllKeys',
'count',
'openCursor',
'openKeyCursor',
@ -205,3 +224,33 @@ test(
check_method(index, method);
}, `IDBIndex ${method}() method with throwing/invalid keys`);
});
// Verify methods that take `IDBGetAllOptions` on `IDBObjectStore`.
['getAll',
'getAllKeys',
'getAllRecords',
].forEach(method => {
indexeddb_upgrade_only_test((t, db) => {
const store = db.createObjectStore('store');
if ('getAllRecords' in store) {
check_method_with_get_all_options(store, method);
} else if (method !== 'getAllRecords') {
// This browser does not support `getAllRecords()` or the
// `IDBGetAllOptions` dictionary.
check_method(store, method);
}
}, `IDBObjectStore ${method}() method with throwing/invalid keys`);
});
// Verify methods that take `IDBGetAllOptions` on `IDBIndex`.
['getAll', 'getAllKeys', 'getAllRecords'].forEach(method => {
indexeddb_upgrade_only_test((t, db) => {
const store = db.createObjectStore('store');
const index = store.createIndex('index', 'keyPath');
if ('getAllRecords' in index) {
check_method_with_get_all_options(index, method);
} else if (method !== 'getAllRecords') {
check_method(store, method);
}
}, `IDBIndex ${method}() method with throwing/invalid keys`);
});

View file

@ -197,15 +197,19 @@ function index_get_all_test_setup(storeName, callback, testDescription) {
// Test `getAll()`, `getAllKeys()` or `getAllRecords()` on either `storeName` or
// `optionalIndexName` with the given `options`.
//
// - `getAllFunctionName` is name of the function to test, which must be
// `getAll`, `getAllKeys` or `getAllRecords`.
// - `options` is an `IDBGetAllRecordsOptions ` dictionary that may contain a
// `query`, `direction` and `count`. Use `direction` to test
// `getAllRecords()` only. `getAll()` and `getAllKeys()` do not support
// `direction`.
//
// - `options` is an `IDBGetAllOptions` dictionary that may contain a `query`,
// `direction` and `count`.
//
// - `shouldUseDictionaryArgument` is true when testing the get all function
// overloads that takes an `IDBGetAllOptions` dictionary. False tests the
// overloads that take two optional arguments: `query` and `count`.
function get_all_test(
getAllFunctionName, storeName, optionalIndexName, options,
testDescription) {
shouldUseDictionaryArgument, testDescription) {
const testGetAllCallback = (test, connection, expectedRecords) => {
// Create a transaction and a get all request.
const transaction = connection.transaction(storeName, 'readonly');
@ -213,8 +217,8 @@ function get_all_test(
if (optionalIndexName) {
queryTarget = queryTarget.index(optionalIndexName);
}
const request =
createGetAllRequest(getAllFunctionName, queryTarget, options);
const request = createGetAllRequest(
getAllFunctionName, queryTarget, options, shouldUseDictionaryArgument);
request.onerror = test.unreached_func('The get all request must succeed');
// Verify the results after the get all request completes.
@ -238,38 +242,77 @@ function get_all_test(
function object_store_get_all_keys_test(storeName, options, testDescription) {
get_all_test(
'getAllKeys', storeName, /*indexName=*/ undefined, options,
testDescription);
/*shouldUseDictionaryArgument=*/ false, testDescription);
}
function object_store_get_all_values_test(storeName, options, testDescription) {
get_all_test(
'getAll', storeName, /*indexName=*/ undefined, options, testDescription);
'getAll', storeName, /*indexName=*/ undefined, options,
/*shouldUseDictionaryArgument=*/ false, testDescription);
}
function object_store_get_all_values_with_options_test(
storeName, options, testDescription) {
get_all_test(
'getAll', storeName, /*indexName=*/ undefined, options,
/*shouldUseDictionaryArgument=*/ true, testDescription);
}
function object_store_get_all_keys_with_options_test(
storeName, options, testDescription) {
get_all_test(
'getAllKeys', storeName, /*indexName=*/ undefined, options,
/*shouldUseDictionaryArgument=*/ true, testDescription);
}
function object_store_get_all_records_test(
storeName, options, testDescription) {
get_all_test(
'getAllRecords', storeName, /*indexName=*/ undefined, options,
testDescription);
/*shouldUseDictionaryArgument=*/ true, testDescription);
}
function index_get_all_keys_test(storeName, options, testDescription) {
get_all_test('getAllKeys', storeName, 'test_idx', options, testDescription);
get_all_test(
'getAllKeys', storeName, 'test_idx', options,
/*shouldUseDictionaryArgument=*/ false, testDescription);
}
function index_get_all_keys_with_options_test(
storeName, options, testDescription) {
get_all_test(
'getAllKeys', storeName, 'test_idx', options,
/*shouldUseDictionaryArgument=*/ true, testDescription);
}
function index_get_all_values_test(storeName, options, testDescription) {
get_all_test('getAll', storeName, 'test_idx', options, testDescription);
get_all_test(
'getAll', storeName, 'test_idx', options,
/*shouldUseDictionaryArgument=*/ false, testDescription);
}
function index_get_all_values_with_options_test(
storeName, options, testDescription) {
get_all_test(
'getAll', storeName, 'test_idx', options,
/*shouldUseDictionaryArgument=*/ true, testDescription);
}
function index_get_all_records_test(storeName, options, testDescription) {
get_all_test(
'getAllRecords', storeName, 'test_idx', options, testDescription);
'getAllRecords', storeName, 'test_idx', options,
/*shouldUseDictionaryArgument=*/ true, testDescription);
}
function createGetAllRequest(getAllFunctionName, queryTarget, options) {
switch (getAllFunctionName) {
case 'getAll':
case 'getAllKeys':
function createGetAllRequest(
getAllFunctionName, queryTarget, options, shouldUseDictionaryArgument) {
if (options && shouldUseDictionaryArgument) {
assert_true(
'getAllRecords' in queryTarget,
`"${queryTarget}" must support "getAllRecords()" to use an "IDBGetAllOptions" dictionary with "${
getAllFunctionName}".`);
return queryTarget[getAllFunctionName](options);
}
// `getAll()` and `getAllKeys()` use optional arguments. Omit the
// optional arguments when undefined.
if (options && options.count) {
@ -279,10 +322,6 @@ function createGetAllRequest(getAllFunctionName, queryTarget, options) {
return queryTarget[getAllFunctionName](options.query);
}
return queryTarget[getAllFunctionName]();
case 'getAllRecords':
return queryTarget.getAllRecords(options);
}
assert_unreached(`Unknown getAllFunctionName: "${getAllFunctionName}"`);
}
// Returns the expected results when `getAllFunctionName` is called with
@ -486,3 +525,37 @@ function assert_idb_values_equals(actual_values, expected_values) {
assert_idb_value_equals(actual_values[i], expected_values[i]);
}
}
// Test passing both an options dictionary and a count to `getAll()` and
// `getAllKeys()`. The get all request must ignore the `count` argument, using
// count from the options dictionary instead.
function get_all_with_options_and_count_test(
getAllFunctionName, storeName, optionalIndexName, testDescription) {
// Set up the object store or index to query.
const setupFunction = optionalIndexName ? index_get_all_test_setup :
object_store_get_all_test_setup;
setupFunction(storeName, (test, connection, expectedRecords) => {
const transaction = connection.transaction(storeName, 'readonly');
let queryTarget = transaction.objectStore(storeName);
if (optionalIndexName) {
queryTarget = queryTarget.index(optionalIndexName);
}
const options = {count: 10};
const request = queryTarget[getAllFunctionName](options, /*count=*/ 17);
request.onerror =
test.unreached_func(`"${getAllFunctionName}()" request must succeed.`);
request.onsuccess = test.step_func(event => {
const expectedResults = calculateExpectedGetAllResults(
getAllFunctionName, expectedRecords, options);
const actualResults = event.target.result;
verifyGetAllResults(getAllFunctionName, actualResults, expectedResults);
test.done();
});
}, testDescription);
}

View file

@ -0,0 +1,22 @@
// META: title=Rewriter Create User Activation
// META: script=/resources/testdriver.js
// META: script=../resources/util.js
// META: timeout=long
'use strict';
// Model download state is shared between test cases of the same file when run
// with `EchoAIManagerImpl`, so this test case needs to be on its own file.
promise_test(async t => {
// Creating Rewriter without user activation rejects with NotAllowedError.
await promise_rejects_dom(t, 'NotAllowedError', Rewriter.create());
// Creating Rewriter with user activation succeeds.
await createRewriter();
// Expect available after create.
assert_equals(await Rewriter.availability(), 'available');
// Now that it is available, we should no longer need user activation.
await Rewriter.create();
}, 'Rewriter.create() requires user activation when availability is "downloadable"');

View file

@ -10,15 +10,5 @@ promise_test(async () => {
}, 'Rewriter must be defined.');
promise_test(async t => {
// Creating Rewriter without user activation rejects with NotAllowedError.
await promise_rejects_dom(t, 'NotAllowedError', Rewriter.create());
// Creating Rewriter with user activation succeeds.
await createRewriter();
// Expect available after create.
assert_equals(await Rewriter.availability(), 'available');
// Now that it is available, we should no longer need user activation.
await Rewriter.create();
}, 'Rewriter.create() requires user activation when availability is "downloadable"');
await testCreateMonitorCallbackThrowsError(t, createRewriter);
}, 'If monitor throws an error, Rewriter.create() rejects with that error');

View file

@ -44,3 +44,17 @@ promise_test(async () => {
rewriter.rewriteStreaming(kTestPrompt)
]);
}, 'Multiple Rewriter.rewriteStreaming() calls are resolved successfully');
promise_test(async () => {
const rewriter = await createRewriter();
const streamingResponse = rewriter.rewriteStreaming(kTestPrompt);
gc();
assert_equals(Object.prototype.toString.call(streamingResponse),
'[object ReadableStream]');
let result = '';
for await (const value of streamingResponse) {
result += value;
gc();
}
assert_greater_than(result.length, 0, 'The result should not be empty.');
}, 'Rewrite Streaming API must continue even after GC has been performed.');

View file

@ -0,0 +1,22 @@
// META: title=Summarizer Create User Activation
// META: script=/resources/testdriver.js
// META: script=../resources/util.js
// META: timeout=long
'use strict';
// Model download state is shared between test cases of the same file when run
// with `EchoAIManagerImpl`, so this test case needs to be on its own file.
promise_test(async t => {
// Creating Summarizer without user activation rejects with NotAllowedError.
await promise_rejects_dom(t, 'NotAllowedError', Summarizer.create());
// Creating Summarizer with user activation succeeds.
await createSummarizer();
// Expect available after create.
assert_equals(await Summarizer.availability(), 'available');
// Now that it is available, we should no longer need user activation.
await Summarizer.create();
}, 'Summarizer.create() requires user activation when availability is "downloadable"');

View file

@ -11,15 +11,5 @@ promise_test(async () => {
}, 'Summarizer.create() is defined');
promise_test(async t => {
// Creating Summarizer without user activation rejects with NotAllowedError.
await promise_rejects_dom(t, 'NotAllowedError', Summarizer.create());
// Creating Summarizer with user activation succeeds.
await createSummarizer();
// Expect available after create.
assert_equals(await Summarizer.availability(), 'available');
// Now that it is available, we should no longer need user activation.
await Summarizer.create();
}, 'Summarizer.create() requires user activation when availability is "downloadable"');
await testCreateMonitorCallbackThrowsError(t, createSummarizer);
}, 'If monitor throws an error, Summarizer.create() rejects with that error');

View file

@ -43,3 +43,17 @@ promise_test(async () => {
summarizer.summarizeStreaming(kTestPrompt)
]);
}, 'Multiple Summarizer.summarizeStreaming() calls are resolved successfully');
promise_test(async t => {
const summarizer = await createSummarizer();
const streamingResponse = summarizer.summarizeStreaming(kTestPrompt);
gc();
assert_equals(Object.prototype.toString.call(streamingResponse),
'[object ReadableStream]');
let result = '';
for await (const value of streamingResponse) {
result += value;
gc();
}
assert_greater_than(result.length, 0, 'The result should not be empty.');
}, 'Summarize Streaming API must continue even after GC has been performed.');

View file

@ -49,6 +49,21 @@ promise_test(async () => {
assert_equals(await translator.translate('hello'), 'こんにちは');
}, 'Simple Translator.translateStreaming() call');
promise_test(async () => {
const translator =
await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'});
const streamingResponse = translator.translateStreaming('hello');
gc();
assert_equals(Object.prototype.toString.call(streamingResponse),
'[object ReadableStream]');
let result = '';
for await (const value of streamingResponse) {
result += value;
gc();
}
assert_greater_than(result.length, 0, 'The result should not be empty.');
}, 'Translate Streaming API must continue even after GC has been performed.');
promise_test(async t => {
const translator =
await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'});

View file

@ -0,0 +1,22 @@
// META: title=Writer Create User Activation
// META: script=/resources/testdriver.js
// META: script=../resources/util.js
// META: timeout=long
'use strict';
// Model download state is shared between test cases of the same file when run
// with `EchoAIManagerImpl`, so this test case needs to be on its own file.
promise_test(async t => {
// Creating Writer without user activation rejects with NotAllowedError.
await promise_rejects_dom(t, 'NotAllowedError', Writer.create());
// Creating Writer with user activation succeeds.
await createWriter();
// Expect available after create.
assert_equals(await Writer.availability(), 'available');
// Now that it is available, we should no longer need user activation.
await Writer.create();
}, 'Writer.create() requires user activation when availability is "downloadable"');

View file

@ -10,15 +10,5 @@ promise_test(async () => {
}, 'Writer must be defined.');
promise_test(async t => {
// Creating Writer without user activation rejects with NotAllowedError.
await promise_rejects_dom(t, 'NotAllowedError', Writer.create());
// Creating Writer with user activation succeeds.
await createWriter();
// Expect available after create.
assert_equals(await Writer.availability(), 'available');
// Now that it is available, we should no longer need user activation.
await Writer.create();
}, 'Writer.create() requires user activation when availability is "downloadable"');
await testCreateMonitorCallbackThrowsError(t, createWriter);
}, 'If monitor throws an error, Writer.create() rejects with that error');

View file

@ -43,3 +43,17 @@ promise_test(async () => {
writer.writeStreaming(kTestPrompt)
]);
}, 'Multiple Writer.writeStreaming() calls are resolved successfully');
promise_test(async () => {
const writer = await createWriter();
const streamingResponse = writer.writeStreaming(kTestPrompt);
gc();
assert_equals(Object.prototype.toString.call(streamingResponse),
'[object ReadableStream]');
let result = '';
for await (const value of streamingResponse) {
result += value;
gc();
}
assert_greater_than(result.length, 0, 'The result should not be empty.');
}, 'Write Streaming API must continue even after GC has been performed.');

View file

@ -0,0 +1,61 @@
<!DOCTYPE html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<title>Clear-Site-Data: cache for bfcache</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="support/clear-cache-helper.sub.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="/common/utils.js"></script>
<script src="/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js"></script>
<script type="module">
// cross-site: example.com (iframe: example.net) -> example.net (iframe example.net clear) -> in bfcache
runBfCacheClearTest(
{
getUrlParams: {
clear: "cache",
secondOrigin: true,
},
targetOrigin: crossSiteOrigin,
mode: "iframe",
shouldBeCached: true,
funcBeforeNavigation: async (url) => {
await new Promise(resolve => {
const iframe = document.createElement("iframe");
iframe.src = url;
document.body.appendChild(iframe);
iframe.onload = resolve;
});
},
argsBeforeNavigation: [crossSiteOrigin],
},
"BfCached document not be dropped when containing cross-site iframe and that cross-site received clear-cache header"
);
// same-site: www.example.com (iframe: www2.example.com) -> www2.example.com (iframe www2.example.com clear) -> bfcache entry gets cleared
runBfCacheClearTest(
{
getUrlParams: {
clear: "cache",
subdomain: true,
},
targetOrigin: subdomainOrigin,
mode: "iframe",
shouldBeCached: false,
funcBeforeNavigation: async (url) => {
await new Promise(resolve => {
const iframe = document.createElement("iframe");
iframe.src = url;
document.body.appendChild(iframe);
iframe.onload = resolve;
});
},
argsBeforeNavigation: [subdomainOrigin],
},
"BfCached document should be dropped when containing same-site iframe and that same-site received clear-cache header"
);
</script>

View file

@ -0,0 +1,63 @@
<!DOCTYPE html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<title>Clear-Site-Data: cache for bfcache</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="support/clear-cache-helper.sub.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="/common/utils.js"></script>
<script src="/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js"></script>
<script type="module">
runBfCacheClearTest(
{
getUrlParams: {
clear: "cache",
},
mode: "iframe",
shouldBeCached: false,
},
"BfCached document shouldn't be cached after receiving clear-cache header from the same origin."
);
runBfCacheClearTest(
{
targetOrigin: subdomainOrigin,
getUrlParams: {
subdomain: true,
clear: "cache",
},
mode: "iframe",
shouldBeCached: true,
},
"BfCached document should be cached after receiving clear-cache header from a subdomain."
);
runBfCacheClearTest(
{
targetOrigin: crossSiteOrigin,
getUrlParams: {
secondOrigin: true,
clear: "cache",
},
mode: "iframe",
shouldBeCached: true,
},
"BfCached document should be cached after receiving clear-cache header from another site."
);
runBfCacheClearTest(
{
getUrlParams: {
clear: "cache",
},
mode: "window",
shouldBeCached: false,
},
"BfCached document shouldn't be cached after receiving clear-cache header from another window."
);
</script>

View file

@ -1,115 +0,0 @@
<!DOCTYPE html>
<meta charset=utf-8>
<meta name="timeout" content="long">
<title>Clear-Site-Data: cache for bfcache</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="support/clear-cache-helper.sub.js"></script>
<script src="/common/dispatcher/dispatcher.js"></script>
<script src="/common/utils.js"></script>
<script src="/html/browsers/browsing-the-web/back-forward-cache/resources/helper.sub.js"></script>
<script type="module">
const sameOrigin =
'https://{{host}}:{{ports[https][0]}}';
const subdomainOrigin =
'https://{{hosts[][www2]}}:{{ports[https][0]}}';
const crossSiteOrigin =
'https://{{hosts[alt][]}}:{{ports[https][0]}}';
// The tests are built on top of the back-forward-cache test harness.
// Here is the steps for the tests:
// 1. Open a new window and navigate to a test URL.
// 2. Navigate the window to a second page.
// 3. Trigger the clear-site-data header either by window.open() or loading an
// iframe from the second page.
// 4. Navigate back to the first page.
// 5. Assert that the first page is or is not cached.
function runBfCacheClearTest(params, description) {
runBfcacheTest(
{
targetOrigin: sameOrigin,
scripts: ["/clear-site-data/support/clear-cache-helper.sub.js"],
funcBeforeBackNavigation: async (getUrlParams, mode) => {
const cacheHelper = self.crypto.randomUUID();
const testUrl = getUrl(cacheHelper, getUrlParams);
let clearingPromise;
if (mode === "window") {
clearingPromise = new Promise(resolve => {
window.addEventListener("message", resolve, {once: true});
window.open(testUrl);
});
} else if (mode === "iframe") {
clearingPromise = new Promise(resolve => {
const iframe = document.createElement("iframe");
iframe.src = testUrl;
document.body.appendChild(iframe);
iframe.onload = resolve;
});
} else {
throw new Error("Unsupported mode");
}
await clearingPromise;
},
argsBeforeBackNavigation: [params.getUrlParams, params.mode],
...params,
},
description
);
}
runBfCacheClearTest(
{
getUrlParams: {
clear: "cache",
},
mode: "iframe",
shouldBeCached: false,
},
"BfCached document shouldn't be cached after receiving clear-cache header from the same origin."
);
runBfCacheClearTest(
{
targetOrigin: subdomainOrigin,
getUrlParams: {
subdomain: true,
clear: "cache",
},
mode: "iframe",
shouldBeCached: true,
},
"BfCached document should be cached after receiving clear-cache header from a subdomain."
);
runBfCacheClearTest(
{
targetOrigin: crossSiteOrigin,
getUrlParams: {
secondOrigin: true,
clear: "cache",
},
mode: "iframe",
shouldBeCached: true,
},
"BfCached document should be cached after receiving clear-cache header from another site."
);
runBfCacheClearTest(
{
getUrlParams: {
clear: "cache",
},
mode: "window",
shouldBeCached: false,
},
"BfCached document shouldn't be cached after receiving clear-cache header from another window."
);
</script>

View file

@ -3,6 +3,15 @@
"use strict"
const sameOrigin =
'https://{{host}}:{{ports[https][0]}}';
const subdomainOrigin =
'https://{{hosts[][www2]}}:{{ports[https][0]}}';
const crossSiteOrigin =
'https://{{hosts[alt][]}}:{{ports[https][0]}}';
const subomdainCrossSiteOrigin =
'https://{{hosts[alt][www2]}}:{{ports[https][0]}}';
/**
* Constructs a url for an intermediate "bounce" hop which represents a tracker.
* @param {string} cacheHelper - Unique uuid for this test
@ -130,3 +139,49 @@ function testCacheClear(test, params, assert) {
openTestPageHelper(test, null, testUrls, 0, assert, resolve)
});
}
// The tests are built on top of the back-forward-cache test harness.
// Here is the steps for the tests:
// 1. Open a new window and navigate to a test URL.
// 2. Navigate the window to a second page.
// 3. Trigger the clear-site-data header either by window.open() or loading an
// iframe from the second page.
// 4. Navigate back to the first page.
// 5. Assert that the first page is or is not cached.
function runBfCacheClearTest(params, description) {
runBfcacheTest(
{
targetOrigin: sameOrigin,
scripts: ["/clear-site-data/support/clear-cache-helper.sub.js"],
funcBeforeBackNavigation: async (getUrlParams, mode) => {
const cacheHelper = self.crypto.randomUUID();
const testUrl = getUrl(cacheHelper, getUrlParams);
let clearingPromise;
if (mode === "window") {
clearingPromise = new Promise(resolve => {
window.addEventListener("message", resolve, {once: true});
window.open(testUrl);
});
} else if (mode === "iframe") {
clearingPromise = new Promise(resolve => {
const iframe = document.createElement("iframe");
iframe.src = testUrl;
document.body.appendChild(iframe);
iframe.onload = resolve;
});
} else {
throw new Error("Unsupported mode");
}
await clearingPromise;
},
argsBeforeBackNavigation: [params.getUrlParams, params.mode],
...params,
},
description
);
}

View file

@ -0,0 +1,4 @@
features:
- name: contenteditable-plaintextonly
files:
- plaintext-only.html

View file

@ -0,0 +1,53 @@
<!DOCTYPE html>
<title>Implicit anchor element for pseudo-elements using anchor functions</title>
<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#implicit">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
body { margin: 0 }
#target {
margin-top: 100px;
margin-left: 50px;
width: 100px;
height: 100px;
background: blue;
}
#target::before, #target::after {
width: 100px;
height: 100px;
position: absolute;
}
#target.moved {
margin-top: 200px;
margin-left: 200px;
}
#target::before {
left: anchor(right);
top: anchor(top);
background: green;
content:'';
}
#target::after {
left: anchor(left);
top: anchor(bottom);
background: green;
content:'';
}
</style>
<div id=target></div>
<script>
test(() => {
assert_equals(getComputedStyle(target, '::before').top, '100px');
assert_equals(getComputedStyle(target, '::before').left, '150px');
assert_equals(getComputedStyle(target, '::after').top, '200px');
assert_equals(getComputedStyle(target, '::after').left, '50px');
}, "The implicit anchor element of a pseudo-element is its originating element");
test(() => {
target.classList.add("moved");
assert_equals(getComputedStyle(target, '::before').top, '200px');
assert_equals(getComputedStyle(target, '::before').left, '300px');
assert_equals(getComputedStyle(target, '::after').top, '300px');
assert_equals(getComputedStyle(target, '::after').left, '200px');
}, "Anchored position after moving");
</script>

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<title>CSS Anchor Positioning Test: Transition on anchored element child with ::selection</title>
<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
#container {
position: absolute;
right: anchor(right);
--i: 1px;
}
#content {
scale: 0;
transition: scale 1000s step-end;
}
#content.show { scale: 1; }
::selection { color: var(--a); }
#container.foo {}
</style>
<div id="container">
<div id="content">FAIL</div>
</div>
<script>
test(() => {
content.offsetTop;
content.classList.toggle("show");
container.classList.toggle("foo");
assert_equals(getComputedStyle(content).scale, "0");
}, "Should transition scale property on anchored child");
</script>

View file

@ -34,7 +34,7 @@ promise_test(async (t) => {
await clickOn(button);
assert_true(popover.matches(':popover-open'));
// Popover should be anchored to the button.
assert_equals(popover.offsetLeft + popover.offsetWidth, 100);
assert_equals(popover.offsetTop + popover.offsetHeight, 100);
assert_equals(popover.offsetLeft + popover.offsetWidth, button.offsetLeft);
assert_equals(popover.offsetTop + popover.offsetHeight, button.offsetTop);
}, 'Popover invokers form an implicit anchor reference');
</script>

View file

@ -0,0 +1,51 @@
<!DOCTYPE html>
<title>Implicit anchor element for pseudo-elements using anchor functions</title>
<link rel="help" href="https://drafts.csswg.org/css-anchor-position-1/#implicit">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<style>
body { margin: 0 }
#target {
margin-top: 100px;
margin-left: 50px;
width: 100px;
height: 100px;
background: blue;
}
#target::before, #target::after {
width: 100px;
height: 100px;
position: absolute;
}
#target.moved {
margin-top: 200px;
margin-left: 200px;
}
#target::before {
position-area: center right;
background: green;
content:'';
}
#target::after {
position-area: bottom center;
background: green;
content:'';
}
</style>
<div id=target></div>
<script>
test(() => {
assert_equals(getComputedStyle(target, '::before').top, '100px');
assert_equals(getComputedStyle(target, '::before').left, '150px');
assert_equals(getComputedStyle(target, '::after').top, '200px');
assert_equals(getComputedStyle(target, '::after').left, '50px');
}, "The implicit anchor element of a pseudo-element is its originating element");
test(() => {
target.classList.add("moved");
assert_equals(getComputedStyle(target, '::before').top, '200px');
assert_equals(getComputedStyle(target, '::before').left, '300px');
assert_equals(getComputedStyle(target, '::after').top, '300px');
assert_equals(getComputedStyle(target, '::after').left, '200px');
}, "Anchored position after moving");
</script>

View file

@ -36,6 +36,8 @@
function cleanup() {
target.className = '';
cb.className = '';
// Clean up running transition
target.getAnimations().forEach(a => a.cancel());
}
test((t) => {

View file

@ -46,6 +46,8 @@
function cleanup() {
target.className = '';
cb.className = '';
// Clean up running transition
target.getAnimations().forEach(a => a.cancel());
}
test((t) => {

View file

@ -23,6 +23,7 @@
<meta name="variant" content="?border-radius=40%&corner-shape=notch&box-shadow=10px%2010px%200%2010px%20yellow">
<meta name="variant" content="?border-radius=50%&corner-top-left-shape=scoop&corner-bottom-right-shape=scoop&corner-top-right-shape=notch&corner-bottom-left-shape=notch&border-width=10px">
<meta name="variant" content="?border-radius=50%&corner-top-right-shape=scoop&corner-bottom-left-shape=scoop&corner-top-left-shape=notch&corner-bottom-right-shape=notch&border-width=10px">
<meta name="variant" content="?border-radius=50%&corner-shape=bevel&box-shadow=10px%2010px%200%2010px%20black">
<style>
body {
margin: 0;

View file

@ -25,7 +25,6 @@
<meta name="variant" content="?corner-bottom-right-shape=bevel&border-width=10px&border-radius=20px">
<meta name="variant" content="?corner-bottom-right-shape=bevel&corner-bottom-left-shape=bevel">
<meta name="variant" content="?border-top-left-radius=50%&corner-shape=superellipse(0.7)&border-left-width=30px&border-top-width=30px">
<meta name="variant" content="?border-radius=50%&corner-shape=bevel&box-shadow=10px%2010px%200%2010px%20black">
<meta name="variant" content="?corner-shape=notch&border-radius=30px&border-width=30px">
<style>
body {

View file

@ -0,0 +1,4 @@
features:
- name: print-color-adjust
files:
- print-color-adjust.html

View file

@ -204,6 +204,7 @@
fuzzy_test_computed_color(`hsl(from rgb(20%, 40%, 60%, 80%) calc(h) calc(s) calc(l) / calc(alpha))`, `color(srgb 0.2 0.4 0.6 / 0.8)`);
fuzzy_test_computed_color(`hsl(from hsl(20 30 40 / 0.8) calc(h + 1) calc(s + 1) calc(l + 1) / calc(alpha + 0.01))`, `color(srgb 0.537 0.372 0.283 / 0.81)`); // hsl(21 31 41)
fuzzy_test_computed_color(`hsl(from rebeccapurple calc((h / 360) * 360deg) calc((s / 100) * 100%) calc((l / 100) * 100%) / calc(alpha * 100%))`, `color(srgb 0.4 0.2 0.6)`);
fuzzy_test_computed_color(`hsl(from rebeccapurple h s calc(50% * l / 100))`, `color(srgb 0.2 0.1 0.3)`);
// Alpha is clamped to [0,1]
fuzzy_test_computed_color(`hsl(from hsl(from rebeccapurple h s l / calc(alpha + 0.5)) h s l / calc(alpha - 0.5))`, `color(srgb 0.4 0.2 0.6 / 0.5)`);

View file

@ -0,0 +1,4 @@
features:
- name: contain-inline-size
files:
- contain-inline-size-*

View file

@ -7,9 +7,11 @@ var kGenericFontFamilyKeywords = [
"fantasy",
"monospace",
"system-ui",
"emoji",
"math",
"fangsong",
"generic(fangsong)",
"generic(kai)",
"generic(khmer-mul)",
"generic(nastaliq)",
"ui-serif",
"ui-sans-serif",
"ui-monospace",
@ -17,11 +19,13 @@ var kGenericFontFamilyKeywords = [
];
// <family-name> values that had/have web-exposed behavior in some browsers, but
// are not defined in the specification.
// are not defined in the specification. They must be treated as <custom-ident>s.
var kNonGenericFontFamilyKeywords = [
"NonGenericFontFamilyName",
"-webkit-body",
"-webkit-standard",
"-webkit-pictograph",
"emoji",
"fangsong",
"BlinkMacSystemFont",
];

View file

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html>
<meta charset="UTF-8">
<title>Crash test found by fuzzer.</title>
<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
<link rel="author" title="Javier Contreras" href="mailto:javiercon@microsoft.com">
<style>
p {
transition: column-rule-width 0.4s ease-in-out;
}
</style>
<p id="test">This is a very long string like the one the fuzzer used</p>
<script>
window.addEventListener('load', () => {
const p = document.getElementById('test');
p.style.setProperty('column-rule-width', 'repeat(4,50px)');
});
</script>
</html>

View file

@ -0,0 +1,49 @@
<!DOCTYPE html>
<head>
<title>Make sure we don't crash while attempting to animate.</title>
<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
<link rel="author" title="Javier Contreras" href="mailto:javiercon@microsoft.com">
</head>
<style>
.crazy-class {
height: 110px;
width: 110px;
display: flex;
column-gap: 10px;
column-rule-style: solid;
column-rule-width: 1px; /* start at 1px */
}
.flex-item {
width: 50px;
}
</style>
</head>
<body>
<div class="crazy-class">
<div class="flex-item"></div>
<div class="flex-item"></div>
</div>
<script>
window.addEventListener('load', () => {
const container = document.querySelector('.crazy-class');
// Animate row rule color from green → red
container.animate(
[
{ rowRuleColor: 'green' },
{ rowRuleColor: 'red' }
],
{
duration: 2000,
easing: 'ease-out',
fill: 'forwards'
}
);
});
</script>
</body>
</html>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<title>
CSS Gap Decorations: Empty flex container should not crash.
</title>
<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
<style>
* {
display: flex;
column-rule-style: dotted;
}
</style>
<div></div>

View file

@ -0,0 +1,58 @@
<!DOCTYPE html>
<html class="reftest-wait">
<head>
<title>
CSS Gap Decorations: Gap decorations are painted on dynamic grid change.
</title>
<link rel="help" href="https://drafts.csswg.org/css-gaps-1/">
<link rel="match" href="../../reference/ref-filled-green-100px-square.xht">
<link rel="author" title="Sam Davis Omekara Jr." href="mailto:samomekarajr@microsoft.com">
<style>
.container {
height: 100px;
width: 100px;
background: red;
}
#grid {
display: grid;
grid-template-columns: auto;
gap: 2px;
row-rule: green solid 2px;
height: 100%;
}
.item {
background: green;
}
</style>
</head>
<body>
<p>Test passes if there is a filled green square and <strong>no red</strong>.</p>
<div class="container">
<div id="grid">
<div class="item"> </div>
<div class="item"> </div>
</div>
</div>
<script>
const grid = document.getElementById('grid');
function addChild(num) {
for (let i = 0; i < num; i++) {
const item = document.createElement('div');
item.className = 'item';
grid.appendChild(item);
}
}
// Use double requestAnimationFrame to remove need of setTimeout.
// Wait for the first frame to ensure that the style is computed.
requestAnimationFrame(() => {
// Wait for the second frame to ensure that the style is painted.
requestAnimationFrame(() => {
addChild(1);
document.documentElement.classList.remove("reftest-wait");
});
});
</script>
</body>
</html>

View file

@ -0,0 +1,6 @@
<!DOCTYPE html>
<p>Test passes if there are two lines below:</p>
<hr>
<div style="display: grid; grid-auto-flow: row;">
line one <span style="position: absolute;"></span> line two
</div>

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<link rel="help" href="https://drafts.csswg.org/css-grid-1/#grid-items">
<link rel="match" href="anonymous-grid-items-002-ref.html">
<p>Test passes if there are two lines below:</p>
<hr>
<div style="display: grid; grid-auto-flow: row;">
line one <span id="target" style="position: absolute; display: none;"></span> line two
</div>
<script>
document.body.offsetTop;
document.getElementById('target').style.display = 'inline';
</script>

View file

@ -20,6 +20,7 @@ html,body {
grid {
display: inline-grid;
display: inline-masonry;
gap: 1px 2px;
grid-template-columns: repeat(4,20px);
grid-template-rows: masonry;

Some files were not shown because too many files have changed in this diff Show more