diff --git a/.flake8 b/.flake8 deleted file mode 100644 index d4fb3a318ee..00000000000 --- a/.flake8 +++ /dev/null @@ -1,18 +0,0 @@ -[flake8] -ignore = - # trailing whitespace; the standard tidy process will enforce no trailing whitespace - W291, - # linebreak before binary operator; replaced by W504 - linebreak after binary operator - W503, - # 80 character line length; the standard tidy process will enforce line length - E501 -exclude = - # temporary local files - target - __pycache__ - python/_venv* - # upstream - third_party - python/mach - components - tests diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3668ac41f48..0bac7800467 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -11,7 +11,6 @@ /components/compositing @mrobinson # Reviewers for layout-related code -/components/layout_2020 @mrobinson @Loirooriol @nicoburns /components/layout @mrobinson @Loirooriol @nicoburns # Reviewers for Minibrowser related code diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8d0fbf3309f..ca278ff2576 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -31,6 +31,9 @@ updates: servo-media-related: patterns: - "servo-media*" + objc2-related: + patterns: + - "objc2*" ignore: # Ignore all stylo crates as their upgrades are coordinated via companion PRs. - dependency-name: selectors diff --git a/.github/workflows/dispatch-workflow.yml b/.github/workflows/dispatch-workflow.yml index c938b1acd39..2e4b8ef7076 100644 --- a/.github/workflows/dispatch-workflow.yml +++ b/.github/workflows/dispatch-workflow.yml @@ -8,12 +8,18 @@ on: profile: required: true type: string + build-args: + required: true + type: string wpt-args: required: true type: string wpt: required: true type: boolean + number-of-wpt-chunks: + required: true + type: number unit-tests: required: true type: boolean @@ -32,6 +38,7 @@ jobs: secrets: inherit with: profile: ${{ inputs.profile }} + build-args: ${{ inputs.build-args }} unit-tests: ${{ inputs.unit-tests }} build-libservo: ${{ inputs.build-libservo }} bencher: ${{ inputs.bencher }} @@ -43,6 +50,7 @@ jobs: secrets: inherit with: profile: ${{ inputs.profile }} + build-args: ${{ inputs.build-args }} wpt: ${{ inputs.wpt }} unit-tests: ${{ inputs.unit-tests }} build-libservo: ${{ inputs.build-libservo }} @@ -56,7 +64,9 @@ jobs: secrets: inherit with: profile: ${{ inputs.profile }} + build-args: ${{ inputs.build-args }} wpt: ${{ inputs.wpt }} + number-of-wpt-chunks: ${{ inputs.number-of-wpt-chunks }} unit-tests: ${{ inputs.unit-tests }} build-libservo: ${{ inputs.build-libservo }} wpt-args: ${{ inputs.wpt-args }} diff --git a/.github/workflows/linux-wpt.yml b/.github/workflows/linux-wpt.yml index 03dce7456b2..e3425a39575 100644 --- a/.github/workflows/linux-wpt.yml +++ b/.github/workflows/linux-wpt.yml @@ -13,6 +13,10 @@ on: default: false required: false type: boolean + number-of-wpt-chunks: + default: 20 + required: false + type: number env: RUST_BACKTRACE: 1 @@ -24,15 +28,25 @@ env: WPT_ALWAYS_SUCCEED_ARG: "${{ inputs.wpt-sync-from-upstream && '--always-succeed' || '' }}" jobs: + chunks: + name: Generate chunks array + runs-on: ubuntu-22.04 + outputs: + chunks-array: ${{ steps.generate-chunks-array.outputs.result }} + steps: + - uses: actions/github-script@v7 + id: generate-chunks-array + with: + script: | + return Array.from({length: ${{ inputs.number-of-wpt-chunks }}}, (_, i) => i + 1) linux-wpt: name: WPT runs-on: ubuntu-22.04 - env: - max_chunk_id: 20 + needs: chunks strategy: fail-fast: false matrix: - chunk_id: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20] + chunk_id: ${{ fromJson(needs.chunks.outputs.chunks-array) }} steps: - uses: actions/checkout@v4 if: github.event_name != 'pull_request_target' @@ -66,7 +80,7 @@ jobs: ./mach test-wpt \ $WPT_ALWAYS_SUCCEED_ARG \ --${{ inputs.profile }} --processes $(nproc) --timeout-multiplier 2 \ - --total-chunks ${{ env.max_chunk_id }} --this-chunk ${{ matrix.chunk_id }} \ + --total-chunks ${{ inputs.number-of-wpt-chunks }} --this-chunk ${{ matrix.chunk_id }} \ --log-raw wpt-full-logs/linux/raw/${{ matrix.chunk_id }}.log \ --log-wptreport wpt-full-logs/linux/wptreport/${{ matrix.chunk_id }}.json \ --log-raw-unexpected wpt-filtered-logs/linux/${{ matrix.chunk_id }}.log \ diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index b8914a2e810..b47f1906160 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -6,6 +6,10 @@ on: required: false default: "release" type: string + build-args: + default: "" + required: false + type: string wpt-args: default: "" required: false @@ -17,6 +21,10 @@ on: wpt: required: false type: boolean + number-of-wpt-chunks: + default: 20 + required: false + type: number unit-tests: required: false default: false @@ -58,6 +66,10 @@ on: wpt: required: false type: boolean + number-of-wpt-chunks: + default: 20 + required: false + type: number unit-tests: required: false default: false @@ -90,10 +102,10 @@ jobs: uses: ./.github/workflows/self-hosted-runner-select.yml secrets: inherit with: - # Ubuntu 22.04 has glibc 2.34 so the binaries produced - # won't run on systems with older glibc e.g wpt.fyi - # runners which still use 20.04. - github-hosted-runner-label: ubuntu-${{ inputs.upload && '20.04' || '22.04' }} + # Before updating the GH action runner image for the nightly job, ensure + # that the system has a glibc version that is compatible with the one + # used by the wpt.fyi runners. + github-hosted-runner-label: ubuntu-22.04 self-hosted-image-name: servo-ubuntu2204 force-github-hosted-runner: ${{ inputs.upload || inputs.force-github-hosted-runner }} runner-timeout: @@ -138,16 +150,8 @@ jobs: tool-cache: false large-packages: false swap-storage: false - - name: Install LLVM and Clang - # Expliclity install Clang 14 on Ubuntu 20.04 used by the nightly job (#34713). - if: ${{ runner.environment != 'self-hosted' && inputs.upload }} - uses: KyleMayes/install-llvm-action@v2 - with: - version: 14 - # FIXME: It would be better to use the above step for regular Linux jobs - # and remove the following step, but currenty that is failing and needs investigation. - name: Set LIBCLANG_PATH env # needed for bindgen in mozangle - if: ${{ runner.environment != 'self-hosted' && !inputs.upload }} # not needed on ubuntu 20.04 used for nightly + if: ${{ runner.environment != 'self-hosted' }} run: echo "LIBCLANG_PATH=/usr/lib/llvm-14/lib" >> $GITHUB_ENV - name: Setup Python if: ${{ runner.environment != 'self-hosted' }} @@ -166,7 +170,7 @@ jobs: - name: Build (${{ inputs.profile }}) run: | - ./mach build --use-crown --locked --${{ inputs.profile }} + ./mach build --use-crown --locked --${{ inputs.profile }} ${{ inputs.build-args }} cp -r target/cargo-timings target/cargo-timings-linux - name: Smoketest run: xvfb-run ./mach smoketest --${{ inputs.profile }} @@ -223,6 +227,7 @@ jobs: wpt-args: ${{ inputs.wpt-args }} profile: ${{ inputs.profile }} wpt-sync-from-upstream: ${{ inputs.wpt-sync-from-upstream }} + number-of-wpt-chunks: ${{ inputs. number-of-wpt-chunks }} secrets: inherit bencher: diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index e27c0a54180..4c19def57d2 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -7,6 +7,10 @@ on: required: false default: "release" type: string + build-args: + default: "" + required: false + type: string wpt-args: default: "" required: false @@ -146,7 +150,7 @@ jobs: brew install gnu-tar - name: Build (${{ inputs.profile }}) run: | - ./mach build --use-crown --locked --${{ inputs.profile }} + ./mach build --use-crown --locked --${{ inputs.profile }} ${{ inputs.build-args }} cp -r target/cargo-timings target/cargo-timings-macos - name: Smoketest uses: nick-fields/retry@v3 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c2bda1a7c2c..1d11033a326 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,6 +56,8 @@ jobs: unit-tests: ${{ matrix.unit_tests }} build-libservo: ${{ matrix.build_libservo }} wpt-args: ${{ matrix.wpt_args }} + build-args: ${{ matrix.build_args }} + number-of-wpt-chunks: ${{ matrix.number_of_wpt_chunks }} bencher: ${{ matrix.bencher }} build-result: diff --git a/.github/workflows/ohos.yml b/.github/workflows/ohos.yml index 8728d31865c..2cda0acbfc9 100644 --- a/.github/workflows/ohos.yml +++ b/.github/workflows/ohos.yml @@ -36,6 +36,7 @@ env: RUST_BACKTRACE: 1 SHELL: /bin/bash CARGO_INCREMENTAL: 0 + BENCHER_PROJECT: ${{ vars.BENCHER_PROJECT || 'servo' }} jobs: build: @@ -168,11 +169,17 @@ jobs: - name: Build for aarch64 HarmonyOS run: | ./mach build --locked --target aarch64-unknown-linux-ohos --profile=${{ inputs.profile }} --flavor=harmonyos --no-default-features --features tracing,tracing-hitrace + - name: Upload supprt/hitrace-bencher/runs.json + uses: actions/upload-artifact@v4 + with: + name: runs.json + path: support/hitrace-bencher/runs.json + overwrite: true - uses: actions/upload-artifact@v4 with: # Upload the **unsigned** artifact - We don't have the signing materials in pull request workflows - path: target/openharmony/aarch64-unknown-linux-ohos/${{ inputs.profile }}/entry/build/harmonyos/outputs/default/servoshell-default-unsigned.hap name: servoshell-hos-${{ inputs.profile }}.hap + path: target/openharmony/aarch64-unknown-linux-ohos/${{ inputs.profile }}/entry/build/harmonyos/outputs/default/servoshell-default-unsigned.hap test-harmonyos-aarch64: @@ -223,10 +230,8 @@ jobs: hdc shell snapshot_display -f /data/local/tmp/servo.jpeg hdc file recv /data/local/tmp/servo.jpeg test_output/servo_hos_screenshot.jpeg hdc file recv /data/local/tmp/ohtrace.txt test_output/servo.ftrace - # To limit the logsize we only save logs from servo. - # Todo: investigate giving servo a custom domain tag, so we can also log appspawn etc, - # since another common error might be the dynamic loader failing to relocate libservoshell.so - hdc shell hilog --exit --pid=${servo_pid} > test_output/servo.log + # To limit the logsize we only save logs from servo. + hdc shell hilog --exit -D 0xE0C3 > test_output/servo.log # todo: Also benchmark some other websites.... - name: Upload artifacts uses: actions/upload-artifact@v4 @@ -240,3 +245,19 @@ jobs: [[ $servo_pid =~ ^[0-9]+$ ]] || { echo "It looks like servo crashed!" ; exit 1; } # If the grep fails, then the trace output for the "page loaded" prompt is missing grep 'org\.servo\.servo-.* tracing_mark_write.*PageLoadEndedPrompt' test_output/servo.ftrace + - name: Getting runs file + uses: actions/download-artifact@v4 + with: + # Name of the artifact to download. + # If unspecified, all artifacts for the run are downloaded. + name: runs.json + - name: "Run benchmark" + run: hitrace-bench -r runs.json + - name: Getting bencher + uses: bencherdev/bencher@main + - name: Getting model name + run: | + echo "MODEL_NAME=$(hdc bugreport | head -n 20 | grep MarketName | awk '{for (i=2; i> $GITHUB_ENV + - name: Uploading to bencher.dev + run: | + bencher run --adapter json --file bench.json --project '${{ env.BENCHER_PROJECT }}' --token '${{ secrets.BENCHER_API_TOKEN }}' --github-actions '${{ secrets.GITHUB_TOKEN }}' --testbed="$MODEL_NAME" diff --git a/.github/workflows/try-label.yml b/.github/workflows/try-label.yml index 5444b75a4bd..d4ce3f944f6 100644 --- a/.github/workflows/try-label.yml +++ b/.github/workflows/try-label.yml @@ -128,6 +128,8 @@ jobs: unit-tests: ${{ matrix.unit_tests }} build-libservo: ${{ matrix.build_libservo }} wpt-args: ${{ matrix.wpt_args }} + build-args: ${{ matrix.build_args }} + number-of-wpt-chunks: ${{ matrix.number_of_wpt_chunks }} bencher: ${{ matrix.bencher }} results: diff --git a/.github/workflows/try.yml b/.github/workflows/try.yml index 274ad7483ff..c3a49af8857 100644 --- a/.github/workflows/try.yml +++ b/.github/workflows/try.yml @@ -10,6 +10,10 @@ on: default: "release" type: choice options: ["release", "debug", "production"] + build-args: + default: "" + required: false + type: string wpt-args: default: "" required: false @@ -79,6 +83,7 @@ jobs: // WPT-related overrides only affect Linux currently, as tests don't run by default on other platforms. configuration.matrix[0].wpt = Boolean(${{ inputs.wpt }}); configuration.matrix[0].wpt_args = "${{ inputs.wpt-args }}" || ""; + configuration.matrix[0].build_args = "${{ inputs.build-args }}" || ""; let unit_tests = Boolean(${{ inputs.unit-tests }}); let profile = '${{ inputs.profile }}'; @@ -107,7 +112,9 @@ jobs: profile: ${{ matrix.profile }} unit-tests: ${{ matrix.unit_tests }} build-libservo: ${{ matrix.build_libservo }} + build-args: ${{ matrix.build_args }} wpt-args: ${{ matrix.wpt_args }} + number-of-wpt-chunks: ${{ matrix.number_of_wpt_chunks }} bencher: ${{ matrix.bencher }} build-result: diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 5cf1e50b853..93e1710e464 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -7,6 +7,10 @@ on: required: false default: "release" type: string + build-args: + default: "" + required: false + type: string unit-tests: required: false default: false @@ -161,7 +165,7 @@ jobs: - name: Build (${{ inputs.profile }}) run: | - .\mach build --use-crown --locked --${{ inputs.profile }} + .\mach build --use-crown --locked --${{ inputs.profile }} ${{ inputs.build-args }} cp C:\a\servo\servo\target\cargo-timings C:\a\servo\servo\target\cargo-timings-windows -Recurse - name: Copy resources if: ${{ runner.environment != 'self-hosted' }} diff --git a/.gitignore b/.gitignore index 73c2fce6cda..630eb63b9bb 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,9 @@ webrender-captures/ Session.vim Sessionx.vim +# Zed +/.zed + /unminified-js /unminified-css diff --git a/.vscode/extensions.json b/.vscode/extensions.json index d776261927d..7bf53b27bf2 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -16,6 +16,8 @@ // IDL language support "mythmon.idl", // TOML files - "tamasfe.even-better-toml" + "tamasfe.even-better-toml", + // Python files + "charliermarsh.ruff" ] } \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a47e5445490..5918a8cf23a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,9 +20,9 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accountable-refcell" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6e2bba6f21fcf0ae382750eb6d9387c42807761fa7329d3a05fcd1334e8c3f2" +checksum = "6afea9052e0b2d90e38572691d87194b2e5d8d6899b9b850b22aaab17e486252" dependencies = [ "backtrace", ] @@ -139,7 +139,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.9.0", + "bitflags 2.9.1", "cc", "cesu8", "jni", @@ -238,20 +238,20 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "3.0.7" +version = "3.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +checksum = "6680de5231bd6ee4c6191b8a1325daa282b415391ec9d3a37bd34f2060dc73fa" dependencies = [ "anstyle", - "once_cell", + "once_cell_polyfill", "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.97" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "app_units" @@ -282,6 +282,12 @@ dependencies = [ "x11rb", ] +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + [[package]] name = "arkui-sys" version = "0.2.4" @@ -324,9 +330,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a194f9d963d8099596278594b3107448656ba73831c9d8c783e613ce86da64" +checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" dependencies = [ "brotli", "flate2", @@ -387,9 +393,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "aws-lc-rs" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b756939cb2f8dc900aa6dcd505e6e2428e9cae7ff7b028c49e3946efa70878" +checksum = "93fcc8f365936c834db5514fc45aee5b1202d677e6b40e48468aaaa8183ca8c7" dependencies = [ "aws-lc-sys", "zeroize", @@ -397,9 +403,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.28.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f7720b74ed28ca77f90769a71fd8c637a0137f6fae4ae947e1050229cff57f" +checksum = "61b1d86e7705efe1be1b569bab41d4fa1e14e220b60a160f78de2db687add079" dependencies = [ "bindgen 0.69.5", "cc", @@ -436,9 +442,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.74" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", "cfg-if", @@ -494,7 +500,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cexpr", "clang-sys", "itertools 0.10.5", @@ -517,10 +523,10 @@ version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "regex", @@ -558,9 +564,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" dependencies = [ "serde", ] @@ -595,7 +601,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "objc2", + "objc2 0.5.2", ] [[package]] @@ -603,7 +609,7 @@ name = "bluetooth" version = "0.0.1" dependencies = [ "base", - "bitflags 2.9.0", + "bitflags 2.9.1", "bluetooth_traits", "blurdroid", "blurmac", @@ -663,9 +669,9 @@ dependencies = [ [[package]] name = "brotli" -version = "7.0.0" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc97b8f16f944bba54f0433f07e30be199b6dc2bd25937444bbad560bcea29bd" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -674,9 +680,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "4.0.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74fa05ad7d803d413eb8380983b092cbbaf9a85f151b871360e7b00cd7060b37" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -707,9 +713,9 @@ checksum = "28346c117b50270785fbc123bd6e4ecad20d0c6d5f43d081dc80a3abcc62be64" [[package]] name = "bytemuck" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" dependencies = [ "bytemuck_derive", ] @@ -731,6 +737,12 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "bytes" version = "1.10.1" @@ -753,7 +765,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "log", "polling", "rustix", @@ -778,34 +790,25 @@ name = "canvas" version = "0.0.1" dependencies = [ "app_units", - "bitflags 2.9.0", - "byteorder", "canvas_traits", + "compositing_traits", "crossbeam-channel", "cssparser", "euclid", - "fnv", "font-kit", "fonts", - "glow", - "half", "ipc-channel", "log", "lyon_geom", "net_traits", - "num-traits", "pixels", "range", "raqote", "servo_arc", + "snapshot", "stylo", - "surfman", "unicode-script", - "webrender", "webrender_api", - "webrender_traits", - "webxr", - "webxr-api", ] [[package]] @@ -823,6 +826,7 @@ dependencies = [ "serde_bytes", "servo_config", "servo_malloc_size_of", + "snapshot", "stylo", "webrender_api", "webxr-api", @@ -845,9 +849,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.18" +version = "1.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c" +checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" dependencies = [ "jobserver", "libc", @@ -871,9 +875,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.17.2" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d4ba6e40bd1184518716a6e1a781bf9160e286d219ccdb8ab2612e74cfe4789" +checksum = "e34e221e91c7eb5e8315b5c9cf1a61670938c0626451f954a51693ed44b37f45" dependencies = [ "smallvec", "target-lexicon", @@ -902,9 +906,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", @@ -965,18 +969,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.35" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" +checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.35" +version = "4.5.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" +checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" dependencies = [ "anstyle", "clap_lex", @@ -1038,10 +1042,11 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ + "serde", "termcolor", "unicode-width", ] @@ -1083,7 +1088,8 @@ name = "compositing" version = "0.0.1" dependencies = [ "base", - "bitflags 2.9.0", + "bincode", + "bitflags 2.9.1", "compositing_traits", "constellation_traits", "crossbeam-channel", @@ -1098,16 +1104,17 @@ dependencies = [ "net", "pixels", "profile_traits", - "script_traits", + "servo_allocator", "servo_config", "servo_geometry", "stylo_traits", "surfman", + "timers", "tracing", "webrender", "webrender_api", - "webrender_traits", "webxr", + "wr_malloc_size_of", ] [[package]] @@ -1115,17 +1122,28 @@ name = "compositing_traits" version = "0.0.1" dependencies = [ "base", + "bincode", "crossbeam-channel", + "dpi", "embedder_traits", "euclid", + "gleam", + "glow", + "image", "ipc-channel", "log", + "malloc_size_of_derive", "pixels", - "script_traits", + "profile_traits", + "raw-window-handle", + "serde", + "servo_geometry", + "servo_malloc_size_of", "strum_macros", + "stylo", "stylo_traits", + "surfman", "webrender_api", - "webrender_traits", ] [[package]] @@ -1176,7 +1194,6 @@ dependencies = [ "webgpu_traits", "webrender", "webrender_api", - "webrender_traits", "webxr-api", ] @@ -1185,27 +1202,36 @@ name = "constellation_traits" version = "0.0.1" dependencies = [ "base", - "bitflags 2.9.0", + "canvas_traits", + "devtools_traits", "embedder_traits", "euclid", + "http 1.3.1", + "hyper_serde", "ipc-channel", + "log", "malloc_size_of_derive", + "net_traits", + "profile_traits", "serde", "servo_malloc_size_of", "servo_url", + "snapshot", + "strum", "strum_macros", - "stylo_traits", + "uuid", + "webgpu_traits", "webrender_api", + "wgpu-core", ] [[package]] name = "content-security-policy" version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f33b8ed5a7a80fdf6b7b1946f0d804c08ba348d72725b09a58fe804c48b7354f" +source = "git+https://github.com/servo/rust-content-security-policy/?branch=servo-csp#58a09ee320fd6fbb828748ae04255e4c8d3f9c9e" dependencies = [ "base64 0.22.1", - "bitflags 2.9.0", + "bitflags 2.9.1", "once_cell", "percent-encoding", "regex", @@ -1370,9 +1396,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ba6d68e24814cb8de6bb986db8222d3a027d15872cabc0d18817bc3c0e4471" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -1464,9 +1490,9 @@ dependencies = [ [[package]] name = "cursor-icon" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" +checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" [[package]] name = "darling" @@ -1488,7 +1514,6 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", "syn", ] @@ -1505,9 +1530,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" [[package]] name = "data-url" @@ -1550,42 +1575,11 @@ dependencies = [ "serde", ] -[[package]] -name = "derive_builder" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "derive_builder_macro" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" -dependencies = [ - "derive_builder_core", - "syn", -] - [[package]] name = "derive_more" -version = "0.99.19" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da29a38df43d6f156149c9b43ded5e018ddff2a855cf2cfd62e8cd7d079c69f" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "proc-macro2", "quote", @@ -1619,7 +1613,7 @@ name = "devtools_traits" version = "0.0.1" dependencies = [ "base", - "bitflags 2.9.0", + "bitflags 2.9.1", "embedder_traits", "http 1.3.1", "ipc-channel", @@ -1683,28 +1677,16 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ - "dirs-sys 0.5.0", + "dirs-sys", ] [[package]] name = "dirs" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys 0.4.1", -] - -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users 0.4.6", - "windows-sys 0.48.0", + "dirs-sys", ] [[package]] @@ -1715,7 +1697,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users 0.5.0", + "redox_users", "windows-sys 0.59.0", ] @@ -1725,6 +1707,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1779,9 +1771,9 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" [[package]] name = "dpi" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "dtoa" @@ -1835,7 +1827,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25dd34cec49ab55d85ebf70139cb1ccd29c977ef6b6ba4fe85489d6877ee9ef3" dependencies = [ "ahash", - "bitflags 2.9.0", + "bitflags 2.9.1", "emath", "epaint", "log", @@ -1913,7 +1905,6 @@ name = "embedder_traits" version = "0.0.1" dependencies = [ "base", - "cfg-if", "cookie 0.18.1", "crossbeam-channel", "euclid", @@ -1930,11 +1921,11 @@ dependencies = [ "servo_malloc_size_of", "servo_url", "strum_macros", + "stylo", "stylo_traits", "url", "webdriver", "webrender_api", - "webxr-api", ] [[package]] @@ -2029,9 +2020,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", "windows-sys 0.59.0", @@ -2039,9 +2030,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.3.1" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5d9305ccc6942a704f4335694ecd3de2ea531b114ac2d51f5f843750787a92f" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" [[package]] name = "etagere" @@ -2134,6 +2125,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" + [[package]] name = "float-ord" version = "0.3.2" @@ -2154,11 +2151,11 @@ checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "font-kit" -version = "0.14.2" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b64b34f4efd515f905952d91bc185039863705592c0c53ae6d979805dd154520" +checksum = "2c7e611d49285d4c4b2e1727b72cf05353558885cc5252f93707b845dfcaf3d3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "byteorder", "core-foundation 0.9.4", "core-graphics", @@ -2177,6 +2174,29 @@ dependencies = [ "yeslogic-fontconfig-sys", ] +[[package]] +name = "fontconfig-parser" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbc773e24e02d4ddd8395fd30dc147524273a83e54e0f312d986ea30de5f5646" +dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" +dependencies = [ + "fontconfig-parser", + "log", + "memmap2", + "slotmap", + "tinyvec", + "ttf-parser", +] + [[package]] name = "fonts" version = "0.0.1" @@ -2184,8 +2204,9 @@ dependencies = [ "app_units", "atomic_refcell", "base", - "bitflags 2.9.0", + "bitflags 2.9.1", "byteorder", + "compositing_traits", "core-foundation 0.9.4", "core-graphics", "core-text", @@ -2197,7 +2218,7 @@ dependencies = [ "freetype-sys", "harfbuzz-sys", "ipc-channel", - "itertools 0.13.0", + "itertools 0.14.0", "libc", "log", "malloc_size_of_derive", @@ -2205,6 +2226,7 @@ dependencies = [ "net_traits", "num-traits", "parking_lot", + "profile_traits", "range", "serde", "servo_allocator", @@ -2221,7 +2243,6 @@ dependencies = [ "unicode-script", "url", "webrender_api", - "webrender_traits", "xml-rs", "yeslogic-fontconfig-sys", ] @@ -2239,7 +2260,7 @@ dependencies = [ [[package]] name = "fontsan" version = "0.5.2" -source = "git+https://github.com/servo/fontsan#8fbc406506cfd1f8ab60e625d1e926a0e72e1d8a" +source = "git+https://github.com/servo/fontsan#c0d0b5333117901e1c31bc3c502c384115b93e6f" dependencies = [ "cc", "glob", @@ -2325,6 +2346,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" +[[package]] +name = "fst" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a" + [[package]] name = "futf" version = "0.1.5" @@ -2481,9 +2508,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "libc", @@ -2525,9 +2552,9 @@ dependencies = [ [[package]] name = "gilrs-core" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed2326d21aa97752d41b2c195aee1d99cd84456ff4d5a7f5e6e1cdbd3dcb0b8" +checksum = "a6d95ae10ce5aa99543a28cf74e41c11f3b9e3c14f0452bbde46024753cd683e" dependencies = [ "core-foundation 0.10.0", "inotify", @@ -2552,9 +2579,9 @@ checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "gio-sys" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "160eb5250a26998c3e1b54e6a3d4ea15c6c7762a6062a19a7b63eff6e2b33f9e" +checksum = "521e93a7e56fc89e84aea9a52cfc9436816a4b363b030260b699950ff1336c83" dependencies = [ "glib-sys", "gobject-sys", @@ -2563,19 +2590,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "git2" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fda788993cc341f69012feba8bf45c0ba4f3291fcc08e214b4d5a7332d88aff" -dependencies = [ - "bitflags 2.9.0", - "libc", - "libgit2-sys", - "log", - "url", -] - [[package]] name = "gl_generator" version = "0.14.0" @@ -2598,11 +2612,11 @@ dependencies = [ [[package]] name = "glib" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707b819af8059ee5395a2de9f2317d87a53dbad8846a2f089f0bb44703f37686" +checksum = "c501c495842c2b23cdacead803a5a343ca2a5d7a7ddaff14cc5f6cf22cfb92c2" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "futures-channel", "futures-core", "futures-executor", @@ -2619,9 +2633,9 @@ dependencies = [ [[package]] name = "glib-macros" -version = "0.20.7" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "715601f8f02e71baef9c1f94a657a9a77c192aea6097cf9ae7e5e177cd8cde68" +checksum = "ebe6dc9ce29887c4b3b74d78d5ba473db160a258ae7ed883d23632ac7fed7bc9" dependencies = [ "heck", "proc-macro-crate", @@ -2632,9 +2646,9 @@ dependencies = [ [[package]] name = "glib-sys" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8928869a44cfdd1fccb17d6746e4ff82c8f82e41ce705aa026a52ca8dc3aefb" +checksum = "8ab79e1ed126803a8fb827e3de0e2ff95191912b8db65cee467edb56fc4cc215" dependencies = [ "libc", "system-deps", @@ -2678,9 +2692,9 @@ dependencies = [ [[package]] name = "gobject-sys" -version = "0.20.9" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c773a3cb38a419ad9c26c81d177d96b4b08980e8bdbbf32dace883e96e96e7e3" +checksum = "ec9aca94bb73989e3cfdbf8f2e0f1f6da04db4d291c431f444838925c4c63eda" dependencies = [ "glib-sys", "libc", @@ -2693,7 +2707,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "gpu-alloc-types", ] @@ -2703,7 +2717,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -2720,13 +2734,13 @@ dependencies = [ [[package]] name = "gpu-descriptor" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "gpu-descriptor-types", - "hashbrown 0.15.2", + "hashbrown", ] [[package]] @@ -2735,7 +2749,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -2746,9 +2760,9 @@ checksum = "36119f3a540b086b4e436bb2b588cf98a68863470e0e880f4d0842f112a3183a" [[package]] name = "gstreamer" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2188fe829b0ebe12e4cf2bbcf6658470a936269daba7afae92847a2af32c9105" +checksum = "50ab4c88f731596a2511a6f14cabdd666e0d8efab62a1d58e6ddb57faa96e22e" dependencies = [ "cfg-if", "futures-channel", @@ -2756,7 +2770,7 @@ dependencies = [ "futures-util", "glib", "gstreamer-sys", - "itertools 0.13.0", + "itertools 0.14.0", "libc", "muldiv", "num-integer", @@ -2799,9 +2813,9 @@ dependencies = [ [[package]] name = "gstreamer-audio" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49118ca684e2fc42207509fcac8497d91079c2ffe8ff2b4ae99e71dbafef1ede" +checksum = "2e7ec7e0374298897e669db7c79544bc44df12011985e7dd5f38644edaf2caf4" dependencies = [ "cfg-if", "glib", @@ -2815,9 +2829,9 @@ dependencies = [ [[package]] name = "gstreamer-audio-sys" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d469526ecf30811b50a6460fd285ee40d189c46048b3d0c69b67a04b414fb51" +checksum = "2b5f3e09e7c04ec91d78c2a6ca78d50b574b9ed49fdf5e72f3693adca4306a87" dependencies = [ "glib-sys", "gobject-sys", @@ -2829,9 +2843,9 @@ dependencies = [ [[package]] name = "gstreamer-base" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad33dd444db0d215ac363164f900f800ffb93361ad8a60840e95e14b7de985e8" +checksum = "f19a74fd04ffdcb847dd322640f2cf520897129d00a7bcb92fd62a63f3e27404" dependencies = [ "atomic_refcell", "cfg-if", @@ -2843,9 +2857,9 @@ dependencies = [ [[package]] name = "gstreamer-base-sys" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114b2a704f19a70f20c54b00e54f5d5376bbf78bd2791e6beb0776c997d8bf24" +checksum = "87f2fb0037b6d3c5b51f60dea11e667910f33be222308ca5a101450018a09840" dependencies = [ "glib-sys", "gobject-sys", @@ -2856,9 +2870,9 @@ dependencies = [ [[package]] name = "gstreamer-gl" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02818bd81028abc4ee7b0106c21625be9a2f86ba5fd41ccff58359537637db59" +checksum = "34aa19feafc4da2c7635abce0e0768892ff97ad73586bef02d9a60b251d9fe09" dependencies = [ "glib", "gstreamer", @@ -2871,9 +2885,9 @@ dependencies = [ [[package]] name = "gstreamer-gl-egl" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5fad98961d18ed5dba4be44787d4735b78a1f53b7db392be004b96f1f2430b" +checksum = "8de1f4247cf2d009b41ab5efb03e4d826b7ccaafb9a75d3ea10e68e46f65e8aa" dependencies = [ "glib", "gstreamer", @@ -2884,9 +2898,9 @@ dependencies = [ [[package]] name = "gstreamer-gl-egl-sys" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7ebdc94dc34e2b135b2610676b47d30ce88b80862f01e2acf7e29b9b42a14e4" +checksum = "dda4d852ed107cc48692af4e109e5e4775b6ce1044d13df79f6f431c195096d7" dependencies = [ "glib-sys", "gstreamer-gl-sys", @@ -2896,9 +2910,9 @@ dependencies = [ [[package]] name = "gstreamer-gl-sys" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2984f8c246407fabbecf852c4595dd1487f4cc495386e17ad31acb69db7d39" +checksum = "a832c21d4522ed5e1b8dfc676a45361969216b144fc03af413a38c471f38bcf7" dependencies = [ "glib-sys", "gobject-sys", @@ -2986,9 +3000,9 @@ dependencies = [ [[package]] name = "gstreamer-sys" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe159238834058725808cf6604a7c5d9e4a50e1eacd7b0c63bce2fe3a067dbd1" +checksum = "feea73b4d92dbf9c24a203c9cd0bcc740d584f6b5960d5faf359febf288919b2" dependencies = [ "glib-sys", "gobject-sys", @@ -2998,9 +3012,9 @@ dependencies = [ [[package]] name = "gstreamer-video" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad242d388b63c91652c8157de3b0c1f709e49c941a0aae1952455f6ee326ca2d" +checksum = "1318b599d77ca4f7702ecbdeac1672d6304cb16b7e5752fabb3ee8260449a666" dependencies = [ "cfg-if", "futures-channel", @@ -3015,9 +3029,9 @@ dependencies = [ [[package]] name = "gstreamer-video-sys" -version = "0.23.5" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465ff496889fb38be47f5e821163c2e83414d87c4aa55f5aae62dc7200971d4d" +checksum = "0a70f0947f12d253b9de9bc3fd92f981e4d025336c18389c7f08cdf388a99f5c" dependencies = [ "glib-sys", "gobject-sys", @@ -3093,12 +3107,13 @@ dependencies = [ [[package]] name = "half" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7db2ff139bba50379da6aa0766b52fdcb62cb5b263009b09ed58ba604e14bbd1" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", + "num-traits", ] [[package]] @@ -3118,21 +3133,12 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" -dependencies = [ - "ahash", - "serde", -] - -[[package]] -name = "hashbrown" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" dependencies = [ "foldhash", + "serde", ] [[package]] @@ -3221,10 +3227,11 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "hilog" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e862eccf1f9bf3ec24a45b65f1369b2dfe053f9066c591623e0b1e58937178d" +checksum = "ae00913bdb22425089eab7ff3307019717b5f948172c0cf8cceac49ada086ce4" dependencies = [ + "arc-swap", "env_filter", "hilog-sys", "log", @@ -3241,9 +3248,9 @@ dependencies = [ [[package]] name = "hitrace" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f92c0ae6f30b32eaeb811143fba3b56864f477b2e69458b13779a07b3aaf2f6e" +checksum = "4e3037245d690359ac992d8e3bb96e15ace6ad0a00305b680f4b1fd4ede684d9" dependencies = [ "hitrace-sys", ] @@ -3265,9 +3272,9 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.30.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5bf3413d61499f71fe4f627bbecfbec2977ce280525701df788f47370b0c507" +checksum = "953cbbe631aae7fc0a112702ad5d3aaf09da38beaf45ea84610d6e1c358f569c" dependencies = [ "log", "mac", @@ -3409,12 +3416,13 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.11" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497bbc33a26fdd4af9ed9c70d63f61cf56a938375fbb32df34db9b1cd6d643f2" +checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" dependencies = [ "bytes", "futures-channel", + "futures-core", "futures-util", "http 1.3.1", "http-body 1.0.1", @@ -3927,6 +3935,22 @@ dependencies = [ "tiff", ] +[[package]] +name = "image-webp" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imagesize" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285" + [[package]] name = "imsz" version = "0.2.2" @@ -3935,12 +3959,12 @@ checksum = "76a49eaebc8750bcba241df1e1e47ebb51b81eb35c65e8f11ffa0aebac353f7f" [[package]] name = "indexmap" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown 0.15.2", + "hashbrown", ] [[package]] @@ -3949,7 +3973,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "inotify-sys", "libc", ] @@ -4042,9 +4066,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] @@ -4057,9 +4081,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jiff" -version = "0.2.5" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c102670231191d07d37a35af3eb77f1f0dbf7a71be51a962dcd57ea607be7260" +checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93" dependencies = [ "jiff-static", "log", @@ -4070,9 +4094,9 @@ dependencies = [ [[package]] name = "jiff-static" -version = "0.2.5" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cdde31a9d349f1b1f51a0b3714a5940ac022976f4b49485fc04be052b183b4c" +checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442" dependencies = [ "proc-macro2", "quote", @@ -4144,7 +4168,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "serde", "unicode-segmentation", ] @@ -4167,14 +4191,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] -name = "layout_2020" +name = "kurbo" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c" +dependencies = [ + "arrayvec", + "smallvec", +] + +[[package]] +name = "layout" version = "0.0.1" dependencies = [ "app_units", "atomic_refcell", "base", - "bitflags 2.9.0", - "canvas_traits", + "bitflags 2.9.1", + "compositing_traits", + "constellation_traits", "data-url", "embedder_traits", "euclid", @@ -4186,21 +4221,28 @@ dependencies = [ "icu_locid", "icu_segmenter", "ipc-channel", - "itertools 0.13.0", + "itertools 0.14.0", "log", + "malloc_size_of_derive", "net_traits", + "num-traits", "parking_lot", "pixels", + "profile_traits", "quickcheck", "range", "rayon", + "script", "script_layout_interface", + "script_traits", "selectors", "servo_arc", "servo_config", "servo_geometry", + "servo_malloc_size_of", "servo_url", "stylo", + "stylo_atoms", "stylo_traits", "taffy", "tracing", @@ -4208,47 +4250,9 @@ dependencies = [ "unicode-script", "url", "webrender_api", - "webrender_traits", "xi-unicode", ] -[[package]] -name = "layout_thread_2020" -version = "0.0.1" -dependencies = [ - "app_units", - "base", - "constellation_traits", - "embedder_traits", - "euclid", - "fnv", - "fonts", - "fonts_traits", - "fxhash", - "ipc-channel", - "layout_2020", - "log", - "metrics", - "net_traits", - "parking_lot", - "profile_traits", - "script", - "script_layout_interface", - "script_traits", - "servo_allocator", - "servo_arc", - "servo_config", - "servo_malloc_size_of", - "servo_url", - "stylo", - "stylo_atoms", - "stylo_traits", - "tracing", - "url", - "webrender_api", - "webrender_traits", -] - [[package]] name = "lazy_static" version = "1.5.0" @@ -4284,9 +4288,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.171" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libdbus-sys" @@ -4297,23 +4301,11 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "libgit2-sys" -version = "0.18.1+1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1dcb20f84ffcdd825c7a311ae347cce604a6f084a767dec4a4929829645290e" -dependencies = [ - "cc", - "libc", - "libz-sys", - "pkg-config", -] - [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", "windows-targets 0.48.5", @@ -4321,9 +4313,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.11" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" @@ -4331,7 +4323,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "libc", "redox_syscall 0.5.7", ] @@ -4340,6 +4332,7 @@ dependencies = [ name = "libservo" version = "0.0.1" dependencies = [ + "anyhow", "arboard", "background_hang_monitor", "base", @@ -4348,7 +4341,6 @@ dependencies = [ "bluetooth_traits", "canvas", "canvas_traits", - "cfg-if", "compositing", "compositing_traits", "constellation", @@ -4367,7 +4359,7 @@ dependencies = [ "http 1.3.1", "ipc-channel", "keyboard-types", - "layout_thread_2020", + "layout", "libservo", "log", "media", @@ -4386,6 +4378,8 @@ dependencies = [ "servo-media", "servo-media-dummy", "servo-media-gstreamer", + "servo-tracing", + "servo_allocator", "servo_config", "servo_geometry", "servo_url", @@ -4395,10 +4389,10 @@ dependencies = [ "tracing", "url", "webdriver_server", + "webgl", "webgpu", "webrender", "webrender_api", - "webrender_traits", "webxr", "webxr-api", "winit", @@ -4446,9 +4440,9 @@ checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" [[package]] name = "lock_api" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -4516,16 +4510,13 @@ dependencies = [ [[package]] name = "markup5ever" -version = "0.15.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a7b81dfb91586d0677086d40a6d755070e0799b71bb897485bac408dfd5c69" +checksum = "d0a8096766c229e8c88a3900c9b44b7e06aa7f7343cc229158c3e58ef8f9973a" dependencies = [ "log", - "phf", - "phf_codegen", - "string_cache", - "string_cache_codegen", "tendril", + "web_atoms", ] [[package]] @@ -4558,6 +4549,7 @@ checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" name = "media" version = "0.0.1" dependencies = [ + "compositing_traits", "euclid", "fnv", "ipc-channel", @@ -4566,7 +4558,6 @@ dependencies = [ "servo-media", "servo_config", "webrender_api", - "webrender_traits", ] [[package]] @@ -4613,7 +4604,7 @@ version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block", "core-graphics-types", "foreign-types 0.5.0", @@ -4671,9 +4662,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", "simd-adler32", @@ -4681,14 +4672,14 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", "wasi", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4707,7 +4698,7 @@ dependencies = [ [[package]] name = "mozjs" version = "0.14.1" -source = "git+https://github.com/servo/mozjs#e4d4f9ac06162fe2647078dc4be8c270b7219807" +source = "git+https://github.com/servo/mozjs#728acdf3d4ce0604e9f75dd1d539dc6f291ccec7" dependencies = [ "bindgen 0.71.1", "cc", @@ -4718,8 +4709,8 @@ dependencies = [ [[package]] name = "mozjs_sys" -version = "0.128.9-0" -source = "git+https://github.com/servo/mozjs#e4d4f9ac06162fe2647078dc4be8c270b7219807" +version = "0.128.9-2" +source = "git+https://github.com/servo/mozjs#728acdf3d4ce0604e9f75dd1d539dc6f291ccec7" dependencies = [ "bindgen 0.71.1", "cc", @@ -4741,22 +4732,25 @@ checksum = "956787520e75e9bd233246045d19f42fb73242759cc57fba9611d940ae96d4b0" [[package]] name = "naga" -version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=2f255edc60e9669c8c737464c59af10d59a31126#2f255edc60e9669c8c737464c59af10d59a31126" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b977c445f26e49757f9aca3631c3b8b836942cb278d69a92e7b80d3b24da632" dependencies = [ "arrayvec", "bit-set", - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg_aliases", "codespan-reporting", - "hashbrown 0.14.5", + "half", + "hashbrown", "hexf-parse", "indexmap", "log", + "num-traits", + "once_cell", "rustc-hash 1.1.0", "spirv", "strum", - "termcolor", "thiserror 2.0.9", "unicode-ident", ] @@ -4794,7 +4788,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32036ede4ef064610304337831e9d49dac23e7edc4e9efd076c8259eab6d19a9" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "ctor", "napi-sys-ohos", ] @@ -4814,7 +4808,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "jni-sys", "log", "ndk-sys 0.6.0+11769913", @@ -4858,6 +4852,7 @@ dependencies = [ "base64 0.22.1", "bytes", "chrono", + "compositing_traits", "content-security-policy", "cookie 0.18.1", "crossbeam-channel", @@ -4865,6 +4860,7 @@ dependencies = [ "devtools_traits", "embedder_traits", "flate2", + "fst", "futures 0.3.31", "futures-core", "futures-util", @@ -4878,6 +4874,7 @@ dependencies = [ "hyper_serde", "imsz", "ipc-channel", + "itertools 0.14.0", "log", "malloc_size_of_derive", "mime", @@ -4886,12 +4883,12 @@ dependencies = [ "pixels", "profile_traits", "rayon", + "resvg", "rustls", "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", - "servo_allocator", "servo_arc", "servo_config", "servo_malloc_size_of", @@ -4908,7 +4905,6 @@ dependencies = [ "uuid", "webpki-roots", "webrender_api", - "webrender_traits", ] [[package]] @@ -4916,9 +4912,11 @@ name = "net_traits" version = "0.0.1" dependencies = [ "base", + "compositing_traits", "content-security-policy", "cookie 0.18.1", "crossbeam-channel", + "data-url", "embedder_traits", "headers 0.4.0", "http 1.3.1", @@ -4931,6 +4929,7 @@ dependencies = [ "num-traits", "percent-encoding", "pixels", + "profile_traits", "rustls-pki-types", "serde", "servo_arc", @@ -4939,7 +4938,7 @@ dependencies = [ "servo_url", "url", "uuid", - "webrender_traits", + "webrender_api", ] [[package]] @@ -4954,7 +4953,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg-if", "cfg_aliases", "libc", @@ -5130,33 +5129,53 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +dependencies = [ + "objc2-encode", +] + [[package]] name = "objc2-app-kit" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "libc", - "objc2", + "objc2 0.5.2", "objc2-core-data", "objc2-core-image", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-quartz-core", ] +[[package]] +name = "objc2-app-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", + "objc2-foundation 0.3.1", +] + [[package]] name = "objc2-cloud-kit" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", - "objc2", + "objc2 0.5.2", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -5166,8 +5185,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -5176,10 +5195,21 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags 2.9.1", + "dispatch2", + "objc2 0.6.1", ] [[package]] @@ -5189,8 +5219,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "objc2-metal", ] @@ -5201,9 +5231,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ "block2", - "objc2", + "objc2 0.5.2", "objc2-contacts", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -5218,11 +5248,22 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "dispatch", "libc", - "objc2", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", + "objc2-core-foundation", ] [[package]] @@ -5232,9 +5273,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ "block2", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -5243,10 +5284,10 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -5255,10 +5296,10 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "objc2-metal", ] @@ -5268,8 +5309,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" dependencies = [ - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -5278,14 +5319,14 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", - "objc2", + "objc2 0.5.2", "objc2-cloud-kit", "objc2-core-data", "objc2-core-image", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-link-presentation", "objc2-quartz-core", "objc2-symbols", @@ -5300,8 +5341,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -5310,11 +5351,11 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", - "objc2", + "objc2 0.5.2", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -5355,9 +5396,9 @@ dependencies = [ [[package]] name = "ohos-ime" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48107e68ed8451c17c2ff95938e1ba86003fb290a04f7a0213ce2d16ce4b3ee6" +checksum = "ee3ea454e31a3372cd9c4ed903db4fae861e92f57cf51852a3cd80f9d3945dcd" dependencies = [ "log", "ohos-ime-sys", @@ -5396,6 +5437,12 @@ version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "oorandom" version = "11.1.5" @@ -5485,9 +5532,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", "parking_lot_core", @@ -5495,9 +5542,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", @@ -5524,9 +5571,9 @@ dependencies = [ [[package]] name = "pathfinder_simd" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cf07ef4804cfa9aea3b04a7bbdd5a40031dbb6b4f2cbaf2b011666c80c5b4f2" +checksum = "bf9027960355bf3afff9841918474a81a5f972ac6d226d518060bba758b5ad57" dependencies = [ "rustc_version", ] @@ -5534,7 +5581,7 @@ dependencies = [ [[package]] name = "peek-poke" version = "0.3.0" -source = "git+https://github.com/servo/webrender?branch=0.66#88462530746749163bcf1dc89be20a19f2394e71" +source = "git+https://github.com/servo/webrender?branch=0.67#ae2477d9a6da403e5b5dce8a17415a2cd1563074" dependencies = [ "euclid", "peek-poke-derive", @@ -5543,13 +5590,12 @@ dependencies = [ [[package]] name = "peek-poke-derive" version = "0.3.0" -source = "git+https://github.com/servo/webrender?branch=0.66#88462530746749163bcf1dc89be20a19f2394e71" +source = "git+https://github.com/servo/webrender?branch=0.67#ae2477d9a6da403e5b5dce8a17415a2cd1563074" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", - "unicode-xid", ] [[package]] @@ -5620,6 +5666,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pico-args" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" + [[package]] name = "pin-project" version = "1.1.10" @@ -5796,9 +5848,9 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" [[package]] name = "prettyplease" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5316f57387668042f561aae71480de936257848f9c43ce528e311d89a07cadeb" +checksum = "664ec5419c51e34154eec046ebcba56312d5a2fc3b09a06da188e1ad21afadf6" dependencies = [ "proc-macro2", "syn", @@ -5815,9 +5867,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.94" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] @@ -5830,7 +5882,6 @@ dependencies = [ "ipc-channel", "libc", "log", - "parking_lot", "profile_traits", "regex", "serde", @@ -5862,10 +5913,10 @@ dependencies = [ "log", "malloc_size_of_derive", "serde", + "servo_allocator", "servo_config", "servo_malloc_size_of", "signpost", - "strum_macros", "time", "tracing", ] @@ -5909,10 +5960,16 @@ dependencies = [ ] [[package]] -name = "quick-xml" -version = "0.37.4" +name = "quick-error" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ce8c88de324ff838700f36fb6ab86c96df0e3c4ab6ef3a9b2044465cce1369" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] @@ -6048,18 +6105,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 2.9.0", -] - -[[package]] -name = "redox_users" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" -dependencies = [ - "getrandom", - "libredox", - "thiserror 1.0.69", + "bitflags 2.9.1", ] [[package]] @@ -6073,12 +6119,6 @@ dependencies = [ "thiserror 2.0.9", ] -[[package]] -name = "ref_filter_map" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b5ceb840e4009da4841ed22a15eb49f64fdd00a2138945c5beacf506b2fb5ed" - [[package]] name = "regex" version = "1.11.1" @@ -6132,6 +6172,32 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +[[package]] +name = "resvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43" +dependencies = [ + "gif", + "image-webp", + "log", + "pico-args", + "rgb", + "svgtypes", + "tiny-skia", + "usvg", + "zune-jpeg", +] + +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.17.14" @@ -6148,16 +6214,23 @@ dependencies = [ [[package]] name = "ron" -version = "0.8.1" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +checksum = "beceb6f7bf81c73e73aeef6dd1356d9a1b2b4909e1f0fc3e59b034f9572d7b7f" dependencies = [ - "base64 0.21.7", - "bitflags 2.9.0", + "base64 0.22.1", + "bitflags 2.9.1", "serde", "serde_derive", + "unicode-ident", ] +[[package]] +name = "roxmltree" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -6191,7 +6264,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", @@ -6200,9 +6273,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.25" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ "aws-lc-rs", "log", @@ -6224,15 +6297,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] [[package]] name = "rustls-webpki" -version = "0.103.1" +version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ "aws-lc-rs", "ring", @@ -6242,9 +6318,27 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" + +[[package]] +name = "rustybuzz" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" +dependencies = [ + "bitflags 2.9.1", + "bytemuck", + "core_maths", + "log", + "smallvec", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", + "unicode-properties", + "unicode-script", +] [[package]] name = "ryu" @@ -6290,12 +6384,13 @@ dependencies = [ "base", "base64 0.22.1", "bincode", - "bitflags 2.9.0", + "bitflags 2.9.1", "bluetooth_traits", "canvas_traits", "cbc", "chrono", "cipher", + "compositing_traits", "constellation_traits", "content-security-policy", "cookie 0.18.1", @@ -6322,12 +6417,13 @@ dependencies = [ "image", "indexmap", "ipc-channel", - "itertools 0.13.0", + "itertools 0.14.0", "jstraceable_derive", "keyboard-types", "libc", "log", "malloc_size_of_derive", + "markup5ever", "media", "metrics", "mime", @@ -6338,13 +6434,11 @@ dependencies = [ "nom", "num-traits", "num_cpus", - "parking_lot", "percent-encoding", "phf", "pixels", "profile_traits", "range", - "ref_filter_map", "regex", "script_bindings", "script_layout_interface", @@ -6361,6 +6455,7 @@ dependencies = [ "servo_rand", "servo_url", "smallvec", + "snapshot", "strum", "strum_macros", "stylo", @@ -6377,12 +6472,12 @@ dependencies = [ "unicode-bidi", "unicode-segmentation", "url", + "urlpattern", "utf-8", "uuid", "webdriver", "webgpu_traits", "webrender_api", - "webrender_traits", "webxr-api", "wgpu-core", "wgpu-types", @@ -6393,7 +6488,7 @@ dependencies = [ name = "script_bindings" version = "0.0.1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "crossbeam-channel", "cssparser", "deny_public_fields", @@ -6433,7 +6528,7 @@ dependencies = [ "app_units", "atomic_refcell", "base", - "canvas_traits", + "compositing_traits", "constellation_traits", "embedder_traits", "euclid", @@ -6445,7 +6540,6 @@ dependencies = [ "ipc-channel", "libc", "malloc_size_of_derive", - "metrics", "net_traits", "pixels", "profile_traits", @@ -6457,9 +6551,7 @@ dependencies = [ "servo_malloc_size_of", "servo_url", "stylo", - "stylo_traits", "webrender_api", - "webrender_traits", ] [[package]] @@ -6478,21 +6570,16 @@ version = "0.0.1" dependencies = [ "background_hang_monitor_api", "base", - "bitflags 2.9.0", "bluetooth_traits", "canvas_traits", + "compositing_traits", "constellation_traits", - "cookie 0.18.1", "crossbeam-channel", "devtools_traits", "embedder_traits", "euclid", - "http 1.3.1", - "hyper_serde", "ipc-channel", "keyboard-types", - "libc", - "log", "malloc_size_of_derive", "media", "net_traits", @@ -6505,13 +6592,9 @@ dependencies = [ "strum_macros", "stylo_atoms", "stylo_traits", - "uuid", - "webdriver", "webgpu_traits", "webrender_api", - "webrender_traits", "webxr-api", - "wgpu-core", ] [[package]] @@ -6529,10 +6612,10 @@ dependencies = [ [[package]] name = "selectors" -version = "0.27.0" -source = "git+https://github.com/servo/stylo?branch=2025-03-15#600b5c42970fdbe301f18c013a0f0ca4ed5f08db" +version = "0.28.0" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cssparser", "derive_more", "fxhash", @@ -6640,7 +6723,7 @@ dependencies = [ [[package]] name = "servo-media" version = "0.1.0" -source = "git+https://github.com/servo/media#c7eab1ae326b8b95b938741660553342f7cd94b7" +source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0" dependencies = [ "once_cell", "servo-media-audio", @@ -6653,7 +6736,7 @@ dependencies = [ [[package]] name = "servo-media-audio" version = "0.2.0" -source = "git+https://github.com/servo/media#c7eab1ae326b8b95b938741660553342f7cd94b7" +source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0" dependencies = [ "byte-slice-cast", "euclid", @@ -6674,7 +6757,7 @@ dependencies = [ [[package]] name = "servo-media-derive" version = "0.1.0" -source = "git+https://github.com/servo/media#c7eab1ae326b8b95b938741660553342f7cd94b7" +source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0" dependencies = [ "proc-macro2", "quote", @@ -6684,7 +6767,7 @@ dependencies = [ [[package]] name = "servo-media-dummy" version = "0.1.0" -source = "git+https://github.com/servo/media#c7eab1ae326b8b95b938741660553342f7cd94b7" +source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0" dependencies = [ "ipc-channel", "servo-media", @@ -6698,7 +6781,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer" version = "0.1.0" -source = "git+https://github.com/servo/media#c7eab1ae326b8b95b938741660553342f7cd94b7" +source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0" dependencies = [ "byte-slice-cast", "glib", @@ -6731,7 +6814,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer-render" version = "0.1.0" -source = "git+https://github.com/servo/media#c7eab1ae326b8b95b938741660553342f7cd94b7" +source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0" dependencies = [ "gstreamer", "gstreamer-video", @@ -6741,7 +6824,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer-render-android" version = "0.1.0" -source = "git+https://github.com/servo/media#c7eab1ae326b8b95b938741660553342f7cd94b7" +source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0" dependencies = [ "glib", "gstreamer", @@ -6755,7 +6838,7 @@ dependencies = [ [[package]] name = "servo-media-gstreamer-render-unix" version = "0.1.0" -source = "git+https://github.com/servo/media#c7eab1ae326b8b95b938741660553342f7cd94b7" +source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0" dependencies = [ "glib", "gstreamer", @@ -6770,7 +6853,7 @@ dependencies = [ [[package]] name = "servo-media-player" version = "0.1.0" -source = "git+https://github.com/servo/media#c7eab1ae326b8b95b938741660553342f7cd94b7" +source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0" dependencies = [ "ipc-channel", "serde", @@ -6782,7 +6865,7 @@ dependencies = [ [[package]] name = "servo-media-streams" version = "0.1.0" -source = "git+https://github.com/servo/media#c7eab1ae326b8b95b938741660553342f7cd94b7" +source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0" dependencies = [ "uuid", ] @@ -6790,18 +6873,28 @@ dependencies = [ [[package]] name = "servo-media-traits" version = "0.1.0" -source = "git+https://github.com/servo/media#c7eab1ae326b8b95b938741660553342f7cd94b7" +source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0" [[package]] name = "servo-media-webrtc" version = "0.1.0" -source = "git+https://github.com/servo/media#c7eab1ae326b8b95b938741660553342f7cd94b7" +source = "git+https://github.com/servo/media#4931a4b380acd9c95b8787cbcfa812823864e2d0" dependencies = [ "log", "servo-media-streams", "uuid", ] +[[package]] +name = "servo-tracing" +version = "0.0.1" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "servo_allocator" version = "0.0.1" @@ -6814,8 +6907,8 @@ dependencies = [ [[package]] name = "servo_arc" -version = "0.4.0" -source = "git+https://github.com/servo/stylo?branch=2025-03-15#600b5c42970fdbe301f18c013a0f0ca4ed5f08db" +version = "0.4.1" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620" dependencies = [ "serde", "stable_deref_trait", @@ -6859,6 +6952,7 @@ version = "0.0.1" dependencies = [ "accountable-refcell", "app_units", + "atomic_refcell", "content-security-policy", "crossbeam-channel", "euclid", @@ -6867,6 +6961,8 @@ dependencies = [ "ipc-channel", "keyboard-types", "markup5ever", + "mime", + "resvg", "servo_allocator", "servo_arc", "smallvec", @@ -6874,9 +6970,13 @@ dependencies = [ "stylo", "stylo_dom", "stylo_malloc_size_of", + "taffy", "thin-vec", "tokio", + "unicode-bidi", + "unicode-script", "url", + "urlpattern", "uuid", "webrender_api", "wr_malloc_size_of", @@ -6926,19 +7026,15 @@ dependencies = [ "euclid", "getopts", "gilrs", - "gleam", "glow", "headers 0.4.0", "hilog", "hitrace", - "http 1.3.1", - "icu_locid", "image", "ipc-channel", "jni", "keyboard-types", "libc", - "libloading", "libservo", "log", "mime_guess", @@ -6947,8 +7043,8 @@ dependencies = [ "net", "net_traits", "nix", - "objc2-app-kit", - "objc2-foundation", + "objc2-app-kit 0.3.1", + "objc2-foundation 0.3.1", "ohos-ime", "ohos-ime-sys", "ohos-vsync", @@ -6956,7 +7052,6 @@ dependencies = [ "rustls", "serde_json", "servo_allocator", - "shellwords", "sig", "surfman", "tokio", @@ -6964,7 +7059,6 @@ dependencies = [ "tracing-perfetto", "tracing-subscriber", "url", - "vergen-git2", "windows-sys 0.59.0", "winit", "winres", @@ -6984,9 +7078,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -7002,16 +7096,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shellwords" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e515aa4699a88148ed5ef96413ceef0048ce95b43fbc955a33bde0a70fcae6" -dependencies = [ - "lazy_static", - "regex", -] - [[package]] name = "shlex" version = "1.3.0" @@ -7050,6 +7134,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "simplecss" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c" +dependencies = [ + "log", +] + [[package]] name = "siphasher" version = "1.0.1" @@ -7082,9 +7175,9 @@ checksum = "d31d263dd118560e1a492922182ab6ca6dc1d03a3bf54e7699993f31a4150e3f" [[package]] name = "smallvec" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" dependencies = [ "serde", ] @@ -7095,7 +7188,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "calloop", "calloop-wayland-source", "cursor-icon", @@ -7134,11 +7227,23 @@ dependencies = [ "serde", ] +[[package]] +name = "snapshot" +version = "0.0.1" +dependencies = [ + "euclid", + "ipc-channel", + "malloc_size_of_derive", + "pixels", + "serde", + "servo_malloc_size_of", +] + [[package]] name = "socket2" -version = "0.5.9" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", @@ -7156,7 +7261,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", ] [[package]] @@ -7192,6 +7297,9 @@ name = "strict-num" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" +dependencies = [ + "float-cmp", +] [[package]] name = "string_cache" @@ -7218,12 +7326,6 @@ dependencies = [ "quote", ] -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - [[package]] name = "strum" version = "0.26.3" @@ -7266,13 +7368,13 @@ dependencies = [ [[package]] name = "stylo" -version = "0.2.0" -source = "git+https://github.com/servo/stylo?branch=2025-03-15#600b5c42970fdbe301f18c013a0f0ca4ed5f08db" +version = "0.3.0" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620" dependencies = [ "app_units", "arrayvec", "atomic_refcell", - "bitflags 2.9.0", + "bitflags 2.9.1", "byteorder", "cssparser", "derive_more", @@ -7281,12 +7383,11 @@ dependencies = [ "fxhash", "icu_segmenter", "indexmap", - "itertools 0.10.5", + "itertools 0.14.0", "itoa", "lazy_static", "log", "malloc_size_of_derive", - "markup5ever", "matches", "mime", "new_debug_unreachable", @@ -7320,12 +7421,13 @@ dependencies = [ "url", "void", "walkdir", + "web_atoms", ] [[package]] name = "stylo_atoms" -version = "0.2.0" -source = "git+https://github.com/servo/stylo?branch=2025-03-15#600b5c42970fdbe301f18c013a0f0ca4ed5f08db" +version = "0.3.0" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620" dependencies = [ "string_cache", "string_cache_codegen", @@ -7333,13 +7435,13 @@ dependencies = [ [[package]] name = "stylo_config" -version = "0.2.0" -source = "git+https://github.com/servo/stylo?branch=2025-03-15#600b5c42970fdbe301f18c013a0f0ca4ed5f08db" +version = "0.3.0" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620" [[package]] name = "stylo_derive" -version = "0.2.0" -source = "git+https://github.com/servo/stylo?branch=2025-03-15#600b5c42970fdbe301f18c013a0f0ca4ed5f08db" +version = "0.3.0" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620" dependencies = [ "darling", "proc-macro2", @@ -7350,17 +7452,17 @@ dependencies = [ [[package]] name = "stylo_dom" -version = "0.2.0" -source = "git+https://github.com/servo/stylo?branch=2025-03-15#600b5c42970fdbe301f18c013a0f0ca4ed5f08db" +version = "0.3.0" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "stylo_malloc_size_of", ] [[package]] name = "stylo_malloc_size_of" -version = "0.2.0" -source = "git+https://github.com/servo/stylo?branch=2025-03-15#600b5c42970fdbe301f18c013a0f0ca4ed5f08db" +version = "0.3.0" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620" dependencies = [ "app_units", "cssparser", @@ -7376,16 +7478,16 @@ dependencies = [ [[package]] name = "stylo_static_prefs" -version = "0.2.0" -source = "git+https://github.com/servo/stylo?branch=2025-03-15#600b5c42970fdbe301f18c013a0f0ca4ed5f08db" +version = "0.3.0" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620" [[package]] name = "stylo_traits" -version = "0.2.0" -source = "git+https://github.com/servo/stylo?branch=2025-03-15#600b5c42970fdbe301f18c013a0f0ca4ed5f08db" +version = "0.3.0" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620" dependencies = [ "app_units", - "bitflags 2.9.0", + "bitflags 2.9.1", "cssparser", "euclid", "malloc_size_of_derive", @@ -7411,7 +7513,7 @@ name = "surfman" version = "0.9.8" source = "git+https://github.com/servo/surfman?rev=f7688b4585f9e0b5d4bf8ee8e4a91e82349610b1#f7688b4585f9e0b5d4bf8ee8e4a91e82349610b1" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg_aliases", "cgl", "cocoa", @@ -7437,9 +7539,19 @@ dependencies = [ [[package]] name = "svg_fmt" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa" +checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" + +[[package]] +name = "svgtypes" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" +dependencies = [ + "kurbo", + "siphasher", +] [[package]] name = "sw-composite" @@ -7455,9 +7567,9 @@ checksum = "e454d048db5527d000bfddb77bd072bbf3a1e2ae785f16d9bd116e07c2ab45eb" [[package]] name = "syn" -version = "2.0.100" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -7466,9 +7578,9 @@ dependencies = [ [[package]] name = "synstructure" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", @@ -7488,9 +7600,9 @@ dependencies = [ [[package]] name = "system-deps" -version = "7.0.3" +version = "7.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d23aaf9f331227789a99e8de4c91bf46703add012bdfd45fdecdfb2975a005" +checksum = "e4be53aa0cba896d2dc615bd42bbc130acdcffa239e0a2d965ea5b3b2a86ffdb" dependencies = [ "cfg-expr", "heck", @@ -7524,9 +7636,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.16" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" [[package]] name = "task_info" @@ -7728,6 +7840,7 @@ dependencies = [ "bytemuck", "cfg-if", "log", + "png", "tiny-skia-path", ] @@ -7762,10 +7875,25 @@ dependencies = [ "serde_json", ] +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "to_shmem" version = "0.2.0" -source = "git+https://github.com/servo/stylo?branch=2025-03-15#600b5c42970fdbe301f18c013a0f0ca4ed5f08db" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620" dependencies = [ "cssparser", "servo_arc", @@ -7778,7 +7906,7 @@ dependencies = [ [[package]] name = "to_shmem_derive" version = "0.1.0" -source = "git+https://github.com/servo/stylo?branch=2025-03-15#600b5c42970fdbe301f18c013a0f0ca4ed5f08db" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#945b70e9a1984cd44ee56b7a674c302b19a4f620" dependencies = [ "darling", "proc-macro2", @@ -7789,9 +7917,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.44.1" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", @@ -7837,9 +7965,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b9590b93e6fcc1739458317cccd391ad3955e2bde8913edf6f95f9e65a8f034" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", @@ -7871,18 +7999,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.8" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.24" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", @@ -7989,9 +8117,9 @@ checksum = "ce607aae8ab0ab3abf3a2723a9ab6f09bb8639ed83fdd888d857b8e556c868d8" [[package]] name = "truetype" -version = "0.47.8" +version = "0.47.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9ffbd4cf26797938aa36b2d03ec051800e6886fbed4bf70333d96b230a575d" +checksum = "05ce9543b570c6e8a392274b67e1001816bce953aa89724e52a4639db02a10e0" dependencies = [ "typeface", ] @@ -8007,6 +8135,9 @@ name = "ttf-parser" version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" +dependencies = [ + "core_maths", +] [[package]] name = "tungstenite" @@ -8055,6 +8186,47 @@ dependencies = [ "arrayvec", ] +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + [[package]] name = "unicase" version = "2.8.1" @@ -8067,6 +8239,18 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" +[[package]] +name = "unicode-bidi-mirroring" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" + +[[package]] +name = "unicode-ccc" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -8091,18 +8275,18 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-vo" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" + [[package]] name = "unicode-width" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - [[package]] name = "universal-hash" version = "0.5.1" @@ -8141,6 +8325,45 @@ dependencies = [ "serde", ] +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "usvg" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef" +dependencies = [ + "base64 0.22.1", + "data-url", + "flate2", + "fontdb", + "imagesize", + "kurbo", + "log", + "pico-args", + "roxmltree", + "rustybuzz", + "simplecss", + "siphasher", + "strict-num", + "svgtypes", + "tiny-skia-path", + "unicode-bidi", + "unicode-script", + "unicode-vo", + "xmlwriter", +] + [[package]] name = "utf-8" version = "0.7.6" @@ -8193,45 +8416,6 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" -[[package]] -name = "vergen" -version = "9.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0d2f179f8075b805a43a2a21728a46f0cc2921b3c58695b28fa8817e103cd9a" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", - "time", - "vergen-lib", -] - -[[package]] -name = "vergen-git2" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86bae87104cb2790cdee615c2bb54729804d307191732ab27b1c5357ea6ddc5" -dependencies = [ - "anyhow", - "derive_builder", - "git2", - "rustversion", - "time", - "vergen", - "vergen-lib", -] - -[[package]] -name = "vergen-lib" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166" -dependencies = [ - "anyhow", - "derive_builder", - "rustversion", -] - [[package]] name = "version-compare" version = "0.2.0" @@ -8372,9 +8556,9 @@ checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "wayland-backend" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7208998eaa3870dad37ec8836979581506e0c5c64c20c9e79e9d2a10d6f47bf" +checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" dependencies = [ "cc", "downcast-rs", @@ -8386,11 +8570,11 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2120de3d33638aaef5b9f4472bff75f07c56379cf76ea320bd3a3d65ecaf73f" +checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "rustix", "wayland-backend", "wayland-scanner", @@ -8402,16 +8586,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.8" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a93029cbb6650748881a00e4922b076092a6a08c11e7fbdb923f064b23968c5d" +checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" dependencies = [ "rustix", "wayland-client", @@ -8420,11 +8604,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.6" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0781cf46869b37e36928f7b432273c0995aa8aed9552c556fb18754420541efc" +checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -8432,11 +8616,11 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ccaacc76703fefd6763022ac565b590fcade92202492381c95b2edfdf7d46b3" +checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -8445,11 +8629,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248a02e6f595aad796561fa82d25601bd2c8c3b145b1c7453fc8f94c1a58f8b2" +checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -8512,12 +8696,24 @@ dependencies = [ ] [[package]] -name = "webdriver" -version = "0.51.0" +name = "web_atoms" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310ce9d3648c5ff1915ca7dd09c44eabb7eb17f9ff4a6e7e5f4a902c8d1e269f" +checksum = "0b9c5f0bc545ea3b20b423e33b9b457764de0b3730cd957f6c6aa6c301785f6e" dependencies = [ - "base64 0.21.7", + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + +[[package]] +name = "webdriver" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91d53921e1bef27512fa358179c9a22428d55778d2c2ae3c5c37a52b82ce6e92" +dependencies = [ + "base64 0.22.1", "bytes", "cookie 0.16.2", "http 0.2.12", @@ -8550,9 +8746,7 @@ dependencies = [ "ipc-channel", "keyboard-types", "log", - "net_traits", "pixels", - "script_traits", "serde", "serde_json", "servo_config", @@ -8562,22 +8756,47 @@ dependencies = [ "webdriver", ] +[[package]] +name = "webgl" +version = "0.0.1" +dependencies = [ + "bitflags 2.9.1", + "byteorder", + "canvas_traits", + "compositing_traits", + "crossbeam-channel", + "euclid", + "fnv", + "glow", + "half", + "ipc-channel", + "itertools 0.14.0", + "log", + "pixels", + "snapshot", + "surfman", + "webrender", + "webrender_api", + "webxr", + "webxr-api", +] + [[package]] name = "webgpu" version = "0.0.1" dependencies = [ "arrayvec", "base", + "compositing_traits", "euclid", "ipc-channel", "log", "serde", "servo_config", - "servo_malloc_size_of", + "snapshot", "webgpu_traits", "webrender", "webrender_api", - "webrender_traits", "wgpu-core", "wgpu-types", ] @@ -8591,6 +8810,7 @@ dependencies = [ "ipc-channel", "serde", "servo_malloc_size_of", + "snapshot", "webrender_api", "wgpu-core", "wgpu-types", @@ -8598,9 +8818,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.26.8" +version = "0.26.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9" +checksum = "37493cadf42a2a939ed404698ded7fb378bf301b5011f973361779a3a74f8c93" dependencies = [ "rustls-pki-types", ] @@ -8608,11 +8828,11 @@ dependencies = [ [[package]] name = "webrender" version = "0.66.0" -source = "git+https://github.com/servo/webrender?branch=0.66#88462530746749163bcf1dc89be20a19f2394e71" +source = "git+https://github.com/servo/webrender?branch=0.67#ae2477d9a6da403e5b5dce8a17415a2cd1563074" dependencies = [ "allocator-api2", "bincode", - "bitflags 2.9.0", + "bitflags 2.9.1", "build-parallel", "byteorder", "derive_more", @@ -8643,10 +8863,10 @@ dependencies = [ [[package]] name = "webrender_api" version = "0.66.0" -source = "git+https://github.com/servo/webrender?branch=0.66#88462530746749163bcf1dc89be20a19f2394e71" +source = "git+https://github.com/servo/webrender?branch=0.67#ae2477d9a6da403e5b5dce8a17415a2cd1563074" dependencies = [ "app_units", - "bitflags 2.9.0", + "bitflags 2.9.1", "byteorder", "crossbeam-channel", "euclid", @@ -8664,36 +8884,12 @@ dependencies = [ [[package]] name = "webrender_build" version = "0.0.2" -source = "git+https://github.com/servo/webrender?branch=0.66#88462530746749163bcf1dc89be20a19f2394e71" +source = "git+https://github.com/servo/webrender?branch=0.67#ae2477d9a6da403e5b5dce8a17415a2cd1563074" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "lazy_static", ] -[[package]] -name = "webrender_traits" -version = "0.0.1" -dependencies = [ - "base", - "constellation_traits", - "dpi", - "embedder_traits", - "euclid", - "gleam", - "glow", - "image", - "ipc-channel", - "libc", - "log", - "raw-window-handle", - "serde", - "servo-media", - "servo_geometry", - "stylo", - "surfman", - "webrender_api", -] - [[package]] name = "webxr" version = "0.0.1" @@ -8715,6 +8911,7 @@ dependencies = [ name = "webxr-api" version = "0.0.1" dependencies = [ + "embedder_traits", "euclid", "ipc-channel", "log", @@ -8723,21 +8920,23 @@ dependencies = [ [[package]] name = "weezl" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" [[package]] name = "wgpu-core" -version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=2f255edc60e9669c8c737464c59af10d59a31126#2f255edc60e9669c8c737464c59af10d59a31126" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19813e647da7aa3cdaa84f5846e2c64114970ea7c86b1e6aae8be08091f4bdc" dependencies = [ "arrayvec", + "bit-set", "bit-vec", - "bitflags 2.9.0", + "bitflags 2.9.1", "cfg_aliases", "document-features", - "hashbrown 0.14.5", + "hashbrown", "indexmap", "log", "naga", @@ -8757,40 +8956,45 @@ dependencies = [ [[package]] name = "wgpu-core-deps-apple" -version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=2f255edc60e9669c8c737464c59af10d59a31126#2f255edc60e9669c8c737464c59af10d59a31126" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd488b3239b6b7b185c3b045c39ca6bf8af34467a4c5de4e0b1a564135d093d" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=2f255edc60e9669c8c737464c59af10d59a31126#2f255edc60e9669c8c737464c59af10d59a31126" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09ad7aceb3818e52539acc679f049d3475775586f3f4e311c30165cf2c00445" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=2f255edc60e9669c8c737464c59af10d59a31126#2f255edc60e9669c8c737464c59af10d59a31126" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cba5fb5f7f9c98baa7c889d444f63ace25574833df56f5b817985f641af58e46" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-hal" -version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=2f255edc60e9669c8c737464c59af10d59a31126#2f255edc60e9669c8c737464c59af10d59a31126" +version = "25.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb7c4a1dc42ff14c23c9b11ebf1ee85cde661a9b1cf0392f79c1faca5bc559fb" dependencies = [ "android_system_properties", "arrayvec", "ash", "bit-set", - "bitflags 2.9.0", + "bitflags 2.9.1", "block", "bytemuck", + "cfg-if", "cfg_aliases", "core-graphics-types", "glow", @@ -8798,7 +9002,7 @@ dependencies = [ "gpu-alloc", "gpu-allocator", "gpu-descriptor", - "hashbrown 0.14.5", + "hashbrown", "js-sys", "khronos-egl", "libc", @@ -8808,7 +9012,6 @@ dependencies = [ "naga", "ndk-sys 0.5.0+25.2.9519653", "objc", - "once_cell", "ordered-float", "parking_lot", "profiling", @@ -8825,10 +9028,12 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=2f255edc60e9669c8c737464c59af10d59a31126#2f255edc60e9669c8c737464c59af10d59a31126" +version = "25.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aa49460c2a8ee8edba3fca54325540d904dd85b2e086ada762767e17d06e8bc" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", + "bytemuck", "js-sys", "log", "serde", @@ -9218,14 +9423,14 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winit" -version = "0.30.9" +version = "0.30.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a809eacf18c8eca8b6635091543f02a5a06ddf3dad846398795460e6e0ae3cc0" +checksum = "a4409c10174df8779dc29a4788cac85ed84024ccbc1743b776b21a520ee1aaf4" dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.9.0", + "bitflags 2.9.1", "block2", "bytemuck", "calloop", @@ -9239,9 +9444,9 @@ dependencies = [ "libc", "memmap2", "ndk", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", "objc2-ui-kit", "orbclient", "percent-encoding", @@ -9270,9 +9475,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.4" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] @@ -9298,7 +9503,7 @@ dependencies = [ [[package]] name = "wr_glyph_rasterizer" version = "0.1.0" -source = "git+https://github.com/servo/webrender?branch=0.66#88462530746749163bcf1dc89be20a19f2394e71" +source = "git+https://github.com/servo/webrender?branch=0.67#ae2477d9a6da403e5b5dce8a17415a2cd1563074" dependencies = [ "core-foundation 0.9.4", "core-graphics", @@ -9322,8 +9527,8 @@ dependencies = [ [[package]] name = "wr_malloc_size_of" -version = "0.0.3" -source = "git+https://github.com/servo/webrender?branch=0.66#88462530746749163bcf1dc89be20a19f2394e71" +version = "0.2.0" +source = "git+https://github.com/servo/webrender?branch=0.67#ae2477d9a6da403e5b5dce8a17415a2cd1563074" dependencies = [ "app_units", "euclid", @@ -9426,7 +9631,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.0", + "bitflags 2.9.1", "dlib", "log", "once_cell", @@ -9441,21 +9646,27 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" +checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" [[package]] name = "xml5ever" -version = "0.21.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9f61eba2105dcadbee9232cfcf75c96abea184bf3805a8bfe022cf7fe7fa11d" +checksum = "0a91563ba5a5ab749488164063f1317e327ca1daa80f00e5bd1e670ad0d78154" dependencies = [ "log", "mac", "markup5ever", ] +[[package]] +name = "xmlwriter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" + [[package]] name = "yeslogic-fontconfig-sys" version = "6.0.0" @@ -9572,6 +9783,12 @@ dependencies = [ "syn", ] +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + [[package]] name = "zune-inflate" version = "0.2.54" @@ -9580,3 +9797,12 @@ checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" dependencies = [ "simd-adler32", ] + +[[package]] +name = "zune-jpeg" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index 413a41c2e08..c290a21f1fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ publish = false rust-version = "1.85.0" [workspace.dependencies] -accountable-refcell = "0.2.0" +accountable-refcell = "0.2.2" aes = "0.8.4" aes-gcm = "0.10.3" aes-kw = { version = "0.2.1", features = ["alloc"] } @@ -41,7 +41,7 @@ chrono = { version = "0.4", features = ["serde"] } cipher = { version = "0.4.4", features = ["alloc"] } compositing_traits = { path = "components/shared/compositing" } constellation_traits = { path = "components/shared/constellation" } -content-security-policy = { version = "0.5", features = ["serde"] } +content-security-policy = { git = "https://github.com/servo/rust-content-security-policy/", branch = "servo-csp", features = ["serde"] } cookie = { package = "cookie", version = "0.18" } crossbeam-channel = "0.5" cssparser = { version = "0.35", features = ["serde"] } @@ -70,8 +70,8 @@ gstreamer-sys = "0.23" gstreamer-video = "0.23" harfbuzz-sys = "0.6.1" headers = "0.4" -hitrace = "0.1.4" -html5ever = "0.30" +hitrace = "0.1.5" +html5ever = "0.31" http = "1.3" http-body-util = "0.1" hyper = "1.6" @@ -82,9 +82,9 @@ icu_locid = "1.5.0" icu_segmenter = "1.5.0" image = "0.24" imsz = "0.2" -indexmap = { version = "2.8.0", features = ["std"] } +indexmap = { version = "2.9.0", features = ["std"] } ipc-channel = "0.19" -itertools = "0.13" +itertools = "0.14" js = { package = "mozjs", git = "https://github.com/servo/mozjs" } keyboard-types = "0.7" libc = "0.2" @@ -92,7 +92,7 @@ log = "0.4" mach2 = "0.4" malloc_size_of = { package = "servo_malloc_size_of", path = "components/malloc_size_of" } malloc_size_of_derive = "0.1" -markup5ever = "0.15" +markup5ever = "0.16.1" memmap2 = "0.9.5" mime = "0.3.13" mime_guess = "2.0.5" @@ -113,32 +113,35 @@ rand_isaac = "0.3" raw-window-handle = "0.6" rayon = "1" regex = "1.11" +resvg = "0.45.0" rustls = { version = "0.23", default-features = false, features = ["logging", "std", "tls12"] } rustls-pemfile = "2.0" -rustls-pki-types = "1.11" +rustls-pki-types = "1.12" script_layout_interface = { path = "components/shared/script_layout" } script_traits = { path = "components/shared/script" } -selectors = { git = "https://github.com/servo/stylo", branch = "2025-03-15" } +selectors = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } serde = "1.0.219" serde_bytes = "0.11" serde_json = "1.0" servo-media = { git = "https://github.com/servo/media" } servo-media-dummy = { git = "https://github.com/servo/media" } servo-media-gstreamer = { git = "https://github.com/servo/media" } -servo_arc = { git = "https://github.com/servo/stylo", branch = "2025-03-15" } +servo-tracing = { path = "components/servo_tracing" } +servo_arc = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } smallbitvec = "2.6.0" -smallvec = "1.14" +smallvec = "1.15" +snapshot = { path = "./components/shared/snapshot" } static_assertions = "1.1" string_cache = "0.8" string_cache_codegen = "0.5" strum = "0.26" strum_macros = "0.26" -stylo = { git = "https://github.com/servo/stylo", branch = "2025-03-15" } -stylo_atoms = { git = "https://github.com/servo/stylo", branch = "2025-03-15" } -stylo_config = { git = "https://github.com/servo/stylo", branch = "2025-03-15" } -stylo_dom = { git = "https://github.com/servo/stylo", branch = "2025-03-15" } -stylo_malloc_size_of = { git = "https://github.com/servo/stylo", branch = "2025-03-15" } -stylo_traits = { git = "https://github.com/servo/stylo", branch = "2025-03-15" } +stylo = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } +stylo_atoms = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } +stylo_config = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } +stylo_dom = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } +stylo_malloc_size_of = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } +stylo_traits = { git = "https://github.com/servo/stylo", branch = "2025-05-01" } surfman = { git = "https://github.com/servo/surfman", rev = "f7688b4585f9e0b5d4bf8ee8e4a91e82349610b1", features = ["chains"] } syn = { version = "2", default-features = false, features = ["clone-impls", "derive", "parsing"] } synstructure = "0.13" @@ -160,22 +163,22 @@ unicode-properties = { version = "0.1.3", features = ["emoji"] } unicode-script = "0.5" unicode-segmentation = "1.12.0" url = "2.5" +urlpattern = "0.3" uuid = { version = "1.12.1", features = ["v4"] } -webdriver = "0.51.0" +webdriver = "0.53.0" webgpu_traits = { path = "components/shared/webgpu" } webpki-roots = "0.26" -webrender = { git = "https://github.com/servo/webrender", branch = "0.66", features = ["capture"] } -webrender_api = { git = "https://github.com/servo/webrender", branch = "0.66" } -webrender_traits = { path = "components/shared/webrender" } +webrender = { git = "https://github.com/servo/webrender", branch = "0.67", features = ["capture"] } +webrender_api = { git = "https://github.com/servo/webrender", branch = "0.67" } webxr-api = { path = "components/shared/webxr" } -wgpu-core = { git = "https://github.com/gfx-rs/wgpu", rev = "2f255edc60e9669c8c737464c59af10d59a31126" } -wgpu-types = { git = "https://github.com/gfx-rs/wgpu", rev = "2f255edc60e9669c8c737464c59af10d59a31126" } +wgpu-core = "25" +wgpu-types = "25" winapi = "0.3" windows-sys = "0.59" wio = "0.2" -wr_malloc_size_of = { git = "https://github.com/servo/webrender", branch = "0.66" } +wr_malloc_size_of = { git = "https://github.com/servo/webrender", branch = "0.67" } xi-unicode = "0.3.0" -xml5ever = "0.21" +xml5ever = "0.22" [profile.release] opt-level = 3 @@ -242,3 +245,7 @@ codegen-units = 1 # # [patch."https://github.com/servo/"] # = { path = "/path/to/local/checkout" } +# +# [patch."https://github.com/servo/rust-content-security-policy"] +# content-security-policy = { path = "../rust-content-security-policy/" } +# content-security-policy = { git = "https://github.com/timvdlippe/rust-content-security-policy/", branch = "fix-report-checks", features = ["serde"] } diff --git a/README.md b/README.md index 8147702f92d..8d51be16439 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,15 @@ Servo is a prototype web browser engine written in the [Rust](https://github.com/rust-lang/rust) language. It is currently developed on 64-bit macOS, 64-bit Linux, 64-bit Windows, 64-bit OpenHarmony, and Android. -Servo welcomes contribution from everyone. Check out [The Servo Book](https://book.servo.org) to get started, or go to [servo.org](https://servo.org/) for news and guides. +Servo welcomes contribution from everyone. Check out: + +- The [Servo Book](https://book.servo.org) for documentation +- [servo.org](https://servo.org/) for news and guides + +Coordination of Servo development happens: +- Here in the Github Issues +- On the [Servo Zulip](https://servo.zulipchat.com/) +- In video calls advertised in the [Servo Project](https://github.com/servo/project/issues) repo. ## Getting started diff --git a/SECURITY.md b/SECURITY.md index 0c5dd453447..c4e75025880 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,4 +1,4 @@ # Security Policy -Given that Servo does not yet have customers or products, we are comfortable accepting the security related vulnerabilities as a [new GitHub issue](https://github.com/servo/servo/security/advisories/new) for now. +Given that Servo does not yet have customers or products, we are comfortable accepting the security related issues as [GitHub security reports](https://github.com/servo/servo/security/advisories/new) for now. diff --git a/components/bluetooth/README.md b/components/bluetooth/README.md index ec6e536d66a..1e3a42a2e1b 100644 --- a/components/bluetooth/README.md +++ b/components/bluetooth/README.md @@ -1,55 +1,165 @@ -# Bluetooth Rust lib using macOS CoreBluetooth +# Bluetooth -[![Build Status](https://travis-ci.org/akosthekiss/blurmac.svg?branch=master)](https://travis-ci.org/akosthekiss/blurmac) -[![Crates.io](https://img.shields.io/crates/v/blurmac.svg)](https://crates.io/crates/blurmac) +Servo-specific APIs to access Bluetooth devices. -The main aim of BlurMac is to enable [WebBluetooth](https://webbluetoothcg.github.io) -in [Servo](https://github.com/servo/servo) on macOS. Thus, API and implementation -decisions are affected by the encapsulating [Devices](https://github.com/servo/devices), -and the sibling [BlurZ](https://github.com/szeged/blurz) and [BlurDroid](https://github.com/szeged/blurdroid) -crates. +Bluetooth related code is located in `bluetooth.rs`. +### Implementation -## Run Servo with WebBluetooth Enabled +Underlying dependency crates: -Usually, you don't want to work with BlurMac on its own but use it within Servo. -So, most probably you'll want to run Servo with WebBluetooth enabled: +- Android platform: [blurdroid](https://crates.io/crates/blurdroid) +- Linux platform: [blurz](https://crates.io/crates/blurz) +- macOS platform: [blurmac](https://crates.io/crates/blurmac) +- `Fake` prefixed structures: [blurmock](https://crates.io/crates/blurmock) -``` -RUST_LOG=blurmac \ -./mach run \ - --dev \ - --pref=dom.bluetooth.enabled \ - --pref=dom.permissions.testing.allowed_in_nonsecure_contexts \ - URL +`Empty` prefixed structures are located in `empty.rs`. + +### Usage + +#### Without the *bluetooth-test* feature + +There are three supported platforms (Android, Linux, macOS), on other platforms we fall back to a default (`Empty` prefixed) implementation. Each enum (`BluetoothAdapter`, `BluetoothDevice`, etc.) will contain only one variant for each targeted platform. See the following `BluetoothAdapter` example: + +Android: + +```rust +pub enum BluetoothAdapter { + Android(Arc), +} ``` -Notes: -* The above command is actually not really BlurMac-specific (except for the `RUST_LOG` - part). It runs Servo with WBT enabled on any platform where WBT is supported. -* You don't need the `RUST_LOG=blurmac` part if you don't want to see BlurMac debug - messages on the console. -* You don't need the `--dev` part if you want to run a release build. -* You don't need the `--pref=dom.permissions.testing.allowed_in_nonsecure_contexts` - part if your `URL` is https (but you do need it if you test a local file). +Linux: +```rust +pub enum BluetoothAdapter { + Bluez(Arc), +} +``` -## Known Issues +macOS: -* Device RSSI can not be retrieved yet. -* Support for included services is incomplete. -* Descriptors are not supported yet. -* Notifications on characteristics are not supported yet (the limitation comes from - Devices). +```rust +pub enum BluetoothAdapter { + Mac(Arc), +} +``` +Unsupported platforms: -## Compatibility +```rust +pub enum BluetoothAdapter { + Empty(Arc), +} +``` -Tested on: +You will have a platform specific adapter, e.g. on Android target, `BluetoothAdapter::init()` will create a `BluetoothAdapter::Android` enum variant, which wraps an `Arc`. -* macOS Sierra 10.12. +```rust +pub fn init() -> Result> { + let blurdroid_adapter = try!(BluetoothAdapterAndroid::get_adapter()); + Ok(BluetoothAdapter::Android(Arc::new(blurdroid_adapter))) +} +``` +On each platform you can call the same functions to reach the same GATT hierarchy elements. The following code can access the same Bluetooth device on all supported platforms: -## Copyright and Licensing +```rust +use device::{BluetoothAdapter, BluetoothDevice}; -Licensed under the BSD 3-Clause [License](LICENSE.md). +fn main() { + // Get the bluetooth adapter. + let adapter = BluetoothAdpater::init().expect("No bluetooth adapter found!"); + // Get a device with the id 01:2A:00:4D:00:04 if it exists. + let device = adapter.get_device("01:2A:00:4D:00:04".to_owned() /*device address*/) + .expect("No bluetooth device found!"); +} +``` + +#### With the *bluetooth-test* feature + +Each enum (`BluetoothAdapter`, `BluetoothDevice`, etc.) will contain one variant of the three possible default target, and a `Mock` variant, which wraps a `Fake` structure. + +Android: + +```rust +pub enum BluetoothAdapter { + Android(Arc), + Mock(Arc), +} +``` + +Linux: + +```rust +pub enum BluetoothAdapter { + Bluez(Arc), + Mock(Arc), +} +``` + +macOS: + +```rust +pub enum BluetoothAdapter { + Mac(Arc), + Mock(Arc), +} +``` + +Unsupported platforms: + +```rust +pub enum BluetoothAdapter { + Empty(Arc), + Mock(Arc), +} +``` + +Beside the platform specific structures, you can create and access mock adapters, devices, services etc. These mock structures implements all the platform specific functions too. To create a mock GATT hierarchy, first you need to call the `BluetoothAdapter::init_mock()` function, insted of `BluetoothAdapter::init()`. + +```rust +use device::{BluetoothAdapter, BluetoothDevice}; +use std::String; + +// This function takes a BluetoothAdapter, +// and print the ids of the devices, which the adapter can find. +fn print_device_ids(adapter: &BluetoothAdpater) { + let devices = match adapter.get_devices().expect("No devices on the adapter!"); + for device in devices { + println!("{:?}", device.get_id()); + } +} + +fn main() { +// This code uses a real adapter. + // Get the bluetooth adapter. + let adapter = BluetoothAdpater::init().expect("No bluetooth adapter found!"); + // Get a device with the id 01:2A:00:4D:00:04 if it exists. + let device = adapter.get_device("01:2A:00:4D:00:04".to_owned() /*device address*/) + .expect("No bluetooth device found!"); + +// This code uses a mock adapter. + // Creating a mock adapter. + let mock_adapter = BluetoothAdpater::init_mock().unwrap(); + // Creating a mock device. + let mock_device = + BluetoothDevice::create_mock_device(mock_adapter, + "device_id_string_goes_here".to_owned()) + .unwrap(); + // Changing its device_id. + let new_device_id = String::from("new_device_id_string".to_owned()); + mock_device.set_id(new_device_id.clone()); + // Calling the get_id function must return the last id we set. + assert_equals!(new_device_id, mock_device.get_id()); + // Getting the mock_device with its id + // must return the same mock device object we created before. + assert_equals!(Some(mock_device), + mock_adapter.get_device(new_device_id.clone()).unwrap()); + // The print_device_ids function accept real and mock adapters too. + print_device_ids(&adapter); + print_device_ids(&mock_adapter); +} +``` + +Calling a test function on a not `Mock` structure, will result an error with the message: `Error! Test functions are not supported on real devices!`. diff --git a/components/canvas/Cargo.toml b/components/canvas/Cargo.toml index 10ac5a35cd4..7aadc8a9bb5 100644 --- a/components/canvas/Cargo.toml +++ b/components/canvas/Cargo.toml @@ -11,37 +11,24 @@ rust-version.workspace = true name = "canvas" path = "lib.rs" -[features] -webgl_backtrace = ["canvas_traits/webgl_backtrace"] -webxr = ["dep:webxr", "dep:webxr-api"] - [dependencies] app_units = { workspace = true } -bitflags = { workspace = true } -byteorder = { workspace = true } canvas_traits = { workspace = true } +compositing_traits = { workspace = true } crossbeam-channel = { workspace = true } cssparser = { workspace = true } euclid = { workspace = true } -fnv = { workspace = true } font-kit = "0.14" fonts = { path = "../fonts" } -glow = { workspace = true } -half = "2" ipc-channel = { workspace = true } log = { workspace = true } lyon_geom = "1.0.4" net_traits = { workspace = true } -num-traits = { workspace = true } pixels = { path = "../pixels" } range = { path = "../range" } raqote = "0.8.5" servo_arc = { workspace = true } +snapshot = { workspace = true } stylo = { workspace = true } -surfman = { workspace = true } unicode-script = { workspace = true } -webrender = { workspace = true } webrender_api = { workspace = true } -webrender_traits = { workspace = true } -webxr = { path = "../webxr", features = ["ipc"], optional = true } -webxr-api = { workspace = true, features = ["ipc"], optional = true } diff --git a/components/canvas/backend.rs b/components/canvas/backend.rs new file mode 100644 index 00000000000..7e348fbc9b9 --- /dev/null +++ b/components/canvas/backend.rs @@ -0,0 +1,270 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use canvas_traits::canvas::{ + CompositionOrBlending, FillOrStrokeStyle, LineCapStyle, LineJoinStyle, +}; +use euclid::Angle; +use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; +use lyon_geom::Arc; +use style::color::AbsoluteColor; + +use crate::canvas_data::{CanvasPaintState, Filter, TextRun}; + +pub(crate) trait Backend: Clone + Sized { + type Pattern<'a>: PatternHelpers + Clone; + type StrokeOptions: StrokeOptionsHelpers + Clone; + type Color: Clone; + type DrawOptions: DrawOptionsHelpers + Clone; + type CompositionOp; + type DrawTarget: GenericDrawTarget; + type PathBuilder: GenericPathBuilder; + type SourceSurface; + type Bytes<'a>: AsRef<[u8]>; + type Path: PathHelpers + Clone; + type GradientStop; + type GradientStops; + + fn get_composition_op(&self, opts: &Self::DrawOptions) -> Self::CompositionOp; + fn need_to_draw_shadow(&self, color: &Self::Color) -> bool; + fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_, Self>); + fn set_fill_style( + &mut self, + style: FillOrStrokeStyle, + state: &mut CanvasPaintState<'_, Self>, + drawtarget: &Self::DrawTarget, + ); + fn set_stroke_style( + &mut self, + style: FillOrStrokeStyle, + state: &mut CanvasPaintState<'_, Self>, + drawtarget: &Self::DrawTarget, + ); + fn set_global_composition( + &mut self, + op: CompositionOrBlending, + state: &mut CanvasPaintState<'_, Self>, + ); + fn create_drawtarget(&self, size: Size2D) -> Self::DrawTarget; + fn new_paint_state<'a>(&self) -> CanvasPaintState<'a, Self>; +} + +// This defines required methods for a DrawTarget (currently only implemented for raqote). The +// prototypes are derived from the now-removed Azure backend's methods. +pub(crate) trait GenericDrawTarget { + fn clear_rect(&mut self, rect: &Rect); + fn copy_surface( + &mut self, + surface: B::SourceSurface, + source: Rect, + destination: Point2D, + ); + fn create_path_builder(&self) -> B::PathBuilder; + fn create_similar_draw_target(&self, size: &Size2D) -> Self; + fn create_source_surface_from_data(&self, data: &[u8]) -> Option; + fn draw_surface( + &mut self, + surface: B::SourceSurface, + dest: Rect, + source: Rect, + filter: Filter, + draw_options: &B::DrawOptions, + ); + fn draw_surface_with_shadow( + &self, + surface: B::SourceSurface, + dest: &Point2D, + color: &B::Color, + offset: &Vector2D, + sigma: f32, + operator: B::CompositionOp, + ); + fn fill(&mut self, path: &B::Path, pattern: B::Pattern<'_>, draw_options: &B::DrawOptions); + fn fill_text( + &mut self, + text_runs: Vec, + start: Point2D, + pattern: &B::Pattern<'_>, + draw_options: &B::DrawOptions, + ); + fn fill_rect( + &mut self, + rect: &Rect, + pattern: B::Pattern<'_>, + draw_options: Option<&B::DrawOptions>, + ); + fn get_size(&self) -> Size2D; + fn get_transform(&self) -> Transform2D; + fn pop_clip(&mut self); + fn push_clip(&mut self, path: &B::Path); + fn set_transform(&mut self, matrix: &Transform2D); + fn stroke( + &mut self, + path: &B::Path, + pattern: B::Pattern<'_>, + stroke_options: &B::StrokeOptions, + draw_options: &B::DrawOptions, + ); + fn stroke_line( + &mut self, + start: Point2D, + end: Point2D, + pattern: B::Pattern<'_>, + stroke_options: &B::StrokeOptions, + draw_options: &B::DrawOptions, + ); + fn stroke_rect( + &mut self, + rect: &Rect, + pattern: B::Pattern<'_>, + stroke_options: &B::StrokeOptions, + draw_options: &B::DrawOptions, + ); + fn surface(&self) -> B::SourceSurface; + fn bytes(&'_ self) -> B::Bytes<'_>; +} + +/// A generic PathBuilder that abstracts the interface for azure's and raqote's PathBuilder. +pub(crate) trait GenericPathBuilder { + fn arc( + &mut self, + origin: Point2D, + radius: f32, + start_angle: f32, + end_angle: f32, + anticlockwise: bool, + ) { + Self::ellipse( + self, + origin, + radius, + radius, + 0., + start_angle, + end_angle, + anticlockwise, + ); + } + fn bezier_curve_to( + &mut self, + control_point1: &Point2D, + control_point2: &Point2D, + control_point3: &Point2D, + ); + fn close(&mut self); + #[allow(clippy::too_many_arguments)] + fn ellipse( + &mut self, + origin: Point2D, + radius_x: f32, + radius_y: f32, + rotation_angle: f32, + start_angle: f32, + end_angle: f32, + anticlockwise: bool, + ) { + let mut start = Angle::radians(start_angle); + let mut end = Angle::radians(end_angle); + + // Wrap angles mod 2 * PI if necessary + if !anticlockwise && start > end + Angle::two_pi() || + anticlockwise && end > start + Angle::two_pi() + { + start = start.positive(); + end = end.positive(); + } + + // Calculate the total arc we're going to sweep. + let sweep = match anticlockwise { + true => { + if end - start == Angle::two_pi() { + -Angle::two_pi() + } else if end > start { + -(Angle::two_pi() - (end - start)) + } else { + -(start - end) + } + }, + false => { + if start - end == Angle::two_pi() { + Angle::two_pi() + } else if start > end { + Angle::two_pi() - (start - end) + } else { + end - start + } + }, + }; + + let arc: Arc = Arc { + center: origin, + radii: Vector2D::new(radius_x, radius_y), + start_angle: start, + sweep_angle: sweep, + x_rotation: Angle::radians(rotation_angle), + }; + + self.line_to(arc.from()); + + arc.for_each_quadratic_bezier(&mut |q| { + self.quadratic_curve_to(&q.ctrl, &q.to); + }); + } + fn get_current_point(&mut self) -> Option>; + fn line_to(&mut self, point: Point2D); + fn move_to(&mut self, point: Point2D); + fn quadratic_curve_to(&mut self, control_point: &Point2D, end_point: &Point2D); + fn svg_arc( + &mut self, + radius_x: f32, + radius_y: f32, + rotation_angle: f32, + large_arc: bool, + sweep: bool, + end_point: Point2D, + ) { + let Some(start) = self.get_current_point() else { + return; + }; + + let arc = lyon_geom::SvgArc { + from: start, + to: end_point, + radii: lyon_geom::vector(radius_x, radius_y), + x_rotation: lyon_geom::Angle::degrees(rotation_angle), + flags: lyon_geom::ArcFlags { large_arc, sweep }, + }; + + arc.for_each_quadratic_bezier(&mut |q| { + self.quadratic_curve_to(&q.ctrl, &q.to); + }); + } + fn finish(&mut self) -> B::Path; +} + +pub(crate) trait PatternHelpers { + fn is_zero_size_gradient(&self) -> bool; + fn draw_rect(&self, rect: &Rect) -> Rect; +} + +pub(crate) trait StrokeOptionsHelpers { + fn set_line_width(&mut self, _val: f32); + fn set_miter_limit(&mut self, _val: f32); + fn set_line_join(&mut self, val: LineJoinStyle); + fn set_line_cap(&mut self, val: LineCapStyle); + fn set_line_dash(&mut self, items: Vec); + fn set_line_dash_offset(&mut self, offset: f32); +} + +pub(crate) trait DrawOptionsHelpers { + fn set_alpha(&mut self, val: f32); +} + +pub(crate) trait PathHelpers { + fn transformed_copy_to_builder(&self, transform: &Transform2D) -> B::PathBuilder; + + fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D) -> bool; + + fn copy_to_builder(&self) -> B::PathBuilder; +} diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 34942388c23..ea30589d0af 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -2,11 +2,13 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +use std::marker::PhantomData; use std::mem; use std::sync::Arc; use app_units::Au; use canvas_traits::canvas::*; +use compositing_traits::{CrossProcessCompositorApi, ImageUpdate, SerializableImageData}; use euclid::default::{Box2D, Point2D, Rect, Size2D, Transform2D, Vector2D}; use euclid::point2; use fonts::{ @@ -15,20 +17,26 @@ use fonts::{ }; use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; use log::warn; -use num_traits::ToPrimitive; use range::Range; use servo_arc::Arc as ServoArc; +use snapshot::Snapshot; use style::color::AbsoluteColor; use style::properties::style_structs::Font as FontStyleStruct; use unicode_script::Script; use webrender_api::units::RectExt as RectExt_; use webrender_api::{ImageDescriptor, ImageDescriptorFlags, ImageFormat, ImageKey}; -use webrender_traits::{CrossProcessCompositorApi, ImageUpdate, SerializableImageData}; -use crate::raqote_backend::Repetition; +use crate::backend::{ + Backend, DrawOptionsHelpers as _, GenericDrawTarget as _, GenericPathBuilder, PathHelpers, + PatternHelpers, StrokeOptionsHelpers as _, +}; -fn to_path(path: &[PathSegment], mut builder: Box) -> Path { - let mut build_ref = PathBuilderRef { +// Asserts on WR texture cache update for zero sized image with raw data. +// https://github.com/servo/webrender/blob/main/webrender/src/texture_cache.rs#L1475 +const MIN_WR_IMAGE_SIZE: Size2D = Size2D::new(1, 1); + +fn to_path(path: &[PathSegment], mut builder: B::PathBuilder) -> B::Path { + let mut build_ref = PathBuilderRef:: { builder: &mut builder, transform: Transform2D::identity(), }; @@ -107,20 +115,20 @@ fn to_path(path: &[PathSegment], mut builder: Box) -> Pa /// draw the path, we convert it back to userspace and draw it /// with the correct transform applied. /// TODO: De-abstract now that Azure is removed? -enum PathState { +enum PathState { /// Path builder in user-space. If a transform has been applied /// but no further path operations have occurred, it is stored /// in the optional field. - UserSpacePathBuilder(Box, Option>), + UserSpacePathBuilder(B::PathBuilder, Option>), /// Path builder in device-space. - DeviceSpacePathBuilder(Box), + DeviceSpacePathBuilder(B::PathBuilder), /// Path in user-space. If a transform has been applied but /// but no further path operations have occurred, it is stored /// in the optional field. - UserSpacePath(Path, Option>), + UserSpacePath(B::Path, Option>), } -impl PathState { +impl PathState { fn is_path(&self) -> bool { match *self { PathState::UserSpacePath(..) => true, @@ -128,7 +136,7 @@ impl PathState { } } - fn path(&self) -> &Path { + fn path(&self) -> &B::Path { match *self { PathState::UserSpacePath(ref p, _) => p, PathState::UserSpacePathBuilder(..) | PathState::DeviceSpacePathBuilder(..) => { @@ -138,84 +146,14 @@ impl PathState { } } -pub trait Backend { - fn get_composition_op(&self, opts: &DrawOptions) -> CompositionOp; - fn need_to_draw_shadow(&self, color: &Color) -> bool; - fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_>); - fn set_fill_style( - &mut self, - style: FillOrStrokeStyle, - state: &mut CanvasPaintState<'_>, - drawtarget: &dyn GenericDrawTarget, - ); - fn set_stroke_style( - &mut self, - style: FillOrStrokeStyle, - state: &mut CanvasPaintState<'_>, - drawtarget: &dyn GenericDrawTarget, - ); - fn set_global_composition( - &mut self, - op: CompositionOrBlending, - state: &mut CanvasPaintState<'_>, - ); - fn create_drawtarget(&self, size: Size2D) -> Box; - fn recreate_paint_state<'a>(&self, state: &CanvasPaintState<'a>) -> CanvasPaintState<'a>; -} - -/// A generic PathBuilder that abstracts the interface for azure's and raqote's PathBuilder. -/// TODO: De-abstract now that Azure is removed? -pub trait GenericPathBuilder { - fn arc( - &mut self, - origin: Point2D, - radius: f32, - start_angle: f32, - end_angle: f32, - anticlockwise: bool, - ); - fn bezier_curve_to( - &mut self, - control_point1: &Point2D, - control_point2: &Point2D, - control_point3: &Point2D, - ); - fn close(&mut self); - #[allow(clippy::too_many_arguments)] - fn ellipse( - &mut self, - origin: Point2D, - radius_x: f32, - radius_y: f32, - rotation_angle: f32, - start_angle: f32, - end_angle: f32, - anticlockwise: bool, - ); - fn get_current_point(&mut self) -> Option>; - fn line_to(&mut self, point: Point2D); - fn move_to(&mut self, point: Point2D); - fn quadratic_curve_to(&mut self, control_point: &Point2D, end_point: &Point2D); - fn svg_arc( - &mut self, - radius_x: f32, - radius_y: f32, - rotation_angle: f32, - large_arc: bool, - sweep: bool, - end_point: Point2D, - ); - fn finish(&mut self) -> Path; -} - /// A wrapper around a stored PathBuilder and an optional transformation that should be /// applied to any points to ensure they are in the matching device space. -struct PathBuilderRef<'a> { - builder: &'a mut Box, +struct PathBuilderRef<'a, B: Backend> { + builder: &'a mut B::PathBuilder, transform: Transform2D, } -impl PathBuilderRef<'_> { +impl PathBuilderRef<'_, B> { fn line_to(&mut self, pt: &Point2D) { let pt = self.transform.transform_point(*pt); self.builder.line_to(pt); @@ -336,7 +274,7 @@ impl PathBuilderRef<'_> { } #[allow(clippy::too_many_arguments)] - pub fn ellipse( + pub(crate) fn ellipse( &mut self, center: &Point2D, radius_x: f32, @@ -425,9 +363,9 @@ impl UnshapedTextRun<'_> { } } -pub struct TextRun { - pub font: FontRef, - pub glyphs: Arc, +pub(crate) struct TextRun { + pub(crate) font: FontRef, + pub(crate) glyphs: Arc, } impl TextRun { @@ -453,149 +391,31 @@ impl TextRun { } } -// This defines required methods for a DrawTarget (currently only implemented for raqote). The -// prototypes are derived from the now-removed Azure backend's methods. -pub trait GenericDrawTarget { - fn clear_rect(&mut self, rect: &Rect); - fn copy_surface( - &mut self, - surface: SourceSurface, - source: Rect, - destination: Point2D, - ); - fn create_gradient_stops(&self, gradient_stops: Vec) -> GradientStops; - fn create_path_builder(&self) -> Box; - fn create_similar_draw_target(&self, size: &Size2D) -> Box; - fn create_source_surface_from_data(&self, data: &[u8]) -> Option; - fn draw_surface( - &mut self, - surface: SourceSurface, - dest: Rect, - source: Rect, - filter: Filter, - draw_options: &DrawOptions, - ); - fn draw_surface_with_shadow( - &self, - surface: SourceSurface, - dest: &Point2D, - color: &Color, - offset: &Vector2D, - sigma: f32, - operator: CompositionOp, - ); - fn fill(&mut self, path: &Path, pattern: Pattern, draw_options: &DrawOptions); - fn fill_text( - &mut self, - text_runs: Vec, - start: Point2D, - pattern: &Pattern, - draw_options: &DrawOptions, - ); - fn fill_rect(&mut self, rect: &Rect, pattern: Pattern, draw_options: Option<&DrawOptions>); - fn get_size(&self) -> Size2D; - fn get_transform(&self) -> Transform2D; - fn pop_clip(&mut self); - fn push_clip(&mut self, path: &Path); - fn set_transform(&mut self, matrix: &Transform2D); - fn snapshot(&self) -> SourceSurface; - fn stroke( - &mut self, - path: &Path, - pattern: Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, - ); - fn stroke_line( - &mut self, - start: Point2D, - end: Point2D, - pattern: Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, - ); - fn stroke_rect( - &mut self, - rect: &Rect, - pattern: Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, - ); - fn snapshot_data(&self, f: &dyn Fn(&[u8]) -> Vec) -> Vec; - fn snapshot_data_owned(&self) -> Vec; -} - -pub enum GradientStop { - Raqote(raqote::GradientStop), -} - -pub enum GradientStops { - Raqote(Vec), -} - -#[derive(Clone)] -pub enum Color { - Raqote(raqote::SolidSource), -} - -#[derive(Clone)] -pub enum CompositionOp { - Raqote(raqote::BlendMode), -} - -#[derive(Clone)] -pub enum SourceSurface { - Raqote(Vec), // TODO: See if we can avoid the alloc (probably?) -} - -#[derive(Clone)] -pub enum Path { - Raqote(raqote::Path), -} - -#[derive(Clone)] -pub enum Pattern<'a> { - Raqote(crate::raqote_backend::Pattern<'a>), -} - -#[derive(Clone)] -pub enum DrawOptions { - Raqote(raqote::DrawOptions), -} - -#[derive(Clone)] -pub enum StrokeOptions { - Raqote(raqote::StrokeStyle), -} - #[derive(Clone, Copy)] -pub enum Filter { +pub(crate) enum Filter { Bilinear, Nearest, } -pub struct CanvasData<'a> { - backend: Box, - drawtarget: Box, - path_state: Option, - state: CanvasPaintState<'a>, - saved_states: Vec>, +pub(crate) struct CanvasData<'a, B: Backend> { + backend: B, + drawtarget: B::DrawTarget, + path_state: Option>, + state: CanvasPaintState<'a, B>, + saved_states: Vec>, compositor_api: CrossProcessCompositorApi, image_key: ImageKey, font_context: Arc, } -fn create_backend() -> Box { - Box::new(crate::raqote_backend::RaqoteBackend) -} - -impl<'a> CanvasData<'a> { - pub fn new( +impl<'a, B: Backend> CanvasData<'a, B> { + pub(crate) fn new( size: Size2D, compositor_api: CrossProcessCompositorApi, font_context: Arc, - ) -> CanvasData<'a> { - let backend = create_backend(); + backend: B, + ) -> CanvasData<'a, B> { + let size = size.max(MIN_WR_IMAGE_SIZE); let draw_target = backend.create_drawtarget(size); let image_key = compositor_api.generate_image_key().unwrap(); let descriptor = ImageDescriptor { @@ -605,15 +425,14 @@ impl<'a> CanvasData<'a> { offset: 0, flags: ImageDescriptorFlags::empty(), }; - let data = SerializableImageData::Raw(IpcSharedMemory::from_bytes( - &draw_target.snapshot_data_owned(), - )); + let data = + SerializableImageData::Raw(IpcSharedMemory::from_bytes(draw_target.bytes().as_ref())); compositor_api.update_images(vec![ImageUpdate::AddImage(image_key, descriptor, data)]); CanvasData { + state: backend.new_paint_state(), backend, drawtarget: draw_target, path_state: None, - state: CanvasPaintState::default(), saved_states: vec![], compositor_api, image_key, @@ -621,14 +440,14 @@ impl<'a> CanvasData<'a> { } } - pub fn image_key(&self) -> ImageKey { + pub(crate) fn image_key(&self) -> ImageKey { self.image_key } - pub fn draw_image( + pub(crate) fn draw_image( &mut self, image_data: &[u8], - image_size: Size2D, + image_size: Size2D, dest_rect: Rect, source_rect: Rect, smoothing_enabled: bool, @@ -637,15 +456,15 @@ impl<'a> CanvasData<'a> { // We round up the floating pixel values to draw the pixels let source_rect = source_rect.ceil(); // It discards the extra pixels (if any) that won't be painted - let image_data = if Rect::from_size(image_size).contains_rect(&source_rect) { - pixels::rgba8_get_rect(image_data, image_size.to_u64(), source_rect.to_u64()).into() + let image_data = if Rect::from_size(image_size.to_f64()).contains_rect(&source_rect) { + pixels::rgba8_get_rect(image_data, image_size, source_rect.to_u64()).into() } else { image_data.into() }; let draw_options = self.state.draw_options.clone(); - let writer = |draw_target: &mut dyn GenericDrawTarget| { - write_image( + let writer = |draw_target: &mut B::DrawTarget| { + write_image::( draw_target, image_data, source_rect.size, @@ -665,15 +484,15 @@ impl<'a> CanvasData<'a> { // TODO(pylbrecht) pass another closure for raqote self.draw_with_shadow(&rect, writer); } else { - writer(&mut *self.drawtarget); + writer(&mut self.drawtarget); } } - pub fn save_context_state(&mut self) { + pub(crate) fn save_context_state(&mut self) { self.saved_states.push(self.state.clone()); } - pub fn restore_context_state(&mut self) { + pub(crate) fn restore_context_state(&mut self) { if let Some(state) = self.saved_states.pop() { let _ = mem::replace(&mut self.state, state); self.drawtarget.set_transform(&self.state.transform); @@ -681,7 +500,7 @@ impl<'a> CanvasData<'a> { } } - pub fn fill_text_with_size( + pub(crate) fn fill_text_with_size( &mut self, text: String, x: f64, @@ -760,7 +579,7 @@ impl<'a> CanvasData<'a> { } /// - pub fn fill_text( + pub(crate) fn fill_text( &mut self, text: String, x: f64, @@ -778,7 +597,7 @@ impl<'a> CanvasData<'a> { /// /// - pub fn measure_text(&mut self, text: String) -> TextMetrics { + pub(crate) fn measure_text(&mut self, text: String) -> TextMetrics { // > Step 2: Replace all ASCII whitespace in text with U+0020 SPACE characters. let text = replace_ascii_whitespace(text); let Some(ref font_style) = self.state.font_style else { @@ -930,49 +749,15 @@ impl<'a> CanvasData<'a> { point2(x + anchor_x, y + anchor_y) } - pub fn fill_rect(&mut self, rect: &Rect) { + pub(crate) fn fill_rect(&mut self, rect: &Rect) { if self.state.fill_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } - let draw_rect = match &self.state.fill_style { - Pattern::Raqote(pattern) => match pattern { - crate::raqote_backend::Pattern::Surface(pattern) => { - let pattern_rect = Rect::new(Point2D::origin(), pattern.size()); - let mut draw_rect = rect.intersection(&pattern_rect).unwrap_or(Rect::zero()); - - match pattern.repetition() { - Repetition::NoRepeat => { - draw_rect.size.width = - draw_rect.size.width.min(pattern_rect.size.width); - draw_rect.size.height = - draw_rect.size.height.min(pattern_rect.size.height); - }, - Repetition::RepeatX => { - draw_rect.size.width = rect.size.width; - draw_rect.size.height = - draw_rect.size.height.min(pattern_rect.size.height); - }, - Repetition::RepeatY => { - draw_rect.size.height = rect.size.height; - draw_rect.size.width = - draw_rect.size.width.min(pattern_rect.size.width); - }, - Repetition::Repeat => { - draw_rect = *rect; - }, - } - - draw_rect - }, - crate::raqote_backend::Pattern::Color(..) | - crate::raqote_backend::Pattern::LinearGradient(..) | - crate::raqote_backend::Pattern::RadialGradient(..) => *rect, - }, - }; + let draw_rect = self.state.fill_style.draw_rect(rect); if self.need_to_draw_shadow() { - self.draw_with_shadow(&draw_rect, |new_draw_target: &mut dyn GenericDrawTarget| { + self.draw_with_shadow(&draw_rect, |new_draw_target: &mut B::DrawTarget| { new_draw_target.fill_rect( &draw_rect, self.state.fill_style.clone(), @@ -988,17 +773,17 @@ impl<'a> CanvasData<'a> { } } - pub fn clear_rect(&mut self, rect: &Rect) { + pub(crate) fn clear_rect(&mut self, rect: &Rect) { self.drawtarget.clear_rect(rect); } - pub fn stroke_rect(&mut self, rect: &Rect) { + pub(crate) fn stroke_rect(&mut self, rect: &Rect) { if self.state.stroke_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } if self.need_to_draw_shadow() { - self.draw_with_shadow(rect, |new_draw_target: &mut dyn GenericDrawTarget| { + self.draw_with_shadow(rect, |new_draw_target: &mut B::DrawTarget| { new_draw_target.stroke_rect( rect, self.state.stroke_style.clone(), @@ -1026,12 +811,12 @@ impl<'a> CanvasData<'a> { } } - pub fn begin_path(&mut self) { + pub(crate) fn begin_path(&mut self) { // Erase any traces of previous paths that existed before this. self.path_state = None; } - pub fn close_path(&mut self) { + pub(crate) fn close_path(&mut self) { self.path_builder().close(); } @@ -1093,14 +878,14 @@ impl<'a> CanvasData<'a> { assert!(self.path_state.as_ref().unwrap().is_path()) } - fn path(&self) -> &Path { + fn path(&self) -> &B::Path { self.path_state .as_ref() .expect("Should have called ensure_path()") .path() } - pub fn fill(&mut self) { + pub(crate) fn fill(&mut self) { if self.state.fill_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } @@ -1109,16 +894,16 @@ impl<'a> CanvasData<'a> { self.drawtarget.fill( &self.path().clone(), self.state.fill_style.clone(), - &self.state.draw_options, + &self.state.draw_options.clone(), ); } - pub fn fill_path(&mut self, path: &[PathSegment]) { + pub(crate) fn fill_path(&mut self, path: &[PathSegment]) { if self.state.fill_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } - let path = to_path(path, self.drawtarget.create_path_builder()); + let path = to_path::(path, self.drawtarget.create_path_builder()); self.drawtarget.fill( &path, @@ -1127,7 +912,7 @@ impl<'a> CanvasData<'a> { ); } - pub fn stroke(&mut self) { + pub(crate) fn stroke(&mut self) { if self.state.stroke_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } @@ -1141,12 +926,12 @@ impl<'a> CanvasData<'a> { ); } - pub fn stroke_path(&mut self, path: &[PathSegment]) { + pub(crate) fn stroke_path(&mut self, path: &[PathSegment]) { if self.state.stroke_style.is_zero_size_gradient() { return; // Paint nothing if gradient size is zero. } - let path = to_path(path, self.drawtarget.create_path_builder()); + let path = to_path::(path, self.drawtarget.create_path_builder()); self.drawtarget.stroke( &path, @@ -1156,18 +941,18 @@ impl<'a> CanvasData<'a> { ); } - pub fn clip(&mut self) { + pub(crate) fn clip(&mut self) { self.ensure_path(); let path = self.path().clone(); self.drawtarget.push_clip(&path); } - pub fn clip_path(&mut self, path: &[PathSegment]) { - let path = to_path(path, self.drawtarget.create_path_builder()); + pub(crate) fn clip_path(&mut self, path: &[PathSegment]) { + let path = to_path::(path, self.drawtarget.create_path_builder()); self.drawtarget.push_clip(&path); } - pub fn is_point_in_path( + pub(crate) fn is_point_in_path( &mut self, x: f64, y: f64, @@ -1186,7 +971,7 @@ impl<'a> CanvasData<'a> { chan.send(result).unwrap(); } - pub fn is_point_in_path_( + pub(crate) fn is_point_in_path_( &mut self, path: &[PathSegment], x: f64, @@ -1198,7 +983,7 @@ impl<'a> CanvasData<'a> { Some(PathState::UserSpacePath(_, Some(transform))) => transform, Some(_) | None => &self.drawtarget.get_transform(), }; - let result = to_path(path, self.drawtarget.create_path_builder()).contains_point( + let result = to_path::(path, self.drawtarget.create_path_builder()).contains_point( x, y, path_transform, @@ -1206,15 +991,15 @@ impl<'a> CanvasData<'a> { chan.send(result).unwrap(); } - pub fn move_to(&mut self, point: &Point2D) { + pub(crate) fn move_to(&mut self, point: &Point2D) { self.path_builder().move_to(point); } - pub fn line_to(&mut self, point: &Point2D) { + pub(crate) fn line_to(&mut self, point: &Point2D) { self.path_builder().line_to(point); } - fn path_builder(&mut self) -> PathBuilderRef { + fn path_builder(&mut self) -> PathBuilderRef { if self.path_state.is_none() { self.path_state = Some(PathState::UserSpacePathBuilder( self.drawtarget.create_path_builder(), @@ -1279,18 +1064,18 @@ impl<'a> CanvasData<'a> { } } - pub fn rect(&mut self, rect: &Rect) { + pub(crate) fn rect(&mut self, rect: &Rect) { self.path_builder().rect(rect); } - pub fn quadratic_curve_to(&mut self, cp: &Point2D, endpoint: &Point2D) { + pub(crate) fn quadratic_curve_to(&mut self, cp: &Point2D, endpoint: &Point2D) { if self.path_state.is_none() { self.move_to(cp); } self.path_builder().quadratic_curve_to(cp, endpoint); } - pub fn bezier_curve_to( + pub(crate) fn bezier_curve_to( &mut self, cp1: &Point2D, cp2: &Point2D, @@ -1302,7 +1087,7 @@ impl<'a> CanvasData<'a> { self.path_builder().bezier_curve_to(cp1, cp2, endpoint); } - pub fn arc( + pub(crate) fn arc( &mut self, center: &Point2D, radius: f32, @@ -1314,12 +1099,12 @@ impl<'a> CanvasData<'a> { .arc(center, radius, start_angle, end_angle, ccw); } - pub fn arc_to(&mut self, cp1: &Point2D, cp2: &Point2D, radius: f32) { + pub(crate) fn arc_to(&mut self, cp1: &Point2D, cp2: &Point2D, radius: f32) { self.path_builder().arc_to(cp1, cp2, radius); } #[allow(clippy::too_many_arguments)] - pub fn ellipse( + pub(crate) fn ellipse( &mut self, center: &Point2D, radius_x: f32, @@ -1340,45 +1125,45 @@ impl<'a> CanvasData<'a> { ); } - pub fn set_fill_style(&mut self, style: FillOrStrokeStyle) { + pub(crate) fn set_fill_style(&mut self, style: FillOrStrokeStyle) { self.backend - .set_fill_style(style, &mut self.state, &*self.drawtarget); + .set_fill_style(style, &mut self.state, &self.drawtarget); } - pub fn set_stroke_style(&mut self, style: FillOrStrokeStyle) { + pub(crate) fn set_stroke_style(&mut self, style: FillOrStrokeStyle) { self.backend - .set_stroke_style(style, &mut self.state, &*self.drawtarget); + .set_stroke_style(style, &mut self.state, &self.drawtarget); } - pub fn set_line_width(&mut self, width: f32) { + pub(crate) fn set_line_width(&mut self, width: f32) { self.state.stroke_opts.set_line_width(width); } - pub fn set_line_cap(&mut self, cap: LineCapStyle) { + pub(crate) fn set_line_cap(&mut self, cap: LineCapStyle) { self.state.stroke_opts.set_line_cap(cap); } - pub fn set_line_join(&mut self, join: LineJoinStyle) { + pub(crate) fn set_line_join(&mut self, join: LineJoinStyle) { self.state.stroke_opts.set_line_join(join); } - pub fn set_miter_limit(&mut self, limit: f32) { + pub(crate) fn set_miter_limit(&mut self, limit: f32) { self.state.stroke_opts.set_miter_limit(limit); } - pub fn set_line_dash(&mut self, items: Vec) { + pub(crate) fn set_line_dash(&mut self, items: Vec) { self.state.stroke_opts.set_line_dash(items); } - pub fn set_line_dash_offset(&mut self, offset: f32) { + pub(crate) fn set_line_dash_offset(&mut self, offset: f32) { self.state.stroke_opts.set_line_dash_offset(offset); } - pub fn get_transform(&self) -> Transform2D { + pub(crate) fn get_transform(&self) -> Transform2D { self.drawtarget.get_transform() } - pub fn set_transform(&mut self, transform: &Transform2D) { + pub(crate) fn set_transform(&mut self, transform: &Transform2D) { // If there is an in-progress path, store the existing transformation required // to move between device and user space. match self.path_state.as_mut() { @@ -1394,34 +1179,28 @@ impl<'a> CanvasData<'a> { self.drawtarget.set_transform(transform) } - pub fn set_global_alpha(&mut self, alpha: f32) { + pub(crate) fn set_global_alpha(&mut self, alpha: f32) { self.state.draw_options.set_alpha(alpha); } - pub fn set_global_composition(&mut self, op: CompositionOrBlending) { + pub(crate) fn set_global_composition(&mut self, op: CompositionOrBlending) { self.backend.set_global_composition(op, &mut self.state); } - pub fn recreate(&mut self, size: Option>) { - let size = size.unwrap_or_else(|| self.drawtarget.get_size().to_u64()); + pub(crate) fn recreate(&mut self, size: Option>) { + let size = size + .unwrap_or_else(|| self.drawtarget.get_size().to_u64()) + .max(MIN_WR_IMAGE_SIZE); self.drawtarget = self .backend .create_drawtarget(Size2D::new(size.width, size.height)); - self.state = self.backend.recreate_paint_state(&self.state); + self.state = self.backend.new_paint_state(); self.saved_states.clear(); self.update_image_rendering(); } - pub fn send_pixels(&mut self, chan: IpcSender) { - self.drawtarget.snapshot_data(&|bytes| { - let data = IpcSharedMemory::from_bytes(bytes); - chan.send(data).unwrap(); - vec![] - }); - } - /// Update image in WebRender - pub fn update_image_rendering(&mut self) { + pub(crate) fn update_image_rendering(&mut self) { let descriptor = ImageDescriptor { size: self.drawtarget.get_size().cast_unit(), stride: None, @@ -1430,7 +1209,7 @@ impl<'a> CanvasData<'a> { flags: ImageDescriptorFlags::empty(), }; let data = SerializableImageData::Raw(IpcSharedMemory::from_bytes( - &self.drawtarget.snapshot_data_owned(), + self.drawtarget.bytes().as_ref(), )); self.compositor_api @@ -1442,7 +1221,7 @@ impl<'a> CanvasData<'a> { } // https://html.spec.whatwg.org/multipage/#dom-context-2d-putimagedata - pub fn put_image_data(&mut self, mut imagedata: Vec, rect: Rect) { + pub(crate) fn put_image_data(&mut self, mut imagedata: Vec, rect: Rect) { assert_eq!(imagedata.len() % 4, 0); assert_eq!(rect.size.area() as usize, imagedata.len() / 4); pixels::rgba8_byte_swap_and_premultiply_inplace(&mut imagedata); @@ -1457,31 +1236,31 @@ impl<'a> CanvasData<'a> { ); } - pub fn set_shadow_offset_x(&mut self, value: f64) { + pub(crate) fn set_shadow_offset_x(&mut self, value: f64) { self.state.shadow_offset_x = value; } - pub fn set_shadow_offset_y(&mut self, value: f64) { + pub(crate) fn set_shadow_offset_y(&mut self, value: f64) { self.state.shadow_offset_y = value; } - pub fn set_shadow_blur(&mut self, value: f64) { + pub(crate) fn set_shadow_blur(&mut self, value: f64) { self.state.shadow_blur = value; } - pub fn set_shadow_color(&mut self, value: AbsoluteColor) { + pub(crate) fn set_shadow_color(&mut self, value: AbsoluteColor) { self.backend.set_shadow_color(value, &mut self.state); } - pub fn set_font(&mut self, font_style: FontStyleStruct) { + pub(crate) fn set_font(&mut self, font_style: FontStyleStruct) { self.state.font_style = Some(ServoArc::new(font_style)) } - pub fn set_text_align(&mut self, text_align: TextAlign) { + pub(crate) fn set_text_align(&mut self, text_align: TextAlign) { self.state.text_align = text_align; } - pub fn set_text_baseline(&mut self, text_baseline: TextBaseline) { + pub(crate) fn set_text_baseline(&mut self, text_baseline: TextBaseline) { self.state.text_baseline = text_baseline; } @@ -1493,7 +1272,7 @@ impl<'a> CanvasData<'a> { self.state.shadow_blur != 0.0f64) } - fn create_draw_target_for_shadow(&self, source_rect: &Rect) -> Box { + fn create_draw_target_for_shadow(&self, source_rect: &Rect) -> B::DrawTarget { let mut draw_target = self.drawtarget.create_similar_draw_target(&Size2D::new( source_rect.size.width as i32, source_rect.size.height as i32, @@ -1507,13 +1286,13 @@ impl<'a> CanvasData<'a> { fn draw_with_shadow(&self, rect: &Rect, draw_shadow_source: F) where - F: FnOnce(&mut dyn GenericDrawTarget), + F: FnOnce(&mut B::DrawTarget), { let shadow_src_rect = self.state.transform.outer_transformed_rect(rect); let mut new_draw_target = self.create_draw_target_for_shadow(&shadow_src_rect); - draw_shadow_source(&mut *new_draw_target); + draw_shadow_source(&mut new_draw_target); self.drawtarget.draw_surface_with_shadow( - new_draw_target.snapshot(), + new_draw_target.surface(), &Point2D::new(shadow_src_rect.origin.x, shadow_src_rect.origin.y), &self.state.shadow_color, &Vector2D::new( @@ -1529,22 +1308,40 @@ impl<'a> CanvasData<'a> { /// canvas_size: The size of the canvas we're reading from /// read_rect: The area of the canvas we want to read from #[allow(unsafe_code)] - pub fn read_pixels(&self, read_rect: Rect, canvas_size: Size2D) -> Vec { - let canvas_rect = Rect::from_size(canvas_size); - if canvas_rect - .intersection(&read_rect) - .is_none_or(|rect| rect.is_empty()) - { - return vec![]; - } + pub(crate) fn read_pixels( + &self, + read_rect: Option>, + canvas_size: Option>, + ) -> Snapshot { + let canvas_size = canvas_size.unwrap_or(self.drawtarget.get_size().cast()); - self.drawtarget.snapshot_data(&|bytes| { - pixels::rgba8_get_rect(bytes, canvas_size, read_rect).into_owned() - }) + let data = if let Some(read_rect) = read_rect { + let canvas_rect = Rect::from_size(canvas_size); + if canvas_rect + .intersection(&read_rect) + .is_none_or(|rect| rect.is_empty()) + { + vec![] + } else { + pixels::rgba8_get_rect(self.drawtarget.bytes().as_ref(), canvas_size, read_rect) + .to_vec() + } + } else { + self.drawtarget.bytes().as_ref().to_vec() + }; + + Snapshot::from_vec( + canvas_size, + snapshot::PixelFormat::BGRA, + snapshot::AlphaMode::Transparent { + premultiplied: true, + }, + data, + ) } } -impl Drop for CanvasData<'_> { +impl Drop for CanvasData<'_, B> { fn drop(&mut self) { self.compositor_api .update_images(vec![ImageUpdate::DeleteImage(self.image_key)]); @@ -1555,20 +1352,21 @@ const HANGING_BASELINE_DEFAULT: f32 = 0.8; const IDEOGRAPHIC_BASELINE_DEFAULT: f32 = 0.5; #[derive(Clone)] -pub struct CanvasPaintState<'a> { - pub draw_options: DrawOptions, - pub fill_style: Pattern<'a>, - pub stroke_style: Pattern<'a>, - pub stroke_opts: StrokeOptions, +pub(crate) struct CanvasPaintState<'a, B: Backend> { + pub(crate) draw_options: B::DrawOptions, + pub(crate) fill_style: B::Pattern<'a>, + pub(crate) stroke_style: B::Pattern<'a>, + pub(crate) stroke_opts: B::StrokeOptions, /// The current 2D transform matrix. - pub transform: Transform2D, - pub shadow_offset_x: f64, - pub shadow_offset_y: f64, - pub shadow_blur: f64, - pub shadow_color: Color, - pub font_style: Option>, - pub text_align: TextAlign, - pub text_baseline: TextBaseline, + pub(crate) transform: Transform2D, + pub(crate) shadow_offset_x: f64, + pub(crate) shadow_offset_y: f64, + pub(crate) shadow_blur: f64, + pub(crate) shadow_color: B::Color, + pub(crate) font_style: Option>, + pub(crate) text_align: TextAlign, + pub(crate) text_baseline: TextBaseline, + pub(crate) _backend: PhantomData, } /// It writes an image to the destination target @@ -1578,14 +1376,14 @@ pub struct CanvasPaintState<'a> { /// dest_rect: Area of the destination target where the pixels will be copied /// smoothing_enabled: It determines if smoothing is applied to the image result /// premultiply: Determines whenever the image data should be premultiplied or not -fn write_image( - draw_target: &mut dyn GenericDrawTarget, +fn write_image( + draw_target: &mut B::DrawTarget, mut image_data: Vec, image_size: Size2D, dest_rect: Rect, smoothing_enabled: bool, premultiply: bool, - draw_options: &DrawOptions, + draw_options: &B::DrawOptions, ) { if image_data.is_empty() { return; @@ -1614,25 +1412,11 @@ fn write_image( draw_target.draw_surface(source_surface, dest_rect, image_rect, filter, draw_options); } -pub trait RectToi32 { - fn to_i32(&self) -> Rect; +pub(crate) trait RectToi32 { fn ceil(&self) -> Rect; } impl RectToi32 for Rect { - fn to_i32(&self) -> Rect { - Rect::new( - Point2D::new( - self.origin.x.to_i32().unwrap(), - self.origin.y.to_i32().unwrap(), - ), - Size2D::new( - self.size.width.to_i32().unwrap(), - self.size.height.to_i32().unwrap(), - ), - ) - } - fn ceil(&self) -> Rect { Rect::new( Point2D::new(self.origin.x.ceil(), self.origin.y.ceil()), @@ -1641,22 +1425,6 @@ impl RectToi32 for Rect { } } -pub trait RectExt { - fn to_u64(&self) -> Rect; -} - -impl RectExt for Rect { - fn to_u64(&self) -> Rect { - self.cast() - } -} - -impl RectExt for Rect { - fn to_u64(&self) -> Rect { - self.cast() - } -} - fn replace_ascii_whitespace(text: String) -> String { text.chars() .map(|c| match c { diff --git a/components/canvas/canvas_paint_thread.rs b/components/canvas/canvas_paint_thread.rs index 1c6877772d3..efadbf8d577 100644 --- a/components/canvas/canvas_paint_thread.rs +++ b/components/canvas/canvas_paint_thread.rs @@ -9,20 +9,23 @@ use std::thread; use canvas_traits::ConstellationCanvasMsg; use canvas_traits::canvas::*; +use compositing_traits::CrossProcessCompositorApi; use crossbeam_channel::{Sender, select, unbounded}; -use euclid::default::Size2D; +use euclid::default::{Point2D, Rect, Size2D, Transform2D}; use fonts::{FontContext, SystemFontServiceProxy}; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; use log::warn; use net_traits::ResourceThreads; +use style::color::AbsoluteColor; +use style::properties::style_structs::Font as FontStyleStruct; use webrender_api::ImageKey; -use webrender_traits::CrossProcessCompositorApi; use crate::canvas_data::*; +use crate::raqote_backend::RaqoteBackend; pub struct CanvasPaintThread<'a> { - canvases: HashMap>, + canvases: HashMap>, next_canvas_id: CanvasId, compositor_api: CrossProcessCompositorApi, font_context: Arc, @@ -76,7 +79,11 @@ impl<'a> CanvasPaintThread<'a> { }, Ok(CanvasMsg::FromScript(message, canvas_id)) => match message { FromScriptMsg::SendPixels(chan) => { - canvas_paint_thread.canvas(canvas_id).send_pixels(chan); + chan.send(canvas_paint_thread + .canvas(canvas_id) + .read_pixels(None, None) + .as_ipc() + ).unwrap(); }, }, Err(e) => { @@ -90,7 +97,10 @@ impl<'a> CanvasPaintThread<'a> { let canvas_data = canvas_paint_thread.create_canvas(size); creator.send(canvas_data).unwrap(); }, - Ok(ConstellationCanvasMsg::Exit) => break, + Ok(ConstellationCanvasMsg::Exit(exit_sender)) => { + let _ = exit_sender.send(()); + break; + }, Err(e) => { warn!("Error on CanvasPaintThread receive ({})", e); break; @@ -109,10 +119,14 @@ impl<'a> CanvasPaintThread<'a> { let canvas_id = self.next_canvas_id; self.next_canvas_id.0 += 1; - let canvas_data = - CanvasData::new(size, self.compositor_api.clone(), self.font_context.clone()); + let canvas_data = CanvasData::new( + size, + self.compositor_api.clone(), + self.font_context.clone(), + RaqoteBackend, + ); let image_key = canvas_data.image_key(); - self.canvases.insert(canvas_id, canvas_data); + self.canvases.insert(canvas_id, Canvas::Raqote(canvas_data)); (canvas_id, image_key) } @@ -159,24 +173,21 @@ impl<'a> CanvasPaintThread<'a> { Canvas2dMsg::IsPointInPath(path, x, y, fill_rule, chan) => self .canvas(canvas_id) .is_point_in_path_(&path[..], x, y, fill_rule, chan), - Canvas2dMsg::DrawImage( - ref image_data, - image_size, - dest_rect, - source_rect, - smoothing_enabled, - ) => self.canvas(canvas_id).draw_image( - image_data, - image_size, - dest_rect, - source_rect, - smoothing_enabled, - true, - ), + Canvas2dMsg::DrawImage(snapshot, dest_rect, source_rect, smoothing_enabled) => { + let snapshot = snapshot.to_owned(); + self.canvas(canvas_id).draw_image( + snapshot.data(), + snapshot.size(), + dest_rect, + source_rect, + smoothing_enabled, + !snapshot.alpha_mode().is_premultiplied(), + ) + }, Canvas2dMsg::DrawEmptyImage(image_size, dest_rect, source_rect) => { self.canvas(canvas_id).draw_image( &vec![0; image_size.area() as usize * 4], - image_size, + image_size.to_u64(), dest_rect, source_rect, false, @@ -192,10 +203,10 @@ impl<'a> CanvasPaintThread<'a> { ) => { let image_data = self .canvas(canvas_id) - .read_pixels(source_rect.to_u64(), image_size.to_u64()); + .read_pixels(Some(source_rect.to_u64()), Some(image_size.to_u64())); self.canvas(other_canvas_id).draw_image( - &image_data, - source_rect.size, + image_data.data(), + source_rect.size.to_u64(), dest_rect, source_rect, smoothing, @@ -244,8 +255,10 @@ impl<'a> CanvasPaintThread<'a> { self.canvas(canvas_id).set_global_composition(op) }, Canvas2dMsg::GetImageData(dest_rect, canvas_size, sender) => { - let pixels = self.canvas(canvas_id).read_pixels(dest_rect, canvas_size); - sender.send(&pixels).unwrap(); + let snapshot = self + .canvas(canvas_id) + .read_pixels(Some(dest_rect), Some(canvas_size)); + sender.send(snapshot.as_ipc()).unwrap(); }, Canvas2dMsg::PutImageData(rect, receiver) => { self.canvas(canvas_id) @@ -273,7 +286,347 @@ impl<'a> CanvasPaintThread<'a> { } } - fn canvas(&mut self, canvas_id: CanvasId) -> &mut CanvasData<'a> { + fn canvas(&mut self, canvas_id: CanvasId) -> &mut Canvas<'a> { self.canvases.get_mut(&canvas_id).expect("Bogus canvas id") } } + +enum Canvas<'a> { + Raqote(CanvasData<'a, RaqoteBackend>), +} + +impl Canvas<'_> { + fn set_fill_style(&mut self, style: FillOrStrokeStyle) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_fill_style(style), + } + } + + fn fill(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.fill(), + } + } + + fn fill_text(&mut self, text: String, x: f64, y: f64, max_width: Option, is_rtl: bool) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.fill_text(text, x, y, max_width, is_rtl), + } + } + + fn fill_rect(&mut self, rect: &Rect) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.fill_rect(rect), + } + } + + fn set_stroke_style(&mut self, style: FillOrStrokeStyle) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_stroke_style(style), + } + } + + fn stroke_rect(&mut self, rect: &Rect) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.stroke_rect(rect), + } + } + + fn begin_path(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.begin_path(), + } + } + + fn close_path(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.close_path(), + } + } + + fn fill_path(&mut self, path: &[PathSegment]) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.fill_path(path), + } + } + + fn stroke(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.stroke(), + } + } + + fn stroke_path(&mut self, path: &[PathSegment]) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.stroke_path(path), + } + } + + fn clip(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.clip(), + } + } + + fn is_point_in_path(&mut self, x: f64, y: f64, fill_rule: FillRule, chan: IpcSender) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.is_point_in_path(x, y, fill_rule, chan), + } + } + + fn is_point_in_path_( + &mut self, + path: &[PathSegment], + x: f64, + y: f64, + fill_rule: FillRule, + chan: IpcSender, + ) { + match self { + Canvas::Raqote(canvas_data) => { + canvas_data.is_point_in_path_(path, x, y, fill_rule, chan) + }, + } + } + + fn clear_rect(&mut self, rect: &Rect) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.clear_rect(rect), + } + } + + fn draw_image( + &mut self, + data: &[u8], + size: Size2D, + dest_rect: Rect, + source_rect: Rect, + smoothing_enabled: bool, + is_premultiplied: bool, + ) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.draw_image( + data, + size, + dest_rect, + source_rect, + smoothing_enabled, + is_premultiplied, + ), + } + } + + fn read_pixels( + &mut self, + read_rect: Option>, + canvas_size: Option>, + ) -> snapshot::Snapshot { + match self { + Canvas::Raqote(canvas_data) => canvas_data.read_pixels(read_rect, canvas_size), + } + } + + fn move_to(&mut self, point: &Point2D) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.move_to(point), + } + } + + fn line_to(&mut self, point: &Point2D) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.line_to(point), + } + } + + fn rect(&mut self, rect: &Rect) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.rect(rect), + } + } + + fn quadratic_curve_to(&mut self, cp: &Point2D, pt: &Point2D) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.quadratic_curve_to(cp, pt), + } + } + + fn bezier_curve_to(&mut self, cp1: &Point2D, cp2: &Point2D, pt: &Point2D) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.bezier_curve_to(cp1, cp2, pt), + } + } + + fn arc(&mut self, center: &Point2D, radius: f32, start: f32, end: f32, ccw: bool) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.arc(center, radius, start, end, ccw), + } + } + + fn arc_to(&mut self, cp1: &Point2D, cp2: &Point2D, radius: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.arc_to(cp1, cp2, radius), + } + } + + #[allow(clippy::too_many_arguments)] + fn ellipse( + &mut self, + center: &Point2D, + radius_x: f32, + radius_y: f32, + rotation: f32, + start: f32, + end: f32, + ccw: bool, + ) { + match self { + Canvas::Raqote(canvas_data) => { + canvas_data.ellipse(center, radius_x, radius_y, rotation, start, end, ccw) + }, + } + } + + fn restore_context_state(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.restore_context_state(), + } + } + + fn save_context_state(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.save_context_state(), + } + } + + fn set_line_width(&mut self, width: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_width(width), + } + } + + fn set_line_cap(&mut self, cap: LineCapStyle) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_cap(cap), + } + } + + fn set_line_join(&mut self, join: LineJoinStyle) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_join(join), + } + } + + fn set_miter_limit(&mut self, limit: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_miter_limit(limit), + } + } + + fn set_line_dash(&mut self, items: Vec) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_dash(items), + } + } + + fn set_line_dash_offset(&mut self, offset: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_line_dash_offset(offset), + } + } + + fn set_transform(&mut self, matrix: &Transform2D) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_transform(matrix), + } + } + + fn set_global_alpha(&mut self, alpha: f32) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_global_alpha(alpha), + } + } + + fn set_global_composition(&mut self, op: CompositionOrBlending) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_global_composition(op), + } + } + + fn set_shadow_offset_x(&mut self, value: f64) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_shadow_offset_x(value), + } + } + + fn set_shadow_offset_y(&mut self, value: f64) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_shadow_offset_y(value), + } + } + + fn set_shadow_blur(&mut self, value: f64) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_shadow_blur(value), + } + } + + fn set_shadow_color(&mut self, color: AbsoluteColor) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_shadow_color(color), + } + } + + fn set_font(&mut self, font_style: FontStyleStruct) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_font(font_style), + } + } + + fn set_text_align(&mut self, text_align: TextAlign) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_text_align(text_align), + } + } + + fn set_text_baseline(&mut self, text_baseline: TextBaseline) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.set_text_baseline(text_baseline), + } + } + + fn measure_text(&mut self, text: String) -> TextMetrics { + match self { + Canvas::Raqote(canvas_data) => canvas_data.measure_text(text), + } + } + + fn clip_path(&mut self, path: &[PathSegment]) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.clip_path(path), + } + } + + fn get_transform(&self) -> Transform2D { + match self { + Canvas::Raqote(canvas_data) => canvas_data.get_transform(), + } + } + + fn put_image_data(&mut self, unwrap: Vec, rect: Rect) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.put_image_data(unwrap, rect), + } + } + + fn update_image_rendering(&mut self) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.update_image_rendering(), + } + } + + fn recreate(&mut self, size: Option>) { + match self { + Canvas::Raqote(canvas_data) => canvas_data.recreate(size), + } + } +} diff --git a/components/canvas/lib.rs b/components/canvas/lib.rs index d2c62c1d8b6..91ab58b0e8b 100644 --- a/components/canvas/lib.rs +++ b/components/canvas/lib.rs @@ -4,14 +4,8 @@ #![deny(unsafe_code)] +mod backend; mod raqote_backend; -pub use webgl_mode::WebGLComm; - pub mod canvas_data; pub mod canvas_paint_thread; -mod webgl_limits; -mod webgl_mode; -pub mod webgl_thread; -#[cfg(feature = "webxr")] -mod webxr; diff --git a/components/canvas/raqote_backend.rs b/components/canvas/raqote_backend.rs index e40367a4ee8..ecf780c36d5 100644 --- a/components/canvas/raqote_backend.rs +++ b/components/canvas/raqote_backend.rs @@ -7,20 +7,19 @@ use std::collections::HashMap; use canvas_traits::canvas::*; use cssparser::color::clamp_unit_f32; -use euclid::Angle; use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D}; use font_kit::font::Font; use fonts::{ByteIndex, FontIdentifier, FontTemplateRefMethods}; use log::warn; -use lyon_geom::Arc; use range::Range; use raqote::PathOp; use style::color::AbsoluteColor; -use crate::canvas_data::{ - self, Backend, CanvasPaintState, Color, CompositionOp, DrawOptions, Filter, GenericDrawTarget, - GenericPathBuilder, GradientStop, GradientStops, Path, SourceSurface, StrokeOptions, TextRun, +use crate::backend::{ + Backend, DrawOptionsHelpers, GenericDrawTarget, GenericPathBuilder, PathHelpers, + PatternHelpers, StrokeOptionsHelpers, }; +use crate::canvas_data::{CanvasPaintState, Filter, TextRun}; thread_local! { /// The shared font cache used by all canvases that render on a thread. It would be nicer @@ -30,80 +29,85 @@ thread_local! { static SHARED_FONT_CACHE: RefCell> = RefCell::default(); } -#[derive(Default)] -pub struct RaqoteBackend; +#[derive(Clone, Default)] +pub(crate) struct RaqoteBackend; impl Backend for RaqoteBackend { - fn get_composition_op(&self, opts: &DrawOptions) -> CompositionOp { - CompositionOp::Raqote(opts.as_raqote().blend_mode) + type Pattern<'a> = Pattern<'a>; + type StrokeOptions = raqote::StrokeStyle; + type Color = raqote::SolidSource; + type DrawOptions = raqote::DrawOptions; + type CompositionOp = raqote::BlendMode; + type DrawTarget = raqote::DrawTarget; + type PathBuilder = PathBuilder; + type SourceSurface = Vec; // TODO: See if we can avoid the alloc (probably?) + type Bytes<'a> = &'a [u8]; + type Path = raqote::Path; + type GradientStop = raqote::GradientStop; + type GradientStops = Vec; + + fn get_composition_op(&self, opts: &Self::DrawOptions) -> Self::CompositionOp { + opts.blend_mode } - fn need_to_draw_shadow(&self, color: &Color) -> bool { - color.as_raqote().a != 0 + fn need_to_draw_shadow(&self, color: &Self::Color) -> bool { + color.a != 0 } - fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_>) { - state.shadow_color = Color::Raqote(color.to_raqote_style()); + fn set_shadow_color(&mut self, color: AbsoluteColor, state: &mut CanvasPaintState<'_, Self>) { + state.shadow_color = color.to_raqote_style(); } fn set_fill_style( &mut self, style: FillOrStrokeStyle, - state: &mut CanvasPaintState<'_>, - _drawtarget: &dyn GenericDrawTarget, + state: &mut CanvasPaintState<'_, Self>, + _drawtarget: &Self::DrawTarget, ) { if let Some(pattern) = style.to_raqote_pattern() { - state.fill_style = canvas_data::Pattern::Raqote(pattern); + state.fill_style = pattern; } } fn set_stroke_style( &mut self, style: FillOrStrokeStyle, - state: &mut CanvasPaintState<'_>, - _drawtarget: &dyn GenericDrawTarget, + state: &mut CanvasPaintState<'_, Self>, + _drawtarget: &Self::DrawTarget, ) { if let Some(pattern) = style.to_raqote_pattern() { - state.stroke_style = canvas_data::Pattern::Raqote(pattern); + state.stroke_style = pattern; } } fn set_global_composition( &mut self, op: CompositionOrBlending, - state: &mut CanvasPaintState<'_>, + state: &mut CanvasPaintState<'_, Self>, ) { - state.draw_options.as_raqote_mut().blend_mode = op.to_raqote_style(); + state.draw_options.blend_mode = op.to_raqote_style(); } - fn create_drawtarget(&self, size: Size2D) -> Box { - Box::new(raqote::DrawTarget::new( - size.width as i32, - size.height as i32, - )) + fn create_drawtarget(&self, size: Size2D) -> Self::DrawTarget { + raqote::DrawTarget::new(size.width as i32, size.height as i32) } - fn recreate_paint_state<'a>(&self, _state: &CanvasPaintState<'a>) -> CanvasPaintState<'a> { - CanvasPaintState::default() - } -} - -impl Default for CanvasPaintState<'_> { - fn default() -> Self { + fn new_paint_state<'a>(&self) -> CanvasPaintState<'a, Self> { let pattern = Pattern::Color(255, 0, 0, 0); CanvasPaintState { - draw_options: DrawOptions::Raqote(raqote::DrawOptions::new()), - fill_style: canvas_data::Pattern::Raqote(pattern.clone()), - stroke_style: canvas_data::Pattern::Raqote(pattern), - stroke_opts: StrokeOptions::Raqote(Default::default()), + draw_options: raqote::DrawOptions::new(), + fill_style: pattern.clone(), + stroke_style: pattern, + stroke_opts: Default::default(), transform: Transform2D::identity(), shadow_offset_x: 0.0, shadow_offset_y: 0.0, shadow_blur: 0.0, - shadow_color: Color::Raqote(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)), + shadow_color: raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0), font_style: None, text_align: TextAlign::default(), text_baseline: TextBaseline::default(), + _backend: std::marker::PhantomData, } } } @@ -230,136 +234,122 @@ impl Repetition { } } -impl canvas_data::Pattern<'_> { - pub fn source(&self) -> raqote::Source { +pub fn source<'a>(pattern: &Pattern<'a>) -> raqote::Source<'a> { + match pattern { + Pattern::Color(a, r, g, b) => raqote::Source::Solid( + raqote::SolidSource::from_unpremultiplied_argb(*a, *r, *g, *b), + ), + Pattern::LinearGradient(pattern) => raqote::Source::new_linear_gradient( + pattern.gradient.clone(), + pattern.start, + pattern.end, + raqote::Spread::Pad, + ), + Pattern::RadialGradient(pattern) => raqote::Source::new_two_circle_radial_gradient( + pattern.gradient.clone(), + pattern.center1, + pattern.radius1, + pattern.center2, + pattern.radius2, + raqote::Spread::Pad, + ), + Pattern::Surface(pattern) => raqote::Source::Image( + pattern.image, + pattern.extend, + pattern.filter, + pattern.transform, + ), + } +} + +impl PatternHelpers for Pattern<'_> { + fn is_zero_size_gradient(&self) -> bool { match self { - canvas_data::Pattern::Raqote(pattern) => match pattern { - Pattern::Color(a, r, g, b) => raqote::Source::Solid( - raqote::SolidSource::from_unpremultiplied_argb(*a, *r, *g, *b), - ), - Pattern::LinearGradient(pattern) => raqote::Source::new_linear_gradient( - pattern.gradient.clone(), - pattern.start, - pattern.end, - raqote::Spread::Pad, - ), - Pattern::RadialGradient(pattern) => raqote::Source::new_two_circle_radial_gradient( - pattern.gradient.clone(), - pattern.center1, - pattern.radius1, - pattern.center2, - pattern.radius2, - raqote::Spread::Pad, - ), - Pattern::Surface(pattern) => raqote::Source::Image( - pattern.image, - pattern.extend, - pattern.filter, - pattern.transform, - ), + Pattern::RadialGradient(pattern) => { + let centers_equal = pattern.center1 == pattern.center2; + let radii_equal = pattern.radius1 == pattern.radius2; + (centers_equal && radii_equal) || pattern.gradient.stops.is_empty() }, - } - } - pub fn is_zero_size_gradient(&self) -> bool { - match self { - canvas_data::Pattern::Raqote(pattern) => match pattern { - Pattern::RadialGradient(pattern) => { - let centers_equal = pattern.center1 == pattern.center2; - let radii_equal = pattern.radius1 == pattern.radius2; - (centers_equal && radii_equal) || pattern.gradient.stops.is_empty() - }, - Pattern::LinearGradient(pattern) => { - (pattern.start == pattern.end) || pattern.gradient.stops.is_empty() - }, - Pattern::Color(..) | Pattern::Surface(..) => false, + Pattern::LinearGradient(pattern) => { + (pattern.start == pattern.end) || pattern.gradient.stops.is_empty() }, + Pattern::Color(..) | Pattern::Surface(..) => false, + } + } + + fn draw_rect(&self, rect: &Rect) -> Rect { + match self { + Pattern::Surface(pattern) => { + let pattern_rect = Rect::new(Point2D::origin(), pattern.size()); + let mut draw_rect = rect.intersection(&pattern_rect).unwrap_or(Rect::zero()); + + match pattern.repetition() { + Repetition::NoRepeat => { + draw_rect.size.width = draw_rect.size.width.min(pattern_rect.size.width); + draw_rect.size.height = draw_rect.size.height.min(pattern_rect.size.height); + }, + Repetition::RepeatX => { + draw_rect.size.width = rect.size.width; + draw_rect.size.height = draw_rect.size.height.min(pattern_rect.size.height); + }, + Repetition::RepeatY => { + draw_rect.size.height = rect.size.height; + draw_rect.size.width = draw_rect.size.width.min(pattern_rect.size.width); + }, + Repetition::Repeat => { + draw_rect = *rect; + }, + } + + draw_rect + }, + Pattern::Color(..) | Pattern::LinearGradient(..) | Pattern::RadialGradient(..) => *rect, } } } -impl StrokeOptions { - pub fn set_line_width(&mut self, _val: f32) { - match self { - StrokeOptions::Raqote(options) => options.width = _val, - } +impl StrokeOptionsHelpers for raqote::StrokeStyle { + fn set_line_width(&mut self, _val: f32) { + self.width = _val; } - pub fn set_miter_limit(&mut self, _val: f32) { - match self { - StrokeOptions::Raqote(options) => options.miter_limit = _val, - } + fn set_miter_limit(&mut self, _val: f32) { + self.miter_limit = _val; } - pub fn set_line_join(&mut self, val: LineJoinStyle) { - match self { - StrokeOptions::Raqote(options) => options.join = val.to_raqote_style(), - } + fn set_line_join(&mut self, val: LineJoinStyle) { + self.join = val.to_raqote_style(); } - pub fn set_line_cap(&mut self, val: LineCapStyle) { - match self { - StrokeOptions::Raqote(options) => options.cap = val.to_raqote_style(), - } + fn set_line_cap(&mut self, val: LineCapStyle) { + self.cap = val.to_raqote_style(); } - pub fn set_line_dash(&mut self, items: Vec) { - match self { - StrokeOptions::Raqote(options) => options.dash_array = items, - } + fn set_line_dash(&mut self, items: Vec) { + self.dash_array = items; } - pub fn set_line_dash_offset(&mut self, offset: f32) { - match self { - StrokeOptions::Raqote(options) => options.dash_offset = offset, - } - } - pub fn as_raqote(&self) -> &raqote::StrokeStyle { - match self { - StrokeOptions::Raqote(options) => options, - } + fn set_line_dash_offset(&mut self, offset: f32) { + self.dash_offset = offset; } } -impl DrawOptions { - pub fn set_alpha(&mut self, val: f32) { - match self { - DrawOptions::Raqote(draw_options) => draw_options.alpha = val, - } - } - pub fn as_raqote(&self) -> &raqote::DrawOptions { - match self { - DrawOptions::Raqote(options) => options, - } - } - fn as_raqote_mut(&mut self) -> &mut raqote::DrawOptions { - match self { - DrawOptions::Raqote(options) => options, - } +impl DrawOptionsHelpers for raqote::DrawOptions { + fn set_alpha(&mut self, val: f32) { + self.alpha = val; } } -impl Path { - pub fn transformed_copy_to_builder( - &self, - transform: &Transform2D, - ) -> Box { - Box::new(PathBuilder(Some(raqote::PathBuilder::from( - self.as_raqote().clone().transform(transform), - )))) +impl PathHelpers for raqote::Path { + fn transformed_copy_to_builder(&self, transform: &Transform2D) -> PathBuilder { + PathBuilder(Some(raqote::PathBuilder::from( + self.clone().transform(transform), + ))) } - pub fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D) -> bool { - self.as_raqote() - .clone() + fn contains_point(&self, x: f64, y: f64, path_transform: &Transform2D) -> bool { + self.clone() .transform(path_transform) .contains_point(0.1, x as f32, y as f32) } - pub fn copy_to_builder(&self) -> Box { - Box::new(PathBuilder(Some(raqote::PathBuilder::from( - self.as_raqote().clone(), - )))) - } - - pub fn as_raqote(&self) -> &raqote::Path { - match self { - Path::Raqote(p) => p, - } + fn copy_to_builder(&self) -> PathBuilder { + PathBuilder(Some(raqote::PathBuilder::from(self.clone()))) } } @@ -373,7 +363,7 @@ fn create_gradient_stops(gradient_stops: Vec) -> Vec for raqote::DrawTarget { fn clear_rect(&mut self, rect: &Rect) { let mut pb = raqote::PathBuilder::new(); pb.rect( @@ -385,59 +375,47 @@ impl GenericDrawTarget for raqote::DrawTarget { let mut options = raqote::DrawOptions::new(); options.blend_mode = raqote::BlendMode::Clear; let pattern = Pattern::Color(0, 0, 0, 0); - GenericDrawTarget::fill( - self, - &Path::Raqote(pb.finish()), - canvas_data::Pattern::Raqote(pattern), - &DrawOptions::Raqote(options), - ); + >::fill(self, &pb.finish(), pattern, &options); } #[allow(unsafe_code)] fn copy_surface( &mut self, - surface: SourceSurface, + surface: ::SourceSurface, source: Rect, destination: Point2D, ) { let mut dt = raqote::DrawTarget::new(source.size.width, source.size.height); - let data = surface.as_raqote(); + let data = surface; let s = unsafe { std::slice::from_raw_parts(data.as_ptr() as *const u32, data.len() / 4) }; dt.get_data_mut().copy_from_slice(s); raqote::DrawTarget::copy_surface(self, &dt, source.to_box2d(), destination); } - // TODO(pylbrecht) - // Somehow a duplicate of `create_gradient_stops()` with different types. - // It feels cumbersome to convert GradientStop back and forth just to use - // `create_gradient_stops()`, so I'll leave this here for now. - fn create_gradient_stops(&self, gradient_stops: Vec) -> GradientStops { - let mut stops = gradient_stops - .into_iter() - .map(|item| *item.as_raqote()) - .collect::>(); - // https://www.w3.org/html/test/results/2dcontext/annotated-spec/canvas.html#testrefs.2d.gradient.interpolate.overlap - stops.sort_by(|a, b| a.position.partial_cmp(&b.position).unwrap()); - GradientStops::Raqote(stops) - } - fn create_path_builder(&self) -> Box { - Box::new(PathBuilder::new()) + fn create_path_builder(&self) -> ::PathBuilder { + PathBuilder::new() } - fn create_similar_draw_target(&self, size: &Size2D) -> Box { - Box::new(raqote::DrawTarget::new(size.width, size.height)) + fn create_similar_draw_target( + &self, + size: &Size2D, + ) -> ::DrawTarget { + raqote::DrawTarget::new(size.width, size.height) } - fn create_source_surface_from_data(&self, data: &[u8]) -> Option { - Some(SourceSurface::Raqote(data.to_vec())) + fn create_source_surface_from_data( + &self, + data: &[u8], + ) -> Option<::SourceSurface> { + Some(data.to_vec()) } #[allow(unsafe_code)] fn draw_surface( &mut self, - surface: SourceSurface, + surface: ::SourceSurface, dest: Rect, source: Rect, filter: Filter, - draw_options: &DrawOptions, + draw_options: &::DrawOptions, ) { - let surface_data = surface.as_raqote(); + let surface_data = surface; let image = raqote::Image { width: source.size.width as i32, height: source.size.height as i32, @@ -470,33 +448,29 @@ impl GenericDrawTarget for raqote::DrawTarget { dest.size.height as f32, ); - GenericDrawTarget::fill( - self, - &Path::Raqote(pb.finish()), - canvas_data::Pattern::Raqote(pattern), - draw_options, - ); + >::fill(self, &pb.finish(), pattern, draw_options); } fn draw_surface_with_shadow( &self, - _surface: SourceSurface, + _surface: ::SourceSurface, _dest: &Point2D, - _color: &Color, + _color: &::Color, _offset: &Vector2D, _sigma: f32, - _operator: CompositionOp, + _operator: ::CompositionOp, ) { warn!("no support for drawing shadows"); } - fn fill(&mut self, path: &Path, pattern: canvas_data::Pattern, draw_options: &DrawOptions) { - match draw_options.as_raqote().blend_mode { + fn fill( + &mut self, + path: &::Path, + pattern: ::Pattern<'_>, + draw_options: &::DrawOptions, + ) { + match draw_options.blend_mode { raqote::BlendMode::Src => { self.clear(raqote::SolidSource::from_unpremultiplied_argb(0, 0, 0, 0)); - self.fill( - path.as_raqote(), - &pattern.source(), - draw_options.as_raqote(), - ); + self.fill(path, &source(&pattern), draw_options); }, raqote::BlendMode::Clear | raqote::BlendMode::SrcAtop | @@ -505,26 +479,19 @@ impl GenericDrawTarget for raqote::DrawTarget { raqote::BlendMode::Xor | raqote::BlendMode::DstOver | raqote::BlendMode::SrcOver => { - self.fill( - path.as_raqote(), - &pattern.source(), - draw_options.as_raqote(), - ); + self.fill(path, &source(&pattern), draw_options); }, raqote::BlendMode::SrcIn | raqote::BlendMode::SrcOut | raqote::BlendMode::DstIn | raqote::BlendMode::DstAtop => { - let mut options = *draw_options.as_raqote(); + let mut options = *draw_options; self.push_layer_with_blend(1., options.blend_mode); options.blend_mode = raqote::BlendMode::SrcOver; - self.fill(path.as_raqote(), &pattern.source(), &options); + self.fill(path, &source(&pattern), &options); self.pop_layer(); }, - _ => warn!( - "unrecognized blend mode: {:?}", - draw_options.as_raqote().blend_mode - ), + _ => warn!("unrecognized blend mode: {:?}", draw_options.blend_mode), } } @@ -532,8 +499,8 @@ impl GenericDrawTarget for raqote::DrawTarget { &mut self, text_runs: Vec, start: Point2D, - pattern: &canvas_data::Pattern, - draw_options: &DrawOptions, + pattern: &::Pattern<'_>, + draw_options: &::DrawOptions, ) { let mut advance = 0.; for run in text_runs.iter() { @@ -581,8 +548,8 @@ impl GenericDrawTarget for raqote::DrawTarget { run.font.descriptor.pt_size.to_f32_px(), &ids, &positions, - &pattern.source(), - draw_options.as_raqote(), + &source(pattern), + draw_options, ); }) } @@ -591,8 +558,8 @@ impl GenericDrawTarget for raqote::DrawTarget { fn fill_rect( &mut self, rect: &Rect, - pattern: canvas_data::Pattern, - draw_options: Option<&DrawOptions>, + pattern: ::Pattern<'_>, + draw_options: Option<&::DrawOptions>, ) { let mut pb = raqote::PathBuilder::new(); pb.rect( @@ -602,16 +569,16 @@ impl GenericDrawTarget for raqote::DrawTarget { rect.size.height, ); let draw_options = if let Some(options) = draw_options { - *options.as_raqote() + *options } else { raqote::DrawOptions::new() }; - GenericDrawTarget::fill( + >::fill( self, - &Path::Raqote(pb.finish()), + &pb.finish(), pattern, - &DrawOptions::Raqote(draw_options), + &draw_options, ); } fn get_size(&self) -> Size2D { @@ -623,41 +590,36 @@ impl GenericDrawTarget for raqote::DrawTarget { fn pop_clip(&mut self) { self.pop_clip(); } - fn push_clip(&mut self, path: &Path) { - self.push_clip(path.as_raqote()); + fn push_clip(&mut self, path: &::Path) { + self.push_clip(path); } fn set_transform(&mut self, matrix: &Transform2D) { self.set_transform(matrix); } - fn snapshot(&self) -> SourceSurface { - SourceSurface::Raqote(self.snapshot_data_owned()) + fn surface(&self) -> ::SourceSurface { + self.bytes().to_vec() } fn stroke( &mut self, - path: &Path, - pattern: canvas_data::Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, + path: &::Path, + pattern: Pattern<'_>, + stroke_options: &::StrokeOptions, + draw_options: &::DrawOptions, ) { - self.stroke( - path.as_raqote(), - &pattern.source(), - stroke_options.as_raqote(), - draw_options.as_raqote(), - ); + self.stroke(path, &source(&pattern), stroke_options, draw_options); } fn stroke_line( &mut self, start: Point2D, end: Point2D, - pattern: canvas_data::Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, + pattern: ::Pattern<'_>, + stroke_options: &::StrokeOptions, + draw_options: &::DrawOptions, ) { let mut pb = raqote::PathBuilder::new(); pb.move_to(start.x, start.y); pb.line_to(end.x, end.y); - let mut stroke_options = stroke_options.as_raqote().clone(); + let mut stroke_options = stroke_options.clone(); let cap = match stroke_options.join { raqote::LineJoin::Round => raqote::LineCap::Round, _ => raqote::LineCap::Butt, @@ -666,17 +628,17 @@ impl GenericDrawTarget for raqote::DrawTarget { self.stroke( &pb.finish(), - &pattern.source(), + &source(&pattern), &stroke_options, - draw_options.as_raqote(), + draw_options, ); } fn stroke_rect( &mut self, rect: &Rect, - pattern: canvas_data::Pattern, - stroke_options: &StrokeOptions, - draw_options: &DrawOptions, + pattern: ::Pattern<'_>, + stroke_options: &::StrokeOptions, + draw_options: &::DrawOptions, ) { let mut pb = raqote::PathBuilder::new(); pb.rect( @@ -688,26 +650,15 @@ impl GenericDrawTarget for raqote::DrawTarget { self.stroke( &pb.finish(), - &pattern.source(), - stroke_options.as_raqote(), - draw_options.as_raqote(), + &source(&pattern), + stroke_options, + draw_options, ); } #[allow(unsafe_code)] - fn snapshot_data(&self, f: &dyn Fn(&[u8]) -> Vec) -> Vec { + fn bytes(&self) -> &[u8] { let v = self.get_data(); - f( - unsafe { - std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of_val(v)) - }, - ) - } - #[allow(unsafe_code)] - fn snapshot_data_owned(&self) -> Vec { - let v = self.get_data(); - unsafe { - std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of_val(v)).into() - } + unsafe { std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of_val(v)) } } } @@ -720,7 +671,7 @@ impl Filter { } } -struct PathBuilder(Option); +pub(crate) struct PathBuilder(Option); impl PathBuilder { fn new() -> PathBuilder { @@ -728,25 +679,7 @@ impl PathBuilder { } } -impl GenericPathBuilder for PathBuilder { - fn arc( - &mut self, - origin: Point2D, - radius: f32, - start_angle: f32, - end_angle: f32, - anticlockwise: bool, - ) { - self.ellipse( - origin, - radius, - radius, - 0., - start_angle, - end_angle, - anticlockwise, - ); - } +impl GenericPathBuilder for PathBuilder { fn bezier_curve_to( &mut self, control_point1: &Point2D, @@ -762,98 +695,16 @@ impl GenericPathBuilder for PathBuilder { control_point3.y, ); } + fn close(&mut self) { self.0.as_mut().unwrap().close(); } - fn ellipse( - &mut self, - origin: Point2D, - radius_x: f32, - radius_y: f32, - rotation_angle: f32, - start_angle: f32, - end_angle: f32, - anticlockwise: bool, - ) { - let mut start = Angle::radians(start_angle); - let mut end = Angle::radians(end_angle); - - // Wrap angles mod 2 * PI if necessary - if !anticlockwise && start > end + Angle::two_pi() || - anticlockwise && end > start + Angle::two_pi() - { - start = start.positive(); - end = end.positive(); - } - - // Calculate the total arc we're going to sweep. - let sweep = match anticlockwise { - true => { - if end - start == Angle::two_pi() { - -Angle::two_pi() - } else if end > start { - -(Angle::two_pi() - (end - start)) - } else { - -(start - end) - } - }, - false => { - if start - end == Angle::two_pi() { - Angle::two_pi() - } else if start > end { - Angle::two_pi() - (start - end) - } else { - end - start - } - }, - }; - - let arc: Arc = Arc { - center: origin, - radii: Vector2D::new(radius_x, radius_y), - start_angle: start, - sweep_angle: sweep, - x_rotation: Angle::radians(rotation_angle), - }; - - self.line_to(arc.from()); - - arc.for_each_quadratic_bezier(&mut |q| { - self.quadratic_curve_to(&q.ctrl, &q.to); - }); - } - - fn svg_arc( - &mut self, - radius_x: f32, - radius_y: f32, - rotation_angle: f32, - large_arc: bool, - sweep: bool, - end_point: Point2D, - ) { - let Some(start) = self.get_current_point() else { - return; - }; - - let arc = lyon_geom::SvgArc { - from: start, - to: end_point, - radii: lyon_geom::vector(radius_x, radius_y), - x_rotation: lyon_geom::Angle::degrees(rotation_angle), - flags: lyon_geom::ArcFlags { large_arc, sweep }, - }; - - arc.for_each_quadratic_bezier(&mut |q| { - self.quadratic_curve_to(&q.ctrl, &q.to); - }); - } fn get_current_point(&mut self) -> Option> { let path = self.finish(); - self.0 = Some(path.as_raqote().clone().into()); + self.0 = Some(path.clone().into()); - path.as_raqote().ops.iter().last().and_then(|op| match op { + path.ops.iter().last().and_then(|op| match op { PathOp::MoveTo(point) | PathOp::LineTo(point) => Some(Point2D::new(point.x, point.y)), PathOp::CubicTo(_, _, point) => Some(Point2D::new(point.x, point.y)), PathOp::QuadTo(_, point) => Some(Point2D::new(point.x, point.y)), @@ -875,8 +726,8 @@ impl GenericPathBuilder for PathBuilder { end_point.y, ); } - fn finish(&mut self) -> Path { - Path::Raqote(self.0.take().unwrap().finish()) + fn finish(&mut self) -> raqote::Path { + self.0.take().unwrap().finish() } } @@ -988,14 +839,6 @@ impl ToRaqotePattern<'_> for FillOrStrokeStyle { } } -impl Color { - fn as_raqote(&self) -> &raqote::SolidSource { - match self { - Color::Raqote(s) => s, - } - } -} - impl ToRaqoteStyle for AbsoluteColor { type Target = raqote::SolidSource; @@ -1065,19 +908,3 @@ impl ToRaqoteStyle for CompositionStyle { } } } - -impl SourceSurface { - fn as_raqote(&self) -> &Vec { - match self { - SourceSurface::Raqote(s) => s, - } - } -} - -impl GradientStop { - fn as_raqote(&self) -> &raqote::GradientStop { - match self { - GradientStop::Raqote(s) => s, - } - } -} diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index 11d87635f32..084bb54a5fc 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -13,12 +13,12 @@ path = "lib.rs" [features] default = [] -multiview = [] tracing = ["dep:tracing"] webxr = ["dep:webxr"] [dependencies] base = { workspace = true } +bincode = { workspace = true } bitflags = { workspace = true } compositing_traits = { workspace = true } constellation_traits = { workspace = true } @@ -34,15 +34,16 @@ log = { workspace = true } net = { path = "../net" } pixels = { path = "../pixels" } profile_traits = { workspace = true } -script_traits = { workspace = true } +servo_allocator = { path = "../allocator" } servo_config = { path = "../config" } servo_geometry = { path = "../geometry" } stylo_traits = { workspace = true } +timers = { path = "../timers" } tracing = { workspace = true, optional = true } webrender = { workspace = true } webrender_api = { workspace = true } -webrender_traits = { workspace = true } webxr = { path = "../webxr", optional = true } +wr_malloc_size_of = { workspace = true } [dev-dependencies] surfman = { workspace = true } diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 7afee58a4cf..a76b0022122 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -7,40 +7,38 @@ use std::collections::HashMap; use std::env; use std::fs::create_dir_all; use std::iter::once; -use std::mem::take; use std::rc::Rc; use std::sync::Arc; -use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; +use std::time::{SystemTime, UNIX_EPOCH}; use base::cross_process_instant::CrossProcessInstant; use base::id::{PipelineId, WebViewId}; use base::{Epoch, WebRenderEpochToU16}; use bitflags::bitflags; +use compositing_traits::display_list::{CompositorDisplayListInfo, HitTestInfo, ScrollTree}; +use compositing_traits::rendering_context::RenderingContext; use compositing_traits::{ - CompositionPipeline, CompositorMsg, CompositorReceiver, SendableFrameTree, + CompositionPipeline, CompositorMsg, ImageUpdate, SendableFrameTree, WebViewTrait, }; -use constellation_traits::{ - AnimationTickType, CompositorHitTestResult, EmbedderToConstellationMessage, PaintMetricEvent, - UntrustedNodeAddress, WindowSizeType, -}; -use crossbeam_channel::Sender; +use constellation_traits::{EmbedderToConstellationMessage, PaintMetricEvent}; +use crossbeam_channel::{Receiver, Sender}; use dpi::PhysicalSize; use embedder_traits::{ - Cursor, InputEvent, MouseButtonEvent, MouseMoveEvent, ScreenGeometry, ShutdownState, - TouchEventType, ViewportDetails, + CompositorHitTestResult, Cursor, InputEvent, MouseButtonEvent, MouseMoveEvent, ShutdownState, + TouchEventType, UntrustedNodeAddress, ViewportDetails, WheelDelta, WheelEvent, WheelMode, }; -use euclid::{Box2D, Point2D, Rect, Scale, Size2D, Transform3D}; +use euclid::{Point2D, Rect, Scale, Size2D, Transform3D, Vector2D}; use fnv::FnvHashMap; use ipc_channel::ipc::{self, IpcSharedMemory}; use libc::c_void; use log::{debug, info, trace, warn}; -use pixels::{CorsStatus, Image, ImageFrame, PixelFormat}; +use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage}; +use profile_traits::mem::{ProcessReports, ProfilerRegistration, Report, ReportKind}; use profile_traits::time::{self as profile_time, ProfilerCategory}; -use profile_traits::time_profile; -use script_traits::AnimationState; +use profile_traits::{path, time_profile}; use servo_config::opts; use servo_geometry::DeviceIndependentPixel; -use style_traits::{CSSPixel, PinchZoomFactor}; +use style_traits::CSSPixel; use webrender::{CaptureBits, RenderApi, Transaction}; use webrender_api::units::{ DeviceIntPoint, DeviceIntRect, DevicePixel, DevicePoint, DeviceRect, LayoutPoint, LayoutRect, @@ -53,14 +51,11 @@ use webrender_api::{ RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo, SpatialId, SpatialTreeItemKey, TransformStyle, }; -use webrender_traits::display_list::{HitTestInfo, ScrollTree}; -use webrender_traits::rendering_context::RenderingContext; -use webrender_traits::{CrossProcessCompositorMessage, ImageUpdate, RendererWebView}; use crate::InitialCompositorState; -use crate::webview::{UnknownWebView, WebView}; +use crate::refresh_driver::RefreshDriver; use crate::webview_manager::WebViewManager; -use crate::windowing::{self, WebRenderDebugOption, WindowMethods}; +use crate::webview_renderer::{PinchZoomResult, UnknownWebView, WebViewRenderer}; #[derive(Debug, PartialEq)] enum UnableToComposite { @@ -74,10 +69,6 @@ enum NotReadyToPaint { WaitingOnConstellation, } -// Default viewport constraints -const MAX_ZOOM: f32 = 8.0; -const MIN_ZOOM: f32 = 0.1; - /// Holds the state when running reftests that determines when it is /// safe to save the output image. #[derive(Clone, Copy, Debug, PartialEq)] @@ -86,8 +77,19 @@ enum ReadyState { WaitingForConstellationReply, ReadyToSaveImage, } + +/// An option to control what kind of WebRender debugging is enabled while Servo is running. +#[derive(Clone)] +pub enum WebRenderDebugOption { + Profiler, + TextureCacheDebug, + RenderTargetDebug, +} /// Data that is shared by all WebView renderers. pub struct ServoRenderer { + /// The [`RefreshDriver`] which manages the rythym of painting. + refresh_driver: RefreshDriver, + /// This is a temporary map between [`PipelineId`]s and their associated [`WebViewId`]. Once /// all renderer operations become per-`WebView` this map can be removed, but we still sometimes /// need to work backwards to figure out what `WebView` is associated with a `Pipeline`. @@ -98,7 +100,7 @@ pub struct ServoRenderer { shutdown_state: Rc>, /// The port on which we receive messages. - compositor_receiver: CompositorReceiver, + compositor_receiver: Receiver, /// The channel on which messages can be sent to the constellation. pub(crate) constellation_sender: Sender, @@ -134,31 +136,12 @@ pub struct IOCompositor { /// Data that is shared by all WebView renderers. global: Rc>, - /// Our top-level browsing contexts. - webviews: WebViewManager, - - /// The application window. - pub window: Rc, - - /// "Mobile-style" zoom that does not reflow the page. - viewport_zoom: PinchZoomFactor, - - /// Viewport zoom constraints provided by @viewport. - min_viewport_zoom: Option, - max_viewport_zoom: Option, - - /// "Desktop-style" zoom that resizes the viewport to fit the window. - page_zoom: Scale, + /// Our [`WebViewRenderer`]s, one for every `WebView`. + webview_renderers: WebViewManager, /// Tracks whether or not the view needs to be repainted. needs_repaint: Cell, - /// Tracks whether the zoom action has happened recently. - zoom_action: bool, - - /// The time of the last zoom action has started. - zoom_time: f64, - /// Used by the logic that determines when it is safe to output an /// image for the reftest framework. ready_to_save_state: ReadyState, @@ -169,21 +152,17 @@ pub struct IOCompositor { /// The surfman instance that webrender targets rendering_context: Rc, - /// The HighDPI factor of the native window, its view and the screen. - /// TODO: Eventually this should be a property of the `WebView`. - hidpi_factor: Scale, - /// The number of frames pending to receive from WebRender. pending_frames: usize, - /// The [`Instant`] of the last animation tick, used to avoid flooding the Constellation and - /// ScriptThread with a deluge of animation ticks. - last_animation_tick: Instant, + /// A handle to the memory profiler which will automatically unregister + /// when it's dropped. + _mem_profiler_registration: ProfilerRegistration, } /// Why we need to be repainted. This is used for debugging. -#[derive(Clone, Copy, Default)] -struct RepaintReason(u8); +#[derive(Clone, Copy, Default, PartialEq)] +pub(crate) struct RepaintReason(u8); bitflags! { impl RepaintReason: u8 { @@ -217,9 +196,6 @@ pub(crate) struct PipelineDetails { /// The pipeline associated with this PipelineDetails object. pub pipeline: Option, - /// The [`PipelineId`] of this pipeline. - pub id: PipelineId, - /// The id of the parent pipeline, if any. pub parent_pipeline_id: Option, @@ -260,38 +236,15 @@ impl PipelineDetails { self.animation_callbacks_running } - pub(crate) fn tick_animations(&self, compositor: &IOCompositor) -> bool { - let animation_callbacks_running = self.animation_callbacks_running; - let animations_running = self.animations_running; - if !animation_callbacks_running && !animations_running { - return false; - } - - if self.throttled { - return false; - } - - let mut tick_type = AnimationTickType::empty(); - if animations_running { - tick_type.insert(AnimationTickType::CSS_ANIMATIONS_AND_TRANSITIONS); - } - if animation_callbacks_running { - tick_type.insert(AnimationTickType::REQUEST_ANIMATION_FRAME); - } - - let msg = EmbedderToConstellationMessage::TickAnimation(self.id, tick_type); - if let Err(e) = compositor.global.borrow().constellation_sender.send(msg) { - warn!("Sending tick to constellation failed ({:?}).", e); - } - true + pub(crate) fn animating(&self) -> bool { + !self.throttled && (self.animation_callbacks_running || self.animations_running) } } impl PipelineDetails { - pub(crate) fn new(id: PipelineId) -> PipelineDetails { + pub(crate) fn new() -> PipelineDetails { PipelineDetails { pipeline: None, - id, parent_pipeline_id: None, most_recent_display_list_epoch: None, animations_running: false, @@ -425,13 +378,18 @@ impl ServoRenderer { } impl IOCompositor { - pub fn new( - window: Rc, - state: InitialCompositorState, - convert_mouse_to_touch: bool, - ) -> Self { + pub fn new(state: InitialCompositorState, convert_mouse_to_touch: bool) -> Self { + let registration = state.mem_profiler_chan.prepare_memory_reporting( + "compositor".into(), + state.sender.clone(), + CompositorMsg::CollectMemoryReport, + ); let compositor = IOCompositor { global: Rc::new(RefCell::new(ServoRenderer { + refresh_driver: RefreshDriver::new( + state.constellation_chan.clone(), + state.event_loop_waker, + ), shutdown_state: state.shutdown_state, pipeline_to_webview_map: Default::default(), compositor_receiver: state.receiver, @@ -446,21 +404,13 @@ impl IOCompositor { cursor: Cursor::None, cursor_pos: DevicePoint::new(0.0, 0.0), })), - webviews: WebViewManager::default(), - hidpi_factor: window.hidpi_factor(), - window, + webview_renderers: WebViewManager::default(), needs_repaint: Cell::default(), - page_zoom: Scale::new(1.0), - viewport_zoom: PinchZoomFactor::new(1.0), - min_viewport_zoom: Some(PinchZoomFactor::new(1.0)), - max_viewport_zoom: None, - zoom_action: false, - zoom_time: 0f64, ready_to_save_state: ReadyState::Unknown, webrender: Some(state.webrender), rendering_context: state.rendering_context, pending_frames: 0, - last_animation_tick: Instant::now(), + _mem_profiler_registration: registration, }; { @@ -481,14 +431,18 @@ impl IOCompositor { } } - pub fn default_webview_viewport_details(&self) -> ViewportDetails { - // The division by 1 represents the page's default zoom of 100%, - // and gives us the appropriate CSSPixel type for the viewport. - let hidpi_scale_factor = self.window.hidpi_factor(); - let scaled_viewport_size = self.rendering_context.size2d().to_f32() / hidpi_scale_factor; - ViewportDetails { - size: scaled_viewport_size / Scale::new(1.0), - hidpi_scale_factor: Scale::new(hidpi_scale_factor.0), + pub fn rendering_context_size(&self) -> Size2D { + self.rendering_context.size2d() + } + + pub fn webxr_running(&self) -> bool { + #[cfg(feature = "webxr")] + { + self.global.borrow().webxr_main_thread.running() + } + #[cfg(not(feature = "webxr"))] + { + false } } @@ -499,7 +453,16 @@ impl IOCompositor { } pub fn needs_repaint(&self) -> bool { - !self.needs_repaint.get().is_empty() + let repaint_reason = self.needs_repaint.get(); + if repaint_reason.is_empty() { + return false; + } + + !self + .global + .borrow() + .refresh_driver + .wait_to_paint(repaint_reason) } pub fn finish_shutting_down(&mut self) { @@ -509,8 +472,8 @@ impl IOCompositor { .global .borrow_mut() .compositor_receiver - .try_recv_compositor_msg() - .is_some() + .try_recv() + .is_ok() {} // Tell the profiler, memory profiler, and scrolling timer to shut down. @@ -539,28 +502,46 @@ impl IOCompositor { } match msg { + CompositorMsg::CollectMemoryReport(sender) => { + let ops = + wr_malloc_size_of::MallocSizeOfOps::new(servo_allocator::usable_size, None); + let report = self.global.borrow().webrender_api.report_memory(ops); + let reports = vec![ + Report { + path: path!["webrender", "fonts"], + kind: ReportKind::ExplicitJemallocHeapSize, + size: report.fonts, + }, + Report { + path: path!["webrender", "images"], + kind: ReportKind::ExplicitJemallocHeapSize, + size: report.images, + }, + Report { + path: path!["webrender", "display-list"], + kind: ReportKind::ExplicitJemallocHeapSize, + size: report.display_list, + }, + ]; + sender.send(ProcessReports::new(reports)); + }, + CompositorMsg::ChangeRunningAnimationsState( webview_id, pipeline_id, animation_state, ) => { - let mut throttled = true; - if let Some(webview) = self.webviews.get_mut(webview_id) { - throttled = - webview.change_running_animations_state(pipeline_id, animation_state); - } + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { + return; + }; - // These operations should eventually happen per-WebView, but they are global now as rendering - // is still global to all WebViews. - if !throttled && animation_state == AnimationState::AnimationsPresent { - self.set_needs_repaint(RepaintReason::ChangedAnimationState); - } - - if !throttled && animation_state == AnimationState::AnimationCallbacksPresent { - // We need to fetch the WebView again in order to avoid a double borrow. - if let Some(webview) = self.webviews.get(webview_id) { - webview.tick_animations_for_pipeline(pipeline_id, self); - } + if webview_renderer + .change_pipeline_running_animations_state(pipeline_id, animation_state) + { + self.global + .borrow() + .refresh_driver + .notify_animation_state_changed(webview_renderer); } }, @@ -573,15 +554,15 @@ impl IOCompositor { }, CompositorMsg::TouchEventProcessed(webview_id, result) => { - let Some(webview) = self.webviews.get_mut(webview_id) else { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { warn!("Handling input event for unknown webview: {webview_id}"); return; }; - webview.on_touch_event_processed(result); + webview_renderer.on_touch_event_processed(result); }, - CompositorMsg::CreatePng(page_rect, reply) => { - let res = self.render_to_shared_memory(page_rect); + CompositorMsg::CreatePng(webview_id, page_rect, reply) => { + let res = self.render_to_shared_memory(webview_id, page_rect); if let Err(ref e) = res { info!("Error retrieving PNG: {:?}", e); } @@ -605,9 +586,15 @@ impl IOCompositor { }, CompositorMsg::SetThrottled(webview_id, pipeline_id, throttled) => { - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.set_throttled(pipeline_id, throttled); - self.process_animations(true); + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { + return; + }; + + if webview_renderer.set_throttled(pipeline_id, throttled) { + self.global + .borrow() + .refresh_driver + .notify_animation_state_changed(webview_renderer); } }, @@ -616,8 +603,8 @@ impl IOCompositor { "Compositor got pipeline exited: {:?} {:?}", webview_id, pipeline_id ); - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.remove_pipeline(pipeline_id); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.remove_pipeline(pipeline_id); } let _ = sender.send(()); }, @@ -648,63 +635,74 @@ impl IOCompositor { } }, - CompositorMsg::WebDriverMouseButtonEvent(webview_id, action, button, x, y) => { - let dppx = self.device_pixels_per_page_pixel(); - let point = dppx.transform_point(Point2D::new(x, y)); - let Some(webview) = self.webviews.get_mut(webview_id) else { + CompositorMsg::WebDriverMouseButtonEvent( + webview_id, + action, + button, + x, + y, + message_id, + ) => { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { warn!("Handling input event for unknown webview: {webview_id}"); return; }; - webview.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent { - point, - action, - button, - })); + let dppx = webview_renderer.device_pixels_per_page_pixel(); + let point = dppx.transform_point(Point2D::new(x, y)); + webview_renderer.dispatch_input_event( + InputEvent::MouseButton(MouseButtonEvent::new(action, button, point)) + .with_webdriver_message_id(Some(message_id)), + ); }, - CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y) => { - let dppx = self.device_pixels_per_page_pixel(); - let point = dppx.transform_point(Point2D::new(x, y)); - let Some(webview) = self.webviews.get_mut(webview_id) else { + CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y, message_id) => { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { warn!("Handling input event for unknown webview: {webview_id}"); return; }; - webview.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point })); + let dppx = webview_renderer.device_pixels_per_page_pixel(); + let point = dppx.transform_point(Point2D::new(x, y)); + webview_renderer.dispatch_input_event( + InputEvent::MouseMove(MouseMoveEvent::new(point)) + .with_webdriver_message_id(Some(message_id)), + ); }, - CompositorMsg::CrossProcess(cross_proces_message) => { - self.handle_cross_process_message(cross_proces_message); + CompositorMsg::WebDriverWheelScrollEvent(webview_id, x, y, delta_x, delta_y) => { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { + warn!("Handling input event for unknown webview: {webview_id}"); + return; + }; + let delta = WheelDelta { + x: delta_x, + y: delta_y, + z: 0.0, + mode: WheelMode::DeltaPixel, + }; + let dppx = webview_renderer.device_pixels_per_page_pixel(); + let point = dppx.transform_point(Point2D::new(x, y)); + let scroll_delta = + dppx.transform_vector(Vector2D::new(delta_x as f32, delta_y as f32)); + webview_renderer + .dispatch_input_event(InputEvent::Wheel(WheelEvent { delta, point })); + webview_renderer.on_webdriver_wheel_action(scroll_delta, point); }, - } - } - /// Accept messages from content processes that need to be relayed to the WebRender - /// instance in the parent process. - #[cfg_attr( - feature = "tracing", - tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") - )] - fn handle_cross_process_message(&mut self, msg: CrossProcessCompositorMessage) { - match msg { - CrossProcessCompositorMessage::SendInitialTransaction(pipeline) => { + CompositorMsg::SendInitialTransaction(pipeline) => { let mut txn = Transaction::new(); txn.set_display_list(WebRenderEpoch(0), (pipeline, Default::default())); self.generate_frame(&mut txn, RenderReasons::SCENE); self.global.borrow_mut().send_transaction(txn); }, - CrossProcessCompositorMessage::SendScrollNode( - webview_id, - pipeline_id, - point, - external_scroll_id, - ) => { - let Some(webview) = self.webviews.get_mut(webview_id) else { + CompositorMsg::SendScrollNode(webview_id, pipeline_id, point, external_scroll_id) => { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { return; }; let pipeline_id = pipeline_id.into(); - let Some(pipeline_details) = webview.pipelines.get_mut(&pipeline_id) else { + let Some(pipeline_details) = webview_renderer.pipelines.get_mut(&pipeline_id) + else { return; }; @@ -732,13 +730,25 @@ impl IOCompositor { self.global.borrow_mut().send_transaction(txn); }, - CrossProcessCompositorMessage::SendDisplayList { + CompositorMsg::SendDisplayList { webview_id, - display_list_info, display_list_descriptor, display_list_receiver, } => { // This must match the order from the sender, currently in `shared/script/lib.rs`. + let display_list_info = match display_list_receiver.recv() { + Ok(display_list_info) => display_list_info, + Err(error) => { + return warn!("Could not receive display list info: {error}"); + }, + }; + let display_list_info: CompositorDisplayListInfo = + match bincode::deserialize(&display_list_info) { + Ok(display_list_info) => display_list_info, + Err(error) => { + return warn!("Could not deserialize display list info: {error}"); + }, + }; let items_data = match display_list_receiver.recv() { Ok(display_list_data) => display_list_data, Err(error) => { @@ -779,12 +789,12 @@ impl IOCompositor { ) .entered(); - let Some(webview) = self.webviews.get_mut(webview_id) else { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { return warn!("Could not find WebView for incoming display list"); }; let pipeline_id = display_list_info.pipeline_id; - let details = webview.ensure_pipeline_details(pipeline_id.into()); + let details = webview_renderer.ensure_pipeline_details(pipeline_id.into()); details.most_recent_display_list_epoch = Some(display_list_info.epoch); details.hit_test_items = display_list_info.hit_test_info; details.install_new_scroll_tree(display_list_info.scroll_tree); @@ -809,7 +819,7 @@ impl IOCompositor { self.global.borrow_mut().send_transaction(transaction); }, - CrossProcessCompositorMessage::HitTest(pipeline, point, flags, sender) => { + CompositorMsg::HitTest(pipeline, point, flags, sender) => { // When a display list is sent to WebRender, it starts scene building in a // separate thread and then that display list is available for hit testing. // Without flushing scene building, any hit test we do might be done against @@ -835,11 +845,11 @@ impl IOCompositor { let _ = sender.send(result); }, - CrossProcessCompositorMessage::GenerateImageKey(sender) => { + CompositorMsg::GenerateImageKey(sender) => { let _ = sender.send(self.global.borrow().webrender_api.generate_image_key()); }, - CrossProcessCompositorMessage::UpdateImages(updates) => { + CompositorMsg::UpdateImages(updates) => { let mut txn = Transaction::new(); for update in updates { match update { @@ -855,26 +865,21 @@ impl IOCompositor { self.global.borrow_mut().send_transaction(txn); }, - CrossProcessCompositorMessage::AddFont(font_key, data, index) => { + CompositorMsg::AddFont(font_key, data, index) => { self.add_font(font_key, index, data); }, - CrossProcessCompositorMessage::AddSystemFont(font_key, native_handle) => { + CompositorMsg::AddSystemFont(font_key, native_handle) => { let mut transaction = Transaction::new(); transaction.add_native_font(font_key, native_handle); self.global.borrow_mut().send_transaction(transaction); }, - CrossProcessCompositorMessage::AddFontInstance( - font_instance_key, - font_key, - size, - flags, - ) => { + CompositorMsg::AddFontInstance(font_instance_key, font_key, size, flags) => { self.add_font_instance(font_instance_key, font_key, size, flags); }, - CrossProcessCompositorMessage::RemoveFonts(keys, instance_keys) => { + CompositorMsg::RemoveFonts(keys, instance_keys) => { let mut transaction = Transaction::new(); for instance in instance_keys.into_iter() { @@ -887,13 +892,13 @@ impl IOCompositor { self.global.borrow_mut().send_transaction(transaction); }, - CrossProcessCompositorMessage::AddImage(key, desc, data) => { + CompositorMsg::AddImage(key, desc, data) => { let mut txn = Transaction::new(); txn.add_image(key, desc, data.into(), None); self.global.borrow_mut().send_transaction(txn); }, - CrossProcessCompositorMessage::GenerateFontKeys( + CompositorMsg::GenerateFontKeys( number_of_font_keys, number_of_font_instance_keys, result_sender, @@ -911,46 +916,41 @@ impl IOCompositor { .collect(); let _ = result_sender.send((font_keys, font_instance_keys)); }, - CrossProcessCompositorMessage::GetClientWindowRect(webview_id, response_sender) => { - let screen_geometry = self.webview_screen_geometry(webview_id); - let rect = DeviceIntRect::from_origin_and_size( - screen_geometry.offset, - self.rendering_context.size2d().to_i32(), - ) - .to_f32() / - self.hidpi_factor; - - if let Err(error) = response_sender.send(rect.to_i32()) { + CompositorMsg::GetClientWindowRect(webview_id, response_sender) => { + let client_window_rect = self + .webview_renderers + .get(webview_id) + .map(|webview_renderer| { + webview_renderer.client_window_rect(self.rendering_context.size2d()) + }) + .unwrap_or_default(); + if let Err(error) = response_sender.send(client_window_rect) { warn!("Sending response to get client window failed ({error:?})."); } }, - CrossProcessCompositorMessage::GetScreenSize(webview_id, response_sender) => { - let screen_geometry = self.webview_screen_geometry(webview_id); - let screen_size = screen_geometry.size.to_f32() / self.hidpi_factor; - - if let Err(error) = response_sender.send(screen_size.to_i32()) { + CompositorMsg::GetScreenSize(webview_id, response_sender) => { + let screen_size = self + .webview_renderers + .get(webview_id) + .map(WebViewRenderer::screen_size) + .unwrap_or_default(); + if let Err(error) = response_sender.send(screen_size) { warn!("Sending response to get screen size failed ({error:?})."); } }, - CrossProcessCompositorMessage::GetAvailableScreenSize(webview_id, response_sender) => { - let screen_geometry = self.webview_screen_geometry(webview_id); - let available_screen_size = - screen_geometry.available_size.to_f32() / self.hidpi_factor; - - if let Err(error) = response_sender.send(available_screen_size.to_i32()) { + CompositorMsg::GetAvailableScreenSize(webview_id, response_sender) => { + let available_screen_size = self + .webview_renderers + .get(webview_id) + .map(WebViewRenderer::available_screen_size) + .unwrap_or_default(); + if let Err(error) = response_sender.send(available_screen_size) { warn!("Sending response to get screen size failed ({error:?})."); } }, } } - fn webview_screen_geometry(&self, webview_id: WebViewId) -> ScreenGeometry { - self.webviews - .get(webview_id) - .and_then(|webview| webview.renderer_webview.screen_geometry()) - .unwrap_or_default() - } - /// Handle messages sent to the compositor during the shutdown process. In general, /// the things the compositor can do in this state are limited. It's very important to /// answer any synchronous messages though as other threads might be waiting on the @@ -967,21 +967,19 @@ impl IOCompositor { "Compositor got pipeline exited: {:?} {:?}", webview_id, pipeline_id ); - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.remove_pipeline(pipeline_id); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.remove_pipeline(pipeline_id); } let _ = sender.send(()); }, - CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GenerateImageKey( - sender, - )) => { + CompositorMsg::GenerateImageKey(sender) => { let _ = sender.send(self.global.borrow().webrender_api.generate_image_key()); }, - CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GenerateFontKeys( + CompositorMsg::GenerateFontKeys( number_of_font_keys, number_of_font_instance_keys, result_sender, - )) => { + ) => { let font_keys = (0..number_of_font_keys) .map(|_| self.global.borrow().webrender_api.generate_font_key()) .collect(); @@ -995,26 +993,17 @@ impl IOCompositor { .collect(); let _ = result_sender.send((font_keys, font_instance_keys)); }, - CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GetClientWindowRect( - _, - response_sender, - )) => { + CompositorMsg::GetClientWindowRect(_, response_sender) => { if let Err(error) = response_sender.send(Default::default()) { warn!("Sending response to get client window failed ({error:?})."); } }, - CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GetScreenSize( - _, - response_sender, - )) => { + CompositorMsg::GetScreenSize(_, response_sender) => { if let Err(error) = response_sender.send(Default::default()) { warn!("Sending response to get client window failed ({error:?})."); } }, - CompositorMsg::CrossProcess(CrossProcessCompositorMessage::GetAvailableScreenSize( - _, - response_sender, - )) => { + CompositorMsg::GetAvailableScreenSize(_, response_sender) => { if let Err(error) = response_sender.send(Default::default()) { warn!("Sending response to get client window failed ({error:?})."); } @@ -1061,43 +1050,50 @@ impl IOCompositor { let mut builder = webrender_api::DisplayListBuilder::new(root_pipeline); builder.begin(); - let zoom_factor = self.device_pixels_per_page_pixel().0; - let zoom_reference_frame = builder.push_reference_frame( + let root_reference_frame = SpatialId::root_reference_frame(root_pipeline); + + let viewport_size = self.rendering_context.size2d().to_f32().to_untyped(); + let viewport_rect = LayoutRect::from_origin_and_size( LayoutPoint::zero(), - SpatialId::root_reference_frame(root_pipeline), - TransformStyle::Flat, - PropertyBinding::Value(Transform3D::scale(zoom_factor, zoom_factor, 1.)), - ReferenceFrameKind::Transform { - is_2d_scale_translation: true, - should_snap: true, - paired_with_perspective: false, - }, - SpatialTreeItemKey::new(0, 0), + LayoutSize::from_untyped(viewport_size), ); - let scaled_viewport_size = - self.rendering_context.size2d().to_f32().to_untyped() / zoom_factor; - let scaled_viewport_rect = LayoutRect::from_origin_and_size( - LayoutPoint::zero(), - LayoutSize::from_untyped(scaled_viewport_size), - ); - - let root_clip_id = builder.define_clip_rect(zoom_reference_frame, scaled_viewport_rect); + let root_clip_id = builder.define_clip_rect(root_reference_frame, viewport_rect); let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]); - for (_, webview) in self.webviews.painting_order() { - if let Some(pipeline_id) = webview.root_pipeline_id { - let scaled_webview_rect = webview.rect / zoom_factor; - builder.push_iframe( - LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()), - LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()), - &SpaceAndClipInfo { - spatial_id: zoom_reference_frame, - clip_chain_id, - }, - pipeline_id.into(), - true, - ); - } + for (_, webview_renderer) in self.webview_renderers.painting_order() { + let Some(pipeline_id) = webview_renderer.root_pipeline_id else { + continue; + }; + + let device_pixels_per_page_pixel = webview_renderer.device_pixels_per_page_pixel().0; + let webview_reference_frame = builder.push_reference_frame( + LayoutPoint::zero(), + root_reference_frame, + TransformStyle::Flat, + PropertyBinding::Value(Transform3D::scale( + device_pixels_per_page_pixel, + device_pixels_per_page_pixel, + 1., + )), + ReferenceFrameKind::Transform { + is_2d_scale_translation: true, + should_snap: true, + paired_with_perspective: false, + }, + SpatialTreeItemKey::new(0, 0), + ); + + let scaled_webview_rect = webview_renderer.rect / device_pixels_per_page_pixel; + builder.push_iframe( + LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()), + LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()), + &SpaceAndClipInfo { + spatial_id: webview_reference_frame, + clip_chain_id, + }, + pipeline_id.into(), + true, + ); } let built_display_list = builder.end(); @@ -1118,8 +1114,8 @@ impl IOCompositor { /// TODO(mrobinson): Could we only send offsets for the branch being modified /// and not the entire scene? fn update_transaction_with_all_scroll_offsets(&self, transaction: &mut Transaction) { - for webview in self.webviews.iter() { - for details in webview.pipelines.values() { + for webview_renderer in self.webview_renderers.iter() { + for details in webview_renderer.pipelines.values() { for node in details.scroll_tree.nodes.iter() { let (Some(offset), Some(external_id)) = (node.offset(), node.external_id()) else { @@ -1139,33 +1135,38 @@ impl IOCompositor { } } - pub fn add_webview(&mut self, webview: Box) { - let size = self.rendering_context.size2d().to_f32(); - self.webviews.entry(webview.id()).or_insert(WebView::new( - webview, - Box2D::from_origin_and_size(Point2D::origin(), size), - self.global.clone(), - )); + pub fn add_webview( + &mut self, + webview: Box, + viewport_details: ViewportDetails, + ) { + self.webview_renderers + .entry(webview.id()) + .or_insert(WebViewRenderer::new( + self.global.clone(), + webview, + viewport_details, + )); } fn set_frame_tree_for_webview(&mut self, frame_tree: &SendableFrameTree) { debug!("{}: Setting frame tree for webview", frame_tree.pipeline.id); let webview_id = frame_tree.pipeline.webview_id; - let Some(webview) = self.webviews.get_mut(webview_id) else { + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { warn!( "Attempted to set frame tree on unknown WebView (perhaps closed?): {webview_id:?}" ); return; }; - webview.set_frame_tree(frame_tree); + webview_renderer.set_frame_tree(frame_tree); self.send_root_pipeline_display_list(); } fn remove_webview(&mut self, webview_id: WebViewId) { debug!("{}: Removing", webview_id); - if self.webviews.remove(webview_id).is_err() { + if self.webview_renderers.remove(webview_id).is_err() { warn!("{webview_id}: Removing unknown webview"); return; }; @@ -1173,31 +1174,6 @@ impl IOCompositor { self.send_root_pipeline_display_list(); } - pub fn move_resize_webview(&mut self, webview_id: WebViewId, rect: DeviceRect) { - debug!("{webview_id}: Moving and/or resizing webview; rect={rect:?}"); - let rect_changed; - let size_changed; - match self.webviews.get_mut(webview_id) { - Some(webview) => { - rect_changed = rect != webview.rect; - size_changed = rect.size() != webview.rect.size(); - webview.rect = rect; - }, - None => { - warn!("{webview_id}: MoveResizeWebView on unknown webview id"); - return; - }, - }; - - if rect_changed { - if size_changed { - self.send_window_size_message_for_top_level_browser_context(rect, webview_id); - } - - self.send_root_pipeline_display_list(); - } - } - pub fn show_webview( &mut self, webview_id: WebViewId, @@ -1206,15 +1182,15 @@ impl IOCompositor { debug!("{webview_id}: Showing webview; hide_others={hide_others}"); let painting_order_changed = if hide_others { let result = self - .webviews + .webview_renderers .painting_order() .map(|(&id, _)| id) .ne(once(webview_id)); - self.webviews.hide_all(); - self.webviews.show(webview_id)?; + self.webview_renderers.hide_all(); + self.webview_renderers.show(webview_id)?; result } else { - self.webviews.show(webview_id)? + self.webview_renderers.show(webview_id)? }; if painting_order_changed { self.send_root_pipeline_display_list(); @@ -1224,7 +1200,7 @@ impl IOCompositor { pub fn hide_webview(&mut self, webview_id: WebViewId) -> Result<(), UnknownWebView> { debug!("{webview_id}: Hiding webview"); - if self.webviews.hide(webview_id)? { + if self.webview_renderers.hide(webview_id)? { self.send_root_pipeline_display_list(); } Ok(()) @@ -1238,15 +1214,15 @@ impl IOCompositor { debug!("{webview_id}: Raising webview to top; hide_others={hide_others}"); let painting_order_changed = if hide_others { let result = self - .webviews + .webview_renderers .painting_order() .map(|(&id, _)| id) .ne(once(webview_id)); - self.webviews.hide_all(); - self.webviews.raise_to_top(webview_id)?; + self.webview_renderers.hide_all(); + self.webview_renderers.raise_to_top(webview_id)?; result } else { - self.webviews.raise_to_top(webview_id)? + self.webview_renderers.raise_to_top(webview_id)? }; if painting_order_changed { self.send_root_pipeline_display_list(); @@ -1254,37 +1230,46 @@ impl IOCompositor { Ok(()) } - fn send_window_size_message_for_top_level_browser_context( - &self, - rect: DeviceRect, - webview_id: WebViewId, - ) { - // The device pixel ratio used by the style system should include the scale from page pixels - // to device pixels, but not including any pinch zoom. - let hidpi_scale_factor = self.device_pixels_per_page_pixel_not_including_page_zoom(); - let size = rect.size().to_f32() / hidpi_scale_factor; - let msg = EmbedderToConstellationMessage::ChangeViewportDetails( - webview_id, - ViewportDetails { - size, - hidpi_scale_factor, - }, - WindowSizeType::Resize, - ); - if let Err(e) = self.global.borrow().constellation_sender.send(msg) { - warn!("Sending window resize to constellation failed ({:?}).", e); + pub fn move_resize_webview(&mut self, webview_id: WebViewId, rect: DeviceRect) { + if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { + return; } + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { + return; + }; + if !webview_renderer.set_rect(rect) { + return; + } + + self.send_root_pipeline_display_list(); + self.set_needs_repaint(RepaintReason::Resize); } - pub fn resize_rendering_context(&mut self, new_size: PhysicalSize) -> bool { + pub fn set_hidpi_scale_factor( + &mut self, + webview_id: WebViewId, + new_scale_factor: Scale, + ) { if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { - return false; + return; + } + let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else { + return; + }; + if !webview_renderer.set_hidpi_scale_factor(new_scale_factor) { + return; } - let old_hidpi_factor = self.hidpi_factor; - self.hidpi_factor = self.window.hidpi_factor(); - if self.hidpi_factor == old_hidpi_factor && self.rendering_context.size() == new_size { - return false; + self.send_root_pipeline_display_list(); + self.set_needs_repaint(RepaintReason::Resize); + } + + pub fn resize_rendering_context(&mut self, new_size: PhysicalSize) { + if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { + return; + } + if self.rendering_context.size() == new_size { + return; } self.rendering_context.resize(new_size); @@ -1297,78 +1282,29 @@ impl IOCompositor { transaction.set_document_view(output_region); self.global.borrow_mut().send_transaction(transaction); - self.update_after_zoom_or_hidpi_change(); + self.send_root_pipeline_display_list(); self.set_needs_repaint(RepaintReason::Resize); - true } - /// If there are any animations running, dispatches appropriate messages to the constellation. - fn process_animations(&mut self, force: bool) { - // When running animations in order to dump a screenshot (not after a full composite), don't send - // animation ticks faster than about 60Hz. - // - // TODO: This should be based on the refresh rate of the screen and also apply to all - // animation ticks, not just ones sent while waiting to dump screenshots. This requires - // something like a refresh driver concept though. - if !force && (Instant::now() - self.last_animation_tick) < Duration::from_millis(16) { - return; - } - self.last_animation_tick = Instant::now(); - - #[cfg(feature = "webxr")] - let webxr_running = self.global.borrow().webxr_main_thread.running(); - #[cfg(not(feature = "webxr"))] - let webxr_running = false; - - let any_webviews_animating = !self - .webviews - .iter() - .all(|webview| !webview.tick_all_animations(self)); - - let animation_state = if !any_webviews_animating && !webxr_running { - windowing::AnimationState::Idle - } else { - windowing::AnimationState::Animating - }; - - self.window.set_animation_state(animation_state); - } - - pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale { - self.device_pixels_per_page_pixel_not_including_page_zoom() * self.pinch_zoom_level() - } - - fn device_pixels_per_page_pixel_not_including_page_zoom( - &self, - ) -> Scale { - self.page_zoom * self.hidpi_factor - } - - pub fn on_zoom_reset_window_event(&mut self) { + pub fn on_zoom_reset_window_event(&mut self, webview_id: WebViewId) { if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { return; } - self.page_zoom = Scale::new(1.0); - self.update_after_zoom_or_hidpi_change(); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.set_page_zoom(1.0); + } + self.send_root_pipeline_display_list(); } - pub fn on_zoom_window_event(&mut self, magnification: f32) { + pub fn on_zoom_window_event(&mut self, webview_id: WebViewId, magnification: f32) { if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { return; } - self.page_zoom = - Scale::new((self.page_zoom.get() * magnification).clamp(MIN_ZOOM, MAX_ZOOM)); - self.update_after_zoom_or_hidpi_change(); - } - - fn update_after_zoom_or_hidpi_change(&mut self) { - for (webview_id, webview) in self.webviews.painting_order() { - self.send_window_size_message_for_top_level_browser_context(webview.rect, *webview_id); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.set_page_zoom(magnification); } - - // Update the root transform in WebRender to reflect the new zoom. self.send_root_pipeline_display_list(); } @@ -1379,21 +1315,24 @@ impl IOCompositor { .pipeline_to_webview_map .get(&pipeline_id) .cloned()?; - self.webviews.get(webview_id)?.pipelines.get(&pipeline_id) + self.webview_renderers + .get(webview_id)? + .pipelines + .get(&pipeline_id) } // Check if any pipelines currently have active animations or animation callbacks. fn animations_or_animation_callbacks_running(&self) -> bool { - self.webviews + self.webview_renderers .iter() - .any(WebView::animations_or_animation_callbacks_running) + .any(WebViewRenderer::animations_or_animation_callbacks_running) } /// Returns true if any animation callbacks (ie `requestAnimationFrame`) are waiting for a response. fn animation_callbacks_running(&self) -> bool { - self.webviews + self.webview_renderers .iter() - .any(WebView::animation_callbacks_running) + .any(WebViewRenderer::animation_callbacks_running) } /// Query the constellation to see if the current compositor @@ -1409,7 +1348,11 @@ impl IOCompositor { // This gets sent to the constellation for comparison with the current // frame tree. let mut pipeline_epochs = HashMap::new(); - for id in self.webviews.iter().flat_map(WebView::pipeline_ids) { + for id in self + .webview_renderers + .iter() + .flat_map(WebViewRenderer::pipeline_ids) + { if let Some(WebRenderEpoch(epoch)) = self .webrender .as_ref() @@ -1449,6 +1392,11 @@ impl IOCompositor { /// Render the WebRender scene to the active `RenderingContext`. If successful, trigger /// the next round of animations. pub fn render(&mut self) -> bool { + self.global + .borrow() + .refresh_driver + .notify_will_paint(self.webview_renderers.iter()); + if let Err(error) = self.render_inner() { warn!("Unable to render: {error:?}"); return false; @@ -1458,9 +1406,6 @@ impl IOCompositor { // the scene no longer needs to be repainted. self.needs_repaint.set(RepaintReason::empty()); - // Queue up any subsequent paints for animations. - self.process_animations(true); - true } @@ -1468,13 +1413,19 @@ impl IOCompositor { /// [`IOCompositor`]. If succesful return the output image in shared memory. fn render_to_shared_memory( &mut self, + webview_id: WebViewId, page_rect: Option>, - ) -> Result, UnableToComposite> { + ) -> Result, UnableToComposite> { self.render_inner()?; let size = self.rendering_context.size2d().to_i32(); let rect = if let Some(rect) = page_rect { - let rect = self.device_pixels_per_page_pixel().transform_rect(&rect); + let scale = self + .webview_renderers + .get(webview_id) + .map(WebViewRenderer::device_pixels_per_page_pixel) + .unwrap_or_else(|| Scale::new(1.0)); + let rect = scale.transform_rect(&rect); let x = rect.origin.x as i32; // We need to convert to the bottom-left origin coordinate @@ -1491,16 +1442,19 @@ impl IOCompositor { Ok(self .rendering_context .read_to_image(rect) - .map(|image| Image { - width: image.width(), - height: image.height(), + .map(|image| RasterImage { + metadata: ImageMetadata { + width: image.width(), + height: image.height(), + }, format: PixelFormat::RGBA8, frames: vec![ImageFrame { delay: None, - bytes: ipc::IpcSharedMemory::from_bytes(&image), + byte_range: 0..image.len(), width: image.width(), height: image.height(), }], + bytes: ipc::IpcSharedMemory::from_bytes(&image), id: None, cors_status: CorsStatus::Safe, })) @@ -1522,10 +1476,8 @@ impl IOCompositor { if opts::get().wait_for_stable_image { // The current image may be ready to output. However, if there are animations active, - // tick those instead and continue waiting for the image output to be stable AND - // all active animations to complete. + // continue waiting for the image output to be stable AND all active animations to complete. if self.animations_or_animation_callbacks_running() { - self.process_animations(false); return Err(UnableToComposite::NotReadyToPaintImage( NotReadyToPaint::AnimationsActive, )); @@ -1569,8 +1521,8 @@ impl IOCompositor { fn send_pending_paint_metrics_messages_after_composite(&mut self) { let paint_time = CrossProcessInstant::now(); let document_id = self.webrender_document(); - for webview_details in self.webviews.iter_mut() { - for (pipeline_id, pipeline) in webview_details.pipelines.iter_mut() { + for webview_renderer in self.webview_renderers.iter_mut() { + for (pipeline_id, pipeline) in webview_renderer.pipelines.iter_mut() { let Some(current_epoch) = self .webrender .as_ref() @@ -1668,12 +1620,7 @@ impl IOCompositor { // Check for new messages coming from the other threads in the system. let mut compositor_messages = vec![]; let mut found_recomposite_msg = false; - while let Some(msg) = self - .global - .borrow_mut() - .compositor_receiver - .try_recv_compositor_msg() - { + while let Ok(msg) = self.global.borrow_mut().compositor_receiver.try_recv() { match msg { CompositorMsg::NewWebRenderFrameReady(..) if found_recomposite_msg => { // Only take one of duplicate NewWebRendeFrameReady messages, but do subtract @@ -1705,15 +1652,6 @@ impl IOCompositor { return false; } - let now = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap_or_default() - .as_secs() as f64; - // If a pinch-zoom happened recently, ask for tiles at the new resolution - if self.zoom_action && now - self.zoom_time > 0.3 { - self.zoom_action = false; - } - #[cfg(feature = "webxr")] // Run the WebXR main thread self.global.borrow_mut().webxr_main_thread.run_one_frame(); @@ -1722,30 +1660,42 @@ impl IOCompositor { if let Err(err) = self.rendering_context.make_current() { warn!("Failed to make the rendering context current: {:?}", err); } - let mut webviews = take(&mut self.webviews); - for webview in webviews.iter_mut() { - webview.process_pending_scroll_events(self); + + let mut need_zoom = false; + let scroll_offset_updates: Vec<_> = self + .webview_renderers + .iter_mut() + .filter_map(|webview_renderer| { + let (zoom, scroll_result) = + webview_renderer.process_pending_scroll_and_pinch_zoom_events(); + need_zoom = need_zoom || (zoom == PinchZoomResult::DidPinchZoom); + scroll_result + }) + .collect(); + + if need_zoom || !scroll_offset_updates.is_empty() { + let mut transaction = Transaction::new(); + if need_zoom { + self.send_root_pipeline_display_list_in_transaction(&mut transaction); + } + for update in scroll_offset_updates { + let offset = LayoutVector2D::new(-update.offset.x, -update.offset.y); + transaction.set_scroll_offsets( + update.external_scroll_id, + vec![SampledScrollOffset { + offset, + generation: 0, + }], + ); + } + + self.generate_frame(&mut transaction, RenderReasons::APZ); + self.global.borrow_mut().send_transaction(transaction); } - self.webviews = webviews; + self.global.borrow().shutdown_state() != ShutdownState::FinishedShuttingDown } - pub fn pinch_zoom_level(&self) -> Scale { - Scale::new(self.viewport_zoom.get()) - } - - pub(crate) fn set_pinch_zoom_level(&mut self, mut zoom: f32) -> bool { - if let Some(min) = self.min_viewport_zoom { - zoom = f32::max(min.get(), zoom); - } - if let Some(max) = self.max_viewport_zoom { - zoom = f32::min(max.get(), zoom); - } - - let old_zoom = std::mem::replace(&mut self.viewport_zoom, PinchZoomFactor::new(zoom)); - old_zoom != self.viewport_zoom - } - pub fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) { let Some(webrender) = self.webrender.as_mut() else { return; @@ -1827,8 +1777,8 @@ impl IOCompositor { } pub fn notify_input_event(&mut self, webview_id: WebViewId, event: InputEvent) { - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.notify_input_event(event); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.notify_input_event(event); } } @@ -1839,20 +1789,20 @@ impl IOCompositor { cursor: DeviceIntPoint, event_type: TouchEventType, ) { - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.notify_scroll_event(scroll_location, cursor, event_type); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.notify_scroll_event(scroll_location, cursor, event_type); } } pub fn on_vsync(&mut self, webview_id: WebViewId) { - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.on_vsync(); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.on_vsync(); } } pub fn set_pinch_zoom(&mut self, webview_id: WebViewId, magnification: f32) { - if let Some(webview) = self.webviews.get_mut(webview_id) { - webview.set_pinch_zoom(magnification); + if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) { + webview_renderer.set_pinch_zoom(magnification); } } diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index be91f584cb0..4faeadf5ba6 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -7,32 +7,32 @@ use std::cell::Cell; use std::rc::Rc; -use compositing_traits::{CompositorProxy, CompositorReceiver}; +use compositing_traits::rendering_context::RenderingContext; +use compositing_traits::{CompositorMsg, CompositorProxy}; use constellation_traits::EmbedderToConstellationMessage; -use crossbeam_channel::Sender; -use embedder_traits::ShutdownState; +use crossbeam_channel::{Receiver, Sender}; +use embedder_traits::{EventLoopWaker, ShutdownState}; use profile_traits::{mem, time}; use webrender::RenderApi; use webrender_api::DocumentId; -use webrender_traits::rendering_context::RenderingContext; -pub use crate::compositor::IOCompositor; +pub use crate::compositor::{IOCompositor, WebRenderDebugOption}; #[macro_use] mod tracing; mod compositor; +mod refresh_driver; mod touch; -pub mod webview; -pub mod webview_manager; -pub mod windowing; +mod webview_manager; +mod webview_renderer; /// Data used to construct a compositor. pub struct InitialCompositorState { /// A channel to the compositor. pub sender: CompositorProxy, /// A port on which messages inbound to the compositor can be received. - pub receiver: CompositorReceiver, + pub receiver: Receiver, /// A channel to the constellation. pub constellation_chan: Sender, /// A channel to the time profiler thread. @@ -50,4 +50,7 @@ pub struct InitialCompositorState { pub webrender_gl: Rc, #[cfg(feature = "webxr")] pub webxr_main_thread: webxr::MainThreadRegistry, + /// An [`EventLoopWaker`] used in order to wake up the embedder when it is + /// time to paint. + pub event_loop_waker: Box, } diff --git a/components/compositing/refresh_driver.rs b/components/compositing/refresh_driver.rs new file mode 100644 index 00000000000..5531a7257c8 --- /dev/null +++ b/components/compositing/refresh_driver.rs @@ -0,0 +1,234 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::cell::Cell; +use std::collections::hash_map::Values; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread::{self, JoinHandle}; +use std::time::Duration; + +use base::id::WebViewId; +use constellation_traits::EmbedderToConstellationMessage; +use crossbeam_channel::{Sender, select}; +use embedder_traits::EventLoopWaker; +use log::warn; +use timers::{BoxedTimerCallback, TimerEventId, TimerEventRequest, TimerScheduler, TimerSource}; + +use crate::compositor::RepaintReason; +use crate::webview_renderer::WebViewRenderer; + +const FRAME_DURATION: Duration = Duration::from_millis(1000 / 120); + +/// The [`RefreshDriver`] is responsible for controlling updates to aall `WebView`s +/// onscreen presentation. Currently, it only manages controlling animation update +/// requests. +/// +/// The implementation is very basic at the moment, only requesting new animation +/// frames at a constant time after a repaint. +pub(crate) struct RefreshDriver { + /// The channel on which messages can be sent to the Constellation. + pub(crate) constellation_sender: Sender, + + /// Whether or not we are currently animating via a timer. + pub(crate) animating: Cell, + + /// Whether or not we are waiting for our frame timeout to trigger + pub(crate) waiting_for_frame_timeout: Arc, + + /// A [`TimerThread`] which is used to schedule frame timeouts in the future. + timer_thread: TimerThread, + + /// An [`EventLoopWaker`] to be used to wake up the embedder when it is + /// time to paint a frame. + event_loop_waker: Box, +} + +impl RefreshDriver { + pub(crate) fn new( + constellation_sender: Sender, + event_loop_waker: Box, + ) -> Self { + Self { + constellation_sender, + animating: Default::default(), + waiting_for_frame_timeout: Default::default(), + timer_thread: Default::default(), + event_loop_waker, + } + } + + fn timer_callback(&self) -> BoxedTimerCallback { + let waiting_for_frame_timeout = self.waiting_for_frame_timeout.clone(); + let event_loop_waker = self.event_loop_waker.clone_box(); + Box::new(move |_| { + waiting_for_frame_timeout.store(false, Ordering::Relaxed); + event_loop_waker.wake(); + }) + } + + /// Notify the [`RefreshDriver`] that a paint is about to happen. This will trigger + /// new animation frames for all active `WebView`s and schedule a new frame deadline. + pub(crate) fn notify_will_paint( + &self, + webview_renderers: Values<'_, WebViewId, WebViewRenderer>, + ) { + // If we are still waiting for the frame to timeout this paint was caused for some + // non-animation related reason and we should wait until the frame timeout to trigger + // the next one. + if self.waiting_for_frame_timeout.load(Ordering::Relaxed) { + return; + } + + // If any WebViews are animating ask them to paint again for another animation tick. + let animating_webviews: Vec<_> = webview_renderers + .filter_map(|webview_renderer| { + if webview_renderer.animating() { + Some(webview_renderer.id) + } else { + None + } + }) + .collect(); + + // If nothing is animating any longer, update our state and exit early without requesting + // any noew frames nor triggering a new animation deadline. + if animating_webviews.is_empty() { + self.animating.set(false); + return; + } + + if let Err(error) = + self.constellation_sender + .send(EmbedderToConstellationMessage::TickAnimation( + animating_webviews, + )) + { + warn!("Sending tick to constellation failed ({error:?})."); + } + + // Queue the next frame deadline. + self.animating.set(true); + self.waiting_for_frame_timeout + .store(true, Ordering::Relaxed); + self.timer_thread + .queue_timer(FRAME_DURATION, self.timer_callback()); + } + + /// Notify the [`RefreshDriver`] that the animation state of a particular `WebView` + /// via its associated [`WebViewRenderer`] has changed. In the case that a `WebView` + /// has started animating, the [`RefreshDriver`] will request a new frame from it + /// immediately, but only render that frame at the next frame deadline. + pub(crate) fn notify_animation_state_changed(&self, webview_renderer: &WebViewRenderer) { + if !webview_renderer.animating() { + // If no other WebView is animating we will officially stop animated once the + // next frame has been painted. + return; + } + + if let Err(error) = + self.constellation_sender + .send(EmbedderToConstellationMessage::TickAnimation(vec![ + webview_renderer.id, + ])) + { + warn!("Sending tick to constellation failed ({error:?})."); + } + + if self.animating.get() { + return; + } + + self.animating.set(true); + self.waiting_for_frame_timeout + .store(true, Ordering::Relaxed); + self.timer_thread + .queue_timer(FRAME_DURATION, self.timer_callback()); + } + + /// Whether or not the renderer should trigger a message to the embedder to request a + /// repaint. This might be false if: we are animating and the repaint reason is just + /// for a new frame. In that case, the renderer should wait until the frame timeout to + /// ask the embedder to repaint. + pub(crate) fn wait_to_paint(&self, repaint_reason: RepaintReason) -> bool { + if !self.animating.get() || repaint_reason != RepaintReason::NewWebRenderFrame { + return false; + } + + self.waiting_for_frame_timeout.load(Ordering::Relaxed) + } +} + +enum TimerThreadMessage { + Request(TimerEventRequest), + Quit, +} + +/// A thread that manages a [`TimerScheduler`] running in the background of the +/// [`RefreshDriver`]. This is necessary because we need a reliable way of waking up the +/// embedder's main thread, which may just be sleeping until the `EventLoopWaker` asks it +/// to wake up. +/// +/// It would be nice to integrate this somehow into the embedder thread, but it would +/// require both some communication with the embedder and for all embedders to be well +/// behave respecting wakeup timeouts -- a bit too much to ask at the moment. +struct TimerThread { + sender: Sender, + join_handle: Option>, +} + +impl Drop for TimerThread { + fn drop(&mut self) { + let _ = self.sender.send(TimerThreadMessage::Quit); + if let Some(join_handle) = self.join_handle.take() { + let _ = join_handle.join(); + } + } +} + +impl Default for TimerThread { + fn default() -> Self { + let (sender, receiver) = crossbeam_channel::unbounded::(); + let join_handle = thread::Builder::new() + .name(String::from("CompositorTimerThread")) + .spawn(move || { + let mut scheduler = TimerScheduler::default(); + + loop { + select! { + recv(receiver) -> message => { + match message { + Ok(TimerThreadMessage::Request(request)) => { + scheduler.schedule_timer(request); + }, + _ => return, + } + }, + recv(scheduler.wait_channel()) -> _message => { + scheduler.dispatch_completed_timers(); + }, + }; + } + }) + .expect("Could not create RefreshDriver timer thread."); + + Self { + sender, + join_handle: Some(join_handle), + } + } +} + +impl TimerThread { + fn queue_timer(&self, duration: Duration, callback: BoxedTimerCallback) { + let _ = self + .sender + .send(TimerThreadMessage::Request(TimerEventRequest { + callback, + source: TimerSource::FromWorker, + id: TimerEventId(0), + duration, + })); + } +} diff --git a/components/compositing/touch.rs b/components/compositing/touch.rs index 19df220a469..76d87732b32 100644 --- a/components/compositing/touch.rs +++ b/components/compositing/touch.rs @@ -282,6 +282,10 @@ impl TouchHandler { debug_assert!(old.is_some(), "Sequence already removed?"); } + pub fn try_get_current_touch_sequence(&self) -> Option<&TouchSequenceInfo> { + self.touch_sequence_map.get(&self.current_sequence_id) + } + pub fn get_current_touch_sequence_mut(&mut self) -> &mut TouchSequenceInfo { self.touch_sequence_map .get_mut(&self.current_sequence_id) diff --git a/components/compositing/tracing.rs b/components/compositing/tracing.rs index 907c931aebe..a8bb8b42bb8 100644 --- a/components/compositing/tracing.rs +++ b/components/compositing/tracing.rs @@ -42,7 +42,23 @@ mod from_constellation { Self::LoadComplete(..) => target!("LoadComplete"), Self::WebDriverMouseButtonEvent(..) => target!("WebDriverMouseButtonEvent"), Self::WebDriverMouseMoveEvent(..) => target!("WebDriverMouseMoveEvent"), - Self::CrossProcess(_) => target!("CrossProcess"), + Self::WebDriverWheelScrollEvent(..) => target!("WebDriverWheelScrollEvent"), + Self::SendInitialTransaction(..) => target!("SendInitialTransaction"), + Self::SendScrollNode(..) => target!("SendScrollNode"), + Self::SendDisplayList { .. } => target!("SendDisplayList"), + Self::HitTest(..) => target!("HitTest"), + Self::GenerateImageKey(..) => target!("GenerateImageKey"), + Self::AddImage(..) => target!("AddImage"), + Self::UpdateImages(..) => target!("UpdateImages"), + Self::GenerateFontKeys(..) => target!("GenerateFontKeys"), + Self::AddFont(..) => target!("AddFont"), + Self::AddSystemFont(..) => target!("AddSystemFont"), + Self::AddFontInstance(..) => target!("AddFontInstance"), + Self::RemoveFonts(..) => target!("RemoveFonts"), + Self::GetClientWindowRect(..) => target!("GetClientWindowRect"), + Self::GetScreenSize(..) => target!("GetScreenSize"), + Self::GetAvailableScreenSize(..) => target!("GetAvailableScreenSize"), + Self::CollectMemoryReport(..) => target!("CollectMemoryReport"), } } } diff --git a/components/compositing/webview.rs b/components/compositing/webview.rs deleted file mode 100644 index e0b54de5139..00000000000 --- a/components/compositing/webview.rs +++ /dev/null @@ -1,831 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::cell::RefCell; -use std::collections::HashMap; -use std::collections::hash_map::Keys; -use std::rc::Rc; - -use base::id::{PipelineId, WebViewId}; -use compositing_traits::SendableFrameTree; -use constellation_traits::{CompositorHitTestResult, EmbedderToConstellationMessage, ScrollState}; -use embedder_traits::{ - InputEvent, MouseButton, MouseButtonAction, MouseButtonEvent, MouseMoveEvent, ShutdownState, - TouchEvent, TouchEventType, TouchId, -}; -use euclid::{Point2D, Scale, Vector2D}; -use fnv::FnvHashSet; -use log::{debug, warn}; -use script_traits::{AnimationState, TouchEventResult}; -use webrender::Transaction; -use webrender_api::units::{DeviceIntPoint, DevicePoint, DeviceRect, LayoutVector2D}; -use webrender_api::{ - ExternalScrollId, HitTestFlags, RenderReasons, SampledScrollOffset, ScrollLocation, -}; -use webrender_traits::RendererWebView; - -use crate::IOCompositor; -use crate::compositor::{PipelineDetails, ServoRenderer}; -use crate::touch::{TouchHandler, TouchMoveAction, TouchMoveAllowed, TouchSequenceState}; - -#[derive(Clone, Copy)] -struct ScrollEvent { - /// Scroll by this offset, or to Start or End - scroll_location: ScrollLocation, - /// Apply changes to the frame at this location - cursor: DeviceIntPoint, - /// The number of OS events that have been coalesced together into this one event. - event_count: u32, -} - -#[derive(Clone, Copy)] -enum ScrollZoomEvent { - /// An pinch zoom event that magnifies the view by the given factor. - PinchZoom(f32), - /// A scroll event that scrolls the scroll node at the given location by the - /// given amount. - Scroll(ScrollEvent), -} - -pub(crate) struct WebView { - /// The [`WebViewId`] of the `WebView` associated with this [`WebViewDetails`]. - pub id: WebViewId, - /// The renderer's view of the embedding layer `WebView` as a trait implementation, - /// so that the renderer doesn't need to depend on the embedding layer. This avoids - /// a dependency cycle. - pub renderer_webview: Box, - /// The root [`PipelineId`] of the currently displayed page in this WebView. - pub root_pipeline_id: Option, - pub rect: DeviceRect, - /// Tracks details about each active pipeline that the compositor knows about. - pub pipelines: HashMap, - /// Data that is shared by all WebView renderers. - pub(crate) global: Rc>, - /// Pending scroll/zoom events. - pending_scroll_zoom_events: Vec, - /// Touch input state machine - touch_handler: TouchHandler, -} - -impl Drop for WebView { - fn drop(&mut self) { - self.global - .borrow_mut() - .pipeline_to_webview_map - .retain(|_, webview_id| self.id != *webview_id); - } -} - -impl WebView { - pub(crate) fn new( - renderer_webview: Box, - rect: DeviceRect, - global: Rc>, - ) -> Self { - Self { - id: renderer_webview.id(), - renderer_webview, - root_pipeline_id: None, - rect, - pipelines: Default::default(), - touch_handler: TouchHandler::new(), - global, - pending_scroll_zoom_events: Default::default(), - } - } - - pub(crate) fn animations_or_animation_callbacks_running(&self) -> bool { - self.pipelines - .values() - .any(PipelineDetails::animations_or_animation_callbacks_running) - } - - pub(crate) fn animation_callbacks_running(&self) -> bool { - self.pipelines - .values() - .any(PipelineDetails::animation_callbacks_running) - } - - pub(crate) fn pipeline_ids(&self) -> Keys<'_, PipelineId, PipelineDetails> { - self.pipelines.keys() - } - - /// Returns the [`PipelineDetails`] for the given [`PipelineId`], creating it if needed. - pub(crate) fn ensure_pipeline_details( - &mut self, - pipeline_id: PipelineId, - ) -> &mut PipelineDetails { - self.pipelines.entry(pipeline_id).or_insert_with(|| { - self.global - .borrow_mut() - .pipeline_to_webview_map - .insert(pipeline_id, self.id); - PipelineDetails::new(pipeline_id) - }) - } - - pub(crate) fn set_throttled(&mut self, pipeline_id: PipelineId, throttled: bool) { - self.ensure_pipeline_details(pipeline_id).throttled = throttled; - } - - pub(crate) fn remove_pipeline(&mut self, pipeline_id: PipelineId) { - self.global - .borrow_mut() - .pipeline_to_webview_map - .remove(&pipeline_id); - self.pipelines.remove(&pipeline_id); - } - - pub(crate) fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) { - let pipeline_id = frame_tree.pipeline.id; - let old_pipeline_id = std::mem::replace(&mut self.root_pipeline_id, Some(pipeline_id)); - - if old_pipeline_id != self.root_pipeline_id { - debug!( - "Updating webview ({:?}) from pipeline {:?} to {:?}", - 3, old_pipeline_id, self.root_pipeline_id - ); - } - - self.set_frame_tree_on_pipeline_details(frame_tree, None); - self.reset_scroll_tree_for_unattached_pipelines(frame_tree); - self.send_scroll_positions_to_layout_for_pipeline(pipeline_id); - } - - pub(crate) fn send_scroll_positions_to_layout_for_pipeline(&self, pipeline_id: PipelineId) { - let Some(details) = self.pipelines.get(&pipeline_id) else { - return; - }; - - let mut scroll_states = Vec::new(); - details.scroll_tree.nodes.iter().for_each(|node| { - if let (Some(scroll_id), Some(scroll_offset)) = (node.external_id(), node.offset()) { - scroll_states.push(ScrollState { - scroll_id, - scroll_offset, - }); - } - }); - - let _ = self.global.borrow().constellation_sender.send( - EmbedderToConstellationMessage::SetScrollStates(pipeline_id, scroll_states), - ); - } - - pub(crate) fn set_frame_tree_on_pipeline_details( - &mut self, - frame_tree: &SendableFrameTree, - parent_pipeline_id: Option, - ) { - let pipeline_id = frame_tree.pipeline.id; - let pipeline_details = self.ensure_pipeline_details(pipeline_id); - pipeline_details.pipeline = Some(frame_tree.pipeline.clone()); - pipeline_details.parent_pipeline_id = parent_pipeline_id; - - for kid in &frame_tree.children { - self.set_frame_tree_on_pipeline_details(kid, Some(pipeline_id)); - } - } - - pub(crate) fn reset_scroll_tree_for_unattached_pipelines( - &mut self, - frame_tree: &SendableFrameTree, - ) { - // TODO(mrobinson): Eventually this can selectively preserve the scroll trees - // state for some unattached pipelines in order to preserve scroll position when - // navigating backward and forward. - fn collect_pipelines( - pipelines: &mut FnvHashSet, - frame_tree: &SendableFrameTree, - ) { - pipelines.insert(frame_tree.pipeline.id); - for kid in &frame_tree.children { - collect_pipelines(pipelines, kid); - } - } - - let mut attached_pipelines: FnvHashSet = FnvHashSet::default(); - collect_pipelines(&mut attached_pipelines, frame_tree); - - self.pipelines - .iter_mut() - .filter(|(id, _)| !attached_pipelines.contains(id)) - .for_each(|(_, details)| { - details.scroll_tree.nodes.iter_mut().for_each(|node| { - node.set_offset(LayoutVector2D::zero()); - }) - }) - } - - /// Sets or unsets the animations-running flag for the given pipeline, and schedules a - /// recomposite if necessary. Returns true if the pipeline is throttled. - pub(crate) fn change_running_animations_state( - &mut self, - pipeline_id: PipelineId, - animation_state: AnimationState, - ) -> bool { - let pipeline_details = self.ensure_pipeline_details(pipeline_id); - match animation_state { - AnimationState::AnimationsPresent => { - pipeline_details.animations_running = true; - }, - AnimationState::AnimationCallbacksPresent => { - pipeline_details.animation_callbacks_running = true; - }, - AnimationState::NoAnimationsPresent => { - pipeline_details.animations_running = false; - }, - AnimationState::NoAnimationCallbacksPresent => { - pipeline_details.animation_callbacks_running = false; - }, - } - pipeline_details.throttled - } - - pub(crate) fn tick_all_animations(&self, compositor: &IOCompositor) -> bool { - let mut ticked_any = false; - for pipeline_details in self.pipelines.values() { - ticked_any = pipeline_details.tick_animations(compositor) || ticked_any; - } - ticked_any - } - - pub(crate) fn tick_animations_for_pipeline( - &self, - pipeline_id: PipelineId, - compositor: &IOCompositor, - ) { - if let Some(pipeline_details) = self.pipelines.get(&pipeline_id) { - pipeline_details.tick_animations(compositor); - } - } - - /// On a Window refresh tick (e.g. vsync) - pub fn on_vsync(&mut self) { - if let Some(fling_action) = self.touch_handler.on_vsync() { - self.on_scroll_window_event( - ScrollLocation::Delta(fling_action.delta), - fling_action.cursor, - ); - } - } - - pub(crate) fn dispatch_input_event(&mut self, event: InputEvent) { - // Events that do not need to do hit testing are sent directly to the - // constellation to filter down. - let Some(point) = event.point() else { - return; - }; - - // If we can't find a pipeline to send this event to, we cannot continue. - let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id); - let Some(result) = self - .global - .borrow() - .hit_test_at_point(point, get_pipeline_details) - else { - return; - }; - - self.global.borrow_mut().update_cursor(point, &result); - - if let Err(error) = self.global.borrow().constellation_sender.send( - EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, Some(result)), - ) { - warn!("Sending event to constellation failed ({error:?})."); - } - } - - pub fn notify_input_event(&mut self, event: InputEvent) { - if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { - return; - } - - if let InputEvent::Touch(event) = event { - self.on_touch_event(event); - return; - } - - if self.global.borrow().convert_mouse_to_touch { - match event { - InputEvent::MouseButton(event) => { - match event.action { - MouseButtonAction::Click => {}, - MouseButtonAction::Down => self.on_touch_down(TouchEvent::new( - TouchEventType::Down, - TouchId(0), - event.point, - )), - MouseButtonAction::Up => self.on_touch_up(TouchEvent::new( - TouchEventType::Up, - TouchId(0), - event.point, - )), - } - return; - }, - InputEvent::MouseMove(event) => { - self.on_touch_move(TouchEvent::new( - TouchEventType::Move, - TouchId(0), - event.point, - )); - return; - }, - _ => {}, - } - } - - self.dispatch_input_event(event); - } - - fn send_touch_event(&self, mut event: TouchEvent) -> bool { - let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id); - let Some(result) = self - .global - .borrow() - .hit_test_at_point(event.point, get_pipeline_details) - else { - return false; - }; - - event.init_sequence_id(self.touch_handler.current_sequence_id); - let event = InputEvent::Touch(event); - if let Err(e) = self.global.borrow().constellation_sender.send( - EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, Some(result)), - ) { - warn!("Sending event to constellation failed ({:?}).", e); - false - } else { - true - } - } - - pub fn on_touch_event(&mut self, event: TouchEvent) { - if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { - return; - } - - match event.event_type { - TouchEventType::Down => self.on_touch_down(event), - TouchEventType::Move => self.on_touch_move(event), - TouchEventType::Up => self.on_touch_up(event), - TouchEventType::Cancel => self.on_touch_cancel(event), - } - } - - fn on_touch_down(&mut self, event: TouchEvent) { - self.touch_handler.on_touch_down(event.id, event.point); - self.send_touch_event(event); - } - - fn on_touch_move(&mut self, mut event: TouchEvent) { - let action: TouchMoveAction = self.touch_handler.on_touch_move(event.id, event.point); - if TouchMoveAction::NoAction != action { - // if first move processed and allowed, we directly process the move event, - // without waiting for the script handler. - if self - .touch_handler - .move_allowed(self.touch_handler.current_sequence_id) - { - // https://w3c.github.io/touch-events/#cancelability - event.disable_cancelable(); - match action { - TouchMoveAction::Scroll(delta, point) => self.on_scroll_window_event( - ScrollLocation::Delta(LayoutVector2D::from_untyped(delta.to_untyped())), - point.cast(), - ), - TouchMoveAction::Zoom(magnification, scroll_delta) => { - let cursor = Point2D::new(-1, -1); // Make sure this hits the base layer. - - // The order of these events doesn't matter, because zoom is handled by - // a root display list and the scroll event here is handled by the scroll - // applied to the content display list. - self.pending_scroll_zoom_events - .push(ScrollZoomEvent::PinchZoom(magnification)); - self.pending_scroll_zoom_events - .push(ScrollZoomEvent::Scroll(ScrollEvent { - scroll_location: ScrollLocation::Delta( - LayoutVector2D::from_untyped(scroll_delta.to_untyped()), - ), - cursor, - event_count: 1, - })); - }, - _ => {}, - } - } - // When the event is touchmove, if the script thread is processing the touch - // move event, we skip sending the event to the script thread. - // This prevents the script thread from stacking up for a large amount of time. - if !self - .touch_handler - .is_handling_touch_move(self.touch_handler.current_sequence_id) && - self.send_touch_event(event) && - event.is_cancelable() - { - self.touch_handler - .set_handling_touch_move(self.touch_handler.current_sequence_id, true); - } - } - } - - fn on_touch_up(&mut self, event: TouchEvent) { - self.touch_handler.on_touch_up(event.id, event.point); - self.send_touch_event(event); - } - - fn on_touch_cancel(&mut self, event: TouchEvent) { - // Send the event to script. - self.touch_handler.on_touch_cancel(event.id, event.point); - self.send_touch_event(event); - } - - pub(crate) fn on_touch_event_processed(&mut self, result: TouchEventResult) { - match result { - TouchEventResult::DefaultPrevented(sequence_id, event_type) => { - debug!( - "Touch event {:?} in sequence {:?} prevented!", - event_type, sequence_id - ); - match event_type { - TouchEventType::Down => { - // prevents both click and move - self.touch_handler.prevent_click(sequence_id); - self.touch_handler.prevent_move(sequence_id); - self.touch_handler - .remove_pending_touch_move_action(sequence_id); - }, - TouchEventType::Move => { - // script thread processed the touch move event, mark this false. - if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) { - info.prevent_move = TouchMoveAllowed::Prevented; - if let TouchSequenceState::PendingFling { .. } = info.state { - info.state = TouchSequenceState::Finished; - } - self.touch_handler.set_handling_touch_move( - self.touch_handler.current_sequence_id, - false, - ); - self.touch_handler - .remove_pending_touch_move_action(sequence_id); - } - }, - TouchEventType::Up => { - // Note: We don't have to consider PendingFling here, since we handle that - // in the DefaultAllowed case of the touch_move event. - // Note: Removing can and should fail, if we still have an active Fling, - let Some(info) = - &mut self.touch_handler.get_touch_sequence_mut(sequence_id) - else { - // The sequence ID could already be removed, e.g. if Fling finished, - // before the touch_up event was handled (since fling can start - // immediately if move was previously allowed, and clicks are anyway not - // happening from fling). - return; - }; - match info.state { - TouchSequenceState::PendingClick(_) => { - info.state = TouchSequenceState::Finished; - self.touch_handler.remove_touch_sequence(sequence_id); - }, - TouchSequenceState::Flinging { .. } => { - // We can't remove the touch sequence yet - }, - TouchSequenceState::Finished => { - self.touch_handler.remove_touch_sequence(sequence_id); - }, - TouchSequenceState::Touching | - TouchSequenceState::Panning { .. } | - TouchSequenceState::Pinching | - TouchSequenceState::MultiTouch | - TouchSequenceState::PendingFling { .. } => { - // It's possible to transition from Pinch to pan, Which means that - // a touch_up event for a pinch might have arrived here, but we - // already transitioned to pan or even PendingFling. - // We don't need to do anything in these cases though. - }, - } - }, - TouchEventType::Cancel => { - // We could still have pending event handlers, so we remove the pending - // actions, and try to remove the touch sequence. - self.touch_handler - .remove_pending_touch_move_action(sequence_id); - self.touch_handler.try_remove_touch_sequence(sequence_id); - }, - } - }, - TouchEventResult::DefaultAllowed(sequence_id, event_type) => { - debug!( - "Touch event {:?} in sequence {:?} allowed", - event_type, sequence_id - ); - match event_type { - TouchEventType::Down => {}, - TouchEventType::Move => { - if let Some(action) = - self.touch_handler.pending_touch_move_action(sequence_id) - { - match action { - TouchMoveAction::Scroll(delta, point) => self - .on_scroll_window_event( - ScrollLocation::Delta(LayoutVector2D::from_untyped( - delta.to_untyped(), - )), - point.cast(), - ), - TouchMoveAction::Zoom(magnification, scroll_delta) => { - let cursor = Point2D::new(-1, -1); - // Make sure this hits the base layer. - // The order of these events doesn't matter, because zoom is handled by - // a root display list and the scroll event here is handled by the scroll - // applied to the content display list. - self.pending_scroll_zoom_events - .push(ScrollZoomEvent::PinchZoom(magnification)); - self.pending_scroll_zoom_events - .push(ScrollZoomEvent::Scroll(ScrollEvent { - scroll_location: ScrollLocation::Delta( - LayoutVector2D::from_untyped( - scroll_delta.to_untyped(), - ), - ), - cursor, - event_count: 1, - })); - }, - TouchMoveAction::NoAction => { - // This shouldn't happen, but we can also just ignore it. - }, - } - self.touch_handler - .remove_pending_touch_move_action(sequence_id); - } - self.touch_handler - .set_handling_touch_move(self.touch_handler.current_sequence_id, false); - if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) { - if info.prevent_move == TouchMoveAllowed::Pending { - info.prevent_move = TouchMoveAllowed::Allowed; - if let TouchSequenceState::PendingFling { velocity, cursor } = - info.state - { - info.state = TouchSequenceState::Flinging { velocity, cursor } - } - } - } - }, - TouchEventType::Up => { - let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) - else { - // The sequence was already removed because there is no default action. - return; - }; - match info.state { - TouchSequenceState::PendingClick(point) => { - info.state = TouchSequenceState::Finished; - // PreventDefault from touch_down may have been processed after - // touch_up already occurred. - if !info.prevent_click { - self.simulate_mouse_click(point); - } - self.touch_handler.remove_touch_sequence(sequence_id); - }, - TouchSequenceState::Flinging { .. } => { - // We can't remove the touch sequence yet - }, - TouchSequenceState::Finished => { - self.touch_handler.remove_touch_sequence(sequence_id); - }, - TouchSequenceState::Panning { .. } | - TouchSequenceState::Pinching | - TouchSequenceState::PendingFling { .. } => { - // It's possible to transition from Pinch to pan, Which means that - // a touch_up event for a pinch might have arrived here, but we - // already transitioned to pan or even PendingFling. - // We don't need to do anything in these cases though. - }, - TouchSequenceState::MultiTouch | TouchSequenceState::Touching => { - // We transitioned to touching from multi-touch or pinching. - }, - } - }, - TouchEventType::Cancel => { - self.touch_handler - .remove_pending_touch_move_action(sequence_id); - self.touch_handler.try_remove_touch_sequence(sequence_id); - }, - } - }, - } - } - - /// - fn simulate_mouse_click(&mut self, point: DevicePoint) { - let button = MouseButton::Left; - self.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent { point })); - self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent { - button, - action: MouseButtonAction::Down, - point, - })); - self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent { - button, - action: MouseButtonAction::Up, - point, - })); - self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent { - button, - action: MouseButtonAction::Click, - point, - })); - } - - pub fn notify_scroll_event( - &mut self, - scroll_location: ScrollLocation, - cursor: DeviceIntPoint, - event_type: TouchEventType, - ) { - if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { - return; - } - - match event_type { - TouchEventType::Move => self.on_scroll_window_event(scroll_location, cursor), - TouchEventType::Up | TouchEventType::Cancel => { - self.on_scroll_window_event(scroll_location, cursor); - }, - TouchEventType::Down => { - self.on_scroll_window_event(scroll_location, cursor); - }, - } - } - - fn on_scroll_window_event(&mut self, scroll_location: ScrollLocation, cursor: DeviceIntPoint) { - self.pending_scroll_zoom_events - .push(ScrollZoomEvent::Scroll(ScrollEvent { - scroll_location, - cursor, - event_count: 1, - })); - } - - pub(crate) fn process_pending_scroll_events(&mut self, compositor: &mut IOCompositor) { - if self.pending_scroll_zoom_events.is_empty() { - return; - } - - // Batch up all scroll events into one, or else we'll do way too much painting. - let mut combined_scroll_event: Option = None; - let mut combined_magnification = 1.0; - for scroll_event in self.pending_scroll_zoom_events.drain(..) { - match scroll_event { - ScrollZoomEvent::PinchZoom(magnification) => { - combined_magnification *= magnification - }, - ScrollZoomEvent::Scroll(scroll_event_info) => { - let combined_event = match combined_scroll_event.as_mut() { - None => { - combined_scroll_event = Some(scroll_event_info); - continue; - }, - Some(combined_event) => combined_event, - }; - - match ( - combined_event.scroll_location, - scroll_event_info.scroll_location, - ) { - (ScrollLocation::Delta(old_delta), ScrollLocation::Delta(new_delta)) => { - // Mac OS X sometimes delivers scroll events out of vsync during a - // fling. This causes events to get bunched up occasionally, causing - // nasty-looking "pops". To mitigate this, during a fling we average - // deltas instead of summing them. - let old_event_count = Scale::new(combined_event.event_count as f32); - combined_event.event_count += 1; - let new_event_count = Scale::new(combined_event.event_count as f32); - combined_event.scroll_location = ScrollLocation::Delta( - (old_delta * old_event_count + new_delta) / new_event_count, - ); - }, - (ScrollLocation::Start, _) | (ScrollLocation::End, _) => { - // Once we see Start or End, we shouldn't process any more events. - break; - }, - (_, ScrollLocation::Start) | (_, ScrollLocation::End) => { - // If this is an event which is scrolling to the start or end of the page, - // disregard other pending events and exit the loop. - *combined_event = scroll_event_info; - break; - }, - } - }, - } - } - - let zoom_changed = compositor - .set_pinch_zoom_level(compositor.pinch_zoom_level().get() * combined_magnification); - let scroll_result = combined_scroll_event.and_then(|combined_event| { - self.scroll_node_at_device_point( - combined_event.cursor.to_f32(), - combined_event.scroll_location, - compositor, - ) - }); - if !zoom_changed && scroll_result.is_none() { - return; - } - - let mut transaction = Transaction::new(); - if zoom_changed { - compositor.send_root_pipeline_display_list_in_transaction(&mut transaction); - } - - if let Some((pipeline_id, external_id, offset)) = scroll_result { - let offset = LayoutVector2D::new(-offset.x, -offset.y); - transaction.set_scroll_offsets( - external_id, - vec![SampledScrollOffset { - offset, - generation: 0, - }], - ); - self.send_scroll_positions_to_layout_for_pipeline(pipeline_id); - } - - compositor.generate_frame(&mut transaction, RenderReasons::APZ); - self.global.borrow_mut().send_transaction(transaction); - } - - /// Perform a hit test at the given [`DevicePoint`] and apply the [`ScrollLocation`] - /// scrolling to the applicable scroll node under that point. If a scroll was - /// performed, returns the [`PipelineId`] of the node scrolled, the id, and the final - /// scroll delta. - fn scroll_node_at_device_point( - &mut self, - cursor: DevicePoint, - scroll_location: ScrollLocation, - compositor: &mut IOCompositor, - ) -> Option<(PipelineId, ExternalScrollId, LayoutVector2D)> { - let scroll_location = match scroll_location { - ScrollLocation::Delta(delta) => { - let device_pixels_per_page = compositor.device_pixels_per_page_pixel(); - let scaled_delta = (Vector2D::from_untyped(delta.to_untyped()) / - device_pixels_per_page) - .to_untyped(); - let calculated_delta = LayoutVector2D::from_untyped(scaled_delta); - ScrollLocation::Delta(calculated_delta) - }, - // Leave ScrollLocation unchanged if it is Start or End location. - ScrollLocation::Start | ScrollLocation::End => scroll_location, - }; - - let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id); - let hit_test_results = self - .global - .borrow() - .hit_test_at_point_with_flags_and_pipeline( - cursor, - HitTestFlags::FIND_ALL, - None, - get_pipeline_details, - ); - - // Iterate through all hit test results, processing only the first node of each pipeline. - // This is needed to propagate the scroll events from a pipeline representing an iframe to - // its ancestor pipelines. - let mut previous_pipeline_id = None; - for CompositorHitTestResult { - pipeline_id, - scroll_tree_node, - .. - } in hit_test_results.iter() - { - let pipeline_details = self.pipelines.get_mut(pipeline_id)?; - if previous_pipeline_id.replace(pipeline_id) != Some(pipeline_id) { - let scroll_result = pipeline_details - .scroll_tree - .scroll_node_or_ancestor(scroll_tree_node, scroll_location); - if let Some((external_id, offset)) = scroll_result { - return Some((*pipeline_id, external_id, offset)); - } - } - } - None - } - - /// Simulate a pinch zoom - pub fn set_pinch_zoom(&mut self, magnification: f32) { - if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { - return; - } - - // TODO: Scroll to keep the center in view? - self.pending_scroll_zoom_events - .push(ScrollZoomEvent::PinchZoom(magnification)); - } -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub struct UnknownWebView(pub WebViewId); diff --git a/components/compositing/webview_manager.rs b/components/compositing/webview_manager.rs index 1f1ced3a468..201f9d1c5c5 100644 --- a/components/compositing/webview_manager.rs +++ b/components/compositing/webview_manager.rs @@ -7,7 +7,7 @@ use std::collections::hash_map::{Entry, Values, ValuesMut}; use base::id::WebViewId; -use crate::webview::UnknownWebView; +use crate::webview_renderer::UnknownWebView; #[derive(Debug)] pub struct WebViewManager { @@ -111,19 +111,15 @@ impl WebViewManager { #[cfg(test)] mod test { - use std::num::NonZeroU32; + use base::id::{BrowsingContextId, Index, PipelineNamespace, PipelineNamespaceId, WebViewId}; - use base::id::{ - BrowsingContextId, BrowsingContextIndex, PipelineNamespace, PipelineNamespaceId, WebViewId, - }; - - use crate::webview::UnknownWebView; use crate::webview_manager::WebViewManager; + use crate::webview_renderer::UnknownWebView; fn top_level_id(namespace_id: u32, index: u32) -> WebViewId { WebViewId(BrowsingContextId { namespace_id: PipelineNamespaceId(namespace_id), - index: BrowsingContextIndex(NonZeroU32::new(index).unwrap()), + index: Index::new(index).unwrap(), }) } diff --git a/components/compositing/webview_renderer.rs b/components/compositing/webview_renderer.rs new file mode 100644 index 00000000000..b0e91ccb02e --- /dev/null +++ b/components/compositing/webview_renderer.rs @@ -0,0 +1,999 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::cell::RefCell; +use std::collections::HashMap; +use std::collections::hash_map::Keys; +use std::rc::Rc; + +use base::id::{PipelineId, WebViewId}; +use compositing_traits::{SendableFrameTree, WebViewTrait}; +use constellation_traits::{EmbedderToConstellationMessage, ScrollState, WindowSizeType}; +use embedder_traits::{ + AnimationState, CompositorHitTestResult, InputEvent, MouseButton, MouseButtonAction, + MouseButtonEvent, MouseMoveEvent, ShutdownState, TouchEvent, TouchEventResult, TouchEventType, + TouchId, ViewportDetails, +}; +use euclid::{Box2D, Point2D, Scale, Size2D, Vector2D}; +use fnv::FnvHashSet; +use log::{debug, warn}; +use servo_geometry::DeviceIndependentPixel; +use style_traits::{CSSPixel, PinchZoomFactor}; +use webrender_api::units::{ + DeviceIntPoint, DeviceIntRect, DevicePixel, DevicePoint, DeviceRect, LayoutVector2D, +}; +use webrender_api::{ExternalScrollId, HitTestFlags, ScrollLocation}; + +use crate::compositor::{PipelineDetails, ServoRenderer}; +use crate::touch::{TouchHandler, TouchMoveAction, TouchMoveAllowed, TouchSequenceState}; + +// Default viewport constraints +const MAX_ZOOM: f32 = 8.0; +const MIN_ZOOM: f32 = 0.1; + +#[derive(Clone, Copy)] +struct ScrollEvent { + /// Scroll by this offset, or to Start or End + scroll_location: ScrollLocation, + /// Apply changes to the frame at this location + cursor: DeviceIntPoint, + /// The number of OS events that have been coalesced together into this one event. + event_count: u32, +} + +#[derive(Clone, Copy)] +enum ScrollZoomEvent { + /// An pinch zoom event that magnifies the view by the given factor. + PinchZoom(f32), + /// A scroll event that scrolls the scroll node at the given location by the + /// given amount. + Scroll(ScrollEvent), +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub(crate) struct ScrollResult { + pub pipeline_id: PipelineId, + pub external_scroll_id: ExternalScrollId, + pub offset: LayoutVector2D, +} + +#[derive(PartialEq)] +pub(crate) enum PinchZoomResult { + DidPinchZoom, + DidNotPinchZoom, +} + +/// A renderer for a libservo `WebView`. This is essentially the [`ServoRenderer`]'s interface to a +/// libservo `WebView`, but the code here cannot depend on libservo in order to prevent circular +/// dependencies, which is why we store a `dyn WebViewTrait` here instead of the `WebView` itself. +pub(crate) struct WebViewRenderer { + /// The [`WebViewId`] of the `WebView` associated with this [`WebViewDetails`]. + pub id: WebViewId, + /// The renderer's view of the embedding layer `WebView` as a trait implementation, + /// so that the renderer doesn't need to depend on the embedding layer. This avoids + /// a dependency cycle. + pub webview: Box, + /// The root [`PipelineId`] of the currently displayed page in this WebView. + pub root_pipeline_id: Option, + pub rect: DeviceRect, + /// Tracks details about each active pipeline that the compositor knows about. + pub pipelines: HashMap, + /// Data that is shared by all WebView renderers. + pub(crate) global: Rc>, + /// Pending scroll/zoom events. + pending_scroll_zoom_events: Vec, + /// Touch input state machine + touch_handler: TouchHandler, + /// "Desktop-style" zoom that resizes the viewport to fit the window. + pub page_zoom: Scale, + /// "Mobile-style" zoom that does not reflow the page. + viewport_zoom: PinchZoomFactor, + /// Viewport zoom constraints provided by @viewport. + min_viewport_zoom: Option, + max_viewport_zoom: Option, + /// The HiDPI scale factor for the `WebView` associated with this renderer. This is controlled + /// by the embedding layer. + hidpi_scale_factor: Scale, + /// Whether or not this [`WebViewRenderer`] isn't throttled and has a pipeline with + /// active animations or animation frame callbacks. + animating: bool, +} + +impl Drop for WebViewRenderer { + fn drop(&mut self) { + self.global + .borrow_mut() + .pipeline_to_webview_map + .retain(|_, webview_id| self.id != *webview_id); + } +} + +impl WebViewRenderer { + pub(crate) fn new( + global: Rc>, + renderer_webview: Box, + viewport_details: ViewportDetails, + ) -> Self { + let hidpi_scale_factor = viewport_details.hidpi_scale_factor; + let size = viewport_details.size * viewport_details.hidpi_scale_factor; + Self { + id: renderer_webview.id(), + webview: renderer_webview, + root_pipeline_id: None, + rect: DeviceRect::from_origin_and_size(DevicePoint::origin(), size), + pipelines: Default::default(), + touch_handler: TouchHandler::new(), + global, + pending_scroll_zoom_events: Default::default(), + page_zoom: Scale::new(1.0), + viewport_zoom: PinchZoomFactor::new(1.0), + min_viewport_zoom: Some(PinchZoomFactor::new(1.0)), + max_viewport_zoom: None, + hidpi_scale_factor: Scale::new(hidpi_scale_factor.0), + animating: false, + } + } + + pub(crate) fn animations_or_animation_callbacks_running(&self) -> bool { + self.pipelines + .values() + .any(PipelineDetails::animations_or_animation_callbacks_running) + } + + pub(crate) fn animation_callbacks_running(&self) -> bool { + self.pipelines + .values() + .any(PipelineDetails::animation_callbacks_running) + } + + pub(crate) fn pipeline_ids(&self) -> Keys<'_, PipelineId, PipelineDetails> { + self.pipelines.keys() + } + + pub(crate) fn animating(&self) -> bool { + self.animating + } + + /// Returns the [`PipelineDetails`] for the given [`PipelineId`], creating it if needed. + pub(crate) fn ensure_pipeline_details( + &mut self, + pipeline_id: PipelineId, + ) -> &mut PipelineDetails { + self.pipelines.entry(pipeline_id).or_insert_with(|| { + self.global + .borrow_mut() + .pipeline_to_webview_map + .insert(pipeline_id, self.id); + PipelineDetails::new() + }) + } + + pub(crate) fn remove_pipeline(&mut self, pipeline_id: PipelineId) { + self.global + .borrow_mut() + .pipeline_to_webview_map + .remove(&pipeline_id); + self.pipelines.remove(&pipeline_id); + } + + pub(crate) fn set_frame_tree(&mut self, frame_tree: &SendableFrameTree) { + let pipeline_id = frame_tree.pipeline.id; + let old_pipeline_id = std::mem::replace(&mut self.root_pipeline_id, Some(pipeline_id)); + + if old_pipeline_id != self.root_pipeline_id { + debug!( + "Updating webview ({:?}) from pipeline {:?} to {:?}", + 3, old_pipeline_id, self.root_pipeline_id + ); + } + + self.set_frame_tree_on_pipeline_details(frame_tree, None); + self.reset_scroll_tree_for_unattached_pipelines(frame_tree); + self.send_scroll_positions_to_layout_for_pipeline(pipeline_id); + } + + pub(crate) fn send_scroll_positions_to_layout_for_pipeline(&self, pipeline_id: PipelineId) { + let Some(details) = self.pipelines.get(&pipeline_id) else { + return; + }; + + let mut scroll_states = Vec::new(); + details.scroll_tree.nodes.iter().for_each(|node| { + if let (Some(scroll_id), Some(scroll_offset)) = (node.external_id(), node.offset()) { + scroll_states.push(ScrollState { + scroll_id, + scroll_offset, + }); + } + }); + + let _ = self.global.borrow().constellation_sender.send( + EmbedderToConstellationMessage::SetScrollStates(pipeline_id, scroll_states), + ); + } + + pub(crate) fn set_frame_tree_on_pipeline_details( + &mut self, + frame_tree: &SendableFrameTree, + parent_pipeline_id: Option, + ) { + let pipeline_id = frame_tree.pipeline.id; + let pipeline_details = self.ensure_pipeline_details(pipeline_id); + pipeline_details.pipeline = Some(frame_tree.pipeline.clone()); + pipeline_details.parent_pipeline_id = parent_pipeline_id; + + for kid in &frame_tree.children { + self.set_frame_tree_on_pipeline_details(kid, Some(pipeline_id)); + } + } + + pub(crate) fn reset_scroll_tree_for_unattached_pipelines( + &mut self, + frame_tree: &SendableFrameTree, + ) { + // TODO(mrobinson): Eventually this can selectively preserve the scroll trees + // state for some unattached pipelines in order to preserve scroll position when + // navigating backward and forward. + fn collect_pipelines( + pipelines: &mut FnvHashSet, + frame_tree: &SendableFrameTree, + ) { + pipelines.insert(frame_tree.pipeline.id); + for kid in &frame_tree.children { + collect_pipelines(pipelines, kid); + } + } + + let mut attached_pipelines: FnvHashSet = FnvHashSet::default(); + collect_pipelines(&mut attached_pipelines, frame_tree); + + self.pipelines + .iter_mut() + .filter(|(id, _)| !attached_pipelines.contains(id)) + .for_each(|(_, details)| { + details.scroll_tree.nodes.iter_mut().for_each(|node| { + node.set_offset(LayoutVector2D::zero()); + }) + }) + } + + /// Sets or unsets the animations-running flag for the given pipeline. Returns + /// true if the [`WebViewRenderer`]'s overall animating state changed. + pub(crate) fn change_pipeline_running_animations_state( + &mut self, + pipeline_id: PipelineId, + animation_state: AnimationState, + ) -> bool { + let pipeline_details = self.ensure_pipeline_details(pipeline_id); + match animation_state { + AnimationState::AnimationsPresent => { + pipeline_details.animations_running = true; + }, + AnimationState::AnimationCallbacksPresent => { + pipeline_details.animation_callbacks_running = true; + }, + AnimationState::NoAnimationsPresent => { + pipeline_details.animations_running = false; + }, + AnimationState::NoAnimationCallbacksPresent => { + pipeline_details.animation_callbacks_running = false; + }, + } + self.update_animation_state() + } + + /// Sets or unsets the throttled flag for the given pipeline. Returns + /// true if the [`WebViewRenderer`]'s overall animating state changed. + pub(crate) fn set_throttled(&mut self, pipeline_id: PipelineId, throttled: bool) -> bool { + self.ensure_pipeline_details(pipeline_id).throttled = throttled; + + // Throttling a pipeline can cause it to be taken into the "not-animating" state. + self.update_animation_state() + } + + pub(crate) fn update_animation_state(&mut self) -> bool { + let animating = self.pipelines.values().any(PipelineDetails::animating); + let old_animating = std::mem::replace(&mut self.animating, animating); + self.webview.set_animating(self.animating); + old_animating != self.animating + } + + /// On a Window refresh tick (e.g. vsync) + pub(crate) fn on_vsync(&mut self) { + if let Some(fling_action) = self.touch_handler.on_vsync() { + self.on_scroll_window_event( + ScrollLocation::Delta(fling_action.delta), + fling_action.cursor, + ); + } + } + + pub(crate) fn dispatch_input_event(&mut self, event: InputEvent) { + // Events that do not need to do hit testing are sent directly to the + // constellation to filter down. + let Some(point) = event.point() else { + return; + }; + + // If we can't find a pipeline to send this event to, we cannot continue. + let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id); + let Some(result) = self + .global + .borrow() + .hit_test_at_point(point, get_pipeline_details) + else { + return; + }; + + self.global.borrow_mut().update_cursor(point, &result); + + if let Err(error) = self.global.borrow().constellation_sender.send( + EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, Some(result)), + ) { + warn!("Sending event to constellation failed ({error:?})."); + } + } + + pub(crate) fn notify_input_event(&mut self, event: InputEvent) { + if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { + return; + } + + if let InputEvent::Touch(event) = event { + self.on_touch_event(event); + return; + } + + if self.global.borrow().convert_mouse_to_touch { + match event { + InputEvent::MouseButton(event) => { + match (event.button, event.action) { + (MouseButton::Left, MouseButtonAction::Down) => self.on_touch_down( + TouchEvent::new(TouchEventType::Down, TouchId(0), event.point), + ), + (MouseButton::Left, MouseButtonAction::Up) => self.on_touch_up( + TouchEvent::new(TouchEventType::Up, TouchId(0), event.point), + ), + _ => {}, + } + return; + }, + InputEvent::MouseMove(event) => { + if let Some(state) = self.touch_handler.try_get_current_touch_sequence() { + // We assume that the debug option `-Z convert-mouse-to-touch` will only + // be used on devices without native touch input, so we can directly + // reuse the touch handler for tracking the state of pressed buttons. + match state.state { + TouchSequenceState::Touching | TouchSequenceState::Panning { .. } => { + self.on_touch_move(TouchEvent::new( + TouchEventType::Move, + TouchId(0), + event.point, + )); + }, + TouchSequenceState::MultiTouch => { + // Multitouch simulation currently is not implemented. + // Since we only get one mouse move event, we would need to + // dispatch one mouse move event per currently pressed mouse button. + }, + TouchSequenceState::Pinching => { + // We only have one mouse button, so Pinching should be impossible. + #[cfg(debug_assertions)] + log::error!( + "Touch handler is in Pinching state, which should be unreachable with \ + -Z convert-mouse-to-touch debug option." + ); + }, + TouchSequenceState::PendingFling { .. } | + TouchSequenceState::Flinging { .. } | + TouchSequenceState::PendingClick(_) | + TouchSequenceState::Finished => { + // Mouse movement without a button being pressed is not + // translated to touch events. + }, + } + } + // We don't want to (directly) dispatch mouse events when simulating touch input. + return; + }, + _ => {}, + } + } + + self.dispatch_input_event(event); + } + + fn send_touch_event(&self, mut event: TouchEvent) -> bool { + let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id); + let Some(result) = self + .global + .borrow() + .hit_test_at_point(event.point, get_pipeline_details) + else { + return false; + }; + + event.init_sequence_id(self.touch_handler.current_sequence_id); + let event = InputEvent::Touch(event); + if let Err(e) = self.global.borrow().constellation_sender.send( + EmbedderToConstellationMessage::ForwardInputEvent(self.id, event, Some(result)), + ) { + warn!("Sending event to constellation failed ({:?}).", e); + false + } else { + true + } + } + + pub(crate) fn on_touch_event(&mut self, event: TouchEvent) { + if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { + return; + } + + match event.event_type { + TouchEventType::Down => self.on_touch_down(event), + TouchEventType::Move => self.on_touch_move(event), + TouchEventType::Up => self.on_touch_up(event), + TouchEventType::Cancel => self.on_touch_cancel(event), + } + } + + fn on_touch_down(&mut self, event: TouchEvent) { + self.touch_handler.on_touch_down(event.id, event.point); + self.send_touch_event(event); + } + + fn on_touch_move(&mut self, mut event: TouchEvent) { + let action: TouchMoveAction = self.touch_handler.on_touch_move(event.id, event.point); + if TouchMoveAction::NoAction != action { + // if first move processed and allowed, we directly process the move event, + // without waiting for the script handler. + if self + .touch_handler + .move_allowed(self.touch_handler.current_sequence_id) + { + // https://w3c.github.io/touch-events/#cancelability + event.disable_cancelable(); + match action { + TouchMoveAction::Scroll(delta, point) => self.on_scroll_window_event( + ScrollLocation::Delta(LayoutVector2D::from_untyped(delta.to_untyped())), + point.cast(), + ), + TouchMoveAction::Zoom(magnification, scroll_delta) => { + let cursor = Point2D::new(-1, -1); // Make sure this hits the base layer. + + // The order of these events doesn't matter, because zoom is handled by + // a root display list and the scroll event here is handled by the scroll + // applied to the content display list. + self.pending_scroll_zoom_events + .push(ScrollZoomEvent::PinchZoom(magnification)); + self.pending_scroll_zoom_events + .push(ScrollZoomEvent::Scroll(ScrollEvent { + scroll_location: ScrollLocation::Delta( + LayoutVector2D::from_untyped(scroll_delta.to_untyped()), + ), + cursor, + event_count: 1, + })); + }, + _ => {}, + } + } + // When the event is touchmove, if the script thread is processing the touch + // move event, we skip sending the event to the script thread. + // This prevents the script thread from stacking up for a large amount of time. + if !self + .touch_handler + .is_handling_touch_move(self.touch_handler.current_sequence_id) && + self.send_touch_event(event) && + event.is_cancelable() + { + self.touch_handler + .set_handling_touch_move(self.touch_handler.current_sequence_id, true); + } + } + } + + fn on_touch_up(&mut self, event: TouchEvent) { + self.touch_handler.on_touch_up(event.id, event.point); + self.send_touch_event(event); + } + + fn on_touch_cancel(&mut self, event: TouchEvent) { + // Send the event to script. + self.touch_handler.on_touch_cancel(event.id, event.point); + self.send_touch_event(event); + } + + pub(crate) fn on_touch_event_processed(&mut self, result: TouchEventResult) { + match result { + TouchEventResult::DefaultPrevented(sequence_id, event_type) => { + debug!( + "Touch event {:?} in sequence {:?} prevented!", + event_type, sequence_id + ); + match event_type { + TouchEventType::Down => { + // prevents both click and move + self.touch_handler.prevent_click(sequence_id); + self.touch_handler.prevent_move(sequence_id); + self.touch_handler + .remove_pending_touch_move_action(sequence_id); + }, + TouchEventType::Move => { + // script thread processed the touch move event, mark this false. + if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) { + info.prevent_move = TouchMoveAllowed::Prevented; + if let TouchSequenceState::PendingFling { .. } = info.state { + info.state = TouchSequenceState::Finished; + } + self.touch_handler.set_handling_touch_move( + self.touch_handler.current_sequence_id, + false, + ); + self.touch_handler + .remove_pending_touch_move_action(sequence_id); + } + }, + TouchEventType::Up => { + // Note: We don't have to consider PendingFling here, since we handle that + // in the DefaultAllowed case of the touch_move event. + // Note: Removing can and should fail, if we still have an active Fling, + let Some(info) = + &mut self.touch_handler.get_touch_sequence_mut(sequence_id) + else { + // The sequence ID could already be removed, e.g. if Fling finished, + // before the touch_up event was handled (since fling can start + // immediately if move was previously allowed, and clicks are anyway not + // happening from fling). + return; + }; + match info.state { + TouchSequenceState::PendingClick(_) => { + info.state = TouchSequenceState::Finished; + self.touch_handler.remove_touch_sequence(sequence_id); + }, + TouchSequenceState::Flinging { .. } => { + // We can't remove the touch sequence yet + }, + TouchSequenceState::Finished => { + self.touch_handler.remove_touch_sequence(sequence_id); + }, + TouchSequenceState::Touching | + TouchSequenceState::Panning { .. } | + TouchSequenceState::Pinching | + TouchSequenceState::MultiTouch | + TouchSequenceState::PendingFling { .. } => { + // It's possible to transition from Pinch to pan, Which means that + // a touch_up event for a pinch might have arrived here, but we + // already transitioned to pan or even PendingFling. + // We don't need to do anything in these cases though. + }, + } + }, + TouchEventType::Cancel => { + // We could still have pending event handlers, so we remove the pending + // actions, and try to remove the touch sequence. + self.touch_handler + .remove_pending_touch_move_action(sequence_id); + self.touch_handler.try_remove_touch_sequence(sequence_id); + }, + } + }, + TouchEventResult::DefaultAllowed(sequence_id, event_type) => { + debug!( + "Touch event {:?} in sequence {:?} allowed", + event_type, sequence_id + ); + match event_type { + TouchEventType::Down => {}, + TouchEventType::Move => { + if let Some(action) = + self.touch_handler.pending_touch_move_action(sequence_id) + { + match action { + TouchMoveAction::Scroll(delta, point) => self + .on_scroll_window_event( + ScrollLocation::Delta(LayoutVector2D::from_untyped( + delta.to_untyped(), + )), + point.cast(), + ), + TouchMoveAction::Zoom(magnification, scroll_delta) => { + let cursor = Point2D::new(-1, -1); + // Make sure this hits the base layer. + // The order of these events doesn't matter, because zoom is handled by + // a root display list and the scroll event here is handled by the scroll + // applied to the content display list. + self.pending_scroll_zoom_events + .push(ScrollZoomEvent::PinchZoom(magnification)); + self.pending_scroll_zoom_events + .push(ScrollZoomEvent::Scroll(ScrollEvent { + scroll_location: ScrollLocation::Delta( + LayoutVector2D::from_untyped( + scroll_delta.to_untyped(), + ), + ), + cursor, + event_count: 1, + })); + }, + TouchMoveAction::NoAction => { + // This shouldn't happen, but we can also just ignore it. + }, + } + self.touch_handler + .remove_pending_touch_move_action(sequence_id); + } + self.touch_handler + .set_handling_touch_move(self.touch_handler.current_sequence_id, false); + if let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) { + if info.prevent_move == TouchMoveAllowed::Pending { + info.prevent_move = TouchMoveAllowed::Allowed; + if let TouchSequenceState::PendingFling { velocity, cursor } = + info.state + { + info.state = TouchSequenceState::Flinging { velocity, cursor } + } + } + } + }, + TouchEventType::Up => { + let Some(info) = self.touch_handler.get_touch_sequence_mut(sequence_id) + else { + // The sequence was already removed because there is no default action. + return; + }; + match info.state { + TouchSequenceState::PendingClick(point) => { + info.state = TouchSequenceState::Finished; + // PreventDefault from touch_down may have been processed after + // touch_up already occurred. + if !info.prevent_click { + self.simulate_mouse_click(point); + } + self.touch_handler.remove_touch_sequence(sequence_id); + }, + TouchSequenceState::Flinging { .. } => { + // We can't remove the touch sequence yet + }, + TouchSequenceState::Finished => { + self.touch_handler.remove_touch_sequence(sequence_id); + }, + TouchSequenceState::Panning { .. } | + TouchSequenceState::Pinching | + TouchSequenceState::PendingFling { .. } => { + // It's possible to transition from Pinch to pan, Which means that + // a touch_up event for a pinch might have arrived here, but we + // already transitioned to pan or even PendingFling. + // We don't need to do anything in these cases though. + }, + TouchSequenceState::MultiTouch | TouchSequenceState::Touching => { + // We transitioned to touching from multi-touch or pinching. + }, + } + }, + TouchEventType::Cancel => { + self.touch_handler + .remove_pending_touch_move_action(sequence_id); + self.touch_handler.try_remove_touch_sequence(sequence_id); + }, + } + }, + } + } + + /// + fn simulate_mouse_click(&mut self, point: DevicePoint) { + let button = MouseButton::Left; + self.dispatch_input_event(InputEvent::MouseMove(MouseMoveEvent::new(point))); + self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent::new( + MouseButtonAction::Down, + button, + point, + ))); + self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent::new( + MouseButtonAction::Up, + button, + point, + ))); + } + + pub(crate) fn notify_scroll_event( + &mut self, + scroll_location: ScrollLocation, + cursor: DeviceIntPoint, + event_type: TouchEventType, + ) { + if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { + return; + } + + match event_type { + TouchEventType::Move => self.on_scroll_window_event(scroll_location, cursor), + TouchEventType::Up | TouchEventType::Cancel => { + self.on_scroll_window_event(scroll_location, cursor); + }, + TouchEventType::Down => { + self.on_scroll_window_event(scroll_location, cursor); + }, + } + } + + fn on_scroll_window_event(&mut self, scroll_location: ScrollLocation, cursor: DeviceIntPoint) { + self.pending_scroll_zoom_events + .push(ScrollZoomEvent::Scroll(ScrollEvent { + scroll_location, + cursor, + event_count: 1, + })); + } + + /// Push scroll pending event when receiving wheel action from webdriver + pub(crate) fn on_webdriver_wheel_action( + &mut self, + scroll_delta: Vector2D, + point: Point2D, + ) { + if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { + return; + } + + let scroll_location = + ScrollLocation::Delta(LayoutVector2D::from_untyped(scroll_delta.to_untyped())); + let cursor = DeviceIntPoint::new(point.x as i32, point.y as i32); + self.on_scroll_window_event(scroll_location, cursor) + } + + /// Process pending scroll events for this [`WebViewRenderer`]. Returns a tuple containing: + /// + /// - A boolean that is true if a zoom occurred. + /// - An optional [`ScrollResult`] if a scroll occurred. + /// + /// It is up to the caller to ensure that these events update the rendering appropriately. + pub(crate) fn process_pending_scroll_and_pinch_zoom_events( + &mut self, + ) -> (PinchZoomResult, Option) { + if self.pending_scroll_zoom_events.is_empty() { + return (PinchZoomResult::DidNotPinchZoom, None); + } + + // Batch up all scroll events into one, or else we'll do way too much painting. + let mut combined_scroll_event: Option = None; + let mut combined_magnification = 1.0; + for scroll_event in self.pending_scroll_zoom_events.drain(..) { + match scroll_event { + ScrollZoomEvent::PinchZoom(magnification) => { + combined_magnification *= magnification + }, + ScrollZoomEvent::Scroll(scroll_event_info) => { + let combined_event = match combined_scroll_event.as_mut() { + None => { + combined_scroll_event = Some(scroll_event_info); + continue; + }, + Some(combined_event) => combined_event, + }; + + match ( + combined_event.scroll_location, + scroll_event_info.scroll_location, + ) { + (ScrollLocation::Delta(old_delta), ScrollLocation::Delta(new_delta)) => { + // Mac OS X sometimes delivers scroll events out of vsync during a + // fling. This causes events to get bunched up occasionally, causing + // nasty-looking "pops". To mitigate this, during a fling we average + // deltas instead of summing them. + let old_event_count = Scale::new(combined_event.event_count as f32); + combined_event.event_count += 1; + let new_event_count = Scale::new(combined_event.event_count as f32); + combined_event.scroll_location = ScrollLocation::Delta( + (old_delta * old_event_count + new_delta) / new_event_count, + ); + }, + (ScrollLocation::Start, _) | (ScrollLocation::End, _) => { + // Once we see Start or End, we shouldn't process any more events. + break; + }, + (_, ScrollLocation::Start) | (_, ScrollLocation::End) => { + // If this is an event which is scrolling to the start or end of the page, + // disregard other pending events and exit the loop. + *combined_event = scroll_event_info; + break; + }, + } + }, + } + } + + let scroll_result = combined_scroll_event.and_then(|combined_event| { + self.scroll_node_at_device_point( + combined_event.cursor.to_f32(), + combined_event.scroll_location, + ) + }); + if let Some(scroll_result) = scroll_result { + self.send_scroll_positions_to_layout_for_pipeline(scroll_result.pipeline_id); + } + + let pinch_zoom_result = match self + .set_pinch_zoom_level(self.pinch_zoom_level().get() * combined_magnification) + { + true => PinchZoomResult::DidPinchZoom, + false => PinchZoomResult::DidNotPinchZoom, + }; + + (pinch_zoom_result, scroll_result) + } + + /// Perform a hit test at the given [`DevicePoint`] and apply the [`ScrollLocation`] + /// scrolling to the applicable scroll node under that point. If a scroll was + /// performed, returns the [`PipelineId`] of the node scrolled, the id, and the final + /// scroll delta. + fn scroll_node_at_device_point( + &mut self, + cursor: DevicePoint, + scroll_location: ScrollLocation, + ) -> Option { + let scroll_location = match scroll_location { + ScrollLocation::Delta(delta) => { + let device_pixels_per_page = self.device_pixels_per_page_pixel(); + let scaled_delta = (Vector2D::from_untyped(delta.to_untyped()) / + device_pixels_per_page) + .to_untyped(); + let calculated_delta = LayoutVector2D::from_untyped(scaled_delta); + ScrollLocation::Delta(calculated_delta) + }, + // Leave ScrollLocation unchanged if it is Start or End location. + ScrollLocation::Start | ScrollLocation::End => scroll_location, + }; + + let get_pipeline_details = |pipeline_id| self.pipelines.get(&pipeline_id); + let hit_test_results = self + .global + .borrow() + .hit_test_at_point_with_flags_and_pipeline( + cursor, + HitTestFlags::FIND_ALL, + None, + get_pipeline_details, + ); + + // Iterate through all hit test results, processing only the first node of each pipeline. + // This is needed to propagate the scroll events from a pipeline representing an iframe to + // its ancestor pipelines. + let mut previous_pipeline_id = None; + for CompositorHitTestResult { + pipeline_id, + scroll_tree_node, + .. + } in hit_test_results.iter() + { + let pipeline_details = self.pipelines.get_mut(pipeline_id)?; + if previous_pipeline_id.replace(pipeline_id) != Some(pipeline_id) { + let scroll_result = pipeline_details + .scroll_tree + .scroll_node_or_ancestor(scroll_tree_node, scroll_location); + if let Some((external_scroll_id, offset)) = scroll_result { + return Some(ScrollResult { + pipeline_id: *pipeline_id, + external_scroll_id, + offset, + }); + } + } + } + None + } + + pub(crate) fn pinch_zoom_level(&self) -> Scale { + Scale::new(self.viewport_zoom.get()) + } + + fn set_pinch_zoom_level(&mut self, mut zoom: f32) -> bool { + if let Some(min) = self.min_viewport_zoom { + zoom = f32::max(min.get(), zoom); + } + if let Some(max) = self.max_viewport_zoom { + zoom = f32::min(max.get(), zoom); + } + + let old_zoom = std::mem::replace(&mut self.viewport_zoom, PinchZoomFactor::new(zoom)); + old_zoom != self.viewport_zoom + } + + pub(crate) fn set_page_zoom(&mut self, magnification: f32) { + self.page_zoom = + Scale::new((self.page_zoom.get() * magnification).clamp(MIN_ZOOM, MAX_ZOOM)); + } + + pub(crate) fn device_pixels_per_page_pixel(&self) -> Scale { + self.page_zoom * self.hidpi_scale_factor * self.pinch_zoom_level() + } + + pub(crate) fn device_pixels_per_page_pixel_not_including_pinch_zoom( + &self, + ) -> Scale { + self.page_zoom * self.hidpi_scale_factor + } + + /// Simulate a pinch zoom + pub(crate) fn set_pinch_zoom(&mut self, magnification: f32) { + if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown { + return; + } + + // TODO: Scroll to keep the center in view? + self.pending_scroll_zoom_events + .push(ScrollZoomEvent::PinchZoom(magnification)); + } + + fn send_window_size_message(&self) { + // The device pixel ratio used by the style system should include the scale from page pixels + // to device pixels, but not including any pinch zoom. + let device_pixel_ratio = self.device_pixels_per_page_pixel_not_including_pinch_zoom(); + let initial_viewport = self.rect.size().to_f32() / device_pixel_ratio; + let msg = EmbedderToConstellationMessage::ChangeViewportDetails( + self.id, + ViewportDetails { + hidpi_scale_factor: device_pixel_ratio, + size: initial_viewport, + }, + WindowSizeType::Resize, + ); + if let Err(e) = self.global.borrow().constellation_sender.send(msg) { + warn!("Sending window resize to constellation failed ({:?}).", e); + } + } + + /// Set the `hidpi_scale_factor` for this renderer, returning `true` if the value actually changed. + pub(crate) fn set_hidpi_scale_factor( + &mut self, + new_scale: Scale, + ) -> bool { + let old_scale_factor = std::mem::replace(&mut self.hidpi_scale_factor, new_scale); + if self.hidpi_scale_factor == old_scale_factor { + return false; + } + + self.send_window_size_message(); + true + } + + /// Set the `rect` for this renderer, returning `true` if the value actually changed. + pub(crate) fn set_rect(&mut self, new_rect: DeviceRect) -> bool { + let old_rect = std::mem::replace(&mut self.rect, new_rect); + if old_rect.size() != self.rect.size() { + self.send_window_size_message(); + } + old_rect != self.rect + } + + pub(crate) fn client_window_rect( + &self, + rendering_context_size: Size2D, + ) -> Box2D { + let screen_geometry = self.webview.screen_geometry().unwrap_or_default(); + let rect = DeviceIntRect::from_origin_and_size( + screen_geometry.offset, + rendering_context_size.to_i32(), + ) + .to_f32() / + self.hidpi_scale_factor; + rect.to_i32() + } + + pub(crate) fn screen_size(&self) -> Size2D { + let screen_geometry = self.webview.screen_geometry().unwrap_or_default(); + (screen_geometry.size.to_f32() / self.hidpi_scale_factor).to_i32() + } + + pub(crate) fn available_screen_size(&self) -> Size2D { + let screen_geometry = self.webview.screen_geometry().unwrap_or_default(); + (screen_geometry.available_size.to_f32() / self.hidpi_scale_factor).to_i32() + } +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct UnknownWebView(pub WebViewId); diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs deleted file mode 100644 index a73e65f11d1..00000000000 --- a/components/compositing/windowing.rs +++ /dev/null @@ -1,69 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Abstract windowing methods. The concrete implementations of these can be found in `platform/`. - -use std::fmt::Debug; - -use embedder_traits::{EventLoopWaker, MouseButton}; -use euclid::Scale; -use net::protocols::ProtocolRegistry; -use servo_geometry::DeviceIndependentPixel; -use webrender_api::units::{DevicePixel, DevicePoint}; - -#[derive(Clone)] -pub enum MouseWindowEvent { - Click(MouseButton, DevicePoint), - MouseDown(MouseButton, DevicePoint), - MouseUp(MouseButton, DevicePoint), -} - -/// Various debug and profiling flags that WebRender supports. -#[derive(Clone)] -pub enum WebRenderDebugOption { - Profiler, - TextureCacheDebug, - RenderTargetDebug, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum AnimationState { - Idle, - Animating, -} - -// TODO: this trait assumes that the window is responsible -// for creating the GL context, making it current, buffer -// swapping, etc. Really that should all be done by surfman. -pub trait WindowMethods { - /// Get the HighDPI factor of the native window, the screen and the framebuffer. - /// TODO(martin): Move this to `RendererWebView` when possible. - fn hidpi_factor(&self) -> Scale; - /// Set whether the application is currently animating. - /// Typically, when animations are active, the window - /// will want to avoid blocking on UI events, and just - /// run the event loop at the vsync interval. - /// TODO(martin): Move this to `RendererWebView` when possible. - fn set_animation_state(&self, _state: AnimationState); -} - -pub trait EmbedderMethods { - /// Returns a thread-safe object to wake up the window's event loop. - fn create_event_loop_waker(&mut self) -> Box; - - #[cfg(feature = "webxr")] - /// Register services with a WebXR Registry. - fn register_webxr( - &mut self, - _: &mut webxr::MainThreadRegistry, - _: embedder_traits::EmbedderProxy, - ) { - } - - /// Returns the protocol handlers implemented by that embedder. - /// They will be merged with the default internal ones. - fn get_protocol_handlers(&self) -> ProtocolRegistry { - ProtocolRegistry::default() - } -} diff --git a/components/config/opts.rs b/components/config/opts.rs index 785b43b0acd..3db866a7443 100644 --- a/components/config/opts.rs +++ b/components/config/opts.rs @@ -104,7 +104,7 @@ pub struct DebugOptions { /// Dumps the rule tree. pub dump_rule_tree: bool, - /// Print the flow tree (Layout 2013) or fragment tree (Layout 2020) after each layout. + /// Print the fragment tree after each layout. pub dump_flow_tree: bool, /// Print the stacking context tree after each layout. diff --git a/components/config/prefs.rs b/components/config/prefs.rs index 88f902f425d..896a4fe0bc4 100644 --- a/components/config/prefs.rs +++ b/components/config/prefs.rs @@ -69,6 +69,7 @@ pub struct Preferences { /// List of comma-separated backends to be used by wgpu. pub dom_webgpu_wgpu_backend: String, pub dom_abort_controller_enabled: bool, + pub dom_async_clipboard_enabled: bool, pub dom_bluetooth_enabled: bool, pub dom_bluetooth_testing_enabled: bool, pub dom_allow_scripts_to_close_windows: bool, @@ -81,7 +82,6 @@ pub struct Preferences { pub dom_document_dblclick_timeout: i64, pub dom_document_dblclick_dist: i64, pub dom_fontface_enabled: bool, - pub dom_forcetouch_enabled: bool, pub dom_fullscreen_test: bool, pub dom_gamepad_enabled: bool, pub dom_imagebitmap_enabled: bool, @@ -99,7 +99,6 @@ pub struct Preferences { pub dom_serviceworker_timeout_seconds: i64, pub dom_servo_helpers_enabled: bool, pub dom_servoparser_async_html_tokenizer_enabled: bool, - pub dom_shadowdom_enabled: bool, pub dom_svg_enabled: bool, pub dom_testable_crash_enabled: bool, pub dom_testbinding_enabled: bool, @@ -116,10 +115,7 @@ pub struct Preferences { pub dom_testperf_enabled: bool, // https://testutils.spec.whatwg.org#availability pub dom_testutils_enabled: bool, - /// Enable the [URLPattern] API. - /// - /// [URLPattern]: https://developer.mozilla.org/en-US/docs/Web/API/URLPattern - pub dom_urlpattern_enabled: bool, + pub dom_trusted_types_enabled: bool, pub dom_xpath_enabled: bool, /// Enable WebGL2 APIs. pub dom_webgl2_enabled: bool, @@ -235,6 +231,8 @@ pub struct Preferences { /// The user-agent to use for Servo. This can also be set via [`UserAgentPlatform`] in /// order to set the value to the default value for the given platform. pub user_agent: String, + + pub log_filter: String, } impl Preferences { @@ -245,6 +243,7 @@ impl Preferences { devtools_server_port: 0, dom_abort_controller_enabled: false, dom_allow_scripts_to_close_windows: false, + dom_async_clipboard_enabled: false, dom_bluetooth_enabled: false, dom_bluetooth_testing_enabled: false, dom_canvas_capture_enabled: false, @@ -256,7 +255,6 @@ impl Preferences { dom_document_dblclick_dist: 1, dom_document_dblclick_timeout: 300, dom_fontface_enabled: false, - dom_forcetouch_enabled: false, dom_fullscreen_test: false, dom_gamepad_enabled: true, dom_imagebitmap_enabled: false, @@ -274,7 +272,6 @@ impl Preferences { dom_serviceworker_timeout_seconds: 60, dom_servo_helpers_enabled: false, dom_servoparser_async_html_tokenizer_enabled: false, - dom_shadowdom_enabled: true, dom_svg_enabled: false, dom_testable_crash_enabled: false, dom_testbinding_enabled: false, @@ -290,7 +287,7 @@ impl Preferences { dom_testing_html_input_element_select_files_enabled: false, dom_testperf_enabled: false, dom_testutils_enabled: false, - dom_urlpattern_enabled: false, + dom_trusted_types_enabled: false, dom_webgl2_enabled: false, dom_webgpu_enabled: false, dom_webgpu_wgpu_backend: String::new(), @@ -396,6 +393,7 @@ impl Preferences { threadpools_webrender_workers_max: 4, webgl_testing_context_creation_error: false, user_agent: String::new(), + log_filter: String::new(), } } } diff --git a/components/constellation/Cargo.toml b/components/constellation/Cargo.toml index 9bc1576fa62..1053a23948a 100644 --- a/components/constellation/Cargo.toml +++ b/components/constellation/Cargo.toml @@ -14,7 +14,6 @@ path = "lib.rs" [features] bluetooth = ["bluetooth_traits"] default = [] -multiview = [] tracing = ["dep:tracing"] webgpu = ["script_traits/webgpu"] @@ -53,7 +52,6 @@ webgpu = { path = "../webgpu" } webgpu_traits = { workspace = true } webrender = { workspace = true } webrender_api = { workspace = true } -webrender_traits = { workspace = true } webxr-api = { workspace = true, features = ["ipc"] } [target.'cfg(any(target_os="macos", all(not(target_os = "windows"), not(target_os = "ios"), not(target_os="android"), not(target_env="ohos"), not(target_arch="arm"), not(target_arch="aarch64"))))'.dependencies] diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index f176f3b60d0..5b4d244e746 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -108,10 +108,17 @@ use bluetooth_traits::BluetoothRequest; use canvas_traits::ConstellationCanvasMsg; use canvas_traits::canvas::{CanvasId, CanvasMsg}; use canvas_traits::webgl::WebGLThreads; -use compositing_traits::{CompositorMsg, CompositorProxy, SendableFrameTree}; +use compositing_traits::{ + CompositorMsg, CompositorProxy, SendableFrameTree, WebrenderExternalImageRegistry, +}; use constellation_traits::{ - AnimationTickType, CompositorHitTestResult, EmbedderToConstellationMessage, LogEntry, - PaintMetricEvent, ScrollState, TraversalDirection, WindowSizeType, + AuxiliaryWebViewCreationRequest, AuxiliaryWebViewCreationResponse, BroadcastMsg, DocumentState, + EmbedderToConstellationMessage, IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, + IFrameSizeMsg, Job, LoadData, LoadOrigin, LogEntry, MessagePortMsg, NavigationHistoryBehavior, + PaintMetricEvent, PortMessageTask, PortTransferInfo, SWManagerMsg, SWManagerSenders, + ScriptToConstellationChan, ScriptToConstellationMessage, ScrollState, + ServiceWorkerManagerFactory, ServiceWorkerMsg, StructuredSerializedData, TraversalDirection, + WindowSizeType, }; use crossbeam_channel::{Receiver, Select, Sender, unbounded}; use devtools_traits::{ @@ -121,9 +128,11 @@ use devtools_traits::{ use embedder_traits::resources::{self, Resource}; use embedder_traits::user_content_manager::UserContentManager; use embedder_traits::{ - Cursor, EmbedderMsg, EmbedderProxy, ImeEvent, InputEvent, MediaSessionActionType, - MediaSessionEvent, MediaSessionPlaybackState, MouseButton, MouseButtonAction, MouseButtonEvent, - Theme, ViewportDetails, WebDriverCommandMsg, WebDriverLoadStatus, + AnimationState, CompositorHitTestResult, Cursor, EmbedderMsg, EmbedderProxy, + FocusSequenceNumber, ImeEvent, InputEvent, JSValue, JavaScriptEvaluationError, + JavaScriptEvaluationId, MediaSessionActionType, MediaSessionEvent, MediaSessionPlaybackState, + MouseButton, MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverCommandMsg, + WebDriverCommandResponse, WebDriverLoadStatus, }; use euclid::Size2D; use euclid::default::Size2D as UntypedSize2D; @@ -139,16 +148,12 @@ use net_traits::pub_domains::reg_host; use net_traits::request::Referrer; use net_traits::storage_thread::{StorageThreadMsg, StorageType}; use net_traits::{self, IpcSend, ReferrerPolicy, ResourceThreads}; +use profile_traits::mem::ProfilerMsg; use profile_traits::{mem, time}; use script_layout_interface::{LayoutFactory, ScriptThreadFactory}; use script_traits::{ - AnimationState, AuxiliaryWebViewCreationRequest, AuxiliaryWebViewCreationResponse, - BroadcastMsg, ConstellationInputEvent, DiscardBrowsingContext, DocumentActivity, DocumentState, - IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState, IFrameSizeMsg, Job, LoadData, - LoadOrigin, MessagePortMsg, NavigationHistoryBehavior, PortMessageTask, - ProgressiveWebMetricType, SWManagerMsg, SWManagerSenders, ScriptThreadMessage, - ScriptToConstellationChan, ScriptToConstellationMessage, ServiceWorkerManagerFactory, - ServiceWorkerMsg, StructuredSerializedData, UpdatePipelineIdReason, + ConstellationInputEvent, DiscardBrowsingContext, DocumentActivity, ProgressiveWebMetricType, + ScriptThreadMessage, UpdatePipelineIdReason, }; use serde::{Deserialize, Serialize}; use servo_config::{opts, pref}; @@ -163,12 +168,12 @@ use webgpu_traits::{WebGPU, WebGPURequest}; use webrender::RenderApi; use webrender::RenderApiSender; use webrender_api::{DocumentId, ImageKey}; -use webrender_traits::WebrenderExternalImageRegistry; use crate::browsingcontext::{ AllBrowsingContextsIterator, BrowsingContext, FullyActiveBrowsingContextsIterator, NewBrowsingContextInfo, }; +use crate::constellation_webview::ConstellationWebView; use crate::event_loop::EventLoop; use crate::pipeline::{InitialPipelineState, Pipeline}; use crate::process_manager::ProcessManager; @@ -201,9 +206,6 @@ enum TransferState { /// While a completion failed, another global requested to complete the transfer. /// We are still buffering messages, and awaiting the return of the buffer from the global who failed. CompletionRequested(MessagePortRouterId, VecDeque), - /// The entangled port has been removed while the port was in-transfer, - /// the current port should be removed as well once it is managed again. - EntangledRemoved, } #[derive(Debug)] @@ -229,18 +231,6 @@ struct WebrenderWGPU { wgpu_image_map: WGPUImageMap, } -/// Servo supports multiple top-level browsing contexts or “webviewsâ€, so `Constellation` needs to -/// store webview-specific data for bookkeeping. -struct WebView { - /// The currently focused browsing context in this webview for key events. - /// The focused pipeline is the current entry of the focused browsing - /// context. - focused_browsing_context_id: BrowsingContextId, - - /// The joint session history for this webview. - session_history: JointSessionHistory, -} - /// A browsing context group. /// /// @@ -324,7 +314,7 @@ pub struct Constellation { compositor_proxy: CompositorProxy, /// Bookkeeping data for all webviews in the constellation. - webviews: WebViewManager, + webviews: WebViewManager, /// Channels for the constellation to send messages to the public /// resource-related threads. There are two groups of resource threads: one @@ -372,6 +362,7 @@ pub struct Constellation { mem_profiler_chan: mem::ProfilerChan, /// A single WebRender document the constellation operates on. + #[cfg(feature = "webgpu")] webrender_document: DocumentId, /// Webrender related objects required by WebGPU threads @@ -531,6 +522,8 @@ pub struct InitialConstellationState { struct WebDriverData { load_channel: Option<(PipelineId, IpcSender)>, resize_channel: Option>>, + // Forward responses from the script thread to the webdriver server. + input_command_response_sender: Option>, } impl WebDriverData { @@ -538,6 +531,7 @@ impl WebDriverData { WebDriverData { load_channel: None, resize_channel: None, + input_command_response_sender: None, } } } @@ -716,6 +710,7 @@ where phantom: PhantomData, webdriver: WebDriverData::new(), document_states: HashMap::new(), + #[cfg(feature = "webgpu")] webrender_document: state.webrender_document, #[cfg(feature = "webgpu")] webrender_wgpu, @@ -890,6 +885,16 @@ where if self.shutting_down { return; } + + let Some(theme) = self + .webviews + .get(webview_id) + .map(ConstellationWebView::theme) + else { + warn!("Tried to create Pipeline for uknown WebViewId: {webview_id:?}"); + return; + }; + debug!( "{}: Creating new pipeline in {}", pipeline_id, browsing_context_id @@ -968,10 +973,10 @@ where time_profiler_chan: self.time_profiler_chan.clone(), mem_profiler_chan: self.mem_profiler_chan.clone(), viewport_details: initial_viewport_details, + theme, event_loop, load_data, prev_throttled: throttled, - webrender_document: self.webrender_document, webgl_chan: self .webgl_threads .as_ref() @@ -1043,6 +1048,44 @@ where } } + /// Enumerate the specified browsing context's ancestor pipelines up to + /// the top-level pipeline. + fn ancestor_pipelines_of_browsing_context_iter( + &self, + browsing_context_id: BrowsingContextId, + ) -> impl Iterator + '_ { + let mut state: Option = self + .browsing_contexts + .get(&browsing_context_id) + .and_then(|browsing_context| browsing_context.parent_pipeline_id); + std::iter::from_fn(move || { + if let Some(pipeline_id) = state { + let pipeline = self.pipelines.get(&pipeline_id)?; + let browsing_context = self.browsing_contexts.get(&pipeline.browsing_context_id)?; + state = browsing_context.parent_pipeline_id; + Some(pipeline) + } else { + None + } + }) + } + + /// Enumerate the specified browsing context's ancestor-or-self pipelines up + /// to the top-level pipeline. + fn ancestor_or_self_pipelines_of_browsing_context_iter( + &self, + browsing_context_id: BrowsingContextId, + ) -> impl Iterator + '_ { + let this_pipeline = self + .browsing_contexts + .get(&browsing_context_id) + .map(|browsing_context| browsing_context.pipeline_id) + .and_then(|pipeline_id| self.pipelines.get(&pipeline_id)); + this_pipeline + .into_iter() + .chain(self.ancestor_pipelines_of_browsing_context_iter(browsing_context_id)) + } + /// Create a new browsing context and update the internal bookkeeping. #[allow(clippy::too_many_arguments)] fn new_browsing_context( @@ -1394,11 +1437,11 @@ where size_type, ); }, - EmbedderToConstellationMessage::ThemeChange(theme) => { - self.handle_theme_change(theme); + EmbedderToConstellationMessage::ThemeChange(webview_id, theme) => { + self.handle_theme_change(webview_id, theme); }, - EmbedderToConstellationMessage::TickAnimation(pipeline_id, tick_type) => { - self.handle_tick_animation(pipeline_id, tick_type) + EmbedderToConstellationMessage::TickAnimation(webview_ids) => { + self.handle_tick_animation(webview_ids) }, EmbedderToConstellationMessage::WebDriverCommand(command) => { self.handle_webdriver_msg(command); @@ -1439,6 +1482,55 @@ where EmbedderToConstellationMessage::PaintMetric(pipeline_id, paint_metric_event) => { self.handle_paint_metric(pipeline_id, paint_metric_event); }, + EmbedderToConstellationMessage::EvaluateJavaScript( + webview_id, + evaluation_id, + script, + ) => { + self.handle_evaluate_javascript(webview_id, evaluation_id, script); + }, + EmbedderToConstellationMessage::CreateMemoryReport(sender) => { + self.mem_profiler_chan.send(ProfilerMsg::Report(sender)); + }, + } + } + + #[cfg_attr( + feature = "tracing", + tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") + )] + fn handle_evaluate_javascript( + &mut self, + webview_id: WebViewId, + evaluation_id: JavaScriptEvaluationId, + script: String, + ) { + let browsing_context_id = BrowsingContextId::from(webview_id); + let Some(pipeline) = self + .browsing_contexts + .get(&browsing_context_id) + .and_then(|browsing_context| self.pipelines.get(&browsing_context.pipeline_id)) + else { + self.handle_finish_javascript_evaluation( + evaluation_id, + Err(JavaScriptEvaluationError::InternalError), + ); + return; + }; + + if pipeline + .event_loop + .send(ScriptThreadMessage::EvaluateJavaScript( + pipeline.id, + evaluation_id, + script, + )) + .is_err() + { + self.handle_finish_javascript_evaluation( + evaluation_id, + Err(JavaScriptEvaluationError::InternalError), + ); } } @@ -1486,12 +1578,12 @@ where ScriptToConstellationMessage::NewMessagePort(router_id, port_id) => { self.handle_new_messageport(router_id, port_id); }, - ScriptToConstellationMessage::RemoveMessagePort(port_id) => { - self.handle_remove_messageport(port_id); - }, ScriptToConstellationMessage::EntanglePorts(port1, port2) => { self.handle_entangle_messageports(port1, port2); }, + ScriptToConstellationMessage::DisentanglePorts(port1, port2) => { + self.handle_disentangle_messageports(port1, port2); + }, ScriptToConstellationMessage::NewBroadcastChannelRouter( router_id, response_sender, @@ -1621,8 +1713,15 @@ where data, ); }, - ScriptToConstellationMessage::Focus => { - self.handle_focus_msg(source_pipeline_id); + ScriptToConstellationMessage::Focus(focused_child_browsing_context_id, sequence) => { + self.handle_focus_msg( + source_pipeline_id, + focused_child_browsing_context_id, + sequence, + ); + }, + ScriptToConstellationMessage::FocusRemoteDocument(focused_browsing_context_id) => { + self.handle_focus_remote_document_msg(focused_browsing_context_id); }, ScriptToConstellationMessage::SetThrottledComplete(throttled) => { self.handle_set_throttled_complete(source_pipeline_id, throttled); @@ -1772,6 +1871,21 @@ where self.mem_profiler_chan .send(mem::ProfilerMsg::Report(sender)); }, + ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, result) => { + self.handle_finish_javascript_evaluation(evaluation_id, result) + }, + ScriptToConstellationMessage::WebDriverInputComplete(msg_id) => { + if let Some(ref reply_sender) = self.webdriver.input_command_response_sender { + reply_sender + .send(WebDriverCommandResponse { id: msg_id }) + .unwrap_or_else(|_| { + warn!("Failed to send WebDriverInputComplete {:?}", msg_id); + self.webdriver.input_command_response_sender = None; + }); + } else { + warn!("No WebDriver input_command_response_sender"); + } + }, } } @@ -2072,17 +2186,6 @@ where Entry::Occupied(entry) => entry, }; match entry.get().state { - TransferState::EntangledRemoved => { - // If the entangled port has been removed while this one was in-transfer, - // remove it now. - if let Some(ipc_sender) = self.message_port_routers.get(&router_id) { - let _ = ipc_sender.send(MessagePortMsg::RemoveMessagePort(port_id)); - } else { - warn!("No message-port sender for {:?}", router_id); - } - entry.remove_entry(); - continue; - }, TransferState::CompletionInProgress(expected_router_id) => { // Here, the transfer was normally completed. @@ -2106,9 +2209,9 @@ where fn handle_message_port_transfer_failed( &mut self, - ports: HashMap>, + ports: HashMap, ) { - for (port_id, mut previous_buffer) in ports.into_iter() { + for (port_id, mut transfer_info) in ports.into_iter() { let entry = match self.message_ports.remove(&port_id) { None => { warn!( @@ -2120,11 +2223,6 @@ where Some(entry) => entry, }; let new_info = match entry.state { - TransferState::EntangledRemoved => { - // If the entangled port has been removed while this one was in-transfer, - // just drop it. - continue; - }, TransferState::CompletionFailed(mut current_buffer) => { // The transfer failed, // and now the global has returned us the buffer we previously sent. @@ -2132,7 +2230,7 @@ where // Tasks in the previous buffer are older, // hence need to be added to the front of the current one. - while let Some(task) = previous_buffer.pop_back() { + while let Some(task) = transfer_info.port_message_queue.pop_back() { current_buffer.push_front(task); } // Update the state to transfer-in-progress. @@ -2151,7 +2249,7 @@ where // Tasks in the previous buffer are older, // hence need to be added to the front of the current one. - while let Some(task) = previous_buffer.pop_back() { + while let Some(task) = transfer_info.port_message_queue.pop_back() { current_buffer.push_front(task); } // Forward the buffered message-queue to complete the current transfer. @@ -2159,7 +2257,10 @@ where if ipc_sender .send(MessagePortMsg::CompletePendingTransfer( port_id, - current_buffer, + PortTransferInfo { + port_message_queue: current_buffer, + disentangled: entry.entangled_with.is_none(), + }, )) .is_err() { @@ -2206,18 +2307,14 @@ where Some(entry) => entry, }; let new_info = match entry.state { - TransferState::EntangledRemoved => { - // If the entangled port has been removed while this one was in-transfer, - // remove it now. - if let Some(ipc_sender) = self.message_port_routers.get(&router_id) { - let _ = ipc_sender.send(MessagePortMsg::RemoveMessagePort(port_id)); - } else { - warn!("No message-port sender for {:?}", router_id); - } - continue; - }, TransferState::TransferInProgress(buffer) => { - response.insert(port_id, buffer); + response.insert( + port_id, + PortTransferInfo { + port_message_queue: buffer, + disentangled: entry.entangled_with.is_none(), + }, + ); // If the port was in transfer, and a global is requesting completion, // we note the start of the completion. @@ -2296,10 +2393,6 @@ where TransferState::TransferInProgress(queue) => queue.push_back(task), TransferState::CompletionFailed(queue) => queue.push_back(task), TransferState::CompletionRequested(_, queue) => queue.push_back(task), - TransferState::EntangledRemoved => warn!( - "Messageport received a message, but entangled has alread been removed {:?}", - port_id - ), } } @@ -2361,59 +2454,6 @@ where } } - #[cfg_attr( - feature = "tracing", - tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") - )] - fn handle_remove_messageport(&mut self, port_id: MessagePortId) { - let entangled = match self.message_ports.remove(&port_id) { - Some(info) => info.entangled_with, - None => { - return warn!( - "Constellation asked to remove unknown messageport {:?}", - port_id - ); - }, - }; - let entangled_id = match entangled { - Some(id) => id, - None => return, - }; - let info = match self.message_ports.get_mut(&entangled_id) { - Some(info) => info, - None => { - return warn!( - "Constellation asked to remove unknown entangled messageport {:?}", - entangled_id - ); - }, - }; - let router_id = match info.state { - TransferState::EntangledRemoved => { - return warn!( - "Constellation asked to remove entangled messageport by a port that was already removed {:?}", - port_id - ); - }, - TransferState::TransferInProgress(_) | - TransferState::CompletionInProgress(_) | - TransferState::CompletionFailed(_) | - TransferState::CompletionRequested(_, _) => { - // Note: since the port is in-transer, we don't have a router to send it a message - // to let it know that its entangled port has been removed. - // Hence we mark it so that it will be messaged and removed once the transfer completes. - info.state = TransferState::EntangledRemoved; - return; - }, - TransferState::Managed(router_id) => router_id, - }; - if let Some(sender) = self.message_port_routers.get(&router_id) { - let _ = sender.send(MessagePortMsg::RemoveMessagePort(entangled_id)); - } else { - warn!("No message-port sender for {:?}", router_id); - } - } - #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") @@ -2437,6 +2477,57 @@ where } } + #[cfg_attr( + feature = "tracing", + tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") + )] + /// + fn handle_disentangle_messageports( + &mut self, + port1: MessagePortId, + port2: Option, + ) { + // Disentangle initiatorPort and otherPort, + // so that they are no longer entangled or associated with each other. + // Note: If `port2` is some, then this is the first message + // and `port1` is the initiatorPort, `port2` is the otherPort. + // We can immediately remove the initiator. + let _ = self.message_ports.remove(&port1); + + // Note: the none case is when otherPort sent this message + // in response to completing its own local disentanglement. + let Some(port2) = port2 else { + return; + }; + + // Start disentanglement of the other port. + if let Some(info) = self.message_ports.get_mut(&port2) { + info.entangled_with = None; + match &mut info.state { + TransferState::Managed(router_id) | + TransferState::CompletionInProgress(router_id) => { + // We try to disentangle the other port now, + // and if it has been transfered out by the time the message is received, + // it will be ignored, + // and disentanglement will be completed as part of the transfer. + if let Some(ipc_sender) = self.message_port_routers.get(router_id) { + let _ = ipc_sender.send(MessagePortMsg::CompleteDisentanglement(port2)); + } else { + warn!("No message-port sender for {:?}", router_id); + } + }, + _ => { + // Note: the port is in transfer, disentanglement will complete along with it. + }, + } + } else { + warn!( + "Constellation asked to disentangle unknown messageport: {:?}", + port2 + ); + } + } + /// /// and /// @@ -2671,7 +2762,11 @@ where } debug!("Exiting Canvas Paint thread."); - if let Err(e) = self.canvas_sender.send(ConstellationCanvasMsg::Exit) { + let (canvas_exit_sender, canvas_exit_receiver) = unbounded(); + if let Err(e) = self + .canvas_sender + .send(ConstellationCanvasMsg::Exit(canvas_exit_sender)) + { warn!("Exit Canvas Paint thread failed ({})", e); } @@ -2713,6 +2808,10 @@ where debug!("Exiting GLPlayer thread."); WindowGLContext::get().exit(); + // Wait for the canvas thread to exit before shutting down the font service, as + // canvas might still be using the system font service before shutting down. + let _ = canvas_exit_receiver.recv(); + debug!("Exiting the system font service thread."); self.system_font_service.exit(); @@ -3055,13 +3154,8 @@ where // Register this new top-level browsing context id as a webview and set // its focused browsing context to be itself. - self.webviews.add( - webview_id, - WebView { - focused_browsing_context_id: browsing_context_id, - session_history: JointSessionHistory::new(), - }, - ); + self.webviews + .add(webview_id, ConstellationWebView::new(browsing_context_id)); // https://html.spec.whatwg.org/multipage/#creating-a-new-browsing-context-group let mut new_bc_group: BrowsingContextGroup = Default::default(); @@ -3156,6 +3250,22 @@ where } } + #[cfg_attr( + feature = "tracing", + tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") + )] + fn handle_finish_javascript_evaluation( + &mut self, + evaluation_id: JavaScriptEvaluationId, + result: Result, + ) { + self.embedder_proxy + .send(EmbedderMsg::FinishJavaScriptEvaluation( + evaluation_id, + result, + )); + } + #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") @@ -3451,10 +3561,7 @@ where self.pipelines.insert(new_pipeline_id, pipeline); self.webviews.add( new_webview_id, - WebView { - focused_browsing_context_id: new_browsing_context_id, - session_history: JointSessionHistory::new(), - }, + ConstellationWebView::new(new_browsing_context_id), ); // https://html.spec.whatwg.org/multipage/#bcg-append @@ -3527,15 +3634,24 @@ where feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] - fn handle_tick_animation(&mut self, pipeline_id: PipelineId, tick_type: AnimationTickType) { - let pipeline = match self.pipelines.get(&pipeline_id) { - Some(pipeline) => pipeline, - None => return warn!("{}: Got script tick after closure", pipeline_id), - }; + fn handle_tick_animation(&mut self, webview_ids: Vec) { + let mut animating_event_loops = HashSet::new(); - let message = ScriptThreadMessage::TickAllAnimations(pipeline_id, tick_type); - if let Err(e) = pipeline.event_loop.send(message) { - self.handle_send_error(pipeline_id, e); + for webview_id in webview_ids.iter() { + for browsing_context in self.fully_active_browsing_contexts_iter(*webview_id) { + let Some(pipeline) = self.pipelines.get(&browsing_context.pipeline_id) else { + continue; + }; + animating_event_loops.insert(pipeline.event_loop.clone()); + } + } + + for event_loop in animating_event_loops { + // No error handling here. It's unclear what to do when this fails as the error isn't associated + // with a particular pipeline. In addition, the danger of not progressing animations is pretty + // low, so it's probably safe to ignore this error and handle the crashed ScriptThread on + // some other message. + let _ = event_loop.send(ScriptThreadMessage::TickAllAnimations(webview_ids.clone())); } } @@ -3735,8 +3851,8 @@ where fn handle_load_complete_msg(&mut self, webview_id: WebViewId, pipeline_id: PipelineId) { let mut webdriver_reset = false; if let Some((expected_pipeline_id, ref reply_chan)) = self.webdriver.load_channel { - debug!("Sending load for {:?} to WebDriver", expected_pipeline_id); if expected_pipeline_id == pipeline_id { + debug!("Sending load for {:?} to WebDriver", expected_pipeline_id); let _ = reply_chan.send(WebDriverLoadStatus::Complete); webdriver_reset = true; } @@ -4061,6 +4177,7 @@ where } new_pipeline.set_throttled(false); + self.notify_focus_state(new_pipeline_id); } self.update_activity(old_pipeline_id); @@ -4266,22 +4383,96 @@ where feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] - fn handle_focus_msg(&mut self, pipeline_id: PipelineId) { - let (browsing_context_id, webview_id) = match self.pipelines.get(&pipeline_id) { - Some(pipeline) => (pipeline.browsing_context_id, pipeline.webview_id), + fn handle_focus_msg( + &mut self, + pipeline_id: PipelineId, + focused_child_browsing_context_id: Option, + sequence: FocusSequenceNumber, + ) { + let (browsing_context_id, webview_id) = match self.pipelines.get_mut(&pipeline_id) { + Some(pipeline) => { + pipeline.focus_sequence = sequence; + (pipeline.browsing_context_id, pipeline.webview_id) + }, None => return warn!("{}: Focus parent after closure", pipeline_id), }; + // Ignore if the pipeline isn't fully active. + if self.get_activity(pipeline_id) != DocumentActivity::FullyActive { + debug!( + "Ignoring the focus request because pipeline {} is not \ + fully active", + pipeline_id + ); + return; + } + // Focus the top-level browsing context. self.webviews.focus(webview_id); self.embedder_proxy .send(EmbedderMsg::WebViewFocused(webview_id)); + // If a container with a non-null nested browsing context is focused, + // the nested browsing context's active document becomes the focused + // area of the top-level browsing context instead. + let focused_browsing_context_id = + focused_child_browsing_context_id.unwrap_or(browsing_context_id); + + // Send focus messages to the affected pipelines, except + // `pipeline_id`, which has already its local focus state + // updated. + self.focus_browsing_context(Some(pipeline_id), focused_browsing_context_id); + } + + fn handle_focus_remote_document_msg(&mut self, focused_browsing_context_id: BrowsingContextId) { + let pipeline_id = match self.browsing_contexts.get(&focused_browsing_context_id) { + Some(browsing_context) => browsing_context.pipeline_id, + None => return warn!("Browsing context {} not found", focused_browsing_context_id), + }; + + // Ignore if its active document isn't fully active. + if self.get_activity(pipeline_id) != DocumentActivity::FullyActive { + debug!( + "Ignoring the remote focus request because pipeline {} of \ + browsing context {} is not fully active", + pipeline_id, focused_browsing_context_id, + ); + return; + } + + self.focus_browsing_context(None, focused_browsing_context_id); + } + + /// Perform [the focusing steps][1] for the active document of + /// `focused_browsing_context_id`. + /// + /// If `initiator_pipeline_id` is specified, this method avoids sending + /// a message to `initiator_pipeline_id`, assuming its local focus state has + /// already been updated. This is necessary for performing the focusing + /// steps for an object that is not the document itself but something that + /// belongs to the document. + /// + /// [1]: https://html.spec.whatwg.org/multipage/#focusing-steps + #[cfg_attr( + feature = "tracing", + tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") + )] + fn focus_browsing_context( + &mut self, + initiator_pipeline_id: Option, + focused_browsing_context_id: BrowsingContextId, + ) { + let webview_id = match self.browsing_contexts.get(&focused_browsing_context_id) { + Some(browsing_context) => browsing_context.top_level_id, + None => return warn!("Browsing context {} not found", focused_browsing_context_id), + }; + // Update the webview’s focused browsing context. - match self.webviews.get_mut(webview_id) { - Some(webview) => { - webview.focused_browsing_context_id = browsing_context_id; - }, + let old_focused_browsing_context_id = match self.webviews.get_mut(webview_id) { + Some(browser) => replace( + &mut browser.focused_browsing_context_id, + focused_browsing_context_id, + ), None => { return warn!( "{}: Browsing context for focus msg does not exist", @@ -4290,42 +4481,133 @@ where }, }; - // Focus parent iframes recursively - self.focus_parent_pipeline(browsing_context_id); - } + // The following part is similar to [the focus update steps][1] except + // that only `Document`s in the given focus chains are considered. It's + // ultimately up to the script threads to fire focus events at the + // affected objects. + // + // [1]: https://html.spec.whatwg.org/multipage/#focus-update-steps + let mut old_focus_chain_pipelines: Vec<&Pipeline> = self + .ancestor_or_self_pipelines_of_browsing_context_iter(old_focused_browsing_context_id) + .collect(); + let mut new_focus_chain_pipelines: Vec<&Pipeline> = self + .ancestor_or_self_pipelines_of_browsing_context_iter(focused_browsing_context_id) + .collect(); - #[cfg_attr( - feature = "tracing", - tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") - )] - fn focus_parent_pipeline(&mut self, browsing_context_id: BrowsingContextId) { - let parent_pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { - Some(ctx) => ctx.parent_pipeline_id, - None => { - return warn!("{}: Focus parent after closure", browsing_context_id); - }, - }; - let parent_pipeline_id = match parent_pipeline_id { - Some(parent_id) => parent_id, - None => { - return debug!("{}: Focus has no parent", browsing_context_id); - }, - }; + debug!( + "old_focus_chain_pipelines = {:?}", + old_focus_chain_pipelines + .iter() + .map(|p| p.id.to_string()) + .collect::>() + ); + debug!( + "new_focus_chain_pipelines = {:?}", + new_focus_chain_pipelines + .iter() + .map(|p| p.id.to_string()) + .collect::>() + ); - // Send a message to the parent of the provided browsing context (if it - // exists) telling it to mark the iframe element as focused. - let msg = ScriptThreadMessage::FocusIFrame(parent_pipeline_id, browsing_context_id); - let (result, parent_browsing_context_id) = match self.pipelines.get(&parent_pipeline_id) { - Some(pipeline) => { - let result = pipeline.event_loop.send(msg); - (result, pipeline.browsing_context_id) + // At least the last entries should match. Otherwise something is wrong, + // and we don't want to proceed and crash the top-level pipeline by + // sending an impossible `Unfocus` message to it. + match ( + &old_focus_chain_pipelines[..], + &new_focus_chain_pipelines[..], + ) { + ([.., p1], [.., p2]) if p1.id == p2.id => {}, + _ => { + warn!("Aborting the focus operation - focus chain sanity check failed"); + return; }, - None => return warn!("{}: Focus after closure", parent_pipeline_id), - }; - if let Err(e) = result { - self.handle_send_error(parent_pipeline_id, e); } - self.focus_parent_pipeline(parent_browsing_context_id); + + // > If the last entry in `old chain` and the last entry in `new chain` + // > are the same, pop the last entry from `old chain` and the last + // > entry from `new chain` and redo this step. + let mut first_common_pipeline_in_chain = None; + while let ([.., p1], [.., p2]) = ( + &old_focus_chain_pipelines[..], + &new_focus_chain_pipelines[..], + ) { + if p1.id != p2.id { + break; + } + old_focus_chain_pipelines.pop(); + first_common_pipeline_in_chain = new_focus_chain_pipelines.pop(); + } + + let mut send_errors = Vec::new(); + + // > For each entry `entry` in `old chain`, in order, run these + // > substeps: [...] + for &pipeline in old_focus_chain_pipelines.iter() { + if Some(pipeline.id) != initiator_pipeline_id { + let msg = ScriptThreadMessage::Unfocus(pipeline.id, pipeline.focus_sequence); + trace!("Sending {:?} to {}", msg, pipeline.id); + if let Err(e) = pipeline.event_loop.send(msg) { + send_errors.push((pipeline.id, e)); + } + } else { + trace!( + "Not notifying {} - it's the initiator of this focus operation", + pipeline.id + ); + } + } + + // > For each entry entry in `new chain`, in reverse order, run these + // > substeps: [...] + let mut child_browsing_context_id = None; + for &pipeline in new_focus_chain_pipelines.iter().rev() { + // Don't send a message to the browsing context that initiated this + // focus operation. It already knows that it has gotten focus. + if Some(pipeline.id) != initiator_pipeline_id { + let msg = if let Some(child_browsing_context_id) = child_browsing_context_id { + // Focus the container element of `child_browsing_context_id`. + ScriptThreadMessage::FocusIFrame( + pipeline.id, + child_browsing_context_id, + pipeline.focus_sequence, + ) + } else { + // Focus the document. + ScriptThreadMessage::FocusDocument(pipeline.id, pipeline.focus_sequence) + }; + trace!("Sending {:?} to {}", msg, pipeline.id); + if let Err(e) = pipeline.event_loop.send(msg) { + send_errors.push((pipeline.id, e)); + } + } else { + trace!( + "Not notifying {} - it's the initiator of this focus operation", + pipeline.id + ); + } + child_browsing_context_id = Some(pipeline.browsing_context_id); + } + + if let (Some(pipeline), Some(child_browsing_context_id)) = + (first_common_pipeline_in_chain, child_browsing_context_id) + { + if Some(pipeline.id) != initiator_pipeline_id { + // Focus the container element of `child_browsing_context_id`. + let msg = ScriptThreadMessage::FocusIFrame( + pipeline.id, + child_browsing_context_id, + pipeline.focus_sequence, + ); + trace!("Sending {:?} to {}", msg, pipeline.id); + if let Err(e) = pipeline.event_loop.send(msg) { + send_errors.push((pipeline.id, e)); + } + } + } + + for (pipeline_id, e) in send_errors { + self.handle_send_error(pipeline_id, e); + } } #[cfg_attr( @@ -4494,6 +4776,7 @@ where NavigationHistoryBehavior::Replace, ); }, + // TODO: This should use the ScriptThreadMessage::EvaluateJavaScript command WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd) => { let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { Some(browsing_context) => browsing_context.pipeline_id, @@ -4572,7 +4855,11 @@ where mouse_button, x, y, + msg_id, + response_sender, ) => { + self.webdriver.input_command_response_sender = Some(response_sender); + self.compositor_proxy .send(CompositorMsg::WebDriverMouseButtonEvent( webview_id, @@ -4580,15 +4867,29 @@ where mouse_button, x, y, + msg_id, )); }, - WebDriverCommandMsg::MouseMoveAction(webview_id, x, y) => { + WebDriverCommandMsg::MouseMoveAction(webview_id, x, y, msg_id, response_sender) => { + self.webdriver.input_command_response_sender = Some(response_sender); + self.compositor_proxy - .send(CompositorMsg::WebDriverMouseMoveEvent(webview_id, x, y)); + .send(CompositorMsg::WebDriverMouseMoveEvent( + webview_id, x, y, msg_id, + )); }, - WebDriverCommandMsg::TakeScreenshot(_, rect, response_sender) => { + WebDriverCommandMsg::WheelScrollAction(webview, x, y, delta_x, delta_y) => { self.compositor_proxy - .send(CompositorMsg::CreatePng(rect, response_sender)); + .send(CompositorMsg::WebDriverWheelScrollEvent( + webview, x, y, delta_x, delta_y, + )); + }, + WebDriverCommandMsg::TakeScreenshot(webview_id, rect, response_sender) => { + self.compositor_proxy.send(CompositorMsg::CreatePng( + webview_id, + rect, + response_sender, + )); }, } } @@ -4917,10 +5218,42 @@ where self.trim_history(top_level_id); } + self.notify_focus_state(change.new_pipeline_id); + self.notify_history_changed(change.webview_id); self.update_webview_in_compositor(change.webview_id); } + /// Update the focus state of the specified pipeline that recently became + /// active (thus doesn't have a focused container element) and may have + /// out-dated information. + fn notify_focus_state(&mut self, pipeline_id: PipelineId) { + let pipeline = match self.pipelines.get(&pipeline_id) { + Some(pipeline) => pipeline, + None => return warn!("Pipeline {} is closed", pipeline_id), + }; + + let is_focused = match self.webviews.get(pipeline.webview_id) { + Some(webview) => webview.focused_browsing_context_id == pipeline.browsing_context_id, + None => { + return warn!( + "Pipeline {}'s top-level browsing context {} is closed", + pipeline_id, pipeline.webview_id + ); + }, + }; + + // If the browsing context is focused, focus the document + let msg = if is_focused { + ScriptThreadMessage::FocusDocument(pipeline_id, pipeline.focus_sequence) + } else { + ScriptThreadMessage::Unfocus(pipeline_id, pipeline.focus_sequence) + }; + if let Err(e) = pipeline.event_loop.send(msg) { + self.handle_send_error(pipeline_id, e); + } + } + #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") @@ -5294,18 +5627,31 @@ where } } - /// Handle theme change events from the embedder and forward them to the script thread + /// Handle theme change events from the embedder and forward them to all appropriate `ScriptThread`s. #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] - fn handle_theme_change(&mut self, theme: Theme) { + fn handle_theme_change(&mut self, webview_id: WebViewId, theme: Theme) { + let Some(webview) = self.webviews.get_mut(webview_id) else { + warn!("Received theme change request for uknown WebViewId: {webview_id:?}"); + return; + }; + if !webview.set_theme(theme) { + return; + } + for pipeline in self.pipelines.values() { - let msg = ScriptThreadMessage::ThemeChange(pipeline.id, theme); - if let Err(err) = pipeline.event_loop.send(msg) { + if pipeline.webview_id != webview_id { + continue; + } + if let Err(error) = pipeline + .event_loop + .send(ScriptThreadMessage::ThemeChange(pipeline.id, theme)) + { warn!( - "{}: Failed to send theme change event to pipeline ({:?}).", - pipeline.id, err + "{}: Failed to send theme change event to pipeline ({error:?}).", + pipeline.id, ); } } @@ -5370,7 +5716,29 @@ where None => { warn!("{parent_pipeline_id}: Child closed after parent"); }, - Some(parent_pipeline) => parent_pipeline.remove_child(browsing_context_id), + Some(parent_pipeline) => { + parent_pipeline.remove_child(browsing_context_id); + + // If `browsing_context_id` has focus, focus the parent + // browsing context + if let Some(webview) = self.webviews.get_mut(browsing_context.top_level_id) { + if webview.focused_browsing_context_id == browsing_context_id { + trace!( + "About-to-be-closed browsing context {} is currently focused, so \ + focusing its parent {}", + browsing_context_id, parent_pipeline.browsing_context_id + ); + webview.focused_browsing_context_id = + parent_pipeline.browsing_context_id; + } + } else { + warn!( + "Browsing context {} contains a reference to \ + a non-existent top-level browsing context {}", + browsing_context_id, browsing_context.top_level_id + ); + } + }, }; } debug!("{}: Closed", browsing_context_id); diff --git a/components/constellation/constellation_webview.rs b/components/constellation/constellation_webview.rs new file mode 100644 index 00000000000..16c19e629fb --- /dev/null +++ b/components/constellation/constellation_webview.rs @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use base::id::BrowsingContextId; +use embedder_traits::Theme; + +use crate::session_history::JointSessionHistory; + +/// The `Constellation`'s view of a `WebView` in the embedding layer. This tracks all of the +/// `Constellation` state for this `WebView`. +pub(crate) struct ConstellationWebView { + /// The currently focused browsing context in this webview for key events. + /// The focused pipeline is the current entry of the focused browsing + /// context. + pub focused_browsing_context_id: BrowsingContextId, + + /// The joint session history for this webview. + pub session_history: JointSessionHistory, + + /// The [`Theme`] that this [`ConstellationWebView`] uses. This is communicated to all + /// `ScriptThread`s so that they know how to render the contents of a particular `WebView. + theme: Theme, +} + +impl ConstellationWebView { + pub(crate) fn new(focused_browsing_context_id: BrowsingContextId) -> Self { + Self { + focused_browsing_context_id, + session_history: JointSessionHistory::new(), + theme: Theme::Light, + } + } + + /// Set the [`Theme`] on this [`ConstellationWebView`] returning true if the theme changed. + pub(crate) fn set_theme(&mut self, new_theme: Theme) -> bool { + let old_theme = std::mem::replace(&mut self.theme, new_theme); + old_theme != self.theme + } + + /// Get the [`Theme`] of this [`ConstellationWebView`]. + pub(crate) fn theme(&self) -> Theme { + self.theme + } +} diff --git a/components/constellation/event_loop.rs b/components/constellation/event_loop.rs index 362960eba64..46542e7212f 100644 --- a/components/constellation/event_loop.rs +++ b/components/constellation/event_loop.rs @@ -6,17 +6,36 @@ //! view of a script thread. When an `EventLoop` is dropped, an `ExitScriptThread` //! message is sent to the script thread, asking it to shut down. +use std::hash::Hash; use std::marker::PhantomData; use std::rc::Rc; +use std::sync::atomic::{AtomicUsize, Ordering}; use ipc_channel::Error; use ipc_channel::ipc::IpcSender; use script_traits::ScriptThreadMessage; +static CURRENT_EVENT_LOOP_ID: AtomicUsize = AtomicUsize::new(0); + /// pub struct EventLoop { script_chan: IpcSender, dont_send_or_sync: PhantomData>, + id: usize, +} + +impl PartialEq for EventLoop { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for EventLoop {} + +impl Hash for EventLoop { + fn hash(&self, state: &mut H) { + self.id.hash(state); + } } impl Drop for EventLoop { @@ -28,9 +47,11 @@ impl Drop for EventLoop { impl EventLoop { /// Create a new event loop from the channel to its script thread. pub fn new(script_chan: IpcSender) -> Rc { + let id = CURRENT_EVENT_LOOP_ID.fetch_add(1, Ordering::Relaxed); Rc::new(EventLoop { script_chan, dont_send_or_sync: PhantomData, + id, }) } diff --git a/components/constellation/lib.rs b/components/constellation/lib.rs index c24543743b7..8927888aa93 100644 --- a/components/constellation/lib.rs +++ b/components/constellation/lib.rs @@ -9,6 +9,7 @@ mod tracing; mod browsingcontext; mod constellation; +mod constellation_webview; mod event_loop; mod logging; mod pipeline; diff --git a/components/constellation/logging.rs b/components/constellation/logging.rs index 264287524d3..97ff0202bd8 100644 --- a/components/constellation/logging.rs +++ b/components/constellation/logging.rs @@ -12,11 +12,13 @@ use std::thread; use backtrace::Backtrace; use base::id::WebViewId; -use constellation_traits::{EmbedderToConstellationMessage, LogEntry}; +use constellation_traits::{ + EmbedderToConstellationMessage, LogEntry, ScriptToConstellationChan, + ScriptToConstellationMessage, +}; use crossbeam_channel::Sender; use log::{Level, LevelFilter, Log, Metadata, Record}; use parking_lot::ReentrantMutex; -use script_traits::{ScriptToConstellationChan, ScriptToConstellationMessage}; /// A logger directed at the constellation from content processes /// #[derive(Clone)] diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index fc988f6e408..a0bf106ea06 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -18,11 +18,14 @@ use base::id::{ #[cfg(feature = "bluetooth")] use bluetooth_traits::BluetoothRequest; use canvas_traits::webgl::WebGLPipeline; -use compositing_traits::{CompositionPipeline, CompositorMsg, CompositorProxy}; +use compositing_traits::{ + CompositionPipeline, CompositorMsg, CompositorProxy, CrossProcessCompositorApi, +}; +use constellation_traits::{LoadData, SWManagerMsg, ScriptToConstellationChan}; use crossbeam_channel::{Sender, unbounded}; use devtools_traits::{DevtoolsControlMsg, ScriptToDevtoolsControlMsg}; -use embedder_traits::ViewportDetails; use embedder_traits::user_content_manager::UserContentManager; +use embedder_traits::{AnimationState, FocusSequenceNumber, Theme, ViewportDetails}; use fonts::{SystemFontServiceProxy, SystemFontServiceProxySender}; use ipc_channel::Error; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; @@ -37,15 +40,13 @@ use profile_traits::mem::{ProfilerMsg, Reporter}; use profile_traits::{mem as profile_mem, time}; use script_layout_interface::{LayoutFactory, ScriptThreadFactory}; use script_traits::{ - AnimationState, DiscardBrowsingContext, DocumentActivity, InitialScriptState, LoadData, - NewLayoutInfo, SWManagerMsg, ScriptThreadMessage, ScriptToConstellationChan, + DiscardBrowsingContext, DocumentActivity, InitialScriptState, NewLayoutInfo, + ScriptThreadMessage, }; use serde::{Deserialize, Serialize}; use servo_config::opts::{self, Opts}; use servo_config::prefs::{self, Preferences}; use servo_url::ServoUrl; -use webrender_api::DocumentId; -use webrender_traits::CrossProcessCompositorApi; use crate::event_loop::EventLoop; use crate::process_manager::Process; @@ -60,7 +61,7 @@ pub struct Pipeline { /// The ID of the browsing context that contains this Pipeline. pub browsing_context_id: BrowsingContextId, - /// The ID of the top-level browsing context that contains this Pipeline. + /// The [`WebViewId`] of the `WebView` that contains this Pipeline. pub webview_id: WebViewId, pub opener: Option, @@ -101,6 +102,8 @@ pub struct Pipeline { /// The last compositor [`Epoch`] that was laid out in this pipeline if "exit after load" is /// enabled. pub layout_epoch: Epoch, + + pub focus_sequence: FocusSequenceNumber, } /// Initial setup data needed to construct a pipeline. @@ -167,6 +170,9 @@ pub struct InitialPipelineState { /// The initial [`ViewportDetails`] to use when starting this new [`Pipeline`]. pub viewport_details: ViewportDetails, + /// The initial [`Theme`] to use when starting this new [`Pipeline`]. + pub theme: Theme, + /// The ID of the pipeline namespace for this script thread. pub pipeline_namespace_id: PipelineNamespaceId, @@ -182,9 +188,6 @@ pub struct InitialPipelineState { /// compositor threads after spawning a pipeline. pub prev_throttled: bool, - /// The ID of the document processed by this script thread. - pub webrender_document: DocumentId, - /// A channel to the WebGL thread. pub webgl_chan: Option, @@ -224,6 +227,7 @@ impl Pipeline { opener: state.opener, load_data: state.load_data.clone(), viewport_details: state.viewport_details, + theme: state.theme, }; if let Err(e) = script_chan.send(ScriptThreadMessage::AttachLayout(new_layout_info)) @@ -280,13 +284,13 @@ impl Pipeline { time_profiler_chan: state.time_profiler_chan, mem_profiler_chan: state.mem_profiler_chan, viewport_details: state.viewport_details, + theme: state.theme, script_chan: script_chan.clone(), load_data: state.load_data.clone(), script_port, opts: (*opts::get()).clone(), prefs: Box::new(prefs::get().clone()), pipeline_namespace_id: state.pipeline_namespace_id, - webrender_document: state.webrender_document, cross_process_compositor_api: state .compositor_proxy .cross_process_compositor_api @@ -373,6 +377,7 @@ impl Pipeline { completely_loaded: false, title: String::new(), layout_epoch: Epoch(0), + focus_sequence: FocusSequenceNumber::default(), }; pipeline.set_throttled(throttled); @@ -494,6 +499,7 @@ pub struct UnprivilegedPipelineContent { time_profiler_chan: time::ProfilerChan, mem_profiler_chan: profile_mem::ProfilerChan, viewport_details: ViewportDetails, + theme: Theme, script_chan: IpcSender, load_data: LoadData, script_port: IpcReceiver, @@ -501,7 +507,6 @@ pub struct UnprivilegedPipelineContent { prefs: Box, pipeline_namespace_id: PipelineNamespaceId, cross_process_compositor_api: CrossProcessCompositorApi, - webrender_document: DocumentId, webgl_chan: Option, webxr_registry: Option, player_context: WindowGLContext, @@ -545,11 +550,11 @@ impl UnprivilegedPipelineContent { memory_profiler_sender: self.mem_profiler_chan.clone(), devtools_server_sender: self.devtools_ipc_sender, viewport_details: self.viewport_details, + theme: self.theme, pipeline_namespace_id: self.pipeline_namespace_id, content_process_shutdown_sender: content_process_shutdown_chan, webgl_chan: self.webgl_chan, webxr_registry: self.webxr_registry, - webrender_document: self.webrender_document, compositor_api: self.cross_process_compositor_api.clone(), player_context: self.player_context.clone(), inherited_secure_context: self.load_data.inherited_secure_context, diff --git a/components/constellation/sandboxing.rs b/components/constellation/sandboxing.rs index 3738b4f288b..b4c6e7a9a39 100644 --- a/components/constellation/sandboxing.rs +++ b/components/constellation/sandboxing.rs @@ -159,6 +159,7 @@ pub fn spawn_multiprocess(content: UnprivilegedContent) -> Result target!("SetWebViewThrottled"), Self::SetScrollStates(..) => target!("SetScrollStates"), Self::PaintMetric(..) => target!("PaintMetric"), + Self::EvaluateJavaScript(..) => target!("EvaluateJavaScript"), + Self::CreateMemoryReport(..) => target!("CreateMemoryReport"), } } } @@ -113,7 +115,7 @@ mod from_script { }; } - impl LogTarget for script_traits::ScriptToConstellationMessage { + impl LogTarget for constellation_traits::ScriptToConstellationMessage { fn log_target(&self) -> &'static str { match self { Self::CompleteMessagePortTransfer(..) => target!("CompleteMessagePortTransfer"), @@ -123,8 +125,8 @@ mod from_script { Self::RemoveMessagePortRouter(..) => target!("RemoveMessagePortRouter"), Self::RerouteMessagePort(..) => target!("RerouteMessagePort"), Self::MessagePortShipped(..) => target!("MessagePortShipped"), - Self::RemoveMessagePort(..) => target!("RemoveMessagePort"), Self::EntanglePorts(..) => target!("EntanglePorts"), + Self::DisentanglePorts(..) => target!("DisentanglePorts"), Self::NewBroadcastChannelRouter(..) => target!("NewBroadcastChannelRouter"), Self::RemoveBroadcastChannelRouter(..) => target!("RemoveBroadcastChannelRouter"), Self::NewBroadcastChannelNameInRouter(..) => { @@ -138,7 +140,8 @@ mod from_script { Self::BroadcastStorageEvent(..) => target!("BroadcastStorageEvent"), Self::ChangeRunningAnimationsState(..) => target!("ChangeRunningAnimationsState"), Self::CreateCanvasPaintThread(..) => target!("CreateCanvasPaintThread"), - Self::Focus => target!("Focus"), + Self::Focus(..) => target!("Focus"), + Self::FocusRemoteDocument(..) => target!("FocusRemoteDocument"), Self::GetTopForBrowsingContext(..) => target!("GetTopForBrowsingContext"), Self::GetBrowsingContextInfo(..) => target!("GetBrowsingContextInfo"), Self::GetChildBrowsingContextId(..) => target!("GetChildBrowsingContextId"), @@ -175,6 +178,8 @@ mod from_script { Self::TitleChanged(..) => target!("TitleChanged"), Self::IFrameSizes(..) => target!("IFrameSizes"), Self::ReportMemory(..) => target!("ReportMemory"), + Self::WebDriverInputComplete(..) => target!("WebDriverInputComplete"), + Self::FinishJavaScriptEvaluation(..) => target!("FinishJavaScriptEvaluation"), } } } @@ -236,7 +241,10 @@ mod from_script { Self::StopGamepadHapticEffect(..) => target_variant!("StopGamepadHapticEffect"), Self::ShutdownComplete => target_variant!("ShutdownComplete"), Self::ShowNotification(..) => target_variant!("ShowNotification"), - Self::ShowSelectElementMenu(..) => target_variant!("ShowSelectElementMenu"), + Self::ShowFormControl(..) => target_variant!("ShowFormControl"), + Self::FinishJavaScriptEvaluation(..) => { + target_variant!("FinishJavaScriptEvaluation") + }, } } } diff --git a/components/constellation/webview_manager.rs b/components/constellation/webview_manager.rs index e5e427d40a1..7d6e98a54ea 100644 --- a/components/constellation/webview_manager.rs +++ b/components/constellation/webview_manager.rs @@ -81,18 +81,14 @@ impl WebViewManager { #[cfg(test)] mod test { - use std::num::NonZeroU32; - - use base::id::{ - BrowsingContextId, BrowsingContextIndex, PipelineNamespace, PipelineNamespaceId, WebViewId, - }; + use base::id::{BrowsingContextId, Index, PipelineNamespace, PipelineNamespaceId, WebViewId}; use crate::webview_manager::WebViewManager; fn id(namespace_id: u32, index: u32) -> WebViewId { WebViewId(BrowsingContextId { namespace_id: PipelineNamespaceId(namespace_id), - index: BrowsingContextIndex(NonZeroU32::new(index).expect("Incorrect test case")), + index: Index::new(index).expect("Incorrect test case"), }) } diff --git a/components/devtools/actors/breakpoint.rs b/components/devtools/actors/breakpoint.rs new file mode 100644 index 00000000000..04f2de140b4 --- /dev/null +++ b/components/devtools/actors/breakpoint.rs @@ -0,0 +1,55 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use serde::Serialize; + +use crate::EmptyReplyMsg; +use crate::actor::{Actor, ActorMessageStatus}; +use crate::protocol::JsonPacketStream; + +#[derive(Serialize)] +pub struct BreakpointListActorMsg { + actor: String, +} + +pub struct BreakpointListActor { + name: String, +} + +impl Actor for BreakpointListActor { + fn name(&self) -> String { + self.name.clone() + } + + fn handle_message( + &self, + _registry: &crate::actor::ActorRegistry, + msg_type: &str, + _msg: &serde_json::Map, + stream: &mut std::net::TcpStream, + _stream_id: crate::StreamId, + ) -> Result { + Ok(match msg_type { + "setBreakpoint" => { + let msg = EmptyReplyMsg { from: self.name() }; + let _ = stream.write_json_packet(&msg); + + ActorMessageStatus::Processed + }, + "setActiveEventBreakpoints" => { + let msg = EmptyReplyMsg { from: self.name() }; + let _ = stream.write_json_packet(&msg); + + ActorMessageStatus::Processed + }, + _ => ActorMessageStatus::Ignored, + }) + } +} + +impl BreakpointListActor { + pub fn new(name: String) -> Self { + Self { name } + } +} diff --git a/components/devtools/actors/browsing_context.rs b/components/devtools/actors/browsing_context.rs index 261eb4112bf..81a00e82d47 100644 --- a/components/devtools/actors/browsing_context.rs +++ b/components/devtools/actors/browsing_context.rs @@ -11,8 +11,11 @@ use std::collections::HashMap; use std::net::TcpStream; use base::id::PipelineId; -use devtools_traits::DevtoolScriptControlMsg::{self, GetCssDatabase, WantsLiveNotifications}; +use devtools_traits::DevtoolScriptControlMsg::{ + self, GetCssDatabase, SimulateColorScheme, WantsLiveNotifications, +}; use devtools_traits::{DevtoolsPageInfo, NavigationState}; +use embedder_traits::Theme; use ipc_channel::ipc::{self, IpcSender}; use serde::Serialize; use serde_json::{Map, Value}; @@ -28,8 +31,15 @@ use crate::actors::thread::ThreadActor; use crate::actors::watcher::{SessionContext, SessionContextType, WatcherActor}; use crate::id::{DevtoolsBrowserId, DevtoolsBrowsingContextId, DevtoolsOuterWindowId, IdMap}; use crate::protocol::JsonPacketStream; +use crate::resource::ResourceAvailable; use crate::{EmptyReplyMsg, StreamId}; +#[derive(Serialize)] +struct ListWorkersReply { + from: String, + workers: Vec<()>, +} + #[derive(Serialize)] struct FrameUpdateReply { from: String, @@ -47,14 +57,6 @@ struct FrameUpdateMsg { title: String, } -#[derive(Serialize)] -struct ResourceAvailableReply { - from: String, - #[serde(rename = "type")] - type_: String, - array: Vec<(String, Vec)>, -} - #[derive(Serialize)] struct TabNavigated { from: String, @@ -143,6 +145,12 @@ pub(crate) struct BrowsingContextActor { pub watcher: String, } +impl ResourceAvailable for BrowsingContextActor { + fn actor_name(&self) -> String { + self.name.clone() + } +} + impl Actor for BrowsingContextActor { fn name(&self) -> String { self.name.clone() @@ -163,6 +171,14 @@ impl Actor for BrowsingContextActor { let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, + "listWorkers" => { + let _ = stream.write_json_packet(&ListWorkersReply { + from: self.name(), + // TODO: Find out what needs to be listed here + workers: vec![], + }); + ActorMessageStatus::Processed + }, _ => ActorMessageStatus::Ignored, }) } @@ -341,15 +357,9 @@ impl BrowsingContextActor { }); } - pub(crate) fn resource_available(&self, message: T, resource_type: String) { - let msg = ResourceAvailableReply:: { - from: self.name(), - type_: "resources-available-array".into(), - array: vec![(resource_type, vec![message])], - }; - - for stream in self.streams.borrow_mut().values_mut() { - let _ = stream.write_json_packet(&msg); - } + pub fn simulate_color_scheme(&self, theme: Theme) -> Result<(), ()> { + self.script_chan + .send(SimulateColorScheme(self.active_pipeline_id.get(), theme)) + .map_err(|_| ()) } } diff --git a/components/devtools/actors/console.rs b/components/devtools/actors/console.rs index ecd718e47d4..fd0bd184bfa 100644 --- a/components/devtools/actors/console.rs +++ b/components/devtools/actors/console.rs @@ -30,6 +30,7 @@ use crate::actors::browsing_context::BrowsingContextActor; use crate::actors::object::ObjectActor; use crate::actors::worker::WorkerActor; use crate::protocol::JsonPacketStream; +use crate::resource::ResourceAvailable; use crate::{StreamId, UniqueId}; trait EncodableConsoleMessage { @@ -251,6 +252,7 @@ impl ConsoleActor { page_error: PageError, id: UniqueId, registry: &ActorRegistry, + stream: &mut TcpStream, ) { self.cached_events .borrow_mut() @@ -261,7 +263,11 @@ impl ConsoleActor { if let Root::BrowsingContext(bc) = &self.root { registry .find::(bc) - .resource_available(PageErrorWrapper { page_error }, "error-message".into()) + .resource_available( + PageErrorWrapper { page_error }, + "error-message".into(), + stream, + ) }; } } @@ -271,6 +277,7 @@ impl ConsoleActor { console_message: ConsoleMessage, id: UniqueId, registry: &ActorRegistry, + stream: &mut TcpStream, ) { let log_message: ConsoleLog = console_message.into(); self.cached_events @@ -282,7 +289,7 @@ impl ConsoleActor { if let Root::BrowsingContext(bc) = &self.root { registry .find::(bc) - .resource_available(log_message, "console-message".into()) + .resource_available(log_message, "console-message".into(), stream) }; } } diff --git a/components/devtools/actors/inspector.rs b/components/devtools/actors/inspector.rs index d41777f56c6..28a4d7ccf94 100644 --- a/components/devtools/actors/inspector.rs +++ b/components/devtools/actors/inspector.rs @@ -166,6 +166,8 @@ impl Actor for InspectorActor { if self.highlighter.borrow().is_none() { let highlighter_actor = HighlighterActor { name: registry.new_name("highlighter"), + pipeline, + script_sender: self.script_chan.clone(), }; let mut highlighter = self.highlighter.borrow_mut(); *highlighter = Some(highlighter_actor.name()); diff --git a/components/devtools/actors/inspector/highlighter.rs b/components/devtools/actors/inspector/highlighter.rs index f75d25f2175..0dbf2b1e52f 100644 --- a/components/devtools/actors/inspector/highlighter.rs +++ b/components/devtools/actors/inspector/highlighter.rs @@ -7,6 +7,9 @@ use std::net::TcpStream; +use base::id::PipelineId; +use devtools_traits::DevtoolScriptControlMsg; +use ipc_channel::ipc::IpcSender; use serde::Serialize; use serde_json::{self, Map, Value}; @@ -21,6 +24,8 @@ pub struct HighlighterMsg { pub struct HighlighterActor { pub name: String, + pub script_sender: IpcSender, + pub pipeline: PipelineId, } #[derive(Serialize)] @@ -41,14 +46,39 @@ impl Actor for HighlighterActor { /// - `hide`: Disables highlighting for the selected node fn handle_message( &self, - _registry: &ActorRegistry, + registry: &ActorRegistry, msg_type: &str, - _msg: &Map, + msg: &Map, stream: &mut TcpStream, _id: StreamId, ) -> Result { Ok(match msg_type { "show" => { + let Some(node_actor) = msg.get("node") else { + // TODO: send missing parameter error + return Ok(ActorMessageStatus::Ignored); + }; + + let Some(node_actor_name) = node_actor.as_str() else { + // TODO: send invalid parameter error + return Ok(ActorMessageStatus::Ignored); + }; + + if node_actor_name.starts_with("inspector") { + // TODO: For some reason, the client initially asks us to highlight + // the inspector? Investigate what this is supposed to mean. + let msg = ShowReply { + from: self.name(), + value: false, + }; + let _ = stream.write_json_packet(&msg); + return Ok(ActorMessageStatus::Processed); + } + + self.instruct_script_thread_to_highlight_node( + Some(node_actor_name.to_owned()), + registry, + ); let msg = ShowReply { from: self.name(), value: true, @@ -58,6 +88,8 @@ impl Actor for HighlighterActor { }, "hide" => { + self.instruct_script_thread_to_highlight_node(None, registry); + let msg = EmptyReplyMsg { from: self.name() }; let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed @@ -67,3 +99,19 @@ impl Actor for HighlighterActor { }) } } + +impl HighlighterActor { + fn instruct_script_thread_to_highlight_node( + &self, + node_actor: Option, + registry: &ActorRegistry, + ) { + let node_id = node_actor.map(|node_actor| registry.actor_to_script(node_actor)); + self.script_sender + .send(DevtoolScriptControlMsg::HighlightDomNode( + self.pipeline, + node_id, + )) + .unwrap(); + } +} diff --git a/components/devtools/actors/inspector/node.rs b/components/devtools/actors/inspector/node.rs index 10ff9801844..a731f15b2d8 100644 --- a/components/devtools/actors/inspector/node.rs +++ b/components/devtools/actors/inspector/node.rs @@ -78,6 +78,18 @@ pub struct NodeActorMsg { shadow_root_mode: Option, traits: HashMap, attrs: Vec, + + /// The `DOCTYPE` name if this is a `DocumentType` node, `None` otherwise + #[serde(skip_serializing_if = "Option::is_none")] + name: Option, + + /// The `DOCTYPE` public identifier if this is a `DocumentType` node, `None` otherwise + #[serde(skip_serializing_if = "Option::is_none")] + public_id: Option, + + /// The `DOCTYPE` system identifier if this is a `DocumentType` node, `None` otherwise + #[serde(skip_serializing_if = "Option::is_none")] + system_id: Option, } pub struct NodeActor { @@ -276,6 +288,9 @@ impl NodeInfoToProtocol for NodeInfo { value: attr.value, }) .collect(), + name: self.doctype_name, + public_id: self.doctype_public_identifier, + system_id: self.doctype_system_identifier, } } } diff --git a/components/devtools/actors/source.rs b/components/devtools/actors/source.rs new file mode 100644 index 00000000000..9d29bc1d3ef --- /dev/null +++ b/components/devtools/actors/source.rs @@ -0,0 +1,50 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::cell::{Ref, RefCell}; +use std::collections::BTreeSet; + +use serde::Serialize; +use servo_url::ServoUrl; + +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct SourceData { + pub actor: String, + /// URL of the script, or URL of the page for inline scripts. + pub url: String, + pub is_black_boxed: bool, +} + +#[derive(Serialize)] +pub(crate) struct SourcesReply { + pub from: String, + pub sources: Vec, +} + +pub(crate) struct Source { + actor_name: String, + source_urls: RefCell>, +} + +impl Source { + pub fn new(actor_name: String) -> Self { + Self { + actor_name, + source_urls: RefCell::new(BTreeSet::default()), + } + } + + pub fn add_source(&self, url: ServoUrl) { + self.source_urls.borrow_mut().insert(SourceData { + actor: self.actor_name.clone(), + url: url.to_string(), + is_black_boxed: false, + }); + } + + pub fn sources(&self) -> Ref> { + self.source_urls.borrow() + } +} diff --git a/components/devtools/actors/thread.rs b/components/devtools/actors/thread.rs index 6e272cd3d28..7ff11dff675 100644 --- a/components/devtools/actors/thread.rs +++ b/components/devtools/actors/thread.rs @@ -7,6 +7,7 @@ use std::net::TcpStream; use serde::Serialize; use serde_json::{Map, Value}; +use super::source::{Source, SourcesReply}; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::protocol::JsonPacketStream; use crate::{EmptyReplyMsg, StreamId}; @@ -49,22 +50,17 @@ struct ThreadInterruptedReply { type_: String, } -#[derive(Serialize)] -struct SourcesReply { - from: String, - sources: Vec, -} - -#[derive(Serialize)] -enum Source {} - pub struct ThreadActor { - name: String, + pub name: String, + pub source_manager: Source, } impl ThreadActor { pub fn new(name: String) -> ThreadActor { - ThreadActor { name } + ThreadActor { + name: name.clone(), + source_manager: Source::new(name), + } } } @@ -125,6 +121,8 @@ impl Actor for ThreadActor { ActorMessageStatus::Processed }, + // Client has attached to the thread and wants to load script sources. + // "sources" => { let msg = SourcesReply { from: self.name(), diff --git a/components/devtools/actors/watcher.rs b/components/devtools/actors/watcher.rs index fab071faa2a..7720daf070d 100644 --- a/components/devtools/actors/watcher.rs +++ b/components/devtools/actors/watcher.rs @@ -19,8 +19,12 @@ use serde::Serialize; use serde_json::{Map, Value}; use self::network_parent::{NetworkParentActor, NetworkParentActorMsg}; +use super::breakpoint::BreakpointListActor; +use super::thread::ThreadActor; +use super::worker::WorkerMsg; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::actors::browsing_context::{BrowsingContextActor, BrowsingContextActorMsg}; +use crate::actors::root::RootActor; use crate::actors::watcher::target_configuration::{ TargetConfigurationActor, TargetConfigurationActorMsg, }; @@ -28,7 +32,8 @@ use crate::actors::watcher::thread_configuration::{ ThreadConfigurationActor, ThreadConfigurationActorMsg, }; use crate::protocol::JsonPacketStream; -use crate::{EmptyReplyMsg, StreamId}; +use crate::resource::ResourceAvailable; +use crate::{EmptyReplyMsg, StreamId, WorkerActor}; pub mod network_parent; pub mod target_configuration; @@ -53,7 +58,7 @@ impl SessionContext { supported_targets: HashMap::from([ ("frame", true), ("process", false), - ("worker", false), + ("worker", true), ("service_worker", false), ("shared_worker", false), ]), @@ -78,7 +83,7 @@ impl SessionContext { ("network-event-stacktrace", false), ("reflow", false), ("stylesheet", false), - ("source", false), + ("source", true), ("thread-state", false), ("server-sent-event", false), ("websocket", false), @@ -100,12 +105,19 @@ pub enum SessionContextType { _All, } +#[derive(Serialize)] +#[serde(untagged)] +enum TargetActorMsg { + BrowsingContext(BrowsingContextActorMsg), + Worker(WorkerMsg), +} + #[derive(Serialize)] struct WatchTargetsReply { from: String, #[serde(rename = "type")] type_: String, - target: BrowsingContextActorMsg, + target: TargetActorMsg, } #[derive(Serialize)] @@ -133,6 +145,18 @@ struct GetThreadConfigurationActorReply { configuration: ThreadConfigurationActorMsg, } +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct GetBreakpointListActorReply { + from: String, + breakpoint_list: GetBreakpointListActorReplyInner, +} + +#[derive(Serialize)] +struct GetBreakpointListActorReplyInner { + actor: String, +} + #[derive(Serialize)] #[serde(rename_all = "camelCase")] struct DocumentEvent { @@ -198,16 +222,38 @@ impl Actor for WatcherActor { _id: StreamId, ) -> Result { let target = registry.find::(&self.browsing_context_actor); + let root = registry.find::("root"); Ok(match msg_type { "watchTargets" => { - let msg = WatchTargetsReply { - from: self.name(), - type_: "target-available-form".into(), - target: target.encodable(), - }; - let _ = stream.write_json_packet(&msg); + // As per logs we either get targetType as "frame" or "worker" + let target_type = msg + .get("targetType") + .and_then(Value::as_str) + .unwrap_or("frame"); // default to "frame" - target.frame_update(stream); + if target_type == "frame" { + let msg = WatchTargetsReply { + from: self.name(), + type_: "target-available-form".into(), + target: TargetActorMsg::BrowsingContext(target.encodable()), + }; + let _ = stream.write_json_packet(&msg); + + target.frame_update(stream); + } else if target_type == "worker" { + for worker_name in &root.workers { + let worker = registry.find::(worker_name); + let worker_msg = WatchTargetsReply { + from: self.name(), + type_: "target-available-form".into(), + target: TargetActorMsg::Worker(worker.encodable()), + }; + let _ = stream.write_json_packet(&worker_msg); + } + } else { + warn!("Unexpected target_type: {}", target_type); + return Ok(ActorMessageStatus::Ignored); + } // Messages that contain a `type` field are used to send event callbacks, but they // don't count as a reply. Since every message needs to be responded, we send an @@ -246,7 +292,28 @@ impl Actor for WatcherActor { title: Some(target.title.borrow().clone()), url: Some(target.url.borrow().clone()), }; - target.resource_available(event, "document-event".into()); + target.resource_available(event, "document-event".into(), stream); + } + }, + "source" => { + let thread_actor = registry.find::(&target.thread); + let sources = thread_actor.source_manager.sources(); + target.resources_available( + sources.iter().collect(), + "source".into(), + stream, + ); + + for worker_name in &root.workers { + let worker = registry.find::(worker_name); + let thread = registry.find::(&worker.thread); + let worker_sources = thread.source_manager.sources(); + + worker.resources_available( + worker_sources.iter().collect(), + "source".into(), + stream, + ); } }, "console-message" | "error-message" => {}, @@ -295,6 +362,19 @@ impl Actor for WatcherActor { let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed }, + "getBreakpointListActor" => { + let breakpoint_list_name = registry.new_name("breakpoint-list"); + let breakpoint_list = BreakpointListActor::new(breakpoint_list_name.clone()); + registry.register_later(Box::new(breakpoint_list)); + + let _ = stream.write_json_packet(&GetBreakpointListActorReply { + from: self.name(), + breakpoint_list: GetBreakpointListActorReplyInner { + actor: breakpoint_list_name, + }, + }); + ActorMessageStatus::Processed + }, _ => ActorMessageStatus::Ignored, }) } diff --git a/components/devtools/actors/watcher/target_configuration.rs b/components/devtools/actors/watcher/target_configuration.rs index 7b83cdde698..0d366e81475 100644 --- a/components/devtools/actors/watcher/target_configuration.rs +++ b/components/devtools/actors/watcher/target_configuration.rs @@ -8,12 +8,16 @@ use std::collections::HashMap; use std::net::TcpStream; +use embedder_traits::Theme; +use log::warn; use serde::Serialize; use serde_json::{Map, Value}; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; +use crate::actors::browsing_context::BrowsingContextActor; +use crate::actors::tab::TabDescriptorActor; use crate::protocol::JsonPacketStream; -use crate::{EmptyReplyMsg, StreamId}; +use crate::{EmptyReplyMsg, RootActor, StreamId}; #[derive(Serialize)] #[serde(rename_all = "camelCase")] @@ -44,15 +48,39 @@ impl Actor for TargetConfigurationActor { /// - `updateConfiguration`: Receives new configuration flags from the devtools host. fn handle_message( &self, - _registry: &ActorRegistry, + registry: &ActorRegistry, msg_type: &str, - _msg: &Map, + msg: &Map, stream: &mut TcpStream, _id: StreamId, ) -> Result { Ok(match msg_type { "updateConfiguration" => { - // TODO: Actually update configuration + let config = match msg.get("configuration").and_then(|v| v.as_object()) { + Some(config) => config, + None => { + let msg = EmptyReplyMsg { from: self.name() }; + let _ = stream.write_json_packet(&msg); + return Ok(ActorMessageStatus::Processed); + }, + }; + if let Some(scheme) = config.get("colorSchemeSimulation").and_then(|v| v.as_str()) { + let theme = match scheme { + "dark" => Theme::Dark, + "light" => Theme::Light, + _ => Theme::Light, + }; + let root_actor = registry.find::("root"); + if let Some(tab_name) = root_actor.active_tab() { + let tab_actor = registry.find::(&tab_name); + let browsing_context_name = tab_actor.browsing_context(); + let browsing_context_actor = + registry.find::(&browsing_context_name); + browsing_context_actor.simulate_color_scheme(theme)?; + } else { + warn!("No active tab for updateConfiguration"); + } + } let msg = EmptyReplyMsg { from: self.name() }; let _ = stream.write_json_packet(&msg); ActorMessageStatus::Processed @@ -69,7 +97,7 @@ impl TargetConfigurationActor { configuration: HashMap::new(), supported_options: HashMap::from([ ("cacheDisabled", false), - ("colorSchemeSimulation", false), + ("colorSchemeSimulation", true), ("customFormatters", false), ("customUserAgent", false), ("javascriptEnabled", false), diff --git a/components/devtools/actors/worker.rs b/components/devtools/actors/worker.rs index b5d1370c320..f3ca4f2aed7 100644 --- a/components/devtools/actors/worker.rs +++ b/components/devtools/actors/worker.rs @@ -17,6 +17,7 @@ use servo_url::ServoUrl; use crate::StreamId; use crate::actor::{Actor, ActorMessageStatus, ActorRegistry}; use crate::protocol::JsonPacketStream; +use crate::resource::ResourceAvailable; #[derive(Clone, Copy)] #[allow(dead_code)] @@ -43,16 +44,24 @@ impl WorkerActor { actor: self.name.clone(), console_actor: self.console.clone(), thread_actor: self.thread.clone(), - worker_id: self.worker_id.0.to_string(), + id: self.worker_id.0.to_string(), url: self.url.to_string(), traits: WorkerTraits { is_parent_intercept_enabled: false, + supports_top_level_target_flag: false, }, type_: self.type_ as u32, + target_type: "worker".to_string(), } } } +impl ResourceAvailable for WorkerActor { + fn actor_name(&self) -> String { + self.name.clone() + } +} + impl Actor for WorkerActor { fn name(&self) -> String { self.name.clone() @@ -149,6 +158,7 @@ struct ConnectReply { #[serde(rename_all = "camelCase")] struct WorkerTraits { is_parent_intercept_enabled: bool, + supports_top_level_target_flag: bool, } #[derive(Serialize)] @@ -157,9 +167,11 @@ pub(crate) struct WorkerMsg { actor: String, console_actor: String, thread_actor: String, - worker_id: String, + id: String, url: String, traits: WorkerTraits, #[serde(rename = "type")] type_: u32, + #[serde(rename = "targetType")] + target_type: String, } diff --git a/components/devtools/lib.rs b/components/devtools/lib.rs index 4fc92457b04..74a45eaf866 100644 --- a/components/devtools/lib.rs +++ b/components/devtools/lib.rs @@ -19,16 +19,18 @@ use std::net::{Shutdown, TcpListener, TcpStream}; use std::sync::{Arc, Mutex}; use std::thread; +use actors::source::SourceData; use base::id::{BrowsingContextId, PipelineId, WebViewId}; use crossbeam_channel::{Receiver, Sender, unbounded}; use devtools_traits::{ ChromeToDevtoolsControlMsg, ConsoleMessage, ConsoleMessageBuilder, DevtoolScriptControlMsg, DevtoolsControlMsg, DevtoolsPageInfo, LogLevel, NavigationState, NetworkEvent, PageError, - ScriptToDevtoolsControlMsg, WorkerId, + ScriptToDevtoolsControlMsg, SourceInfo, WorkerId, }; use embedder_traits::{AllowOrDeny, EmbedderMsg, EmbedderProxy}; use ipc_channel::ipc::{self, IpcSender}; use log::trace; +use resource::ResourceAvailable; use serde::Serialize; use servo_rand::RngCore; @@ -51,6 +53,7 @@ use crate::protocol::JsonPacketStream; mod actor; /// mod actors { + pub mod breakpoint; pub mod browsing_context; pub mod console; pub mod device; @@ -64,6 +67,7 @@ mod actors { pub mod process; pub mod reflow; pub mod root; + pub mod source; pub mod stylesheets; pub mod tab; pub mod thread; @@ -74,6 +78,7 @@ mod actors { mod id; mod network_handler; mod protocol; +mod resource; #[derive(Clone, Debug, Eq, Hash, PartialEq)] enum UniqueId { @@ -247,6 +252,10 @@ impl DevtoolsInstance { console_message, worker_id, )) => self.handle_console_message(pipeline_id, worker_id, console_message), + DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ScriptSourceLoaded( + pipeline_id, + source_info, + )) => self.handle_script_source_info(pipeline_id, source_info), DevtoolsControlMsg::FromScript(ScriptToDevtoolsControlMsg::ReportPageError( pipeline_id, page_error, @@ -329,7 +338,7 @@ impl DevtoolsInstance { assert!(self.pipelines.contains_key(&pipeline_id)); assert!(self.browsing_contexts.contains_key(&browsing_context_id)); - let thread = ThreadActor::new(actors.new_name("context")); + let thread = ThreadActor::new(actors.new_name("thread")); let thread_name = thread.name(); actors.register(Box::new(thread)); @@ -406,7 +415,7 @@ impl DevtoolsInstance { } fn handle_page_error( - &self, + &mut self, pipeline_id: PipelineId, worker_id: Option, page_error: PageError, @@ -418,11 +427,13 @@ impl DevtoolsInstance { let actors = self.actors.lock().unwrap(); let console_actor = actors.find::(&console_actor_name); let id = worker_id.map_or(UniqueId::Pipeline(pipeline_id), UniqueId::Worker); - console_actor.handle_page_error(page_error, id, &actors); + for stream in self.connections.values_mut() { + console_actor.handle_page_error(page_error.clone(), id.clone(), &actors, stream); + } } fn handle_console_message( - &self, + &mut self, pipeline_id: PipelineId, worker_id: Option, console_message: ConsoleMessage, @@ -434,7 +445,9 @@ impl DevtoolsInstance { let actors = self.actors.lock().unwrap(); let console_actor = actors.find::(&console_actor_name); let id = worker_id.map_or(UniqueId::Pipeline(pipeline_id), UniqueId::Worker); - console_actor.handle_console_api(console_message, id, &actors); + for stream in self.connections.values_mut() { + console_actor.handle_console_api(console_message.clone(), id.clone(), &actors, stream); + } } fn find_console_actor( @@ -498,6 +511,65 @@ impl DevtoolsInstance { }, } } + + fn handle_script_source_info(&mut self, pipeline_id: PipelineId, source_info: SourceInfo) { + let mut actors = self.actors.lock().unwrap(); + + if let Some(worker_id) = source_info.worker_id { + let Some(worker_actor_name) = self.actor_workers.get(&worker_id) else { + return; + }; + + let thread_actor_name = actors.find::(worker_actor_name).thread.clone(); + + let thread_actor = actors.find_mut::(&thread_actor_name); + thread_actor + .source_manager + .add_source(source_info.url.clone()); + + let source = SourceData { + actor: thread_actor_name.clone(), + url: source_info.url.to_string(), + is_black_boxed: false, + }; + + let worker_actor = actors.find::(worker_actor_name); + + for stream in self.connections.values_mut() { + worker_actor.resource_available(&source, "source".into(), stream); + } + } else { + let Some(browsing_context_id) = self.pipelines.get(&pipeline_id) else { + return; + }; + let Some(actor_name) = self.browsing_contexts.get(browsing_context_id) else { + return; + }; + + let thread_actor_name = actors + .find::(actor_name) + .thread + .clone(); + + let thread_actor = actors.find_mut::(&thread_actor_name); + thread_actor + .source_manager + .add_source(source_info.url.clone()); + + let source = SourceData { + actor: thread_actor_name.clone(), + url: source_info.url.to_string(), + is_black_boxed: false, + }; + + // Notify browsing context about the new source + let browsing_context = actors.find::(actor_name); + + for stream in self.connections.values_mut() { + browsing_context.resource_available(&source, "source".into(), stream); + } + } + } } fn allow_devtools_client(stream: &mut TcpStream, embedder: &EmbedderProxy, token: &str) -> bool { diff --git a/components/devtools/resource.rs b/components/devtools/resource.rs new file mode 100644 index 00000000000..4e6aa4042b8 --- /dev/null +++ b/components/devtools/resource.rs @@ -0,0 +1,45 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::net::TcpStream; + +use serde::Serialize; + +use crate::protocol::JsonPacketStream; + +#[derive(Serialize)] +pub(crate) struct ResourceAvailableReply { + pub from: String, + #[serde(rename = "type")] + pub type_: String, + pub array: Vec<(String, Vec)>, +} + +pub(crate) trait ResourceAvailable { + fn actor_name(&self) -> String; + + fn resource_available( + &self, + resource: T, + resource_type: String, + stream: &mut TcpStream, + ) { + self.resources_available(vec![resource], resource_type, stream); + } + + fn resources_available( + &self, + resources: Vec, + resource_type: String, + stream: &mut TcpStream, + ) { + let msg = ResourceAvailableReply:: { + from: self.actor_name(), + type_: "resources-available-array".into(), + array: vec![(resource_type, resources)], + }; + + let _ = stream.write_json_packet(&msg); + } +} diff --git a/components/fonts/Cargo.toml b/components/fonts/Cargo.toml index ee8a4f36412..ce51a9f9112 100644 --- a/components/fonts/Cargo.toml +++ b/components/fonts/Cargo.toml @@ -21,6 +21,7 @@ app_units = { workspace = true } atomic_refcell = { workspace = true } base = { workspace = true } bitflags = { workspace = true } +compositing_traits = { workspace = true } euclid = { workspace = true } fnv = { workspace = true } fonts_traits = { workspace = true } @@ -37,6 +38,7 @@ memmap2 = { workspace = true } net_traits = { workspace = true } num-traits = { workspace = true } parking_lot = { workspace = true } +profile_traits = { workspace = true } range = { path = "../range" } serde = { workspace = true } servo_arc = { workspace = true } @@ -50,7 +52,6 @@ unicode-properties = { workspace = true } unicode-script = { workspace = true } url = { workspace = true } webrender_api = { workspace = true } -webrender_traits = { workspace = true } [target.'cfg(target_os = "macos")'.dependencies] byteorder = { workspace = true } diff --git a/components/fonts/font_context.rs b/components/fonts/font_context.rs index b11a922abdf..58c08b29d3e 100644 --- a/components/fonts/font_context.rs +++ b/components/fonts/font_context.rs @@ -10,6 +10,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use app_units::Au; use base::id::WebViewId; +use compositing_traits::CrossProcessCompositorApi; use fnv::FnvHasher; use fonts_traits::StylesheetWebFontLoadFinishedCallback; use log::{debug, trace}; @@ -32,7 +33,6 @@ use style::stylesheets::{CssRule, DocumentStyleSheet, FontFaceRule, StylesheetIn use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFontFamily}; use url::Url; use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey}; -use webrender_traits::CrossProcessCompositorApi; use crate::font::{ Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontRef, FontSearchScope, @@ -900,9 +900,9 @@ impl RemoteWebFontDownloader { response_message: FetchResponseMsg, ) -> DownloaderResponseResult { match response_message { - FetchResponseMsg::ProcessRequestBody(..) | FetchResponseMsg::ProcessRequestEOF(..) => { - DownloaderResponseResult::InProcess - }, + FetchResponseMsg::ProcessRequestBody(..) | + FetchResponseMsg::ProcessRequestEOF(..) | + FetchResponseMsg::ProcessCspViolations(..) => DownloaderResponseResult::InProcess, FetchResponseMsg::ProcessResponse(_, meta_result) => { trace!( "@font-face {} metadata ok={:?}", diff --git a/components/fonts/font_store.rs b/components/fonts/font_store.rs index 826be947672..0099c56c266 100644 --- a/components/fonts/font_store.rs +++ b/components/fonts/font_store.rs @@ -5,8 +5,8 @@ use std::collections::HashMap; use std::sync::Arc; -use atomic_refcell::AtomicRefCell; use log::warn; +use malloc_size_of_derive::MallocSizeOf; use parking_lot::RwLock; use style::stylesheets::DocumentStyleSheet; use style::values::computed::{FontStyle, FontWeight}; @@ -15,7 +15,7 @@ use crate::font::FontDescriptor; use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods, IsOblique}; use crate::system_font_service::{FontIdentifier, LowercaseFontFamilyName}; -#[derive(Default)] +#[derive(Default, MallocSizeOf)] pub struct FontStore { pub(crate) families: HashMap, web_fonts_loading_for_stylesheets: Vec<(DocumentStyleSheet, usize)>, @@ -134,7 +134,7 @@ impl FontStore { /// /// This optimization is taken from: /// . -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, MallocSizeOf)] struct SimpleFamily { regular: Option, bold: Option, @@ -190,7 +190,7 @@ impl SimpleFamily { } } /// A list of font templates that make up a given font family. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, MallocSizeOf)] pub struct FontTemplates { pub(crate) templates: Vec, simple_family: Option, @@ -263,7 +263,7 @@ impl FontTemplates { } } - let new_template = Arc::new(AtomicRefCell::new(new_template)); + let new_template = FontTemplateRef::new(new_template); self.templates.push(new_template.clone()); self.update_simple_family(new_template); } diff --git a/components/fonts/font_template.rs b/components/fonts/font_template.rs index eca1017d14e..b8173ee0317 100644 --- a/components/fonts/font_template.rs +++ b/components/fonts/font_template.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::fmt::{Debug, Error, Formatter}; -use std::ops::RangeInclusive; +use std::ops::{Deref, RangeInclusive}; use std::sync::Arc; use atomic_refcell::AtomicRefCell; @@ -20,7 +20,21 @@ use crate::system_font_service::{ }; /// A reference to a [`FontTemplate`] with shared ownership and mutability. -pub type FontTemplateRef = Arc>; +#[derive(Clone, Debug, MallocSizeOf)] +pub struct FontTemplateRef(#[conditional_malloc_size_of] Arc>); + +impl FontTemplateRef { + pub fn new(template: FontTemplate) -> Self { + Self(Arc::new(AtomicRefCell::new(template))) + } +} + +impl Deref for FontTemplateRef { + type Target = Arc>; + fn deref(&self) -> &Self::Target { + &self.0 + } +} /// Describes how to select a font from a given family. This is very basic at the moment and needs /// to be expanded or refactored when we support more of the font styling parameters. diff --git a/components/fonts/glyph.rs b/components/fonts/glyph.rs index e0e85ed7f74..db97ee0ebee 100644 --- a/components/fonts/glyph.rs +++ b/components/fonts/glyph.rs @@ -741,9 +741,10 @@ impl fmt::Debug for GlyphStore { } /// A single series of glyphs within a text run. -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] pub struct GlyphRun { /// The glyphs. + #[conditional_malloc_size_of] pub glyph_store: Arc, /// The byte range of characters in the containing run. pub range: Range, diff --git a/components/fonts/platform/macos/font.rs b/components/fonts/platform/macos/font.rs index 56eb8f4dd2c..5249621be81 100644 --- a/components/fonts/platform/macos/font.rs +++ b/components/fonts/platform/macos/font.rs @@ -287,7 +287,7 @@ impl PlatformFontMethods for PlatformFont { .unwrap_or(average_advance); let metrics = FontMetrics { - underline_size: Au::from_f64_au(underline_thickness), + underline_size: Au::from_f64_px(underline_thickness), // TODO(Issue #201): underline metrics are not reliable. Have to pull out of font table // directly. // diff --git a/components/fonts/platform/windows/font.rs b/components/fonts/platform/windows/font.rs index e33b2ad9d3e..74f592e63b9 100644 --- a/components/fonts/platform/windows/font.rs +++ b/components/fonts/platform/windows/font.rs @@ -132,7 +132,9 @@ impl PlatformFontMethods for PlatformFont { pt_size: Option, ) -> Result { let font_face = FontCollection::system() - .get_font_from_descriptor(&font_identifier.font_descriptor) + .font_from_descriptor(&font_identifier.font_descriptor) + .ok() + .flatten() .ok_or("Could not create Font from descriptor")? .create_font_face(); Self::new(font_face, pt_size) diff --git a/components/fonts/platform/windows/font_list.rs b/components/fonts/platform/windows/font_list.rs index d1aa19e178a..e9cdaac8562 100644 --- a/components/fonts/platform/windows/font_list.rs +++ b/components/fonts/platform/windows/font_list.rs @@ -25,7 +25,9 @@ where { let system_fc = FontCollection::system(); for family in system_fc.families_iter() { - callback(family.name()); + if let Ok(family_name) = family.family_name() { + callback(family_name); + } } } @@ -40,13 +42,17 @@ pub struct LocalFontIdentifier { impl LocalFontIdentifier { pub fn index(&self) -> u32 { FontCollection::system() - .get_font_from_descriptor(&self.font_descriptor) + .font_from_descriptor(&self.font_descriptor) + .ok() + .flatten() .map_or(0, |font| font.create_font_face().get_index()) } pub(crate) fn native_font_handle(&self) -> NativeFontHandle { let face = FontCollection::system() - .get_font_from_descriptor(&self.font_descriptor) + .font_from_descriptor(&self.font_descriptor) + .ok() + .flatten() .expect("Could not create Font from FontDescriptor") .create_font_face(); let path = face @@ -62,7 +68,9 @@ impl LocalFontIdentifier { } pub(crate) fn read_data_from_file(&self) -> Option> { - let font = FontCollection::system().get_font_from_descriptor(&self.font_descriptor)?; + let font = FontCollection::system() + .font_from_descriptor(&self.font_descriptor) + .ok()??; let face = font.create_font_face(); let files = face.get_files(); assert!(!files.is_empty()); @@ -86,10 +94,12 @@ where F: FnMut(FontTemplate), { let system_fc = FontCollection::system(); - if let Some(family) = system_fc.get_font_family_by_name(family_name) { + if let Ok(Some(family)) = system_fc.font_family_by_name(family_name) { let count = family.get_font_count(); for i in 0..count { - let font = family.get_font(i); + let Ok(font) = family.font(i) else { + continue; + }; let template_descriptor = (&font).into(); let local_font_identifier = LocalFontIdentifier { font_descriptor: Arc::new(font.to_descriptor()), diff --git a/components/fonts/system_font_service.rs b/components/fonts/system_font_service.rs index 61b36e699df..f799affa7c8 100644 --- a/components/fonts/system_font_service.rs +++ b/components/fonts/system_font_service.rs @@ -6,15 +6,19 @@ use std::borrow::ToOwned; use std::cell::OnceCell; use std::collections::HashMap; use std::ops::{Deref, RangeInclusive}; -use std::sync::Arc; use std::{fmt, thread}; use app_units::Au; -use atomic_refcell::AtomicRefCell; +use compositing_traits::CrossProcessCompositorApi; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use log::debug; +use malloc_size_of::MallocSizeOf as MallocSizeOfTrait; use malloc_size_of_derive::MallocSizeOf; use parking_lot::{Mutex, RwLock}; +use profile_traits::mem::{ + ProcessReports, ProfilerChan, Report, ReportKind, ReportsChan, perform_memory_report, +}; +use profile_traits::path; use serde::{Deserialize, Serialize}; use servo_config::pref; use servo_url::ServoUrl; @@ -25,7 +29,6 @@ use style::values::computed::font::{ use style::values::computed::{FontStretch, FontWeight}; use style::values::specified::FontStretch as SpecifiedFontStretch; use webrender_api::{FontInstanceFlags, FontInstanceKey, FontKey}; -use webrender_traits::CrossProcessCompositorApi; use crate::font::FontDescriptor; use crate::font_store::FontStore; @@ -66,11 +69,12 @@ pub enum SystemFontServiceMessage { ), GetFontKey(IpcSender), GetFontInstanceKey(IpcSender), + CollectMemoryReport(ReportsChan), Exit(IpcSender<()>), Ping, } -#[derive(Default)] +#[derive(Default, MallocSizeOf)] struct ResolvedGenericFontFamilies { default: OnceCell, serif: OnceCell, @@ -84,6 +88,7 @@ struct ResolvedGenericFontFamilies { /// The system font service. There is one of these for every Servo instance. This is a thread, /// responsible for reading the list of system fonts, handling requests to match against /// them, and ensuring that only one copy of system font data is loaded at a time. +#[derive(MallocSizeOf)] pub struct SystemFontService { port: IpcReceiver, local_families: FontStore, @@ -118,8 +123,12 @@ impl SystemFontServiceProxySender { } impl SystemFontService { - pub fn spawn(compositor_api: CrossProcessCompositorApi) -> SystemFontServiceProxySender { + pub fn spawn( + compositor_api: CrossProcessCompositorApi, + memory_profiler_sender: ProfilerChan, + ) -> SystemFontServiceProxySender { let (sender, receiver) = ipc::channel().unwrap(); + let memory_reporter_sender = sender.clone(); thread::Builder::new() .name("SystemFontService".to_owned()) @@ -138,7 +147,13 @@ impl SystemFontService { cache.fetch_new_keys(); cache.refresh_local_families(); - cache.run(); + + memory_profiler_sender.run_with_memory_reporting( + || cache.run(), + "system-fonts".to_owned(), + memory_reporter_sender, + SystemFontServiceMessage::CollectMemoryReport, + ); }) .expect("Thread spawning failed"); @@ -172,6 +187,9 @@ impl SystemFontService { self.fetch_new_keys(); let _ = result_sender.send(self.free_font_instance_keys.pop().unwrap()); }, + SystemFontServiceMessage::CollectMemoryReport(report_sender) => { + self.collect_memory_report(report_sender); + }, SystemFontServiceMessage::Ping => (), SystemFontServiceMessage::Exit(result) => { let _ = result.send(()); @@ -181,6 +199,17 @@ impl SystemFontService { } } + fn collect_memory_report(&self, report_sender: ReportsChan) { + perform_memory_report(|ops| { + let reports = vec![Report { + path: path!["system-fonts"], + kind: ReportKind::ExplicitSystemHeapSize, + size: self.size_of(ops), + }]; + report_sender.send(ProcessReports::new(reports)); + }); + } + #[cfg_attr( feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") @@ -528,11 +557,7 @@ impl SystemFontServiceProxy { panic!("SystemFontService has already exited."); }; - let templates: Vec<_> = templates - .into_iter() - .map(AtomicRefCell::new) - .map(Arc::new) - .collect(); + let templates: Vec<_> = templates.into_iter().map(FontTemplateRef::new).collect(); self.templates.write().insert(cache_key, templates.clone()); templates diff --git a/components/fonts/tests/font.rs b/components/fonts/tests/font.rs index 78c507e7b93..a473be9222b 100644 --- a/components/fonts/tests/font.rs +++ b/components/fonts/tests/font.rs @@ -5,14 +5,13 @@ use std::fs::File; use std::io::Read; use std::path::PathBuf; -use std::sync::Arc; use app_units::Au; use euclid::num::Zero; use fonts::platform::font::PlatformFont; use fonts::{ - Font, FontData, FontDescriptor, FontIdentifier, FontTemplate, PlatformFontMethods, - ShapingFlags, ShapingOptions, + Font, FontData, FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef, + PlatformFontMethods, ShapingFlags, ShapingOptions, }; use servo_url::ServoUrl; use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps; @@ -42,13 +41,7 @@ fn make_font(path: PathBuf) -> Font { variant: FontVariantCaps::Normal, pt_size: Au::from_px(24), }; - Font::new( - Arc::new(atomic_refcell::AtomicRefCell::new(template)), - descriptor, - Some(data), - None, - ) - .unwrap() + Font::new(FontTemplateRef::new(template), descriptor, Some(data), None).unwrap() } #[test] diff --git a/components/fonts/tests/font_context.rs b/components/fonts/tests/font_context.rs index ed102069eee..0793c1e4ce1 100644 --- a/components/fonts/tests/font_context.rs +++ b/components/fonts/tests/font_context.rs @@ -14,6 +14,7 @@ mod font_context { use std::thread; use app_units::Au; + use compositing_traits::CrossProcessCompositorApi; use fonts::platform::font::PlatformFont; use fonts::{ FallbackFontSelectionOptions, FontContext, FontDescriptor, FontFamilyDescriptor, @@ -34,7 +35,6 @@ mod font_context { }; use stylo_atoms::Atom; use webrender_api::{FontInstanceKey, FontKey, IdNamespace}; - use webrender_traits::CrossProcessCompositorApi; struct TestContext { context: FontContext, @@ -137,6 +137,7 @@ mod font_context { break; }, SystemFontServiceMessage::Ping => {}, + SystemFontServiceMessage::CollectMemoryReport(..) => {}, } } } diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml new file mode 100644 index 00000000000..0e4cd0b79fd --- /dev/null +++ b/components/layout/Cargo.toml @@ -0,0 +1,68 @@ +[package] +name = "layout" +version.workspace = true +authors.workspace = true +license.workspace = true +edition.workspace = true +publish.workspace = true +rust-version.workspace = true + +[lib] +name = "layout" +path = "lib.rs" +test = true +doctest = false + +[features] +tracing = ["dep:tracing"] + +[dependencies] +app_units = { workspace = true } +atomic_refcell = { workspace = true } +base = { workspace = true } +bitflags = { workspace = true } +compositing_traits = { workspace = true } +constellation_traits = { workspace = true } +data-url = { workspace = true } +embedder_traits = { workspace = true } +euclid = { workspace = true } +fnv = { workspace = true } +fonts = { path = "../fonts" } +fonts_traits = { workspace = true } +fxhash = { workspace = true } +html5ever = { workspace = true } +icu_locid = { workspace = true } +icu_segmenter = { workspace = true } +ipc-channel = { workspace = true } +itertools = { workspace = true } +log = { workspace = true } +malloc_size_of = { workspace = true } +malloc_size_of_derive = { workspace = true } +net_traits = { workspace = true } +parking_lot = { workspace = true } +pixels = { path = "../pixels" } +profile_traits = { workspace = true } +range = { path = "../range" } +rayon = { workspace = true } +script = { path = "../script" } +script_layout_interface = { workspace = true } +script_traits = { workspace = true } +selectors = { workspace = true } +servo_arc = { workspace = true } +servo_config = { path = "../config" } +servo_geometry = { path = "../geometry" } +servo_url = { path = "../url" } +stylo = { workspace = true } +stylo_atoms = { workspace = true } +stylo_traits = { workspace = true } +taffy = { workspace = true } +tracing = { workspace = true, optional = true } +unicode-bidi = { workspace = true } +unicode-script = { workspace = true } +url = { workspace = true } +webrender_api = { workspace = true } +xi-unicode = { workspace = true } + +[dev-dependencies] +num-traits = { workspace = true } +quickcheck = "1" diff --git a/components/layout_2020/cell.rs b/components/layout/cell.rs similarity index 89% rename from components/layout_2020/cell.rs rename to components/layout/cell.rs index 910a6ab2104..8b35fdf7943 100644 --- a/components/layout_2020/cell.rs +++ b/components/layout/cell.rs @@ -6,9 +6,12 @@ use std::fmt; use std::ops::Deref; use atomic_refcell::AtomicRefCell; +use malloc_size_of_derive::MallocSizeOf; use servo_arc::Arc; -pub(crate) struct ArcRefCell { +#[derive(MallocSizeOf)] +pub struct ArcRefCell { + #[conditional_malloc_size_of] value: Arc>, } diff --git a/components/layout_2020/construct_modern.rs b/components/layout/construct_modern.rs similarity index 87% rename from components/layout_2020/construct_modern.rs rename to components/layout/construct_modern.rs index 22f179d146c..d09744b2031 100644 --- a/components/layout_2020/construct_modern.rs +++ b/components/layout/construct_modern.rs @@ -12,7 +12,7 @@ use style::selector_parser::PseudoElement; use crate::PropagatedBoxTreeData; use crate::context::LayoutContext; -use crate::dom::{BoxSlot, NodeExt}; +use crate::dom::BoxSlot; use crate::dom_traversal::{Contents, NodeAndStyleInfo, TraversalHandler}; use crate::flow::inline::construct::InlineFormattingContextBuilder; use crate::flow::{BlockContainer, BlockFormattingContext}; @@ -24,32 +24,32 @@ use crate::layout_box_base::LayoutBoxBase; use crate::style_ext::DisplayGeneratingBox; /// A builder used for both flex and grid containers. -pub(crate) struct ModernContainerBuilder<'a, 'dom, Node> { +pub(crate) struct ModernContainerBuilder<'a, 'dom> { context: &'a LayoutContext<'a>, - info: &'a NodeAndStyleInfo, + info: &'a NodeAndStyleInfo<'dom>, propagated_data: PropagatedBoxTreeData, - contiguous_text_runs: Vec>, + contiguous_text_runs: Vec>, /// To be run in parallel with rayon in `finish` - jobs: Vec>, + jobs: Vec>, has_text_runs: bool, } -enum ModernContainerJob<'dom, Node> { +enum ModernContainerJob<'dom> { ElementOrPseudoElement { - info: NodeAndStyleInfo, + info: NodeAndStyleInfo<'dom>, display: DisplayGeneratingBox, contents: Contents, box_slot: BoxSlot<'dom>, }, - TextRuns(Vec>), + TextRuns(Vec>), } -struct ModernContainerTextRun<'dom, Node> { - info: NodeAndStyleInfo, +struct ModernContainerTextRun<'dom> { + info: NodeAndStyleInfo<'dom>, text: Cow<'dom, str>, } -impl ModernContainerTextRun<'_, Node> { +impl ModernContainerTextRun<'_> { /// fn is_only_document_white_space(&self) -> bool { // FIXME: is this the right definition? See @@ -73,11 +73,8 @@ pub(crate) struct ModernItem<'dom> { pub formatting_context: IndependentFormattingContext, } -impl<'dom, Node: 'dom> TraversalHandler<'dom, Node> for ModernContainerBuilder<'_, 'dom, Node> -where - Node: NodeExt<'dom>, -{ - fn handle_text(&mut self, info: &NodeAndStyleInfo, text: Cow<'dom, str>) { +impl<'dom> TraversalHandler<'dom> for ModernContainerBuilder<'_, 'dom> { + fn handle_text(&mut self, info: &NodeAndStyleInfo<'dom>, text: Cow<'dom, str>) { self.contiguous_text_runs.push(ModernContainerTextRun { info: info.clone(), text, @@ -87,7 +84,7 @@ where /// Or pseudo-element fn handle_element( &mut self, - info: &NodeAndStyleInfo, + info: &NodeAndStyleInfo<'dom>, display: DisplayGeneratingBox, contents: Contents, box_slot: BoxSlot<'dom>, @@ -103,13 +100,10 @@ where } } -impl<'a, 'dom, Node: 'dom> ModernContainerBuilder<'a, 'dom, Node> -where - Node: NodeExt<'dom>, -{ +impl<'a, 'dom> ModernContainerBuilder<'a, 'dom> { pub fn new( context: &'a LayoutContext<'a>, - info: &'a NodeAndStyleInfo, + info: &'a NodeAndStyleInfo<'dom>, propagated_data: PropagatedBoxTreeData, ) -> Self { ModernContainerBuilder { @@ -148,7 +142,7 @@ where .filter_map(|job| match job { ModernContainerJob::TextRuns(runs) => { let mut inline_formatting_context_builder = - InlineFormattingContextBuilder::new(); + InlineFormattingContextBuilder::new(self.info); for flex_text_run in runs.into_iter() { inline_formatting_context_builder .push_text(flex_text_run.text, &flex_text_run.info); @@ -156,7 +150,6 @@ where let inline_formatting_context = inline_formatting_context_builder.finish( self.context, - self.propagated_data, true, /* has_first_formatted_line */ false, /* is_single_line_text_box */ self.info.style.writing_mode.to_bidi_level(), @@ -165,7 +158,7 @@ where let block_formatting_context = BlockFormattingContext::from_block_container( BlockContainer::InlineFormattingContext(inline_formatting_context), ); - let info: &NodeAndStyleInfo<_> = &*anonymous_info; + let info: &NodeAndStyleInfo = &anonymous_info; let formatting_context = IndependentFormattingContext { base: LayoutBoxBase::new(info.into(), info.style.clone()), contents: IndependentFormattingContextContents::NonReplaced( diff --git a/components/layout/context.rs b/components/layout/context.rs new file mode 100644 index 00000000000..1ee76606b0b --- /dev/null +++ b/components/layout/context.rs @@ -0,0 +1,316 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use std::sync::Arc; + +use base::id::PipelineId; +use euclid::Size2D; +use fnv::FnvHashMap; +use fonts::FontContext; +use fxhash::FxHashMap; +use net_traits::image_cache::{ + Image as CachedImage, ImageCache, ImageCacheResult, ImageOrMetadataAvailable, PendingImageId, + UsePlaceholder, +}; +use parking_lot::{Mutex, RwLock}; +use pixels::RasterImage; +use script_layout_interface::{ + IFrameSizes, ImageAnimationState, PendingImage, PendingImageState, PendingRasterizationImage, +}; +use servo_url::{ImmutableOrigin, ServoUrl}; +use style::context::SharedStyleContext; +use style::dom::OpaqueNode; +use style::values::computed::image::{Gradient, Image}; +use webrender_api::units::{DeviceIntSize, DeviceSize}; + +pub(crate) type CachedImageOrError = Result; + +pub struct LayoutContext<'a> { + pub id: PipelineId, + pub use_rayon: bool, + pub origin: ImmutableOrigin, + + /// Bits shared by the layout and style system. + pub style_context: SharedStyleContext<'a>, + + /// A FontContext to be used during layout. + pub font_context: Arc, + + /// Reference to the script thread image cache. + pub image_cache: Arc, + + /// A list of in-progress image loads to be shared with the script thread. + pub pending_images: Mutex>, + + /// A list of fully loaded vector images that need to be rasterized to a specific + /// size determined by layout. This will be shared with the script thread. + pub pending_rasterization_images: Mutex>, + + /// A collection of ` + + + + + diff --git a/tests/wpt/mozilla/tests/mozilla/form_reset-crash.html b/tests/wpt/mozilla/tests/mozilla/form_reset-crash.html new file mode 100644 index 00000000000..b23cbf6aefd --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/form_reset-crash.html @@ -0,0 +1,7 @@ + +
+ +
+ diff --git a/tests/wpt/mozilla/tests/mozilla/global-enumerate-crash.html b/tests/wpt/mozilla/tests/mozilla/global-enumerate-crash.html new file mode 100644 index 00000000000..a77e79b1465 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/global-enumerate-crash.html @@ -0,0 +1,15 @@ + + + + + + diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html index dce05a55fd3..eee8c799727 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html @@ -38,7 +38,9 @@ test_interfaces([ "ChannelMergerNode", "ChannelSplitterNode", "CharacterData", + "Clipboard", "ClipboardEvent", + "ClipboardItem", "CloseEvent", "ConstantSourceNode", "CryptoKey", @@ -104,6 +106,45 @@ test_interfaces([ "GamepadEvent", "GamepadHapticActuator", "GamepadPose", + "GPU", + "GPUAdapter", + "GPUAdapterInfo", + "GPUBindGroup", + "GPUBindGroupLayout", + "GPUBuffer", + "GPUBufferUsage", + "GPUCanvasContext", + "GPUColorWrite", + "GPUCommandBuffer", + "GPUCommandEncoder", + "GPUCompilationInfo", + "GPUCompilationMessage", + "GPUComputePassEncoder", + "GPUComputePipeline", + "GPUDevice", + "GPUDeviceLostInfo", + "GPUError", + "GPUInternalError", + "GPUMapMode", + "GPUOutOfMemoryError", + "GPUPipelineError", + "GPUPipelineLayout", + "GPUQuerySet", + "GPUQueue", + "GPURenderBundle", + "GPURenderBundleEncoder", + "GPURenderPassEncoder", + "GPURenderPipeline", + "GPUSampler", + "GPUShaderModule", + "GPUShaderStage", + "GPUSupportedFeatures", + "GPUSupportedLimits", + "GPUTexture", + "GPUTextureUsage", + "GPUTextureView", + "GPUUncapturedErrorEvent", + "GPUValidationError", "HashChangeEvent", "Headers", "History", @@ -236,6 +277,8 @@ test_interfaces([ "PerformanceObserverEntryList", "PerformancePaintTiming", "PerformanceResourceTiming", + "PermissionStatus", + "Permissions", "Plugin", "PluginArray", "PointerEvent", @@ -270,7 +313,11 @@ test_interfaces([ "StyleSheetList", "SubmitEvent", "SubtleCrypto", + "SVGElement", + "SVGGraphicsElement", + "SVGImageElement", "SVGRect", + "SVGSVGElement", "Text", "TextTrack", "TextTrackCue", @@ -285,6 +332,11 @@ test_interfaces([ "TrackEvent", "TransitionEvent", "TreeWalker", + "TrustedHTML", + "TrustedScript", + "TrustedScriptURL", + "TrustedTypePolicy", + "TrustedTypePolicyFactory", "UIEvent", "URL", "URLPattern", @@ -294,6 +346,7 @@ test_interfaces([ "VideoTrackList", "VisibilityStateEntry", "WebAssembly", + "WebGL2RenderingContext", "WebGLRenderingContext", "WebGLUniformLocation", "WebGLBuffer", @@ -305,7 +358,12 @@ test_interfaces([ "WebGLShader", "WebGLObject", "WebGLActiveInfo", + "WebGLQuery", + "WebGLSampler", "WebGLShaderPrecisionFormat", + "WebGLSync", + "WebGLTransformFeedback", + "WebGLVertexArrayObject", "WebKitCSSMatrix", "WebSocket", "WheelEvent", @@ -314,6 +372,9 @@ test_interfaces([ "WritableStream", "WritableStreamDefaultController", "WritableStreamDefaultWriter", + "TransformStream", + "TransformStreamDefaultController", + "WGSLLanguageFeatures", "XMLDocument", "XMLHttpRequest", "XMLHttpRequestEventTarget", diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js index d028090d6a8..e86f34f2614 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js @@ -14,6 +14,7 @@ test_interfaces([ "CanvasPattern", "CloseEvent", "CountQueuingStrategy", + "CustomEvent", "DOMMatrix", "DOMMatrixReadOnly", "DOMPoint", @@ -22,7 +23,6 @@ test_interfaces([ "DOMRect", "DOMRectReadOnly", "DOMStringList", - "CustomEvent", "DedicatedWorkerGlobalScope", "DOMException", "ErrorEvent", @@ -35,6 +35,45 @@ test_interfaces([ "FileReaderSync", "FinalizationRegistry", "FormData", + "GPU", + "GPUAdapter", + "GPUAdapterInfo", + "GPUBindGroup", + "GPUBindGroupLayout", + "GPUBuffer", + "GPUBufferUsage", + "GPUCanvasContext", + "GPUColorWrite", + "GPUCommandBuffer", + "GPUCommandEncoder", + "GPUCompilationInfo", + "GPUCompilationMessage", + "GPUComputePassEncoder", + "GPUComputePipeline", + "GPUDevice", + "GPUDeviceLostInfo", + "GPUError", + "GPUInternalError", + "GPUMapMode", + "GPUOutOfMemoryError", + "GPUPipelineError", + "GPUPipelineLayout", + "GPUQuerySet", + "GPUQueue", + "GPURenderBundle", + "GPURenderBundleEncoder", + "GPURenderPassEncoder", + "GPURenderPipeline", + "GPUSampler", + "GPUShaderModule", + "GPUShaderStage", + "GPUSupportedFeatures", + "GPUSupportedLimits", + "GPUTexture", + "GPUTextureUsage", + "GPUTextureView", + "GPUUncapturedErrorEvent", + "GPUValidationError", "Headers", "History", "ImageBitmap", @@ -54,6 +93,8 @@ test_interfaces([ "PerformanceObserverEntryList", "PerformancePaintTiming", "PerformanceResourceTiming", + "PermissionStatus", + "Permissions", "ProgressEvent", "PromiseRejectionEvent", "ReadableStream", @@ -68,6 +109,11 @@ test_interfaces([ "ServiceWorkerContainer", "TextDecoder", "TextEncoder", + "TrustedHTML", + "TrustedScript", + "TrustedScriptURL", + "TrustedTypePolicy", + "TrustedTypePolicyFactory", "URL", "URLPattern", "URLSearchParams", @@ -91,6 +137,9 @@ test_interfaces([ "WritableStream", "WritableStreamDefaultController", "WritableStreamDefaultWriter", + "TransformStream", + "TransformStreamDefaultController", + "WGSLLanguageFeatures", "XMLHttpRequest", "XMLHttpRequestEventTarget", "XMLHttpRequestUpload", diff --git a/tests/wpt/mozilla/tests/mozilla/location-set-crash.html b/tests/wpt/mozilla/tests/mozilla/location-set-crash.html new file mode 100644 index 00000000000..0b1695df79b --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/location-set-crash.html @@ -0,0 +1,5 @@ + + + diff --git a/tests/wpt/mozilla/tests/mozilla/path2D_addPath-crash.html b/tests/wpt/mozilla/tests/mozilla/path2D_addPath-crash.html new file mode 100644 index 00000000000..f913e6baf83 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/path2D_addPath-crash.html @@ -0,0 +1,5 @@ + + diff --git a/tests/wpt/mozilla/tests/mozilla/preferences.html b/tests/wpt/mozilla/tests/mozilla/preferences.html index a1da58a825e..8a65161198c 100644 --- a/tests/wpt/mozilla/tests/mozilla/preferences.html +++ b/tests/wpt/mozilla/tests/mozilla/preferences.html @@ -7,16 +7,16 @@ test(function() { var testBinding = new TestBinding(); assert_equals(typeof testBinding.BooleanMozPreference, "function"); - assert_equals(testBinding.BooleanMozPreference("dom.testbinding.preference_value.falsy"), false); - assert_equals(testBinding.BooleanMozPreference("dom.testbinding.preference_value.truthy"), true); - assert_equals(testBinding.BooleanMozPreference("dom.testbinding.preference_value.string_test"), false); - assert_equals(testBinding.BooleanMozPreference("dom.testbinding.preference_value.string_empty"), false); + assert_equals(testBinding.BooleanMozPreference("dom_testbinding_preference_value_falsy"), false); + assert_equals(testBinding.BooleanMozPreference("dom_testbinding_preference_value_truthy"), true); + assert_equals(testBinding.BooleanMozPreference("dom_testbinding_preference_value_string_test"), false); + assert_equals(testBinding.BooleanMozPreference("dom_testbinding_preference_value_string_empty"), false); assert_equals(typeof testBinding.StringMozPreference, "function"); - assert_equals(testBinding.StringMozPreference("dom.testbinding.preference_value.string_test"), "test"); - assert_equals(testBinding.StringMozPreference("dom.testbinding.preference_value.string_empty"), ""); - assert_equals(testBinding.StringMozPreference("dom.testbinding.preference_value.falsy"), ""); - assert_equals(testBinding.StringMozPreference("dom.testbinding.preference_value.truthy"), ""); - assert_equals(testBinding.StringMozPreference("dom.testbinding.preference_value.space_string_test"), "test1 test2"); - assert_equals(testBinding.StringMozPreference("dom.testbinding.preference_value.space_string_test"), "test1 test2"); + assert_equals(testBinding.StringMozPreference("dom_testbinding_preference_value_string_test"), "test"); + assert_equals(testBinding.StringMozPreference("dom_testbinding_preference_value_string_empty"), ""); + assert_equals(testBinding.StringMozPreference("dom_testbinding_preference_value_falsy"), ""); + assert_equals(testBinding.StringMozPreference("dom_testbinding_preference_value_truthy"), ""); + assert_equals(testBinding.StringMozPreference("dom_testbinding_preference_value_space_string_test"), "test1 test2"); + assert_equals(testBinding.StringMozPreference("dom_testbinding_preference_value_quote_string_test"), "\"test\""); }, "prefs"); diff --git a/tests/wpt/mozilla/tests/mozilla/slice-blob-twice-crash.html b/tests/wpt/mozilla/tests/mozilla/slice-blob-twice-crash.html new file mode 100644 index 00000000000..94227dcc74d --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/slice-blob-twice-crash.html @@ -0,0 +1,5 @@ + + + diff --git a/tests/wpt/mozilla/tests/mozilla/video_poster_csp.sub.html b/tests/wpt/mozilla/tests/mozilla/video_poster_csp.sub.html new file mode 100644 index 00000000000..cc5dfd54c1e --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/video_poster_csp.sub.html @@ -0,0 +1,25 @@ + + + + + + + diff --git a/tests/wpt/mozilla/tests/mozilla/window_resizeTo.html b/tests/wpt/mozilla/tests/mozilla/window_resizeTo.html index 87d60498e80..3f6c5a610d5 100644 --- a/tests/wpt/mozilla/tests/mozilla/window_resizeTo.html +++ b/tests/wpt/mozilla/tests/mozilla/window_resizeTo.html @@ -1,16 +1,21 @@ -Verify that the resize event is fired when the window is resized (particularly in headless mode) +Verify that the resize event is fired when the window is resized (in popup) diff --git a/tests/wpt/mozilla/tests/mozilla/window_resizeTo_not_permitted-crash.html b/tests/wpt/mozilla/tests/mozilla/window_resizeTo_not_permitted-crash.html new file mode 100644 index 00000000000..7b6a67ff814 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/window_resizeTo_not_permitted-crash.html @@ -0,0 +1,6 @@ + + +Test that calling resizeTo() on normal window does not crash + diff --git a/tests/wpt/tests/.azure-pipelines.yml b/tests/wpt/tests/.azure-pipelines.yml index 56b67ef6057..5e37bc4d3f4 100644 --- a/tests/wpt/tests/.azure-pipelines.yml +++ b/tests/wpt/tests/.azure-pipelines.yml @@ -71,7 +71,7 @@ jobs: displayName: './wpt test-jobs' condition: eq(variables['Build.Reason'], 'PullRequest') pool: - vmImage: 'ubuntu-20.04' + vmImage: 'ubuntu-24.04' steps: - task: UsePythonVersion@0 inputs: diff --git a/tests/wpt/tests/.github/workflows/docker.yml b/tests/wpt/tests/.github/workflows/docker.yml index 66794b69aba..06704fff106 100644 --- a/tests/wpt/tests/.github/workflows/docker.yml +++ b/tests/wpt/tests/.github/workflows/docker.yml @@ -40,7 +40,7 @@ jobs: latest type=raw,value=${{ inputs.tag }} - name: Build and push the Docker image - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 + uses: docker/build-push-action@1dc73863535b631f98b2378be8619f83b136f4a0 # v6.17.0 with: context: ./tools/docker push: true diff --git a/tests/wpt/tests/.github/workflows/documentation.yml b/tests/wpt/tests/.github/workflows/documentation.yml index 6c92e435c69..41cf7c331e1 100644 --- a/tests/wpt/tests/.github/workflows/documentation.yml +++ b/tests/wpt/tests/.github/workflows/documentation.yml @@ -2,7 +2,11 @@ name: documentation on: push: branches: - - ubuntu-24.04 + - master + paths: + - 'docs/**' + - 'resources/**' + - 'tools/**' pull_request: paths: - 'docs/**' @@ -30,4 +34,4 @@ jobs: - name: Run website_build.sh run: ./tools/ci/website_build.sh env: - DEPLOY_TOKEN: dummy + DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} diff --git a/tests/wpt/tests/.github/workflows/update_codeowners.yml b/tests/wpt/tests/.github/workflows/update_codeowners.yml new file mode 100644 index 00000000000..9e3f23efa7d --- /dev/null +++ b/tests/wpt/tests/.github/workflows/update_codeowners.yml @@ -0,0 +1,39 @@ +name: update codeowners +on: + schedule: + - cron: 0 0 * * * + workflow_dispatch: +jobs: + update: + runs-on: ubuntu-24.04 + if: github.repository == 'web-platform-tests/wpt' + steps: + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + - name: Checkout wpt + uses: actions/checkout@v4 + with: + path: wpt + - name: Checkout wpt-metadata + uses: actions/checkout@v4 + with: + path: wpt-metadata + repository: web-platform-tests/wpt-metadata + - name: Update codeowners + run: | + cd wpt + ./wpt update-codeowners ../wpt-metadata + - name: Commit and create pull request + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + author: wpt-pr-bot + commit-message: Automated update of CODEOWNERS + title: Update CODEOWNERS + body: | + This automated pull request updates CODEOWNERS + + See the [workflow](https://github.com/web-platform-tests/wpt/blob/master/.github/workflows/update_codeowners.yml) for how this pull request was created. + branch: actions/update-codeowners diff --git a/tests/wpt/tests/.taskcluster.yml b/tests/wpt/tests/.taskcluster.yml index 18c82321f92..9ade5168bbb 100644 --- a/tests/wpt/tests/.taskcluster.yml +++ b/tests/wpt/tests/.taskcluster.yml @@ -57,7 +57,7 @@ tasks: owner: ${owner} source: ${event.repository.clone_url} payload: - image: ghcr.io/web-platform-tests/wpt:1 + image: ghcr.io/web-platform-tests/wpt:2 maxRunTime: 7200 artifacts: public/results: diff --git a/tests/wpt/tests/CODEOWNERS b/tests/wpt/tests/CODEOWNERS index c5db991b400..da5b3f0d330 100644 --- a/tests/wpt/tests/CODEOWNERS +++ b/tests/wpt/tests/CODEOWNERS @@ -11,3 +11,3752 @@ tools/docker/Dockerfile @web-platform-tests/wpt-core-team @web-platform-tests/ad # Prevent accidentally touching wasm/core which is updated by a workflow wasm/core/ @web-platform-tests/wpt-core-team + +# GENERATED: interop-tests +css/css-anchor-position/anchor-as-multicol-crash.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-composited-scrolling-001-crash.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-composited-scrolling-002-crash.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-composited-scrolling-003-crash.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-composited-scrolling-004-crash.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-composited-scrolling-005-crash.html @web-platform-tests/interop +css/css-anchor-position/grid-anchor-center-crash.html @web-platform-tests/interop +css/css-anchor-position/inline-grid-try-fallbacks-crash.html @web-platform-tests/interop +css/css-anchor-position/position-try-invalid-anchor-crash.html @web-platform-tests/interop +css/css-cascade/scope-declaration-list-crash.html @web-platform-tests/interop +css/css-flexbox/contain-size-layout-abspos-flex-container-crash.html @web-platform-tests/interop +css/css-flexbox/fixedpos-video-in-abspos-quirk-crash.html @web-platform-tests/interop +css/css-flexbox/flex-shrink-large-value-crash.html @web-platform-tests/interop +css/css-flexbox/frameset-crash.html @web-platform-tests/interop +css/css-flexbox/inline-flex-editing-crash.html @web-platform-tests/interop +css/css-flexbox/inline-flex-editing-with-updating-text-crash.html @web-platform-tests/interop +css/css-flexbox/inline-flex-frameset-main-axis-crash.html @web-platform-tests/interop +css/css-flexbox/negative-flex-margins-crash.html @web-platform-tests/interop +css/css-flexbox/negative-flex-rounding-crash.html @web-platform-tests/interop +css/css-flexbox/negative-item-margins-002-crash.html @web-platform-tests/interop +css/css-flexbox/negative-item-margins-crash.html @web-platform-tests/interop +css/css-flexbox/orthogonal-flex-item-crash.html @web-platform-tests/interop +css/css-flexbox/position-relative-with-scrollable-with-abspos-crash.html @web-platform-tests/interop +css/css-flexbox/remove-out-of-flow-child-crash.html @web-platform-tests/interop +css/css-flexbox/zero-content-size-with-scrollbar-crash.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-crash.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-add-item-with-positioned-items-crash.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-add-positioned-block-item-after-inline-item-crash.html @web-platform-tests/interop +css/css-grid/subgrid/crashtests/contain-strict-nested-subgrid.html @web-platform-tests/interop +css/css-grid/subgrid/crashtests/contain-strict-subgrid.html @web-platform-tests/interop +css/css-grid/subgrid/crashtests/subgrid-reflow-root.html @web-platform-tests/interop +css/css-view-transitions/document-element-detached-crash.html @web-platform-tests/interop +css/css-view-transitions/empty-render-target-crash.html @web-platform-tests/interop +css/css-view-transitions/get-computed-style-crash.html @web-platform-tests/interop +css/css-view-transitions/iframe-transition-destroyed-document-crash.html @web-platform-tests/interop +css/css-view-transitions/list-style-position-style-change-crash.html @web-platform-tests/interop +css/css-view-transitions/root-element-cv-hidden-crash.html @web-platform-tests/interop +css/css-view-transitions/root-element-display-none-crash.html @web-platform-tests/interop +css/css-view-transitions/root-element-display-none-during-transition-crash.html @web-platform-tests/interop +css/css-viewport/zoom/perspective-small-effective-zoom-crash.html @web-platform-tests/interop +css/css-viewport/zoom/scroll-corner-crash.html @web-platform-tests/interop +css/css-viewport/zoom/scrollbar-crash.html @web-platform-tests/interop +css/css-viewport/zoom/textarea-very-small-zoom-crash.html @web-platform-tests/interop +css/css-writing-modes/link-writing-mode-dependency-crash.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-feimage-crash.html @web-platform-tests/interop +html/rendering/the-details-element/details-autofocus-crash.html @web-platform-tests/interop +html/rendering/the-details-element/empty-crash.html @web-platform-tests/interop +html/rendering/the-details-element/two-summaries-removal-crash.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-005-print.html @web-platform-tests/interop +css/css-cascade/scope-implicit-crash-print.html @web-platform-tests/interop +css/css-anchor-position/anchor-center-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-center-scroll.html @web-platform-tests/interop +css/css-anchor-position/anchor-in-css-min-max-function.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-005.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-circular.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-dynamic-005.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-007.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-008.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-011.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-top-layer-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-top-layer-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-top-layer-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-top-layer-004.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-top-layer-005.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-top-layer-006.html @web-platform-tests/interop +css/css-anchor-position/anchor-scope-scroll.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-chained-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-chained-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-chained-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-chained-004.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-chained-fallback.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-composited-scrolling-006.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-fixedpos-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-fixedpos.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-nested.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-overflow-hidden.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-012.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-scrollable-anchor.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-to-sticky-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-to-sticky-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-to-sticky-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-to-sticky-004.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-update-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-update-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-update-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-update-004.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-update-005.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-update-006.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-update-007.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-vlr.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-vrl.html @web-platform-tests/interop +css/css-anchor-position/position-anchor-001.html @web-platform-tests/interop +css/css-anchor-position/position-anchor-002.html @web-platform-tests/interop +css/css-anchor-position/position-anchor-004.html @web-platform-tests/interop +css/css-anchor-position/position-anchor-target-with-children.html @web-platform-tests/interop +css/css-anchor-position/position-area-abs-inline-container.html @web-platform-tests/interop +css/css-anchor-position/position-area-inline-container.html @web-platform-tests/interop +css/css-anchor-position/position-area-scroll-adjust.html @web-platform-tests/interop +css/css-anchor-position/position-try-switch-from-fixed-anchor.html @web-platform-tests/interop +css/css-anchor-position/position-try-switch-to-fixed-anchor.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-add-no-overflow.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible-after-scroll-in.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible-after-scroll-out.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible-chained-001.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible-chained-002.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible-chained-003.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible-chained-004.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible-change-anchor.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible-change-css-visibility.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible-css-visibility.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible-non-intervening-container.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible-stacked-child.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible-with-position.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-anchors-visible.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-no-overflow-scroll.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-no-overflow-stacked-child.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-no-overflow.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-remove-anchors-visible.html @web-platform-tests/interop +css/css-anchor-position/position-visibility-remove-no-overflow.html @web-platform-tests/interop +css/css-anchor-position/sticky-anchor-position-invalid.html @web-platform-tests/interop +css/css-break/transform-025.html @web-platform-tests/interop +css/css-cascade/scope-featureless.html @web-platform-tests/interop +css/css-cascade/scope-part.html @web-platform-tests/interop +css/css-cascade/scope-pseudo-element.html @web-platform-tests/interop +css/css-cascade/scope-shadow-sharing.html @web-platform-tests/interop +css/css-cascade/scope-visited.html @web-platform-tests/interop +css/css-flexbox/abspos/abspos-autopos-htb-ltr.html @web-platform-tests/interop +css/css-flexbox/abspos/abspos-autopos-htb-rtl.html @web-platform-tests/interop +css/css-flexbox/abspos/abspos-autopos-vlr-ltr.html @web-platform-tests/interop +css/css-flexbox/abspos/abspos-autopos-vlr-rtl.html @web-platform-tests/interop +css/css-flexbox/abspos/abspos-autopos-vrl-ltr.html @web-platform-tests/interop +css/css-flexbox/abspos/abspos-autopos-vrl-rtl.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-safe-001.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-fallback-justify-content-001.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-self-001.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-margin-001.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-margin-002.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-margin-003.html @web-platform-tests/interop +css/css-flexbox/abspos/flexbox-abspos-child-001a.html @web-platform-tests/interop +css/css-flexbox/abspos/flexbox-abspos-child-001b.html @web-platform-tests/interop +css/css-flexbox/abspos/flexbox-abspos-child-002.html @web-platform-tests/interop +css/css-flexbox/abspos/flexbox_absolute-atomic.html @web-platform-tests/interop +css/css-flexbox/abspos/flexbox_inline-abspos.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-005.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-006.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-007.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-008.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-009.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-010.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-011.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-containing-block-001.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-containing-block-002.html @web-platform-tests/interop +css/css-flexbox/align-baseline.html @web-platform-tests/interop +css/css-flexbox/align-content-001.htm @web-platform-tests/interop +css/css-flexbox/align-content-002.htm @web-platform-tests/interop +css/css-flexbox/align-content-003.htm @web-platform-tests/interop +css/css-flexbox/align-content-004.htm @web-platform-tests/interop +css/css-flexbox/align-content-005.htm @web-platform-tests/interop +css/css-flexbox/align-content-006.htm @web-platform-tests/interop +css/css-flexbox/align-content-wrap-004.html @web-platform-tests/interop +css/css-flexbox/align-content_center.html @web-platform-tests/interop +css/css-flexbox/align-content_flex-end.html @web-platform-tests/interop +css/css-flexbox/align-content_flex-start.html @web-platform-tests/interop +css/css-flexbox/align-content_space-around.html @web-platform-tests/interop +css/css-flexbox/align-content_space-between.html @web-platform-tests/interop +css/css-flexbox/align-content_stretch.html @web-platform-tests/interop +css/css-flexbox/align-items-001.htm @web-platform-tests/interop +css/css-flexbox/align-items-002.htm @web-platform-tests/interop +css/css-flexbox/align-items-003.htm @web-platform-tests/interop +css/css-flexbox/align-items-004.htm @web-platform-tests/interop +css/css-flexbox/align-items-005.htm @web-platform-tests/interop +css/css-flexbox/align-items-006.html @web-platform-tests/interop +css/css-flexbox/align-items-007.html @web-platform-tests/interop +css/css-flexbox/align-items-008.html @web-platform-tests/interop +css/css-flexbox/align-items-009.html @web-platform-tests/interop +css/css-flexbox/align-items-baseline-column-horz.html @web-platform-tests/interop +css/css-flexbox/align-items-baseline-column-vert.html @web-platform-tests/interop +css/css-flexbox/align-items-baseline-overflow-non-visible.html @web-platform-tests/interop +css/css-flexbox/align-items-baseline-row-horz.html @web-platform-tests/interop +css/css-flexbox/align-items-baseline-row-vert.html @web-platform-tests/interop +css/css-flexbox/align-self-001.html @web-platform-tests/interop +css/css-flexbox/align-self-002.html @web-platform-tests/interop +css/css-flexbox/align-self-003.html @web-platform-tests/interop +css/css-flexbox/align-self-004.html @web-platform-tests/interop +css/css-flexbox/align-self-005.html @web-platform-tests/interop +css/css-flexbox/align-self-007.html @web-platform-tests/interop +css/css-flexbox/align-self-008.html @web-platform-tests/interop +css/css-flexbox/align-self-009.html @web-platform-tests/interop +css/css-flexbox/align-self-011.html @web-platform-tests/interop +css/css-flexbox/align-self-012.html @web-platform-tests/interop +css/css-flexbox/align-self-013.html @web-platform-tests/interop +css/css-flexbox/align-self-015.html @web-platform-tests/interop +css/css-flexbox/anonymous-block.html @web-platform-tests/interop +css/css-flexbox/anonymous-flex-item-001.html @web-platform-tests/interop +css/css-flexbox/anonymous-flex-item-002.html @web-platform-tests/interop +css/css-flexbox/anonymous-flex-item-003.html @web-platform-tests/interop +css/css-flexbox/anonymous-flex-item-004.html @web-platform-tests/interop +css/css-flexbox/anonymous-flex-item-005.html @web-platform-tests/interop +css/css-flexbox/anonymous-flex-item-006.html @web-platform-tests/interop +css/css-flexbox/aspect-ratio-intrinsic-size-001.html @web-platform-tests/interop +css/css-flexbox/aspect-ratio-intrinsic-size-002.html @web-platform-tests/interop +css/css-flexbox/aspect-ratio-intrinsic-size-003.html @web-platform-tests/interop +css/css-flexbox/aspect-ratio-intrinsic-size-004.html @web-platform-tests/interop +css/css-flexbox/aspect-ratio-intrinsic-size-005.html @web-platform-tests/interop +css/css-flexbox/aspect-ratio-intrinsic-size-006.html @web-platform-tests/interop +css/css-flexbox/aspect-ratio-intrinsic-size-007.html @web-platform-tests/interop +css/css-flexbox/auto-height-column-with-border-and-padding.html @web-platform-tests/interop +css/css-flexbox/auto-height-with-flex.html @web-platform-tests/interop +css/css-flexbox/auto-margins-001.html @web-platform-tests/interop +css/css-flexbox/auto-margins-002.html @web-platform-tests/interop +css/css-flexbox/auto-margins-003.html @web-platform-tests/interop +css/css-flexbox/baseline-synthesis-001.html @web-platform-tests/interop +css/css-flexbox/baseline-synthesis-002.html @web-platform-tests/interop +css/css-flexbox/baseline-synthesis-003.html @web-platform-tests/interop +css/css-flexbox/baseline-synthesis-004.html @web-platform-tests/interop +css/css-flexbox/canvas-contain-size.html @web-platform-tests/interop +css/css-flexbox/content-height-with-scrollbars.html @web-platform-tests/interop +css/css-flexbox/cross-axis-scrollbar.html @web-platform-tests/interop +css/css-flexbox/css-box-justify-content.html @web-platform-tests/interop +css/css-flexbox/css-flexbox-img-expand-evenly.html @web-platform-tests/interop +css/css-flexbox/css-flexbox-row-reverse-wrap-reverse.html @web-platform-tests/interop +css/css-flexbox/css-flexbox-row-reverse-wrap.html @web-platform-tests/interop +css/css-flexbox/css-flexbox-row-reverse.html @web-platform-tests/interop +css/css-flexbox/css-flexbox-row-wrap-reverse.html @web-platform-tests/interop +css/css-flexbox/css-flexbox-row-wrap.html @web-platform-tests/interop +css/css-flexbox/css-flexbox-row.html @web-platform-tests/interop +css/css-flexbox/css-flexbox-test1.html @web-platform-tests/interop +css/css-flexbox/display-flex-001.htm @web-platform-tests/interop +css/css-flexbox/dynamic-baseline-change-nested.html @web-platform-tests/interop +css/css-flexbox/dynamic-baseline-change.html @web-platform-tests/interop +css/css-flexbox/dynamic-bsize-change.html @web-platform-tests/interop +css/css-flexbox/dynamic-change-simplified-layout-002.html @web-platform-tests/interop +css/css-flexbox/dynamic-change-simplified-layout.html @web-platform-tests/interop +css/css-flexbox/dynamic-isize-change-001.html @web-platform-tests/interop +css/css-flexbox/dynamic-isize-change-002.html @web-platform-tests/interop +css/css-flexbox/dynamic-isize-change-003.html @web-platform-tests/interop +css/css-flexbox/dynamic-isize-change-004.html @web-platform-tests/interop +css/css-flexbox/dynamic-stretch-change.html @web-platform-tests/interop +css/css-flexbox/fieldset-as-item-dynamic.html @web-platform-tests/interop +css/css-flexbox/fieldset-as-item-overflow.html @web-platform-tests/interop +css/css-flexbox/fieldset-baseline-alignment.html @web-platform-tests/interop +css/css-flexbox/fit-content-item-001.html @web-platform-tests/interop +css/css-flexbox/fit-content-item-002.html @web-platform-tests/interop +css/css-flexbox/fit-content-item-003.html @web-platform-tests/interop +css/css-flexbox/fit-content-item-004.html @web-platform-tests/interop +css/css-flexbox/fixed-table-layout-with-percentage-width-in-flex-item.html @web-platform-tests/interop +css/css-flexbox/flex-001.htm @web-platform-tests/interop +css/css-flexbox/flex-002.htm @web-platform-tests/interop +css/css-flexbox/flex-003.htm @web-platform-tests/interop +css/css-flexbox/flex-004.htm @web-platform-tests/interop +css/css-flexbox/flex-align-content-center.html @web-platform-tests/interop +css/css-flexbox/flex-align-content-end.html @web-platform-tests/interop +css/css-flexbox/flex-align-content-space-around.html @web-platform-tests/interop +css/css-flexbox/flex-align-content-space-between.html @web-platform-tests/interop +css/css-flexbox/flex-align-content-start.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-001.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-002.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-003.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-004.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-005.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-006.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-007.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-008.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-009.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-010.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-012.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-013.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-014.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-015.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-016.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-018.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-001.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-002.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-003.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-004.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-006.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-007.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-008.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-009.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-010.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-011.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-012.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-014.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-015.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-016.html @web-platform-tests/interop +css/css-flexbox/flex-basis-001.html @web-platform-tests/interop +css/css-flexbox/flex-basis-002.html @web-platform-tests/interop +css/css-flexbox/flex-basis-003.html @web-platform-tests/interop +css/css-flexbox/flex-basis-004.html @web-platform-tests/interop +css/css-flexbox/flex-basis-005.html @web-platform-tests/interop +css/css-flexbox/flex-basis-006.html @web-platform-tests/interop +css/css-flexbox/flex-basis-007.html @web-platform-tests/interop +css/css-flexbox/flex-basis-008.html @web-platform-tests/interop +css/css-flexbox/flex-basis-010.html @web-platform-tests/interop +css/css-flexbox/flex-basis-011.html @web-platform-tests/interop +css/css-flexbox/flex-basis-012.html @web-platform-tests/interop +css/css-flexbox/flex-basis-item-margins-001.html @web-platform-tests/interop +css/css-flexbox/flex-box-wrap.html @web-platform-tests/interop +css/css-flexbox/flex-child-percent-basis-resize-1.html @web-platform-tests/interop +css/css-flexbox/flex-container-margin.html @web-platform-tests/interop +css/css-flexbox/flex-direction-column-reverse.html @web-platform-tests/interop +css/css-flexbox/flex-direction-column.html @web-platform-tests/interop +css/css-flexbox/flex-direction-modify.html @web-platform-tests/interop +css/css-flexbox/flex-direction-row-reverse.html @web-platform-tests/interop +css/css-flexbox/flex-direction-row-vertical.html @web-platform-tests/interop +css/css-flexbox/flex-direction-with-element-insert.html @web-platform-tests/interop +css/css-flexbox/flex-direction.html @web-platform-tests/interop +css/css-flexbox/flex-flow-001.html @web-platform-tests/interop +css/css-flexbox/flex-flow-002.html @web-platform-tests/interop +css/css-flexbox/flex-flow-003.html @web-platform-tests/interop +css/css-flexbox/flex-flow-004.html @web-platform-tests/interop +css/css-flexbox/flex-flow-005.html @web-platform-tests/interop +css/css-flexbox/flex-flow-006.html @web-platform-tests/interop +css/css-flexbox/flex-flow-007.html @web-platform-tests/interop +css/css-flexbox/flex-flow-008.html @web-platform-tests/interop +css/css-flexbox/flex-flow-009.html @web-platform-tests/interop +css/css-flexbox/flex-flow-010.html @web-platform-tests/interop +css/css-flexbox/flex-flow-011.html @web-platform-tests/interop +css/css-flexbox/flex-flow-012.html @web-platform-tests/interop +css/css-flexbox/flex-grow-001.xht @web-platform-tests/interop +css/css-flexbox/flex-grow-002.html @web-platform-tests/interop +css/css-flexbox/flex-grow-003.html @web-platform-tests/interop +css/css-flexbox/flex-grow-004.html @web-platform-tests/interop +css/css-flexbox/flex-grow-005.html @web-platform-tests/interop +css/css-flexbox/flex-grow-006.html @web-platform-tests/interop +css/css-flexbox/flex-grow-007.html @web-platform-tests/interop +css/css-flexbox/flex-grow-008.html @web-platform-tests/interop +css/css-flexbox/flex-height-min-content.html @web-platform-tests/interop +css/css-flexbox/flex-inline.html @web-platform-tests/interop +css/css-flexbox/flex-item-and-percentage-abspos.html @web-platform-tests/interop +css/css-flexbox/flex-item-contains-size-layout-001.html @web-platform-tests/interop +css/css-flexbox/flex-item-transferred-sizes-padding-border-sizing.html @web-platform-tests/interop +css/css-flexbox/flex-item-transferred-sizes-padding-content-sizing.html @web-platform-tests/interop +css/css-flexbox/flex-item-vertical-align.html @web-platform-tests/interop +css/css-flexbox/flex-item-z-ordering-001.html @web-platform-tests/interop +css/css-flexbox/flex-lines/multi-line-wrap-reverse-column-reverse.html @web-platform-tests/interop +css/css-flexbox/flex-lines/multi-line-wrap-reverse-row-reverse.html @web-platform-tests/interop +css/css-flexbox/flex-lines/multi-line-wrap-with-column-reverse.html @web-platform-tests/interop +css/css-flexbox/flex-lines/multi-line-wrap-with-row-reverse.html @web-platform-tests/interop +css/css-flexbox/flex-margin-no-collapse.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-001.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-002.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-003.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-004.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-005.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-006.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-007.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-008.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-011.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-013.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-014.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-015.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-016.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-017.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-018.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-019.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-020.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-021.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-022.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-023.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-024.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-026.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-027.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-028.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-029.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-030.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-001.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-002.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-003.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-004.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-005.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-006.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-007.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-008.xht @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-009.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-010.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-011.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-012.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-013.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-015.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-016.html @web-platform-tests/interop +css/css-flexbox/flex-order.html @web-platform-tests/interop +css/css-flexbox/flex-shrink-001.html @web-platform-tests/interop +css/css-flexbox/flex-shrink-002.html @web-platform-tests/interop +css/css-flexbox/flex-shrink-003.html @web-platform-tests/interop +css/css-flexbox/flex-shrink-004.html @web-platform-tests/interop +css/css-flexbox/flex-shrink-005.html @web-platform-tests/interop +css/css-flexbox/flex-shrink-006.html @web-platform-tests/interop +css/css-flexbox/flex-shrink-007.html @web-platform-tests/interop +css/css-flexbox/flex-shrink-008.html @web-platform-tests/interop +css/css-flexbox/flex-vertical-align-effect.html @web-platform-tests/interop +css/css-flexbox/flex-wrap-002.html @web-platform-tests/interop +css/css-flexbox/flex-wrap-003.html @web-platform-tests/interop +css/css-flexbox/flex-wrap-004.html @web-platform-tests/interop +css/css-flexbox/flex-wrap-005.html @web-platform-tests/interop +css/css-flexbox/flex-wrap-006.html @web-platform-tests/interop +css/css-flexbox/flexbox-align-items-center-nested-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-baseline-horiz-001a.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-baseline-horiz-001b.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-baseline-horiz-002.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-baseline-horiz-003.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-baseline-horiz-004.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-baseline-horiz-005.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-baseline-horiz-006.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-baseline-horiz-007.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-baseline-horiz-008.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-horiz-001-block.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-horiz-001-table.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-horiz-002.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-horiz-003.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-horiz-004.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-horiz-005.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-stretch-vert-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-stretch-vert-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-vert-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-vert-002.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-vert-003.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-vert-004.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-vert-rtl-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-vert-rtl-002.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-vert-rtl-003.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-vert-rtl-004.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-align-self-vert-rtl-005.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-anonymous-items-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-align-self-baseline-horiz-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-align-self-baseline-vert-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-empty-001a.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-empty-001b.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-multi-item-horiz-001a.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-multi-item-horiz-001b.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-multi-item-vert-001a.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-multi-item-vert-001b.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-multi-line-horiz-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-multi-line-horiz-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-multi-line-horiz-003.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-multi-line-horiz-004.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-multi-line-vert-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-multi-line-vert-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-nested-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-single-item-001a.html @web-platform-tests/interop +css/css-flexbox/flexbox-baseline-single-item-001b.html @web-platform-tests/interop +css/css-flexbox/flexbox-basic-block-horiz-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-block-horiz-001v.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-block-vert-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-block-vert-001v.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-canvas-horiz-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-canvas-horiz-001v.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-canvas-vert-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-canvas-vert-001v.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-fieldset-horiz-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-fieldset-vert-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-iframe-horiz-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-iframe-vert-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-img-horiz-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-img-vert-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-textarea-horiz-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-textarea-vert-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-video-horiz-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-basic-video-vert-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-break-request-horiz-001a.html @web-platform-tests/interop +css/css-flexbox/flexbox-break-request-horiz-001b.html @web-platform-tests/interop +css/css-flexbox/flexbox-break-request-horiz-002a.html @web-platform-tests/interop +css/css-flexbox/flexbox-break-request-horiz-002b.html @web-platform-tests/interop +css/css-flexbox/flexbox-break-request-vert-001a.html @web-platform-tests/interop +css/css-flexbox/flexbox-break-request-vert-001b.html @web-platform-tests/interop +css/css-flexbox/flexbox-break-request-vert-002a.html @web-platform-tests/interop +css/css-flexbox/flexbox-break-request-vert-002b.html @web-platform-tests/interop +css/css-flexbox/flexbox-column-row-gap-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-column-row-gap-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-column-row-gap-003.html @web-platform-tests/interop +css/css-flexbox/flexbox-column-row-gap-004.html @web-platform-tests/interop +css/css-flexbox/flexbox-definite-cross-size-constrained-percentage.html @web-platform-tests/interop +css/css-flexbox/flexbox-definite-sizes-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-definite-sizes-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-definite-sizes-003.html @web-platform-tests/interop +css/css-flexbox/flexbox-definite-sizes-004.html @web-platform-tests/interop +css/css-flexbox/flexbox-definite-sizes-005.html @web-platform-tests/interop +css/css-flexbox/flexbox-definite-sizes-006.html @web-platform-tests/interop +css/css-flexbox/flexbox-dyn-resize-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-basis-content-001a.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-basis-content-001b.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-basis-content-002a.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-basis-content-002b.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-basis-content-003a.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-basis-content-003b.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-basis-content-004a.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-basis-content-004b.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-direction-column-percentage-ignored.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-direction-column-reverse.htm @web-platform-tests/interop +css/css-flexbox/flexbox-flex-direction-column.htm @web-platform-tests/interop +css/css-flexbox/flexbox-flex-direction-default.htm @web-platform-tests/interop +css/css-flexbox/flexbox-flex-direction-row-reverse.htm @web-platform-tests/interop +css/css-flexbox/flexbox-flex-direction-row.htm @web-platform-tests/interop +css/css-flexbox/flexbox-flex-flow-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-flow-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-wrap-default.htm @web-platform-tests/interop +css/css-flexbox/flexbox-flex-wrap-flexing.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-wrap-horiz-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-wrap-horiz-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-wrap-nowrap.htm @web-platform-tests/interop +css/css-flexbox/flexbox-flex-wrap-vert-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-wrap-vert-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-flex-wrap-wrap-reverse.htm @web-platform-tests/interop +css/css-flexbox/flexbox-flex-wrap-wrap.htm @web-platform-tests/interop +css/css-flexbox/flexbox-gap-position-absolute.html @web-platform-tests/interop +css/css-flexbox/flexbox-iframe-intrinsic-size-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-items-as-stacking-contexts-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-items-as-stacking-contexts-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-items-as-stacking-contexts-003.html @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-horiz-001a.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-horiz-001b.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-horiz-002.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-horiz-003.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-horiz-004.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-horiz-005.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-horiz-006.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-vert-001a.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-vert-001b.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-vert-002.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-vert-003.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-vert-004.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-vert-005.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-vert-006.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-wmvert-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-margin-auto-horiz-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-margin-auto-horiz-002.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-mbp-horiz-001-reverse.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-mbp-horiz-001-rtl-reverse.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-mbp-horiz-001-rtl.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-mbp-horiz-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-mbp-horiz-002a.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-mbp-horiz-002b.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-mbp-horiz-002v.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-mbp-horiz-003-reverse.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-mbp-horiz-003.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-mbp-horiz-003v.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-mbp-horiz-004.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-min-height-auto-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-height-auto-002a.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-height-auto-002b.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-height-auto-002c.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-height-auto-003.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-height-auto-004.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-width-auto-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-width-auto-002a.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-width-auto-002b.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-width-auto-002c.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-width-auto-003.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-width-auto-004.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-width-auto-005.html @web-platform-tests/interop +css/css-flexbox/flexbox-min-width-auto-006.html @web-platform-tests/interop +css/css-flexbox/flexbox-order-only-flexitems.html @web-platform-tests/interop +css/css-flexbox/flexbox-overflow-horiz-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-overflow-horiz-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-overflow-horiz-003.html @web-platform-tests/interop +css/css-flexbox/flexbox-overflow-horiz-004.html @web-platform-tests/interop +css/css-flexbox/flexbox-overflow-horiz-005.html @web-platform-tests/interop +css/css-flexbox/flexbox-overflow-padding-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-overflow-padding-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-overflow-vert-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-overflow-vert-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-overflow-vert-003.html @web-platform-tests/interop +css/css-flexbox/flexbox-overflow-vert-004.html @web-platform-tests/interop +css/css-flexbox/flexbox-overflow-vert-005.html @web-platform-tests/interop +css/css-flexbox/flexbox-paint-ordering-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-paint-ordering-002.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-paint-ordering-003.html @web-platform-tests/interop +css/css-flexbox/flexbox-root-node-001a.html @web-platform-tests/interop +css/css-flexbox/flexbox-root-node-001b.html @web-platform-tests/interop +css/css-flexbox/flexbox-safe-overflow-position-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-single-line-clamp-1.html @web-platform-tests/interop +css/css-flexbox/flexbox-single-line-clamp-2.html @web-platform-tests/interop +css/css-flexbox/flexbox-single-line-clamp-3.html @web-platform-tests/interop +css/css-flexbox/flexbox-sizing-horiz-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-sizing-horiz-002.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-sizing-vert-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-sizing-vert-002.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-table-fixup-001.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-whitespace-handling-001a.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-whitespace-handling-001b.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-whitespace-handling-002.xhtml @web-platform-tests/interop +css/css-flexbox/flexbox-with-multi-column-property.html @web-platform-tests/interop +css/css-flexbox/flexbox-with-pseudo-elements-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-with-pseudo-elements-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-with-pseudo-elements-003.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-001.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-003.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-004.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-005.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-006.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-007.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-008.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-009.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-010.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-011.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-012.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-013.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-014.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-015.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-016.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-slr-row-mix.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-slr-rtl.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-slr.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-srl-row-mix.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-srl-rtl.html @web-platform-tests/interop +css/css-flexbox/flexbox-writing-mode-srl.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-content-center.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-content-flexend.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-content-flexstart.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-content-spacearound.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-content-spacebetween.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-content-stretch-2.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-content-stretch.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-items-baseline.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-items-center-2.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-items-center-3.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-items-center.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-items-flexend-2.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-items-flexend.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-items-flexstart-2.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-items-flexstart.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-items-stretch-2.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-items-stretch-3.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-items-stretch-4.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-items-stretch.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-self-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-self-baseline.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-self-center.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-self-flexend.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-self-flexstart.html @web-platform-tests/interop +css/css-flexbox/flexbox_align-self-stretch.html @web-platform-tests/interop +css/css-flexbox/flexbox_block.html @web-platform-tests/interop +css/css-flexbox/flexbox_box-clear.html @web-platform-tests/interop +css/css-flexbox/flexbox_columns-flexitems-2.html @web-platform-tests/interop +css/css-flexbox/flexbox_columns-flexitems.html @web-platform-tests/interop +css/css-flexbox/flexbox_columns.html @web-platform-tests/interop +css/css-flexbox/flexbox_direction-column-reverse.html @web-platform-tests/interop +css/css-flexbox/flexbox_direction-column.html @web-platform-tests/interop +css/css-flexbox/flexbox_direction-row-reverse.html @web-platform-tests/interop +css/css-flexbox/flexbox_display.html @web-platform-tests/interop +css/css-flexbox/flexbox_fbfc.html @web-platform-tests/interop +css/css-flexbox/flexbox_fbfc2.html @web-platform-tests/interop +css/css-flexbox/flexbox_first-line.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-0-0-unitless.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-0-0.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-0-1-unitless-basis.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-0-N-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-0-N-unitless-basis.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-0-N.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-0-Npercent-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-0-Npercent.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-0-auto-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-0-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-0.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-1-0-unitless.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-1-0.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-1-1-unitless-basis.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-1-N-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-1-N-unitless-basis.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-1-N.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-1-Npercent-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-1-Npercent.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-1-auto-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-1-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-1.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-N-0-unitless.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-N-0.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-N-N-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-N-N.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-N-Npercent-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-N-Npercent.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-N-auto-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-N-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-N.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-0-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-0-0-unitless.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-0-0.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-0-N-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-0-N.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-0-Npercent-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-0-Npercent.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-0-auto-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-0-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-0.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-1-0-unitless.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-1-0.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-1-N-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-1-N.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-1-Npercent-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-1-Npercent.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-1-auto-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-1-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-1.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-N-0-unitless.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-N-0.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-N-N-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-N-N.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-N-Npercent-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-N-Npercent.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-N-auto-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-N-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-1-N.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-0-0-unitless.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-0-0.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-0-N-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-0-N.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-0-Npercent-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-0-Npercent.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-0-auto-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-0-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-0.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-1-0-unitless.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-1-0.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-1-N-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-1-N.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-1-Npercent-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-1-Npercent.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-1-auto-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-1-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-1.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-N-0-unitless.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-N-0.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-N-N-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-N-N.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-N-Npercent-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-N-Npercent.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-N-auto-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-N-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-N-N.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-basis-shrink.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-basis.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-formatting-interop.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-initial-2.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-initial.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-natural-mixed-basis-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-natural-mixed-basis.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-natural-variable-auto-basis.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-natural-variable-zero-basis.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-natural.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-none-wrappable-content.html @web-platform-tests/interop +css/css-flexbox/flexbox_flex-none.html @web-platform-tests/interop +css/css-flexbox/flexbox_flow-column-reverse-wrap-reverse.html @web-platform-tests/interop +css/css-flexbox/flexbox_flow-column-reverse-wrap.html @web-platform-tests/interop +css/css-flexbox/flexbox_flow-column-wrap-reverse.html @web-platform-tests/interop +css/css-flexbox/flexbox_flow-column-wrap.html @web-platform-tests/interop +css/css-flexbox/flexbox_flow-row-wrap-reverse.html @web-platform-tests/interop +css/css-flexbox/flexbox_flow-row-wrap.html @web-platform-tests/interop +css/css-flexbox/flexbox_generated-flex.html @web-platform-tests/interop +css/css-flexbox/flexbox_generated-nested-flex.html @web-platform-tests/interop +css/css-flexbox/flexbox_generated.html @web-platform-tests/interop +css/css-flexbox/flexbox_inline-float.html @web-platform-tests/interop +css/css-flexbox/flexbox_inline.html @web-platform-tests/interop +css/css-flexbox/flexbox_item-bottom-float.html @web-platform-tests/interop +css/css-flexbox/flexbox_item-clear.html @web-platform-tests/interop +css/css-flexbox/flexbox_item-float.html @web-platform-tests/interop +css/css-flexbox/flexbox_item-top-float.html @web-platform-tests/interop +css/css-flexbox/flexbox_item-vertical-align.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-center.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-end-rtl.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-end.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-flex-end.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-flex-start.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-left-001.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-left-002.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-right-001.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-right-002.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-spacearound-negative.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-spacearound-only.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-spacearound.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-spacebetween-negative.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-spacebetween-only.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-spacebetween.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-start-rtl.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-start.html @web-platform-tests/interop +css/css-flexbox/flexbox_margin-auto-overflow.html @web-platform-tests/interop +css/css-flexbox/flexbox_margin-auto.html @web-platform-tests/interop +css/css-flexbox/flexbox_margin-left-ex.html @web-platform-tests/interop +css/css-flexbox/flexbox_margin.html @web-platform-tests/interop +css/css-flexbox/flexbox_nested-flex.html @web-platform-tests/interop +css/css-flexbox/flexbox_object.html @web-platform-tests/interop +css/css-flexbox/flexbox_order-abspos-space-around.html @web-platform-tests/interop +css/css-flexbox/flexbox_order-box.html @web-platform-tests/interop +css/css-flexbox/flexbox_order-noninteger-invalid.html @web-platform-tests/interop +css/css-flexbox/flexbox_order.html @web-platform-tests/interop +css/css-flexbox/flexbox_quirks_body.html @web-platform-tests/interop +css/css-flexbox/flexbox_rowspan-overflow-automatic.html @web-platform-tests/interop +css/css-flexbox/flexbox_rowspan-overflow.html @web-platform-tests/interop +css/css-flexbox/flexbox_rowspan.html @web-platform-tests/interop +css/css-flexbox/flexbox_rtl-direction.html @web-platform-tests/interop +css/css-flexbox/flexbox_rtl-flow-reverse.html @web-platform-tests/interop +css/css-flexbox/flexbox_rtl-flow.html @web-platform-tests/interop +css/css-flexbox/flexbox_rtl-order.html @web-platform-tests/interop +css/css-flexbox/flexbox_stf-abspos.html @web-platform-tests/interop +css/css-flexbox/flexbox_stf-fixpos.html @web-platform-tests/interop +css/css-flexbox/flexbox_stf-float.html @web-platform-tests/interop +css/css-flexbox/flexbox_stf-inline-block.html @web-platform-tests/interop +css/css-flexbox/flexbox_stf-table-caption.html @web-platform-tests/interop +css/css-flexbox/flexbox_stf-table-cell.html @web-platform-tests/interop +css/css-flexbox/flexbox_stf-table-row-group.html @web-platform-tests/interop +css/css-flexbox/flexbox_stf-table-row.html @web-platform-tests/interop +css/css-flexbox/flexbox_stf-table-singleline-2.html @web-platform-tests/interop +css/css-flexbox/flexbox_stf-table-singleline.html @web-platform-tests/interop +css/css-flexbox/flexbox_stf-table.html @web-platform-tests/interop +css/css-flexbox/flexbox_table-fixed-layout.html @web-platform-tests/interop +css/css-flexbox/flexbox_width-overflow.html @web-platform-tests/interop +css/css-flexbox/flexbox_wrap-long.html @web-platform-tests/interop +css/css-flexbox/flexbox_wrap-reverse.html @web-platform-tests/interop +css/css-flexbox/flexbox_wrap.html @web-platform-tests/interop +css/css-flexbox/flexbox_writing_mode_vertical_lays_out_contents_from_top_to_bottom.html @web-platform-tests/interop +css/css-flexbox/flexible-box-float.html @web-platform-tests/interop +css/css-flexbox/flexible-order.html @web-platform-tests/interop +css/css-flexbox/flexitem-stretch-range.html @web-platform-tests/interop +css/css-flexbox/gap-001-lr.html @web-platform-tests/interop +css/css-flexbox/gap-001-ltr.html @web-platform-tests/interop +css/css-flexbox/gap-001-rl.html @web-platform-tests/interop +css/css-flexbox/gap-001-rtl.html @web-platform-tests/interop +css/css-flexbox/gap-002-lr.html @web-platform-tests/interop +css/css-flexbox/gap-002-ltr.html @web-platform-tests/interop +css/css-flexbox/gap-002-rl.html @web-platform-tests/interop +css/css-flexbox/gap-002-rtl.html @web-platform-tests/interop +css/css-flexbox/gap-003-lr.html @web-platform-tests/interop +css/css-flexbox/gap-003-ltr.html @web-platform-tests/interop +css/css-flexbox/gap-003-rl.html @web-platform-tests/interop +css/css-flexbox/gap-003-rtl.html @web-platform-tests/interop +css/css-flexbox/gap-004-lr.html @web-platform-tests/interop +css/css-flexbox/gap-004-ltr.html @web-platform-tests/interop +css/css-flexbox/gap-004-rl.html @web-platform-tests/interop +css/css-flexbox/gap-004-rtl.html @web-platform-tests/interop +css/css-flexbox/gap-005-lr.html @web-platform-tests/interop +css/css-flexbox/gap-005-ltr.html @web-platform-tests/interop +css/css-flexbox/gap-005-rl.html @web-platform-tests/interop +css/css-flexbox/gap-005-rtl.html @web-platform-tests/interop +css/css-flexbox/gap-006-lr.html @web-platform-tests/interop +css/css-flexbox/gap-006-ltr.html @web-platform-tests/interop +css/css-flexbox/gap-006-rl.html @web-platform-tests/interop +css/css-flexbox/gap-006-rtl.html @web-platform-tests/interop +css/css-flexbox/gap-007-lr.html @web-platform-tests/interop +css/css-flexbox/gap-007-ltr.html @web-platform-tests/interop +css/css-flexbox/gap-007-rl.html @web-platform-tests/interop +css/css-flexbox/gap-007-rtl.html @web-platform-tests/interop +css/css-flexbox/gap-008-ltr.html @web-platform-tests/interop +css/css-flexbox/gap-009-ltr.html @web-platform-tests/interop +css/css-flexbox/gap-010-ltr.html @web-platform-tests/interop +css/css-flexbox/gap-011.html @web-platform-tests/interop +css/css-flexbox/gap-012.html @web-platform-tests/interop +css/css-flexbox/gap-013.html @web-platform-tests/interop +css/css-flexbox/gap-014.html @web-platform-tests/interop +css/css-flexbox/gap-015.html @web-platform-tests/interop +css/css-flexbox/gap-016.html @web-platform-tests/interop +css/css-flexbox/grid-flex-item-001.html @web-platform-tests/interop +css/css-flexbox/grid-flex-item-002.html @web-platform-tests/interop +css/css-flexbox/grid-flex-item-003.html @web-platform-tests/interop +css/css-flexbox/grid-flex-item-004.html @web-platform-tests/interop +css/css-flexbox/grid-flex-item-005.html @web-platform-tests/interop +css/css-flexbox/grid-flex-item-006.html @web-platform-tests/interop +css/css-flexbox/image-items-flake-001.html @web-platform-tests/interop +css/css-flexbox/image-nested-within-definite-column-flexbox.html @web-platform-tests/interop +css/css-flexbox/inline-flex-min-content-height.html @web-platform-tests/interop +css/css-flexbox/item-with-max-height-and-scrollbar.html @web-platform-tests/interop +css/css-flexbox/item-with-table-with-infinite-max-intrinsic-width.html @web-platform-tests/interop +css/css-flexbox/justify-content-001.htm @web-platform-tests/interop +css/css-flexbox/justify-content-002.htm @web-platform-tests/interop +css/css-flexbox/justify-content-003.htm @web-platform-tests/interop +css/css-flexbox/justify-content-004.htm @web-platform-tests/interop +css/css-flexbox/justify-content-005.htm @web-platform-tests/interop +css/css-flexbox/justify-content-sideways-001.html @web-platform-tests/interop +css/css-flexbox/layout-algorithm_algo-cross-line-001.html @web-platform-tests/interop +css/css-flexbox/layout-algorithm_algo-cross-line-002.html @web-platform-tests/interop +css/css-flexbox/multiline-column-max-height.html @web-platform-tests/interop +css/css-flexbox/multiline-reverse-wrap-baseline.html @web-platform-tests/interop +css/css-flexbox/negative-margins-001.html @web-platform-tests/interop +css/css-flexbox/nested-orthogonal-flexbox-relayout.html @web-platform-tests/interop +css/css-flexbox/order/order-abs-children-painting-order.html @web-platform-tests/interop +css/css-flexbox/order/order-with-column-reverse.html @web-platform-tests/interop +css/css-flexbox/order/order-with-row-reverse.html @web-platform-tests/interop +css/css-flexbox/order-painting.html @web-platform-tests/interop +css/css-flexbox/ortho-table-item-001.html @web-platform-tests/interop +css/css-flexbox/overflow-area-001.html @web-platform-tests/interop +css/css-flexbox/overflow-area-002.html @web-platform-tests/interop +css/css-flexbox/overflow-area-003.html @web-platform-tests/interop +css/css-flexbox/overflow-auto-001.html @web-platform-tests/interop +css/css-flexbox/overflow-auto-005.html @web-platform-tests/interop +css/css-flexbox/overflow-auto-007.html @web-platform-tests/interop +css/css-flexbox/overflow-top-left.html @web-platform-tests/interop +css/css-flexbox/padding-overflow.html @web-platform-tests/interop +css/css-flexbox/percentage-descendant-of-anonymous-flex-item.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-002.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-004.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-005.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-006.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-007.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-008.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-009.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-010.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-014.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-015.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-016.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-017.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-018.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-019.html @web-platform-tests/interop +css/css-flexbox/percentage-max-height-001.html @web-platform-tests/interop +css/css-flexbox/percentage-max-height-002.html @web-platform-tests/interop +css/css-flexbox/percentage-max-height-003.html @web-platform-tests/interop +css/css-flexbox/percentage-max-height-004.html @web-platform-tests/interop +css/css-flexbox/percentage-padding-002.html @web-platform-tests/interop +css/css-flexbox/percentage-size-subitems-001.html @web-platform-tests/interop +css/css-flexbox/percentage-widths-001.html @web-platform-tests/interop +css/css-flexbox/position-absolute-scrollbar-freeze.html @web-platform-tests/interop +css/css-flexbox/position-fixed-001.html @web-platform-tests/interop +css/css-flexbox/position-relative-percentage-top-002.html @web-platform-tests/interop +css/css-flexbox/position-relative-percentage-top-003.html @web-platform-tests/interop +css/css-flexbox/scrollbars-auto.html @web-platform-tests/interop +css/css-flexbox/scrollbars-no-margin.html @web-platform-tests/interop +css/css-flexbox/scrollbars.html @web-platform-tests/interop +css/css-flexbox/select-element-zero-height-001.html @web-platform-tests/interop +css/css-flexbox/select-element-zero-height-002.html @web-platform-tests/interop +css/css-flexbox/stretch-flex-item-checkbox-input.html @web-platform-tests/interop +css/css-flexbox/stretch-flex-item-radio-input.html @web-platform-tests/interop +css/css-flexbox/stretch-input-in-column.html @web-platform-tests/interop +css/css-flexbox/stretch-obeys-min-max-001.html @web-platform-tests/interop +css/css-flexbox/stretch-obeys-min-max-002.html @web-platform-tests/interop +css/css-flexbox/stretch-obeys-min-max-003.html @web-platform-tests/interop +css/css-flexbox/stretching-orthogonal-flows.html @web-platform-tests/interop +css/css-flexbox/svg-no-natural-size-grandchild.html @web-platform-tests/interop +css/css-flexbox/svg-root-as-flex-item-001.html @web-platform-tests/interop +css/css-flexbox/svg-root-as-flex-item-002.html @web-platform-tests/interop +css/css-flexbox/svg-root-as-flex-item-003.html @web-platform-tests/interop +css/css-flexbox/svg-root-as-flex-item-004.html @web-platform-tests/interop +css/css-flexbox/svg-root-as-flex-item-005.html @web-platform-tests/interop +css/css-flexbox/synthesize-vrl-baseline.html @web-platform-tests/interop +css/css-flexbox/table-as-item-auto-min-width.html @web-platform-tests/interop +css/css-flexbox/table-as-item-change-cell.html @web-platform-tests/interop +css/css-flexbox/table-as-item-fixed-min-width-2.html @web-platform-tests/interop +css/css-flexbox/table-as-item-fixed-min-width-3.html @web-platform-tests/interop +css/css-flexbox/table-as-item-fixed-min-width.html @web-platform-tests/interop +css/css-flexbox/table-as-item-flex-cross-size.html @web-platform-tests/interop +css/css-flexbox/table-as-item-inflexible-in-column-1.html @web-platform-tests/interop +css/css-flexbox/table-as-item-inflexible-in-column-2.html @web-platform-tests/interop +css/css-flexbox/table-as-item-inflexible-in-row-1.html @web-platform-tests/interop +css/css-flexbox/table-as-item-inflexible-in-row-2.html @web-platform-tests/interop +css/css-flexbox/table-as-item-large-intrinsic-size.html @web-platform-tests/interop +css/css-flexbox/table-as-item-min-content-height-1.tentative.html @web-platform-tests/interop +css/css-flexbox/table-as-item-min-content-height-2.tentative.html @web-platform-tests/interop +css/css-flexbox/table-as-item-min-height-1.html @web-platform-tests/interop +css/css-flexbox/table-as-item-narrow-content-2.html @web-platform-tests/interop +css/css-flexbox/table-as-item-narrow-content.html @web-platform-tests/interop +css/css-flexbox/table-as-item-percent-width-cell-001.html @web-platform-tests/interop +css/css-flexbox/table-as-item-specified-height.html @web-platform-tests/interop +css/css-flexbox/table-as-item-specified-width-vertical.html @web-platform-tests/interop +css/css-flexbox/table-as-item-specified-width.html @web-platform-tests/interop +css/css-flexbox/table-as-item-stretch-cross-size-2.html @web-platform-tests/interop +css/css-flexbox/table-as-item-stretch-cross-size-3.html @web-platform-tests/interop +css/css-flexbox/table-as-item-stretch-cross-size-4.html @web-platform-tests/interop +css/css-flexbox/table-as-item-stretch-cross-size-5.html @web-platform-tests/interop +css/css-flexbox/table-as-item-stretch-cross-size.html @web-platform-tests/interop +css/css-flexbox/table-as-item-wide-content.html @web-platform-tests/interop +css/css-flexbox/table-item-flex-percentage-min-width.html @web-platform-tests/interop +css/css-flexbox/table-item-flex-percentage-width.html @web-platform-tests/interop +css/css-flexbox/table-with-float-paint.html @web-platform-tests/interop +css/css-flexbox/table-with-infinite-max-intrinsic-width.html @web-platform-tests/interop +css/css-flexbox/text-overflow-on-flexbox-001.html @web-platform-tests/interop +css/css-flexbox/whitespace-in-flexitem-001.html @web-platform-tests/interop +css/css-grid/abspos/absolute-positioning-changing-containing-block-001.html @web-platform-tests/interop +css/css-grid/abspos/descendant-static-position-001.html @web-platform-tests/interop +css/css-grid/abspos/descendant-static-position-002.html @web-platform-tests/interop +css/css-grid/abspos/descendant-static-position-003.html @web-platform-tests/interop +css/css-grid/abspos/descendant-static-position-004.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-img-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-img-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-img-last-baseline-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-img-last-baseline-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-last-baseline-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-last-baseline-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-003.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-004.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-last-baseline-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-last-baseline-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-last-baseline-003.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-rtl-last-baseline-004.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-safe-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-vertWM-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-vertWM-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-vertWM-003.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-vertWM-004.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-vertWM-last-baseline-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-vertWM-last-baseline-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-vertWM-last-baseline-003.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-align-self-vertWM-last-baseline-004.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-img-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-img-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-img-last-baseline-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-img-last-baseline-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-last-baseline-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-last-baseline-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-003.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-004.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-last-baseline-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-last-baseline-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-last-baseline-003.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-rtl-last-baseline-004.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-vertWM-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-vertWM-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-vertWM-003.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-vertWM-004.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-vertWM-last-baseline-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-vertWM-last-baseline-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-vertWM-last-baseline-003.html @web-platform-tests/interop +css/css-grid/abspos/grid-abspos-staticpos-justify-self-vertWM-last-baseline-004.html @web-platform-tests/interop +css/css-grid/abspos/grid-item-absolute-positioning-dynamic-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-paint-positioned-children-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-children-writing-modes-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-item-dynamic-change-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-item-dynamic-change-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-item-dynamic-change-003.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-item-dynamic-change-004.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-item-dynamic-change-005.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-item-dynamic-change-006.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-item-dynamic-change-007.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-background-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-background-rtl-001.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-001.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-002.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-003.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-004.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-005.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-006.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-007.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-008.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-009.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-010.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-011.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-012.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-013.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-014.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-015.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-016.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-items-017.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-017.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-001.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-002.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-003.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-004.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-005.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-006.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-007.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-008.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-009.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-010.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-011.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-012.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-013.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-014.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-015.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-016.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-017.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-018.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-019.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-020.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-021.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-022.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-023.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-024.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-025.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-negative-indices-001.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-negative-indices-002.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-negative-indices-003.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-sizing-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-baseline-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-baseline-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-baseline-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-baseline-align-cycles-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-block-axis-alignment-auto-margins-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-container-auto-margins-scrollbars-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-009.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-010.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-011.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-012.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-013.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-014.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-015.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-016.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-017.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-018.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-019.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-020.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-021.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-022.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-023.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-024.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-025.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-026.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-027.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-028.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-009.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-010.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-011.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-012.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-013.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-014.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-015.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-016.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-017.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-018.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-019.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-020.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-021.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-022.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-023.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-distribution-with-collapsed-tracks-024.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-009.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-010.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-011.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-012.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-013.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-014.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-015.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-016.html @web-platform-tests/interop +css/css-grid/alignment/grid-inline-axis-alignment-auto-margins-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-inline-baseline.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-aspect-ratio-stretch-1.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-aspect-ratio-stretch-2.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-aspect-ratio-stretch-3.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-aspect-ratio-stretch-4.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-auto-margins-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-auto-margins-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-1.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-2.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-3.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-4.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-5.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-no-aspect-ratio-stretch-6.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-self-baseline-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-justify-baseline-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-baseline-with-grid-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-baseline-with-grid-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-baseline-with-grid-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-baseline-with-grid-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-input-range.html @web-platform-tests/interop +css/css-grid/alignment/replaced-alignment-with-aspect-ratio-001.html @web-platform-tests/interop +css/css-grid/alignment/replaced-alignment-with-aspect-ratio-002.html @web-platform-tests/interop +css/css-grid/alignment/replaced-alignment-with-aspect-ratio-003.tentative.html @web-platform-tests/interop +css/css-grid/alignment/replaced-alignment-with-aspect-ratio-004.html @web-platform-tests/interop +css/css-grid/alignment/replaced-alignment-with-aspect-ratio-005.html @web-platform-tests/interop +css/css-grid/alignment/replaced-alignment-with-aspect-ratio-006.html @web-platform-tests/interop +css/css-grid/alignment/replaced-alignment-with-aspect-ratio-007.html @web-platform-tests/interop +css/css-grid/alignment/replaced-alignment-with-aspect-ratio-008.html @web-platform-tests/interop +css/css-grid/alignment/replaced-alignment-with-aspect-ratio-009.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-001.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-002-b.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-002.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-003.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-004.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-005.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-006.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-007.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-008.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-001.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-002.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-003.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-004.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-005.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-006.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-007.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-008.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-009.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-010.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-011.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-changes-grid-area-size-012.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-001.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-002.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-003.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-004.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-005.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-006.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-horiz-007.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-001.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-002.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-003.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-004.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-005.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-006.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-lr-007.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-001.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-002.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-003.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-004.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-005.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-006.html @web-platform-tests/interop +css/css-grid/alignment/self-baseline/grid-self-baseline-vertical-rl-007.html @web-platform-tests/interop +css/css-grid/animation/grid-template-columns-001.html @web-platform-tests/interop +css/css-grid/animation/grid-template-rows-001.html @web-platform-tests/interop +css/css-grid/anonymous-grid-items-001.html @web-platform-tests/interop +css/css-grid/child-border-box-and-max-content-001.html @web-platform-tests/interop +css/css-grid/child-border-box-and-max-content-002.html @web-platform-tests/interop +css/css-grid/chrome-bug-001.html @web-platform-tests/interop +css/css-grid/dynamic-grid-with-auto-fill.html @web-platform-tests/interop +css/css-grid/dynamic-grid-within-flexbox.html @web-platform-tests/interop +css/css-grid/empty-grid-within-flexbox.html @web-platform-tests/interop +css/css-grid/grid-child-percent-basis-resize-1.html @web-platform-tests/interop +css/css-grid/grid-container-baseline-synthesized-001.html @web-platform-tests/interop +css/css-grid/grid-container-baseline-synthesized-002.html @web-platform-tests/interop +css/css-grid/grid-container-baseline-synthesized-003.html @web-platform-tests/interop +css/css-grid/grid-container-baseline-synthesized-004.html @web-platform-tests/interop +css/css-grid/grid-definition/flex-item-grid-container-percentage-rows-001.html @web-platform-tests/interop +css/css-grid/grid-definition/fr-unit-with-percentage.html @web-platform-tests/interop +css/css-grid/grid-definition/fr-unit.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-aspect-ratio-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-aspect-ratio-002.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-dynamic-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-dynamic-002.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-dynamic-003.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-minmax.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-multiple-values-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-multiple-values-002.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-multiple-values-003.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-multiple-values-004.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-multiple-values-005.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-positioned-container-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-layout-auto-tracks.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-layout-basic.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-layout-repeat-notation.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-repeat-max-width-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-support-named-grid-lines-002.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-support-named-grid-lines-003.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-template-columns-fit-content-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-template-rows-fit-content-001.html @web-platform-tests/interop +css/css-grid/grid-item-non-auto-height-stretch-001.html @web-platform-tests/interop +css/css-grid/grid-item-non-auto-height-stretch-002.html @web-platform-tests/interop +css/css-grid/grid-item-non-auto-height-stretch-003.html @web-platform-tests/interop +css/css-grid/grid-item-non-auto-height-stretch-004.html @web-platform-tests/interop +css/css-grid/grid-item-percentage-quirk-001.html @web-platform-tests/interop +css/css-grid/grid-item-percentage-quirk-002.html @web-platform-tests/interop +css/css-grid/grid-items/anonymous-grid-item-001.html @web-platform-tests/interop +css/css-grid/grid-items/aspect-ratio-001.html @web-platform-tests/interop +css/css-grid/grid-items/aspect-ratio-002.html @web-platform-tests/interop +css/css-grid/grid-items/aspect-ratio-003.html @web-platform-tests/interop +css/css-grid/grid-items/aspect-ratio-004.html @web-platform-tests/interop +css/css-grid/grid-items/aspect-ratio-005.html @web-platform-tests/interop +css/css-grid/grid-items/explicitly-sized-grid-item-as-table.html @web-platform-tests/interop +css/css-grid/grid-items/grid-auto-margin-and-replaced-item-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-img-item-percent-max-height-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-items-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-items-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-items-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-items-inline-blocks-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-order-property-auto-placement-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-order-property-auto-placement-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-order-property-auto-placement-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-order-property-auto-placement-004.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-order-property-auto-placement-005.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-order-property-painting-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-order-property-painting-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-order-property-painting-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-order-property-painting-004.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-order-property-painting-005.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-z-axis-ordering-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-z-axis-ordering-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-z-axis-ordering-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-z-axis-ordering-004.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-z-axis-ordering-005.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-z-axis-ordering-overlapped-items-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-z-axis-ordering-overlapped-items-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-z-axis-ordering-overlapped-items-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-z-axis-ordering-overlapped-items-004.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-z-axis-ordering-overlapped-items-005.html @web-platform-tests/interop +css/css-grid/grid-items/grid-inline-z-axis-ordering-overlapped-items-006.html @web-platform-tests/interop +css/css-grid/grid-items/grid-intrinsic-maximums.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-containing-block-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-containing-block-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-containing-block-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-containing-block-004.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-inline-contribution-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-inline-contribution-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-inline-contribution-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-margins-and-writing-modes-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-overflow-auto-max-height-percentage.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-percentage-sizes-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-percentage-sizes-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-percentage-sizes-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-rel-pos-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-rel-pos-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-script-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-contribution-negative-margins.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-inline-blocks-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-004.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-005.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-006.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-007.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-008.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-009.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-010.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-011.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-012.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-013.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-014.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-004.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-005.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-006.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-007.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-008.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-009.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-010.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-011.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-012.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-013.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-014.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-sizing-alignment-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-layout-grid-in-grid.html @web-platform-tests/interop +css/css-grid/grid-items/grid-layout-z-order-a.html @web-platform-tests/interop +css/css-grid/grid-items/grid-layout-z-order-b.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-004.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-005.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-006.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-007.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-008.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-009.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-010.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-011.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-012.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-013.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-014.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-015.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-016.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-017.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-018.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-019.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-020.html @web-platform-tests/interop +css/css-grid/grid-items/grid-order-property-auto-placement-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-order-property-auto-placement-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-order-property-auto-placement-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-order-property-auto-placement-004.html @web-platform-tests/interop +css/css-grid/grid-items/grid-order-property-auto-placement-005.html @web-platform-tests/interop +css/css-grid/grid-items/grid-order-property-painting-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-order-property-painting-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-order-property-painting-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-order-property-painting-004.html @web-platform-tests/interop +css/css-grid/grid-items/grid-order-property-painting-005.html @web-platform-tests/interop +css/css-grid/grid-items/grid-size-with-orthogonal-child-dynamic.html @web-platform-tests/interop +css/css-grid/grid-items/grid-z-axis-ordering-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-z-axis-ordering-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-z-axis-ordering-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-z-axis-ordering-004.html @web-platform-tests/interop +css/css-grid/grid-items/grid-z-axis-ordering-005.html @web-platform-tests/interop +css/css-grid/grid-items/grid-z-axis-ordering-overlapped-items-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-z-axis-ordering-overlapped-items-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-z-axis-ordering-overlapped-items-003.html @web-platform-tests/interop +css/css-grid/grid-items/grid-z-axis-ordering-overlapped-items-004.html @web-platform-tests/interop +css/css-grid/grid-items/grid-z-axis-ordering-overlapped-items-005.html @web-platform-tests/interop +css/css-grid/grid-items/grid-z-axis-ordering-overlapped-items-006.html @web-platform-tests/interop +css/css-grid/grid-items/item-with-table-with-infinite-max-intrinsic-width.html @web-platform-tests/interop +css/css-grid/grid-items/percentage-margin-dynamic.html @web-platform-tests/interop +css/css-grid/grid-items/percentage-size-indefinite-replaced.html @web-platform-tests/interop +css/css-grid/grid-items/percentage-size-replaced-subitems-001.html @web-platform-tests/interop +css/css-grid/grid-items/percentage-size-subitems-001.html @web-platform-tests/interop +css/css-grid/grid-items/percentage-size-subitems-002.html @web-platform-tests/interop +css/css-grid/grid-items/remove-svg-grid-item-001.html @web-platform-tests/interop +css/css-grid/grid-items/replaced-element-001.html @web-platform-tests/interop +css/css-grid/grid-items/replaced-element-002.html @web-platform-tests/interop +css/css-grid/grid-items/replaced-element-007.html @web-platform-tests/interop +css/css-grid/grid-items/replaced-element-010.html @web-platform-tests/interop +css/css-grid/grid-items/replaced-element-011.html @web-platform-tests/interop +css/css-grid/grid-items/replaced-element-012.html @web-platform-tests/interop +css/css-grid/grid-items/replaced-element-013.html @web-platform-tests/interop +css/css-grid/grid-items/replaced-element-014.html @web-platform-tests/interop +css/css-grid/grid-items/replaced-element-015.html @web-platform-tests/interop +css/css-grid/grid-items/table-with-infinite-max-intrinsic-width.html @web-platform-tests/interop +css/css-grid/grid-items/whitespace-in-grid-item-001.html @web-platform-tests/interop +css/css-grid/grid-model/column-property-should-not-apply-on-grid-container-001.html @web-platform-tests/interop +css/css-grid/grid-model/display-grid.html @web-platform-tests/interop +css/css-grid/grid-model/display-inline-grid.html @web-platform-tests/interop +css/css-grid/grid-model/grid-areas-overflowing-grid-container-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-areas-overflowing-grid-container-002.html @web-platform-tests/interop +css/css-grid/grid-model/grid-areas-overflowing-grid-container-003.html @web-platform-tests/interop +css/css-grid/grid-model/grid-areas-overflowing-grid-container-004.html @web-platform-tests/interop +css/css-grid/grid-model/grid-areas-overflowing-grid-container-005.html @web-platform-tests/interop +css/css-grid/grid-model/grid-areas-overflowing-grid-container-006.html @web-platform-tests/interop +css/css-grid/grid-model/grid-areas-overflowing-grid-container-007.html @web-platform-tests/interop +css/css-grid/grid-model/grid-areas-overflowing-grid-container-008.html @web-platform-tests/interop +css/css-grid/grid-model/grid-areas-overflowing-grid-container-009.html @web-platform-tests/interop +css/css-grid/grid-model/grid-container-ignores-first-letter-002.html @web-platform-tests/interop +css/css-grid/grid-model/grid-container-scrollbar-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-container-scrollbar-vertical-lr-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-container-scrollbar-vertical-rl-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-container-scrollbars-sizing-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-container-scrollbars-sizing-002.html @web-platform-tests/interop +css/css-grid/grid-model/grid-display-grid-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-display-inline-grid-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-first-letter-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-first-letter-002.html @web-platform-tests/interop +css/css-grid/grid-model/grid-first-letter-003.html @web-platform-tests/interop +css/css-grid/grid-model/grid-first-line-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-first-line-002.html @web-platform-tests/interop +css/css-grid/grid-model/grid-first-line-003.html @web-platform-tests/interop +css/css-grid/grid-model/grid-float-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-floats-no-intrude-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-floats-no-intrude-002.html @web-platform-tests/interop +css/css-grid/grid-model/grid-inline-first-letter-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-inline-first-letter-002.html @web-platform-tests/interop +css/css-grid/grid-model/grid-inline-first-letter-003.html @web-platform-tests/interop +css/css-grid/grid-model/grid-inline-first-line-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-inline-first-line-002.html @web-platform-tests/interop +css/css-grid/grid-model/grid-inline-first-line-003.html @web-platform-tests/interop +css/css-grid/grid-model/grid-inline-float-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-inline-floats-no-intrude-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-inline-margins-no-collapse-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-inline-multicol-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-inline-vertical-align-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-layout-stale-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-layout-stale-002.html @web-platform-tests/interop +css/css-grid/grid-model/grid-margins-no-collapse-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-margins-no-collapse-002.html @web-platform-tests/interop +css/css-grid/grid-model/grid-multicol-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-overflow-padding-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-overflow-padding-002.html @web-platform-tests/interop +css/css-grid/grid-model/grid-vertical-align-001.html @web-platform-tests/interop +css/css-grid/grid-with-content-dynamic-display-001.html @web-platform-tests/interop +css/css-grid/grid-with-content-dynamic-display-002.html @web-platform-tests/interop +css/css-grid/grid-with-dynamic-img.html @web-platform-tests/interop +css/css-grid/grid-with-orthogonal-child-within-flexbox.html @web-platform-tests/interop +css/css-grid/grid-within-flexbox-definite-change.html @web-platform-tests/interop +css/css-grid/grid-within-flexbox-indefinite.html @web-platform-tests/interop +css/css-grid/implicit-grids/grid-support-grid-auto-columns-rows-001.html @web-platform-tests/interop +css/css-grid/implicit-grids/grid-support-grid-auto-columns-rows-002.html @web-platform-tests/interop +css/css-grid/implicit-grids/grid-support-grid-auto-columns-rows-003.html @web-platform-tests/interop +css/css-grid/layout-algorithm/auto-margins-ignored-during-track-sizing-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/flex-and-intrinsic-sizes-002.html @web-platform-tests/interop +css/css-grid/layout-algorithm/flex-sizing-rows-indefinite-height.html @web-platform-tests/interop +css/css-grid/layout-algorithm/flex-tracks-with-fractional-size.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-as-flex-item-should-not-shrink-to-fit-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-as-flex-item-should-not-shrink-to-fit-002.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-as-flex-item-should-not-shrink-to-fit-003.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-as-flex-item-should-not-shrink-to-fit-004.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-as-flex-item-should-not-shrink-to-fit-005.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-as-flex-item-should-not-shrink-to-fit-006.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-as-flex-item-should-not-shrink-to-fit-007.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-as-flex-item-should-not-shrink-to-fit-008.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-find-fr-size-restart-algorithm.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-intrinsic-size-dynamic-block-size.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-layout-free-space-unit.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-percent-cols-filled-shrinkwrap-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-percent-cols-spanned-shrinkwrap-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-percent-rows-filled-shrinkwrap-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-percent-rows-spanned-shrinkwrap-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-stretch-respects-min-size-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-template-flexible-rerun-track-sizing.html @web-platform-tests/interop +css/css-grid/nested-grid-item-block-size-001.html @web-platform-tests/interop +css/css-grid/placement/grid-layout-grid-span.html @web-platform-tests/interop +css/css-grid/placement/grid-layout-lines-shorthands.html @web-platform-tests/interop +css/css-grid/placement/grid-layout-lines.html @web-platform-tests/interop +css/css-grid/placement/grid-layout-placement-shorthands.html @web-platform-tests/interop +css/css-grid/placement/grid-placement-items-spanning-multiple-rows-001.html @web-platform-tests/interop +css/css-grid/placement/grid-placement-items-spanning-multiple-rows-002.html @web-platform-tests/interop +css/css-grid/placement/grid-placement-using-named-grid-lines-001.html @web-platform-tests/interop +css/css-grid/placement/grid-placement-using-named-grid-lines-002.html @web-platform-tests/interop +css/css-grid/placement/grid-placement-using-named-grid-lines-003.html @web-platform-tests/interop +css/css-grid/placement/grid-placement-using-named-grid-lines-004.html @web-platform-tests/interop +css/css-grid/placement/grid-placement-using-named-grid-lines-005.html @web-platform-tests/interop +css/css-grid/placement/grid-placement-using-named-grid-lines-006.html @web-platform-tests/interop +css/css-grid/placement/grid-placement-using-named-grid-lines-007.html @web-platform-tests/interop +css/css-grid/placement/grid-placement-using-named-grid-lines-008.html @web-platform-tests/interop +css/css-grid/placement/grid-placement-using-named-grid-lines-009.html @web-platform-tests/interop +css/css-grid/placement/grid-template-areas-must-keep-named-columns-order-001.html @web-platform-tests/interop +css/css-grid/relative-grandchild.html @web-platform-tests/interop +css/css-grid/stretch-grid-item-checkbox-input.html @web-platform-tests/interop +css/css-grid/stretch-grid-item-radio-input.html @web-platform-tests/interop +css/css-grid/subgrid/abs-pos-001.html @web-platform-tests/interop +css/css-grid/subgrid/abs-pos-002.html @web-platform-tests/interop +css/css-grid/subgrid/auto-track-sizing-001.html @web-platform-tests/interop +css/css-grid/subgrid/auto-track-sizing-002.html @web-platform-tests/interop +css/css-grid/subgrid/auto-track-sizing-003.html @web-platform-tests/interop +css/css-grid/subgrid/baseline-001.html @web-platform-tests/interop +css/css-grid/subgrid/contribution-size-flex-tracks-001.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-001.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-002.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-003.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-004.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-005.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-006.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-007.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-008.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-009.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-010.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-011.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-larger-001.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-larger-002.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-normal-001.html @web-platform-tests/interop +css/css-grid/subgrid/grid-gap-smaller-001.html @web-platform-tests/interop +css/css-grid/subgrid/independent-formatting-context.html @web-platform-tests/interop +css/css-grid/subgrid/item-percentage-height-001.html @web-platform-tests/interop +css/css-grid/subgrid/line-names-001.html @web-platform-tests/interop +css/css-grid/subgrid/line-names-002.html @web-platform-tests/interop +css/css-grid/subgrid/line-names-003.html @web-platform-tests/interop +css/css-grid/subgrid/line-names-004.html @web-platform-tests/interop +css/css-grid/subgrid/line-names-005.html @web-platform-tests/interop +css/css-grid/subgrid/line-names-006.html @web-platform-tests/interop +css/css-grid/subgrid/line-names-007.html @web-platform-tests/interop +css/css-grid/subgrid/line-names-008.html @web-platform-tests/interop +css/css-grid/subgrid/line-names-009.html @web-platform-tests/interop +css/css-grid/subgrid/line-names-010.html @web-platform-tests/interop +css/css-grid/subgrid/line-names-011.html @web-platform-tests/interop +css/css-grid/subgrid/line-names-012.html @web-platform-tests/interop +css/css-grid/subgrid/orthogonal-writing-mode-001.html @web-platform-tests/interop +css/css-grid/subgrid/orthogonal-writing-mode-002.html @web-platform-tests/interop +css/css-grid/subgrid/orthogonal-writing-mode-003.html @web-platform-tests/interop +css/css-grid/subgrid/orthogonal-writing-mode-004.html @web-platform-tests/interop +css/css-grid/subgrid/orthogonal-writing-mode-005.html @web-platform-tests/interop +css/css-grid/subgrid/orthogonal-writing-mode-006.html @web-platform-tests/interop +css/css-grid/subgrid/parent-repeat-auto-fit-001.html @web-platform-tests/interop +css/css-grid/subgrid/parent-repeat-auto-fit-002.html @web-platform-tests/interop +css/css-grid/subgrid/placement-implicit-001.html @web-platform-tests/interop +css/css-grid/subgrid/repeat-auto-fill-001.html @web-platform-tests/interop +css/css-grid/subgrid/repeat-auto-fill-002.html @web-platform-tests/interop +css/css-grid/subgrid/repeat-auto-fill-003.html @web-platform-tests/interop +css/css-grid/subgrid/repeat-auto-fill-004.html @web-platform-tests/interop +css/css-grid/subgrid/repeat-auto-fill-005.html @web-platform-tests/interop +css/css-grid/subgrid/repeat-auto-fill-006.html @web-platform-tests/interop +css/css-grid/subgrid/repeat-auto-fill-007.html @web-platform-tests/interop +css/css-grid/subgrid/repeat-auto-fill-008.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-baseline-001.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-baseline-002.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-baseline-003.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-baseline-004.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-item-block-size-001.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-no-items-on-edges-001.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-no-items-on-edges-002.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-stretch.html @web-platform-tests/interop +css/css-grid/subgrid/writing-directions-001.html @web-platform-tests/interop +css/css-grid/subgrid/writing-directions-002.html @web-platform-tests/interop +css/css-grid/subgrid/writing-directions-003.html @web-platform-tests/interop +css/css-grid/table-grid-item-dynamic-001.html @web-platform-tests/interop +css/css-grid/table-grid-item-dynamic-002.html @web-platform-tests/interop +css/css-grid/table-grid-item-dynamic-003.html @web-platform-tests/interop +css/css-grid/table-grid-item-dynamic-004.html @web-platform-tests/interop +css/css-grid/whitespace-reattach.html @web-platform-tests/interop +css/css-lists/list-style-position-001.html @web-platform-tests/interop +css/css-multicol/multicol-span-all-rule-002.html @web-platform-tests/interop +css/css-shapes/shape-outside/shape-box/shape-outside-border-box-border-radius-009.html @web-platform-tests/interop +css/css-shapes/shape-outside/shape-box/shape-outside-border-box-border-radius-010.html @web-platform-tests/interop +css/css-shapes/shape-outside/shape-box/shape-outside-border-box-border-radius-011.html @web-platform-tests/interop +css/css-shapes/shape-outside/shape-box/shape-outside-border-box-border-radius-012.html @web-platform-tests/interop +css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-007.html @web-platform-tests/interop +css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-008.html @web-platform-tests/interop +css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-013.html @web-platform-tests/interop +css/css-shapes/shape-outside/shape-image/gradients/shape-outside-linear-gradient-014.html @web-platform-tests/interop +css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-052.html @web-platform-tests/interop +css/css-shapes/shape-outside/supported-shapes/circle/shape-outside-circle-053.html @web-platform-tests/interop +css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-050.html @web-platform-tests/interop +css/css-shapes/shape-outside/supported-shapes/ellipse/shape-outside-ellipse-051.html @web-platform-tests/interop +css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-026.html @web-platform-tests/interop +css/css-shapes/shape-outside/supported-shapes/inset/shape-outside-inset-027.html @web-platform-tests/interop +css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-024.html @web-platform-tests/interop +css/css-shapes/shape-outside/supported-shapes/polygon/shape-outside-polygon-025.html @web-platform-tests/interop +css/css-text-decor/text-decoration-skip-ink-sidewayslr-001.html @web-platform-tests/interop +css/css-text-decor/text-decoration-skip-ink-sidewayslr-002.html @web-platform-tests/interop +css/css-text-decor/text-decoration-skip-ink-sidewaysrl-001.html @web-platform-tests/interop +css/css-text-decor/text-decoration-skip-ink-sidewaysrl-002.html @web-platform-tests/interop +css/css-text-decor/text-underline-offset-vertical-002.html @web-platform-tests/interop +css/css-text-decor/text-underline-position-001a.html @web-platform-tests/interop +css/css-text-decor/text-underline-position-001b.html @web-platform-tests/interop +css/css-ui/compute-kind-widget-generated/grouped-kind-of-widget-fallback-background-attachment-001.html @web-platform-tests/interop +css/css-ui/compute-kind-widget-generated/grouped-kind-of-widget-fallback-background-color-001.html @web-platform-tests/interop +css/css-ui/compute-kind-widget-generated/grouped-kind-of-widget-fallback-border-block-start-color-001.html @web-platform-tests/interop +css/css-ui/compute-kind-widget-generated/grouped-kind-of-widget-fallback-border-block-start-style-001.html @web-platform-tests/interop +css/css-ui/compute-kind-widget-generated/grouped-kind-of-widget-fallback-border-block-start-width-001.html @web-platform-tests/interop +css/css-ui/compute-kind-widget-generated/grouped-kind-of-widget-fallback-border-image-source-001.html @web-platform-tests/interop +css/css-ui/compute-kind-widget-generated/grouped-kind-of-widget-fallback-border-top-color-001.html @web-platform-tests/interop +css/css-ui/compute-kind-widget-generated/grouped-kind-of-widget-fallback-border-top-left-radius-001.html @web-platform-tests/interop +css/css-ui/compute-kind-widget-generated/grouped-kind-of-widget-fallback-border-top-style-001.html @web-platform-tests/interop +css/css-ui/compute-kind-widget-generated/grouped-kind-of-widget-fallback-border-top-width-001.html @web-platform-tests/interop +css/css-view-transitions/3d-transform-incoming.html @web-platform-tests/interop +css/css-view-transitions/3d-transform-outgoing.html @web-platform-tests/interop +css/css-view-transitions/active-view-transition-on-non-root.html @web-platform-tests/interop +css/css-view-transitions/active-view-transition-pseudo-class-match.html @web-platform-tests/interop +css/css-view-transitions/active-view-transition-type-on-non-root.html @web-platform-tests/interop +css/css-view-transitions/animating-new-content-subset.html @web-platform-tests/interop +css/css-view-transitions/animating-new-content.html @web-platform-tests/interop +css/css-view-transitions/auto-name-from-id-shadow.html @web-platform-tests/interop +css/css-view-transitions/auto-name-from-id.html @web-platform-tests/interop +css/css-view-transitions/auto-name.html @web-platform-tests/interop +css/css-view-transitions/backdrop-filter-animated.html @web-platform-tests/interop +css/css-view-transitions/backdrop-filter-captured.html @web-platform-tests/interop +css/css-view-transitions/block-with-overflowing-text.html @web-platform-tests/interop +css/css-view-transitions/break-inside-avoid-child.html @web-platform-tests/interop +css/css-view-transitions/capture-old-state-error-flag-cleanup.html @web-platform-tests/interop +css/css-view-transitions/capture-with-offscreen-child-translated.html @web-platform-tests/interop +css/css-view-transitions/capture-with-offscreen-child.html @web-platform-tests/interop +css/css-view-transitions/capture-with-opacity-zero-child.html @web-platform-tests/interop +css/css-view-transitions/capture-with-visibility-hidden-child.html @web-platform-tests/interop +css/css-view-transitions/capture-with-visibility-mixed-descendants.html @web-platform-tests/interop +css/css-view-transitions/class-specificity.html @web-platform-tests/interop +css/css-view-transitions/clip-path-larger-than-border-box-on-child-of-named-element.html @web-platform-tests/interop +css/css-view-transitions/content-smaller-than-box-size.html @web-platform-tests/interop +css/css-view-transitions/content-visibility-auto-shared-element.html @web-platform-tests/interop +css/css-view-transitions/content-with-child-with-transparent-background.html @web-platform-tests/interop +css/css-view-transitions/content-with-clip-root.html @web-platform-tests/interop +css/css-view-transitions/content-with-clip.html @web-platform-tests/interop +css/css-view-transitions/content-with-inline-child.html @web-platform-tests/interop +css/css-view-transitions/content-with-transform-new-image.html @web-platform-tests/interop +css/css-view-transitions/content-with-transform-old-image.html @web-platform-tests/interop +css/css-view-transitions/content-with-transparent-background.html @web-platform-tests/interop +css/css-view-transitions/css-tags-paint-order-with-entry.html @web-platform-tests/interop +css/css-view-transitions/css-tags-paint-order.html @web-platform-tests/interop +css/css-view-transitions/css-tags-shared-element.html @web-platform-tests/interop +css/css-view-transitions/dialog-in-rtl-iframe.html @web-platform-tests/interop +css/css-view-transitions/dialog-in-top-layer-during-transition-new.html @web-platform-tests/interop +css/css-view-transitions/dialog-in-top-layer-during-transition-old.html @web-platform-tests/interop +css/css-view-transitions/element-is-grouping-during-animation.html @web-platform-tests/interop +css/css-view-transitions/element-stops-grouping-after-animation.html @web-platform-tests/interop +css/css-view-transitions/element-with-overflow.html @web-platform-tests/interop +css/css-view-transitions/exit-transition-with-anonymous-layout-object.html @web-platform-tests/interop +css/css-view-transitions/far-away-capture.html @web-platform-tests/interop +css/css-view-transitions/fractional-box-new.html @web-platform-tests/interop +css/css-view-transitions/fractional-box-old.html @web-platform-tests/interop +css/css-view-transitions/fractional-box-with-overflow-children-new.html @web-platform-tests/interop +css/css-view-transitions/fractional-box-with-overflow-children-old.html @web-platform-tests/interop +css/css-view-transitions/fractional-box-with-shadow-new.html @web-platform-tests/interop +css/css-view-transitions/fractional-box-with-shadow-old.html @web-platform-tests/interop +css/css-view-transitions/fractional-translation-from-position.html @web-platform-tests/interop +css/css-view-transitions/fractional-translation-from-transform.html @web-platform-tests/interop +css/css-view-transitions/fragmented-at-start-ignored.html @web-platform-tests/interop +css/css-view-transitions/fragmented-during-transition-skips.html @web-platform-tests/interop +css/css-view-transitions/hit-test-unpainted-element.html @web-platform-tests/interop +css/css-view-transitions/hit-test-unrelated-element.html @web-platform-tests/interop +css/css-view-transitions/iframe-and-main-frame-transition-new-main-new-iframe.html @web-platform-tests/interop +css/css-view-transitions/iframe-and-main-frame-transition-new-main-old-iframe.html @web-platform-tests/interop +css/css-view-transitions/iframe-and-main-frame-transition-old-main-new-iframe.html @web-platform-tests/interop +css/css-view-transitions/iframe-and-main-frame-transition-old-main-old-iframe.html @web-platform-tests/interop +css/css-view-transitions/iframe-and-main-frame-transition-old-main.html @web-platform-tests/interop +css/css-view-transitions/iframe-and-main-frame-transition-with-name-on-iframe.html @web-platform-tests/interop +css/css-view-transitions/iframe-new-has-scrollbar.html @web-platform-tests/interop +css/css-view-transitions/iframe-old-has-scrollbar.html @web-platform-tests/interop +css/css-view-transitions/iframe-transition.sub.html @web-platform-tests/interop +css/css-view-transitions/inline-child-with-filter.html @web-platform-tests/interop +css/css-view-transitions/inline-child-with-overflow-shadow.html @web-platform-tests/interop +css/css-view-transitions/inline-element-size.html @web-platform-tests/interop +css/css-view-transitions/inline-with-offset-from-containing-block.html @web-platform-tests/interop +css/css-view-transitions/japanese-tag.html @web-platform-tests/interop +css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-new.html @web-platform-tests/interop +css/css-view-transitions/massive-element-below-and-on-top-of-viewport-partially-onscreen-old.html @web-platform-tests/interop +css/css-view-transitions/massive-element-below-viewport-offscreen-new.html @web-platform-tests/interop +css/css-view-transitions/massive-element-below-viewport-offscreen-old.html @web-platform-tests/interop +css/css-view-transitions/massive-element-below-viewport-partially-onscreen-new.html @web-platform-tests/interop +css/css-view-transitions/massive-element-below-viewport-partially-onscreen-old.html @web-platform-tests/interop +css/css-view-transitions/massive-element-left-of-viewport-offscreen-new.html @web-platform-tests/interop +css/css-view-transitions/massive-element-left-of-viewport-offscreen-old.html @web-platform-tests/interop +css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-new.html @web-platform-tests/interop +css/css-view-transitions/massive-element-left-of-viewport-partially-onscreen-old.html @web-platform-tests/interop +css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-new.html @web-platform-tests/interop +css/css-view-transitions/massive-element-on-top-of-viewport-offscreen-old.html @web-platform-tests/interop +css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-new.html @web-platform-tests/interop +css/css-view-transitions/massive-element-on-top-of-viewport-partially-onscreen-old.html @web-platform-tests/interop +css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-new.html @web-platform-tests/interop +css/css-view-transitions/massive-element-right-and-left-of-viewport-partially-onscreen-old.html @web-platform-tests/interop +css/css-view-transitions/massive-element-right-of-viewport-offscreen-new.html @web-platform-tests/interop +css/css-view-transitions/massive-element-right-of-viewport-offscreen-old.html @web-platform-tests/interop +css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-new.html @web-platform-tests/interop +css/css-view-transitions/massive-element-right-of-viewport-partially-onscreen-old.html @web-platform-tests/interop +css/css-view-transitions/match-element-name.html @web-platform-tests/interop +css/css-view-transitions/modify-style-via-cssom.html @web-platform-tests/interop +css/css-view-transitions/multiline-span-with-overflowing-text-and-box-decorations.html @web-platform-tests/interop +css/css-view-transitions/named-element-with-fix-pos-child-new.html @web-platform-tests/interop +css/css-view-transitions/named-element-with-fix-pos-child-old.html @web-platform-tests/interop +css/css-view-transitions/names-are-tree-scoped.html @web-platform-tests/interop +css/css-view-transitions/nested-elements-in-overflow.html @web-platform-tests/interop +css/css-view-transitions/new-and-old-sizes-match.html @web-platform-tests/interop +css/css-view-transitions/new-content-ancestor-clipped-2.html @web-platform-tests/interop +css/css-view-transitions/new-content-ancestor-clipped.html @web-platform-tests/interop +css/css-view-transitions/new-content-captures-clip-path.html @web-platform-tests/interop +css/css-view-transitions/new-content-captures-different-size.html @web-platform-tests/interop +css/css-view-transitions/new-content-captures-opacity.html @web-platform-tests/interop +css/css-view-transitions/new-content-captures-positioned-spans.html @web-platform-tests/interop +css/css-view-transitions/new-content-captures-root.html @web-platform-tests/interop +css/css-view-transitions/new-content-captures-spans.html @web-platform-tests/interop +css/css-view-transitions/new-content-changes-overflow-left.html @web-platform-tests/interop +css/css-view-transitions/new-content-changes-overflow.html @web-platform-tests/interop +css/css-view-transitions/new-content-container-writing-modes.html @web-platform-tests/interop +css/css-view-transitions/new-content-element-writing-modes.html @web-platform-tests/interop +css/css-view-transitions/new-content-escapes-clip-with-abspos-child.html @web-platform-tests/interop +css/css-view-transitions/new-content-flat-transform-ancestor.html @web-platform-tests/interop +css/css-view-transitions/new-content-from-root-display-none.html @web-platform-tests/interop +css/css-view-transitions/new-content-has-scrollbars.html @web-platform-tests/interop +css/css-view-transitions/new-content-inline-with-offset-from-containing-block-clipped.html @web-platform-tests/interop +css/css-view-transitions/new-content-intrinsic-aspect-ratio.html @web-platform-tests/interop +css/css-view-transitions/new-content-is-empty-div.html @web-platform-tests/interop +css/css-view-transitions/new-content-is-inline.html @web-platform-tests/interop +css/css-view-transitions/new-content-object-fit-fill.html @web-platform-tests/interop +css/css-view-transitions/new-content-object-fit-none.html @web-platform-tests/interop +css/css-view-transitions/new-content-preserve-3d-ancestor.html @web-platform-tests/interop +css/css-view-transitions/new-content-root-scrollbar-with-fixed-background.html @web-platform-tests/interop +css/css-view-transitions/new-content-scaling.html @web-platform-tests/interop +css/css-view-transitions/new-content-transform-position-fixed.html @web-platform-tests/interop +css/css-view-transitions/new-content-with-overflow-zoomed.html @web-platform-tests/interop +css/css-view-transitions/new-content-with-overflow.html @web-platform-tests/interop +css/css-view-transitions/new-element-on-start.html @web-platform-tests/interop +css/css-view-transitions/new-root-vertical-writing-mode.html @web-platform-tests/interop +css/css-view-transitions/no-named-elements.html @web-platform-tests/interop +css/css-view-transitions/no-painting-while-render-blocked.html @web-platform-tests/interop +css/css-view-transitions/no-root-capture.html @web-platform-tests/interop +css/css-view-transitions/no-white-flash-before-activation.html @web-platform-tests/interop +css/css-view-transitions/nothing-captured.html @web-platform-tests/interop +css/css-view-transitions/offscreen-element-modified-before-coming-onscreen.html @web-platform-tests/interop +css/css-view-transitions/old-content-captures-clip-path.html @web-platform-tests/interop +css/css-view-transitions/old-content-captures-different-size.html @web-platform-tests/interop +css/css-view-transitions/old-content-captures-opacity.html @web-platform-tests/interop +css/css-view-transitions/old-content-captures-root.html @web-platform-tests/interop +css/css-view-transitions/old-content-container-writing-modes.html @web-platform-tests/interop +css/css-view-transitions/old-content-element-writing-modes.html @web-platform-tests/interop +css/css-view-transitions/old-content-escapes-clip-with-abspos-child.html @web-platform-tests/interop +css/css-view-transitions/old-content-has-scrollbars.html @web-platform-tests/interop +css/css-view-transitions/old-content-inline-with-offset-from-containing-block-clipped.html @web-platform-tests/interop +css/css-view-transitions/old-content-intrinsic-aspect-ratio.html @web-platform-tests/interop +css/css-view-transitions/old-content-is-empty-div.html @web-platform-tests/interop +css/css-view-transitions/old-content-is-inline.html @web-platform-tests/interop +css/css-view-transitions/old-content-object-fit-fill.html @web-platform-tests/interop +css/css-view-transitions/old-content-object-fit-none.html @web-platform-tests/interop +css/css-view-transitions/old-content-root-scrollbar-with-fixed-background.html @web-platform-tests/interop +css/css-view-transitions/old-content-with-overflow-zoomed.html @web-platform-tests/interop +css/css-view-transitions/old-content-with-overflow.html @web-platform-tests/interop +css/css-view-transitions/old-root-vertical-writing-mode.html @web-platform-tests/interop +css/css-view-transitions/outer-padding-inner-background.html @web-platform-tests/interop +css/css-view-transitions/paint-holding-in-iframe.html @web-platform-tests/interop +css/css-view-transitions/pseudo-element-overflow-hidden.html @web-platform-tests/interop +css/css-view-transitions/pseudo-element-preserve-3d.html @web-platform-tests/interop +css/css-view-transitions/pseudo-rendering-invalidation.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-entry.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-exit.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-match-ident.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-match-multiple-wildcard.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-match-multiple.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-match-wildcard-no-star.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-match-wildcard.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-mismatch-ident.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-mismatch-partial.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-mismatch-wildcard.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-multiple-vt-classes.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-new-with-class-old-without.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-old-with-class-new-without.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-view-transition-group.html @web-platform-tests/interop +css/css-view-transitions/pseudo-with-classes-view-transition-image-pair.html @web-platform-tests/interop +css/css-view-transitions/reset-state-after-scrolled-view-transition.html @web-platform-tests/interop +css/css-view-transitions/root-captured-as-different-tag.html @web-platform-tests/interop +css/css-view-transitions/root-style-change-during-animation.html @web-platform-tests/interop +css/css-view-transitions/root-to-shared-animation-end.html @web-platform-tests/interop +css/css-view-transitions/root-to-shared-animation-incoming.html @web-platform-tests/interop +css/css-view-transitions/root-to-shared-animation-start.html @web-platform-tests/interop +css/css-view-transitions/rotated-cat-off-top-edge.html @web-platform-tests/interop +css/css-view-transitions/rtl-with-scrollbar.html @web-platform-tests/interop +css/css-view-transitions/scroller-child-abspos.html @web-platform-tests/interop +css/css-view-transitions/scroller-child.html @web-platform-tests/interop +css/css-view-transitions/scroller.html @web-platform-tests/interop +css/css-view-transitions/set-current-time-transform.html @web-platform-tests/interop +css/css-view-transitions/set-current-time.html @web-platform-tests/interop +css/css-view-transitions/set-universal-specificity.html @web-platform-tests/interop +css/css-view-transitions/shadow-part-with-class-inside-shadow-important.html @web-platform-tests/interop +css/css-view-transitions/shadow-part-with-class-inside-shadow.html @web-platform-tests/interop +css/css-view-transitions/shadow-part-with-class.html @web-platform-tests/interop +css/css-view-transitions/shadow-part-with-name-nested.html @web-platform-tests/interop +css/css-view-transitions/shadow-part-with-name-overridden-by-important.html @web-platform-tests/interop +css/css-view-transitions/shadow-part-with-name.html @web-platform-tests/interop +css/css-view-transitions/sibling-frames-transition.html @web-platform-tests/interop +css/css-view-transitions/snapshot-containing-block-absolute.html @web-platform-tests/interop +css/css-view-transitions/snapshot-containing-block-includes-scrollbar-gutter.html @web-platform-tests/interop +css/css-view-transitions/snapshot-containing-block-static.html @web-platform-tests/interop +css/css-view-transitions/span-with-overflowing-text-and-box-decorations.html @web-platform-tests/interop +css/css-view-transitions/span-with-overflowing-text-hidden.html @web-platform-tests/interop +css/css-view-transitions/span-with-overflowing-text.html @web-platform-tests/interop +css/css-view-transitions/transform-origin-view-transition-group.html @web-platform-tests/interop +css/css-view-transitions/transformed-element-scroll-transform.html @web-platform-tests/interop +css/css-view-transitions/transition-in-empty-iframe.html @web-platform-tests/interop +css/css-view-transitions/update-callback-called-once.html @web-platform-tests/interop +css/css-view-transitions/view-transition-name-is-backdrop-filter-root.html @web-platform-tests/interop +css/css-view-transitions/view-transition-name-is-grouping.html @web-platform-tests/interop +css/css-view-transitions/view-transition-name-on-document-root.html @web-platform-tests/interop +css/css-view-transitions/view-transition-name-removed-mid-transition.html @web-platform-tests/interop +css/css-view-transitions/web-animations-api-parse-pseudo-argument.html @web-platform-tests/interop +css/css-view-transitions/web-animations-api.html @web-platform-tests/interop +css/css-view-transitions/writing-mode-container-resize.html @web-platform-tests/interop +css/css-viewport/zoom/background-image.html @web-platform-tests/interop +css/css-viewport/zoom/basic.html @web-platform-tests/interop +css/css-viewport/zoom/border-spacing.html @web-platform-tests/interop +css/css-viewport/zoom/canvas.html @web-platform-tests/interop +css/css-viewport/zoom/container-queries.html @web-platform-tests/interop +css/css-viewport/zoom/font-size.html @web-platform-tests/interop +css/css-viewport/zoom/iframe-zoom-nested.html @web-platform-tests/interop +css/css-viewport/zoom/iframe-zoom.sub.html @web-platform-tests/interop +css/css-viewport/zoom/image-intrinsic-size.html @web-platform-tests/interop +css/css-viewport/zoom/inherited-length.html @web-platform-tests/interop +css/css-viewport/zoom/inherited.html @web-platform-tests/interop +css/css-viewport/zoom/letter-spacing.html @web-platform-tests/interop +css/css-viewport/zoom/line-height.html @web-platform-tests/interop +css/css-viewport/zoom/list-style-image.html @web-platform-tests/interop +css/css-viewport/zoom/relative-units-from-parent.html @web-platform-tests/interop +css/css-viewport/zoom/stroke.html @web-platform-tests/interop +css/css-viewport/zoom/svg-path-simple.html @web-platform-tests/interop +css/css-viewport/zoom/svg-path.html @web-platform-tests/interop +css/css-viewport/zoom/svg-transform.html @web-platform-tests/interop +css/css-viewport/zoom/svg-viewBox.html @web-platform-tests/interop +css/css-viewport/zoom/svg.html @web-platform-tests/interop +css/css-viewport/zoom/text-indent.html @web-platform-tests/interop +css/css-viewport/zoom/text-shadow.html @web-platform-tests/interop +css/css-viewport/zoom/text-stroke-width.html @web-platform-tests/interop +css/css-viewport/zoom/text-underline-offset.html @web-platform-tests/interop +css/css-viewport/zoom/word-spacing.html @web-platform-tests/interop +css/css-writing-modes/abs-pos-border-offset-002.html @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-slr-043.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-slr-047.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-slr-048.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-slr-050.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-slr-054.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-slr-055.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-slr-056.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-slr-058.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-slr-060.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-slr-062.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-slr-063.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-slr-066.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-srl-042.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-srl-045.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-srl-046.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-srl-049.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-srl-051.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-srl-052.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-srl-053.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-srl-057.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-srl-059.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-srl-061.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-srl-064.xht @web-platform-tests/interop +css/css-writing-modes/block-flow-direction-srl-065.xht @web-platform-tests/interop +css/css-writing-modes/inline-block-alignment-slr-009.xht @web-platform-tests/interop +css/css-writing-modes/inline-block-alignment-srl-008.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-slr-043.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-slr-047.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-slr-048.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-slr-050.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-slr-053.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-slr-054.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-slr-056.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-slr-058.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-slr-060.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-srl-042.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-srl-045.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-srl-046.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-srl-049.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-srl-051.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-srl-052.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-srl-055.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-srl-057.xht @web-platform-tests/interop +css/css-writing-modes/line-box-direction-srl-059.xht @web-platform-tests/interop +css/css-writing-modes/logical-physical-mapping-001.html @web-platform-tests/interop +css/css-writing-modes/row-progression-slr-023.xht @web-platform-tests/interop +css/css-writing-modes/row-progression-slr-029.xht @web-platform-tests/interop +css/css-writing-modes/row-progression-srl-022.xht @web-platform-tests/interop +css/css-writing-modes/row-progression-srl-028.xht @web-platform-tests/interop +css/css-writing-modes/row-progression-vlr-009.xht @web-platform-tests/interop +css/css-writing-modes/slr-alongside-vlr-floats.html @web-platform-tests/interop +css/css-writing-modes/srl-alongside-vrl-floats.html @web-platform-tests/interop +css/css-writing-modes/table-column-order-slr-007.xht @web-platform-tests/interop +css/css-writing-modes/table-column-order-srl-006.xht @web-platform-tests/interop +css/css-writing-modes/table-progression-slr-001.html @web-platform-tests/interop +css/css-writing-modes/table-progression-slr-002.html @web-platform-tests/interop +css/css-writing-modes/table-progression-srl-001.html @web-platform-tests/interop +css/css-writing-modes/table-progression-srl-002.html @web-platform-tests/interop +css/css-writing-modes/text-baseline-slr-009.xht @web-platform-tests/interop +css/css-writing-modes/text-baseline-srl-008.xht @web-platform-tests/interop +css/css-writing-modes/text-combine-upright-sideways-001.html @web-platform-tests/interop +css/css-writing-modes/text-combine-upright-sideways-002.html @web-platform-tests/interop +css/css-writing-modes/text-orientation-mixed-srl-016.xht @web-platform-tests/interop +css/css-writing-modes/text-orientation-upright-directionality-001.html @web-platform-tests/interop +css/css-writing-modes/text-orientation-upright-srl-018.xht @web-platform-tests/interop +css/css-writing-modes/vertical-alignment-slr-029.xht @web-platform-tests/interop +css/css-writing-modes/vertical-alignment-slr-031.xht @web-platform-tests/interop +css/css-writing-modes/vertical-alignment-slr-033.xht @web-platform-tests/interop +css/css-writing-modes/vertical-alignment-slr-035.xht @web-platform-tests/interop +css/css-writing-modes/vertical-alignment-slr-041.xht @web-platform-tests/interop +css/css-writing-modes/vertical-alignment-srl-028.xht @web-platform-tests/interop +css/css-writing-modes/vertical-alignment-srl-030.xht @web-platform-tests/interop +css/css-writing-modes/vertical-alignment-srl-032.xht @web-platform-tests/interop +css/css-writing-modes/vertical-alignment-srl-034.xht @web-platform-tests/interop +css/css-writing-modes/vertical-alignment-srl-040.xht @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-032.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-033.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-034.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-035.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-036.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-037.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-038.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-039.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-040.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-041.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-043.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-044.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-045.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-046.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-048.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-050.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-051.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-052.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-053.html @web-platform-tests/interop +css/css-writing-modes/wm-propagation-body-055.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-backdrop-root-backdrop-filter.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-backdrop-root-clip-path.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-backdrop-root-filter.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-backdrop-root-mix-blend-mode.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-backdrop-root-opacity.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-basic-blur.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-basic-opacity-2.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-basic-opacity.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-basic.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-boundary.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-clip-radius-zoom.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-clip-rect-2.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-clip-rect-zoom.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-clip-rect.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-clip-rounded-clip.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-clipped.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-containing-block.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-edge-behavior.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-edge-clipping-2.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-edge-clipping.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-edge-mirror.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-edge-pixels-2.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-edge-pixels.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-fixed-clip.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-invalid.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-isolation-fixed.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-isolation-isolate.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-opacity-rounded-clip.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-paint-order.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-plus-filter.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-plus-mask-large.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-plus-mask.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-plus-opacity.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-plus-will-change-opacity.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-reference-filter-mutated.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-reference-filter.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-root-element.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-svg-background-image-blur.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-svg-foreignObject.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-svg.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-transform.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-update.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-zero-size.html @web-platform-tests/interop +css/filter-effects/backdrop-filters-brightness.html @web-platform-tests/interop +css/filter-effects/backdrop-filters-contrast.html @web-platform-tests/interop +css/filter-effects/backdrop-filters-grayscale-001.html @web-platform-tests/interop +css/filter-effects/backdrop-filters-grayscale-002.html @web-platform-tests/interop +css/filter-effects/backdrop-filters-grayscale-003.html @web-platform-tests/interop +css/filter-effects/backdrop-filters-hue-rotate.html @web-platform-tests/interop +css/filter-effects/backdrop-filters-invert.html @web-platform-tests/interop +css/filter-effects/backdrop-filters-opacity.html @web-platform-tests/interop +css/filter-effects/backdrop-filters-saturate.html @web-platform-tests/interop +css/filter-effects/backdrop-filters-sepia.html @web-platform-tests/interop +css/filter-effects/css-backdrop-filters-animation-blur.html @web-platform-tests/interop +css/filter-effects/css-backdrop-filters-animation-brightness.html @web-platform-tests/interop +css/filter-effects/css-backdrop-filters-animation-combined.html @web-platform-tests/interop +css/filter-effects/css-backdrop-filters-animation-contrast.html @web-platform-tests/interop +css/filter-effects/css-backdrop-filters-animation-drop-shadow.html @web-platform-tests/interop +css/filter-effects/css-backdrop-filters-animation-grayscale.html @web-platform-tests/interop +css/filter-effects/css-backdrop-filters-animation-hue-rotate.html @web-platform-tests/interop +css/filter-effects/css-backdrop-filters-animation-invert.html @web-platform-tests/interop +css/filter-effects/css-backdrop-filters-animation-opacity.html @web-platform-tests/interop +css/filter-effects/css-backdrop-filters-animation-saturate.html @web-platform-tests/interop +css/filter-effects/css-backdrop-filters-animation-sepia.html @web-platform-tests/interop +css/filter-effects/repaint-added-backdrop-filter.html @web-platform-tests/interop +html/editing/the-hidden-attribute/hidden-until-found-001.html @web-platform-tests/interop +html/editing/the-hidden-attribute/hidden-until-found-004.html @web-platform-tests/interop +html/editing/the-hidden-attribute/hidden-until-found-005.html @web-platform-tests/interop +html/editing/the-hidden-attribute/hidden-until-found-007.html @web-platform-tests/interop +html/rendering/the-details-element/details-after.html @web-platform-tests/interop +html/rendering/the-details-element/details-before.html @web-platform-tests/interop +html/rendering/the-details-element/details-display-type-001-ref.html @web-platform-tests/interop +html/rendering/the-details-element/details-display-type-001.html @web-platform-tests/interop +html/rendering/the-details-element/details-display-type-002.html @web-platform-tests/interop +html/rendering/the-details-element/details-pseudo-elements-001.html @web-platform-tests/interop +html/rendering/the-details-element/details-pseudo-elements-002.html @web-platform-tests/interop +html/rendering/the-details-element/details-pseudo-elements-003.html @web-platform-tests/interop +html/rendering/the-details-element/details-pseudo-elements-004.html @web-platform-tests/interop +html/rendering/the-details-element/details-pseudo-elements-005.html @web-platform-tests/interop +html/rendering/the-details-element/details-revert.html @web-platform-tests/interop +html/rendering/the-details-element/details-summary-display-inline-001.html @web-platform-tests/interop +html/rendering/the-details-element/details-summary-display-inline-002.html @web-platform-tests/interop +html/rendering/the-details-element/summary-display-flex.html @web-platform-tests/interop +html/rendering/the-details-element/summary-display-grid.html @web-platform-tests/interop +html/rendering/the-details-element/summary-display-inline-flex.html @web-platform-tests/interop +html/rendering/the-details-element/summary-display-inline-grid.html @web-platform-tests/interop +html/rendering/the-details-element/summary-display-list-item-001.html @web-platform-tests/interop +html/rendering/the-details-element/summary-display-list-item-002.html @web-platform-tests/interop +html/rendering/the-details-element/summary-in-ol.html @web-platform-tests/interop +html/rendering/the-details-element/summary-text-decoration.html @web-platform-tests/interop +css/css-anchor-position/anchor-center-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-center-htb-htb.html @web-platform-tests/interop +css/css-anchor-position/anchor-center-htb-vrl.html @web-platform-tests/interop +css/css-anchor-position/anchor-center-offset-change.html @web-platform-tests/interop +css/css-anchor-position/anchor-center-vrl-htb.html @web-platform-tests/interop +css/css-anchor-position/anchor-center-vrl-vrl.html @web-platform-tests/interop +css/css-anchor-position/anchor-fallback-invalidation.html @web-platform-tests/interop +css/css-anchor-position/anchor-getComputedStyle-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-getComputedStyle-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-getComputedStyle-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-inherited.html @web-platform-tests/interop +css/css-anchor-position/anchor-inside-outside.html @web-platform-tests/interop +css/css-anchor-position/anchor-invalid-fallback.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-004.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-basics.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-cross-shadow.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-in-shadow-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-in-shadow.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-inline-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-multicol-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-multicol-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-multicol-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-name-multicol-004.html @web-platform-tests/interop +css/css-anchor-position/anchor-parse-invalid.html @web-platform-tests/interop +css/css-anchor-position/anchor-parse-valid.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-004.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-borders-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-borders-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-dynamic-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-dynamic-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-dynamic-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-dynamic-004.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-grid-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-inline-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-inline-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-inline-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-inline-004.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-004.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-005.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-006.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-colspan-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-colspan-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-fixed-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-multicol-nested-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-principal-box.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-top-layer-007.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-writing-modes-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-position-writing-modes-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-query-custom-property-registration.html @web-platform-tests/interop +css/css-anchor-position/anchor-query-fallback.html @web-platform-tests/interop +css/css-anchor-position/anchor-scope-basic.html @web-platform-tests/interop +css/css-anchor-position/anchor-scope-dynamic.html @web-platform-tests/interop +css/css-anchor-position/anchor-scope-shadow-all.html @web-platform-tests/interop +css/css-anchor-position/anchor-scope-shadow-flat-tree.html @web-platform-tests/interop +css/css-anchor-position/anchor-scope-shadow-names.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-004.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-005.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-006.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-007.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-cleanup.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-js-expose.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-002.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-003.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-004.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-005.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-006.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-007.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-008.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-009.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-010.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-011.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-013.html @web-platform-tests/interop +css/css-anchor-position/anchor-scroll-position-try-014.html @web-platform-tests/interop +css/css-anchor-position/anchor-size-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-size-minmax-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-size-parse-invalid.html @web-platform-tests/interop +css/css-anchor-position/anchor-size-parse-valid.html @web-platform-tests/interop +css/css-anchor-position/anchor-size-replaced-001.html @web-platform-tests/interop +css/css-anchor-position/anchor-size-writing-modes-001.html @web-platform-tests/interop +css/css-anchor-position/at-position-try-allowed-declarations.html @web-platform-tests/interop +css/css-anchor-position/at-position-try-cssom.html @web-platform-tests/interop +css/css-anchor-position/at-position-try-invalidation-shadow-dom.html @web-platform-tests/interop +css/css-anchor-position/at-position-try-invalidation.html @web-platform-tests/interop +css/css-anchor-position/at-position-try-parse.html @web-platform-tests/interop +css/css-anchor-position/auto-margins-position-area.html @web-platform-tests/interop +css/css-anchor-position/base-style-invalidation.html @web-platform-tests/interop +css/css-anchor-position/idlharness.html @web-platform-tests/interop +css/css-anchor-position/last-successful-basic.html @web-platform-tests/interop +css/css-anchor-position/last-successful-change-fallbacks.html @web-platform-tests/interop +css/css-anchor-position/last-successful-change-try-rule.html @web-platform-tests/interop +css/css-anchor-position/last-successful-iframe.html @web-platform-tests/interop +css/css-anchor-position/last-successful-intermediate-ignored.html @web-platform-tests/interop +css/css-anchor-position/parsing/anchor-scope-computed.html @web-platform-tests/interop +css/css-anchor-position/parsing/anchor-scope-parsing.html @web-platform-tests/interop +css/css-anchor-position/parsing/position-try-computed.html @web-platform-tests/interop +css/css-anchor-position/parsing/position-try-fallbacks-computed.html @web-platform-tests/interop +css/css-anchor-position/parsing/position-try-fallbacks-parsing.html @web-platform-tests/interop +css/css-anchor-position/parsing/position-try-order-computed.html @web-platform-tests/interop +css/css-anchor-position/parsing/position-try-order-parsing.html @web-platform-tests/interop +css/css-anchor-position/parsing/position-try-parsing.html @web-platform-tests/interop +css/css-anchor-position/popover-implicit-anchor.html @web-platform-tests/interop +css/css-anchor-position/position-anchor-003.html @web-platform-tests/interop +css/css-anchor-position/position-anchor-basics.html @web-platform-tests/interop +css/css-anchor-position/position-area-align-justify-wm-dir.html @web-platform-tests/interop +css/css-anchor-position/position-area-align-justify.html @web-platform-tests/interop +css/css-anchor-position/position-area-anchor-outside.html @web-platform-tests/interop +css/css-anchor-position/position-area-anchor-partially-outside.html @web-platform-tests/interop +css/css-anchor-position/position-area-basic.html @web-platform-tests/interop +css/css-anchor-position/position-area-computed-insets.html @web-platform-tests/interop +css/css-anchor-position/position-area-computed.html @web-platform-tests/interop +css/css-anchor-position/position-area-in-grid.html @web-platform-tests/interop +css/css-anchor-position/position-area-in-position-try.html @web-platform-tests/interop +css/css-anchor-position/position-area-interpolation.html @web-platform-tests/interop +css/css-anchor-position/position-area-parsing.html @web-platform-tests/interop +css/css-anchor-position/position-area-value.html @web-platform-tests/interop +css/css-anchor-position/position-area-with-insets.html @web-platform-tests/interop +css/css-anchor-position/position-area-wm-dir.html @web-platform-tests/interop +css/css-anchor-position/position-try-001.html @web-platform-tests/interop +css/css-anchor-position/position-try-002.html @web-platform-tests/interop +css/css-anchor-position/position-try-003.html @web-platform-tests/interop +css/css-anchor-position/position-try-004.html @web-platform-tests/interop +css/css-anchor-position/position-try-backdrop.html @web-platform-tests/interop +css/css-anchor-position/position-try-cascade-layer-reorder.html @web-platform-tests/interop +css/css-anchor-position/position-try-cascade.html @web-platform-tests/interop +css/css-anchor-position/position-try-container-query.html @web-platform-tests/interop +css/css-anchor-position/position-try-custom-property.html @web-platform-tests/interop +css/css-anchor-position/position-try-dynamic.html @web-platform-tests/interop +css/css-anchor-position/position-try-fallbacks-limit.html @web-platform-tests/interop +css/css-anchor-position/position-try-grid-001.html @web-platform-tests/interop +css/css-anchor-position/position-try-order-basic.html @web-platform-tests/interop +css/css-anchor-position/position-try-order-position-area.html @web-platform-tests/interop +css/css-anchor-position/position-try-position-anchor.html @web-platform-tests/interop +css/css-anchor-position/position-try-pseudo-element.html @web-platform-tests/interop +css/css-anchor-position/position-try-tree-scoped.html @web-platform-tests/interop +css/css-anchor-position/property-interpolations.html @web-platform-tests/interop +css/css-anchor-position/pseudo-element-anchor-dynamic.html @web-platform-tests/interop +css/css-anchor-position/pseudo-element-anchor.html @web-platform-tests/interop +css/css-anchor-position/remove-anchor-dirty-layout.html @web-platform-tests/interop +css/css-anchor-position/try-tactic-alignment.html @web-platform-tests/interop +css/css-anchor-position/try-tactic-anchor.html @web-platform-tests/interop +css/css-anchor-position/try-tactic-back-to-base.html @web-platform-tests/interop +css/css-anchor-position/try-tactic-base.html @web-platform-tests/interop +css/css-anchor-position/try-tactic-basic.html @web-platform-tests/interop +css/css-anchor-position/try-tactic-margin.html @web-platform-tests/interop +css/css-anchor-position/try-tactic-percentage.html @web-platform-tests/interop +css/css-anchor-position/try-tactic-position-area.html @web-platform-tests/interop +css/css-anchor-position/try-tactic-sizing.html @web-platform-tests/interop +css/css-anchor-position/try-tactic-wm.html @web-platform-tests/interop +css/css-cascade/at-scope-parsing.html @web-platform-tests/interop +css/css-cascade/at-scope-relative-syntax.html @web-platform-tests/interop +css/css-cascade/scope-container.html @web-platform-tests/interop +css/css-cascade/scope-cssom.html @web-platform-tests/interop +css/css-cascade/scope-declarations.html @web-platform-tests/interop +css/css-cascade/scope-deep.html @web-platform-tests/interop +css/css-cascade/scope-evaluation.html @web-platform-tests/interop +css/css-cascade/scope-focus.html @web-platform-tests/interop +css/css-cascade/scope-hover.html @web-platform-tests/interop +css/css-cascade/scope-implicit-external.html @web-platform-tests/interop +css/css-cascade/scope-implicit.html @web-platform-tests/interop +css/css-cascade/scope-invalidation.html @web-platform-tests/interop +css/css-cascade/scope-layer.html @web-platform-tests/interop +css/css-cascade/scope-media.html @web-platform-tests/interop +css/css-cascade/scope-name-defining-rules.html @web-platform-tests/interop +css/css-cascade/scope-nesting.html @web-platform-tests/interop +css/css-cascade/scope-overlapping-has.html @web-platform-tests/interop +css/css-cascade/scope-proximity.html @web-platform-tests/interop +css/css-cascade/scope-shadow.html @web-platform-tests/interop +css/css-cascade/scope-specificity.html @web-platform-tests/interop +css/css-cascade/scope-starting-style.html @web-platform-tests/interop +css/css-cascade/scope-style-sharing-001.html @web-platform-tests/interop +css/css-cascade/scope-style-sharing-002.html @web-platform-tests/interop +css/css-cascade/scope-supports.html @web-platform-tests/interop +css/css-cascade/scope-visited-cssom.html @web-platform-tests/interop +css/css-flexbox/abspos/abspos-descendent-001.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-content-001.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-001.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-002.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-003.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-004.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-005.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-006.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-007.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-008.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-rtl-001.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-rtl-002.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-rtl-003.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-rtl-004.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-vertWM-001.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-vertWM-002.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-vertWM-003.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-align-self-vertWM-004.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-001.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-002.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-003.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-004.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-005.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-006.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-007.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-008.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-rtl-001.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-rtl-002.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-vertWM-001.html @web-platform-tests/interop +css/css-flexbox/abspos/flex-abspos-staticpos-justify-content-vertWM-002.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-001.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-002.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-003.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-004.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-012.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-013.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-014.html @web-platform-tests/interop +css/css-flexbox/abspos/position-absolute-015.html @web-platform-tests/interop +css/css-flexbox/align-content-horiz-001a.html @web-platform-tests/interop +css/css-flexbox/align-content-horiz-001b.html @web-platform-tests/interop +css/css-flexbox/align-content-horiz-002.html @web-platform-tests/interop +css/css-flexbox/align-content-vert-001a.html @web-platform-tests/interop +css/css-flexbox/align-content-vert-001b.html @web-platform-tests/interop +css/css-flexbox/align-content-vert-002.html @web-platform-tests/interop +css/css-flexbox/align-content-wmvert-001.html @web-platform-tests/interop +css/css-flexbox/align-content-wrap-001.html @web-platform-tests/interop +css/css-flexbox/align-content-wrap-002.html @web-platform-tests/interop +css/css-flexbox/align-content-wrap-003.html @web-platform-tests/interop +css/css-flexbox/align-content-wrap-005.html @web-platform-tests/interop +css/css-flexbox/align-self-014.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-001.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-002.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-003.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-004.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-005.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-006.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-007.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-fieldset-001.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-fieldset-002.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-fieldset-003.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-flex-001.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-flex-002.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-flex-003.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-flex-004.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-grid-001.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-grid-002.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-grid-003.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-multicol-001.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-multicol-002.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-multicol-003.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-overflow-001.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-overflow-002.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-overflow-003.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-table-001.html @web-platform-tests/interop +css/css-flexbox/alignment/flex-align-baseline-table-003.html @web-platform-tests/interop +css/css-flexbox/animation/flex-basis-composition.html @web-platform-tests/interop +css/css-flexbox/animation/flex-basis-interpolation.html @web-platform-tests/interop +css/css-flexbox/animation/flex-grow-interpolation.html @web-platform-tests/interop +css/css-flexbox/animation/flex-shrink-interpolation.html @web-platform-tests/interop +css/css-flexbox/animation/order-interpolation.html @web-platform-tests/interop +css/css-flexbox/box-sizing-001.html @web-platform-tests/interop +css/css-flexbox/box-sizing-min-max-sizes-001.html @web-platform-tests/interop +css/css-flexbox/canvas-dynamic-change-001.html @web-platform-tests/interop +css/css-flexbox/change-column-flex-width.html @web-platform-tests/interop +css/css-flexbox/column-flex-child-with-overflow-scroll.html @web-platform-tests/interop +css/css-flexbox/column-reverse-gap.html @web-platform-tests/interop +css/css-flexbox/columns-height-set-via-top-bottom.html @web-platform-tests/interop +css/css-flexbox/dynamic-grid-flex-abspos.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-011.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-column-017.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-005.html @web-platform-tests/interop +css/css-flexbox/flex-aspect-ratio-img-row-013.html @web-platform-tests/interop +css/css-flexbox/flex-basis-009.html @web-platform-tests/interop +css/css-flexbox/flex-basis-intrinsics-001.html @web-platform-tests/interop +css/css-flexbox/flex-column-relayout-assert.html @web-platform-tests/interop +css/css-flexbox/flex-direction-column-overlap-001.html @web-platform-tests/interop +css/css-flexbox/flex-factor-less-than-one.html @web-platform-tests/interop +css/css-flexbox/flex-flow-013.html @web-platform-tests/interop +css/css-flexbox/flex-item-compressible-001.html @web-platform-tests/interop +css/css-flexbox/flex-item-compressible-002.html @web-platform-tests/interop +css/css-flexbox/flex-item-contains-strict.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-009.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-010.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-012.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-025.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-height-flex-items-031.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-size-001.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-size-002.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-size-003.html @web-platform-tests/interop +css/css-flexbox/flex-minimum-width-flex-items-014.html @web-platform-tests/interop +css/css-flexbox/flex-one-sets-flex-basis-to-zero-px.html @web-platform-tests/interop +css/css-flexbox/flex-outer-flexbox-column-recalculate-height-on-resize-001.html @web-platform-tests/interop +css/css-flexbox/flex-shorthand-flex-basis-middle.html @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-wmvert-002.html @web-platform-tests/interop +css/css-flexbox/flexbox-justify-content-wmvert-003.html @web-platform-tests/interop +css/css-flexbox/flexbox-lines-must-be-stretched-by-default.html @web-platform-tests/interop +css/css-flexbox/flexbox_first-letter.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-center-overflow.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-rtl-001.html @web-platform-tests/interop +css/css-flexbox/flexbox_justifycontent-rtl-002.html @web-platform-tests/interop +css/css-flexbox/flexbox_width-change-and-relayout-children.html @web-platform-tests/interop +css/css-flexbox/flexbox_width-wrapping-column.html @web-platform-tests/interop +css/css-flexbox/flexitem-no-margin-collapsing.html @web-platform-tests/interop +css/css-flexbox/flexitem-stretch-image.html @web-platform-tests/interop +css/css-flexbox/gap-017.html @web-platform-tests/interop +css/css-flexbox/gap-018.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-content-center.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-content-flex-end.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-content-flex-start.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-content-space-around.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-content-space-between.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-items-baseline.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-items-center.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-items-flex-end.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-items-flex-start.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-items-invalid.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-items-stretch.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-self-baseline.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-self-center.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-self-flex-end.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-self-flex-start.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-self-invalid.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_align-self-stretch.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_display-inline.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_display.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-basis-0.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-basis-0percent.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-basis-auto.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-basis-percent.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-direction-column-reverse.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-direction-column.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-direction-invalid.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-direction-row-reverse.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-direction-row.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-column-nowrap.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-column-reverse-nowrap.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-column-reverse-wrap.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-column-reverse.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-column-wrap-reverse.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-column-wrap.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-column.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-nowrap.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-row-nowrap.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-row-reverse-nowrap.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-row-reverse-wrap-reverse.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-row-reverse-wrap.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-row-reverse.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-row-wrap-reverse.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-row-wrap.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-row.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-flow-wrap.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-grow-0.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-grow-invalid.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-grow-number.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-shorthand-0-auto.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-shorthand-auto.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-shorthand-initial.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-shorthand-invalid.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-shorthand-none.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-shorthand-number.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-shorthand.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-shrink-0.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-shrink-invalid.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-shrink-number.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-wrap-invalid.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-wrap-nowrap.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-wrap-wrap-reverse.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_flex-wrap-wrap.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_justify-content-center.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_justify-content-flex-end.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_justify-content-flex-start.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_justify-content-space-around.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_justify-content-space-between.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_min-auto-size.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_min-height-auto.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_min-width-auto.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_order-inherit.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_order-integer.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_order-invalid.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_order-negative.html @web-platform-tests/interop +css/css-flexbox/getcomputedstyle/flexbox_computedstyle_order.html @web-platform-tests/interop +css/css-flexbox/height-percentage-with-dynamic-container-size.html @web-platform-tests/interop +css/css-flexbox/hittest-anonymous-box.html @web-platform-tests/interop +css/css-flexbox/hittest-before-pseudo.html @web-platform-tests/interop +css/css-flexbox/hittest-overlapping-margin.html @web-platform-tests/interop +css/css-flexbox/hittest-overlapping-order.html @web-platform-tests/interop +css/css-flexbox/hittest-overlapping-relative.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-001.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-001v.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-002.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-002v.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-003.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-003v.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-004.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-004v.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-005.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-005v.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-006.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-006v.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-007.html @web-platform-tests/interop +css/css-flexbox/image-as-flexitem-size-007v.html @web-platform-tests/interop +css/css-flexbox/inheritance.html @web-platform-tests/interop +css/css-flexbox/inline-flex.html @web-platform-tests/interop +css/css-flexbox/intrinsic-width-orthogonal-writing-mode.html @web-platform-tests/interop +css/css-flexbox/justify-content-006.html @web-platform-tests/interop +css/css-flexbox/justify-content_space-between-002.html @web-platform-tests/interop +css/css-flexbox/layout-with-inline-svg-001.html @web-platform-tests/interop +css/css-flexbox/max-width-violation.html @web-platform-tests/interop +css/css-flexbox/multiline-min-max.html @web-platform-tests/interop +css/css-flexbox/multiline-min-preferred-width.html @web-platform-tests/interop +css/css-flexbox/negative-overflow-002.html @web-platform-tests/interop +css/css-flexbox/negative-overflow-003.html @web-platform-tests/interop +css/css-flexbox/negative-overflow.html @web-platform-tests/interop +css/css-flexbox/order_value.html @web-platform-tests/interop +css/css-flexbox/orthogonal-writing-modes-and-intrinsic-sizing.html @web-platform-tests/interop +css/css-flexbox/overflow-auto-002.html @web-platform-tests/interop +css/css-flexbox/overflow-auto-003.html @web-platform-tests/interop +css/css-flexbox/overflow-auto-004.html @web-platform-tests/interop +css/css-flexbox/overflow-auto-006.html @web-platform-tests/interop +css/css-flexbox/overflow-auto-008.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-basis-computed.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-basis-invalid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-basis-valid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-computed.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-direction-computed.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-direction-invalid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-direction-valid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-flow-computed.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-flow-invalid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-flow-shorthand.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-flow-valid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-grow-computed.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-grow-invalid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-grow-valid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-invalid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-shorthand.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-shrink-computed.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-shrink-invalid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-shrink-valid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-valid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-wrap-computed.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-wrap-invalid.html @web-platform-tests/interop +css/css-flexbox/parsing/flex-wrap-valid.html @web-platform-tests/interop +css/css-flexbox/parsing/order-computed.html @web-platform-tests/interop +css/css-flexbox/parsing/order-invalid.html @web-platform-tests/interop +css/css-flexbox/parsing/order-valid.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-000.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-001.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-003.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-011.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-012.html @web-platform-tests/interop +css/css-flexbox/percentage-heights-013.html @web-platform-tests/interop +css/css-flexbox/percentage-margins-001.html @web-platform-tests/interop +css/css-flexbox/percentage-max-width-cross-axis.html @web-platform-tests/interop +css/css-flexbox/percentage-padding-001.html @web-platform-tests/interop +css/css-flexbox/percentage-size-quirks-002.html @web-platform-tests/interop +css/css-flexbox/percentage-size-quirks.html @web-platform-tests/interop +css/css-flexbox/percentage-size.html @web-platform-tests/interop +css/css-flexbox/position-relative-percentage-top-001.html @web-platform-tests/interop +css/css-flexbox/quirks-auto-block-size-with-percentage-item.html @web-platform-tests/interop +css/css-flexbox/radiobutton-min-size.html @web-platform-tests/interop +css/css-flexbox/relayout-align-items.html @web-platform-tests/interop +css/css-flexbox/relayout-image-load.html @web-platform-tests/interop +css/css-flexbox/relayout-input.html @web-platform-tests/interop +css/css-flexbox/scrollbars-auto-min-content-sizing.html @web-platform-tests/interop +css/css-flexbox/shrinking-column-flexbox.html @web-platform-tests/interop +css/css-flexbox/stretch-after-sibling-size-change.html @web-platform-tests/interop +css/css-flexbox/stretched-child-shrink-on-relayout.html @web-platform-tests/interop +css/css-flexbox/svg-root-as-flex-item-006.html @web-platform-tests/interop +css/css-flexbox/table-as-item-cross-size.html @web-platform-tests/interop +css/css-flexbox/table-with-percent-intrinsic-width.html @web-platform-tests/interop +css/css-flexbox/text-as-flexitem-size-001.html @web-platform-tests/interop +css/css-grid/abspos/absolute-positioning-definite-sizes-001.html @web-platform-tests/interop +css/css-grid/abspos/absolute-positioning-grid-container-containing-block-001.html @web-platform-tests/interop +css/css-grid/abspos/absolute-positioning-grid-container-parent-001.html @web-platform-tests/interop +css/css-grid/abspos/empty-grid-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-and-autofit-tracks-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-and-autofit-tracks-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-and-autofit-tracks-003.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-and-autofit-tracks-004.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-and-autofit-tracks-005.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-and-autofit-tracks-006.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-and-autofit-tracks-007.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-content-alignment-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-content-alignment-rtl-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-gaps-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-gaps-002-rtl.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-gaps-002.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-gaps-rtl-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-implicit-grid-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-implicit-grid-line-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-padding-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-unknown-named-grid-line-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-positioned-items-within-grid-implicit-track-001.html @web-platform-tests/interop +css/css-grid/abspos/grid-sizing-positioned-items-001.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-001.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-002.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-003.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-004.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-005.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-006.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-007.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-008.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-009.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-010.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-011.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-012.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-013.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-014.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-015.html @web-platform-tests/interop +css/css-grid/abspos/orthogonal-positioned-grid-descendants-016.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-001.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-002.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-003.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-004.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-005.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-006.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-007.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-008.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-009.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-010.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-011.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-012.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-013.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-014.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-015.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-descendants-016.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-should-not-create-implicit-tracks-001.html @web-platform-tests/interop +css/css-grid/abspos/positioned-grid-items-should-not-take-up-space-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-fieldset-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-fieldset-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-fieldset-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-flex-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-flex-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-flex-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-flex-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-grid-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-grid-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-grid-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-multicol-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-multicol-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-multicol-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-overflow-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-overflow-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-overflow-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-table-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-table-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline-vertical.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-baseline.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-content-distribution-vertical-lr.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-content-distribution-vertical-rl.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-content-distribution.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-content-vertical-lr.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-content-vertical-rl.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-content.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-justify-margin-border-padding-vertical-lr.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-justify-margin-border-padding-vertical-rl.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-justify-margin-border-padding.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-justify-overflow.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-justify-stretch-with-orthogonal-flows.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-justify-stretch.html @web-platform-tests/interop +css/css-grid/alignment/grid-align-stretching-replaced-items.html @web-platform-tests/interop +css/css-grid/alignment/grid-align.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-009.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-010.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-011.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-012.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-013.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-014.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-015.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-016.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-017.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-018.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-019.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-020.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-021.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-022.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-023.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-024.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-025.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-026.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-027.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-028.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-029.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-030.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-031.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-032.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-033.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-034.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-035.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-implies-size-change-036.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-style-changes-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-style-changes-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-style-changes-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-style-changes-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-style-changes-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-style-changes-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-style-changes-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-alignment-style-changes-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-baseline-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-block-axis-alignment-auto-margins-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-block-axis-alignment-auto-margins-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-block-axis-alignment-auto-margins-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-block-axis-alignment-auto-margins-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-block-axis-alignment-auto-margins-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-block-axis-alignment-auto-margins-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-block-axis-alignment-auto-margins-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-009.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-010.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-011.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-012.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-013.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-014.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-015.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-016.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-positioned-items-017.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-sticky-positioned-items-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-alignment-sticky-positioned-items-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-self-baseline-synthesized-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-self-baseline-synthesized-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-self-baseline-synthesized-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-self-baseline-synthesized-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-column-axis-self-baseline-synthesized-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-container-baseline-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-alignment-and-self-alignment-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-alignment-and-self-alignment-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-alignment-auto-sized-tracks-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-alignment-overflow-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-alignment-overflow-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-alignment-second-pass-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-alignment-second-pass-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-alignment-with-abspos-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-alignment-with-span-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-alignment-with-span-vertical-lr-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-content-alignment-with-span-vertical-rl-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-fit-content-tracks-dont-stretch-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-gutters-and-alignment.html @web-platform-tests/interop +css/css-grid/alignment/grid-inline-axis-alignment-auto-margins-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-inline-axis-alignment-auto-margins-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-inline-axis-alignment-auto-margins-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-inline-axis-alignment-auto-margins-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-inline-axis-alignment-auto-margins-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-inline-axis-alignment-auto-margins-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-inline-axis-alignment-auto-margins-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-alignment-with-orthogonal-flows-vertical-lr.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-alignment-with-orthogonal-flows-vertical-rl.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-alignment-with-orthogonal-flows.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-auto-margins-alignment-vertical-lr.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-auto-margins-alignment-vertical-rl.html @web-platform-tests/interop +css/css-grid/alignment/grid-item-auto-margins-alignment.html @web-platform-tests/interop +css/css-grid/alignment/grid-justify-baseline-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-justify-baseline-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-justify-baseline-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-justify-baseline-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-place-content-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-009.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-010.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-011.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-012.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-013.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-014.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-015.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-016.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-positioned-items-017.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-sticky-positioned-items-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-alignment-sticky-positioned-items-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-row-axis-self-baseline-synthesized-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-009.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-010.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-011.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-non-static-positioned-items-012.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-009.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-010.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-011.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-012.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-013.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-014.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-015.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-positioned-items-with-margin-border-padding-016.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-009.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-010.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-011.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-012.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-013.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-014.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-015.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-016.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-009.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-010.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-011.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-012.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-013.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-014.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-015.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-lr-016.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-003.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-004.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-005.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-006.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-007.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-008.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-009.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-010.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-011.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-012.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-013.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-014.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-015.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment-stretch-vertical-rl-016.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-alignment.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-001.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-002.html @web-platform-tests/interop +css/css-grid/alignment/grid-self-baseline-not-applied-if-sizing-cyclic-dependency-003.html @web-platform-tests/interop +css/css-grid/animation/grid-template-columns-composition.html @web-platform-tests/interop +css/css-grid/animation/grid-template-columns-interpolation.html @web-platform-tests/interop +css/css-grid/animation/grid-template-columns-neutral-keyframe-001.html @web-platform-tests/interop +css/css-grid/animation/grid-template-columns-neutral-keyframe-002.html @web-platform-tests/interop +css/css-grid/animation/grid-template-columns-neutral-keyframe-003.html @web-platform-tests/interop +css/css-grid/animation/grid-template-columns-neutral-keyframe-004.html @web-platform-tests/interop +css/css-grid/animation/grid-template-rows-composition.html @web-platform-tests/interop +css/css-grid/animation/grid-template-rows-interpolation.html @web-platform-tests/interop +css/css-grid/animation/grid-template-rows-neutral-keyframe-001.html @web-platform-tests/interop +css/css-grid/animation/grid-template-rows-neutral-keyframe-002.html @web-platform-tests/interop +css/css-grid/animation/grid-template-rows-neutral-keyframe-003.html @web-platform-tests/interop +css/css-grid/animation/grid-template-rows-neutral-keyframe-004.html @web-platform-tests/interop +css/css-grid/chrome-crash-001.html @web-platform-tests/interop +css/css-grid/grid-definition/explicit-grid-size-001.html @web-platform-tests/interop +css/css-grid/grid-definition/flex-content-distribution-001.html @web-platform-tests/interop +css/css-grid/grid-definition/flex-content-resolution-columns-001.html @web-platform-tests/interop +css/css-grid/grid-definition/flex-content-resolution-columns-002.html @web-platform-tests/interop +css/css-grid/grid-definition/flex-content-resolution-rows-001.html @web-platform-tests/interop +css/css-grid/grid-definition/flex-content-resolution-rows-002.html @web-platform-tests/interop +css/css-grid/grid-definition/flex-factor-sum-less-than-1-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-explicit-rows-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-fill-columns-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-fill-rows-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-fit-columns-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-fit-rows-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-intrinsic-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-max-size-001.tentative.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-max-size-002.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-min-max-size-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-min-size-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-min-size-002.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-min-size-003.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-auto-repeat-min-size-004.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-change-auto-repeat-tracks.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-change-fit-content-argument-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-change-intrinsic-size-with-auto-repeat-tracks-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-inline-auto-repeat-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-inline-support-flexible-lengths-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-inline-support-grid-template-areas-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-inline-support-grid-template-columns-rows-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-inline-support-named-grid-lines-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-inline-support-repeat-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-inline-template-columns-rows-resolved-values-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-limits-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-minimum-contribution-with-percentages.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-percentage-rows-indefinite-height-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-percentage-rows-indefinite-height-002.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-shorthand-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-support-flexible-lengths-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-support-grid-template-areas-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-support-grid-template-columns-rows-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-support-named-grid-lines-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-support-repeat-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-support-repeat-002.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-template-columns-rows-changes-001.html @web-platform-tests/interop +css/css-grid/grid-definition/grid-template-columns-rows-resolved-values-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-automatic-minimum-intrinsic-aspect-ratio-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-dynamic-min-contribution-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-flex-container-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-item-min-auto-size-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-minimum-height-orthogonal-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-minimum-width-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-minimum-width-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-minimum-width-orthogonal-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-minimum-width-orthogonal-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-minimum-width-vertical-lr-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-minimum-width-vertical-lr-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-minimum-width-vertical-rl-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-minimum-width-vertical-rl-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-vertical-lr-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-vertical-lr-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-vertical-rl-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-margins-vertical-rl-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-015.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-vertical-lr-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-vertical-lr-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-vertical-rl-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-percentage-paddings-vertical-rl-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-relative-offsets-001.html @web-platform-tests/interop +css/css-grid/grid-items/grid-items-relative-offsets-002.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-021.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-022.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-023.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-024.html @web-platform-tests/interop +css/css-grid/grid-items/grid-minimum-size-grid-items-025.html @web-platform-tests/interop +css/css-grid/grid-layout-properties.html @web-platform-tests/interop +css/css-grid/grid-model/compute-intrinsic-widths-scrollbar-001.html @web-platform-tests/interop +css/css-grid/grid-model/fixed-width-intrinsic-width-should-exclude-scrollbar-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-box-sizing-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-button-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-computed-value-display-floated-items-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-container-ignores-first-letter-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-container-ignores-first-line-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-container-margin-border-padding-scrollbar-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-container-sizing-constraints-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-float-002.html @web-platform-tests/interop +css/css-grid/grid-model/grid-gutters-and-flex-content-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-gutters-and-tracks-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-gutters-as-percentage-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-item-accepts-first-letter-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-item-accepts-first-line-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-item-hit-test.html @web-platform-tests/interop +css/css-grid/grid-model/grid-min-max-height-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-size-shrink-to-fit-001.html @web-platform-tests/interop +css/css-grid/grid-model/grid-support-display-001.html @web-platform-tests/interop +css/css-grid/grid-tracks-stretched-with-different-flex-factors-sum.html @web-platform-tests/interop +css/css-grid/inheritance.html @web-platform-tests/interop +css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-002.html @web-platform-tests/interop +css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-003.html @web-platform-tests/interop +css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-004.html @web-platform-tests/interop +css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-005.html @web-platform-tests/interop +css/css-grid/layout-algorithm/baseline-alignment-affects-intrinsic-size-006.html @web-platform-tests/interop +css/css-grid/layout-algorithm/flex-and-intrinsic-sizes-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/flex-sizing-columns-min-max-width-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/flex-sizing-rows-min-max-height-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-automatic-minimum-for-auto-columns-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-container-percentage-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-container-percentage-002.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-content-distribution-must-account-for-track-sizing-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-content-distribution-must-account-for-track-sizing-002.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-content-distribution-must-account-for-track-sizing-003.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-content-distribution-must-account-for-track-sizing-004.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-find-fr-size-gutters-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-find-fr-size-gutters-002.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-fit-content-percentage.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-flex-track-intrinsic-sizes-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-flex-track-intrinsic-sizes-002.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-flex-track-intrinsic-sizes-003.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-intrinsic-size-with-orthogonal-items.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-intrinsic-track-sizes-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-item-margin-auto-columns-rows-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-item-margin-auto-columns-rows-vertical-lr-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-item-margin-auto-columns-rows-vertical-rl-001.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-minimum-contribution-baseline-shim-vertical-lr.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-minimum-contribution-baseline-shim-vertical-rl.html @web-platform-tests/interop +css/css-grid/layout-algorithm/grid-minimum-contribution-baseline-shim.html @web-platform-tests/interop +css/css-grid/parsing/grid-area-computed.html @web-platform-tests/interop +css/css-grid/parsing/grid-area-invalid.html @web-platform-tests/interop +css/css-grid/parsing/grid-area-shorthand.html @web-platform-tests/interop +css/css-grid/parsing/grid-area-valid.html @web-platform-tests/interop +css/css-grid/parsing/grid-auto-columns-computed.html @web-platform-tests/interop +css/css-grid/parsing/grid-auto-columns-invalid.html @web-platform-tests/interop +css/css-grid/parsing/grid-auto-columns-valid.html @web-platform-tests/interop +css/css-grid/parsing/grid-auto-flow-computed.html @web-platform-tests/interop +css/css-grid/parsing/grid-auto-flow-invalid.html @web-platform-tests/interop +css/css-grid/parsing/grid-auto-flow-valid.html @web-platform-tests/interop +css/css-grid/parsing/grid-auto-rows-computed.html @web-platform-tests/interop +css/css-grid/parsing/grid-auto-rows-invalid.html @web-platform-tests/interop +css/css-grid/parsing/grid-auto-rows-valid.html @web-platform-tests/interop +css/css-grid/parsing/grid-columns-rows-get-set-multiple.html @web-platform-tests/interop +css/css-grid/parsing/grid-content-sized-columns-resolution.html @web-platform-tests/interop +css/css-grid/parsing/grid-shorthand-invalid.html @web-platform-tests/interop +css/css-grid/parsing/grid-shorthand-serialization.html @web-platform-tests/interop +css/css-grid/parsing/grid-shorthand-valid.html @web-platform-tests/interop +css/css-grid/parsing/grid-shorthand.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-areas-computed.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-areas-invalid.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-areas-one-cell.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-areas-valid.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-columns-computed-implicit-track.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-columns-computed-nogrid.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-columns-computed-withcontent.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-columns-computed.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-columns-invalid.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-columns-valid.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-repeat-auto-computed-withcontent-001.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-repeat-auto-computed-withcontent-002.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-rows-computed-implicit-track.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-rows-computed-nogrid.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-rows-computed-withcontent.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-rows-computed.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-rows-invalid.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-rows-valid.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-shorthand-areas-valid.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-shorthand-invalid.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-shorthand-valid.html @web-platform-tests/interop +css/css-grid/parsing/grid-template-shorthand.html @web-platform-tests/interop +css/css-grid/placement/grid-auto-flow-sparse-001.html @web-platform-tests/interop +css/css-grid/placement/grid-auto-placement-implicit-tracks-001.html @web-platform-tests/interop +css/css-grid/placement/grid-container-change-grid-tracks-recompute-child-positions-001.html @web-platform-tests/interop +css/css-grid/placement/grid-container-change-named-grid-recompute-child-positions-001.html @web-platform-tests/interop +css/css-grid/subgrid/grid-template-computed-nogrid.html @web-platform-tests/interop +css/css-grid/subgrid/grid-template-invalid.html @web-platform-tests/interop +css/css-grid/subgrid/grid-template-valid.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-baseline-005.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-baseline-006.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-baseline-007.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-baseline-008.html @web-platform-tests/interop +css/css-grid/subgrid/subgrid-baseline-009.html @web-platform-tests/interop +css/css-grid/table-grid-item-005.html @web-platform-tests/interop +css/css-logical/logical-box-border-color.html @web-platform-tests/interop +css/css-logical/logical-box-border-radius.html @web-platform-tests/interop +css/css-logical/logical-box-border-shorthands.html @web-platform-tests/interop +css/css-logical/logical-box-border-style.html @web-platform-tests/interop +css/css-logical/logical-box-border-width.html @web-platform-tests/interop +css/css-logical/logical-box-inset.html @web-platform-tests/interop +css/css-logical/logical-box-margin.html @web-platform-tests/interop +css/css-logical/logical-box-padding.html @web-platform-tests/interop +css/css-logical/logical-box-size.html @web-platform-tests/interop +css/css-multicol/getclientrects-001.html @web-platform-tests/interop +css/css-overflow/logical-overflow-001.html @web-platform-tests/interop +css/css-overflow/overflow-shorthand-002.html @web-platform-tests/interop +css/css-overflow/parsing/overflow-computed.html @web-platform-tests/interop +css/css-overflow/parsing/overflow-invalid.html @web-platform-tests/interop +css/css-overflow/parsing/overflow-valid.html @web-platform-tests/interop +css/css-overscroll-behavior/overscroll-behavior-root.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-color-computed.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-color-invalid.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-color-valid.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-computed.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-invalid.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-line-computed.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-line-invalid.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-line-valid.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-shorthand.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-skip-ink-computed.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-skip-ink-invalid.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-skip-ink-valid.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-style-computed.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-style-invalid.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-style-valid.html @web-platform-tests/interop +css/css-text-decor/parsing/text-decoration-valid.html @web-platform-tests/interop +css/css-view-transitions/auto-name-get-animations.html @web-platform-tests/interop +css/css-view-transitions/duplicate-tag-rejects-capture.html @web-platform-tests/interop +css/css-view-transitions/duplicate-tag-rejects-start.html @web-platform-tests/interop +css/css-view-transitions/dynamic-stylesheet-animations.html @web-platform-tests/interop +css/css-view-transitions/event-pseudo-name.html @web-platform-tests/interop +css/css-view-transitions/group-animation-for-root-transition.html @web-platform-tests/interop +css/css-view-transitions/hit-test-pseudo-element-element-from-point.html @web-platform-tests/interop +css/css-view-transitions/hit-test-unpainted-element-from-point.html @web-platform-tests/interop +css/css-view-transitions/input-targets-root-while-render-blocked.html @web-platform-tests/interop +css/css-view-transitions/mix-blend-mode-only-on-transition.html @web-platform-tests/interop +css/css-view-transitions/no-crash-set-exception.html @web-platform-tests/interop +css/css-view-transitions/no-crash-view-transition-in-massive-iframe.html @web-platform-tests/interop +css/css-view-transitions/no-css-animation-while-render-blocked.html @web-platform-tests/interop +css/css-view-transitions/no-raf-while-render-blocked.html @web-platform-tests/interop +css/css-view-transitions/only-child-group.html @web-platform-tests/interop +css/css-view-transitions/only-child-image-pair.html @web-platform-tests/interop +css/css-view-transitions/only-child-new.html @web-platform-tests/interop +css/css-view-transitions/only-child-no-transition.html @web-platform-tests/interop +css/css-view-transitions/only-child-old.html @web-platform-tests/interop +css/css-view-transitions/only-child-on-root-element-with-view-transition.html @web-platform-tests/interop +css/css-view-transitions/only-child-view-transition.html @web-platform-tests/interop +css/css-view-transitions/parsing/pseudo-elements-invalid-with-classes.html @web-platform-tests/interop +css/css-view-transitions/parsing/pseudo-elements-invalid.html @web-platform-tests/interop +css/css-view-transitions/parsing/pseudo-elements-valid-with-classes.html @web-platform-tests/interop +css/css-view-transitions/parsing/pseudo-elements-valid.html @web-platform-tests/interop +css/css-view-transitions/parsing/view-transition-class-computed.html @web-platform-tests/interop +css/css-view-transitions/parsing/view-transition-class-invalid.html @web-platform-tests/interop +css/css-view-transitions/parsing/view-transition-class-valid.html @web-platform-tests/interop +css/css-view-transitions/parsing/view-transition-name-computed.html @web-platform-tests/interop +css/css-view-transitions/parsing/view-transition-name-invalid.html @web-platform-tests/interop +css/css-view-transitions/parsing/view-transition-name-valid.html @web-platform-tests/interop +css/css-view-transitions/paused-animation-at-end.html @web-platform-tests/interop +css/css-view-transitions/pseudo-computed-style-stays-in-sync-with-new-element.html @web-platform-tests/interop +css/css-view-transitions/pseudo-element-animations-rerun.html @web-platform-tests/interop +css/css-view-transitions/pseudo-element-animations.html @web-platform-tests/interop +css/css-view-transitions/pseudo-get-computed-style.html @web-platform-tests/interop +css/css-view-transitions/ready_resolves_after_dom_before_raf.html @web-platform-tests/interop +css/css-view-transitions/style-inheritance.html @web-platform-tests/interop +css/css-view-transitions/synchronous-callback-skipped-before-run.html @web-platform-tests/interop +css/css-view-transitions/transition-in-hidden-page.html @web-platform-tests/interop +css/css-view-transitions/transition-skipped-after-animation-started.html @web-platform-tests/interop +css/css-view-transitions/transition-skipped-from-invalid-callback.html @web-platform-tests/interop +css/css-view-transitions/unset-and-initial-view-transition-name.html @web-platform-tests/interop +css/css-view-transitions/update-callback-timeout.html @web-platform-tests/interop +css/css-view-transitions/view-transition-name-on-removed-element.html @web-platform-tests/interop +css/css-view-transitions/web-animation-pseudo-incorrect-name.html @web-platform-tests/interop +css/css-view-transitions/window-resize-aborts-transition-before-ready.html @web-platform-tests/interop +css/css-view-transitions/window-resize-aborts-transition.html @web-platform-tests/interop +css/css-viewport/zoom/parsing/zoom-computed.html @web-platform-tests/interop +css/css-viewport/zoom/parsing/zoom-valid.html @web-platform-tests/interop +css/css-viewport/zoom/relative-units.html @web-platform-tests/interop +css/css-viewport/zoom/scroll-top-test-with-zoom.html @web-platform-tests/interop +css/css-viewport/zoom/widget.html @web-platform-tests/interop +css/css-writing-modes/forms/select-multiple-keyboard-selection.optional.html @web-platform-tests/interop +css/css-writing-modes/forms/select-multiple-options-visual-order.html @web-platform-tests/interop +css/css-writing-modes/forms/select-multiple-scrolling.optional.html @web-platform-tests/interop +css/css-writing-modes/forms/select-size-scrolling-and-sizing.optional.html @web-platform-tests/interop +css/css-writing-modes/forms/text-input-block-size.optional.html @web-platform-tests/interop +css/css-writing-modes/forms/text-input-vertical-overflow-no-scroll.html @web-platform-tests/interop +css/css-writing-modes/forms/textarea-rows-cols-sizing.html @web-platform-tests/interop +css/css-writing-modes/writing-mode-parsing-sideways-lr-001.html @web-platform-tests/interop +css/css-writing-modes/writing-mode-parsing-sideways-rl-001.html @web-platform-tests/interop +css/cssom/caretPositionFromPoint-with-transformation.html @web-platform-tests/interop +css/cssom/caretPositionFromPoint.html @web-platform-tests/interop +css/cssom-view/scrollIntoView-sideways-lr-writing-mode-and-rtl-direction.html @web-platform-tests/interop +css/cssom-view/scrollIntoView-sideways-lr-writing-mode.html @web-platform-tests/interop +css/cssom-view/scrollIntoView-sideways-rl-writing-mode-and-rtl-direction.html @web-platform-tests/interop +css/cssom-view/scrollIntoView-sideways-rl-writing-mode.html @web-platform-tests/interop +css/filter-effects/animation/backdrop-filter-interpolation-001.html @web-platform-tests/interop +css/filter-effects/animation/backdrop-filter-interpolation-002.html @web-platform-tests/interop +css/filter-effects/animation/backdrop-filter-interpolation-003.html @web-platform-tests/interop +css/filter-effects/animation/backdrop-filter-interpolation-004.html @web-platform-tests/interop +css/filter-effects/backdrop-filter-important.html @web-platform-tests/interop +css/filter-effects/parsing/backdrop-filter-computed.html @web-platform-tests/interop +css/filter-effects/parsing/backdrop-filter-parsing-invalid.html @web-platform-tests/interop +css/filter-effects/parsing/backdrop-filter-parsing-valid.html @web-platform-tests/interop +dom/events/scrolling/scrollend-event-fired-for-mandatory-snap-point-after-load.html @web-platform-tests/interop +dom/events/scrolling/scrollend-event-fired-for-programmatic-scroll.html @web-platform-tests/interop +dom/events/scrolling/scrollend-event-fired-for-scroll-attr-change.html @web-platform-tests/interop +dom/events/scrolling/scrollend-event-fired-for-scrollIntoView.html @web-platform-tests/interop +dom/events/scrolling/scrollend-event-fired-to-document.html @web-platform-tests/interop +dom/events/scrolling/scrollend-event-fired-to-window.html @web-platform-tests/interop +dom/events/scrolling/scrollend-event-handler-content-attributes.html @web-platform-tests/interop +dom/events/scrolling/scrollend-event-not-fired-on-no-scroll.html @web-platform-tests/interop +dom/events/scrolling/scrollend-fires-to-text-input.html @web-platform-tests/interop +dom/events/scrolling/scrollend-with-snap-on-fractional-offset.html @web-platform-tests/interop +dom/historical-mutation-events.html @web-platform-tests/interop +event-timing/auxclick.html @web-platform-tests/interop +event-timing/buffered-and-duration-threshold.html @web-platform-tests/interop +event-timing/buffered-flag.html @web-platform-tests/interop +event-timing/click-timing.html @web-platform-tests/interop +event-timing/click.html @web-platform-tests/interop +event-timing/contextmenu.html @web-platform-tests/interop +event-timing/crossiframe.html @web-platform-tests/interop +event-timing/dblclick.html @web-platform-tests/interop +event-timing/disconnect-target.html @web-platform-tests/interop +event-timing/duration-with-target-low.html @web-platform-tests/interop +event-timing/event-click-counts.html @web-platform-tests/interop +event-timing/event-click-visibilitychange.html @web-platform-tests/interop +event-timing/event-counts-zero.html @web-platform-tests/interop +event-timing/event-retarget.html @web-platform-tests/interop +event-timing/first-input-interactionid-click.html @web-platform-tests/interop +event-timing/first-input-interactionid-key.html @web-platform-tests/interop +event-timing/first-input-interactionid-tap.html @web-platform-tests/interop +event-timing/first-input-shadow-dom.html @web-platform-tests/interop +event-timing/idlharness.any.js @web-platform-tests/interop +event-timing/interaction-count-click.html @web-platform-tests/interop +event-timing/interaction-count-press-key.html @web-platform-tests/interop +event-timing/interaction-count-tap.html @web-platform-tests/interop +event-timing/interactionid-aux-pointerdown-and-pointerdown.html @web-platform-tests/interop +event-timing/interactionid-aux-pointerdown.html @web-platform-tests/interop +event-timing/interactionid-auxclick.html @web-platform-tests/interop +event-timing/interactionid-click.html @web-platform-tests/interop +event-timing/interactionid-keyboard-event-simulated-click-button-space.html @web-platform-tests/interop +event-timing/interactionid-keyboard-event-simulated-click-checkbox-space.html @web-platform-tests/interop +event-timing/interactionid-keyboard-event-simulated-click-link-enter.html @web-platform-tests/interop +event-timing/interactionid-keypress.html @web-platform-tests/interop +event-timing/interactionid-orphan-pointerup.html @web-platform-tests/interop +event-timing/interactionid-press-key-as-input.html @web-platform-tests/interop +event-timing/interactionid-press-key-no-effect.html @web-platform-tests/interop +event-timing/interactionid-tap.html @web-platform-tests/interop +event-timing/keydown.html @web-platform-tests/interop +event-timing/keyup.html @web-platform-tests/interop +event-timing/large-duration-threshold.html @web-platform-tests/interop +event-timing/medium-duration-threshold.html @web-platform-tests/interop +event-timing/min-duration-threshold.html @web-platform-tests/interop +event-timing/modal-dialog-interrupt-paint.html @web-platform-tests/interop +event-timing/mousedown.html @web-platform-tests/interop +event-timing/mouseenter.html @web-platform-tests/interop +event-timing/mouseleave.html @web-platform-tests/interop +event-timing/mouseout.html @web-platform-tests/interop +event-timing/mouseover.html @web-platform-tests/interop +event-timing/mouseup.html @web-platform-tests/interop +event-timing/only-observe-firstInput.html @web-platform-tests/interop +event-timing/pointerdown.html @web-platform-tests/interop +event-timing/pointerenter.html @web-platform-tests/interop +event-timing/pointerleave.html @web-platform-tests/interop +event-timing/pointerout.html @web-platform-tests/interop +event-timing/pointerover.html @web-platform-tests/interop +event-timing/pointerup.html @web-platform-tests/interop +event-timing/programmatic-click-not-observed.html @web-platform-tests/interop +event-timing/retrievability.html @web-platform-tests/interop +event-timing/retrieve-firstInput.html @web-platform-tests/interop +event-timing/selection-autoscroll.html @web-platform-tests/interop +event-timing/shadow-dom-null-target.html @web-platform-tests/interop +event-timing/supported-types-consistent-with-self.html @web-platform-tests/interop +event-timing/supported-types.window.js @web-platform-tests/interop +event-timing/timingconditions.html @web-platform-tests/interop +event-timing/toJSON.html @web-platform-tests/interop +html/editing/the-hidden-attribute/beforematch-element-fragment-navigation.html @web-platform-tests/interop +html/editing/the-hidden-attribute/beforematch-scroll-to-text-fragment.html @web-platform-tests/interop +html/editing/the-hidden-attribute/hidden-idl.html @web-platform-tests/interop +html/editing/the-hidden-attribute/hidden-ua-stylesheet.html @web-platform-tests/interop +html/editing/the-hidden-attribute/hidden-until-found-002.html @web-platform-tests/interop +html/editing/the-hidden-attribute/hidden-until-found-text-fragment.html @web-platform-tests/interop +html/rendering/the-details-element/auto-expand-details-text-fragment.html @web-platform-tests/interop +html/rendering/the-details-element/details-blockification.html @web-platform-tests/interop +html/rendering/the-details-element/details-display.html @web-platform-tests/interop +html/rendering/the-details-element/details-pseudo-elements-006.html @web-platform-tests/interop +html/semantics/disabled-elements/disabled-event-dispatch.tentative.html @web-platform-tests/interop +html/semantics/disabled-elements/event-propagate-disabled.tentative.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/import-attributes/dynamic-import-with-attributes-argument.any.js @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/import-attributes/empty-attributes-clause.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/import-attributes/invalid-import-errors-order.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/import-attributes/invalid-type-attribute-error.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/import-attributes/unsupported-attribute.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/charset-2.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/charset-bom.any.js @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/charset.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/cors-crossorigin-requests.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/credentials.sub.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/integrity.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/invalid-content-type.any.js @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/json-module-service-worker-test.https.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/load-error-events.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/module.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/non-object.any.js @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/parse-error.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/referrer-policies.sub.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/repeated-imports.any.js @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/script-element-json-src.html @web-platform-tests/interop +html/semantics/scripting-1/the-script-element/json-module/valid-content-type.html @web-platform-tests/interop +html/user-activation/activation-trigger-pointerevent.html @web-platform-tests/interop +largest-contentful-paint/background-image-set-image.html @web-platform-tests/interop +largest-contentful-paint/broken-image-icon.html @web-platform-tests/interop +largest-contentful-paint/contracted-image.html @web-platform-tests/interop +largest-contentful-paint/cross-origin-image.sub.html @web-platform-tests/interop +largest-contentful-paint/element-only-when-fully-active.html @web-platform-tests/interop +largest-contentful-paint/expanded-image.html @web-platform-tests/interop +largest-contentful-paint/first-letter-background.html @web-platform-tests/interop +largest-contentful-paint/first-paint-equals-lcp-text.html @web-platform-tests/interop +largest-contentful-paint/idlharness.html @web-platform-tests/interop +largest-contentful-paint/iframe-content-not-observed.html @web-platform-tests/interop +largest-contentful-paint/image-TAO.sub.html @web-platform-tests/interop +largest-contentful-paint/image-full-viewport.html @web-platform-tests/interop +largest-contentful-paint/image-inside-svg.html @web-platform-tests/interop +largest-contentful-paint/image-not-fully-visible.html @web-platform-tests/interop +largest-contentful-paint/image-removed-before-load.html @web-platform-tests/interop +largest-contentful-paint/image-src-change.html @web-platform-tests/interop +largest-contentful-paint/image-sw-same-origin.https.html @web-platform-tests/interop +largest-contentful-paint/image-upscaling.html @web-platform-tests/interop +largest-contentful-paint/initially-invisible-images.html @web-platform-tests/interop +largest-contentful-paint/invisible-images-composited-1.html @web-platform-tests/interop +largest-contentful-paint/invisible-images-composited-2.html @web-platform-tests/interop +largest-contentful-paint/invisible-images.html @web-platform-tests/interop +largest-contentful-paint/larger-image.html @web-platform-tests/interop +largest-contentful-paint/larger-text.html @web-platform-tests/interop +largest-contentful-paint/loadTime-after-appendChild.html @web-platform-tests/interop +largest-contentful-paint/multiple-image-same-src.html @web-platform-tests/interop +largest-contentful-paint/multiple-redirects-TAO.html @web-platform-tests/interop +largest-contentful-paint/observe-after-untrusted-scroll.html @web-platform-tests/interop +largest-contentful-paint/observe-css-generated-image.html @web-platform-tests/interop +largest-contentful-paint/observe-css-generated-text.html @web-platform-tests/interop +largest-contentful-paint/observe-image.html @web-platform-tests/interop +largest-contentful-paint/observe-mathml.html @web-platform-tests/interop +largest-contentful-paint/observe-random-namespace.html @web-platform-tests/interop +largest-contentful-paint/observe-svg-background-image.html @web-platform-tests/interop +largest-contentful-paint/observe-svg-data-uri-background-image.html @web-platform-tests/interop +largest-contentful-paint/observe-svg-data-uri-image.html @web-platform-tests/interop +largest-contentful-paint/observe-svg-image.html @web-platform-tests/interop +largest-contentful-paint/observe-text.html @web-platform-tests/interop +largest-contentful-paint/placeholder-image.html @web-platform-tests/interop +largest-contentful-paint/progressively-loaded-image.html @web-platform-tests/interop +largest-contentful-paint/redirects-tao-star.html @web-platform-tests/interop +largest-contentful-paint/repeated-image.html @web-platform-tests/interop +largest-contentful-paint/resized-image-not-reconsidered.html @web-platform-tests/interop +largest-contentful-paint/same-origin-redirects.html @web-platform-tests/interop +largest-contentful-paint/supported-lcp-type.html @web-platform-tests/interop +largest-contentful-paint/text-with-display-style.html @web-platform-tests/interop +largest-contentful-paint/toJSON.html @web-platform-tests/interop +largest-contentful-paint/transparent-text-with-shadow.html @web-platform-tests/interop +largest-contentful-paint/transparent-text-with-text-stroke.html @web-platform-tests/interop +largest-contentful-paint/transparent-text.html @web-platform-tests/interop +largest-contentful-paint/update-on-style-change.html @web-platform-tests/interop +largest-contentful-paint/video-data-uri.html @web-platform-tests/interop +largest-contentful-paint/video-play-after-poster.html @web-platform-tests/interop +largest-contentful-paint/video-poster.html @web-platform-tests/interop +largest-contentful-paint/web-font-styled-text-resize-block.html @web-platform-tests/interop +largest-contentful-paint/web-font-styled-text-resize-swap-after-interaction.html @web-platform-tests/interop +navigation-api/currententrychange-event/anchor-click.html @web-platform-tests/interop +navigation-api/currententrychange-event/constructor.html @web-platform-tests/interop +navigation-api/currententrychange-event/history-back-same-doc.html @web-platform-tests/interop +navigation-api/currententrychange-event/history-pushState.html @web-platform-tests/interop +navigation-api/currententrychange-event/history-replaceState.html @web-platform-tests/interop +navigation-api/currententrychange-event/location-api.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigate-from-initial-about-blank-same-doc-popup.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigate-from-initial-about-blank-same-doc.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigate-from-initial-about-blank.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigation-back-forward-cross-doc.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigation-back-forward-same-doc.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigation-navigate-cross-doc.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigation-navigate-intercept.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigation-navigate-preventDefault.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigation-navigate-replace-cross-doc.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigation-navigate-replace-intercept.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigation-navigate-replace-same-doc.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigation-navigate-same-doc.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigation-reload-cross-doc.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigation-reload-intercept.html @web-platform-tests/interop +navigation-api/currententrychange-event/navigation-updateCurrentEntry.html @web-platform-tests/interop +navigation-api/currententrychange-event/not-on-load.html @web-platform-tests/interop +navigation-api/currententrychange-event/properties.html @web-platform-tests/interop +navigation-api/focus-reset/autofocus.html @web-platform-tests/interop +navigation-api/focus-reset/basic.html @web-platform-tests/interop +navigation-api/focus-reset/change-focus-again-in-blur-during-intercept.html @web-platform-tests/interop +navigation-api/focus-reset/change-focus-back-to-origial-during-intercept.html @web-platform-tests/interop +navigation-api/focus-reset/change-focus-during-intercept.html @web-platform-tests/interop +navigation-api/focus-reset/change-focus-then-remove-during-intercept.html @web-platform-tests/interop +navigation-api/focus-reset/focus-reset-timing.html @web-platform-tests/interop +navigation-api/focus-reset/multiple-intercept.html @web-platform-tests/interop +navigation-api/navigate-event/cross-origin-traversal-does-not-fire-navigate.html @web-platform-tests/interop +navigation-api/navigate-event/cross-origin-traversal-redirect.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/click-crossdocument-crossorigin-sameorigindomain.sub.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/click-crossdocument-crossorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/click-crossdocument-sameorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/click-samedocument-crossorigin-sameorigindomain.sub.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/click-samedocument-crossorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/click-samedocument-sameorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin-sameorigindomain.sub.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/location-crossdocument-crossorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/location-crossdocument-sameorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/location-samedocument-crossorigin-sameorigindomain.sub.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/location-samedocument-crossorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/location-samedocument-sameorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin-sameorigindomain.sub.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/open-crossdocument-crossorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/open-crossdocument-sameorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/open-samedocument-crossorigin-sameorigindomain.sub.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/open-samedocument-crossorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/open-samedocument-sameorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/submit-crossdocument-crossorigin-sameorigindomain.sub.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/submit-crossdocument-crossorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/submit-crossdocument-sameorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/submit-samedocument-crossorigin-sameorigindomain.sub.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/submit-samedocument-crossorigin.html @web-platform-tests/interop +navigation-api/navigate-event/cross-window/submit-samedocument-sameorigin.html @web-platform-tests/interop +navigation-api/navigate-event/defaultPrevented-navigation-preempted.html @web-platform-tests/interop +navigation-api/navigate-event/defaultPrevented-window-stop-after-dispatch.html @web-platform-tests/interop +navigation-api/navigate-event/event-constructor.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-after-dispatch.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-and-navigate.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-canceled-event.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-cross-document-same-origin.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-cross-origin.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-detach-multiple.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-detach.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-handler-null-or-undefined.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-handler-returns-non-promise.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-handler-throws.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-history-pushState.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-history-replaceState.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-multiple-times-reject.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-multiple-times.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-navigation-back.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-on-synthetic-event.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-popstate.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-reject.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-resolve.html @web-platform-tests/interop +navigation-api/navigate-event/intercept-same-document-history-back.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-anchor-cross-origin.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-anchor-download-userInitiated.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-anchor-download.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-anchor-fragment.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-anchor-same-origin-cross-document.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-anchor-userInitiated.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-anchor-with-target.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-destination-after-detach.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-destination-dynamic-index.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-destination-getState-back-forward.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-destination-getState-navigate.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-destination-getState-reload.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-form-get.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-form-reload.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-form-requestSubmit.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-form-traverse.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-form-userInitiated.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-form-with-target.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-form.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-history-back-after-fragment.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-history-back-after-pushState.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-history-back-bfcache.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-history-back-cross-document.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-history-back-noop.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-history-go-0.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-history-pushState.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-history-replaceState.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-iframe-location.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-location.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-meta-refresh.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-navigation-back-cross-document.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-navigation-back-same-document-in-iframe.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-navigation-back-same-document.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-navigation-navigate.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-svg-anchor-fragment.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-to-javascript.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-to-srcdoc.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-window-open-self.html @web-platform-tests/interop +navigation-api/navigate-event/navigate-window-open.html @web-platform-tests/interop +navigation-api/navigate-event/navigatesuccess-cross-document.html @web-platform-tests/interop +navigation-api/navigate-event/navigatesuccess-same-document.html @web-platform-tests/interop +navigation-api/navigate-event/navigation-back-cross-document-preventDefault.html @web-platform-tests/interop +navigation-api/navigate-event/navigation-back-same-document-preventDefault.html @web-platform-tests/interop +navigation-api/navigate-event/navigation-traverseTo-in-iframe-same-document-preventDefault.html @web-platform-tests/interop +navigation-api/navigate-event/navigation-traverseTo-navigates-top-and-same-doc-child-and-cross-doc-child.html @web-platform-tests/interop +navigation-api/navigate-event/navigation-traverseTo-same-document-preventDefault-multiple-windows.html @web-platform-tests/interop +navigation-api/navigate-event/navigation-traverseTo-top-cancels-cross-document-child.html @web-platform-tests/interop +navigation-api/navigate-event/replaceState-inside-back-handler.html @web-platform-tests/interop +navigation-api/navigate-event/same-url-replace-cross-document.html @web-platform-tests/interop +navigation-api/navigate-event/same-url-replace-same-document.html @web-platform-tests/interop +navigation-api/navigate-event/signal-abort-detach-in-onnavigate.html @web-platform-tests/interop +navigation-api/navigate-event/signal-abort-intercept.html @web-platform-tests/interop +navigation-api/navigate-event/signal-abort-preventDefault.html @web-platform-tests/interop +navigation-api/navigate-event/signal-abort-window-stop-after-intercept.html @web-platform-tests/interop +navigation-api/navigate-event/signal-abort-window-stop-in-onnavigate.html @web-platform-tests/interop +navigation-api/navigate-event/signal-abort-window-stop.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-after-bfcache-cross-origin.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-after-bfcache.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-history-pushState.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-history-replaceState.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-initial-about-blank.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-push-cross-origin.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-push.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-reload.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-replace-cross-origin.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-replace.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-same-document-then-cross-document.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-traverse-not-in-entries.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-traverse-then-clobber.html @web-platform-tests/interop +navigation-api/navigation-activation/activation-traverse.html @web-platform-tests/interop +navigation-api/navigation-history-entry/after-detach.html @web-platform-tests/interop +navigation-api/navigation-history-entry/current-basic.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-across-origins.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-after-bfcache-in-iframe.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-after-bfcache.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-after-blank-navigation-from-cross-origin.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-after-blank-navigation.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-after-blob-navigation.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-after-cross-document-forward-pruning.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-after-javascript-url-navigation.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-after-navigations-in-multiple-windows.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-after-srcdoc-navigation.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-array-equality.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-in-new-javascript-url-iframe.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-in-new-srcdoc-iframe.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entries-when-inactive.html @web-platform-tests/interop +navigation-api/navigation-history-entry/entry-after-detach.html @web-platform-tests/interop +navigation-api/navigation-history-entry/index-not-in-entries.html @web-platform-tests/interop +navigation-api/navigation-history-entry/key-id-back-cross-document.html @web-platform-tests/interop +navigation-api/navigation-history-entry/key-id-back-same-document.html @web-platform-tests/interop +navigation-api/navigation-history-entry/key-id-location-reload-intercept.html @web-platform-tests/interop +navigation-api/navigation-history-entry/key-id-location-reload.html @web-platform-tests/interop +navigation-api/navigation-history-entry/key-id-location-replace-cross-origin.html @web-platform-tests/interop +navigation-api/navigation-history-entry/key-id-location-replace.html @web-platform-tests/interop +navigation-api/navigation-history-entry/no-referrer-dynamic-url-censored.html @web-platform-tests/interop +navigation-api/navigation-history-entry/no-referrer-from-meta-url-censored.html @web-platform-tests/interop +navigation-api/navigation-history-entry/no-referrer-url-censored.html @web-platform-tests/interop +navigation-api/navigation-history-entry/opaque-origin-data-url.html @web-platform-tests/interop +navigation-api/navigation-history-entry/opaque-origin.html @web-platform-tests/interop +navigation-api/navigation-history-entry/sameDocument-after-fragment-navigate.html @web-platform-tests/interop +navigation-api/navigation-history-entry/sameDocument-after-navigate-restore.html @web-platform-tests/interop +navigation-api/navigation-history-entry/sameDocument-after-navigate.html @web-platform-tests/interop +navigation-api/navigation-history-entry/state-after-navigate-restore.html @web-platform-tests/interop +navigation-api/navigation-methods/back-forward-multiple-frames.html @web-platform-tests/interop +navigation-api/navigation-methods/disambigaute-back.html @web-platform-tests/interop +navigation-api/navigation-methods/disambigaute-forward.html @web-platform-tests/interop +navigation-api/navigation-methods/disambigaute-traverseTo-back-multiple.html @web-platform-tests/interop +navigation-api/navigation-methods/disambigaute-traverseTo-forward-multiple.html @web-platform-tests/interop +navigation-api/navigation-methods/forward-to-pruned-entry.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-base-url.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-from-initial-about-blank-gc.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-from-initial-about-blank-src.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-from-initial-about-blank.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-history-push-not-loaded.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-history-push-same-url-cross-document.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-history-push-same-url.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-history-state-replace.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-history-state.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-info-and-state.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-intercept-history-state.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-relative-url-utf8.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-relative-url.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-replace-cross-document.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-replace-same-document.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-same-document.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-state-repeated-await.html @web-platform-tests/interop +navigation-api/navigation-methods/navigate-state-repeated.html @web-platform-tests/interop +navigation-api/navigation-methods/reload-base-url.html @web-platform-tests/interop +navigation-api/navigation-methods/reload-info.html @web-platform-tests/interop +navigation-api/navigation-methods/reload-navigation-timing.html @web-platform-tests/interop +navigation-api/navigation-methods/reload-no-args.html @web-platform-tests/interop +navigation-api/navigation-methods/reload-service-worker-fetch-event.html @web-platform-tests/interop +navigation-api/navigation-methods/reload-state-and-info.html @web-platform-tests/interop +navigation-api/navigation-methods/reload-state-undefined.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/back-204-205-download.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/back-already-detached.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/back-beforeunload.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/back-forward-initial-about-blank.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/back-forward-opaque-origin.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/back-forward-out-of-bounds.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/back-intercept-rejected.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/back-intercept.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/back.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/forward-already-detached.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/forward-beforeunload.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/forward-intercept-rejected.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/forward-intercept.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/forward.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-204-205-download.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-already-detached.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-beforeunload.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-cross-document.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-detach-in-onnavigate.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-detach-in-serialization.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-file-url.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-initial-about-blank-cross-document.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-initial-about-blank.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-intercept-interrupted.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-intercept-rejected.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-intercept.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-interrupted-within-onnavigate.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-interrupted.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-invalid-url.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-opaque-origin.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-pagehide.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-preventDefault.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-push-initial-about-blank.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-push-javascript-url.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-rejection-order-beforeunload-unserializablestate.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-rejection-order-detached-unserializablestate.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-beforeunload.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-detached.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-pagehide.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-rejection-order-invalidurl-unserializablestate.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-rejection-order-pagehide-unserializablestate.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate-unserializable-state.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/navigate.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-already-detached.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-beforeunload.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-detach-in-onnavigate.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-detach-in-serialization.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-initial-about-blank.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-intercept-rejected.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-intercept.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-pagehide.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-preventDefault.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-rejection-order-beforeunload-unserializablestate.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-rejection-order-detached-unserializablestate.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-rejection-order-pagehide-unserializablestate.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload-unserializable-state.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/reload.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo-already-detached.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo-beforeunload.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo-cross-document-preventDefault.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo-current.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo-detach-cross-document-before-navigate-event.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo-detach-cross-document.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo-detach-same-document-before-navigate-event.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo-detach-same-document.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo-intercept-rejected.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo-intercept.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo-invalid-key.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo-repeated.html @web-platform-tests/interop +navigation-api/navigation-methods/return-value/traverseTo.html @web-platform-tests/interop +navigation-api/navigation-methods/sandboxing-back-parent.html @web-platform-tests/interop +navigation-api/navigation-methods/sandboxing-back-sibling.html @web-platform-tests/interop +navigation-api/navigation-methods/sandboxing-navigate-parent.html @web-platform-tests/interop +navigation-api/navigation-methods/sandboxing-navigate-sibling.html @web-platform-tests/interop +navigation-api/navigation-methods/traverseTo-after-adding-iframe.html @web-platform-tests/interop +navigation-api/navigation-methods/traverseTo-after-data-url.html @web-platform-tests/interop +navigation-api/navigation-methods/traverseTo-cross-document.html @web-platform-tests/interop +navigation-api/navigation-methods/traverseTo-detach-between-navigate-and-navigatesuccess.html @web-platform-tests/interop +navigation-api/navigation-methods/traverseTo-multiple-steps.html @web-platform-tests/interop +navigation-api/navigation-methods/traverseTo-navigates-multiple-iframes.html @web-platform-tests/interop +navigation-api/navigation-methods/traverseTo-same-document.html @web-platform-tests/interop +navigation-api/navigation-methods/traverseTo-with-cross-origin-in-history.html @web-platform-tests/interop +navigation-api/ordering-and-transition/anchor-download-intercept-reject.html @web-platform-tests/interop +navigation-api/ordering-and-transition/anchor-download-intercept.html @web-platform-tests/interop +navigation-api/ordering-and-transition/anchor-download.html @web-platform-tests/interop +navigation-api/ordering-and-transition/back-cross-document-event-order.html @web-platform-tests/interop +navigation-api/ordering-and-transition/back-same-document-intercept-reject.html @web-platform-tests/interop +navigation-api/ordering-and-transition/back-same-document-intercept.html @web-platform-tests/interop +navigation-api/ordering-and-transition/back-same-document.html @web-platform-tests/interop +navigation-api/ordering-and-transition/currententrychange-before-popstate-intercept.html @web-platform-tests/interop +navigation-api/ordering-and-transition/currententrychange-dispose-ordering.html @web-platform-tests/interop +navigation-api/ordering-and-transition/intercept-async.html @web-platform-tests/interop +navigation-api/ordering-and-transition/location-href-canceled.html @web-platform-tests/interop +navigation-api/ordering-and-transition/location-href-double-intercept.html @web-platform-tests/interop +navigation-api/ordering-and-transition/location-href-intercept-reentrant.html @web-platform-tests/interop +navigation-api/ordering-and-transition/location-href-intercept-reject.html @web-platform-tests/interop +navigation-api/ordering-and-transition/location-href-intercept.html @web-platform-tests/interop +navigation-api/ordering-and-transition/navigate-204-205-download-then-same-document.html @web-platform-tests/interop +navigation-api/ordering-and-transition/navigate-canceled.html @web-platform-tests/interop +navigation-api/ordering-and-transition/navigate-cross-document-double.html @web-platform-tests/interop +navigation-api/ordering-and-transition/navigate-cross-document-event-order.html @web-platform-tests/interop +navigation-api/ordering-and-transition/navigate-double-intercept.html @web-platform-tests/interop +navigation-api/ordering-and-transition/navigate-in-transition-finished.html @web-platform-tests/interop +navigation-api/ordering-and-transition/navigate-intercept-stop.html @web-platform-tests/interop +navigation-api/ordering-and-transition/navigate-intercept.html @web-platform-tests/interop +navigation-api/ordering-and-transition/navigate-same-document-intercept-reentrant.html @web-platform-tests/interop +navigation-api/ordering-and-transition/navigate-same-document-intercept-reject.html @web-platform-tests/interop +navigation-api/ordering-and-transition/navigate-same-document.html @web-platform-tests/interop +navigation-api/ordering-and-transition/reload-canceled.html @web-platform-tests/interop +navigation-api/ordering-and-transition/reload-intercept-reject.html @web-platform-tests/interop +navigation-api/ordering-and-transition/reload-intercept.html @web-platform-tests/interop +navigation-api/ordering-and-transition/transition-cross-document.html @web-platform-tests/interop +navigation-api/ordering-and-transition/transition-finished-mark-as-handled.html @web-platform-tests/interop +navigation-api/ordering-and-transition/transition-realms-and-identity.html @web-platform-tests/interop +navigation-api/per-entry-events/dispose-after-bfcache.html @web-platform-tests/interop +navigation-api/per-entry-events/dispose-cross-document.html @web-platform-tests/interop +navigation-api/per-entry-events/dispose-for-navigation-in-child.html @web-platform-tests/interop +navigation-api/per-entry-events/dispose-same-document-intercept.html @web-platform-tests/interop +navigation-api/per-entry-events/dispose-same-document-navigate-during.html @web-platform-tests/interop +navigation-api/per-entry-events/dispose-same-document-reload-with-intercept.html @web-platform-tests/interop +navigation-api/per-entry-events/dispose-same-document-replace-with-intercept.html @web-platform-tests/interop +navigation-api/per-entry-events/dispose-same-document-replaceState.html @web-platform-tests/interop +navigation-api/per-entry-events/dispose-same-document.html @web-platform-tests/interop +navigation-api/per-entry-events/dispose-skip-current-on-truncate.html @web-platform-tests/interop +navigation-api/scroll-behavior/after-transition-basic.html @web-platform-tests/interop +navigation-api/scroll-behavior/after-transition-change-history-scroll-restoration-during-promise.html @web-platform-tests/interop +navigation-api/scroll-behavior/after-transition-explicit-scroll.html @web-platform-tests/interop +navigation-api/scroll-behavior/after-transition-intercept-handler-modifies.html @web-platform-tests/interop +navigation-api/scroll-behavior/after-transition-push.html @web-platform-tests/interop +navigation-api/scroll-behavior/after-transition-reject.html @web-platform-tests/interop +navigation-api/scroll-behavior/after-transition-reload.html @web-platform-tests/interop +navigation-api/scroll-behavior/after-transition-replace.html @web-platform-tests/interop +navigation-api/scroll-behavior/after-transition-timing.html @web-platform-tests/interop +navigation-api/scroll-behavior/manual-basic.html @web-platform-tests/interop +navigation-api/scroll-behavior/manual-immediate-scroll.html @web-platform-tests/interop +navigation-api/scroll-behavior/manual-scroll-after-dispatch.html @web-platform-tests/interop +navigation-api/scroll-behavior/manual-scroll-after-resolve.html @web-platform-tests/interop +navigation-api/scroll-behavior/manual-scroll-fragment-does-not-exist.html @web-platform-tests/interop +navigation-api/scroll-behavior/manual-scroll-push.html @web-platform-tests/interop +navigation-api/scroll-behavior/manual-scroll-reload.html @web-platform-tests/interop +navigation-api/scroll-behavior/manual-scroll-repeated.html @web-platform-tests/interop +navigation-api/scroll-behavior/manual-scroll-replace.html @web-platform-tests/interop +navigation-api/scroll-behavior/manual-scroll-resets-when-no-fragment.html @web-platform-tests/interop +navigation-api/scroll-behavior/scroll-after-preventDefault.html @web-platform-tests/interop +navigation-api/scroll-behavior/scroll-on-synthetic-event.html @web-platform-tests/interop +navigation-api/scroll-behavior/scroll-without-intercept.html @web-platform-tests/interop +navigation-api/state/cross-document-away-and-back.html @web-platform-tests/interop +navigation-api/state/cross-document-getState-undefined.html @web-platform-tests/interop +navigation-api/state/cross-document-getState.html @web-platform-tests/interop +navigation-api/state/cross-document-location-api.html @web-platform-tests/interop +navigation-api/state/history-pushState.html @web-platform-tests/interop +navigation-api/state/history-replaceState.html @web-platform-tests/interop +navigation-api/state/location-reload.html @web-platform-tests/interop +navigation-api/state/same-document-away-and-back-location-api.html @web-platform-tests/interop +navigation-api/state/same-document-away-and-back-navigation-api.html @web-platform-tests/interop +navigation-api/updateCurrentEntry-method/basic.html @web-platform-tests/interop +navigation-api/updateCurrentEntry-method/exception-order-initial-about-blank-unserializablestate.html @web-platform-tests/interop +navigation-api/updateCurrentEntry-method/exception-order-not-fully-active-unserializablestate.html @web-platform-tests/interop +navigation-api/updateCurrentEntry-method/initial-about-blank.html @web-platform-tests/interop +navigation-api/updateCurrentEntry-method/no-args.html @web-platform-tests/interop +navigation-api/updateCurrentEntry-method/not-fully-active.html @web-platform-tests/interop +navigation-api/updateCurrentEntry-method/opaque-origin.html @web-platform-tests/interop +navigation-api/updateCurrentEntry-method/same-document-away-and-back-location-api.html @web-platform-tests/interop +navigation-api/updateCurrentEntry-method/unserializable.html @web-platform-tests/interop +performance-timeline/buffered-flag-with-entryTypes-observer.tentative.any.js @web-platform-tests/interop +pointerevents/capturing_boundary_event_handler_at_ua_shadowdom.html @web-platform-tests/interop +pointerevents/coalesced_events_attributes.https.html @web-platform-tests/interop +pointerevents/idlharness.https.window.js @web-platform-tests/interop +pointerevents/mouse-pointer-boundary-events-for-shadowdom.html @web-platform-tests/interop +pointerevents/pointerevent_after_target_appended.html @web-platform-tests/interop +pointerevents/pointerevent_after_target_removed.html @web-platform-tests/interop +pointerevents/pointerevent_attributes.html @web-platform-tests/interop +pointerevents/pointerevent_auxclick_is_a_pointerevent.html @web-platform-tests/interop +pointerevents/pointerevent_boundary_events_at_implicit_release_hoverable_pointers.html @web-platform-tests/interop +pointerevents/pointerevent_capture_suppressing_mouse.html @web-platform-tests/interop +pointerevents/pointerevent_click_during_capture.html @web-platform-tests/interop +pointerevents/pointerevent_click_is_a_pointerevent.html @web-platform-tests/interop +pointerevents/pointerevent_click_is_a_pointerevent_multiple_clicks.html @web-platform-tests/interop +pointerevents/pointerevent_constructor.html @web-platform-tests/interop +pointerevents/pointerevent_contextmenu_is_a_pointerevent.html @web-platform-tests/interop +pointerevents/pointerevent_disabled_form_control.html @web-platform-tests/interop +pointerevents/pointerevent_fractional_coordinates.html @web-platform-tests/interop +pointerevents/pointerevent_hit_test_scroll_visible_descendant.html @web-platform-tests/interop +pointerevents/pointerevent_lostpointercapture_for_disconnected_node.html @web-platform-tests/interop +pointerevents/pointerevent_lostpointercapture_remove_setcapture_node.html @web-platform-tests/interop +pointerevents/pointerevent_mouse_capture_change_hover.html @web-platform-tests/interop +pointerevents/pointerevent_mouse_pointercapture_inactivate_pointer.html @web-platform-tests/interop +pointerevents/pointerevent_movementxy.html @web-platform-tests/interop +pointerevents/pointerevent_pointer_boundary_events_after_removing_last_over_element.html @web-platform-tests/interop +pointerevents/pointerevent_pointercapture_in_frame.html @web-platform-tests/interop +pointerevents/pointerevent_pointermove_on_chorded_mouse_button.html @web-platform-tests/interop +pointerevents/pointerevent_pointerout_no_pointer_movement.html @web-platform-tests/interop +pointerevents/pointerevent_pointerrawupdate.html @web-platform-tests/interop +pointerevents/pointerevent_releasepointercapture_events_to_original_target.html @web-platform-tests/interop +pointerevents/pointerevent_sequence_at_implicit_release_on_click.html @web-platform-tests/interop +pointerevents/pointerevent_setpointercapture_inactive_button_mouse.html @web-platform-tests/interop +pointerevents/pointerevent_touch-action-keyboard.html @web-platform-tests/interop +pointerevents/pointerup_after_pointerdown_target_removed.html @web-platform-tests/interop +pointerevents/predicted_events_attributes.html @web-platform-tests/interop +shadow-dom/Document-caretPositionFromPoint.tentative.html @web-platform-tests/interop +storage-access-api/hasStorageAccess-ABA.sub.https.window.js @web-platform-tests/interop +storage-access-api/hasStorageAccess-insecure.sub.window.js @web-platform-tests/interop +storage-access-api/hasStorageAccess.sub.https.window.js @web-platform-tests/interop +storage-access-api/idlharness.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-ABA.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-cross-origin-iframe-navigation-relax.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-cross-origin-iframe-navigation.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-cross-site-iframe.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-cross-site-sibling-iframes.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-dedicated-worker.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-insecure.sub.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-nested-cross-origin-iframe.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-nested-cross-site-iframe.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-nested-same-origin-iframe.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-non-fully-active.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-same-site-iframe.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-sandboxed-iframe-allow-storage-access.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-sandboxed-iframe-no-storage-access.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess-web-socket.sub.https.window.js @web-platform-tests/interop +storage-access-api/requestStorageAccess.sub.https.window.js @web-platform-tests/interop +storage-access-api/storage-access-permission.sub.https.window.js @web-platform-tests/interop +storage-access-api/storageAccess.testdriver.sub.html @web-platform-tests/interop +uievents/mouse/attributes.html @web-platform-tests/interop +uievents/mouse/cancel-mousedown-in-subframe.html @web-platform-tests/interop +uievents/mouse/mousemove_after_mouseover_target_removed.html @web-platform-tests/interop +uievents/mouse/mousemove_prevent_default_action.html @web-platform-tests/interop +urlpattern/urlpattern-hasregexpgroups.any.js @web-platform-tests/interop +urlpattern/urlpattern.any.js @web-platform-tests/interop +urlpattern/urlpattern.https.any.js @web-platform-tests/interop +wasm/jsapi/js-string/basic.any.js @web-platform-tests/interop +wasm/jsapi/js-string/constants.any.js @web-platform-tests/interop +wasm/jsapi/js-string/imports.any.js @web-platform-tests/interop +wasm/jsapi/memory/to-fixed-length-buffer.any.js @web-platform-tests/interop +wasm/jsapi/memory/to-resizable-buffer-shared.any.js @web-platform-tests/interop +wasm/jsapi/memory/to-resizable-buffer.any.js @web-platform-tests/interop +webrtc/transfer-datachannel.html @web-platform-tests/interop +webrtc-encoded-transform/RTCRtpScriptTransform-bad-chunk.https.html @web-platform-tests/interop +webrtc-encoded-transform/RTCRtpScriptTransform-encoded-transform.https.html @web-platform-tests/interop +webrtc-encoded-transform/RTCRtpScriptTransform-sender-worker-single-frame.https.html @web-platform-tests/interop +webrtc-encoded-transform/idlharness.https.window.js @web-platform-tests/interop +webrtc-encoded-transform/script-audio-transform.https.html @web-platform-tests/interop +webrtc-encoded-transform/script-change-transform.https.html @web-platform-tests/interop +webrtc-encoded-transform/script-metadata-transform.https.html @web-platform-tests/interop +webrtc-encoded-transform/script-transform-generateKeyFrame-simulcast.https.html @web-platform-tests/interop +webrtc-encoded-transform/script-transform-generateKeyFrame.https.html @web-platform-tests/interop +webrtc-encoded-transform/script-transform-sendKeyFrameRequest.https.html @web-platform-tests/interop +webrtc-encoded-transform/script-transform.https.html @web-platform-tests/interop +webrtc-encoded-transform/script-write-twice-transform.https.html @web-platform-tests/interop +workers/constructors/SharedWorker/same-origin.html @web-platform-tests/interop +workers/constructors/Worker/same-origin.html @web-platform-tests/interop diff --git a/tests/wpt/tests/IndexedDB/idbcursor-request-source.any.js b/tests/wpt/tests/IndexedDB/idbcursor-request-source.any.js index 2fe8c66f2e5..8e1b34ee798 100644 --- a/tests/wpt/tests/IndexedDB/idbcursor-request-source.any.js +++ b/tests/wpt/tests/IndexedDB/idbcursor-request-source.any.js @@ -6,21 +6,111 @@ 'use strict'; -[cursor => cursor.update(0), cursor => cursor.delete()].forEach( - func => indexeddb_test( - (t, db) => { - db.createObjectStore('store', {autoIncrement: true}); - }, - (t, db) => { - const tx = db.transaction('store', 'readwrite'); - const store = tx.objectStore('store'); - store.put('value'); - store.openCursor().onsuccess = t.step_func(e => { - const cursor = e.target.result; - assert_equals( - func(cursor).source, cursor, - `${func}.source should be the cursor itself`); +// Setup each test by populating an object store with an index for the cursor to +// iterate and manipulate. +function initializeDatabase(db) { + const store = db.createObjectStore('store', {autoIncrement: true}); + store.createIndex('index', /*keypath=*/ 'value'); + store.put({value: 'z'}); + store.put({value: 'y'}); + store.put({value: 'x'}); + store.put({value: 'w'}); +} + +function isIndex(cursorSourceType) { + return cursorSourceType === 'IDBIndex'; +} + +// Return the object store or index, depending on the test's `cursorSourceType`. +function getCursorSource(transaction, cursorSourceType) { + let cursorSource = transaction.objectStore('store'); + if (isIndex(cursorSourceType)) { + cursorSource = cursorSource.index('index'); + } + return cursorSource; +} + +// Verify the request source after calling delete() or update() on the cursor. +function cursor_request_source_test( + cursorSourceType, createRequestFunctionName, createRequestFunctionArgs) { + indexeddb_test( + (t, db) => initializeDatabase(db), + (t, db) => { + const tx = db.transaction('store', 'readwrite'); + const cursorSource = getCursorSource(tx, cursorSourceType); + + // Open the cursor. + const openCursorRequest = cursorSource.openCursor(); + openCursorRequest.onerror = + t.unreached_func('The cursor must not fail to open.'); + + openCursorRequest.onsuccess = t.step_func(e => { + // Use the cursor to create a new request. + const cursor = e.target.result; + const request = + cursor[createRequestFunctionName](...createRequestFunctionArgs); + assert_equals( + request.source, cursor, + `The request's source must be the cursor itself.`); + t.done(); + }); + }, + `The source of the request from ${cursorSourceType}::${ + createRequestFunctionName}() is the cursor itself`); +} + +// Verify the request source after calling openCursor() or openKeyCursor() and +// then using the cursor to iterate. +function open_cursor_request_source_test( + cursorSourceType, openCursorFunctionName) { + indexeddb_test( + (t, db) => initializeDatabase(db), + (t, db) => { + const tx = db.transaction('store', 'readonly'); + const cursorSource = getCursorSource(tx, cursorSourceType); + + // Open the cursor. + const openCursorRequest = cursorSource[openCursorFunctionName](); + openCursorRequest.onerror = + t.unreached_func('The cursor must not fail to open or iterate.'); + + assert_equals( + openCursorRequest.source, cursorSource, + 'The request source must be the opener of the cursor.'); + + // Verify the cursor's `request.source` after iterating with + // `advance()`, `continue()`, and `continuePrimaryKey()`. + let iterationCount = 0; + openCursorRequest.onsuccess = t.step_func(e => { + assert_equals( + openCursorRequest.source, cursorSource, + 'The request source must be the opener of the cursor after iterating.'); + + const cursor = e.target.result; + ++iterationCount; + + if (iterationCount == 1) { + cursor.advance(1); + } else if (iterationCount == 2) { + cursor.continue(); + } else if (iterationCount == 3 && isIndex(cursorSourceType)) { + cursor.continuePrimaryKey('z', 0); + } else { t.done(); - }); - }, - `The source of the request from ${func} is the cursor itself`)); + } + }); + }, + `${cursorSourceType}::${ + openCursorFunctionName}'s request source must be the ${ + cursorSourceType} instance that opened the cursor`); +} + +open_cursor_request_source_test('IDBObjectStore', 'openCursor'); +open_cursor_request_source_test('IDBObjectStore', 'openKeyCursor'); +open_cursor_request_source_test('IDBIndex', 'openCursor'); +open_cursor_request_source_test('IDBIndex', 'openKeyCursor'); + +cursor_request_source_test('IDBObjectStore', 'update', /*args=*/[0]); +cursor_request_source_test('IDBObjectStore', 'delete', /*args=*/[]); +cursor_request_source_test('IDBIndex', 'update', /*args=*/[0]); +cursor_request_source_test('IDBIndex', 'delete', /*args=*/[]); diff --git a/tests/wpt/tests/IndexedDB/key-conversion-exceptions.any.js b/tests/wpt/tests/IndexedDB/key-conversion-exceptions.any.js new file mode 100644 index 00000000000..08b99946c01 --- /dev/null +++ b/tests/wpt/tests/IndexedDB/key-conversion-exceptions.any.js @@ -0,0 +1,207 @@ +// META: global=window,worker +// META: title=IndexedDB: Exceptions thrown during key conversion +// META: script=resources/support.js +// META: timeout=long + +'use strict'; + +// Convenience function for tests that only need to run code in onupgradeneeded. +function indexeddb_upgrade_only_test(upgrade_callback, description) { + indexeddb_test(upgrade_callback, t => t.done(), description); +} + +// Key that throws during conversion. +function throwing_key(name) { + const throws = []; + throws.length = 1; + const err = new Error('throwing from getter'); + err.name = name; + Object.defineProperty(throws, '0', { + get: function() { + throw err; + }, + enumerable: true, + }); + return [throws, err]; +} + +const valid_key = []; +const invalid_key = {}; + +// Calls method on receiver with the specified number of args (default 1) +// and asserts that the method fails appropriately (rethrowing if +// conversion throws, or DataError if not a valid key), and that +// the first argument is fully processed before the second argument +// (if appropriate). +function check_method(receiver, method, args) { + args = args || 1; + if (args < 2) { + const [key, err] = throwing_key('getter'); + assert_throws_exactly(err, () => { + receiver[method](key); + }, 'key conversion with throwing getter should rethrow'); + + assert_throws_dom('DataError', () => { + receiver[method](invalid_key); + }, 'key conversion with invalid key should throw DataError'); + } else { + const [key1, err1] = throwing_key('getter 1'); + const [key2, err2] = throwing_key('getter 2'); + assert_throws_exactly(err1, () => { + receiver[method](key1, key2); + }, 'first key conversion with throwing getter should rethrow'); + + assert_throws_dom('DataError', () => { + receiver[method](invalid_key, key2); + }, 'first key conversion with invalid key should throw DataError'); + + assert_throws_exactly(err2, () => { + receiver[method](valid_key, key2); + }, 'second key conversion with throwing getter should rethrow'); + + assert_throws_dom('DataError', () => { + receiver[method](valid_key, invalid_key); + }, 'second key conversion with invalid key should throw DataError'); + } +} + +// Static key comparison utility on IDBFactory. +test( + t => check_method(indexedDB, 'cmp', 2), + 'IDBFactory cmp() static with throwing/invalid keys'); + +// Continue methods on IDBCursor. +indexeddb_upgrade_only_test((t, db) => { + const store = db.createObjectStore('store'); + store.put('a', 1).onerror = t.unreached_func('put should succeed'); + + const request = store.openCursor(); + request.onerror = t.unreached_func('openCursor should succeed'); + request.onsuccess = t.step_func(() => { + const cursor = request.result; + assert_not_equals(cursor, null, 'cursor should find a value'); + check_method(cursor, 'continue'); + }); +}, 'IDBCursor continue() method with throwing/invalid keys'); + +indexeddb_upgrade_only_test((t, db) => { + const store = db.createObjectStore('store'); + const index = store.createIndex('index', 'prop'); + store.put({prop: 'a'}, 1).onerror = t.unreached_func('put should succeed'); + + const request = index.openCursor(); + request.onerror = t.unreached_func('openCursor should succeed'); + request.onsuccess = t.step_func(() => { + const cursor = request.result; + assert_not_equals(cursor, null, 'cursor should find a value'); + + check_method(cursor, 'continuePrimaryKey', 2); + }); +}, null, 'IDBCursor continuePrimaryKey() method with throwing/invalid keys'); + +// Mutation methods on IDBCursor. +indexeddb_upgrade_only_test((t, db) => { + const store = db.createObjectStore('store', {keyPath: 'prop'}); + store.put({prop: 1}).onerror = t.unreached_func('put should succeed'); + + const request = store.openCursor(); + request.onerror = t.unreached_func('openCursor should succeed'); + request.onsuccess = t.step_func(() => { + const cursor = request.result; + assert_not_equals(cursor, null, 'cursor should find a value'); + + const value = {}; + let err; + [value.prop, err] = throwing_key('getter'); + assert_throws_exactly(err, () => { + cursor.update(value); + }, 'throwing getter should rethrow during clone'); + + // Throwing from the getter during key conversion is + // not possible since (1) a clone is used, (2) only own + // properties are cloned, and (3) only own properties + // are used for key path evaluation. + + value.prop = invalid_key; + assert_throws_dom('DataError', () => { + cursor.update(value); + }, 'key conversion with invalid key should throw DataError'); + }); +}, 'IDBCursor update() method with throwing/invalid keys'); + +// Static constructors on IDBKeyRange +['only', 'lowerBound', 'upperBound'].forEach((method) => { + test( + t => check_method(IDBKeyRange, method), + 'IDBKeyRange ' + method + '() static with throwing/invalid keys'); +}); + +test( + t => check_method(IDBKeyRange, 'bound', 2), + 'IDBKeyRange bound() static with throwing/invalid keys'); + +// Insertion methods on IDBObjectStore. +['add', 'put'].forEach((method) => { + indexeddb_upgrade_only_test((t, db) => { + const out_of_line = db.createObjectStore('out-of-line keys'); + const in_line = db.createObjectStore('in-line keys', {keyPath: 'prop'}); + let [key, err] = throwing_key('getter'); + assert_throws_exactly(err, () => { + out_of_line[method]('value', key); + }, 'key conversion with throwing getter should rethrow'); + + assert_throws_dom('DataError', () => { + out_of_line[method]('value', invalid_key); + }, 'key conversion with invalid key should throw DataError'); + + const value = {}; + [value.prop, err] = throwing_key('getter'); + assert_throws_exactly(err, () => { + in_line[method](value); + }, 'throwing getter should rethrow during clone'); + + // Throwing from the getter during key conversion is + // not possible since (1) a clone is used, (2) only own + // properties are cloned, and (3) only own properties + // are used for key path evaluation. + + value.prop = invalid_key; + assert_throws_dom('DataError', () => { + in_line[method](value); + }, 'key conversion with invalid key should throw DataError'); + }, `IDBObjectStore ${method}() method with throwing/invalid keys`); +}); + +// Generic (key-or-key-path) methods on IDBObjectStore. +['delete', + 'get', + 'getKey', + 'getAll', + 'getAllKeys', + 'count', + 'openCursor', + 'openKeyCursor', +].forEach(method => { + indexeddb_upgrade_only_test((t, db) => { + const store = db.createObjectStore('store'); + + check_method(store, method); + }, `IDBObjectStore ${method}() method with throwing/invalid keys`); +}); + +// Generic (key-or-key-path) methods on IDBIndex. +['get', + 'getKey', + 'getAll', + 'getAllKeys', + 'count', + 'openCursor', + 'openKeyCursor', +].forEach((method) => { + indexeddb_upgrade_only_test((t, db) => { + const store = db.createObjectStore('store'); + const index = store.createIndex('index', 'keyPath'); + + check_method(index, method); + }, `IDBIndex ${method}() method with throwing/invalid keys`); +}); diff --git a/tests/wpt/tests/IndexedDB/key-conversion-exceptions.htm b/tests/wpt/tests/IndexedDB/key-conversion-exceptions.htm deleted file mode 100644 index 9fdab58eb12..00000000000 --- a/tests/wpt/tests/IndexedDB/key-conversion-exceptions.htm +++ /dev/null @@ -1,199 +0,0 @@ - - -IndexedDB: Exceptions thrown during key conversion - - - - - diff --git a/tests/wpt/tests/IndexedDB/key_invalid.any.js b/tests/wpt/tests/IndexedDB/key_invalid.any.js new file mode 100644 index 00000000000..2d673ed5e91 --- /dev/null +++ b/tests/wpt/tests/IndexedDB/key_invalid.any.js @@ -0,0 +1,134 @@ +// META: global=window,worker +// META: title=Invalid key +// META: script=resources/support.js + +// Spec: https://w3c.github.io/IndexedDB/#key-construct + +'use strict'; + +const invalid_key = (desc, key) => { + async_test(t => { + const db = createdb_for_multiple_tests(); + let objStore = null; + let objStore2 = null; + + const is_cloneable = o => { + try { + self.postMessage(o, '*'); + return true; + } catch (ex) { + return false; + } + }; + + db.setTest(t).onupgradeneeded = t.step_func(e => { + objStore = objStore || e.target.result.createObjectStore('store'); + assert_throws_dom('DataError', () => { + objStore.add('value', key); + }); + + if (is_cloneable(key)) { + objStore2 = objStore2 || e.target.result.createObjectStore('store2', { + keyPath: ['x', 'keypath'], + }); + assert_throws_dom('DataError', () => { + objStore.add('value', key); + }); + } + t.done(); + }); + }, 'Invalid key - ' + desc); +}; + +const fake_array = { + length: 0, + constructor: Array, +}; + +const ArrayClone = function() {}; +ArrayClone.prototype = Array; +const ArrayClone_instance = new ArrayClone(); + +// booleans +invalid_key('true', true); +invalid_key('false', false); + +// null/NaN/undefined +invalid_key('null', null); +invalid_key('NaN', NaN); +invalid_key('undefined', undefined); +invalid_key('undefined2'); + +// functions +invalid_key('function() {}', function() {}); + +// objects +invalid_key('{}', {}); +invalid_key('{ obj: 1 }', {obj: 1}); +invalid_key('Math', Math); +invalid_key('self', self); +invalid_key('{length:0,constructor:Array}', fake_array); +invalid_key('Array clone’s instance', ArrayClone_instance); +invalid_key('Array (object)', Array); +invalid_key('String (object)', String); +invalid_key('new String()', new String()); +invalid_key('new Number()', new Number()); +invalid_key('new Boolean()', new Boolean()); + +// arrays +invalid_key('[{}]', [{}]); +invalid_key('[[], [], [], [[ Date ]]]', [[], [], [], [[Date]]]); +invalid_key('[undefined]', [undefined]); +invalid_key('[,1]', [, 1]); + +if (typeof document !== 'undefined') { + invalid_key( + 'document.getElementsByTagName("script")', + document.getElementsByTagName('script')); +} + +// dates +invalid_key('new Date(NaN)', new Date(NaN)); +invalid_key('new Date(Infinity)', new Date(Infinity)); + +// regexes +invalid_key('/foo/', /foo/); +invalid_key('new RegExp()', new RegExp()); + +const sparse = []; +sparse[10] = 'hei'; +invalid_key('sparse array', sparse); + +const sparse2 = []; +sparse2[0] = 1; +sparse2[''] = 2; +sparse2[2] = 3; +invalid_key('sparse array 2', sparse2); + +invalid_key('[[1], [3], [7], [[ sparse array ]]]', [ + [1], + [3], + [7], + [[sparse2]], +]); + +// sparse3 +invalid_key('[1,2,3,,]', [ + 1, + 2, + 3, + , +]); + +const recursive = []; +recursive.push(recursive); +invalid_key('array directly contains self', recursive); + +const recursive2 = []; +recursive2.push([recursive2]); +invalid_key('array indirectly contains self', recursive2); + +const recursive3 = [recursive]; +invalid_key('array member contains self', recursive3); + +invalid_key('proxy of an array', new Proxy([1, 2, 3], {})); diff --git a/tests/wpt/tests/IndexedDB/key_invalid.htm b/tests/wpt/tests/IndexedDB/key_invalid.htm deleted file mode 100644 index cf649b07d05..00000000000 --- a/tests/wpt/tests/IndexedDB/key_invalid.htm +++ /dev/null @@ -1,131 +0,0 @@ - - - -Invalid key - - - - - - - - - - - -
diff --git a/tests/wpt/tests/IndexedDB/key_valid.any.js b/tests/wpt/tests/IndexedDB/key_valid.any.js new file mode 100644 index 00000000000..8c015d331ba --- /dev/null +++ b/tests/wpt/tests/IndexedDB/key_valid.any.js @@ -0,0 +1,68 @@ +// META: global=window,worker +// META: title=Valid key +// META: script=resources/support.js + +// Spec: https://w3c.github.io/IndexedDB/#key-construct + +'use strict'; + +const valid_key = (desc, key) => { + async_test(t => { + let db; + const open_rq = createdb(t); + open_rq.onupgradeneeded = t.step_func(e => { + db = e.target.result; + const store = db.createObjectStore('store'); + assert_true(store.add('value', key) instanceof IDBRequest); + + const store2 = db.createObjectStore('store2', { + keyPath: ['x', 'keypath'], + }); + assert_true(store2.add({x: 'v', keypath: key}) instanceof IDBRequest); + }); + + open_rq.onsuccess = t.step_func(e => { + const rq = + db.transaction('store', 'readonly').objectStore('store').get(key); + rq.onsuccess = t.step_func(e => { + assert_equals(e.target.result, 'value'); + const rq2 = + db.transaction('store2', 'readonly').objectStore('store2').get([ + 'v', key + ]); + rq2.onsuccess = t.step_func(e => { + assert_equals(e.target.result.x, 'v'); + assert_key_equals(e.target.result.keypath, key); + t.done(); + }); + }); + }); + }, 'Valid key - ' + desc); +}; + +// Date +valid_key('new Date()', new Date()); +valid_key('new Date(0)', new Date(0)); + +// Array +valid_key('[]', []); +valid_key('new Array()', new Array()); + +valid_key('["undefined"]', ['undefined']); + +// Float +valid_key('Infinity', Infinity); +valid_key('-Infinity', -Infinity); +valid_key('0', 0); +valid_key('1.5', 1.5); +valid_key('3e38', 3e38); +valid_key('3e-38', 3e38); + +// String +valid_key('"foo"', 'foo'); +valid_key('"\\n"', '\n'); +valid_key('""', ''); +valid_key('"\\""', '"'); +valid_key('"\\u1234"', '\u1234'); +valid_key('"\\u0000"', '\u0000'); +valid_key('"NaN"', 'NaN'); diff --git a/tests/wpt/tests/IndexedDB/key_valid.html b/tests/wpt/tests/IndexedDB/key_valid.html deleted file mode 100644 index 0cca54cdbb3..00000000000 --- a/tests/wpt/tests/IndexedDB/key_valid.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - -Valid key - - - - - - - - - -
diff --git a/tests/wpt/tests/IndexedDB/keyorder.any.js b/tests/wpt/tests/IndexedDB/keyorder.any.js new file mode 100644 index 00000000000..fe3eb11fa92 --- /dev/null +++ b/tests/wpt/tests/IndexedDB/keyorder.any.js @@ -0,0 +1,169 @@ +// META: global=window,worker +// META: title=Key sort order +// META: script=resources/support.js + +// Spec: https://w3c.github.io/IndexedDB/#key-construct + +'use strict'; + +const global_db = createdb_for_multiple_tests(); + +const keysort = (desc, unsorted, expected) => { + async_test(t => { + const store_name = 'store-' + Date.now() + Math.random(); + + // The database test + const open_rq = global_db.setTest(t); + open_rq.onupgradeneeded = t.step_func(e => { + const db = e.target.result; + const objStore = db.createObjectStore(store_name); + + for (let i = 0; i < unsorted.length; i++) + objStore.add('value', unsorted[i]); + }); + + open_rq.onsuccess = t.step_func(e => { + const db = e.target.result; + const actual_keys = []; + const rq = + db.transaction(store_name).objectStore(store_name).openCursor(); + + rq.onsuccess = t.step_func(e => { + const cursor = e.target.result; + + if (cursor) { + actual_keys.push(cursor.key); + cursor.continue(); + } else { + assert_key_equals(actual_keys, expected, 'keyorder array'); + assert_equals(actual_keys.length, expected.length, 'array length'); + + t.done(); + } + }); + }); + }, `Database readback sort - ${desc}`); + + // The IDBKey.cmp test + test(() => { + const sorted = unsorted.slice(0).sort((a, b) => indexedDB.cmp(a, b)); + assert_key_equals(sorted, expected, 'sorted array'); + }, `IDBKey.cmp sort - ${desc}`); +}; + +const now = new Date(); +const one_sec_ago = new Date(now - 1000); +const one_min_future = new Date(now.getTime() + 1000 * 60); + +keysort('String < Array', [[0], 'yo', '', []], ['', 'yo', [], [0]]); + +keysort( + 'float < String', [Infinity, 'yo', 0, '', 100], + [0, 100, Infinity, '', 'yo']); + +keysort( + 'float < Date', [now, 0, 9999999999999, -0.22], + [-0.22, 0, 9999999999999, now]); + +keysort( + 'float < Date < String < Array', [[], '', now, [0], '-1', 0, 9999999999999], + [0, 9999999999999, now, '', '-1', [], [0]]); + +keysort( + 'Date(1 sec ago) < Date(now) < Date(1 minute in future)', + [now, one_sec_ago, one_min_future], [one_sec_ago, now, one_min_future]); + +keysort( + '-1.1 < 1 < 1.01337 < 1.013373 < 2', [1.013373, 2, 1.01337, -1.1, 1], + [-1.1, 1, 1.01337, 1.013373, 2]); + +keysort( + '-Infinity < -0.01 < 0 < Infinity', [0, -0.01, -Infinity, Infinity], + [-Infinity, -0.01, 0, Infinity]); + +keysort( + '"" < "a" < "ab" < "b" < "ba"', ['a', 'ba', '', 'b', 'ab'], + ['', 'a', 'ab', 'b', 'ba']); + +keysort( + 'Arrays', [[[0]], [0], [], [0, 0], [0, [0]]], + [[], [0], [0, 0], [0, [0]], [[0]]]); + +const big_array = []; +const bigger_array = []; +for (let i = 0; i < 10000; i++) { + big_array.push(i); + bigger_array.push(i); +} +bigger_array.push(0); + +keysort( + 'Array.length: 10,000 < Array.length: 10,001', + [bigger_array, [0, 2, 3], [0], [9], big_array], + [[0], big_array, bigger_array, [0, 2, 3], [9]]); + +keysort( + 'Infinity inside arrays', + [ + [Infinity, 1], + [Infinity, Infinity], + [1, 1], + [1, Infinity], + [1, -Infinity], + [-Infinity, Infinity], + ], + [ + [-Infinity, Infinity], + [1, -Infinity], + [1, 1], + [1, Infinity], + [Infinity, 1], + [Infinity, Infinity], + ]); + +keysort( + 'Test different stuff at once', + [ + now, + [0, []], + 'test', + 1, + ['a', [1, [-1]]], + ['b', 'a'], + [0, 2, 'c'], + ['a', [1, 2]], + [], + [0, [], 3], + ['a', 'b'], + [1, 2], + ['a', 'b', 'c'], + one_sec_ago, + [0, 'b', 'c'], + Infinity, + -Infinity, + 2.55, + [0, now], + [1], + ], + [ + -Infinity, + 1, + 2.55, + Infinity, + one_sec_ago, + now, + 'test', + [], + [0, 2, 'c'], + [0, now], + [0, 'b', 'c'], + [0, []], + [0, [], 3], + [1], + [1, 2], + ['a', 'b'], + ['a', 'b', 'c'], + ['a', [1, 2]], + ['a', [1, [-1]]], + ['b', 'a'], + ]); diff --git a/tests/wpt/tests/IndexedDB/keyorder.htm b/tests/wpt/tests/IndexedDB/keyorder.htm deleted file mode 100644 index 7e8b3d41264..00000000000 --- a/tests/wpt/tests/IndexedDB/keyorder.htm +++ /dev/null @@ -1,175 +0,0 @@ - - - -Key sort order - - - - - - - - -
diff --git a/tests/wpt/tests/IndexedDB/worker-termination-aborts-upgrade.window.js b/tests/wpt/tests/IndexedDB/worker-termination-aborts-upgrade.window.js index e84ca2c2a64..c2d8d91787e 100644 --- a/tests/wpt/tests/IndexedDB/worker-termination-aborts-upgrade.window.js +++ b/tests/wpt/tests/IndexedDB/worker-termination-aborts-upgrade.window.js @@ -50,7 +50,7 @@ promise_test(async t => { // At this point we know that the open request was issued on the worker // worker thread. An ordering concern at this point is that IDB only - // specifies that the the connection opening algorithm is run in parallel and + // specifies that the connection opening algorithm is run in parallel and // we are not guaranteed that when we go "in parallel" here that our operation // won't run first. As such, it may be necessary to add some kind of // arbitrary delay in the future if implementations do not effectively diff --git a/tests/wpt/tests/WebCryptoAPI/idlharness.https.any.js b/tests/wpt/tests/WebCryptoAPI/idlharness.https.any.js index ae65eb49f21..5ddf7eab6db 100644 --- a/tests/wpt/tests/WebCryptoAPI/idlharness.https.any.js +++ b/tests/wpt/tests/WebCryptoAPI/idlharness.https.any.js @@ -5,7 +5,7 @@ // https://w3c.github.io/webcrypto/Overview.html idl_test( - ['WebCryptoAPI'], + ['webcrypto'], ['html', 'dom'], idl_array => { idl_array.add_objects({ diff --git a/tests/wpt/tests/WebCryptoAPI/import_export/importKey_failures.js b/tests/wpt/tests/WebCryptoAPI/import_export/importKey_failures.js index 453461a8771..39c70a85470 100644 --- a/tests/wpt/tests/WebCryptoAPI/import_export/importKey_failures.js +++ b/tests/wpt/tests/WebCryptoAPI/import_export/importKey_failures.js @@ -213,7 +213,7 @@ function run_test(algorithmNames) { allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { getValidKeyData(algorithm).forEach(function(test) { if (test.format === "jwk") { - var data = {crv: test.data.crv, kty: test.data.kty, d: test.data.d, x: test.data.x, d: test.data.d}; + var data = {crv: test.data.crv, kty: test.data.kty, d: test.data.d, x: test.data.x, y: test.data.y}; data.kty = getMismatchedKtyField(algorithm); var usages = validUsages(vector, 'jwk', test.data); testError('jwk', algorithm, data, name, usages, true, "DataError", "Invalid 'kty' field"); @@ -228,7 +228,7 @@ function run_test(algorithmNames) { allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { getValidKeyData(algorithm).forEach(function(test) { if (test.format === "jwk") { - var data = {crv: test.data.crv, kty: test.data.kty, d: test.data.d, x: test.data.x, d: test.data.d}; + var data = {crv: test.data.crv, kty: test.data.kty, d: test.data.d, x: test.data.x, y: test.data.y}; data.ext = false; var usages = validUsages(vector, 'jwk', test.data); testError('jwk', algorithm, data, name, usages, true, "DataError", "Import from a non-extractable"); @@ -259,7 +259,7 @@ function run_test(algorithmNames) { allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { getValidKeyData(algorithm).forEach(function(test) { if (test.format === "jwk") { - var data = {crv: test.data.crv, kty: test.data.kty, d: test.data.d, x: test.data.x, d: test.data.d}; + var data = {crv: test.data.crv, kty: test.data.kty, d: test.data.d, x: test.data.x, y: test.data.y}; data.crv = getMismatchedCrvField(algorithm) var usages = validUsages(vector, 'jwk', test.data); testError('jwk', algorithm, data, name, usages, true, "DataError", "Invalid 'crv' field"); @@ -267,4 +267,23 @@ function run_test(algorithmNames) { }); }); }); + + // Use an 'alg' field with incorrect casing. + testVectors.forEach(function(vector) { + var name = vector.name; + if (name !== "Ed25519" && name !== "Ed448") + return; // The rest ignore the 'alg' field. + allAlgorithmSpecifiersFor(name).forEach(function(algorithm) { + getValidKeyData(algorithm).forEach(function(test) { + if (test.format === "jwk") { + var data = {crv: test.data.crv, kty: test.data.kty, d: test.data.d, x: test.data.x, y: test.data.y}; + var usages = validUsages(vector, 'jwk', test.data); + [name.toLowerCase(), name.toUpperCase()].forEach(function(algName) { + data.alg = algName; + testError('jwk', algorithm, data, name, usages, true, "DataError", "Invalid 'alg' field '" + data.alg + "'"); + }); + } + }); + }); + }); } diff --git a/tests/wpt/tests/accelerometer/Accelerometer-iframe-access.https.html b/tests/wpt/tests/accelerometer/Accelerometer-iframe-access.https.html index 56005696a7f..887c6139061 100644 --- a/tests/wpt/tests/accelerometer/Accelerometer-iframe-access.https.html +++ b/tests/wpt/tests/accelerometer/Accelerometer-iframe-access.https.html @@ -6,7 +6,7 @@ - + diff --git a/tests/wpt/tests/accelerometer/Accelerometer.https.html b/tests/wpt/tests/accelerometer/Accelerometer.https.html index d422fef7264..6d6dae5c619 100644 --- a/tests/wpt/tests/accelerometer/Accelerometer.https.html +++ b/tests/wpt/tests/accelerometer/Accelerometer.https.html @@ -6,7 +6,7 @@ - + diff --git a/tests/wpt/tests/accelerometer/GravitySensor.https.html b/tests/wpt/tests/accelerometer/GravitySensor.https.html index 0f98f3e00de..c632f0ffe31 100644 --- a/tests/wpt/tests/accelerometer/GravitySensor.https.html +++ b/tests/wpt/tests/accelerometer/GravitySensor.https.html @@ -6,7 +6,7 @@ - + diff --git a/tests/wpt/tests/accelerometer/LinearAccelerationSensor.https.html b/tests/wpt/tests/accelerometer/LinearAccelerationSensor.https.html index 91035cc9628..c8883b4c8a1 100644 --- a/tests/wpt/tests/accelerometer/LinearAccelerationSensor.https.html +++ b/tests/wpt/tests/accelerometer/LinearAccelerationSensor.https.html @@ -6,7 +6,7 @@ - + diff --git a/tests/wpt/tests/accname/name/comp_labeledby_non_standard.html b/tests/wpt/tests/accname/name/comp_labeledby_non_standard.html new file mode 100644 index 00000000000..f86a62b34ef --- /dev/null +++ b/tests/wpt/tests/accname/name/comp_labeledby_non_standard.html @@ -0,0 +1,37 @@ + + + + Name Comp: aria-labeledby (non-standard) + + + + + + + + + +

Tests that the non-standard (misspelled) attribute name aria-labeledby is not supported.

+ +

Tests for aria-labeledby

+ +
+

first heading

+

text inside div group

+
+ +
+

+ first heading

+

text inside div group

+
+ +
+

+ first heading

+

text inside div group

+
+ + + + diff --git a/tests/wpt/tests/accname/name/comp_name_from_content.html b/tests/wpt/tests/accname/name/comp_name_from_content.html index 6626eecb0dd..cd108a190e3 100644 --- a/tests/wpt/tests/accname/name/comp_name_from_content.html +++ b/tests/wpt/tests/accname/name/comp_name_from_content.html @@ -24,6 +24,10 @@ */ + .alt-counter::before { + counter-set: cnt 5051; + content: "" / counter(cnt); + } .simple-before::before { content: " before "; /* [sic] leading and trailing space */ margin:0 0.1em; @@ -103,6 +107,12 @@ label

+

alt counter with ::before

+
+

label

+label
+
+

simple with ::before


label

diff --git a/tests/wpt/tests/accname/name/comp_name_from_content_alt_counter_invalidation.html b/tests/wpt/tests/accname/name/comp_name_from_content_alt_counter_invalidation.html new file mode 100644 index 00000000000..d659a6e7b3c --- /dev/null +++ b/tests/wpt/tests/accname/name/comp_name_from_content_alt_counter_invalidation.html @@ -0,0 +1,38 @@ + +Name Comp: Name From Alt Counter in Content Invalidation + + + + + + + + + +

Tests the #comp_name_from_content portions of the AccName Name Computation algorithm.

+ +

This series of tests exercises the button, heading, and link elements, because each have different characteristics worthy of testing in each of the name computation algorithm scenarios:

+
    +
  • button is a leaf node with sub-level elements presentational.
  • +
  • heading is block level, and can contain sub-level interactives like links.
  • +
  • link (a[href]) is an interactive inline element that can include non-presentational descendants.
  • +
+ +

alt counter with ::before

+
+

label

+label
+
+ + diff --git a/tests/wpt/tests/ai/language_detection/availability-detached-crash.https.html b/tests/wpt/tests/ai/language_detection/availability-detached-crash.https.html new file mode 100644 index 00000000000..ee5e27e35b6 --- /dev/null +++ b/tests/wpt/tests/ai/language_detection/availability-detached-crash.https.html @@ -0,0 +1,20 @@ + +Assures no crash upon call of LanguageDetector.availability() on a detached document + + + + + + + + diff --git a/tests/wpt/tests/ai/language_detection/availability-detached-crash.tentative.https.html b/tests/wpt/tests/ai/language_detection/availability-detached-crash.tentative.https.html deleted file mode 100644 index 5f76d59eb35..00000000000 --- a/tests/wpt/tests/ai/language_detection/availability-detached-crash.tentative.https.html +++ /dev/null @@ -1,13 +0,0 @@ - -Assures no crash upon call of LanguageDetector.availability() on a detached document - - - - - - diff --git a/tests/wpt/tests/ai/language_detection/detector-iframe.https.html b/tests/wpt/tests/ai/language_detection/detector-iframe.https.html new file mode 100644 index 00000000000..9dc39d44a00 --- /dev/null +++ b/tests/wpt/tests/ai/language_detection/detector-iframe.https.html @@ -0,0 +1,104 @@ + + + + + + + + + \ No newline at end of file diff --git a/tests/wpt/tests/ai/language_detection/detector-locale.https.window.js b/tests/wpt/tests/ai/language_detection/detector-locale.https.window.js new file mode 100644 index 00000000000..80cbfa485e3 --- /dev/null +++ b/tests/wpt/tests/ai/language_detection/detector-locale.https.window.js @@ -0,0 +1,113 @@ +// META: title=Detect english +// META: global=window +// META: timeout=long +// META: script=resources/util.js +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: script=../resources/locale-util.js + +'use strict'; + +function getAvailability(expectedInputLanguages) { + return LanguageDetector.availability({expectedInputLanguages}); +} + +function assert_availability_consistent( + language_subtag_availability, base_availability) { + if (base_availability == 'unavailable') { + // If the language subtag is not available then no variation of it should + // be available. + assert_equals(language_subtag_availability, 'unavailable'); + } else { + // If the language subtag is available, then it definitely shouldn't be + // unavailable since whatever backing it has could support any variation of + // it. A variation could have a different availability if a more specific + // backing is required. + assert_in_array( + language_subtag_availability, + ['downloadable', 'downloading', 'available']); + } +} + +promise_test(async t => { + for (const [languageSubtag, variations] of Object.entries( + valid_language_tags)) { + const languageSubtagAvailability = await getAvailability([languageSubtag]); + + // Test each variation individually. + for (const variation of variations) { + assert_availability_consistent( + await getAvailability([variation]), languageSubtagAvailability); + } + + // Test all variations. + assert_availability_consistent( + await getAvailability(variations), languageSubtagAvailability); + } +}, 'LanguageDetector.availability() is consistent between language tag variations'); + + +async function getExpectedInputLanguages(expectedInputLanguages) { + return (await createLanguageDetector({expectedInputLanguages})) + .expectedInputLanguages; +} + +async function assert_valid_expected_input_languages(language) { + const expectedInputLanguages = await getExpectedInputLanguages([language]); + assert_equals(expectedInputLanguages.length, 1); + assert_is_variation(language, expectedInputLanguages[0]); + assert_is_canonical(expectedInputLanguages[0]); +} + +function uniqueCount(array) { + return (new Set(array)).size; +} + +promise_test(async t => { + for (const [languageSubtag, variations] of Object.entries( + valid_language_tags)) { + if (await getAvailability([languageSubtag]) === 'unavailable') { + continue; + } + + await assert_valid_expected_input_languages(languageSubtag); + + for (const variation of variations) { + await assert_valid_expected_input_languages(variation); + } + + const expectedInputLanguages = await getExpectedInputLanguages(variations); + + // There should be no duplicates. + assert_equals( + expectedInputLanguages.length, uniqueCount(expectedInputLanguages)); + + for (const language of expectedInputLanguages) { + assert_is_canonical(language); + assert_is_variation(language, languageSubtag); + } + } +}, 'LanguageDetector has valid expectedInputLanguages'); + +function assert_rejects_invalid_expected_input_languages( + t, method, expectedInputLanguages) { + return promise_rejects_js(t, RangeError, method({expectedInputLanguages})); +} + +promise_test(async t => { + for (const languageTag of invalid_language_tags) { + assert_rejects_invalid_expected_input_languages( + t, createLanguageDetector, [languageTag]); + } + assert_rejects_invalid_expected_input_languages( + t, createLanguageDetector, invalid_language_tags); +}, 'LanguageDetector.create() throws RangeError for invalid language tags'); + +promise_test(async t => { + for (const languageTag of invalid_language_tags) { + assert_rejects_invalid_expected_input_languages( + t, LanguageDetector.availability, [languageTag]); + } + assert_rejects_invalid_expected_input_languages( + t, LanguageDetector.availability, invalid_language_tags); +}, 'LanguageDetector.availability() throws RangeError for invalid language tags'); diff --git a/tests/wpt/tests/ai/language_detection/detector.https.tentative.any.js b/tests/wpt/tests/ai/language_detection/detector.https.tentative.any.js deleted file mode 100644 index 8c02df18cbe..00000000000 --- a/tests/wpt/tests/ai/language_detection/detector.https.tentative.any.js +++ /dev/null @@ -1,100 +0,0 @@ -// META: title=Detect english -// META: global=window,worker -// META: script=../resources/util.js - -'use strict'; - -promise_test(async t => { - // Language detection is available after call to `create()`. - await LanguageDetector.create(); - const availability = await LanguageDetector.availability(); - assert_equals(availability, 'available'); -}, 'Simple LanguageDetector.availability() call'); - -promise_test(async t => { - const detector = await LanguageDetector.create(); - const results = await detector.detect('this string is in English'); - // "en" should be highest confidence. - assert_equals(results[0].detectedLanguage, 'en'); - // Results should be from high to low confidence. - for (let i = 0; i < results.length - 1; i++) { - assert_greater_than_equal(results[i].confidence, results[i + 1].confidence); - } -}, 'Simple LanguageDetector.detect() call'); - -promise_test(async t => { - const controller = new AbortController(); - controller.abort(); - - const createPromise = LanguageDetector.create({signal: controller.signal}); - - await promise_rejects_dom(t, 'AbortError', createPromise); -}, 'LanguageDetector.create() call with an aborted signal.'); - -promise_test(async t => { - await testAbortPromise(t, signal => { - return LanguageDetector.create({signal}); - }); -}, 'Aborting LanguageDetector.create().'); - -promise_test(async t => { - const controller = new AbortController(); - controller.abort(); - - const detector = await LanguageDetector.create(); - const detectPromise = - detector.detect('this string is in English', {signal: controller.signal}); - - await promise_rejects_dom(t, 'AbortError', detectPromise); -}, 'LanguageDetector.detect() call with an aborted signal.'); - -promise_test(async t => { - const detector = await LanguageDetector.create(); - await testAbortPromise(t, signal => { - return detector.detect('this string is in English', {signal}); - }); -}, 'Aborting LanguageDetector.detect().'); - -promise_test(async t => { - const detector = await LanguageDetector.create(); - - const text = 'this string is in English'; - const inputUsage = await detector.measureInputUsage(text); - - assert_greater_than_equal(detector.inputQuota, 0); - assert_greater_than_equal(inputUsage, 0); - - const detectPromise = detector.detect(text); - - if (inputUsage < detector.inputQuota) { - assert_equals((await detectPromise)[0].detectedLanguage, 'en'); - } else { - await promise_rejects_dom(t, 'QuotaExceededError', detectPromise); - } -}, 'LanguageDetector.measureInputUsage() and inputQuota basic usage.'); - -promise_test(async t => { - const controller = new AbortController(); - controller.abort(); - - const detector = await LanguageDetector.create(); - const measureInputUsagePromise = - detector.measureInputUsage('hello', {signal: controller.signal}); - - await promise_rejects_dom(t, 'AbortError', measureInputUsagePromise); -}, 'Translator.measureInputUsage() call with an aborted signal.'); - -promise_test(async t => { - const detector = await LanguageDetector.create(); - await testAbortPromise(t, signal => { - return detector.measureInputUsage('hello', {signal}); - }); -}, 'Aborting Translator.measureInputUsage().'); - -promise_test(async () => { - const expected_languages = ['en', 'es']; - const detector = await languageDetector.create({ - expectedInputLanguages: expected_languages - }); - assert_array_equals(detector.expectedInputLanguages(), expected_languages); -}, 'Creating LanguageDetector with expectedInputLanguages'); diff --git a/tests/wpt/tests/ai/language_detection/detector.https.window.js b/tests/wpt/tests/ai/language_detection/detector.https.window.js new file mode 100644 index 00000000000..379b8741534 --- /dev/null +++ b/tests/wpt/tests/ai/language_detection/detector.https.window.js @@ -0,0 +1,199 @@ +// META: title=Detect english +// META: global=window +// META: timeout=long +// META: script=resources/util.js +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: script=../resources/locale-util.js + +'use strict'; + +promise_test(async t => { + // Creating the language detector without user activation rejects with + // NotAllowedError. + const createPromise = LanguageDetector.create(); + await promise_rejects_dom(t, 'NotAllowedError', createPromise); + + // Creating the translator with user activation succeeds. + await createLanguageDetector(); + + // Creating it should have switched it to available. + const availability = await LanguageDetector.availability(); + assert_equals(availability, 'available'); + + // Now that it is available, we should no longer need user activation. + await LanguageDetector.create(); +}, 'LanguageDetector.create() requires user activation when availability is "downloadable.'); + +promise_test(async t => { + const detector = await createLanguageDetector(); + const results = await detector.detect('Hello world!'); + + // must at least have the 'und' result. + assert_greater_than_equal(results.length, 1); + + // The last result should be 'und'. + const undResult = results.pop(); + assert_equals(undResult.detectedLanguage, 'und'); + assert_greater_than(undResult.confidence, 0); + + let total_confidence_without_und = 0; + let last_confidence = 1; + for (const {detectedLanguage, confidence} of results) { + // All results must be in canonical form. + assert_is_canonical(detectedLanguage); + + assert_greater_than(confidence, 0); + assert_greater_than(confidence, undResult.confidence); + + total_confidence_without_und += confidence; + + // Except for 'und', results must be from high to low confidence. + assert_greater_than_equal(last_confidence, confidence); + last_confidence = confidence; + } + + // If we have non-und results, their confidences, excluding the last non-'und' + // result, must be less than 0.99. + if (results.length > 0) { + assert_less_than( + total_confidence_without_und - results.at(-1).confidence, 0.99); + } + + // Confidences, including 'und', should be less than or equal to one. + assert_less_than_equal( + total_confidence_without_und + undResult.confidence, 1); +}, 'LanguageDetector.detect() returns valid results'); + +promise_test(async t => { + const error = new Error('CreateMonitorCallback threw an error'); + function monitor(m) { + m.addEventListener('downloadprogress', e => { + assert_unreached( + 'This should never be reached since monitor throws an error.'); + }); + throw error; + } + + await promise_rejects_exactly(t, error, createLanguageDetector({monitor})); +}, 'If monitor throws an error, LanguageDetector.create() rejects with that error'); + +promise_test(async t => { + testMonitor(createLanguageDetector); +}, 'LanguageDetector.create() notifies its monitor on downloadprogress'); + +promise_test(async t => { + const controller = new AbortController(); + controller.abort(); + + const createPromise = createLanguageDetector({signal: controller.signal}); + + await promise_rejects_dom(t, 'AbortError', createPromise); +}, 'LanguageDetector.create() call with an aborted signal.'); + +promise_test(async t => { + await testAbortPromise(t, signal => { + return createLanguageDetector({signal}); + }); +}, 'Aborting createLanguageDetector().'); + +promise_test(async t => { + const detector = await createLanguageDetector(); + + const text = 'Hello world!'; + const promises = [detector.detect(text), detector.measureInputUsage(text)]; + + detector.destroy(); + + promises.push(detector.detect(text), detector.measureInputUsage(text)); + + for (const promise of promises) { + await promise_rejects_dom(t, 'AbortError', promise); + } +}, 'Calling LanguageDetector.destroy() aborts calls to detect and measureInputUsage.'); + +promise_test(async t => { + const controller = new AbortController(); + const detector = await createLanguageDetector({signal: controller.signal}); + + const text = 'Hello world!'; + const promises = [detector.detect(text), detector.measureInputUsage(text)]; + + const error = new Error('The create abort signal was aborted.'); + controller.abort(error); + + promises.push(detector.detect(text), detector.measureInputUsage(text)); + + for (const promise of promises) { + await promise_rejects_exactly(t, error, promise); + } +}, 'LanguageDetector.create()\'s abort signal destroys its LanguageDetector after creation.'); + +promise_test(async t => { + const controller = new AbortController(); + controller.abort(); + + const detector = await createLanguageDetector(); + const detectPromise = + detector.detect('Hello world!', {signal: controller.signal}); + + await promise_rejects_dom(t, 'AbortError', detectPromise); +}, 'LanguageDetector.detect() call with an aborted signal.'); + +promise_test(async t => { + const detector = await createLanguageDetector(); + await testAbortPromise(t, signal => { + return detector.detect('Hello world!', {signal}); + }); +}, 'Aborting LanguageDetector.detect().'); + +promise_test(async t => { + const detector = await createLanguageDetector(); + + const text = 'Hello world!'; + const largeText = text.repeat(10000); + const inputUsage = await detector.measureInputUsage(largeText); + + assert_greater_than_equal(detector.inputQuota, 0); + assert_greater_than_equal(inputUsage, 0); + + const detectPromise = detector.detect(text); + + if (inputUsage >= detector.inputQuota) { + await promise_rejects_dom(t, 'QuotaExceededError', detectPromise); + } else { + await detectPromise; + } +}, 'LanguageDetector.measureInputUsage() and inputQuota basic usage.'); + +promise_test(async t => { + const controller = new AbortController(); + controller.abort(); + + const detector = await createLanguageDetector(); + const measureInputUsagePromise = + detector.measureInputUsage('hello', {signal: controller.signal}); + + await promise_rejects_dom(t, 'AbortError', measureInputUsagePromise); +}, 'LanguageDetector.measureInputUsage() call with an aborted signal.'); + +promise_test(async t => { + const detector = await createLanguageDetector(); + await testAbortPromise(t, signal => { + return detector.measureInputUsage('hello', {signal}); + }); +}, 'Aborting LanguageDetector.measureInputUsage().'); + +promise_test(async () => { + const detector = await createLanguageDetector({expectedInputLanguages: []}); + assert_equals(detector.expectedInputLanguages, null); +}, 'Creating LanguageDetector with empty expectedInputLanguages array'); + +promise_test(async () => { + const detector = await createLanguageDetector(); + assert_equals(detector.expectedInputLanguages, null); +}, 'Creating LanguageDetector without expectedInputLanguages'); + +promise_test(async t => { + await testCreateMonitorWithAbort(t, createLanguageDetector); +}, 'Progress events are not emitted after aborted.'); diff --git a/tests/wpt/tests/ai/language_detection/detector.optional.https.window.js b/tests/wpt/tests/ai/language_detection/detector.optional.https.window.js new file mode 100644 index 00000000000..ee8765adf85 --- /dev/null +++ b/tests/wpt/tests/ai/language_detection/detector.optional.https.window.js @@ -0,0 +1,66 @@ +// META: title=Detect english +// META: global=window +// META: timeout=long +// META: script=resources/util.js +// META: script=/resources/testdriver.js +// META: script=../resources/util.js + +'use strict'; + +async function assert_detects_correct_language( + detector, input, expectedLanguage) { + const results = await detector.detect(input); + // The highest confidence language should be + assert_equals(results[0].detectedLanguage, expectedLanguage); +} + +promise_test(async t => { + const testInput = { + af: 'Dit is \'n voorbeeldsin.', + el: 'Αυτή είναι μια παÏαδειγματική Ï€Ïόταση.', + 'el-Latn': 'Aete einai mia paratheiymatike protase.', + en: 'This is an example sentence.', + es: 'Esta es una oración de ejemplo.', + fr: 'Ceci est un exemple de phrase.', + hi: 'यह à¤à¤• उदाहरण वाकà¥à¤¯ है.', + 'hi-Latn': 'yh ek udaahrn vaaky hai.', + it: 'Questa è una frase di esempio.', + ja: 'ã“れã¯ä¾‹æ–‡ã§ã™ã€‚', + 'ja-Latn': 'Kore wa reibundesu.', + ko: 'ì´ê²ƒì€ 예문입니다.', + mi: 'He tauira rerenga korero tenei.', + nl: 'Dit is een voorbeeldzin.', + ru: 'Это пример предложениÑ.', + sr: 'Ово је пример реченице.', + tr: 'Bu bir örnek cümledir.', + zh: '这是一个例å¥ã€‚', + zu: 'Lona umusho oyisibonelo.', + } + + const expectedInputLanguages = Object.keys(testInput); + + const detector = await createLanguageDetector({expectedInputLanguages}); + + for (const [language, input] of Object.entries(testInput)) { + await assert_detects_correct_language(detector, input, language); + } +}, 'LanguageDetector.detect() detects the correct language'); + +promise_test(async () => { + const expectedInputLanguages = ['en', 'es']; + const detector = await createLanguageDetector({expectedInputLanguages}); + assert_array_equals(detector.expectedInputLanguages, expectedInputLanguages); + assert_true(Object.isFrozen(detector.expectedInputLanguages)); +}, 'Creating LanguageDetector with expectedInputLanguages'); + + +promise_test(async () => { + const detector = await createLanguageDetector(); + + const results = await detector.detect(''); + assert_equals(results.length, 1); + + const [result] = results; + assert_equals(result.detectedLanguage, 'und'); + assert_equals(result.confidence, 1); +}, 'LanguageDetector.detect() detects empty string'); diff --git a/tests/wpt/tests/ai/language_detection/resources/iframe-helper.html b/tests/wpt/tests/ai/language_detection/resources/iframe-helper.html new file mode 100644 index 00000000000..35ba8525587 --- /dev/null +++ b/tests/wpt/tests/ai/language_detection/resources/iframe-helper.html @@ -0,0 +1,28 @@ + + + + + + diff --git a/tests/wpt/tests/ai/language_detection/resources/util.js b/tests/wpt/tests/ai/language_detection/resources/util.js new file mode 100644 index 00000000000..7cb3e7c2e13 --- /dev/null +++ b/tests/wpt/tests/ai/language_detection/resources/util.js @@ -0,0 +1,4 @@ +async function createLanguageDetector(options = {}) { + await test_driver.bless(); + return await LanguageDetector.create(options); +} diff --git a/tests/wpt/tests/ai/resources/locale-util.js b/tests/wpt/tests/ai/resources/locale-util.js new file mode 100644 index 00000000000..cacb11f4f25 --- /dev/null +++ b/tests/wpt/tests/ai/resources/locale-util.js @@ -0,0 +1,44 @@ +const valid_language_tags = { + en: [ + 'en-Latn', + 'en-Latn-GB', + 'en-GB', + 'en-fonipa-scouse', + 'en-Latn-fonipa-scouse', + 'en-Latn-GB-fonipa-scouse', + 'en-Latn-x-this-is-a-private-use-extensio-n', + 'EN', + 'en-lATN', + 'EN-lATN-gb', + 'EN-gb', + 'EN-scouse-fonipa', + 'EN-lATN-scouse-fonipa', + 'EN-lATN-gb-scouse-fonipa', + ], + es: [ + 'es-419', + 'es-ES', + 'es-ES-1979', + ], +}; + +const invalid_language_tags = [ + 'e', + 'Latn', + 'enLatnGBfonipa', + '11', + 'en_Latn', + 'en-Lat', + 'en-A999', +]; + +function assert_is_canonical(language_tag) { + const locale = new Intl.Locale(language_tag); + assert_equals(locale.toString(), language_tag); +} + +function assert_is_variation(variation_language_tag, expected_language_tag) { + const variation_locale = new Intl.Locale(variation_language_tag); + const expected_locale = new Intl.Locale(expected_language_tag); + assert_equals(variation_locale.language, expected_locale.language); +} diff --git a/tests/wpt/tests/ai/resources/util.js b/tests/wpt/tests/ai/resources/util.js index 49d677edae2..84507a409e9 100644 --- a/tests/wpt/tests/ai/resources/util.js +++ b/tests/wpt/tests/ai/resources/util.js @@ -1,5 +1,55 @@ +const kValidAvailabilities = + ['unavailable', 'downloadable', 'downloading', 'available']; +const kAvailableAvailabilities = ['downloadable', 'downloading', 'available']; + +const kTestPrompt = 'Please write a sentence in English.'; +const kTestContext = 'This is a test; this is only a test.'; + +const getId = (() => { + let idCount = 0; + return () => idCount++; +})(); + +// Takes an array of dictionaries mapping keys to value arrays, e.g.: +// [ {Shape: ["Square", "Circle", undefined]}, {Count: [1, 2]} ] +// Returns an array of dictionaries with all value combinations, i.e.: +// [ {Shape: "Square", Count: 1}, {Shape: "Square", Count: 2}, +// {Shape: "Circle", Count: 1}, {Shape: "Circle", Count: 2}, +// {Shape: undefined, Count: 1}, {Shape: undefined, Count: 2} ] +// Omits dictionary members when the value is undefined; supports array values. +function generateOptionCombinations(optionsSpec) { + // 1. Extract keys from the input specification. + const keys = optionsSpec.map(o => Object.keys(o)[0]); + // 2. Extract the arrays of possible values for each key. + const valueArrays = optionsSpec.map(o => Object.values(o)[0]); + // 3. Compute the Cartesian product of the value arrays using reduce. + const valueCombinations = valueArrays.reduce((accumulator, currentValues) => { + // Init the empty accumulator (first iteration), with single-element + // arrays. + if (accumulator.length === 0) { + return currentValues.map(value => [value]); + } + // Otherwise, expand existing combinations with current values. + return accumulator.flatMap( + existingCombo => currentValues.map( + currentValue => [...existingCombo, currentValue])); + }, []); + + // 4. Map each value combination to a result dictionary, skipping + // undefined. + return valueCombinations.map(combination => { + const result = {}; + keys.forEach((key, index) => { + if (combination[index] !== undefined) { + result[key] = combination[index]; + } + }); + return result; + }); +} + // The method should take the AbortSignal as an option and return a promise. -const testAbortPromise = async (t, method) => { +async function testAbortPromise(t, method) { // Test abort signal without custom error. { const controller = new AbortController(); @@ -25,3 +75,153 @@ const testAbortPromise = async (t, method) => { await promise_rejects_exactly(t, err, anotherPromise); } }; + +async function testCreateMonitorWithAbortAt( + t, loadedToAbortAt, method, options = {}) { + const {promise: eventPromise, resolve} = Promise.withResolvers(); + let hadEvent = false; + function monitor(m) { + m.addEventListener('downloadprogress', e => { + if (e.loaded != loadedToAbortAt) { + return; + } + + if (hadEvent) { + assert_unreached( + 'This should never be reached since LanguageDetector.create() was aborted.'); + return; + } + + resolve(); + hadEvent = true; + }); + } + + const controller = new AbortController(); + + const createPromise = + method({...options, monitor, signal: controller.signal}); + + await eventPromise; + + const err = new Error('test'); + controller.abort(err); + await promise_rejects_exactly(t, err, createPromise); +} + +async function testCreateMonitorWithAbort(t, method, options = {}) { + await testCreateMonitorWithAbortAt(t, 0, method, options); + await testCreateMonitorWithAbortAt(t, 1, method, options); +} + +// The method should take the AbortSignal as an option and return a +// ReadableStream. +async function testAbortReadableStream(t, method) { + // Test abort signal without custom error. + { + const controller = new AbortController(); + const stream = method(controller.signal); + controller.abort(); + let writableStream = new WritableStream(); + await promise_rejects_dom(t, 'AbortError', stream.pipeTo(writableStream)); + + // Using the same aborted controller will get the `AbortError` as well. + await promise_rejects_dom(t, 'AbortError', new Promise(() => { + method(controller.signal); + })); + } + + // Test abort signal with custom error. + { + const error = new DOMException('test', 'VersionError'); + const controller = new AbortController(); + const stream = method(controller.signal); + controller.abort(error); + let writableStream = new WritableStream(); + await promise_rejects_exactly(t, error, stream.pipeTo(writableStream)); + + // Using the same aborted controller will get the same error. + await promise_rejects_exactly(t, error, new Promise(() => { + method(controller.signal); + })); + } +}; + +async function testMonitor(createFunc, options = {}) { + let created = false; + const progressEvents = []; + function monitor(m) { + m.addEventListener('downloadprogress', e => { + // No progress events should be fired after `createFunc` resolves. + assert_false(created); + + progressEvents.push(e); + }); + } + + result = await createFunc({...options, monitor}); + created = true; + + assert_greater_than_equal(progressEvents.length, 2); + assert_equals(progressEvents.at(0).loaded, 0); + assert_equals(progressEvents.at(-1).loaded, 1); + + let lastProgressEventLoaded = -1; + for (const progressEvent of progressEvents) { + assert_equals(progressEvent.lengthComputable, true); + assert_equals(progressEvent.total, 1); + assert_less_than_equal(progressEvent.loaded, progressEvent.total); + + // `loaded` must be rounded to the nearest 0x10000th. + assert_equals(progressEvent.loaded % (1 / 0x10000), 0); + + // Progress events should have monotonically increasing `loaded` values. + assert_greater_than(progressEvent.loaded, lastProgressEventLoaded); + lastProgressEventLoaded = progressEvent.loaded; + } + return result; +} + +function run_iframe_test(iframe, test_name) { + const id = getId(); + iframe.contentWindow.postMessage({id, type: test_name}, '*'); + const {promise, resolve, reject} = Promise.withResolvers(); + window.onmessage = message => { + if (message.data.id !== id) { + return; + } + if (message.data.success) { + resolve(message.data.success); + } else { + reject(message.data.err) + } + }; + return promise; +} + +function load_iframe(src, permission_policy) { + let iframe = document.createElement('iframe'); + const {promise, resolve} = Promise.withResolvers(); + iframe.onload = () => { + resolve(iframe); + }; + iframe.src = src; + iframe.allow = permission_policy; + document.body.appendChild(iframe); + return promise; +} + +async function createSummarizer(options = {}) { + await test_driver.bless(); + return await Summarizer.create(options); +} + +async function createWriter(options = {}) { + await test_driver.bless(); + return await Writer.create(options); +} + +async function createRewriter(options = {}) { + await test_driver.bless(); + return await Rewriter.create(options); +} diff --git a/tests/wpt/tests/ai/rewriter/resources/iframe-helper.html b/tests/wpt/tests/ai/rewriter/resources/iframe-helper.html new file mode 100644 index 00000000000..c9cefebfd74 --- /dev/null +++ b/tests/wpt/tests/ai/rewriter/resources/iframe-helper.html @@ -0,0 +1,25 @@ + + + + + diff --git a/tests/wpt/tests/ai/rewriter/rewriter-abort.tentative.https.window.js b/tests/wpt/tests/ai/rewriter/rewriter-abort.tentative.https.window.js new file mode 100644 index 00000000000..992bb5c637c --- /dev/null +++ b/tests/wpt/tests/ai/rewriter/rewriter-abort.tentative.https.window.js @@ -0,0 +1,35 @@ +// META: title=Rewriter Abort +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async t => { + await testAbortPromise(t, signal => { + return createRewriter({signal: signal}); + }); +}, 'Aborting Rewriter.create()'); + +promise_test(async t => { + const rewriter = await createRewriter(); + await testAbortPromise(t, signal => { + return rewriter.rewrite(kTestPrompt, { signal: signal }); + }); +}, 'Aborting Rewriter.rewrite()'); + +promise_test(async t => { + const rewriter = await createRewriter(); + await testAbortReadableStream(t, signal => { + return rewriter.rewriteStreaming(kTestPrompt, { signal: signal }); + }); +}, 'Aborting Rewriter.rewriteStreaming()'); + +promise_test(async t => { + const rewriter = await createRewriter(); + const controller = new AbortController(); + const streamingResponse = rewriter.rewriteStreaming( + kTestPrompt, { signal: controller.signal }); + for await (const chunk of streamingResponse); // Do nothing + controller.abort(); +}, 'Aborting Rewriter.rewriteStreaming() after finished reading'); diff --git a/tests/wpt/tests/ai/rewriter/rewriter-availability-available.tentative.https.window.js b/tests/wpt/tests/ai/rewriter/rewriter-availability-available.tentative.https.window.js new file mode 100644 index 00000000000..a2117486e23 --- /dev/null +++ b/tests/wpt/tests/ai/rewriter/rewriter-availability-available.tentative.https.window.js @@ -0,0 +1,34 @@ +// META: title=Rewriter Availability Available +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + const availability = await Rewriter.availability(); + assert_in_array(availability, kAvailableAvailabilities); +}, 'Rewriter.availability() is available with no options'); + +promise_test(async () => { + const availability = await Rewriter.availability({ + tone: 'as-is', + format: 'as-is', + length: 'as-is', + expectedInputLanguages: ['en-GB'], + expectedContextLanguages: ['en'], + outputLanguage: 'en', + }); + assert_in_array(availability, kAvailableAvailabilities); +}, 'Rewriter.availability() returns available with supported options'); + +promise_test(async () => { + const availability = await Rewriter.availability({ + tone: 'as-is', + format: 'as-is', + length: 'as-is', + expectedInputLanguages: ['es'], // not supported + expectedContextLanguages: ['en'], + outputLanguage: 'es', // not supported + }); + assert_equals(availability, 'unavailable'); +}, 'Rewriter.availability() returns unavailable for unsupported languages'); diff --git a/tests/wpt/tests/ai/rewriter/rewriter-availability.tentative.https.window.js b/tests/wpt/tests/ai/rewriter/rewriter-availability.tentative.https.window.js new file mode 100644 index 00000000000..e966b5df31e --- /dev/null +++ b/tests/wpt/tests/ai/rewriter/rewriter-availability.tentative.https.window.js @@ -0,0 +1,31 @@ +// META: title=Rewriter Availability +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + assert_true(!!Rewriter); + assert_equals(typeof Rewriter.availability, 'function'); +}, 'Rewriter.availability() is defined'); + +promise_test(async () => { + const availability = await Rewriter.availability(); + assert_in_array(availability, kValidAvailabilities); +}, 'Rewriter.availability() returns a valid value with no options'); + +promise_test(async () => { + // An array of plausible test option values. + const kCreateOptionsSpec = [ + {tone: [undefined, 'as-is', 'more-formal', 'more-casual']}, + {format: [undefined, 'as-is', 'plain-text', 'markdown']}, + {length: [undefined, 'as-is', 'shorter', 'longer']}, + {expectedInputLanguages: [[], ['en'], ['es'], ['jp', 'fr']]}, + {expectedContextLanguages: [[], ['en'], ['es'], ['jp', 'fr']]}, + {outputLanguage: [undefined, 'en', 'es', 'jp', 'fr']} + ]; + for (const options of generateOptionCombinations(kCreateOptionsSpec)) { + const availability = await Rewriter.availability(options); + assert_in_array(availability, kValidAvailabilities, options); + } +}, 'Rewriter.availability() returns a valid value with plausible options'); diff --git a/tests/wpt/tests/ai/rewriter/rewriter-create-available.tentative.https.window.js b/tests/wpt/tests/ai/rewriter/rewriter-create-available.tentative.https.window.js new file mode 100644 index 00000000000..41371d5db56 --- /dev/null +++ b/tests/wpt/tests/ai/rewriter/rewriter-create-available.tentative.https.window.js @@ -0,0 +1,102 @@ +// META: title=Rewriter Create Available +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + const rewriter = await createRewriter(); + assert_equals(typeof rewriter, 'object'); + + assert_equals(typeof rewriter.rewrite, 'function'); + assert_equals(typeof rewriter.rewriteStreaming, 'function'); + assert_equals(typeof rewriter.measureInputUsage, 'function'); + assert_equals(typeof rewriter.destroy, 'function'); + + assert_equals(typeof rewriter.expectedContextLanguages, 'object'); + assert_equals(typeof rewriter.expectedInputLanguages, 'object'); + assert_equals(typeof rewriter.inputQuota, 'number'); + assert_equals(typeof rewriter.outputLanguage, 'object'); + assert_equals(typeof rewriter.sharedContext, 'string'); + + assert_equals(typeof rewriter.tone, 'string'); + assert_equals(typeof rewriter.format, 'string'); + assert_equals(typeof rewriter.length, 'string'); + + assert_equals(rewriter.tone, 'as-is'); + assert_equals(rewriter.format, 'as-is'); + assert_equals(rewriter.length, 'as-is'); +}, 'Rewriter.create() returns a valid object with default options'); + +promise_test(async () => { + await testMonitor(createRewriter); +}, 'Rewriter.create() notifies its monitor on downloadprogress'); + +promise_test(async t => { + await testCreateMonitorWithAbort(t, Rewriter.create); +}, 'Progress events are not emitted after aborted'); + +promise_test(async () => { + const sharedContext = 'This is a shared context string'; + const rewriter = await createRewriter({sharedContext: sharedContext}); + assert_equals(rewriter.sharedContext, sharedContext); +}, 'Rewriter.sharedContext'); + +promise_test(async () => { + const rewriter = await createRewriter({ tone: 'more-formal' }); + assert_equals(rewriter.tone, 'more-formal'); +}, 'Creating a Rewriter with "more-formal" tone'); + +promise_test(async () => { + const rewriter = await createRewriter({ tone: 'more-casual' }); + assert_equals(rewriter.tone, 'more-casual'); +}, 'Creating a Rewriter with "more-casual" tone'); + +promise_test(async () => { + const rewriter = await createRewriter({ format: 'plain-text' }); + assert_equals(rewriter.format, 'plain-text'); +}, 'Creating a Rewriter with "plain-text" format'); + +promise_test(async () => { + const rewriter = await createRewriter({ format: 'markdown' }); + assert_equals(rewriter.format, 'markdown'); +}, 'Creating a Rewriter with "markdown" format'); + +promise_test(async () => { + const rewriter = await createRewriter({ length: 'shorter' }); + assert_equals(rewriter.length, 'shorter'); +}, 'Creating a Rewriter with "shorter" length'); + +promise_test(async () => { + const rewriter = await createRewriter({ length: 'longer' }); + assert_equals(rewriter.length, 'longer'); +}, 'Creating a Rewriter with "longer" length'); + +promise_test(async () => { + const rewriter = await createRewriter({expectedInputLanguages: ['en']}); + assert_array_equals(rewriter.expectedInputLanguages, ['en']); +}, 'Rewriter.expectedInputLanguages'); + +promise_test(async () => { + const rewriter = await createRewriter({expectedContextLanguages: ['en']}); + assert_array_equals(rewriter.expectedContextLanguages, ['en']); +}, 'Rewriter.expectedContextLanguages'); + +promise_test(async () => { + const rewriter = await createRewriter({outputLanguage: 'en'}); + assert_equals(rewriter.outputLanguage, 'en'); +}, 'Rewriter.outputLanguage'); + +promise_test(async (t) => { + promise_rejects_js( + t, RangeError, + createRewriter({ expectedInputLanguages: ['en-abc-invalid'] })); +}, 'Creating Rewriter with malformed language string'); + +promise_test(async () => { + const rewriter = await createRewriter({}); + assert_equals(rewriter.expectedInputLanguages, null); + assert_equals(rewriter.expectedContextLanguages, null); + assert_equals(rewriter.outputLanguage, null); +}, 'Rewriter optional attributes return null'); diff --git a/tests/wpt/tests/ai/rewriter/rewriter-create.tentative.https.window.js b/tests/wpt/tests/ai/rewriter/rewriter-create.tentative.https.window.js new file mode 100644 index 00000000000..4494f591226 --- /dev/null +++ b/tests/wpt/tests/ai/rewriter/rewriter-create.tentative.https.window.js @@ -0,0 +1,24 @@ +// META: title=Rewriter Create +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + assert_true(!!Rewriter); +}, '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"'); diff --git a/tests/wpt/tests/ai/rewriter/rewriter-from-detached-iframe.tentative.https.window.js b/tests/wpt/tests/ai/rewriter/rewriter-from-detached-iframe.tentative.https.window.js new file mode 100644 index 00000000000..27f1d96d546 --- /dev/null +++ b/tests/wpt/tests/ai/rewriter/rewriter-from-detached-iframe.tentative.https.window.js @@ -0,0 +1,57 @@ +// META: title=Rewriter Detached Iframe +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Rewriter create()', null, iframe.contentWindow); + iframe.contentWindow.Rewriter.create(); + iframe.remove(); +}, 'Detaching iframe during Rewriter.create() should not leak memory'); + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Rewriter create()', null, iframe.contentWindow); + const iframeWindow = iframe.contentWindow; + const iframeDOMException = iframeWindow.DOMException; + const iframeRewriter = iframeWindow.Rewriter; + iframe.remove(); + + await promise_rejects_dom( + t, 'InvalidStateError', iframeDOMException, iframeRewriter.create()); +}, 'Rewriter.create() fails on a detached iframe.'); + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Rewriter create()', null, iframe.contentWindow); + const iframeDOMException = iframe.contentWindow.DOMException; + const rewriter = await iframe.contentWindow.Rewriter.create(); + iframe.remove(); + + await promise_rejects_dom( + t, 'InvalidStateError', iframeDOMException, rewriter.rewrite('hello')); +}, 'Rewriter.rewrite() fails on a detached iframe.'); + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Rewriter create()', null, iframe.contentWindow); + const iframeWindow = iframe.contentWindow; + const iframeDOMException = iframeWindow.DOMException; + const rewriter = await iframeWindow.Rewriter.create(); + iframe.remove(); + + assert_throws_dom( + 'InvalidStateError', + iframeDOMException, () => rewriter.rewriteStreaming('hello')); +}, 'Rewriter.rewriteStreaming() fails on a detached iframe.'); + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Rewriter create()', null, iframe.contentWindow); + const rewriter = await iframe.contentWindow.Rewriter.create(); + rewriter.rewrite('hello'); + iframe.remove(); +}, 'Detaching iframe during Rewriter.rewrite() should not leak memory'); diff --git a/tests/wpt/tests/ai/rewriter/rewriter-iframe.tentative.https.html b/tests/wpt/tests/ai/rewriter/rewriter-iframe.tentative.https.html new file mode 100644 index 00000000000..202ee780af7 --- /dev/null +++ b/tests/wpt/tests/ai/rewriter/rewriter-iframe.tentative.https.html @@ -0,0 +1,58 @@ + + + + + + + + + + diff --git a/tests/wpt/tests/ai/rewriter/rewriter-measureInputUsage.tentative.https.window.js b/tests/wpt/tests/ai/rewriter/rewriter-measureInputUsage.tentative.https.window.js new file mode 100644 index 00000000000..beefa358f3e --- /dev/null +++ b/tests/wpt/tests/ai/rewriter/rewriter-measureInputUsage.tentative.https.window.js @@ -0,0 +1,12 @@ +// META: title=Rewriter measureInputUsage +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + const rewriter = await createRewriter(); + const result = await rewriter.measureInputUsage(kTestPrompt); + assert_greater_than(result, 0); +}, 'Rewriter.measureInputUsage() returns non-empty result'); diff --git a/tests/wpt/tests/ai/rewriter/rewriter-rewrite-streaming.tentative.https.window.js b/tests/wpt/tests/ai/rewriter/rewriter-rewrite-streaming.tentative.https.window.js new file mode 100644 index 00000000000..81b73fbdabd --- /dev/null +++ b/tests/wpt/tests/ai/rewriter/rewriter-rewrite-streaming.tentative.https.window.js @@ -0,0 +1,46 @@ +// META: title=Rewriter Rewrite Streaming +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + const rewriter = await createRewriter(); + const streamingResponse = + rewriter.rewriteStreaming(kTestPrompt, { context: kTestContext }); + assert_equals( + Object.prototype.toString.call(streamingResponse), + '[object ReadableStream]'); + let result = ''; + for await (const chunk of streamingResponse) { + result += chunk; + } + assert_greater_than(result.length, 0); +}, 'Simple Rewriter.rewriteStreaming() call'); + +promise_test(async (t) => { + const rewriter = await createRewriter(); + rewriter.destroy(); + assert_throws_dom( + 'InvalidStateError', () => rewriter.rewriteStreaming(kTestPrompt)); +}, 'Rewriter.rewriteStreaming() fails after destroyed'); + +promise_test(async t => { + const rewriter = await createRewriter(); + const streamingResponse = rewriter.rewriteStreaming(''); + assert_equals( + Object.prototype.toString.call(streamingResponse), + "[object ReadableStream]" + ); + const { result, done } = await streamingResponse.getReader().read(); + assert_true(done); +}, 'Rewriter.rewriteStreaming() returns a ReadableStream without any chunk on an empty input'); + +promise_test(async () => { + const rewriter = await createRewriter(); + await Promise.all([ + rewriter.rewriteStreaming(kTestPrompt), + rewriter.rewriteStreaming(kTestPrompt) + ]); +}, 'Multiple Rewriter.rewriteStreaming() calls are resolved successfully'); diff --git a/tests/wpt/tests/ai/rewriter/rewriter-rewrite.tentative.https.window.js b/tests/wpt/tests/ai/rewriter/rewriter-rewrite.tentative.https.window.js new file mode 100644 index 00000000000..07e8b419a4a --- /dev/null +++ b/tests/wpt/tests/ai/rewriter/rewriter-rewrite.tentative.https.window.js @@ -0,0 +1,43 @@ +// META: title=Rewriter Rewrite +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async (t) => { + const rewriter = await createRewriter(); + let result = await rewriter.rewrite(''); + assert_equals(result, ''); +}, 'Rewriter.rewrite() with an empty input returns an empty text'); + +promise_test(async (t) => { + const rewriter = await createRewriter(); + let result = await rewriter.rewrite(' '); + assert_equals(result, ' '); +}, 'Rewriter.rewrite() with a whitespace input returns a whitespace text'); + +promise_test(async (t) => { + const rewriter = await createRewriter(); + const result = await rewriter.rewrite(kTestPrompt, { context: ' ' }); + assert_not_equals(result, ''); +}, 'Rewriter.rewrite() with a whitespace context returns a non-empty result'); + +promise_test(async (t) => { + const rewriter = await createRewriter(); + rewriter.destroy(); + await promise_rejects_dom( + t, 'InvalidStateError', rewriter.rewrite(kTestPrompt)); +}, 'Rewriter.rewrite() fails after destroyed'); + +promise_test(async () => { + const rewriter = await createRewriter(); + const result = await rewriter.rewrite(kTestPrompt, { context: kTestContext }); + assert_equals(typeof result, 'string'); +}, 'Simple Rewriter.rewrite() call'); + +promise_test(async () => { + const rewriter = await createRewriter(); + await Promise.all( + [rewriter.rewrite(kTestPrompt), rewriter.rewrite(kTestPrompt)]); +}, 'Multiple Rewriter.rewrite() calls are resolved successfully'); diff --git a/tests/wpt/tests/ai/summarizer/resources/iframe-helper.html b/tests/wpt/tests/ai/summarizer/resources/iframe-helper.html new file mode 100644 index 00000000000..2d417e04f62 --- /dev/null +++ b/tests/wpt/tests/ai/summarizer/resources/iframe-helper.html @@ -0,0 +1,26 @@ + + + + + + diff --git a/tests/wpt/tests/ai/summarizer/summarizer-abort.tentative.https.window.js b/tests/wpt/tests/ai/summarizer/summarizer-abort.tentative.https.window.js new file mode 100644 index 00000000000..6c98a845fa6 --- /dev/null +++ b/tests/wpt/tests/ai/summarizer/summarizer-abort.tentative.https.window.js @@ -0,0 +1,35 @@ +// META: title=Summarizer Abort +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async t => { + await testAbortPromise(t, signal => { + return createSummarizer({signal: signal}); + }); +}, 'Aborting Summarizer.create().'); + +promise_test(async t => { + const summarizer = await createSummarizer(); + await testAbortPromise(t, signal => { + return summarizer.summarize(kTestPrompt, { signal: signal }); + }); +}, 'Aborting Summarizer.summarize()'); + +promise_test(async t => { + const summarizer = await createSummarizer(); + await testAbortReadableStream(t, signal => { + return summarizer.summarizeStreaming(kTestPrompt, { signal: signal }); + }); +}, 'Aborting Summarizer.summarizeStreaming()'); + +promise_test(async (t) => { + const summarizer = await createSummarizer(); + const controller = new AbortController(); + const streamingResponse = summarizer.summarizeStreaming( + kTestPrompt, { signal: controller.signal }); + for await (const chunk of streamingResponse); // Do nothing + controller.abort(); +}, 'Aborting Summarizer.summarizeStreaming() after finished reading'); diff --git a/tests/wpt/tests/ai/summarizer/summarizer-availability-available.tentative.https.window.js b/tests/wpt/tests/ai/summarizer/summarizer-availability-available.tentative.https.window.js new file mode 100644 index 00000000000..df4e161d637 --- /dev/null +++ b/tests/wpt/tests/ai/summarizer/summarizer-availability-available.tentative.https.window.js @@ -0,0 +1,35 @@ +// META: title=Summarizer Availability Available +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + const availability = await Summarizer.availability(); + assert_in_array(availability, kAvailableAvailabilities); +}, 'Summarizer.availability() is available with no options'); + +promise_test(async () => { + const availability = await Summarizer.availability({ + type: 'tldr', + format: 'plain-text', + length: 'medium', + expectedInputLanguages: ['en-GB'], + expectedContextLanguages: ['en'], + outputLanguage: 'en', + }); + assert_in_array(availability, kAvailableAvailabilities); +}, 'Summarizer.availability() returns available with supported options'); + +promise_test(async () => { + const availability = await Summarizer.availability({ + type: 'tldr', + format: 'plain-text', + length: 'medium', + expectedInputLanguages: ['es'], // not supported + expectedContextLanguages: ['en'], + outputLanguage: 'es', // not supported + }); + assert_equals(availability, 'unavailable'); +}, 'Summarizer.availability() returns unavailable for unsupported languages'); diff --git a/tests/wpt/tests/ai/summarizer/summarizer-availability.tentative.https.window.js b/tests/wpt/tests/ai/summarizer/summarizer-availability.tentative.https.window.js new file mode 100644 index 00000000000..91cb3129557 --- /dev/null +++ b/tests/wpt/tests/ai/summarizer/summarizer-availability.tentative.https.window.js @@ -0,0 +1,32 @@ +// META: title=Summarizer Availability +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + assert_true(!!Summarizer); + assert_equals(typeof Summarizer.availability, 'function'); +}, 'Summarizer.availability() is defined'); + +promise_test(async () => { + const availability = await Summarizer.availability(); + assert_in_array(availability, kValidAvailabilities); +}, 'Summarizer.availability() returns a valid value with no options'); + +promise_test(async () => { + // An array of plausible test option values. + const kCreateOptionsSpec = [ + {type: [undefined, 'tldr', 'teaser', 'key-points', 'headline']}, + {format: [undefined, 'plain-text', 'markdown']}, + {length: [undefined, 'short', 'medium', 'long']}, + {expectedInputLanguages: [[], ['en'], ['es'], ['jp', 'fr']]}, + {expectedContextLanguages: [[], ['en'], ['es'], ['jp', 'fr']]}, + {outputLanguage: [undefined, 'en', 'es', 'jp', 'fr']} + ]; + for (const options of generateOptionCombinations(kCreateOptionsSpec)) { + const availability = await Summarizer.availability(options); + assert_in_array(availability, kValidAvailabilities, options); + } +}, 'Summarizer.availability() returns a valid value with plausible options'); diff --git a/tests/wpt/tests/ai/summarizer/summarizer-create-available.tentative.https.window.js b/tests/wpt/tests/ai/summarizer/summarizer-create-available.tentative.https.window.js new file mode 100644 index 00000000000..0153ca784ce --- /dev/null +++ b/tests/wpt/tests/ai/summarizer/summarizer-create-available.tentative.https.window.js @@ -0,0 +1,88 @@ +// META: title=Summarizer Create Available +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + const summarizer = await createSummarizer(); + assert_equals(typeof summarizer, 'object'); + + assert_equals(typeof summarizer.summarize, 'function'); + assert_equals(typeof summarizer.summarizeStreaming, 'function'); + assert_equals(typeof summarizer.measureInputUsage, 'function'); + assert_equals(typeof summarizer.destroy, 'function'); + + assert_equals(typeof summarizer.expectedContextLanguages, 'object'); + assert_equals(typeof summarizer.expectedInputLanguages, 'object'); + assert_equals(typeof summarizer.inputQuota, 'number'); + assert_equals(typeof summarizer.outputLanguage, 'object'); + assert_equals(typeof summarizer.sharedContext, 'string'); + + assert_equals(typeof summarizer.type, 'string'); + assert_equals(typeof summarizer.format, 'string'); + assert_equals(typeof summarizer.length, 'string'); + + assert_equals(summarizer.type, 'key-points'); + assert_equals(summarizer.format, 'markdown'); + assert_equals(summarizer.length, 'short'); +}, 'Summarizer.create() returns a valid object with default options'); + +promise_test(async () => { + const summarizer = await testMonitor(createSummarizer); + assert_equals(typeof summarizer, 'object'); +}, 'Summarizer.create() notifies its monitor on downloadprogress'); + +promise_test(async t => { + await testCreateMonitorWithAbort(t, Summarizer.create); +}, 'Progress events are not emitted after aborted'); + +promise_test(async () => { + const sharedContext = 'This is a shared context string'; + const summarizer = await createSummarizer({sharedContext: sharedContext}); + assert_equals(summarizer.sharedContext, sharedContext); +}, 'Summarizer.sharedContext'); + +promise_test(async () => { + const summarizer = await createSummarizer({type: 'headline'}); + assert_equals(summarizer.type, 'headline'); +}, 'Summarizer.type'); + +promise_test(async () => { + const summarizer = await createSummarizer({format: 'plain-text'}); + assert_equals(summarizer.format, 'plain-text'); +}, 'Summarizer.format'); + +promise_test(async () => { + const summarizer = await createSummarizer({length: 'medium'}); + assert_equals(summarizer.length, 'medium'); +}, 'Summarizer.length'); + +promise_test(async () => { + const summarizer = await createSummarizer({expectedInputLanguages: ['en']}); + assert_array_equals(summarizer.expectedInputLanguages, ['en']); +}, 'Summarizer.expectedInputLanguages'); + +promise_test(async () => { + const summarizer = await createSummarizer({expectedContextLanguages: ['en']}); + assert_array_equals(summarizer.expectedContextLanguages, ['en']); +}, 'Summarizer.expectedContextLanguages'); + +promise_test(async () => { + const summarizer = await createSummarizer({outputLanguage: 'en'}); + assert_equals(summarizer.outputLanguage, 'en'); +}, 'Summarizer.outputLanguage'); + +promise_test(async (t) => { + promise_rejects_js( + t, RangeError, + createSummarizer({ expectedInputLanguages: ['en-abc-invalid'] })); +}, 'Creating Summarizer with malformed language string'); + +promise_test(async () => { + const summarizer = await createSummarizer(); + assert_equals(summarizer.expectedInputLanguages, null); + assert_equals(summarizer.expectedContextLanguages, null); + assert_equals(summarizer.outputLanguage, null); +}, 'Summarizer optional attributes return null'); diff --git a/tests/wpt/tests/ai/summarizer/summarizer-create.tentative.https.window.js b/tests/wpt/tests/ai/summarizer/summarizer-create.tentative.https.window.js new file mode 100644 index 00000000000..ff5d42b100f --- /dev/null +++ b/tests/wpt/tests/ai/summarizer/summarizer-create.tentative.https.window.js @@ -0,0 +1,25 @@ +// META: title=Summarizer Create +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + assert_true(!!Summarizer); + assert_equals(typeof Summarizer.create, 'function'); +}, '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"'); diff --git a/tests/wpt/tests/ai/summarizer/summarizer-from-detached-iframe.tentative.https.window.js b/tests/wpt/tests/ai/summarizer/summarizer-from-detached-iframe.tentative.https.window.js new file mode 100644 index 00000000000..8bfdc923196 --- /dev/null +++ b/tests/wpt/tests/ai/summarizer/summarizer-from-detached-iframe.tentative.https.window.js @@ -0,0 +1,57 @@ +// META: title=Summarizer Detached Iframe +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Summarizer create()', null, iframe.contentWindow); + iframe.contentWindow.Summarizer.create(); + iframe.remove(); +}, 'Detaching iframe during Summarizer.create() should not leak memory'); + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Summarizer create()', null, iframe.contentWindow); + const iframeWindow = iframe.contentWindow; + const iframeDOMException = iframeWindow.DOMException; + const iframeSummarizer = iframeWindow.Summarizer; + iframe.remove(); + + await promise_rejects_dom( + t, 'InvalidStateError', iframeDOMException, iframeSummarizer.create()); +}, 'Summarizer.create() fails on a detached iframe'); + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Summarizer create()', null, iframe.contentWindow); + const iframeDOMException = iframe.contentWindow.DOMException; + const summarizer = await iframe.contentWindow.Summarizer.create(); + iframe.remove(); + + await promise_rejects_dom( + t, 'InvalidStateError', iframeDOMException, summarizer.summarize(kTestPrompt)); +}, 'Summarizer.summarize() fails on a detached iframe'); + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Summarizer create()', null, iframe.contentWindow); + const iframeWindow = iframe.contentWindow; + const iframeDOMException = iframeWindow.DOMException; + const summarizer = await iframeWindow.Summarizer.create(); + iframe.remove(); + + assert_throws_dom( + 'InvalidStateError', + iframeDOMException, () => summarizer.summarizeStreaming(kTestPrompt)); +}, 'Summarizer.summarizeStreaming() fails on a detached iframe'); + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Summarizer create()', null, iframe.contentWindow); + const summarizer = await iframe.contentWindow.Summarizer.create(); + summarizer.summarize(kTestPrompt); + iframe.remove(); +}, 'Detaching iframe during Summarizer.summarize() should not leak memory'); diff --git a/tests/wpt/tests/ai/summarizer/summarizer-iframe.tentative.https.html b/tests/wpt/tests/ai/summarizer/summarizer-iframe.tentative.https.html new file mode 100644 index 00000000000..9de80e1fd91 --- /dev/null +++ b/tests/wpt/tests/ai/summarizer/summarizer-iframe.tentative.https.html @@ -0,0 +1,58 @@ + + + + + + + + + + diff --git a/tests/wpt/tests/ai/summarizer/summarizer-measureInputUsage.tentative.https.window.js b/tests/wpt/tests/ai/summarizer/summarizer-measureInputUsage.tentative.https.window.js new file mode 100644 index 00000000000..8368096f1f3 --- /dev/null +++ b/tests/wpt/tests/ai/summarizer/summarizer-measureInputUsage.tentative.https.window.js @@ -0,0 +1,13 @@ +// META: title=Summarizer measureInputUsage +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + const summarizer = await createSummarizer(); + const result = await summarizer.measureInputUsage(kTestPrompt); + assert_equals(typeof result, 'number'); + assert_greater_than(result, 0); +}, 'Summarizer.measureInputUsage() returns non-empty result'); diff --git a/tests/wpt/tests/ai/summarizer/summarizer-summarize-streaming.tentative.https.window.js b/tests/wpt/tests/ai/summarizer/summarizer-summarize-streaming.tentative.https.window.js new file mode 100644 index 00000000000..8d85dc7b836 --- /dev/null +++ b/tests/wpt/tests/ai/summarizer/summarizer-summarize-streaming.tentative.https.window.js @@ -0,0 +1,45 @@ +// META: title=Summarizer Summarize Streaming +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async t => { + const summarizer = await createSummarizer(); + const streamingResponse = summarizer.summarizeStreaming(kTestPrompt); + assert_equals( + Object.prototype.toString.call(streamingResponse), + "[object ReadableStream]" + ); + let result = ''; + for await (const chunk of streamingResponse) { + result += chunk; + } + assert_greater_than(result.length, 0); +}, 'Simple Summarizer.summarizeStreaming() call'); + +promise_test(async (t) => { + const summarizer = await createSummarizer(); + summarizer.destroy(); + assert_throws_dom('InvalidStateError', () => summarizer.summarizeStreaming(kTestPrompt)); +}, 'Summarizer.summarizeStreaming() fails after destroyed'); + +promise_test(async t => { + const summarizer = await createSummarizer(); + const streamingResponse = summarizer.summarizeStreaming(''); + assert_equals( + Object.prototype.toString.call(streamingResponse), + "[object ReadableStream]" + ); + const { result, done } = await streamingResponse.getReader().read(); + assert_true(done); +}, 'Summarizer.summarizeStreaming() returns a ReadableStream without any chunk on an empty input'); + +promise_test(async () => { + const summarizer = await createSummarizer(); + await Promise.all([ + summarizer.summarizeStreaming(kTestPrompt), + summarizer.summarizeStreaming(kTestPrompt) + ]); +}, 'Multiple Summarizer.summarizeStreaming() calls are resolved successfully'); diff --git a/tests/wpt/tests/ai/summarizer/summarizer-summarize.tentative.https.window.js b/tests/wpt/tests/ai/summarizer/summarizer-summarize.tentative.https.window.js new file mode 100644 index 00000000000..a9be36dace0 --- /dev/null +++ b/tests/wpt/tests/ai/summarizer/summarizer-summarize.tentative.https.window.js @@ -0,0 +1,41 @@ +// META: title=Summarizer Summarize +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async (t) => { + const summarizer = await createSummarizer(); + let result = await summarizer.summarize(''); + assert_equals(result, ''); + result = await summarizer.summarize(' '); + assert_equals(result, ''); +}, 'Summarizer.summarize() with an empty input returns an empty text'); + +promise_test(async (t) => { + const summarizer = await createSummarizer(); + const result = await summarizer.summarize(kTestPrompt, { context: ' ' }); + assert_not_equals(result, ''); +}, 'Summarizer.summarize() with a whitespace context returns an empty result'); + +promise_test(async (t) => { + const summarizer = await createSummarizer(); + summarizer.destroy(); + await promise_rejects_dom(t, 'InvalidStateError', summarizer.summarize(kTestPrompt)); +}, 'Summarizer.summarize() fails after destroyed'); + +promise_test(async () => { + const summarizer = await createSummarizer(); + const result = await summarizer.summarize(kTestPrompt); + assert_equals(typeof result, 'string'); + assert_greater_than(result.length, 0); +}, 'Simple Summarizer.summarize() call'); + +promise_test(async () => { + const summarizer = await createSummarizer(); + await Promise.all([ + summarizer.summarize(kTestPrompt), + summarizer.summarize(kTestPrompt) + ]); +}, 'Multiple Summarizer.summarize() calls are resolved successfully'); diff --git a/tests/wpt/tests/ai/translator/resources/util.js b/tests/wpt/tests/ai/translator/resources/util.js new file mode 100644 index 00000000000..ad06086a123 --- /dev/null +++ b/tests/wpt/tests/ai/translator/resources/util.js @@ -0,0 +1,4 @@ +async function createTranslator(options) { + await test_driver.bless(); + return await Translator.create(options); +} diff --git a/tests/wpt/tests/ai/translator/translator-bad-input.https.window.js b/tests/wpt/tests/ai/translator/translator-bad-input.https.window.js new file mode 100644 index 00000000000..db8905a61f6 --- /dev/null +++ b/tests/wpt/tests/ai/translator/translator-bad-input.https.window.js @@ -0,0 +1,22 @@ +// META: title=translator.create without options do not crash +// META: global=window +// META: timeout=long +// +// Setting `timeout=long` as this test may require downloading the translation +// library and the language models. + +'use strict'; + +promise_test(async t => { + await promise_rejects_js(t, TypeError, Translator.create(/*empty options*/)); +}, 'Translator.create rejects with TypeError if no options are passed.'); + +promise_test(async t => { + await promise_rejects_js( + t, TypeError, Translator.create({sourceLanguage: 'en'})); +}, 'Translator.create rejects with TypeError targetLanguage is not provided.'); + +promise_test(async t => { + await promise_rejects_js( + t, TypeError, Translator.create({targetLanguage: 'en'})); +}, 'Translator.create rejects with TypeError sourceLanguage is not provided.'); diff --git a/tests/wpt/tests/ai/translator/translator-locale.https.window.js b/tests/wpt/tests/ai/translator/translator-locale.https.window.js new file mode 100644 index 00000000000..73e7eff20d2 --- /dev/null +++ b/tests/wpt/tests/ai/translator/translator-locale.https.window.js @@ -0,0 +1,50 @@ +// META: title=Detect english +// META: global=window +// META: timeout=long +// META: script=resources/util.js +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: script=../resources/locale-util.js + +'use strict'; + +function assert_rejects_invalid_expected_input_languages( + t, method, sourceLanguage, targetLanguage) { + return promise_rejects_js( + t, RangeError, method({sourceLanguage, targetLanguage})); +} + +function testInvalidLanguagePairs(t, method) { + const allValidLanguageTags = Object.values(valid_language_tags).flat(); + // Invalid source language. + for (const sourceLanguage of invalid_language_tags) { + for (const targetLanguage of allValidLanguageTags) { + assert_rejects_invalid_expected_input_languages( + t, method, sourceLanguage, targetLanguage); + } + } + // Invalid target language. + for (const sourceLanguage of allValidLanguageTags) { + for (const targetLanguage of invalid_language_tags) { + assert_rejects_invalid_expected_input_languages( + t, method, sourceLanguage, targetLanguage); + } + } + // Invalid source and target language + for (const sourceLanguage of invalid_language_tags) { + for (const targetLanguage of invalid_language_tags) { + assert_rejects_invalid_expected_input_languages( + t, method, sourceLanguage, targetLanguage); + } + } +} + +promise_test(async t => { + // We don't need to consume user activation since it should throw a RangeError + // before it even can check if it needs to consume user activation. + testInvalidLanguagePairs(t, Translator.create); +}, 'LanguageDetector.create() throws RangeError for invalid language tags'); + +promise_test(async t => { + testInvalidLanguagePairs(t, Translator.availability); +}, 'LanguageDetector.availability() throws RangeError for invalid language tags'); diff --git a/tests/wpt/tests/ai/translator/translator.optional.https.window.js b/tests/wpt/tests/ai/translator/translator.optional.https.window.js new file mode 100644 index 00000000000..2a4c5a6c5dd --- /dev/null +++ b/tests/wpt/tests/ai/translator/translator.optional.https.window.js @@ -0,0 +1,211 @@ +// META: title=Translator Translate +// META: global=window +// META: timeout=long +// META: script=../resources/util.js +// META: script=../resources/language_codes.js +// META: script=/resources/testdriver.js +// META: script=resources/util.js +// +// Setting `timeout=long` as this test may require downloading the translation +// library and the language models. + +'use strict'; + +promise_test(async t => { + const languagePair = {sourceLanguage: 'en', targetLanguage: 'ja'}; + + // Creating the translator without user activation rejects with + // NotAllowedError. + const createPromise = Translator.create(languagePair); + await promise_rejects_dom(t, 'NotAllowedError', createPromise); + + // Creating the translator with user activation succeeds. + await createTranslator(languagePair); + + // Creating it should have switched it to available. + const availability = await Translator.availability(languagePair); + assert_equals(availability, 'available'); + + // Now that it is available, we should no longer need user activation. + await Translator.create(languagePair); +}, 'Translator.create() requires user activation when availability is "downloadable.'); + +promise_test(async t => { + const translator = + await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); + assert_equals(await translator.translate('hello'), 'ã“ã‚“ã«ã¡ã¯'); +}, 'Simple Translator.translate() call'); + +promise_test(async () => { + const translator = + await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); + const streamingResponse = translator.translateStreaming('hello'); + assert_equals( + Object.prototype.toString.call(streamingResponse), + '[object ReadableStream]'); + let result = ''; + for await (const chunk of streamingResponse) { + result += chunk; + } + assert_equals(await translator.translate('hello'), 'ã“ã‚“ã«ã¡ã¯'); +}, 'Simple Translator.translateStreaming() call'); + +promise_test(async t => { + const translator = + await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); + assert_equals(translator.sourceLanguage, 'en'); + assert_equals(translator.targetLanguage, 'ja'); +}, 'Translator: sourceLanguage and targetLanguage are equal to their respective option passed in to Translator.create.'); + +promise_test(async t => { + const controller = new AbortController(); + controller.abort(); + + const createPromise = createTranslator( + {signal: controller.signal, sourceLanguage: 'en', targetLanguage: 'ja'}); + + await promise_rejects_dom(t, 'AbortError', createPromise); +}, 'Translator.create() call with an aborted signal.'); + +promise_test(async t => { + await testAbortPromise(t, signal => { + return createTranslator( + {signal, sourceLanguage: 'en', targetLanguage: 'ja'}); + }); +}, 'Aborting Translator.create().'); + +promise_test(async t => { + const controller = new AbortController(); + controller.abort(); + + const translator = + await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); + const translatePromise = + translator.translate('hello', {signal: controller.signal}); + + await promise_rejects_dom(t, 'AbortError', translatePromise); +}, 'Translator.translate() call with an aborted signal.'); + +promise_test(async t => { + const translator = + await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); + await testAbortPromise(t, signal => { + return translator.translate('hello', {signal}); + }); +}, 'Aborting Translator.translate().'); + +promise_test(async t => { + const translator = + await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); + + const text = 'this string is in English'; + const promises = + [translator.translate(text), translator.measureInputUsage(text)]; + + translator.destroy(); + + promises.push(translator.translate(text), translator.measureInputUsage(text)); + + for (const promise of promises) { + await promise_rejects_dom(t, 'AbortError', promise); + } +}, 'Calling Translator.destroy() aborts calls to translate and measureInputUsage.'); + +promise_test(async t => { + const controller = new AbortController(); + const translator = await createTranslator( + {sourceLanguage: 'en', targetLanguage: 'ja', signal: controller.signal}); + + const text = 'this string is in English'; + const promises = + [translator.translate(text), translator.measureInputUsage(text)]; + + const error = new Error('The create abort signal was aborted.'); + controller.abort(error); + + promises.push(translator.translate(text), translator.measureInputUsage(text)); + + for (const promise of promises) { + await promise_rejects_exactly(t, error, promise); + } +}, 'Translator.create()\'s abort signal destroys its Translator after creation.'); + + +promise_test(async t => { + await testMonitor( + createTranslator, {sourceLanguage: 'en', targetLanguage: 'ja'}); +}, 'Translator.create() notifies its monitor on downloadprogress'); + +promise_test(async t => { + await testCreateMonitorWithAbort( + t, createTranslator, {sourceLanguage: 'en', targetLanguage: 'ja'}); +}, 'Progress events are not emitted after aborted.'); + +promise_test(async t => { + const translator = + await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); + + // Strings containing only white space are not translatable. + const nonTranslatableStrings = ['', ' ', ' ', ' \r\n\t\f']; + + // Strings containing only control characters are not translatable. + for (let c = 0; c < 0x1F; c++) { + nonTranslatableStrings.push(String.fromCharCode(c)); + } + + const translatedNonTranslatableString = await Promise.all( + nonTranslatableStrings.map(str => translator.translate(str))); + + // Non translatable strings should be echoed back + assert_array_equals(translatedNonTranslatableString, nonTranslatableStrings); + + // Adding translatable text makes it translatable. + const translatableStrings = + nonTranslatableStrings.map(str => `Hello ${str} world`); + + const translatedTranslatableString = await Promise.all( + translatableStrings.map(str => translator.translate(str))); + + // All the strings should have been translated in some way. + for (let i = 0; i < translatableStrings.length; i++) { + assert_not_equals(translatedTranslatableString[i], translatableStrings[i]); + } +}, 'Translator.translate() echoes non-translatable content'); + +promise_test(async t => { + const translator = + await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); + + const text = 'hello'; + const inputUsage = await translator.measureInputUsage(text); + + assert_greater_than_equal(translator.inputQuota, 0); + assert_greater_than_equal(inputUsage, 0); + + if (inputUsage < translator.inputQuota) { + assert_equals(await translator.translate(text), 'ã“ã‚“ã«ã¡ã¯'); + } else { + await promise_rejects_dom( + t, 'QuotaExceededError', translator.translate(text)); + } +}, 'Translator.measureInputUsage() and inputQuota basic usage.'); + +promise_test(async t => { + const controller = new AbortController(); + controller.abort(); + + const translator = + await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); + const measureInputUsagePromise = + translator.measureInputUsage('hello', {signal: controller.signal}); + + await promise_rejects_dom(t, 'AbortError', measureInputUsagePromise); +}, 'Translator.measureInputUsage() call with an aborted signal.'); + +promise_test(async t => { + const translator = + await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); + await testAbortPromise(t, signal => { + return translator.measureInputUsage('hello', {signal}); + }); +}, 'Aborting Translator.measureInputUsage().'); diff --git a/tests/wpt/tests/ai/translator/translator_bad_input.tentative.https.any.js b/tests/wpt/tests/ai/translator/translator_bad_input.tentative.https.any.js deleted file mode 100644 index 2879543f29a..00000000000 --- a/tests/wpt/tests/ai/translator/translator_bad_input.tentative.https.any.js +++ /dev/null @@ -1,23 +0,0 @@ -// META: title=translator.create without options do not crash -// META: global=window,worker -// META: timeout=long -// -// Setting `timeout=long` as this test may require downloading the translation -// library and the language models. - -'use strict'; - -promise_test(async t => { - await promise_rejects_js( - t, TypeError, Translator.create(/*empty options*/)); -}, 'Translator.create rejects with TypeError if no options are passed.'); - -promise_test(async t => { - await promise_rejects_js( - t, TypeError, Translator.create({sourceLanguage: 'en'})); -}, 'Translator.create rejects with TypeError targetLanguage is not provided.'); - -promise_test(async t => { - await promise_rejects_js( - t, TypeError, Translator.create({targetLanguage: 'en'})); -}, 'Translator.create rejects with TypeError sourceLanguage is not provided.'); diff --git a/tests/wpt/tests/ai/translator/translator_translate.tentative.https.any.js b/tests/wpt/tests/ai/translator/translator_translate.tentative.https.any.js deleted file mode 100644 index 5a800c00c69..00000000000 --- a/tests/wpt/tests/ai/translator/translator_translate.tentative.https.any.js +++ /dev/null @@ -1,212 +0,0 @@ -// META: title=Translate from English to Japanese -// META: global=window -// META: timeout=long -// META: script=../resources/util.js -// META: script=../resources/language_codes.js -// META: script=/resources/testdriver.js -// -// Setting `timeout=long` as this test may require downloading the translation -// library and the language models. - -'use strict'; - -async function createTranslator(options) { - return await test_driver.bless('Create translator', async () => { - return await Translator.create(options); - }); -} - -promise_test(async t => { - const languagePair = {sourceLanguage: 'en', targetLanguage: 'ja'}; - - // Creating the translator without user activation rejects with - // NotAllowedError. - const createPromise = Translator.create(languagePair); - await promise_rejects_dom(t, 'NotAllowedError', createPromise); - - // Creating the translator with user activation succeeds. - await createTranslator(languagePair); - - // Creating it should have switched it to available. - const availability = await Translator.availability(languagePair); - assert_equals(availability, 'available'); - - // Now that it is available, we should no longer need user activation. - await Translator.create(languagePair); -}, 'Translator.create() requires user activation when availability is "downloadable.'); - -promise_test(async t => { - const translator = - await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); - assert_equals(await translator.translate('hello'), 'ã“ã‚“ã«ã¡ã¯'); -}, 'Simple Translator.translate() call'); - -promise_test(async () => { - const translator = - await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); - const streamingResponse = translator.translateStreaming('hello'); - assert_equals( - Object.prototype.toString.call(streamingResponse), - '[object ReadableStream]'); - let result = ''; - for await (const chunk of streamingResponse) { - result += chunk; - } - assert_equals(await translator.translate('hello'), 'ã“ã‚“ã«ã¡ã¯'); -}, 'Simple Translator.translateStreaming() call'); - -promise_test(async t => { - const translator = - await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); - assert_equals(translator.sourceLanguage, 'en'); - assert_equals(translator.targetLanguage, 'ja'); -}, 'Translator: sourceLanguage and targetLanguage are equal to their respective option passed in to Translator.create.'); - -promise_test(async (t) => { - const translator = - await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); - translator.destroy(); - await promise_rejects_dom( - t, 'InvalidStateError', translator.translate('hello')); -}, 'Translator.translate() fails after destroyed'); - -promise_test(async t => { - const controller = new AbortController(); - controller.abort(); - - const createPromise = createTranslator( - {signal: controller.signal, sourceLanguage: 'en', targetLanguage: 'ja'}); - - await promise_rejects_dom(t, 'AbortError', createPromise); -}, 'Translator.create() call with an aborted signal.'); - -promise_test(async t => { - await testAbortPromise(t, signal => { - return createTranslator( - {signal, sourceLanguage: 'en', targetLanguage: 'ja'}); - }); -}, 'Aborting Translator.create().'); - -promise_test(async t => { - const controller = new AbortController(); - controller.abort(); - - const translator = - await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); - const translatePromise = - translator.translate('hello', {signal: controller.signal}); - - await promise_rejects_dom(t, 'AbortError', translatePromise); -}, 'Translator.translate() call with an aborted signal.'); - -promise_test(async t => { - const translator = - await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); - await testAbortPromise(t, signal => { - return translator.translate('hello', {signal}); - }); -}, 'Aborting Translator.translate().'); - -promise_test(async t => { - let monitorCalled = false; - let createdTranslator = false; - const progressEvents = []; - function monitor(m) { - monitorCalled = true; - - m.addEventListener('downloadprogress', e => { - // No progress events should have been fired after we've created the - // translator. - assert_false(createdTranslator); - - progressEvents.push(e); - }); - } - - await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja', monitor}); - createdTranslator = true; - - // Monitor callback must be called. - assert_true(monitorCalled); - - // Must have at least 2 progress events, one for 0 and one for 1. - assert_greater_than_equal(progressEvents.length, 2); - - // 0 should be the first event and 1 should be the last event. - assert_equals(progressEvents.at(0).loaded, 0); - assert_equals(progressEvents.at(-1).loaded, 1); - - // All progress events must have a total of 1. - for (const progressEvent of progressEvents) { - assert_equals(progressEvent.total, 1); - } -}, 'Translator.create() monitor option is called correctly.'); - -promise_test(async t => { - const translator = - await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); - - // Strings containing only white space are not translatable. - const nonTranslatableStrings = ['', ' ', ' ', ' \r\n\t\f']; - - // Strings containing only control characters are not translatable. - for (let c = 0; c < 0x1F; c++) { - nonTranslatableStrings.push(String.fromCharCode(c)); - } - - const translatedNonTranslatableString = await Promise.all( - nonTranslatableStrings.map(str => translator.translate(str))); - - // Non translatable strings should be echoed back - assert_array_equals(translatedNonTranslatableString, nonTranslatableStrings); - - // Adding translatable text makes it translatable. - const translatableStrings = - nonTranslatableStrings.map(str => `Hello ${str} world`); - - const translatedTranslatableString = await Promise.all( - translatableStrings.map(str => translator.translate(str))); - - // All the strings should have been translated in some way. - for (let i = 0; i < translatableStrings.length; i++) { - assert_not_equals(translatedTranslatableString[i], translatableStrings[i]); - } -}, 'Translator.translate() echos non-translatable content'); - -promise_test(async t => { - const translator = - await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); - - const text = 'hello'; - const inputUsage = await translator.measureInputUsage(text); - - assert_greater_than_equal(translator.inputQuota, 0); - assert_greater_than_equal(inputUsage, 0); - - if (inputUsage < translator.inputQuota) { - assert_equals(await translator.translate(text), 'ã“ã‚“ã«ã¡ã¯'); - } else { - await promise_rejects_dom( - t, 'QuotaExceededError', translator.translate(text)); - } -}, 'Translator.measureInputUsage() and inputQuota basic usage.'); - -promise_test(async t => { - const controller = new AbortController(); - controller.abort(); - - const translator = - await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); - const measureInputUsagePromise = - translator.measureInputUsage('hello', {signal: controller.signal}); - - await promise_rejects_dom(t, 'AbortError', measureInputUsagePromise); -}, 'Translator.measureInputUsage() call with an aborted signal.'); - -promise_test(async t => { - const translator = - await createTranslator({sourceLanguage: 'en', targetLanguage: 'ja'}); - await testAbortPromise(t, signal => { - return translator.measureInputUsage('hello', {signal}); - }); -}, 'Aborting Translator.measureInputUsage().'); diff --git a/tests/wpt/tests/ai/writer/resources/iframe-helper.html b/tests/wpt/tests/ai/writer/resources/iframe-helper.html new file mode 100644 index 00000000000..bca56e1ecfd --- /dev/null +++ b/tests/wpt/tests/ai/writer/resources/iframe-helper.html @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/tests/wpt/tests/ai/writer/writer-abort.tentative.https.window.js b/tests/wpt/tests/ai/writer/writer-abort.tentative.https.window.js new file mode 100644 index 00000000000..be0d50f7102 --- /dev/null +++ b/tests/wpt/tests/ai/writer/writer-abort.tentative.https.window.js @@ -0,0 +1,35 @@ +// META: title=Writer Abort +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async t => { + await testAbortPromise(t, signal => { + return createWriter({signal: signal}); + }); +}, 'Aborting Writer.create()'); + +promise_test(async t => { + const writer = await createWriter(); + await testAbortPromise(t, signal => { + return writer.write(kTestPrompt, { signal: signal }); + }); +}, 'Aborting Writer.write()'); + +promise_test(async t => { + const writer = await createWriter(); + await testAbortReadableStream(t, signal => { + return writer.writeStreaming(kTestPrompt, { signal: signal }); + }); +}, 'Aborting Writer.writeStreaming()'); + +promise_test(async (t) => { + const writer = await createWriter(); + const controller = new AbortController(); + const streamingResponse = writer.writeStreaming( + kTestPrompt, { signal: controller.signal }); + for await (const chunk of streamingResponse); // Do nothing + controller.abort(); +}, 'Aborting Writer.writeStreaming() after finished reading'); diff --git a/tests/wpt/tests/ai/writer/writer-availability-available.tentative.https.window.js b/tests/wpt/tests/ai/writer/writer-availability-available.tentative.https.window.js new file mode 100644 index 00000000000..0e3106a14c2 --- /dev/null +++ b/tests/wpt/tests/ai/writer/writer-availability-available.tentative.https.window.js @@ -0,0 +1,34 @@ +// META: title=Writer Availability Available +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + const availability = await Writer.availability(); + assert_in_array(availability, kAvailableAvailabilities); +}, 'Writer.availability() is available with no options'); + +promise_test(async () => { + const availability = await Writer.availability({ + tone: 'neutral', + format: 'plain-text', + length: 'medium', + expectedInputLanguages: ['en-GB'], + expectedContextLanguages: ['en'], + outputLanguage: 'en', + }); + assert_in_array(availability, kAvailableAvailabilities); +}, 'Writer.availability() returns available with supported options'); + +promise_test(async () => { + const availability = await Writer.availability({ + tone: 'neutral', + format: 'plain-text', + length: 'medium', + expectedInputLanguages: ['es'], // not supported + expectedContextLanguages: ['en'], + outputLanguage: 'es', // not supported + }); + assert_equals(availability, 'unavailable'); +}, 'Writer.availability() returns unavailable for unsupported languages'); diff --git a/tests/wpt/tests/ai/writer/writer-availability.tentative.https.window.js b/tests/wpt/tests/ai/writer/writer-availability.tentative.https.window.js new file mode 100644 index 00000000000..a68b7d35e64 --- /dev/null +++ b/tests/wpt/tests/ai/writer/writer-availability.tentative.https.window.js @@ -0,0 +1,31 @@ +// META: title=Writer Availability +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + assert_true(!!Writer); + assert_equals(typeof Writer.availability, 'function'); +}, 'Writer.availability() is defined'); + +promise_test(async () => { + const availability = await Writer.availability(); + assert_in_array(availability, kValidAvailabilities); +}, 'Writer.availability() returns a valid value with no options'); + +promise_test(async () => { + // An array of plausible test option values. + const kCreateOptionsSpec = [ + {tone: [undefined, 'formal', 'neutral', 'casual']}, + {format: [undefined, 'plain-text', 'markdown']}, + {length: [undefined, 'short', 'medium', 'long']}, + {expectedInputLanguages: [[], ['en'], ['es'], ['jp', 'fr']]}, + {expectedContextLanguages: [[], ['en'], ['es'], ['jp', 'fr']]}, + {outputLanguage: [undefined, 'en', 'es', 'jp', 'fr']} + ]; + for (const options of generateOptionCombinations(kCreateOptionsSpec)) { + const availability = await Writer.availability(options); + assert_in_array(availability, kValidAvailabilities, options); + } +}, 'Writer.availability() returns a valid value with plausible options'); diff --git a/tests/wpt/tests/ai/writer/writer-create-available.tentative.https.window.js b/tests/wpt/tests/ai/writer/writer-create-available.tentative.https.window.js new file mode 100644 index 00000000000..dd9e40d45e8 --- /dev/null +++ b/tests/wpt/tests/ai/writer/writer-create-available.tentative.https.window.js @@ -0,0 +1,97 @@ +// META: title=Writer Create Available +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + const writer = await createWriter(); + assert_equals(typeof writer, 'object'); + + assert_equals(typeof writer.write, 'function'); + assert_equals(typeof writer.writeStreaming, 'function'); + assert_equals(typeof writer.measureInputUsage, 'function'); + assert_equals(typeof writer.destroy, 'function'); + + assert_equals(typeof writer.expectedContextLanguages, 'object'); + assert_equals(typeof writer.expectedInputLanguages, 'object'); + assert_equals(typeof writer.inputQuota, 'number'); + assert_equals(typeof writer.outputLanguage, 'object'); + assert_equals(typeof writer.sharedContext, 'string'); + + assert_equals(typeof writer.tone, 'string'); + assert_equals(typeof writer.format, 'string'); + assert_equals(typeof writer.length, 'string'); + + assert_equals(writer.tone, 'neutral'); + assert_equals(writer.format, 'plain-text'); + assert_equals(writer.length, 'medium'); +}, 'Writer.create() returns a valid object with default options'); + +promise_test(async () => { + await testMonitor(createWriter); +}, 'Writer.create() notifies its monitor on downloadprogress'); + +promise_test(async t => { + await testCreateMonitorWithAbort(t, Writer.create); +}, 'Progress events are not emitted after aborted'); + +promise_test(async () => { + const sharedContext = 'This is a shared context string'; + const writer = await createWriter({sharedContext: sharedContext}); + assert_equals(writer.sharedContext, sharedContext); +}, 'Writer.sharedContext'); + +promise_test(async () => { + const writer = await createWriter({tone: 'formal'}); + assert_equals(writer.tone, 'formal'); +}, 'Creating a Writer with "formal" tone'); + +promise_test(async () => { + const writer = await createWriter({tone: 'casual'}); + assert_equals(writer.tone, 'casual'); +}, 'Creating a Writer with "casual" tone'); + +promise_test(async () => { + const writer = await createWriter({format: 'markdown'}); + assert_equals(writer.format, 'markdown'); +}, 'Creating a Writer with "markdown" format'); + +promise_test(async () => { + const writer = await createWriter({length: 'short'}); + assert_equals(writer.length, 'short'); +}, 'Creating a Writer with "short" length'); + +promise_test(async () => { + const writer = await createWriter({length: 'long'}); + assert_equals(writer.length, 'long'); +}, 'Creating a Writer with "long" length'); + +promise_test(async () => { + const writer = await createWriter({expectedInputLanguages: ['en']}); + assert_array_equals(writer.expectedInputLanguages, ['en']); +}, 'Writer.expectedInputLanguages'); + +promise_test(async () => { + const writer = await createWriter({expectedContextLanguages: ['en']}); + assert_array_equals(writer.expectedContextLanguages, ['en']); +}, 'Writer.expectedContextLanguages'); + +promise_test(async () => { + const writer = await createWriter({outputLanguage: 'en'}); + assert_equals(writer.outputLanguage, 'en'); +}, 'Writer.outputLanguage'); + +promise_test(async (t) => { + promise_rejects_js( + t, RangeError, + createWriter({ expectedInputLanguages: ['en-abc-invalid'] })); +}, 'Creating Writer with malformed language string'); + +promise_test(async () => { + const writer = await createWriter({}); + assert_equals(writer.expectedInputLanguages, null); + assert_equals(writer.expectedContextLanguages, null); + assert_equals(writer.outputLanguage, null); +}, 'Writer optional attributes return null'); diff --git a/tests/wpt/tests/ai/writer/writer-create.tentative.https.window.js b/tests/wpt/tests/ai/writer/writer-create.tentative.https.window.js new file mode 100644 index 00000000000..2f328c8d7cc --- /dev/null +++ b/tests/wpt/tests/ai/writer/writer-create.tentative.https.window.js @@ -0,0 +1,24 @@ +// META: title=Writer Create +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + assert_true(!!Writer); +}, '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"'); diff --git a/tests/wpt/tests/ai/writer/writer-from-detached-iframe.tentative.https.window.js b/tests/wpt/tests/ai/writer/writer-from-detached-iframe.tentative.https.window.js new file mode 100644 index 00000000000..a8fcb6f1264 --- /dev/null +++ b/tests/wpt/tests/ai/writer/writer-from-detached-iframe.tentative.https.window.js @@ -0,0 +1,57 @@ +// META: title=Writer Detached Iframe +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Writer create()', null, iframe.contentWindow); + iframe.contentWindow.Writer.create(); + iframe.remove(); +}, 'Detaching iframe during Writer.create() should not leak memory'); + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Writer create()', null, iframe.contentWindow); + const iframeWindow = iframe.contentWindow; + const iframeDOMException = iframeWindow.DOMException; + const iframeWriter = iframeWindow.Writer; + iframe.remove(); + + await promise_rejects_dom( + t, 'InvalidStateError', iframeDOMException, iframeWriter.create()); +}, 'Writer.create() fails on a detached iframe'); + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Writer create()', null, iframe.contentWindow); + const iframeDOMException = iframe.contentWindow.DOMException; + const writer = await iframe.contentWindow.Writer.create(); + iframe.remove(); + + await promise_rejects_dom( + t, 'InvalidStateError', iframeDOMException, writer.write(kTestPrompt)); +}, 'Writer.write() fails on a detached iframe'); + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Writer create()', null, iframe.contentWindow); + const iframeWindow = iframe.contentWindow; + const iframeDOMException = iframeWindow.DOMException; + const writer = await iframeWindow.Writer.create(); + iframe.remove(); + + assert_throws_dom( + 'InvalidStateError', + iframeDOMException, () => writer.writeStreaming(kTestPrompt)); +}, 'Writer.writeStreaming() fails on a detached iframe'); + +promise_test(async (t) => { + const iframe = document.body.appendChild(document.createElement('iframe')); + await test_driver.bless('Writer create()', null, iframe.contentWindow); + const writer = await iframe.contentWindow.Writer.create(); + writer.write(kTestPrompt); + iframe.remove(); +}, 'Detaching iframe during Writer.write() should not leak memory'); diff --git a/tests/wpt/tests/ai/writer/writer-iframe.tentative.https.html b/tests/wpt/tests/ai/writer/writer-iframe.tentative.https.html new file mode 100644 index 00000000000..004ba9ea307 --- /dev/null +++ b/tests/wpt/tests/ai/writer/writer-iframe.tentative.https.html @@ -0,0 +1,58 @@ + + + + + + + + + + diff --git a/tests/wpt/tests/ai/writer/writer-measureInputUsage.tentative.https.window.js b/tests/wpt/tests/ai/writer/writer-measureInputUsage.tentative.https.window.js new file mode 100644 index 00000000000..e836e8fd9cc --- /dev/null +++ b/tests/wpt/tests/ai/writer/writer-measureInputUsage.tentative.https.window.js @@ -0,0 +1,12 @@ +// META: title=Writer measureInputUsage +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + const writer = await createWriter(); + const result = await writer.measureInputUsage(kTestPrompt); + assert_greater_than(result, 0); +}, 'Writer.measureInputUsage() returns non-empty result'); diff --git a/tests/wpt/tests/ai/writer/writer-write-streaming.tentative.https.window.js b/tests/wpt/tests/ai/writer/writer-write-streaming.tentative.https.window.js new file mode 100644 index 00000000000..52992ad2139 --- /dev/null +++ b/tests/wpt/tests/ai/writer/writer-write-streaming.tentative.https.window.js @@ -0,0 +1,45 @@ +// META: title=Writer Write Streaming +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async () => { + const writer = await createWriter(); + const streamingResponse = + writer.writeStreaming(kTestPrompt, { context: kTestContext }); + assert_equals( + Object.prototype.toString.call(streamingResponse), + '[object ReadableStream]'); + let result = ''; + for await (const chunk of streamingResponse) { + result += chunk; + } + assert_greater_than(result.length, 0); +}, 'Simple Writer.writeStreaming() call'); + +promise_test(async (t) => { + const writer = await createWriter(); + writer.destroy(); + assert_throws_dom('InvalidStateError', () => writer.writeStreaming(kTestPrompt)); +}, 'Writer.writeStreaming() fails after destroyed'); + +promise_test(async t => { + const writer = await createWriter(); + const streamingResponse = writer.writeStreaming(''); + assert_equals( + Object.prototype.toString.call(streamingResponse), + "[object ReadableStream]" + ); + const { result, done } = await streamingResponse.getReader().read(); + assert_true(done); +}, 'Writer.writeStreaming() returns a ReadableStream without any chunk on an empty input'); + +promise_test(async () => { + const writer = await createWriter(); + await Promise.all([ + writer.writeStreaming(kTestPrompt), + writer.writeStreaming(kTestPrompt) + ]); +}, 'Multiple Writer.writeStreaming() calls are resolved successfully'); diff --git a/tests/wpt/tests/ai/writer/writer-write.tentative.https.window.js b/tests/wpt/tests/ai/writer/writer-write.tentative.https.window.js new file mode 100644 index 00000000000..3383ceb9239 --- /dev/null +++ b/tests/wpt/tests/ai/writer/writer-write.tentative.https.window.js @@ -0,0 +1,41 @@ +// META: title=Writer Write +// META: script=/resources/testdriver.js +// META: script=../resources/util.js +// META: timeout=long + +'use strict'; + +promise_test(async (t) => { + const writer = await createWriter(); + let result = await writer.write(''); + assert_equals(result, ''); +}, 'Writer.write() with an empty input returns an empty text'); + +promise_test(async (t) => { + const writer = await createWriter(); + let result = await writer.write(' '); + assert_equals(result, ''); +}, 'Writer.write() with a whitespace input returns an empty text'); + +promise_test(async (t) => { + const writer = await createWriter(); + const result = await writer.write(kTestPrompt, { context: ' ' }); + assert_not_equals(result, ''); +}, 'Writer.write() with a whitespace context returns a non-empty result'); + +promise_test(async (t) => { + const writer = await createWriter(); + writer.destroy(); + await promise_rejects_dom(t, 'InvalidStateError', writer.write(kTestPrompt)); +}, 'Writer.write() fails after destroyed'); + +promise_test(async () => { + const writer = await createWriter(); + const result = await writer.write(kTestPrompt, {context: kTestContext}); + assert_equals(typeof result, 'string'); +}, 'Simple Writer.write() call'); + +promise_test(async () => { + const writer = await createWriter(); + await Promise.all([writer.write(kTestPrompt), writer.write(kTestPrompt)]); +}, 'Multiple Writer.write() calls are resolved successfully'); diff --git a/tests/wpt/tests/ambient-light/AmbientLightSensor-iframe-access.https.html b/tests/wpt/tests/ambient-light/AmbientLightSensor-iframe-access.https.html index 765c1bee1f8..c45804a51f7 100644 --- a/tests/wpt/tests/ambient-light/AmbientLightSensor-iframe-access.https.html +++ b/tests/wpt/tests/ambient-light/AmbientLightSensor-iframe-access.https.html @@ -5,7 +5,7 @@ - + diff --git a/tests/wpt/tests/ambient-light/AmbientLightSensor.https.html b/tests/wpt/tests/ambient-light/AmbientLightSensor.https.html index f2ed655082e..65049ed826f 100644 --- a/tests/wpt/tests/ambient-light/AmbientLightSensor.https.html +++ b/tests/wpt/tests/ambient-light/AmbientLightSensor.https.html @@ -6,7 +6,7 @@ - + diff --git a/tests/wpt/tests/audio-output/setSinkId.https.html b/tests/wpt/tests/audio-output/setSinkId.https.html index be65f0ac81b..662596910ee 100644 --- a/tests/wpt/tests/audio-output/setSinkId.https.html +++ b/tests/wpt/tests/audio-output/setSinkId.https.html @@ -26,14 +26,8 @@ promise_test(async t => { const list = await navigator.mediaDevices.enumerateDevices(); assert_greater_than(list.length, 0, "media device list includes at least one device"); - const audioInputList = list.filter(({kind}) => kind == "audioinput"); const outputDevicesList = list.filter(({kind}) => kind == "audiooutput"); - // List of exposed microphone groupIds - const exposedGroupIds = new Set(audioInputList.map(device => device.groupId)); - for (const { deviceId, groupId } of outputDevicesList) { - assert_true(exposedGroupIds.has(groupId), - "audiooutput device groupId must match an exposed microphone"); assert_greater_than(deviceId.length, 0, "deviceId.length"); const p1 = audio.setSinkId(deviceId); diff --git a/tests/wpt/tests/badging/WEB_FEATURES.yml b/tests/wpt/tests/badging/WEB_FEATURES.yml new file mode 100644 index 00000000000..3c4f69200f6 --- /dev/null +++ b/tests/wpt/tests/badging/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: badging + files: "**" diff --git a/tests/wpt/tests/bluetooth/resources/bluetooth-test.js b/tests/wpt/tests/bluetooth/resources/bluetooth-test.js index bd597efd01b..05981c07e15 100644 --- a/tests/wpt/tests/bluetooth/resources/bluetooth-test.js +++ b/tests/wpt/tests/bluetooth/resources/bluetooth-test.js @@ -370,7 +370,7 @@ function assert_promise_resolves_after_event( } /** - * Returns a promise that resolves after 100ms unless the the event is fired on + * Returns a promise that resolves after 100ms unless the event is fired on * the object in which case the promise rejects. * @param {EventTarget} object The target object to listen for events. * @param {string} event_name The event type to listen for. diff --git a/tests/wpt/tests/clear-site-data/WEB_FEATURES.yml b/tests/wpt/tests/clear-site-data/WEB_FEATURES.yml new file mode 100644 index 00000000000..d55b5faaf11 --- /dev/null +++ b/tests/wpt/tests/clear-site-data/WEB_FEATURES.yml @@ -0,0 +1,3 @@ +features: +- name: clear-site-data + files: "**" diff --git a/tests/wpt/tests/clear-site-data/clear-cache-bfcache.sub.https.html b/tests/wpt/tests/clear-site-data/clear-cache-bfcache.sub.https.html new file mode 100644 index 00000000000..61d810f453b --- /dev/null +++ b/tests/wpt/tests/clear-site-data/clear-cache-bfcache.sub.https.html @@ -0,0 +1,115 @@ + + + +Clear-Site-Data: cache for bfcache + + + + + + + + + diff --git a/tests/wpt/tests/clear-site-data/clear-cache-partitioning.https.html b/tests/wpt/tests/clear-site-data/clear-cache-partitioning.tentative.https.html similarity index 100% rename from tests/wpt/tests/clear-site-data/clear-cache-partitioning.https.html rename to tests/wpt/tests/clear-site-data/clear-cache-partitioning.tentative.https.html diff --git a/tests/wpt/tests/clear-site-data/support/clear-cache-helper.sub.js b/tests/wpt/tests/clear-site-data/support/clear-cache-helper.sub.js index 2b2d35abfc5..5db0caf834c 100644 --- a/tests/wpt/tests/clear-site-data/support/clear-cache-helper.sub.js +++ b/tests/wpt/tests/clear-site-data/support/clear-cache-helper.sub.js @@ -39,7 +39,7 @@ function getUrl(cacheHelper, { } else { // !second_origin && !subdomain url += "{{hosts[][]}}"; } - url += ":{{ports[https][1]}}"; + url += ":{{ports[https][0]}}"; url += "/clear-site-data/support/clear-site-data-cache.py"; url = new URL(url); let params = new URLSearchParams(); @@ -130,4 +130,3 @@ function testCacheClear(test, params, assert) { openTestPageHelper(test, null, testUrls, 0, assert, resolve) }); } - diff --git a/tests/wpt/tests/clear-site-data/support/clear-site-data-prefetchCache.py b/tests/wpt/tests/clear-site-data/support/clear-site-data-prefetchCache.py new file mode 100644 index 00000000000..3a332dd58b2 --- /dev/null +++ b/tests/wpt/tests/clear-site-data/support/clear-site-data-prefetchCache.py @@ -0,0 +1,18 @@ +def main(request, response): + headers = [(b"Content-Type", b"text/html")] + headers += [(b"Clear-Site-Data", b'"prefetchCache"')] + content = f''' + + + {request.url} + ''' + return 200, headers, content diff --git a/tests/wpt/tests/clear-site-data/support/clear-site-data-prerenderCache.py b/tests/wpt/tests/clear-site-data/support/clear-site-data-prerenderCache.py new file mode 100644 index 00000000000..bf845cfb699 --- /dev/null +++ b/tests/wpt/tests/clear-site-data/support/clear-site-data-prerenderCache.py @@ -0,0 +1,18 @@ +def main(request, response): + headers = [(b"Content-Type", b"text/html")] + headers += [(b"Clear-Site-Data", b'"prerenderCache"')] + content = f''' + + + {request.url} + ''' + return 200, headers, content diff --git a/tests/wpt/tests/clipboard-apis/async-navigator-clipboard-basics.https.html b/tests/wpt/tests/clipboard-apis/async-navigator-clipboard-basics.https.html index f7aed80b17e..b71d6665bcb 100644 --- a/tests/wpt/tests/clipboard-apis/async-navigator-clipboard-basics.https.html +++ b/tests/wpt/tests/clipboard-apis/async-navigator-clipboard-basics.https.html @@ -24,56 +24,6 @@ test(() => { assert_equals(navigator.clipboard, navigator.clipboard); }, 'navigator.clipboard exists'); -promise_test(async t => { - await getPermissions(); - const text_plain = "This text was copied using `Clipboard.prototype.write`."; - const html_text = "

Test

"; - await navigator.clipboard.write([ - new ClipboardItem({ - "text/plain": text_plain, - "text/html" : html_text - }), - ]); - }, 'navigator.clipboard.write(DOMString) succeeds'); - -promise_test(async () => { - await getPermissions(); - const promise_text_string = Promise.resolve('hello'); - const promise_html_string = Promise.resolve("

hello

"); - const item = new ClipboardItem({ - 'text/plain': promise_text_string, - 'text/html': promise_html_string - }); - await navigator.clipboard.write([item]); -}, 'navigator.clipboard.write(Promise) succeeds'); - -promise_test(async t => { - await getPermissions(); - const text_plain = 'hello'; - const html_text = "

hello

"; - const image = await fetch("/clipboard-apis/resources/greenbox.png"); - const item = new ClipboardItem({ - 'text/plain': text_plain, - 'text/html': new Blob([html_text], {type: 'text/html'}), - 'image/png': image.blob(), // Promise - 'web text/csv': 'hello,world' - }); - await navigator.clipboard.write([item]); -}, 'navigator.clipboard.write(web_custom_format) succeeds'); - -promise_test(async () => { - await getPermissions(); - const html_text = "

Test

"; - const item = new ClipboardItem({ - 'text/plain': 'hello', - 'text/html': new Blob([html_text], {type: 'text/html'}) - }); - const text = await item.getType('text/plain'); - const blob = await item.getType('text/html'); - assert_true(text instanceof Blob, "item.getType('text/plain') didn't return a Blob"); - assert_true(blob instanceof Blob, "item.getType('text/html') didn't return a Blob"); -}, 'validate GetType(type) on a contructed ClipboardItem returns Blob'); - promise_test(async () => { await getPermissions(); const blob = new Blob(['hello'], {type: 'text/plain'}); diff --git a/tests/wpt/tests/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html b/tests/wpt/tests/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html new file mode 100644 index 00000000000..bf32f4851b3 --- /dev/null +++ b/tests/wpt/tests/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html @@ -0,0 +1,218 @@ + + + + 'clipboardchange' event should be fired upon setting clipboard using JS + + + + + Body needed for test_driver.click() +

+
+ + + + + + + + + + + \ No newline at end of file diff --git a/tests/wpt/tests/clipboard-apis/async-navigator-clipboard-write-domstring.https.html b/tests/wpt/tests/clipboard-apis/async-navigator-clipboard-write-domstring.https.html new file mode 100644 index 00000000000..a16f358f46d --- /dev/null +++ b/tests/wpt/tests/clipboard-apis/async-navigator-clipboard-write-domstring.https.html @@ -0,0 +1,112 @@ + + +Async Clipboard input type validation tests - DOMString input in write API + + +Body needed for test_driver.click() + + + + + + \ No newline at end of file diff --git a/tests/wpt/tests/close-watcher/iframes/dialog-same-origin-nn.html b/tests/wpt/tests/close-watcher/iframes/dialog-same-origin-nn.html new file mode 100644 index 00000000000..b2da2d415a8 --- /dev/null +++ b/tests/wpt/tests/close-watcher/iframes/dialog-same-origin-nn.html @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + diff --git a/tests/wpt/tests/close-watcher/iframes/dialog-same-origin-ynn.html b/tests/wpt/tests/close-watcher/iframes/dialog-same-origin-ynn.html new file mode 100644 index 00000000000..3ab5b758684 --- /dev/null +++ b/tests/wpt/tests/close-watcher/iframes/dialog-same-origin-ynn.html @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + diff --git a/tests/wpt/tests/close-watcher/iframes/dialog-same-origin-ynyn.html b/tests/wpt/tests/close-watcher/iframes/dialog-same-origin-ynyn.html new file mode 100644 index 00000000000..e486fcb3682 --- /dev/null +++ b/tests/wpt/tests/close-watcher/iframes/dialog-same-origin-ynyn.html @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + diff --git a/tests/wpt/tests/close-watcher/iframes/resources/dialog-prevents-close.html b/tests/wpt/tests/close-watcher/iframes/resources/dialog-prevents-close.html new file mode 100644 index 00000000000..580e5220dae --- /dev/null +++ b/tests/wpt/tests/close-watcher/iframes/resources/dialog-prevents-close.html @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/tests/wpt/tests/compat/webkit-box-ignores-flex-wrap.tentative.html b/tests/wpt/tests/compat/webkit-box-ignores-flex-wrap.tentative.html new file mode 100644 index 00000000000..98086f83570 --- /dev/null +++ b/tests/wpt/tests/compat/webkit-box-ignores-flex-wrap.tentative.html @@ -0,0 +1,7 @@ + + + +
+
+
+
diff --git a/tests/wpt/tests/compute-pressure/compute_pressure_basic.https.window.js b/tests/wpt/tests/compute-pressure/compute_pressure_basic.https.window.js index a120f97a403..a13d1766c53 100644 --- a/tests/wpt/tests/compute-pressure/compute_pressure_basic.https.window.js +++ b/tests/wpt/tests/compute-pressure/compute_pressure_basic.https.window.js @@ -21,6 +21,26 @@ pressure_test(async (t) => { return promise_rejects_dom(t, 'NotSupportedError', observer.observe('cpu')); }, 'Return NotSupportedError when calling observer()'); +pressure_test(async (t) => { + await create_virtual_pressure_source('cpu'); + t.add_cleanup(async () => { + await remove_virtual_pressure_source('cpu'); + }); + + const changes = await new Promise((resolve, reject) => { + const observer = new PressureObserver(resolve); + t.add_cleanup(() => observer.disconnect()); + observer.observe('cpu').catch(reject); + update_virtual_pressure_source('cpu', 'critical', 0.5).catch(reject); + }); + assert_equals(1, changes.length); + assert_equals(changes[0].state, 'critical'); + assert_equals(changes[0].source, 'cpu'); + assert_equals(typeof changes[0].time, 'number'); + assert_equals(typeof changes[0].ownContributionEstimate, 'number'); + assert_equals(changes[0].ownContributionEstimate, 0.5); +}, 'Basic functionality test'); + pressure_test(async (t) => { await create_virtual_pressure_source('cpu'); t.add_cleanup(async () => { @@ -37,7 +57,8 @@ pressure_test(async (t) => { assert_equals(changes[0].state, 'critical'); assert_equals(changes[0].source, 'cpu'); assert_equals(typeof changes[0].time, 'number'); -}, 'Basic functionality test'); + assert_equals(changes[0].ownContributionEstimate, null); +}, 'Basic functionality test with no ownContributionEstimate'); pressure_test(async (t) => { await create_virtual_pressure_source('cpu'); diff --git a/tests/wpt/tests/compute-pressure/compute_pressure_duplicate_updates.https.window.js b/tests/wpt/tests/compute-pressure/compute_pressure_duplicate_updates.https.window.js index 231d4afc345..abf53854b40 100644 --- a/tests/wpt/tests/compute-pressure/compute_pressure_duplicate_updates.https.window.js +++ b/tests/wpt/tests/compute-pressure/compute_pressure_duplicate_updates.https.window.js @@ -19,15 +19,15 @@ pressure_test(async (t) => { const syncObserver = new SyncPressureObserver(t); await syncObserver.observer().observe('cpu'); - await update_virtual_pressure_source('cpu', 'critical'); + await update_virtual_pressure_source('cpu', 'critical', 0.2); await syncObserver.waitForUpdate(); assert_equals(syncObserver.changes()[0][0].state, 'critical'); - await update_virtual_pressure_source('cpu', 'critical'); + await update_virtual_pressure_source('cpu', 'critical', 0.2); await new Promise(resolve => {t.step_timeout(resolve, 3000)}); assert_equals(syncObserver.changes().length, 1); - await update_virtual_pressure_source('cpu', 'nominal'); + await update_virtual_pressure_source('cpu', 'nominal'), 0.2; await syncObserver.waitForUpdate(); assert_equals(syncObserver.changes()[1][0].state, 'nominal'); diff --git a/tests/wpt/tests/compute-pressure/resources/worker-support.js b/tests/wpt/tests/compute-pressure/resources/worker-support.js index 39caeb6192f..72e1fbfb204 100644 --- a/tests/wpt/tests/compute-pressure/resources/worker-support.js +++ b/tests/wpt/tests/compute-pressure/resources/worker-support.js @@ -43,8 +43,8 @@ function remove_virtual_pressure_source(source) { return send_message({command: 'remove', params: [source]}); } -function update_virtual_pressure_source(source, state) { - return send_message({command: 'update', params: [source, state]}); +function update_virtual_pressure_source(source, state, estimate) { + return send_message({command: 'update', params: [source, state, estimate]}); } const uuid = new URLSearchParams(location.search).get('uuid'); diff --git a/tests/wpt/tests/container-timing/tentative/containertiming-observe-after-paint-with-buffering.html b/tests/wpt/tests/container-timing/tentative/containertiming-observe-after-paint-with-buffering.html new file mode 100644 index 00000000000..4ceb38afd7d --- /dev/null +++ b/tests/wpt/tests/container-timing/tentative/containertiming-observe-after-paint-with-buffering.html @@ -0,0 +1,44 @@ + + +Container Timing: starting observation after image paint, with buffering, reports paint. + + + + + + + + + diff --git a/tests/wpt/tests/container-timing/tentative/containertiming-observe-after-paint-without-buffering.html b/tests/wpt/tests/container-timing/tentative/containertiming-observe-after-paint-without-buffering.html new file mode 100644 index 00000000000..0191b2ee758 --- /dev/null +++ b/tests/wpt/tests/container-timing/tentative/containertiming-observe-after-paint-without-buffering.html @@ -0,0 +1,43 @@ + + +Container Timing: observe after painting happened without buffering + + + + + + + + + diff --git a/tests/wpt/tests/content-security-policy/connect-src/connect-src-websocket-allowed.sub.html b/tests/wpt/tests/content-security-policy/connect-src/connect-src-websocket-allowed.sub.html index 4263d97fe2d..1d83bdd18e3 100644 --- a/tests/wpt/tests/content-security-policy/connect-src/connect-src-websocket-allowed.sub.html +++ b/tests/wpt/tests/content-security-policy/connect-src/connect-src-websocket-allowed.sub.html @@ -3,7 +3,7 @@ - + connect-src-websocket-blocked @@ -18,9 +18,14 @@ }); try { - var ws = new WebSocket("ws://{{domains[www1]}}:{{ports[http][0]}}/echo"); + var ws = new WebSocket("ws://{{domains[www1]}}:{{ports[ws][0]}}/echo"); - if (ws.readyState == WebSocket.CLOSING || ws.readyState == WebSocket.CLOSED) { + // Not all browsers fail the connection in a synchronous fashion. + if (ws.readyState == WebSocket.CONNECTING) { + setTimeout( function() { + ws.readyState != WebSocket.CLOSED ? log("allowed") : log("blocked"); + }, 1000); + } else if (ws.readyState == WebSocket.CLOSED) { log("blocked"); } else { log("allowed"); diff --git a/tests/wpt/tests/content-security-policy/connect-src/connect-src-websocket-blocked.sub.html b/tests/wpt/tests/content-security-policy/connect-src/connect-src-websocket-blocked.sub.html index 02c52837bb8..2cc3f1b5a05 100644 --- a/tests/wpt/tests/content-security-policy/connect-src/connect-src-websocket-blocked.sub.html +++ b/tests/wpt/tests/content-security-policy/connect-src/connect-src-websocket-blocked.sub.html @@ -18,9 +18,14 @@ }); try { - var ws = new WebSocket("ws://{{domains[www1]}}:{{ports[http][0]}}/echo"); + var ws = new WebSocket("ws://{{domains[www1]}}:{{ports[ws][0]}}/echo"); - if (ws.readyState == WebSocket.CLOSING || ws.readyState == WebSocket.CLOSED) { + // Not all browsers fail the connection in a synchronous fashion. + if (ws.readyState == WebSocket.CONNECTING) { + setTimeout( function() { + ws.readyState != WebSocket.CLOSED ? log("allowed") : log("blocked"); + }, 1000); + } else if (ws.readyState == WebSocket.CLOSED) { log("blocked"); } else { log("allowed"); diff --git a/tests/wpt/tests/content-security-policy/frame-src/frame-src-blocked.sub.html b/tests/wpt/tests/content-security-policy/frame-src/frame-src-blocked.sub.html index a4957f8715c..76fcc2cbb53 100644 --- a/tests/wpt/tests/content-security-policy/frame-src/frame-src-blocked.sub.html +++ b/tests/wpt/tests/content-security-policy/frame-src/frame-src-blocked.sub.html @@ -18,17 +18,17 @@ }, false); function alert_assert(msg) { - t_alert.step(function() { + t_log.step(function() { if (msg.match(/^FAIL/i)) { assert_unreached(msg); - t_alert.done(); + t_log.done(); } for (var i = 0; i < expected_alerts.length; i++) { if (expected_alerts[i] == msg) { assert_equals(expected_alerts[i], msg); expected_alerts.splice(i, 1); if (expected_alerts.length == 0) { - t_alert.done(); + t_log.done(); } return; } diff --git a/tests/wpt/tests/content-security-policy/img-src/icon-blocked.sub.html b/tests/wpt/tests/content-security-policy/img-src/icon-blocked.sub.html index cc882347a1a..4c39e5dec73 100644 --- a/tests/wpt/tests/content-security-policy/img-src/icon-blocked.sub.html +++ b/tests/wpt/tests/content-security-policy/img-src/icon-blocked.sub.html @@ -12,6 +12,7 @@ var t_spv = async_test("Test that spv event is fired"); window.addEventListener("securitypolicyviolation", t_spv.step_func_done(function(e) { assert_equals(e.violatedDirective, 'img-src'); + assert_equals(e.target, document); assert_true(e.blockedURI.endsWith('/support/fail.png')); })); diff --git a/tests/wpt/tests/content-security-policy/img-src/img-src-targeting.html b/tests/wpt/tests/content-security-policy/img-src/img-src-targeting.html new file mode 100644 index 00000000000..3b4fe7c690b --- /dev/null +++ b/tests/wpt/tests/content-security-policy/img-src/img-src-targeting.html @@ -0,0 +1,24 @@ + + + + + + + + +

Check that img-src sets correct target

+ + + + + diff --git a/tests/wpt/tests/content-security-policy/navigation/to-javascript-parent-initiated-child-csp.html b/tests/wpt/tests/content-security-policy/navigation/to-javascript-parent-initiated-child-csp.html index 41945fa460f..41c57c6e046 100644 --- a/tests/wpt/tests/content-security-policy/navigation/to-javascript-parent-initiated-child-csp.html +++ b/tests/wpt/tests/content-security-policy/navigation/to-javascript-parent-initiated-child-csp.html @@ -24,7 +24,7 @@ const kIframeURLPath = "support/frame-with-csp.sub.html"; - // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#unsafe-inline + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy#unsafe-inline document.getElementById("iframeWithScriptSrcUnsafeInline").src = encodeURIWithApostrophes(kIframeURLPath + "?csp=script-src 'unsafe-inline'"); document.getElementById("iframeWithScriptSrcNone").src = diff --git a/tests/wpt/tests/content-security-policy/style-src/style-src-inline-style-with-csstext.html b/tests/wpt/tests/content-security-policy/style-src/style-src-inline-style-with-csstext.html new file mode 100644 index 00000000000..5e812b4aee9 --- /dev/null +++ b/tests/wpt/tests/content-security-policy/style-src/style-src-inline-style-with-csstext.html @@ -0,0 +1,29 @@ + + + + + + + + + + +
+ +
Lorem ipsum
+ + + + + diff --git a/tests/wpt/tests/content-security-policy/tentative/require-sri-for/script-allowed-meta.https.html b/tests/wpt/tests/content-security-policy/tentative/require-sri-for/script-allowed-meta.https.html deleted file mode 100644 index 087498f0c57..00000000000 --- a/tests/wpt/tests/content-security-policy/tentative/require-sri-for/script-allowed-meta.https.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - diff --git a/tests/wpt/tests/content-security-policy/tentative/require-sri-for/script-blocked-meta.https.html b/tests/wpt/tests/content-security-policy/tentative/require-sri-for/script-blocked-meta.https.html deleted file mode 100644 index fe69c61f5b6..00000000000 --- a/tests/wpt/tests/content-security-policy/tentative/require-sri-for/script-blocked-meta.https.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - diff --git a/tests/wpt/tests/content-security-policy/tentative/require-sri-for/script.https.html b/tests/wpt/tests/content-security-policy/tentative/require-sri-for/script.https.html deleted file mode 100644 index 65553e523cc..00000000000 --- a/tests/wpt/tests/content-security-policy/tentative/require-sri-for/script.https.html +++ /dev/null @@ -1,164 +0,0 @@ - - - - - - - - - diff --git a/tests/wpt/tests/content-security-policy/unsafe-eval/eval-blocked-in-about-blank-iframe.html b/tests/wpt/tests/content-security-policy/unsafe-eval/eval-blocked-in-about-blank-iframe.html index 054e75b5274..b2286f56a23 100644 --- a/tests/wpt/tests/content-security-policy/unsafe-eval/eval-blocked-in-about-blank-iframe.html +++ b/tests/wpt/tests/content-security-policy/unsafe-eval/eval-blocked-in-about-blank-iframe.html @@ -19,18 +19,27 @@ const document_loaded = new Promise(resolve => window.onload = resolve); await document_loaded; - const eval_error = new Promise(resolve => { - window.addEventListener('message', function(e) { - assert_not_equals(e.data, 'FAIL', 'eval was executed in the frame'); - if (e.data === 'PASS') - resolve(); + const eval_error = new Promise((resolve, reject) => { + window.addEventListener('message', function(event) { + try { + assert_not_equals(event.data, 'FAIL', 'eval was executed in the frame'); + if (event.data === 'PASS') { + resolve(); + } + } catch (e) { + reject(e); + } }); }); - const csp_violation_report = new Promise(resolve => { - window.addEventListener('message', function(e) { - if (e.data["violated-directive"]) { - assert_equals(e.data["violated-directive"], "script-src"); - resolve(); + const csp_violation_report = new Promise((resolve, reject) => { + window.addEventListener('message', function(event) { + try { + if (event.data["violated-directive"]) { + assert_equals(event.data["violated-directive"], "script-src"); + resolve(); + } + } catch (e) { + reject(e); } }); }); diff --git a/tests/wpt/tests/cookie-store/change_eventhandler_for_already_expired.https.window.js b/tests/wpt/tests/cookie-store/change_eventhandler_for_already_expired.https.window.js index 89d84b13d1b..f3bbe0ea560 100644 --- a/tests/wpt/tests/cookie-store/change_eventhandler_for_already_expired.https.window.js +++ b/tests/wpt/tests/cookie-store/change_eventhandler_for_already_expired.https.window.js @@ -20,3 +20,22 @@ cookie_test(async t => { {deleted: [], changed: [{name: 'alt-cookie', value: 'IGNORE'}]}, 'Deletion not observed after document.cookie sets already-expired cookie'); }, 'CookieStore setting already-expired cookie should not be observed'); + +cookie_test(async t => { + const eventPromise = observeNextCookieChangeEvent(); + await cookieStore.set({ + name: 'cookie', + value: 'ALREADY-EXPIRED', + expires: new Date(new Date() - 10_000), + partitioned: true, + }); + await cookieStore.set('alt-cookie', 'IGNORE'); + assert_equals( + await getCookieString(), + 'alt-cookie=IGNORE', + 'Already-expired cookie not included in CookieStore'); + await verifyCookieChangeEvent( + eventPromise, + {deleted: [], changed: [{name: 'alt-cookie', value: 'IGNORE'}]}, + 'Deletion not observed after document.cookie sets already-expired cookie'); +}, 'CookieStore setting already-expired partitioned cookie should not be observed'); diff --git a/tests/wpt/tests/cookie-store/change_eventhandler_for_document_cookie.https.window.js b/tests/wpt/tests/cookie-store/change_eventhandler_for_document_cookie.https.window.js index 82b0f2baa2b..1937ebdae35 100644 --- a/tests/wpt/tests/cookie-store/change_eventhandler_for_document_cookie.https.window.js +++ b/tests/wpt/tests/cookie-store/change_eventhandler_for_document_cookie.https.window.js @@ -78,6 +78,21 @@ cookie_test(async t => { 'Deletion not observed after document.cookie sets already-expired cookie'); }, 'document.cookie set already-expired cookie should not be observed by CookieStore'); +cookie_test(async t => { + let eventPromise = observeNextCookieChangeEvent(); + await setCookieStringDocument('DOCUMENT-cookie=VALUE; path=/'); + await verifyCookieChangeEvent( + eventPromise, {changed: [{name: 'DOCUMENT-cookie', value: 'VALUE'}]}, + 'Original cookie is observed.'); + + eventPromise = observeNextCookieChangeEvent(); + // Overwrite the original cookie with a duplicate, this should not dispatch an event. + await setCookieStringDocument('DOCUMENT-cookie=VALUE; path=/'); + await setCookieStringDocument('DOCUMENT-alt-cookie=IGNORE; path=/'); + await verifyCookieChangeEvent( + eventPromise, {changed: [{name: 'DOCUMENT-alt-cookie', value: 'IGNORE'}]}, + 'Duplicate cookie is not observed.'); +}, 'document.cookie duplicate cookie should not be observed by CookieStore'); cookie_test(async t => { let eventPromise = observeNextCookieChangeEvent(); diff --git a/tests/wpt/tests/cookie-store/change_eventhandler_for_http_cookie_and_set_cookie_headers.https.window.js b/tests/wpt/tests/cookie-store/change_eventhandler_for_http_cookie_and_set_cookie_headers.https.window.js index 8f5ef1cabbe..8517995acfe 100644 --- a/tests/wpt/tests/cookie-store/change_eventhandler_for_http_cookie_and_set_cookie_headers.https.window.js +++ b/tests/wpt/tests/cookie-store/change_eventhandler_for_http_cookie_and_set_cookie_headers.https.window.js @@ -62,6 +62,22 @@ cookie_test(async t => { 'Deletion not observed after HTTP sets already-expired cookie'); }, 'HTTP set already-expired cookie should not be observed by CookieStore'); +cookie_test(async t => { + let eventPromise = observeNextCookieChangeEvent(); + await setCookieStringHttp('HTTP-cookie=VALUE; path=/'); + await verifyCookieChangeEvent( + eventPromise, {changed: [{name: 'HTTP-cookie', value: 'VALUE'}]}, + 'Original cookie is observed.'); + + eventPromise = observeNextCookieChangeEvent(); + // Overwrite the original cookie with a duplicate, this should not dispatch an event. + await setCookieStringHttp('HTTP-cookie=VALUE; path=/'); + await setCookieStringHttp('HTTP-alt-cookie=IGNORE; path=/'); + await verifyCookieChangeEvent( + eventPromise, {changed: [{name: 'HTTP-alt-cookie', value: 'IGNORE'}]}, + 'Duplicate cookie is not observed.'); +}, 'HTTP duplicate cookie should not be observed by CookieStore'); + cookie_test(async t => { let eventPromise = observeNextCookieChangeEvent(); diff --git a/tests/wpt/tests/cookie-store/change_eventhandler_for_no_change.https.window.js b/tests/wpt/tests/cookie-store/change_eventhandler_for_no_change.https.window.js new file mode 100644 index 00000000000..5f9c2927433 --- /dev/null +++ b/tests/wpt/tests/cookie-store/change_eventhandler_for_no_change.https.window.js @@ -0,0 +1,43 @@ +// META: title=Cookie Store API: Test that setting a duplicate cookie does not fire a second event. +// META: script=resources/cookie-test-helpers.js + +'use strict'; + +cookie_test(async t => { + let eventPromise = observeNextCookieChangeEvent(); + await cookieStore.set('cookie', 'VALUE'); + await verifyCookieChangeEvent( + eventPromise, {changed: [{name: 'cookie', value: 'VALUE'}]}, + 'Original cookie is observed.'); + + eventPromise = observeNextCookieChangeEvent(); + await cookieStore.set('cookie', 'VALUE'); + await cookieStore.set('alt-cookie', 'IGNORE'); + await verifyCookieChangeEvent( + eventPromise, {changed: [{name: 'alt-cookie', value: 'IGNORE'}]}, + 'Duplicate cookie is not observed.'); +}, 'CookieStore duplicate cookie should not be observed'); + +cookie_test(async t => { + let eventPromise = observeNextCookieChangeEvent(); + await cookieStore.set({ + name: 'cookie', + value: 'VALUE', + partitioned: true, + }); + await verifyCookieChangeEvent( + eventPromise, + {changed: [{name: 'cookie', value: 'VALUE', partitioned: true}]}, + 'Original cookie is observed.'); + + eventPromise = observeNextCookieChangeEvent(); + await cookieStore.set({ + name: 'cookie', + value: 'VALUE', + partitioned: true, + }); + await cookieStore.set('alt-cookie', 'IGNORE'); + await verifyCookieChangeEvent( + eventPromise, {changed: [{name: 'alt-cookie', value: 'IGNORE'}]}, + 'Duplicate cookie is not observed.'); +}, 'CookieStore duplicate partitioned cookie should not be observed'); diff --git a/tests/wpt/tests/cookie-store/cookieListItem_attributes.https.any.js b/tests/wpt/tests/cookie-store/cookieListItem_attributes.https.any.js index 6716d91788d..b42a745d3e4 100644 --- a/tests/wpt/tests/cookie-store/cookieListItem_attributes.https.any.js +++ b/tests/wpt/tests/cookie-store/cookieListItem_attributes.https.any.js @@ -161,7 +161,7 @@ promise_test(async testCase => { assert_equals(cookie.name, 'cookie-name'); assert_equals(cookie.value, 'cookie-value'); assert_equals(cookie.domain, null); - assert_equals(cookie.path, currentDirectory + '/'); + assert_equals(cookie.path, currentDirectory); assert_equals(cookie.expires, null); assert_equals(cookie.secure, true); assert_equals(cookie.sameSite, 'strict'); @@ -169,7 +169,7 @@ promise_test(async testCase => { for (const key of kCookieListItemKeys) { assert_in_array(key, itemKeys); } -}, 'CookieListItem - cookieStore.set adds / to path if it does not end with /'); +}, 'CookieListItem - cookieStore.set does not add / to path if it does not end with /'); ['strict', 'lax', 'none'].forEach(sameSiteValue => { promise_test(async testCase => { @@ -207,3 +207,23 @@ promise_test(async testCase => { const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie.secure, true); }, 'CookieListItem - secure defaults to true'); + + +if (self.GLOBAL.isWindow()) { + promise_test(async testCase => { + await cookieStore.delete('cookie-name'); + testCase.add_cleanup(async () => { + await cookieStore.delete('cookie-name'); + }); + + let encodedCookie = encodeURIComponent(JSON.stringify("cookie-name=1; max-age=99999999999999999999999999999; path=/")); + await fetch(`/cookies/resources/cookie.py?set=${encodedCookie}`); + + assert_equals(document.cookie, "cookie-name=1", 'The cookie was set as expected.'); + + const cookie = await cookieStore.get('cookie-name'); + assert_equals(cookie.name, 'cookie-name'); + assert_equals(cookie.value, '1'); + assert_approx_equals(cookie.expires, kFourHundredDaysFromNow, kOneDay); + }, "Test max-age attribute over the 400 days"); +} diff --git a/tests/wpt/tests/cookie-store/cookieStore_delete.sub.https.html b/tests/wpt/tests/cookie-store/cookieStore_delete.sub.https.html new file mode 100644 index 00000000000..8cdfae6776d --- /dev/null +++ b/tests/wpt/tests/cookie-store/cookieStore_delete.sub.https.html @@ -0,0 +1,65 @@ + + +Async Cookies: cookieStore basic API across origins + + + + + + + + diff --git a/tests/wpt/tests/cookie-store/cookieStore_delete_arguments.https.any.js b/tests/wpt/tests/cookie-store/cookieStore_delete_arguments.https.any.js index 37a551b3a9f..2503de0abb0 100644 --- a/tests/wpt/tests/cookie-store/cookieStore_delete_arguments.https.any.js +++ b/tests/wpt/tests/cookie-store/cookieStore_delete_arguments.https.any.js @@ -1,11 +1,14 @@ // META: title=Cookie Store API: cookieStore.delete() arguments +// META: script=resources/cookie-test-helpers.js // META: global=window,serviceworker 'use strict'; promise_test(async testCase => { await cookieStore.set('cookie-name', 'cookie-value'); - + testCase.add_cleanup(async () => { + await setCookieStringHttp(`cookie-name=deleted; Max-Age=0`); + }); await cookieStore.delete('cookie-name'); const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie, null); @@ -14,7 +17,7 @@ promise_test(async testCase => { promise_test(async testCase => { await cookieStore.set('cookie-name', 'cookie-value'); testCase.add_cleanup(async () => { - await cookieStore.delete('cookie-name'); + await setCookieStringHttp(`cookie-name=deleted; Max-Age=0`); }); await cookieStore.delete({ name: 'cookie-name' }); @@ -42,7 +45,7 @@ promise_test(async testCase => { await cookieStore.set( { name: 'cookie-name', value: 'cookie-value', domain: currentDomain }); testCase.add_cleanup(async () => { - await cookieStore.delete({ name: 'cookie-name', domain: currentDomain }); + await setCookieStringHttp(`cookie-name=deleted; Domain=${currentDomain}; Max-Age=0`); }); await cookieStore.delete({ name: 'cookie-name', domain: currentDomain }); @@ -79,7 +82,8 @@ promise_test(async testCase => { await cookieStore.set( { name: 'cookie-name', value: 'cookie-value', path: currentDirectory }); testCase.add_cleanup(async () => { - await cookieStore.delete({ name: 'cookie-name', path: currentDirectory }); + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}; Max-Age=0`); + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}/; Max-Age=0`); }); await cookieStore.delete({ name: 'cookie-name', path: currentDirectory }); @@ -96,7 +100,8 @@ promise_test(async testCase => { await cookieStore.set( { name: 'cookie-name', value: 'cookie-value', path: currentDirectory }); testCase.add_cleanup(async () => { - await cookieStore.delete({ name: 'cookie-name', path: currentDirectory }); + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}; Max-Age=0`); + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}/; Max-Age=0`); }); await cookieStore.delete({ name: 'cookie-name', path: subDirectory }); @@ -109,18 +114,36 @@ promise_test(async testCase => { const currentUrl = new URL(self.location.href); const currentPath = currentUrl.pathname; const currentDirectory = currentPath.substr(0, currentPath.lastIndexOf('/')); - await cookieStore.set( - { name: 'cookie-name', - value: 'cookie-value', - path: currentDirectory + '/' }); + await setCookieStringHttp(`cookie-name=cookie-value; Path=${currentDirectory};`); + testCase.add_cleanup(async () => { - await cookieStore.delete({ name: 'cookie-name', path: currentDirectory }); + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}; Max-Age=0`); + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}/; Max-Age=0`); }); await cookieStore.delete({ name: 'cookie-name', path: currentDirectory }); const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie, null); -}, 'cookieStore.delete with missing / at the end of path'); +}, 'cookieStore.delete does not append / at the end of path'); + +promise_test(async testCase => { + if (typeof self.document === 'undefined') { + // The test is being run from a service worker context where document is undefined + testCase.done(); + return; + } + const currentUrl = new URL(self.location.href); + const currentPath = currentUrl.pathname; + const currentDirectory = currentPath.substr(0, currentPath.lastIndexOf('/')); + await setCookieStringDocument('cookie-name=cookie-value; path=' + currentDirectory); + testCase.add_cleanup(async () => { + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}; Max-Age=0`); + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}/; Max-Age=0`); + }); + await cookieStore.delete({ name: 'cookie-name', path: currentDirectory }); + const cookie = await cookieStore.get('cookie-name'); + assert_equals(cookie, null); +}, 'cookieStore.delete can delete a cookie set by document.cookie if document is defined'); promise_test(async testCase => { const currentUrl = new URL(self.location.href); @@ -136,7 +159,7 @@ promise_test(async testCase => { promise_test(async testCase => { await cookieStore.set('cookie-name', 'cookie-value'); testCase.add_cleanup(async () => { - await cookieStore.delete('cookie-name'); + await setCookieStringHttp(`cookie-name=deleted; Max-Age=0`); }); const cookie_attributes = await cookieStore.get('cookie-name'); @@ -151,7 +174,7 @@ promise_test(async testCase => { promise_test(async testCase => { await cookieStore.set('', 'cookie-value'); testCase.add_cleanup(async () => { - await cookieStore.delete(''); + await setCookieStringHttp(`=deleted; Max-Age=0`); }); await cookieStore.delete(''); @@ -162,10 +185,25 @@ promise_test(async testCase => { promise_test(async testCase => { await cookieStore.set('', 'cookie-value'); testCase.add_cleanup(async () => { - await cookieStore.delete(''); + await setCookieStringHttp(`=deleted; Max-Age=0`); }); await cookieStore.delete({ name: '' }); const cookie = await cookieStore.get(''); assert_equals(cookie, null); }, 'cookieStore.delete with empty name in options'); + +promise_test(async testCase => { + // Cookies having a __Host- prefix are not allowed to specify a domain + await cookieStore.delete('cookie-name'); + + const currentUrl = new URL(self.location.href); + const currentDomain = currentUrl.hostname; + + await promise_rejects_js(testCase, TypeError, cookieStore.delete( + { name: '__Host-cookie-name', + value: 'cookie-value', + domain: currentDomain })); + const cookie = await cookieStore.get('cookie-name'); + assert_equals(cookie, null); +}, 'cookieStore.delete with a __Host- prefix should not have a domain'); diff --git a/tests/wpt/tests/cookie-store/cookieStore_get_set_across_origins.sub.https.html b/tests/wpt/tests/cookie-store/cookieStore_get_set_across_origins.sub.https.html index c67ef98bcc9..004e37630ac 100644 --- a/tests/wpt/tests/cookie-store/cookieStore_get_set_across_origins.sub.https.html +++ b/tests/wpt/tests/cookie-store/cookieStore_get_set_across_origins.sub.https.html @@ -63,4 +63,87 @@ promise_test(async t => { assert_equals(frameCookie.name, 'cookie-name'); assert_equals(frameCookie.value, 'cookie-value'); }, 'cookieStore.get() in cross-origin frame sees cookieStore.set()'); + +promise_test(async t => { + const iframe = await createIframe(kCorsUrl, t); + assert_true(iframe != null); + + document.cookie = "__Host-test=a; Path=/; Secure"; + iframe.contentWindow.postMessage({ + opname: 'set-host-cookie', + name: '__Host-test', + value: 'b', + }, kCorsBase); + + const message = await waitForMessage(); + t.add_cleanup(async () => { + await cookieStore.delete({ name: '__Host-test'}); + }); + + assert_equals(document.cookie, '__Host-test=a'); + + // Cleanup iframe cookie + iframe.contentWindow.postMessage({ + opname: 'delete-host-cookie', + name: '__Host-test', + }, kCorsBase); + await waitForMessage(); +}, 'cookieStore.set() in cross-origin does not overwrite the __Host- cookie'); + +promise_test(async t => { + const iframe = await createIframe(kCorsUrl, t); + assert_true(iframe != null); + + document.cookie = "__Host-test=a; Path=/; Secure"; + await cookieStore.set({name: "__Host-test", value: "a", path: "/"}); + t.add_cleanup(async () => { + await cookieStore.delete({ name: '__Host-test'}); + }); + + iframe.contentWindow.postMessage({ + opname: 'set-host-cookie', + name: '__Host-test', + value: 'b', + }, kCorsBase); + + let message = await waitForMessage(); + + let cookies = await cookieStore.getAll(); + assert_equals(cookies.length, 1); + assert_equals(cookies[0].name, '__Host-test'); + assert_equals(cookies[0].value, 'a'); + + iframe.contentWindow.postMessage({ + opname: 'get-cookie', + name: '__Host-test', + }, kCorsBase); + message = await waitForMessage(); + let { frameCookie } = message; + assert_not_equals(frameCookie, null); + assert_equals(frameCookie.name, '__Host-test'); + assert_equals(frameCookie.value, 'b'); + + // Make sure deleting the cookie doesn't affect the other domain's cookie + await cookieStore.delete({ name: '__Host-test'}); + cookies = await cookieStore.getAll(); + assert_equals(cookies.length, 0); + + iframe.contentWindow.postMessage({ + opname: 'get-cookie', + name: '__Host-test', + }, kCorsBase); + message = await waitForMessage(); + ({ frameCookie } = message); + assert_not_equals(frameCookie, null); + assert_equals(frameCookie.name, '__Host-test'); + assert_equals(frameCookie.value, 'b'); + + // Cleanup iframe cookie + iframe.contentWindow.postMessage({ + opname: 'delete-host-cookie', + name: '__Host-test', + }, kCorsBase); + await waitForMessage(); +}, "__Host- cookies set via cookieStore.set() in same-site domains don't overwrite each other"); + diff --git a/tests/wpt/tests/cookie-store/cookieStore_set_arguments.https.any.js b/tests/wpt/tests/cookie-store/cookieStore_set_arguments.https.any.js index 064fcc5de52..3e9ac5f3e98 100644 --- a/tests/wpt/tests/cookie-store/cookieStore_set_arguments.https.any.js +++ b/tests/wpt/tests/cookie-store/cookieStore_set_arguments.https.any.js @@ -1,4 +1,5 @@ // META: title=Cookie Store API: cookieStore.set() arguments +// META: script=resources/cookie-test-helpers.js // META: global=window,serviceworker 'use strict'; @@ -8,7 +9,7 @@ promise_test(async testCase => { await cookieStore.set('cookie-name', 'cookie-value'); testCase.add_cleanup(async () => { - await cookieStore.delete('cookie-name'); + await setCookieStringHttp(`cookie-name=deleted; Max-Age=0`); }); const cookie = await cookieStore.get('cookie-name'); @@ -21,7 +22,7 @@ promise_test(async testCase => { await cookieStore.set({ name: 'cookie-name', value: 'cookie-value' }); testCase.add_cleanup(async () => { - await cookieStore.delete('cookie-name'); + await setCookieStringHttp(`cookie-name=deleted; Max-Age=0`); }); const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie.name, 'cookie-name'); @@ -42,7 +43,7 @@ promise_test(async testCase => { await cookieStore.delete('cookie-name'); cookieStore.set('cookie-name', 'suspicious-value=resembles-name-and-value'); testCase.add_cleanup(async () => { - await cookieStore.delete('cookie-name'); + await setCookieStringHttp(`cookie-name=deleted; Max-Age=0`); }); const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie.name, 'cookie-name'); @@ -81,7 +82,7 @@ promise_test(async testCase => { value: 'cookie-value', expires: new Date(tenYearsFromNow) }); testCase.add_cleanup(async () => { - await cookieStore.delete('cookie-name'); + await setCookieStringHttp(`cookie-name=deleted; Max-Age=0`); }); const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie.name, 'cookie-name'); @@ -98,7 +99,7 @@ promise_test(async testCase => { value: 'cookie-value', expires: new Date(tenYearsAgo) }); testCase.add_cleanup(async () => { - await cookieStore.delete('cookie-name'); + await setCookieStringHttp(`cookie-name=deleted; Max-Age=0`); }); const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie, null); @@ -112,7 +113,7 @@ promise_test(async testCase => { await cookieStore.set( { name: 'cookie-name', value: 'cookie-value', expires: tenYearsFromNow }); testCase.add_cleanup(async () => { - await cookieStore.delete('cookie-name'); + await setCookieStringHttp(`cookie-name=deleted; Max-Age=0`); }); const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie.name, 'cookie-name'); @@ -127,7 +128,7 @@ promise_test(async testCase => { await cookieStore.set( { name: 'cookie-name', value: 'cookie-value', expires: tenYearsAgo }); testCase.add_cleanup(async () => { - await cookieStore.delete('cookie-name'); + await setCookieStringHttp(`cookie-name=deleted; Max-Age=0`); }); const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie, null); @@ -156,7 +157,7 @@ promise_test(async testCase => { await cookieStore.set( { name: 'cookie-name', value: 'cookie-value', domain: currentDomain }); testCase.add_cleanup(async () => { - await cookieStore.delete({ name: 'cookie-name', domain: currentDomain }); + await setCookieStringHttp(`cookie-name=deleted; Domain=${currentDomain}; Path=/; Max-Age=0`); }); const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie.name, 'cookie-name'); @@ -195,12 +196,12 @@ promise_test(async testCase => { await cookieStore.set('cookie-name', 'cookie-value1'); testCase.add_cleanup(async () => { - await cookieStore.delete('cookie-name'); + await setCookieStringHttp(`cookie-name=deleted; Path=/; Max-Age=0`); }); await cookieStore.set( { name: 'cookie-name', value: 'cookie-value2', domain: currentDomain }); testCase.add_cleanup(async () => { - await cookieStore.delete({ name: 'cookie-name', domain: currentDomain }); + await setCookieStringHttp(`cookie-name=deleted; Domain=${currentDomain}; Path=/; Max-Age=0`); }); const cookies = await cookieStore.getAll('cookie-name'); @@ -224,7 +225,7 @@ promise_test(async testCase => { await cookieStore.set( { name: 'cookie-name', value: 'cookie-value', path: currentDirectory }); testCase.add_cleanup(async () => { - await cookieStore.delete({ name: 'cookie-name', path: currentDirectory }); + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}; Max-Age=0`); }); const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie.name, 'cookie-name'); @@ -243,7 +244,7 @@ promise_test(async testCase => { await cookieStore.set( { name: 'cookie-name', value: 'cookie-value', path: subDirectory }); testCase.add_cleanup(async () => { - await cookieStore.delete({ name: 'cookie-name', path: subDirectory }); + await setCookieStringHttp(`cookie-name=deleted; Path=${subDirectory}; Max-Age=0`); }); const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie, null); @@ -254,12 +255,13 @@ promise_test(async testCase => { await cookieStore.set('cookie-name', 'cookie-old-value'); testCase.add_cleanup(async () => { - await cookieStore.delete('cookie-name'); + await setCookieStringHttp(`cookie-name=deleted; Max-Age=0`); }); + await cookieStore.set( { name: 'cookie-name', value: 'cookie-new-value', path: '/' }); testCase.add_cleanup(async () => { - await cookieStore.delete({ name: 'cookie-name', path: '/' }); + await setCookieStringHttp(`cookie-name=deleted; Path=/; Max-Age=0`); }); const cookies = await cookieStore.getAll('cookie-name'); @@ -277,13 +279,34 @@ promise_test(async testCase => { await cookieStore.set( { name: 'cookie-name', value: 'cookie-value', path: currentDirectory }); testCase.add_cleanup(async () => { - await cookieStore.delete({ name: 'cookie-name', path: currentDirectory }); + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}/; Max-Age=0`); }); + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}; Max-Age=0`); const cookie = await cookieStore.get('cookie-name'); - assert_equals(cookie.name, 'cookie-name'); - assert_equals(cookie.value, 'cookie-value'); - assert_equals(cookie.path, currentDirectory + '/'); -}, 'cookieStore.set adds / to path that does not end with /'); + assert_equals(cookie, null); +}, 'cookieStore.set does not add / to path that does not end with /'); + +promise_test(async testCase => { + if (typeof self.document === 'undefined') { + // The test is being run from a service worker context where document is undefined + testCase.done(); + return; + } + const currentUrl = new URL(self.location.href); + const currentPath = currentUrl.pathname; + const currentDirectory = currentPath.substr(0, currentPath.lastIndexOf('/')); + await setCookieStringDocument('cookie-name=cookie-value; path=' + currentDirectory); + await cookieStore.set( + { name: 'cookie-name', value: 'new-cookie-value', path: currentDirectory }); + testCase.add_cleanup(async () => { + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}; Max-Age=0`); + await setCookieStringHttp(`cookie-name=deleted; Path=${currentDirectory}/; Max-Age=0`); + }); + const cookies = await cookieStore.getAll('cookie-name'); + assert_equals(cookies.length, 1); + assert_equals(cookies[0].name, 'cookie-name'); + assert_equals(cookies[0].value, 'cookie-new-value'); +}, 'cookieStore.set can modify a cookie set by document.cookie if document is defined'); promise_test(async testCase => { const currentUrl = new URL(self.location.href); @@ -299,7 +322,7 @@ promise_test(async testCase => { promise_test(async testCase => { await cookieStore.set('cookie-name', 'old-cookie-value'); testCase.add_cleanup(async () => { - await cookieStore.delete('cookie-name'); + await setCookieStringHttp(`cookie-name=deleted; Max-Age=0`); }); const cookie_attributes = await cookieStore.get('cookie-name'); @@ -336,3 +359,57 @@ promise_test(async testCase => { const cookie = await cookieStore.get('cookie-name'); assert_equals(cookie, null); }, 'cookieStore.set checks if the domain is too long'); + +promise_test(async testCase => { + // Cookies having a __Host- prefix are not allowed to specify a domain + await cookieStore.delete('cookie-name'); + + const currentUrl = new URL(self.location.href); + const currentDomain = currentUrl.hostname; + + await promise_rejects_js(testCase, TypeError, cookieStore.set( + { name: '__Host-cookie-name', + value: 'cookie-value', + domain: currentDomain })); + const cookie = await cookieStore.get('cookie-name'); + assert_equals(cookie, null); +}, 'cookieStore.set with a __Host- prefix should not have a domain'); + +promise_test(async testCase => { + await promise_rejects_js(testCase, TypeError, cookieStore.set( + { name: '', + value: ' ' })); + + await promise_rejects_js(testCase, TypeError, cookieStore.set( + { name: ' ', + value: '' })); + + await promise_rejects_js(testCase, TypeError, cookieStore.set( + { name: ' ', + value: ' ' })); +}, 'cookieStore.set with whitespace only name and value'); + +promise_test(async testCase => { + testCase.add_cleanup(async () => { + await cookieStore.delete('a b'); + }); + await cookieStore.set('a b', 'x y'); + const cookie = await cookieStore.get('a b'); + assert_equals(cookie.value, "x y"); + + await promise_rejects_js(testCase, TypeError, cookieStore.set( + { name: 'a ', + value: 'x' })); + + await promise_rejects_js(testCase, TypeError, cookieStore.set( + { name: ' a', + value: 'x' })); + + await promise_rejects_js(testCase, TypeError, cookieStore.set( + { name: 'a', + value: 'x ' })); + + await promise_rejects_js(testCase, TypeError, cookieStore.set( + { name: 'a', + value: 'x ' })); +}, 'cookieStore.set with whitespace at begining or end'); diff --git a/tests/wpt/tests/cookie-store/cookieStore_special_names.https.any.js b/tests/wpt/tests/cookie-store/cookieStore_special_names.https.any.js index 6b18c4f0668..e2a3df7fe33 100644 --- a/tests/wpt/tests/cookie-store/cookieStore_special_names.https.any.js +++ b/tests/wpt/tests/cookie-store/cookieStore_special_names.https.any.js @@ -62,3 +62,33 @@ promise_test(async testCase => { } assert_true(exceptionThrown, "No exception thrown."); }, 'cookieStore.set with malformed name.'); + +promise_test(async testCase => { + // Nameless cookies cannot have a __Host- prefix + await cookieStore.delete(''); + + const currentUrl = new URL(self.location.href); + const currentDomain = currentUrl.hostname; + + await promise_rejects_js(testCase, TypeError, cookieStore.set( + { name: '', + value: '__Host-nameless-cookie', + domain: `.${currentDomain}` })); + const cookie = await cookieStore.get(''); + assert_equals(cookie, null); +}, 'cookieStore.set a nameless cookie cannot have __Host- prefix'); + +promise_test(async testCase => { + // Nameless cookies cannot have a __Secure- prefix + await cookieStore.delete(''); + + const currentUrl = new URL(self.location.href); + const currentDomain = currentUrl.hostname; + + await promise_rejects_js(testCase, TypeError, cookieStore.set( + { name: '', + value: '__Secure-nameless-cookie', + domain: `.${currentDomain}` })); + const cookie = await cookieStore.get(''); + assert_equals(cookie, null); +}, 'cookieStore.set a nameless cookie cannot have __Secure- prefix'); diff --git a/tests/wpt/tests/cookie-store/httponly_cookies.https.window.js b/tests/wpt/tests/cookie-store/httponly_cookies.https.window.js index 8a10e358ef6..605e94e6744 100644 --- a/tests/wpt/tests/cookie-store/httponly_cookies.https.window.js +++ b/tests/wpt/tests/cookie-store/httponly_cookies.https.window.js @@ -67,3 +67,33 @@ cookie_test(async t => { 'cookie1=value1; cookie2=value2; cookie3=value3', 'httpOnly is not an option for CookieStore.set()'); }, 'HttpOnly cookies can not be set by CookieStore'); + +cookie_test(async t => { + await setCookieStringHttp('HTTPONLY-cookie=value; path=/; httponly'); + assert_equals( + await getCookieString(), + undefined, + 'HttpOnly cookie we wrote using HTTP in cookie jar' + + ' is invisible to script'); + assert_equals( + await getCookieStringHttp(), + 'HTTPONLY-cookie=value', + 'HttpOnly cookie we wrote using HTTP in HTTP cookie jar'); + + try { + await cookieStore.set('HTTPONLY-cookie', 'dummy'); + } catch(e) {} + + assert_equals( + await getCookieString(), + undefined, + 'HttpOnly cookie is not overwritten'); + + try { + await cookieStore.delete('HTTPONLY-cookie'); + } catch(e) {} + + assert_equals(await getCookieString(), undefined, 'HttpOnly cookie is not overwritten'); + + assert_equals(await getCookieStringHttp(), 'HTTPONLY-cookie=value', 'HttpOnly cookie is not deleted'); +}, 'HttpOnly cookies are not deleted/overwritten'); diff --git a/tests/wpt/tests/cookie-store/resources/cookie-test-helpers.js b/tests/wpt/tests/cookie-store/resources/cookie-test-helpers.js index 178947ad6ec..8e23ff2c422 100644 --- a/tests/wpt/tests/cookie-store/resources/cookie-test-helpers.js +++ b/tests/wpt/tests/cookie-store/resources/cookie-test-helpers.js @@ -210,9 +210,10 @@ async function cookie_test(func, description) { // Wipe cookies used by tests before and after the test. async function deleteAllCookies() { - (await cookieStore.getAll()).forEach(({name, value}) => { - cookieStore.delete(name); - }); + await Promise.all((await cookieStore.getAll()).map(async ({name, value}) => { + await cookieStore.delete(name); + await cookieStore.delete({name: name, partitioned: true}); + })); } return promise_test(async t => { diff --git a/tests/wpt/tests/cookie-store/resources/helper_iframe.sub.html b/tests/wpt/tests/cookie-store/resources/helper_iframe.sub.html index 750f19b56d3..d5cae23d160 100644 --- a/tests/wpt/tests/cookie-store/resources/helper_iframe.sub.html +++ b/tests/wpt/tests/cookie-store/resources/helper_iframe.sub.html @@ -22,13 +22,35 @@ domain: '{{host}}', }); event.source.postMessage('Cookie has been set', event.origin); + } else if (opname === 'set-cookie-without-domain-attr') { + const { name, value } = event.data + await cookieStore.set({ + name, + value, + }); + event.source.postMessage('Cookie has been set', event.origin); } else if (opname === 'get-cookie') { const { name, options } = event.data const frameCookie = await cookieStore.get(name, options); event.source.postMessage({frameCookie}, event.origin); + } else if (opname === 'delete-cookie-without-domain-attr') { + const { name } = event.data + await cookieStore.delete({name}); + event.source.postMessage('Cookie has been deleted', event.origin); } else if (opname === 'push-state') { history.pushState("foo", null, "some/path"); event.source.postMessage('pushState called'); + } else if (opname === "set-host-cookie") { + const { name, value } = event.data + await cookieStore.set({ + name, + value, + }); + event.source.postMessage('Cookie has been set', event.origin); + } else if (opname === "delete-host-cookie") { + const { name} = event.data + await cookieStore.delete({ name: name}); + event.source.postMessage('Cookie has been deleted', event.origin); } }); diff --git a/tests/wpt/tests/cookie-store/serviceworker_cookiechange_eventhandler_already_expired.https.any.js b/tests/wpt/tests/cookie-store/serviceworker_cookiechange_eventhandler_already_expired.https.any.js new file mode 100644 index 00000000000..c40fdbeb6f1 --- /dev/null +++ b/tests/wpt/tests/cookie-store/serviceworker_cookiechange_eventhandler_already_expired.https.any.js @@ -0,0 +1,86 @@ +// META: title=Cookie Store API: cookiechange event in ServiceWorker with already-expired cookie. +// META: global=serviceworker + +'use strict'; + +const kScope = '/cookie-store/does/not/exist'; + +function WorkerActivationPromise() { + return new Promise((resolve) => { + if (registration.active) { + resolve(); + return; + } + self.addEventListener('activate', () => { resolve(); }); + }); +} + +function RunOnceCookieChangeReceivedPromise() { + return new Promise(resolve => { + const listener = ev => { + resolve(ev); + self.removeEventListener('cookiechange', listener); + }; + self.addEventListener('cookiechange', listener); + }); +} + +promise_test(async t => { + await WorkerActivationPromise(); + + const subscriptions = [{url: `${kScope}/path`}]; + await registration.cookies.subscribe(subscriptions); + t.add_cleanup(() => registration.cookies.unsubscribe(subscriptions)); + + const eventPromise = RunOnceCookieChangeReceivedPromise(); + + await cookieStore.set({ + name: 'cookie-name', + value: 'already-expired', + expires: new Date(new Date() - 10_000), + }); + + await cookieStore.set('another-cookie-name', 'ignore'); + t.add_cleanup(() => cookieStore.delete('another-cookie-name')); + + const event = await eventPromise; + assert_equals(event.type, 'cookiechange'); + assert_equals(event.changed.length, 1); + assert_equals(event.changed[0].name, 'another-cookie-name'); + assert_equals(event.changed[0].value, 'ignore'); + assert_equals(event.deleted.length, 0); +}); + +promise_test(async t => { + await WorkerActivationPromise(); + + const subscriptions = [{url: `${kScope}/path`}]; + await registration.cookies.subscribe(subscriptions); + t.add_cleanup(() => registration.cookies.unsubscribe(subscriptions)); + + const eventPromise = RunOnceCookieChangeReceivedPromise(); + + await cookieStore.set({ + name: 'cookie-name', + value: 'already-expired', + expires: new Date(new Date() - 10_000), + partitioned: true, + }); + + await cookieStore.set({ + name: 'another-cookie-name', + value: 'ignore', + partitioned: true, + }); + t.add_cleanup(() => cookieStore.delete({ + name: 'another-cookie-name', + partitioned: true, + })); + + const event = await eventPromise; + assert_equals(event.type, 'cookiechange'); + assert_equals(event.changed.length, 1); + assert_equals(event.changed[0].name, 'another-cookie-name'); + assert_equals(event.changed[0].value, 'ignore'); + assert_equals(event.deleted.length, 0); +}); diff --git a/tests/wpt/tests/cookie-store/serviceworker_cookiechange_eventhandler_no_change.https.any.js b/tests/wpt/tests/cookie-store/serviceworker_cookiechange_eventhandler_no_change.https.any.js new file mode 100644 index 00000000000..fb2e2db7381 --- /dev/null +++ b/tests/wpt/tests/cookie-store/serviceworker_cookiechange_eventhandler_no_change.https.any.js @@ -0,0 +1,128 @@ +// META: title=Cookie Store API: cookiechange event in ServiceWorker with already-expired cookie. +// META: global=serviceworker + +'use strict'; + +const kScope = '/cookie-store/does/not/exist'; + +// Resolves when the service worker receives the 'activate' event. +function WorkerActivationPromise() { + return new Promise((resolve) => { + if (registration.active) { + resolve(); + return; + } + self.addEventListener('activate', () => { resolve(); }); + }); +} + +// Resolves when a cookiechange event is received. +function RunOnceCookieChangeReceivedPromise() { + return new Promise(resolve => { + const listener = ev => { + resolve(ev); + self.removeEventListener('cookiechange', listener); + }; + self.addEventListener('cookiechange', listener); + }); +} + +promise_test(async t => { + await WorkerActivationPromise(); + + const subscriptions = [{url: `${kScope}/path`}]; + await registration.cookies.subscribe(subscriptions); + t.add_cleanup(() => registration.cookies.unsubscribe(subscriptions)); + + let cookie_change_promise = RunOnceCookieChangeReceivedPromise(); + + await cookieStore.set('cookie-name', 'value'); + t.add_cleanup(async () => { + await cookieStore.delete('cookie-name'); + }); + + // Observes original cookie. + let event = await cookie_change_promise; + assert_equals(event.type, 'cookiechange'); + assert_equals(event.changed.length, 1); + assert_equals(event.changed[0].name, 'cookie-name'); + assert_equals(event.changed[0].value, 'value'); + assert_equals(event.deleted.length, 0); + + cookie_change_promise = RunOnceCookieChangeReceivedPromise(); + + // Duplicate overwrite should not be observed. + await cookieStore.set('cookie-name', 'value'); + + // This cookie should be observed instead. + await cookieStore.set('alternate-cookie-name', 'ignore'); + t.add_cleanup(async () => { + await cookieStore.delete('alternate-cookie-name'); + }); + + event = await cookie_change_promise; + assert_equals(event.type, 'cookiechange'); + assert_equals(event.changed.length, 1); + assert_equals(event.changed[0].name, 'alternate-cookie-name'); + assert_equals(event.changed[0].value, 'ignore'); + assert_equals(event.deleted.length, 0); +}); + +promise_test(async t => { + await WorkerActivationPromise(); + + const subscriptions = [{url: `${kScope}/path`}]; + await registration.cookies.subscribe(subscriptions); + t.add_cleanup(() => registration.cookies.unsubscribe(subscriptions)); + + let cookie_change_promise = RunOnceCookieChangeReceivedPromise(); + + await cookieStore.set({ + name: 'cookie-name', + value: 'value', + partitioned: true, + }); + t.add_cleanup(async () => { + await cookieStore.delete({ + name: 'cookie-name', + partitioned: true, + }); + }); + + // Observes original cookie. + let event = await cookie_change_promise; + assert_equals(event.type, 'cookiechange'); + assert_equals(event.changed.length, 1); + assert_equals(event.changed[0].name, 'cookie-name'); + assert_equals(event.changed[0].value, 'value'); + assert_equals(event.deleted.length, 0); + + cookie_change_promise = RunOnceCookieChangeReceivedPromise(); + + // Duplicate overwrite should not be observed. + await cookieStore.set({ + name: 'cookie-name', + value: 'value', + partitioned: true, + }); + + // This cookie should instead. + await cookieStore.set({ + name: 'alternate-cookie-name', + value: 'ignore', + partitioned: true, + }); + t.add_cleanup(async () => { + await cookieStore.delete({ + name: 'alternate-cookie-name', + partitioned: true, + }); + }); + + event = await cookie_change_promise; + assert_equals(event.type, 'cookiechange'); + assert_equals(event.changed.length, 1); + assert_equals(event.changed[0].name, 'alternate-cookie-name'); + assert_equals(event.changed[0].value, 'ignore'); + assert_equals(event.deleted.length, 0); +}); diff --git a/tests/wpt/tests/cookies/partitioned-cookies/partitioned-cookies-parallel-iframes.embed.tentative.https.html b/tests/wpt/tests/cookies/partitioned-cookies/partitioned-cookies-parallel-iframes.embed.tentative.https.html index 68de567afee..e92a15bcc29 100644 --- a/tests/wpt/tests/cookies/partitioned-cookies/partitioned-cookies-parallel-iframes.embed.tentative.https.html +++ b/tests/wpt/tests/cookies/partitioned-cookies/partitioned-cookies-parallel-iframes.embed.tentative.https.html @@ -102,6 +102,9 @@ promise_test( async() => { assert_false(iframe2.contentWindow.document.cookie.includes(partitionedCookie), iframe2.contentWindow.document.cookie); + erase_cookie_from_js("partitionedCookie", "Secure; Path=/; SameSite=None; Partitioned"); + erase_cookie_from_js("second", "Secure; Path=/; SameSite=None; Partitioned"); + }, "Partitioned cookies set in same-site contexts are available in other same-site documents."); diff --git a/tests/wpt/tests/cookies/partitioned-cookies/partitioned-cookies-samesite-attribute.https.html b/tests/wpt/tests/cookies/partitioned-cookies/partitioned-cookies-samesite-attribute.https.html new file mode 100644 index 00000000000..323fb08236e --- /dev/null +++ b/tests/wpt/tests/cookies/partitioned-cookies/partitioned-cookies-samesite-attribute.https.html @@ -0,0 +1,59 @@ + + + + + + + + + + + + Test SameSite attribute behavior for partitioned cookies + + + + diff --git a/tests/wpt/tests/cookies/partitioned-cookies/resources/partitioned-cookies-samesite-attributes-embed.html b/tests/wpt/tests/cookies/partitioned-cookies/resources/partitioned-cookies-samesite-attributes-embed.html new file mode 100644 index 00000000000..c6f64639eac --- /dev/null +++ b/tests/wpt/tests/cookies/partitioned-cookies/resources/partitioned-cookies-samesite-attributes-embed.html @@ -0,0 +1,31 @@ + + + +Partitioned cookie SameSite test site embedded in a cross-site context + + + + + + + diff --git a/tests/wpt/tests/credential-management/non-fully-active.https.html b/tests/wpt/tests/credential-management/non-fully-active.https.html index 508b4325d78..a0a76f0fce1 100644 --- a/tests/wpt/tests/credential-management/non-fully-active.https.html +++ b/tests/wpt/tests/credential-management/non-fully-active.https.html @@ -26,7 +26,7 @@ const iframe = document.querySelector("iframe"); // The signal check happens after the fully active check. - // This allows us to confirm the the right error is thrown + // This allows us to confirm the right error is thrown // and in the right order. const controller = new iframe.contentWindow.AbortController(); const signal = controller.signal; diff --git a/tests/wpt/tests/css/CSS2/css1/c414-flt-ln-002.xht b/tests/wpt/tests/css/CSS2/css1/c414-flt-ln-002.xht index 99965439661..ef5448c4893 100644 --- a/tests/wpt/tests/css/CSS2/css1/c414-flt-ln-002.xht +++ b/tests/wpt/tests/css/CSS2/css1/c414-flt-ln-002.xht @@ -30,7 +30,7 @@

Test passes if for each matching pair of colored boxes either:

  • the top of the small box is level with the top of the big box or
  • -
  • the top of the big box is level with the bottom of the the small box +
  • the top of the big box is level with the bottom of the small box and either
    • the small box crosses into the righthand region marked by the yellow box or
    • diff --git a/tests/wpt/tests/css/CSS2/i18n/visuren/direction-unicode-bidi-007.xht b/tests/wpt/tests/css/CSS2/i18n/visuren/direction-unicode-bidi-007.xht index d7f00230f9d..ff45a8cb230 100644 --- a/tests/wpt/tests/css/CSS2/i18n/visuren/direction-unicode-bidi-007.xht +++ b/tests/wpt/tests/css/CSS2/i18n/visuren/direction-unicode-bidi-007.xht @@ -6,7 +6,7 @@ - + + +

      Test passes if there is a filled green square and no red.

      + +
      +
      + + + + + diff --git a/tests/wpt/tests/css/compositing/root-element-background-image-opaque-crash.html b/tests/wpt/tests/css/compositing/root-element-background-image-opaque-crash.html new file mode 100644 index 00000000000..d380310d096 --- /dev/null +++ b/tests/wpt/tests/css/compositing/root-element-background-image-opaque-crash.html @@ -0,0 +1,9 @@ + + diff --git a/tests/wpt/tests/css/css-align/abspos/align-items-static-position-001-ref.tentative.html b/tests/wpt/tests/css/css-align/abspos/align-items-static-position-001-ref.tentative.html new file mode 100644 index 00000000000..22c1fd03e38 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-items-static-position-001-ref.tentative.html @@ -0,0 +1,30 @@ + + +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-items-static-position-001.tentative.html b/tests/wpt/tests/css/css-align/abspos/align-items-static-position-001.tentative.html new file mode 100644 index 00000000000..06c11e6cdb6 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-items-static-position-001.tentative.html @@ -0,0 +1,39 @@ + +Align-items only applies to OOF elements of block elements when statically positioned if it would apply if not OOF. + + + + + + + + +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-items-static-position-002-ref.tentative.html b/tests/wpt/tests/css/css-align/abspos/align-items-static-position-002-ref.tentative.html new file mode 100644 index 00000000000..6f53b041787 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-items-static-position-002-ref.tentative.html @@ -0,0 +1,32 @@ + + + +
      text +
      +
      text +
      + diff --git a/tests/wpt/tests/css/css-align/abspos/align-items-static-position-002.tentative.html b/tests/wpt/tests/css/css-align/abspos/align-items-static-position-002.tentative.html new file mode 100644 index 00000000000..f90004b5672 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-items-static-position-002.tentative.html @@ -0,0 +1,41 @@ + +Align-items doesn't apply to OOF elements of inline elements when statically positioned because they would apply if not OOF. + + + + + + + + + +
      text +
      +
      text +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-items-static-position-ref.html b/tests/wpt/tests/css/css-align/abspos/align-items-static-position-ref.html index 1b36d26bbe1..fd7093f9874 100644 --- a/tests/wpt/tests/css/css-align/abspos/align-items-static-position-ref.html +++ b/tests/wpt/tests/css/css-align/abspos/align-items-static-position-ref.html @@ -53,21 +53,21 @@
      -
      +
      +
      -

      + +
      -
      +
      +
      -
      +
      +
      -
      +
      +
      -

      + +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-items-static-position.html b/tests/wpt/tests/css/css-align/abspos/align-items-static-position.html index 8f3dbb7ae41..0b653ca5d6c 100644 --- a/tests/wpt/tests/css/css-align/abspos/align-items-static-position.html +++ b/tests/wpt/tests/css/css-align/abspos/align-items-static-position.html @@ -59,21 +59,21 @@
      -
      +
      +
      -

      + +
      -
      +
      +
      -
      +
      +
      -
      +
      +
      -

      + +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-001-ref.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-001-ref.html new file mode 100644 index 00000000000..94d0b43b212 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-001-ref.html @@ -0,0 +1,59 @@ + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-001.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-001.html new file mode 100644 index 00000000000..1ff73ef116d --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-001.html @@ -0,0 +1,101 @@ + +Align-self applies to OOF elements of block elements when statically positioned. + + + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-002-ref.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-002-ref.html new file mode 100644 index 00000000000..d8bef63aa29 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-002-ref.html @@ -0,0 +1,59 @@ + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-002.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-002.html new file mode 100644 index 00000000000..4c794bac287 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-002.html @@ -0,0 +1,109 @@ + +Align-self applies to OOF elements of block elements when statically positioned with different writing modes. + + + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-003-ref.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-003-ref.html new file mode 100644 index 00000000000..132bbb7360f --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-003-ref.html @@ -0,0 +1,59 @@ + + + + +
      text +
      +
      +
      text +
      +
      +
      text +
      +
      +
      + +
      text +
      +
      +
      text +
      +
      +
      text +
      +
      +
      + +
      text +
      +
      +
      text +
      +
      +
      text +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-003.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-003.html new file mode 100644 index 00000000000..39f871f4c2b --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-003.html @@ -0,0 +1,110 @@ + +Align-self applies to OOF elements of inline elements when statically positioned. + + + + + + + + + +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      + +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      + +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-004-ref.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-004-ref.html new file mode 100644 index 00000000000..68130ee1d7e --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-004-ref.html @@ -0,0 +1,67 @@ + + + + +
      text +
      +
      +
      text +
      +
      +
      text +
      +
      +
      + +
      text +
      +
      +
      text +
      +
      +
      text +
      +
      +
      + +
      text +
      +
      +
      text +
      +
      +
      text +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-004.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-004.html new file mode 100644 index 00000000000..afba7215b11 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-004.html @@ -0,0 +1,118 @@ + +Align-self applies to OOF elements of inline elements when statically positioned with different writing modes. + + + + + + + + + +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      + +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      + +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-005-ref.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-005-ref.html new file mode 100644 index 00000000000..62597fba605 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-005-ref.html @@ -0,0 +1,21 @@ + + + +
      + hello + hello + world + +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-005.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-005.html new file mode 100644 index 00000000000..207a5443331 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-005.html @@ -0,0 +1,29 @@ + +Align-self applies to OOF elements of inline elements when statically positioned. + + + + + + + + + +
      + hello + hello + world + +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-006-ref.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-006-ref.html new file mode 100644 index 00000000000..5bbafc64845 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-006-ref.html @@ -0,0 +1,48 @@ + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-006.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-006.html new file mode 100644 index 00000000000..253b2a52f61 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-006.html @@ -0,0 +1,64 @@ + +Align-self:self-start/end applies to OOF elements of block elements when statically positioned. + + + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-007-ref.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-007-ref.html new file mode 100644 index 00000000000..bc9b5f4f650 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-007-ref.html @@ -0,0 +1,48 @@ + + + + +
      text +
      +
      +
      text +
      +
      +
      text +
      +
      +
      + +
      text +
      +
      +
      text +
      +
      +
      text +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-007.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-007.html new file mode 100644 index 00000000000..ebe872aaab9 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-007.html @@ -0,0 +1,71 @@ + +Align-self:self-start/end applies to OOF elements of inline elements when statically positioned. + + + + + + + + + +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      + +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      +
      text +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-008-ref.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-008-ref.html new file mode 100644 index 00000000000..79cf612b8f8 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-008-ref.html @@ -0,0 +1,103 @@ + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-static-position-008.html b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-008.html new file mode 100644 index 00000000000..187974884b4 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-static-position-008.html @@ -0,0 +1,110 @@ + +Align-self applies to OOF elements of block elements when statically positioned. + + + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-stretch-auto-margins-aspect-ratio.html b/tests/wpt/tests/css/css-align/abspos/align-self-stretch-auto-margins-aspect-ratio.html new file mode 100644 index 00000000000..347005204e3 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-stretch-auto-margins-aspect-ratio.html @@ -0,0 +1,30 @@ + + + + +

      Test passes if there is a filled green square and no red.

      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/align-self-stretch-auto-margins.html b/tests/wpt/tests/css/css-align/abspos/align-self-stretch-auto-margins.html new file mode 100644 index 00000000000..9d6eb230653 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/align-self-stretch-auto-margins.html @@ -0,0 +1,29 @@ + + + + +

      Test passes if there is a filled green square and no red.

      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/justify-self-stretch-auto-margins-aspect-ratio.html b/tests/wpt/tests/css/css-align/abspos/justify-self-stretch-auto-margins-aspect-ratio.html new file mode 100644 index 00000000000..bc6120584c2 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/justify-self-stretch-auto-margins-aspect-ratio.html @@ -0,0 +1,30 @@ + + + + +

      Test passes if there is a filled green square and no red.

      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/abspos/justify-self-stretch-auto-margins.html b/tests/wpt/tests/css/css-align/abspos/justify-self-stretch-auto-margins.html new file mode 100644 index 00000000000..675321fc2b1 --- /dev/null +++ b/tests/wpt/tests/css/css-align/abspos/justify-self-stretch-auto-margins.html @@ -0,0 +1,29 @@ + + + + +

      Test passes if there is a filled green square and no red.

      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/blocks/justify-items-anonymous.html b/tests/wpt/tests/css/css-align/blocks/justify-items-anonymous.html new file mode 100644 index 00000000000..641dea1f54b --- /dev/null +++ b/tests/wpt/tests/css/css-align/blocks/justify-items-anonymous.html @@ -0,0 +1,10 @@ + + + +

      Test passes if there is a filled green square and no red.

      +
      +
      + +
      +
      +
      diff --git a/tests/wpt/tests/css/css-align/blocks/justify-self-auto-margins-2.html b/tests/wpt/tests/css/css-align/blocks/justify-self-auto-margins-2.html index 33c98d6c1df..e81c4bba6f4 100644 --- a/tests/wpt/tests/css/css-align/blocks/justify-self-auto-margins-2.html +++ b/tests/wpt/tests/css/css-align/blocks/justify-self-auto-margins-2.html @@ -1,8 +1,7 @@ - + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-center-visibility-change.html b/tests/wpt/tests/css/css-anchor-position/anchor-center-visibility-change.html new file mode 100644 index 00000000000..e5559433206 --- /dev/null +++ b/tests/wpt/tests/css/css-anchor-position/anchor-center-visibility-change.html @@ -0,0 +1,89 @@ + + +Tests 'anchor-center' value when target visibility changes (by changing 'display', 'visibility', or popover trigger) + + + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + + diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-ident-function.html b/tests/wpt/tests/css/css-anchor-position/anchor-ident-function.html new file mode 100644 index 00000000000..fbc6fc5560c --- /dev/null +++ b/tests/wpt/tests/css/css-anchor-position/anchor-ident-function.html @@ -0,0 +1,74 @@ + +CSS Anchor Positioning: The ident() function + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      + + diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-in-multicol-crash.html b/tests/wpt/tests/css/css-anchor-position/anchor-in-multicol-crash.html new file mode 100644 index 00000000000..8a4206630a1 --- /dev/null +++ b/tests/wpt/tests/css/css-anchor-position/anchor-in-multicol-crash.html @@ -0,0 +1,46 @@ + +CSS Anchor Positioning: Crash with anchor in multicol + + + + PASS if no crash +
      +
      +
      +
      +
      +
      + diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-in-multicol-inherit-crash.html b/tests/wpt/tests/css/css-anchor-position/anchor-in-multicol-inherit-crash.html new file mode 100644 index 00000000000..233f8382e8a --- /dev/null +++ b/tests/wpt/tests/css/css-anchor-position/anchor-in-multicol-inherit-crash.html @@ -0,0 +1,53 @@ + +CSS Anchor Positioning: Crash with anchor in multicol (inheritance) + + + + PASS if no crash +
      +
      +
      +
      +
      +
      +
      +
      + diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-invalid-fallback.html b/tests/wpt/tests/css/css-anchor-position/anchor-invalid-fallback.html index 254ba185c62..b1a4770de53 100644 --- a/tests/wpt/tests/css/css-anchor-position/anchor-invalid-fallback.html +++ b/tests/wpt/tests/css/css-anchor-position/anchor-invalid-fallback.html @@ -93,6 +93,46 @@ test((t) => { assert_equals(cs.height, '43px'); }, 'Element can use fallback if present'); +test((t) => { + let target = createTarget(t, main); + target.style = ` + /* No position-anchor here */ + left:anchor(right, 8.5%); + top:calc(8.5% + anchor(top, 1px)); + width:anchor-size(width, 21%); + height:calc(21% + anchor-size(height, 1px)); + `; + let cs = getComputedStyle(target); + assert_equals(cs.left, '17px'); + assert_equals(cs.top, '18px'); + assert_equals(cs.width, '42px'); + assert_equals(cs.height, '43px'); +}, 'Element can use fallback with percentage'); + +test((t) => { + let target = createTarget(t, main); + target.style = ` + /* No position-anchor here */ + max-width:anchor-size(width, 28%); + max-height:calc(28% + anchor-size(height, 1px)); + `; + let cs = getComputedStyle(target); + assert_equals(cs.maxWidth, '28%'); + assert_equals(cs.maxHeight, 'calc(28% + 1px)'); +}, 'Element can use fallback for max size with percentage'); + +test((t) => { + let target = createTarget(t, main); + target.style = ` + /* No position-anchor here */ + margin-left:anchor-size(width, 6%); + margin-top:calc(6% + anchor-size(height, 1px)); + `; + let cs = getComputedStyle(target); + assert_equals(cs.marginLeft, '12px'); + assert_equals(cs.marginTop, '13px'); +}, 'Element can use fallback for margin with percentage'); + // Now test that any invalid anchor*() behaves as invalid at computed-value // time if there's no fallback specified. diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-position-005-print-ref.html b/tests/wpt/tests/css/css-anchor-position/anchor-position-005-print-ref.html index 94a1f8e55d3..2c8824bbc35 100644 --- a/tests/wpt/tests/css/css-anchor-position/anchor-position-005-print-ref.html +++ b/tests/wpt/tests/css/css-anchor-position/anchor-position-005-print-ref.html @@ -1,5 +1,10 @@ +

      There should be a green square below, and no red.

      diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-position-005-print.html b/tests/wpt/tests/css/css-anchor-position/anchor-position-005-print.html index eeae841405a..98c663e3918 100644 --- a/tests/wpt/tests/css/css-anchor-position/anchor-position-005-print.html +++ b/tests/wpt/tests/css/css-anchor-position/anchor-position-005-print.html @@ -3,6 +3,11 @@ +

      There should be a green square below, and no red.

      diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-position-grid-001.html b/tests/wpt/tests/css/css-anchor-position/anchor-position-grid-001.html index 92fb4d275b8..7bd389b39d8 100644 --- a/tests/wpt/tests/css/css-anchor-position/anchor-position-grid-001.html +++ b/tests/wpt/tests/css/css-anchor-position/anchor-position-grid-001.html @@ -75,8 +75,8 @@
      -
      -
      +
      +
      diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-002.html b/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-002.html index 7b2691a2b90..1e1f0a5c95b 100644 --- a/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-002.html +++ b/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-002.html @@ -71,15 +71,15 @@
      + data-offset-x=278 data-offset-y=55>
      + data-offset-x=286 data-offset-y=60> diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-004.html b/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-004.html index 399494120ea..8f7a3dad129 100644 --- a/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-004.html +++ b/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-004.html @@ -69,16 +69,16 @@
      + data-offset-x=248 data-offset-y=55>
      + data-offset-x=484 data-offset-y=15> diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html b/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html index 35ab2cfc15c..c5ce41299af 100644 --- a/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html +++ b/tests/wpt/tests/css/css-anchor-position/anchor-position-multicol-nested-001.html @@ -68,8 +68,8 @@ data-expected-width=180 data-expected-height=100> -
      diff --git a/tests/wpt/tests/css/css-anchor-position/anchor-size-parse-valid.html b/tests/wpt/tests/css/css-anchor-position/anchor-size-parse-valid.html index 8e69476512b..312d3c71801 100644 --- a/tests/wpt/tests/css/css-anchor-position/anchor-size-parse-valid.html +++ b/tests/wpt/tests/css/css-anchor-position/anchor-size-parse-valid.html @@ -102,7 +102,7 @@ test_valid_value('width', 'anchor-size(10px)'); for (const prop of ['width', 'max-width', 'margin-left']) { test_valid_value(prop, 'calc((anchor-size(--foo width) + anchor-size(--bar height)) / 2)', 'calc(0.5 * (anchor-size(--foo width) + anchor-size(--bar height)))'); test_valid_value(prop, 'calc(0.5 * (anchor-size(--foo width) + anchor-size(--bar height)))'); - test_valid_value(prop, 'anchor-size(--foo width, calc(anchor-size(--bar height) * 0.5))'); + test_valid_value(prop, 'anchor-size(--foo width, calc(0.5 * anchor-size(--bar height)))'); test_valid_value(prop, 'min(100px, 10%, anchor-size(--foo width), anchor-size(--bar height))'); } diff --git a/tests/wpt/tests/css/css-anchor-position/chrome-419501749-crash.html b/tests/wpt/tests/css/css-anchor-position/chrome-419501749-crash.html new file mode 100644 index 00000000000..2ab29fc87f5 --- /dev/null +++ b/tests/wpt/tests/css/css-anchor-position/chrome-419501749-crash.html @@ -0,0 +1,12 @@ + + + +

      Pass if no crash

      +
      diff --git a/tests/wpt/tests/css/css-anchor-position/position-area-align-justify-wm-dir.html b/tests/wpt/tests/css/css-anchor-position/position-area-align-justify-wm-dir.html index 25b53d44f78..82a11a8868a 100644 --- a/tests/wpt/tests/css/css-anchor-position/position-area-align-justify-wm-dir.html +++ b/tests/wpt/tests/css/css-anchor-position/position-area-align-justify-wm-dir.html @@ -22,7 +22,7 @@ position: absolute; width: 10px; height: 10px; - inset: 10px; + inset: 10px 15px 20px 25px; position-anchor: --anchor; } @@ -41,10 +41,10 @@ }, "Offsets for: " + position_area + " with writing-mode / direction: " + writing_direction); } - const top_left = {left:80, top:80, width:10, height:10}; - const top_right = {left:210, top:80, width:10, height:10}; - const bottom_left = {left:80, top:210, width:10, height:10}; - const bottom_right = {left:210, top:210, width:10, height:10}; + const top_left = {left:75, top:70, width:10, height:10}; + const top_right = {left:225, top:70, width:10, height:10}; + const bottom_left = {left:75, top:210, width:10, height:10}; + const bottom_right = {left:225, top:210, width:10, height:10}; anchored.style.writingMode = "horizontal-tb"; anchored.style.direction = "ltr"; diff --git a/tests/wpt/tests/css/css-anchor-position/position-area-align-justify.html b/tests/wpt/tests/css/css-anchor-position/position-area-align-justify.html index e0bce2c8268..483a9d6ec0a 100644 --- a/tests/wpt/tests/css/css-anchor-position/position-area-align-justify.html +++ b/tests/wpt/tests/css/css-anchor-position/position-area-align-justify.html @@ -22,7 +22,7 @@ position: absolute; width: 10px; height: 10px; - inset: 10px; + inset: 10px 15px 20px 25px; position-anchor: --anchor; } @@ -46,20 +46,20 @@ test_position_area("span-all", {left:145, top:145, width:10, height:10}); // Single region spans - test_position_area("top left", {left:80, top:80, width:10, height:10}); - test_position_area("top center", {left:145, top:80, width:10, height:10}); - test_position_area("top right", {left:210, top:80, width:10, height:10}); - test_position_area("center left", {left:80, top:145, width:10, height:10}); - test_position_area("center center", {left:145, top:145, width:10, height:10}); - test_position_area("center right", {left:210, top:145, width:10, height:10}); - test_position_area("bottom left", {left:80, top:210, width:10, height:10}); - test_position_area("bottom center", {left:145, top:210, width:10, height:10}); - test_position_area("bottom right", {left:210, top:210, width:10, height:10}); + test_position_area("top left", {left:75, top:70, width:10, height:10}); + test_position_area("top center", {left:150, top:70, width:10, height:10}); + test_position_area("top right", {left:225, top:70, width:10, height:10}); + test_position_area("center left", {left:75, top:140, width:10, height:10}); + test_position_area("center center", {left:150, top:140, width:10, height:10}); + test_position_area("center right", {left:225, top:140, width:10, height:10}); + test_position_area("bottom left", {left:75, top:210, width:10, height:10}); + test_position_area("bottom center", {left:150, top:210, width:10, height:10}); + test_position_area("bottom right", {left:225, top:210, width:10, height:10}); // Multi-region spans - test_position_area("top span-left", {left:180, top:80, width:10, height:10}); - test_position_area("top span-right", {left:110, top:80, width:10, height:10}); - test_position_area("span-top left", {left:80, top:180, width:10, height:10}); - test_position_area("span-bottom left", {left:80, top:110, width:10, height:10}); + test_position_area("top span-left", {left:175, top:70, width:10, height:10}); + test_position_area("top span-right", {left:125, top:70, width:10, height:10}); + test_position_area("span-top left", {left:75, top:170, width:10, height:10}); + test_position_area("span-bottom left", {left:75, top:110, width:10, height:10}); diff --git a/tests/wpt/tests/css/css-anchor-position/position-area-in-grid.html b/tests/wpt/tests/css/css-anchor-position/position-area-in-grid.html index 85dd4f48306..3407fcbc351 100644 --- a/tests/wpt/tests/css/css-anchor-position/position-area-in-grid.html +++ b/tests/wpt/tests/css/css-anchor-position/position-area-in-grid.html @@ -56,6 +56,7 @@ position: relative; width: 400px; height: 400px; + outline: 1px solid; } #anchor { @@ -65,6 +66,7 @@ width: 150px; height: 75px; anchor-name: --anchor; + background: blue; } #anchored { @@ -76,6 +78,7 @@ align-self: stretch; justify-self: stretch; position-anchor: --anchor; + border: solid orange; } @@ -99,8 +102,8 @@ } test_position_area("span-bottom span-left", {left:"auto", right:"auto", top:"auto", bottom:"auto"}, - {left:200, top:150, width:50, height:150}); + {left:100, top:150, width:150, height:150}); test_position_area("span-bottom span-left", {left:"10px", right:"10px", top:"10px", bottom:"10px"}, - {left:210, top:160, width:30, height:130}); + {left:110, top:160, width:130, height:130}); diff --git a/tests/wpt/tests/css/css-anchor-position/position-area-scrolling-006.html b/tests/wpt/tests/css/css-anchor-position/position-area-scrolling-006.html index 0d49a030359..fb478385ef3 100644 --- a/tests/wpt/tests/css/css-anchor-position/position-area-scrolling-006.html +++ b/tests/wpt/tests/css/css-anchor-position/position-area-scrolling-006.html @@ -16,16 +16,11 @@ height: 50%; background: cyan; } - #container.thicker > .pos { - border-width: thick; - }
      -
      -
      -
      -
      -
      +
      +
      +
      + + + + + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      +
      +
      +
      + + + + + \ No newline at end of file diff --git a/tests/wpt/tests/css/css-anchor-position/position-try-ident-function.html b/tests/wpt/tests/css/css-anchor-position/position-try-ident-function.html new file mode 100644 index 00000000000..4457c4f14f8 --- /dev/null +++ b/tests/wpt/tests/css/css-anchor-position/position-try-ident-function.html @@ -0,0 +1,23 @@ + +CSS Anchor Positioning: The ident() function in @position-try/position-try + + + + + + +
      + diff --git a/tests/wpt/tests/css/css-anchor-position/position-visibility-no-overflow-without-anchor.html b/tests/wpt/tests/css/css-anchor-position/position-visibility-no-overflow-without-anchor.html new file mode 100644 index 00000000000..01f4d97947b --- /dev/null +++ b/tests/wpt/tests/css/css-anchor-position/position-visibility-no-overflow-without-anchor.html @@ -0,0 +1,36 @@ + + +CSS Anchor Positioning Test: position-visibility: no-overflow without an anchor + + + + +
      +
      anchor1
      + +
      target1
      +
      diff --git a/tests/wpt/tests/css/css-anchor-position/reference/position-area-visibility-change-ref.html b/tests/wpt/tests/css/css-anchor-position/reference/position-area-visibility-change-ref.html new file mode 100644 index 00000000000..87e18049409 --- /dev/null +++ b/tests/wpt/tests/css/css-anchor-position/reference/position-area-visibility-change-ref.html @@ -0,0 +1,72 @@ + + + + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      +
      +
      +
      + \ No newline at end of file diff --git a/tests/wpt/tests/css/css-anchor-position/remove-position-try-rules-001.html b/tests/wpt/tests/css/css-anchor-position/remove-position-try-rules-001.html new file mode 100644 index 00000000000..56ad5cf7b32 --- /dev/null +++ b/tests/wpt/tests/css/css-anchor-position/remove-position-try-rules-001.html @@ -0,0 +1,59 @@ + +Remove current fallback @position-try rules + + + + + +
      +
      + + + + + diff --git a/tests/wpt/tests/css/css-anchor-position/vertical-aligned-table-cell.html b/tests/wpt/tests/css/css-anchor-position/vertical-aligned-table-cell.html new file mode 100644 index 00000000000..9b16621b713 --- /dev/null +++ b/tests/wpt/tests/css/css-anchor-position/vertical-aligned-table-cell.html @@ -0,0 +1,17 @@ + +Anchor inside table cell with non-top alignment + + + + + +

      Test passes if there is a filled green square and no red.

      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-animations/crashtests/chrome-bug-405795970.html b/tests/wpt/tests/css/css-animations/crashtests/chrome-bug-405795970.html new file mode 100644 index 00000000000..5b45527fd9c --- /dev/null +++ b/tests/wpt/tests/css/css-animations/crashtests/chrome-bug-405795970.html @@ -0,0 +1,17 @@ + + + +

      Pass if no crash

      +
      diff --git a/tests/wpt/tests/css/css-animations/crashtests/chrome-bug-415627003.html b/tests/wpt/tests/css/css-animations/crashtests/chrome-bug-415627003.html new file mode 100644 index 00000000000..af942fbcc97 --- /dev/null +++ b/tests/wpt/tests/css/css-animations/crashtests/chrome-bug-415627003.html @@ -0,0 +1,17 @@ + + + +

      Pass if no crash

      +
      diff --git a/tests/wpt/tests/css/css-animations/parsing/animation-range-start-valid.html b/tests/wpt/tests/css/css-animations/parsing/animation-range-start-valid.html index bb1dbe35c04..311df59b51b 100644 --- a/tests/wpt/tests/css/css-animations/parsing/animation-range-start-valid.html +++ b/tests/wpt/tests/css/css-animations/parsing/animation-range-start-valid.html @@ -29,5 +29,5 @@ test_valid_value("animation-range-start", "cover"); test_valid_value("animation-range-start", "contain"); test_valid_value("animation-range-start", "entry"); test_valid_value("animation-range-start", "exit"); -test_valid_value("animation-range-start", "cover calc(sign(100em - 1px) * 1%)"); +test_valid_value("animation-range-start", "cover calc(sign(100em - 1px) * 1%)", "cover calc(1% * sign(100em - 1px))"); diff --git a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-end-computed.html b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-end-computed.html index d2baab5b2a5..c1aa5d16c5a 100644 --- a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-end-computed.html +++ b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-end-computed.html @@ -1,12 +1,12 @@ - - +
      @@ -18,4 +17,5 @@ test_invalid_value("animation-trigger-exit-range-end", "50% exit"); test_invalid_value("animation-trigger-exit-range-end", "contain contain"); test_invalid_value("animation-trigger-exit-range-end", "none"); test_invalid_value("animation-trigger-exit-range-end", "cover 50% enter 50%"); +test_invalid_value("animation-trigger-exit-range-end", "auto contain"); diff --git a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-end-valid.html b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-end-valid.html index 9a41889d339..04ea80de236 100644 --- a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-end-valid.html +++ b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-end-valid.html @@ -1,11 +1,11 @@ - - + trigger- + diff --git a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-shorthand.html b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-shorthand.html index 5ddc0682ea8..1f1c9496c0d 100644 --- a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-shorthand.html +++ b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-shorthand.html @@ -1,7 +1,6 @@ animation-trigger-exit-range shorthand - - + @@ -18,6 +17,14 @@ test_valid_value("animation-trigger-exit-range", "entry-crossing"); test_valid_value("animation-trigger-exit-range", "exit"); test_valid_value("animation-trigger-exit-range", "exit-crossing"); test_valid_value("animation-trigger-exit-range", "entry, exit"); +test_valid_value("animation-trigger-exit-range", "auto"); +test_valid_value("animation-trigger-exit-range", "auto auto", "auto"); +test_valid_value("animation-trigger-exit-range", "cover auto"); +test_valid_value("animation-trigger-exit-range", "cover 10% auto"); +test_valid_value("animation-trigger-exit-range", "auto cover"); +test_valid_value("animation-trigger-exit-range", "auto cover 90%"); +test_valid_value("animation-trigger-exit-range", "normal auto", "normal"); +test_valid_value("animation-trigger-exit-range", "auto normal", "auto"); test_valid_value("animation-trigger-exit-range", "entry 0% entry 100%", "entry"); test_valid_value("animation-trigger-exit-range", "entry-crossing 0% entry-crossing 100%", @@ -110,9 +117,14 @@ test_invalid_value("animation-trigger-exit-range", "thing 100%"); test_invalid_value("animation-trigger-exit-range", "thing 100px"); test_invalid_value("animation-trigger-exit-range", "100% thing"); +test_shorthand_value('animation-trigger-exit-range', 'auto', { + 'animation-trigger-exit-range-start': 'auto', + 'animation-trigger-exit-range-end': 'auto', +}); + test_shorthand_value('animation-trigger-exit-range', 'normal', { 'animation-trigger-exit-range-start': 'normal', - 'animation-trigger-exit-range-end': 'normal', + 'animation-trigger-exit-range-end': 'auto', }); test_shorthand_value('animation-trigger-exit-range', 'normal normal', { @@ -120,6 +132,16 @@ test_shorthand_value('animation-trigger-exit-range', 'normal normal', { 'animation-trigger-exit-range-end': 'normal', }); +test_shorthand_value('animation-trigger-exit-range', 'normal auto', { + 'animation-trigger-exit-range-start': 'normal', + 'animation-trigger-exit-range-end': 'auto', +}); + +test_shorthand_value('animation-trigger-exit-range', 'auto normal', { + 'animation-trigger-exit-range-start': 'auto', + 'animation-trigger-exit-range-end': 'normal', +}); + test_shorthand_value('animation-trigger-exit-range', 'normal entry 100%', { 'animation-trigger-exit-range-start': 'normal', 'animation-trigger-exit-range-end': 'entry', @@ -171,10 +193,10 @@ test_shorthand_value('animation-trigger-exit-range', 'exit calc(10% + 50px)', { }); test_shorthand_value('animation-trigger-exit-range', '100px', { 'animation-trigger-exit-range-start': '100px', - 'animation-trigger-exit-range-end': 'normal', + 'animation-trigger-exit-range-end': 'auto', }); test_shorthand_value('animation-trigger-exit-range', '10%', { 'animation-trigger-exit-range-start': '10%', - 'animation-trigger-exit-range-end': 'normal', + 'animation-trigger-exit-range-end': 'auto', }); diff --git a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-start-computed.html b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-start-computed.html index a48e15a578d..b8f5bd87738 100644 --- a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-start-computed.html +++ b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-start-computed.html @@ -6,7 +6,9 @@
      @@ -14,4 +13,5 @@ test_invalid_value("animation-trigger-exit-range-start", "50% exit"); test_invalid_value("animation-trigger-exit-range-start", "contain contain"); test_invalid_value("animation-trigger-exit-range-start", "none"); test_invalid_value("animation-trigger-exit-range-start", "cover 50% enter 50%"); +test_invalid_value("animation-trigger-exit-range-start", "auto contain"); diff --git a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-start-valid.html b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-start-valid.html index 11d1adb26f8..881ad833c4b 100644 --- a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-start-valid.html +++ b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-exit-range-start-valid.html @@ -6,6 +6,7 @@ diff --git a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-range-end-invalid.html b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-range-end-invalid.html index 8d66ac661d5..30924969482 100644 --- a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-range-end-invalid.html +++ b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-range-end-invalid.html @@ -1,6 +1,5 @@ - - + diff --git a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-range-end-valid.html b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-range-end-valid.html index 6d9533e2648..1b72ef2b60d 100644 --- a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-range-end-valid.html +++ b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-range-end-valid.html @@ -1,6 +1,5 @@ - - + diff --git a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-range-start-valid.html b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-range-start-valid.html index d48151b6db0..6a091fa4d07 100644 --- a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-range-start-valid.html +++ b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-range-start-valid.html @@ -30,5 +30,5 @@ test_valid_value("animation-trigger-range-start", "cover"); test_valid_value("animation-trigger-range-start", "contain"); test_valid_value("animation-trigger-range-start", "entry"); test_valid_value("animation-trigger-range-start", "exit"); -test_valid_value("animation-trigger-range-start", "cover calc(sign(100em - 1px) * 1%)"); +test_valid_value("animation-trigger-range-start", "cover calc(sign(100em - 1px) * 1%)", "cover calc(1% * sign(100em - 1px))"); diff --git a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-shorthand.tentative.html b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-shorthand.tentative.html index d0b6befea8e..784790e750c 100644 --- a/tests/wpt/tests/css/css-animations/parsing/animation-trigger-shorthand.tentative.html +++ b/tests/wpt/tests/css/css-animations/parsing/animation-trigger-shorthand.tentative.html @@ -66,8 +66,8 @@ 'animation-trigger-timeline': 'view()', 'animation-trigger-range-start': 'contain', 'animation-trigger-range-end': 'contain', - 'animation-trigger-exit-range-start': 'normal', - 'animation-trigger-exit-range-end': 'normal', + 'animation-trigger-exit-range-start': 'auto', + 'animation-trigger-exit-range-end': 'auto', }); test_shorthand_value('animation-trigger', 'view() alternate contain 20% contain 80%', @@ -76,8 +76,8 @@ 'animation-trigger-timeline': 'view()', 'animation-trigger-range-start': 'contain 20%', 'animation-trigger-range-end': 'contain 80%', - 'animation-trigger-exit-range-start': 'normal', - 'animation-trigger-exit-range-end': 'normal', + 'animation-trigger-exit-range-start': 'auto', + 'animation-trigger-exit-range-end': 'auto', }); test_shorthand_value('animation-trigger', 'view() alternate contain 0%', @@ -86,8 +86,8 @@ 'animation-trigger-timeline': 'view()', 'animation-trigger-range-start': 'contain', 'animation-trigger-range-end': 'contain', - 'animation-trigger-exit-range-start': 'normal', - 'animation-trigger-exit-range-end': 'normal', + 'animation-trigger-exit-range-start': 'auto', + 'animation-trigger-exit-range-end': 'auto', }); test_shorthand_value('animation-trigger', 'view() alternate contain 10%', @@ -96,8 +96,8 @@ 'animation-trigger-timeline': 'view()', 'animation-trigger-range-start': 'contain 10%', 'animation-trigger-range-end': 'contain', - 'animation-trigger-exit-range-start': 'normal', - 'animation-trigger-exit-range-end': 'normal', + 'animation-trigger-exit-range-start': 'auto', + 'animation-trigger-exit-range-end': 'auto', }); test_shorthand_value('animation-trigger', 'view() alternate contain', @@ -106,8 +106,8 @@ 'animation-trigger-timeline': 'view()', 'animation-trigger-range-start': 'contain', 'animation-trigger-range-end': 'contain', - 'animation-trigger-exit-range-start': 'normal', - 'animation-trigger-exit-range-end': 'normal', + 'animation-trigger-exit-range-start': 'auto', + 'animation-trigger-exit-range-end': 'auto', }); test_shorthand_value('animation-trigger', 'view() alternate', { @@ -115,8 +115,8 @@ 'animation-trigger-timeline': 'view()', 'animation-trigger-range-start': 'normal', 'animation-trigger-range-end': 'normal', - 'animation-trigger-exit-range-start': 'normal', - 'animation-trigger-exit-range-end': 'normal', + 'animation-trigger-exit-range-start': 'auto', + 'animation-trigger-exit-range-end': 'auto', }); test_shorthand_value('animation-trigger', 'repeat', { @@ -124,8 +124,8 @@ 'animation-trigger-timeline': 'auto', 'animation-trigger-range-start': 'normal', 'animation-trigger-range-end': 'normal', - 'animation-trigger-exit-range-start': 'normal', - 'animation-trigger-exit-range-end': 'normal', + 'animation-trigger-exit-range-start': 'auto', + 'animation-trigger-exit-range-end': 'auto', }); test_computed_value('animation-trigger', 'view() once contain 0% contain 100% cover 0% cover 100%', @@ -140,8 +140,8 @@ 'view() once contain 20% contain 80% cover 10% cover'); test_computed_value('animation-trigger', 'view() once contain 0% contain 100%', - 'view() once contain contain normal normal'); + 'view() once contain contain auto auto'); test_computed_value('animation-trigger', 'view() once', - 'view() once normal normal normal normal'); + 'view() once normal normal auto auto'); diff --git a/tests/wpt/tests/css/css-animations/parsing/keyframe-selectors.html b/tests/wpt/tests/css/css-animations/parsing/keyframe-selectors.html new file mode 100644 index 00000000000..900cba5a05a --- /dev/null +++ b/tests/wpt/tests/css/css-animations/parsing/keyframe-selectors.html @@ -0,0 +1,45 @@ + +CSS Animations Test: Parse tests for keyframe selectors + + + + + + diff --git a/tests/wpt/tests/css/css-backgrounds/border-image-width-005.xht b/tests/wpt/tests/css/css-backgrounds/border-image-width-005.xht index eeceedef90e..1e93b7219d4 100644 --- a/tests/wpt/tests/css/css-backgrounds/border-image-width-005.xht +++ b/tests/wpt/tests/css/css-backgrounds/border-image-width-005.xht @@ -34,7 +34,7 @@ border-width: 0px; /* so the border belt (or border area) of the element is 0px tall and 0px wide */ border-image-source: url("support/outline-5px-10px-15px-20px-green.png"); border-image-slice: 5% 10% 15% 20%; /* Percentages are relative to the size of the image: the width of the image for the horizontal offsets, the height for vertical offsets. */ - border-image-width: 50px; /* The four values of ‘border-image-width’ specify offsets that are used to divide the border image area into nine parts. They represent inward distances from the the top, right, bottom, and left sides of the area, respectively. */ + border-image-width: 50px; /* The four values of ‘border-image-width’ specify offsets that are used to divide the border image area into nine parts. They represent inward distances from the top, right, bottom, and left sides of the area, respectively. */ border-image-outset: 25px 25px 25px 25px; /* The border-image-outset values specify the amount by which the border image area *_ extends beyond the border box_*. */ height: 20px; margin-left: 25px; /* == border-image-outset . This margin-left value is just to position the rendered layout to accurately match the reference file . */ diff --git a/tests/wpt/tests/css/css-backgrounds/border-image-width-006.xht b/tests/wpt/tests/css/css-backgrounds/border-image-width-006.xht index aa487b76c53..bd046c25876 100644 --- a/tests/wpt/tests/css/css-backgrounds/border-image-width-006.xht +++ b/tests/wpt/tests/css/css-backgrounds/border-image-width-006.xht @@ -34,7 +34,7 @@ border-width: 0px; /* so the border belt (or border area) of the element is 0px tall and 0px wide */ border-image-source: url("support/outline-5px-10px-15px-20px-green.png"); border-image-slice: 5% 10% 15% 20%; /* Percentages are relative to the size of the image: the width of the image for the horizontal offsets, the height for vertical offsets. */ - border-image-width: 50px; /* The four values of ‘border-image-width’ specify offsets that are used to divide the border image area into nine parts. They represent inward distances from the the top, right, bottom, and left sides of the area, respectively. */ + border-image-width: 50px; /* The four values of ‘border-image-width’ specify offsets that are used to divide the border image area into nine parts. They represent inward distances from the top, right, bottom, and left sides of the area, respectively. */ border-image-outset: 25px 25px 25px 25px; /* The border-image-outset values specify the amount by which the border image area *_ extends beyond the border box_*. */ height: 0px; margin-left: 25px; /* == border-image-outset . This margin-left value is just to position the rendered layout to accurately match the reference file . */ diff --git a/tests/wpt/tests/css/css-backgrounds/border-image-width-007.xht b/tests/wpt/tests/css/css-backgrounds/border-image-width-007.xht index 61726c00873..6c4647dd87f 100644 --- a/tests/wpt/tests/css/css-backgrounds/border-image-width-007.xht +++ b/tests/wpt/tests/css/css-backgrounds/border-image-width-007.xht @@ -28,7 +28,7 @@ border-width: 0px; /* so the border belt (or border area) of the element is 0px tall and 0px wide */ border-image-source: url("support/outline-5px-10px-15px-20px-green.png"); border-image-slice: 5% 10% 15% 20%; /* Percentages are relative to the size of the image: the width of the image for the horizontal offsets, the height for vertical offsets. */ - border-image-width: 50px; /* The four values of ‘border-image-width’ specify offsets that are used to divide the border image area into nine parts. They represent inward distances from the the top, right, bottom, and left sides of the area, respectively. */ + border-image-width: 50px; /* The four values of ‘border-image-width’ specify offsets that are used to divide the border image area into nine parts. They represent inward distances from the top, right, bottom, and left sides of the area, respectively. */ border-image-outset: 50px 50px 50px 50px; /* The border-image-outset values specify the amount by which the border image area !_ extends beyond the border box_!. */ height: 0px; margin: 66px 50px 50px 50px; diff --git a/tests/wpt/tests/css/css-backgrounds/border-image-width-009.html b/tests/wpt/tests/css/css-backgrounds/border-image-width-009.html index 112c2a485ac..109f34fe9fd 100644 --- a/tests/wpt/tests/css/css-backgrounds/border-image-width-009.html +++ b/tests/wpt/tests/css/css-backgrounds/border-image-width-009.html @@ -39,7 +39,7 @@ /* The four values of 'border-image-width' specify offsets that are used to divide the border image area into nine - parts. They represent inward distances from the the top, + parts. They represent inward distances from the top, right, bottom, and left sides of the area, respectively. In this sub-test1, the border-image-width divide the border image area into four (4) parts, not nine parts. The center diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-backdrop-filter-overflow-ref.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-backdrop-filter-overflow-ref.html new file mode 100644 index 00000000000..4aabd7e8e35 --- /dev/null +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-backdrop-filter-overflow-ref.html @@ -0,0 +1,27 @@ + + + + + +
      + diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-backdrop-filter-overflow.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-backdrop-filter-overflow.html new file mode 100644 index 00000000000..550428a37e3 --- /dev/null +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-backdrop-filter-overflow.html @@ -0,0 +1,31 @@ + + + + + + + +
      + diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-backdrop-filter-ref.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-backdrop-filter-ref.html new file mode 100644 index 00000000000..165f93e6be1 --- /dev/null +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-backdrop-filter-ref.html @@ -0,0 +1,18 @@ + + + + + +
      + diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-backdrop-filter.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-backdrop-filter.html new file mode 100644 index 00000000000..c4a2523b1f8 --- /dev/null +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-backdrop-filter.html @@ -0,0 +1,22 @@ + + + + + + + +
      + diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-inset-shadow-ref.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-inset-shadow-ref.html new file mode 100644 index 00000000000..fa36c9d6ecc --- /dev/null +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-inset-shadow-ref.html @@ -0,0 +1,39 @@ + + + + + +
      + diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-inset-shadow.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-inset-shadow.html new file mode 100644 index 00000000000..7a83caff70c --- /dev/null +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-inset-shadow.html @@ -0,0 +1,20 @@ + + + + + + + +
      + diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-interpolation.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-interpolation.html new file mode 100644 index 00000000000..2ba9d43cb1f --- /dev/null +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-interpolation.html @@ -0,0 +1,130 @@ + + +corner-shape interpolation + + + + + + + + + + diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-notch.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-notch.html index 79f64070bd8..30acefe527f 100644 --- a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-notch.html +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-notch.html @@ -10,7 +10,7 @@ height: 100px; border-radius: 25px; box-sizing: border-box; - corner-shape: notch superellipse(0.00001) round superellipse(0); + corner-shape: notch superellipse(-100) round superellipse(-infinity); }
      diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-outside-left.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-outside-left.html new file mode 100644 index 00000000000..b38b7f3f278 --- /dev/null +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-outside-left.html @@ -0,0 +1,71 @@ + +shape-outside with corner-shape: notch + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      + + \ No newline at end of file diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-outside-right.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-outside-right.html new file mode 100644 index 00000000000..96a297230bb --- /dev/null +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-outside-right.html @@ -0,0 +1,71 @@ + +shape-outside with corner-shape: notch + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      + + \ No newline at end of file diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-overflow-clip-margin-ref.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-overflow-clip-margin-ref.html new file mode 100644 index 00000000000..2c1ccf86cc6 --- /dev/null +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-overflow-clip-margin-ref.html @@ -0,0 +1,16 @@ + + +overflow-clip-margin with border-radius & corner-shape + +
      +
      diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-overflow-clip-margin.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-overflow-clip-margin.html new file mode 100644 index 00000000000..97371aa6c3f --- /dev/null +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-overflow-clip-margin.html @@ -0,0 +1,29 @@ + + +overflow-clip-margin with border-radius & corner-shape + + + + + + + +
      +
      +
      diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-render-fuzzy.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-render-fuzzy.html index 5c276b6d5ac..12cd2546bf4 100644 --- a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-render-fuzzy.html +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-render-fuzzy.html @@ -4,22 +4,25 @@ - - - + - - + + - - + + - + + + + + + +
      diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-straight.html b/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-straight.html deleted file mode 100644 index 04cd37b5a31..00000000000 --- a/tests/wpt/tests/css/css-borders/tentative/corner-shape/corner-shape-straight.html +++ /dev/null @@ -1,17 +0,0 @@ - - -CSS Borders and Box Decorations 4: 'corner-shape: straight' - - - -
      diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/resources/corner-shape.js b/tests/wpt/tests/css/css-borders/tentative/corner-shape/resources/corner-shape.js index de471f95dfc..4757a43ad05 100644 --- a/tests/wpt/tests/css/css-borders/tentative/corner-shape/resources/corner-shape.js +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/resources/corner-shape.js @@ -110,7 +110,7 @@ function render_rect_with_corner_shapes(style, ctx, width, height) { } function draw_inner_corner_from_params(params) { - add_corner(ctx, ...params.inner_rect, params.inner_shape); + add_corner(ctx, ...params.inner_rect, params.shape); } function draw_inner_corner(corner) { @@ -127,7 +127,7 @@ function render_rect_with_corner_shapes(style, ctx, width, height) { ctx.save(); ctx.translate(...offset); ctx.beginPath(); - ctx.lineTo(params['top-right'].inner_rect[0], -spread); + ctx.lineTo(params['top-right'].inner_rect[0], params['top-right'].inner_rect[1]); draw_inner_corner_from_params(params['top-right']); ctx.lineTo(params['top-right'].inner_rect[2], params['top-right'].inner_rect[3]) ctx.lineTo(params['bottom-right'].inner_rect[0], params['bottom-right'].inner_rect[1]) diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/resources/corner-utils.js b/tests/wpt/tests/css/css-borders/tentative/corner-shape/resources/corner-utils.js index bacaaa8a6e4..ad3b235addf 100644 --- a/tests/wpt/tests/css/css-borders/tentative/corner-shape/resources/corner-utils.js +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/resources/corner-utils.js @@ -18,19 +18,6 @@ function offset_for_curvature(curvature) { return [norm_a, -norm_b]; } -function compute_inner_curvature(curvature, outer_length, inner_length) { - if (curvature === 0) - return 0; - if (curvature < 1) - return 1 / - compute_inner_curvature(1 / curvature, outer_length, inner_length); - const target_length = (inner_length - outer_length) / Math.SQRT2; - return Math.log(0.5) / - Math.log( - (Math.pow(0.5, 1 / curvature) * outer_length + target_length) / - inner_length); -} - /** * * @param {number} curvature @@ -107,8 +94,6 @@ function resolve_corner_params(style, width, height, outset = null) { return Object.fromEntries( Object.entries(params).map(([corner, {outer, inset}]) => { const outer_rect = outer; - if (outset !== null) - inset = [-outset, -outset]; const shape = style[`corner-${corner}-shape`]; const s1 = Math.sign(outer[2] - outer[0]); const s2 = Math.sign(outer[3] - outer[1]); @@ -120,7 +105,7 @@ function resolve_corner_params(style, width, height, outset = null) { offset.reverse(); } - const inner_rect = [ + let inner_rect = [ outer_rect[0] + inner_offset[0] * offset[0], outer_rect[1] + inner_offset[1] * offset[1], outer_rect[2] + inner_offset[2] * offset[1], @@ -128,13 +113,20 @@ function resolve_corner_params(style, width, height, outset = null) { ]; let inner_shape = shape; - if (shape > 2 || shape < 0.5) { + if (outset) { + const new_width = width + outset * 2; + const new_height = height + outset * 2; + inner_rect = [ + (outer_rect[0] / width) * new_width - outset, + (outer_rect[1] / height) * new_height - outset, + (outer_rect[2] / width) * new_width - outset, + (outer_rect[3] / height) * new_height - outset + ] + } else if (shape > 2 || shape < 0.5) { const outer_length = Math.hypot( outer_rect[2] - outer_rect[0], outer_rect[3] - outer_rect[1]); const inner_length = Math.hypot( inner_rect[2] - inner_rect[0], inner_rect[3] - inner_rect[1]) - inner_shape = - compute_inner_curvature(shape, outer_length, inner_length); } return [ @@ -146,7 +138,6 @@ function resolve_corner_params(style, width, height, outset = null) { inset, inner_rect, inner_offset, - inner_shape }, ]; })); diff --git a/tests/wpt/tests/css/css-borders/tentative/corner-shape/resources/resolve-corner-style.js b/tests/wpt/tests/css/css-borders/tentative/corner-shape/resources/resolve-corner-style.js index 93e5112ea40..a890c57b868 100644 --- a/tests/wpt/tests/css/css-borders/tentative/corner-shape/resources/resolve-corner-style.js +++ b/tests/wpt/tests/css/css-borders/tentative/corner-shape/resources/resolve-corner-style.js @@ -3,28 +3,33 @@ // found in the LICENSE file. const keywords = { - notch: 0, - scoop: 0.5, - bevel: 1, - round: 2, - squircle: 4, - straight: 1000, + notch: -16, + scoop: -1, + bevel: 0, + round: 1, + squircle: 2, + square: 16, }; function resolve_corner_style(style, w, h) { ['top', 'bottom'].forEach((vSide) => ['left', 'right'].forEach((hSide) => { - let shape = style[`corner-${vSide}-${hSide}-shape`] || + let shape_param = style[`corner-${vSide}-${hSide}-shape`] || style['corner-shape'] || 'round'; - const match = shape.match(/superellipse\((\.?[0-9]+(.[0-9]+)?)\)/); - shape = match ? +match[1] : keywords[shape]; + const match = shape_param.match(/superellipse\((-?(infinity|[0-9]*(\.[0-9]+)?))\)/i); + shape_param = match ? match[1] : keywords[shape_param]; const hWidth = parseFloat(style[`border-${hSide}-width`] || style['border-width'] || 0); const vWidth = parseFloat(style[`border-${vSide}-width`] || style['border-width'] || 0); let radius = style[`border-${vSide}-${hSide}-radius`] || style['border-radius'] || 0; if (!Array.isArray(radius)) radius = [radius, radius]; - if (shape > 1000) + let shape = 0; + if (shape_param >= keywords["square"] || shape_param == "infinity") shape = 1000; + else if (shape_param <= keywords["notch"] || shape_param == "-infinity") + shape = 0; + else + shape = Math.pow(2, shape_param); if (String(radius[0]).endsWith('%')) radius[0] = (parseFloat(radius[0]) * w) / 100; if (String(radius[1]).endsWith('%')) @@ -39,11 +44,11 @@ function resolve_corner_style(style, w, h) { if ('box-shadow' in style) { const shadows = style['box-shadow'].split(","); style.shadow = []; - const boxShadowRegex = /(?:(-?\d+(?:\.\d+)?)px)\s+(?:(-?\d+(?:\.\d+)?)px)\s+(?:(-?\d+(?:\.\d+)?)(?:px)?)?(?:\s+(?:(-?\d+(?:\.\d+)?)px))?\s+([^\$]*)/i; + const boxShadowRegex = /(?:(-?\d+(?:\.\d+)?)px)\s+(?:(-?\d+(?:\.\d+)?)px)\s+(?:(-?\d+(?:\.\d+)?)(?:px)?)?(?:\s+(?:(-?\d+(?:\.\d+)?)px))?\s+([^\$\s]*(\s+inset)?)/i; for (const shadow of shadows.toReversed()) { const parsed = shadow.match(boxShadowRegex) if (parsed) - style.shadow.push({offset: [parseFloat(parsed[1]), parseFloat(parsed[2])], blur: parsed[3], spread: parsed[4], color: parsed[5] || "black" }); + style.shadow.push({offset: [parseFloat(parsed[1]), parseFloat(parsed[2])], blur: parsed[3], spread: parsed[4], color: parsed[5] || "black", inset: !!parsed[6] }); } } })); diff --git a/tests/wpt/tests/css/css-borders/tentative/parsing/corner-shape-computed.html b/tests/wpt/tests/css/css-borders/tentative/parsing/corner-shape-computed.html index 1d084e23397..e1b458fd06e 100644 --- a/tests/wpt/tests/css/css-borders/tentative/parsing/corner-shape-computed.html +++ b/tests/wpt/tests/css/css-borders/tentative/parsing/corner-shape-computed.html @@ -15,12 +15,12 @@ test_computed_value("corner-top-left-shape", "round"); test_computed_value("corner-top-left-shape", "scoop"); test_computed_value("corner-top-left-shape", "superellipse(5)"); test_computed_value("corner-top-left-shape", "superellipse(0.2)"); -test_computed_value("corner-top-left-shape", "superellipse(0)", "notch"); -test_computed_value("corner-top-left-shape", "superellipse(infinity)", "straight"); -test_computed_value("corner-top-left-shape", "superellipse(2)", "round"); -test_computed_value("corner-top-left-shape", "superellipse(1)", "bevel"); -test_computed_value("corner-top-left-shape", "superellipse(4)", "squircle"); -test_computed_value("corner-top-left-shape", "superellipse( .5)", "scoop"); +test_computed_value("corner-top-left-shape", "superellipse(-infinity)", "notch"); +test_computed_value("corner-top-left-shape", "superellipse(infinity)", "square"); +test_computed_value("corner-top-left-shape", "superellipse(1)", "round"); +test_computed_value("corner-top-left-shape", "superellipse(0)", "bevel"); +test_computed_value("corner-top-left-shape", "superellipse(2)", "squircle"); +test_computed_value("corner-top-left-shape", "superellipse( -1)", "scoop"); test_computed_value("corner-top-right-shape", "round"); test_computed_value("corner-top-right-shape", "superellipse(5)"); test_computed_value("corner-bottom-right-shape", "scoop"); @@ -28,14 +28,14 @@ test_computed_value("corner-bottom-left-shape", "superellipse(5)"); test_computed_value("corner-shape", "superellipse(5) round"); test_computed_value("corner-shape", "round"); test_computed_value("corner-shape", "bevel superellipse(0.1) round squircle"); -test_computed_value("corner-shape", "superellipse(0.1) superellipse(3) superellipse(7) superellipse(0.1)"); +test_computed_value("corner-shape", "superellipse(-5) superellipse(3) superellipse(7) superellipse(-5.5)"); test_computed_value("corner-shape", "round round round round", "round"); test_computed_value("corner-shape", "round scoop"); test_computed_value("corner-shape", "round scoop round scoop", "round scoop"); -test_computed_value("corner-shape", "bevel superellipse(2)", "bevel round"); -test_computed_value("corner-shape", "superellipse(0.5) superellipse(3) straight", "scoop superellipse(3) straight"); -test_computed_value("corner-shape", "superellipse(0.5) superellipse(3) superellipse(1)", "scoop superellipse(3) bevel"); -test_computed_value("corner-shape", "bevel superellipse(2) squircle round", "bevel round squircle"); -test_computed_value("corner-shape", "superellipse(0.5) superellipse(3) superellipse(1) superellipse(infinity)", "scoop superellipse(3) bevel straight"); +test_computed_value("corner-shape", "bevel superellipse(1)", "bevel round"); +test_computed_value("corner-shape", "superellipse(-1) superellipse(3) square", "scoop superellipse(3) square"); +test_computed_value("corner-shape", "superellipse(-1) superellipse(3) superellipse(0)", "scoop superellipse(3) bevel"); +test_computed_value("corner-shape", "bevel superellipse(1) squircle round", "bevel round squircle"); +test_computed_value("corner-shape", "superellipse(-1) superellipse(3) superellipse(0) superellipse(infinity)", "scoop superellipse(3) bevel square"); diff --git a/tests/wpt/tests/css/css-borders/tentative/parsing/corner-shape-invalid.html b/tests/wpt/tests/css/css-borders/tentative/parsing/corner-shape-invalid.html index 582617a634a..6aee773f4a4 100644 --- a/tests/wpt/tests/css/css-borders/tentative/parsing/corner-shape-invalid.html +++ b/tests/wpt/tests/css/css-borders/tentative/parsing/corner-shape-invalid.html @@ -17,6 +17,8 @@ test_invalid_value("corner-shape", "round round round round round"); test_invalid_value("corner-shape", "superellipse(8 8)"); test_invalid_value("corner-shape", "superellipse(,)"); test_invalid_value("corner-shape", "superellipse(4,0.1)"); +test_invalid_value("corner-shape", "straight"); +test_invalid_value("corner-shape", "nonsense"); test_invalid_value("corner-shape", "superellipse(foo)"); test_invalid_value("corner-shape", "superellipse(1 abc)"); test_invalid_value("corner-shape", "superellipse(1) / bevel"); diff --git a/tests/wpt/tests/css/css-borders/tentative/parsing/corner-shape-valid.html b/tests/wpt/tests/css/css-borders/tentative/parsing/corner-shape-valid.html index 32aaa89fc5b..0ad14c81a74 100644 --- a/tests/wpt/tests/css/css-borders/tentative/parsing/corner-shape-valid.html +++ b/tests/wpt/tests/css/css-borders/tentative/parsing/corner-shape-valid.html @@ -15,7 +15,7 @@ test_valid_value(prop, "notch"); test_valid_value(prop, "bevel"); test_valid_value(prop, "squircle"); - test_valid_value(prop, "straight"); + test_valid_value(prop, "square"); test_valid_value(prop, "superellipse(2)"); test_valid_value(prop, "superellipse(.5)", "superellipse(0.5)"); test_valid_value(prop, "superellipse(7)"); @@ -23,6 +23,9 @@ test_valid_value(prop, "superellipse( 0)", "superellipse(0)"); test_valid_value(prop, "superellipse(2 )", "superellipse(2)"); test_valid_value(prop, "superellipse(infinity)"); + test_valid_value(prop, "superellipse(-infinity)"); + test_valid_value(prop, "superellipse(-0.5)"); + test_valid_value(prop, "superellipse(-4)"); test_valid_value(prop, "superellipse(calc(0.5 * 4))", "superellipse(calc(2))"); } @@ -40,7 +43,8 @@ test_valid_value("corner-shape", "round scoop"); test_valid_value("corner-shape", "round scoop round scoop", "round scoop"); test_valid_value("corner-shape", "bevel superellipse(2)"); - test_valid_value("corner-shape", "superellipse(0.5) superellipse(3) straight"); + test_valid_value("corner-shape", "superellipse(0.5) superellipse(3) square"); + test_valid_value("corner-shape", "superellipse(-0.5) superellipse(3) square superellipse(-30)"); test_valid_value("corner-shape", "superellipse(0.5) superellipse(3) superellipse(1)"); test_valid_value("corner-shape", "bevel superellipse(2) squircle round", "bevel superellipse(2) squircle round"); test_valid_value("corner-shape", "superellipse(0.5) superellipse(3) superellipse(1) superellipse(infinity)"); diff --git a/tests/wpt/tests/css/css-box/parsing/margin-trim-computed.html b/tests/wpt/tests/css/css-box/parsing/margin-trim-computed.html index ad2d17f2ef0..c38adea9a68 100644 --- a/tests/wpt/tests/css/css-box/parsing/margin-trim-computed.html +++ b/tests/wpt/tests/css/css-box/parsing/margin-trim-computed.html @@ -27,8 +27,13 @@ test_computed_value("margin-trim", "inline-start block-start", "block-start inli test_computed_value("margin-trim", "inline-end block-start", "block-start inline-end"); test_computed_value("margin-trim", "inline-end block-end", "block-end inline-end"); test_computed_value("margin-trim", "block-start block-end inline-start", "block-start inline-start block-end"); -test_computed_value("margin-trim", "inline-start block-start inline-end block-end", "block-start inline-start block-end inline-end"); -test_computed_value("margin-trim", "inline-start inline-end block-start", "block-start inline-start inline-end"); + +test_computed_value("margin-trim", "block-start inline-start block-end inline-end", "block inline"); +test_computed_value("margin-trim", "block-start block-end inline-end inline-start", "block inline"); +test_computed_value("margin-trim", "block-start block-end inline-start inline-end", "block inline"); +test_computed_value("margin-trim", "inline-start block-end block-start inline-end", "block inline"); +test_computed_value("margin-trim", "inline-start inline-end block-start block-end", "block inline"); +test_computed_value("margin-trim", "inline-end block-end inline-start block-start", "block inline"); diff --git a/tests/wpt/tests/css/css-box/parsing/margin-trim.html b/tests/wpt/tests/css/css-box/parsing/margin-trim.html index 0d92f80b9b9..e8679f9cbf3 100644 --- a/tests/wpt/tests/css/css-box/parsing/margin-trim.html +++ b/tests/wpt/tests/css/css-box/parsing/margin-trim.html @@ -21,6 +21,9 @@ test_valid_value("margin-trim", "block-end"); test_valid_value("margin-trim", "inline-start"); test_valid_value("margin-trim", "inline-end"); +test_valid_value("margin-trim", "block inline"); +test_valid_value("margin-trim", "inline block"); + // Serialize values into either block or inline test_valid_value("margin-trim", "block-start block-end", "block"); test_valid_value("margin-trim", "inline-start inline-end", "inline"); @@ -29,10 +32,15 @@ test_valid_value("margin-trim", "inline-end inline-start", "inline"); test_valid_value("margin-trim", "inline-start block-start"); test_valid_value("margin-trim", "inline-end block-start block-end"); -test_valid_value("margin-trim", "block-start inline-start block-end inline-end"); -test_valid_value("margin-trim", "inline-end block-end inline-start block-start"); -test_invalid_value("margin-trim", "block inline"); +// Serialize 4 values into "block inline" +test_valid_value("margin-trim", "block-start inline-start block-end inline-end", "block inline"); +test_valid_value("margin-trim", "block-start block-end inline-end inline-start", "block inline"); +test_valid_value("margin-trim", "block-start block-end inline-start inline-end", "block inline"); +test_valid_value("margin-trim", "inline-start block-end block-start inline-end", "block inline"); +test_valid_value("margin-trim", "inline-start inline-end block-start block-end", "block inline"); +test_valid_value("margin-trim", "inline-end block-end inline-start block-start", "block inline"); + test_invalid_value("margin-trim", "block block"); test_invalid_value("margin-trim", "inline inline"); test_invalid_value("margin-trim", "block inline-start inline-end"); diff --git a/tests/wpt/tests/css/css-break/block-001-wm-vlr-print.html b/tests/wpt/tests/css/css-break/block-001-wm-vlr-print.html index 6cd5ea07c7b..bfb48428dd5 100644 --- a/tests/wpt/tests/css/css-break/block-001-wm-vlr-print.html +++ b/tests/wpt/tests/css/css-break/block-001-wm-vlr-print.html @@ -11,6 +11,10 @@ margin: 0.5in; } +:root { + print-color-adjust: exact; +} + html,body { color:black; background-color:white; font:20px/1 monospace; padding:0; margin:0; writing-mode: vertical-lr; diff --git a/tests/wpt/tests/css/css-break/block-001-wm-vrl-print.html b/tests/wpt/tests/css/css-break/block-001-wm-vrl-print.html index 6e073121c6a..60ee504dd77 100644 --- a/tests/wpt/tests/css/css-break/block-001-wm-vrl-print.html +++ b/tests/wpt/tests/css/css-break/block-001-wm-vrl-print.html @@ -11,6 +11,10 @@ margin: 0.5in; } +:root { + print-color-adjust: exact; +} + html,body { color:black; background-color:white; font:20px/1 monospace; padding:0; margin:0; writing-mode: vertical-rl; diff --git a/tests/wpt/tests/css/css-break/block-002-wm-vlr-print.html b/tests/wpt/tests/css/css-break/block-002-wm-vlr-print.html index 99949623c39..d427242dc40 100644 --- a/tests/wpt/tests/css/css-break/block-002-wm-vlr-print.html +++ b/tests/wpt/tests/css/css-break/block-002-wm-vlr-print.html @@ -11,6 +11,10 @@ margin: 0.5in; } +:root { + print-color-adjust: exact; +} + html,body { color:black; background-color:white; font:20px/1 monospace; padding:0; margin:0; writing-mode: vertical-lr; diff --git a/tests/wpt/tests/css/css-break/block-002-wm-vrl-print.html b/tests/wpt/tests/css/css-break/block-002-wm-vrl-print.html index 084b5325b7a..0ca2b73012f 100644 --- a/tests/wpt/tests/css/css-break/block-002-wm-vrl-print.html +++ b/tests/wpt/tests/css/css-break/block-002-wm-vrl-print.html @@ -11,6 +11,10 @@ margin: 0.5in; } +:root { + print-color-adjust: exact; +} + html,body { color:black; background-color:white; font:20px/1 monospace; padding:0; margin:0; writing-mode: vertical-rl; diff --git a/tests/wpt/tests/css/css-break/break-inside-avoid-multicol-001-print-ref.html b/tests/wpt/tests/css/css-break/break-inside-avoid-multicol-001-print-ref.html index baf114d35c9..0e6f2500d92 100644 --- a/tests/wpt/tests/css/css-break/break-inside-avoid-multicol-001-print-ref.html +++ b/tests/wpt/tests/css/css-break/break-inside-avoid-multicol-001-print-ref.html @@ -10,6 +10,9 @@ size: 5in 3in; margin: 0.5in; } + :root { + print-color-adjust: exact; + } body { margin: 0; } diff --git a/tests/wpt/tests/css/css-break/break-inside-avoid-multicol-001-print.html b/tests/wpt/tests/css/css-break/break-inside-avoid-multicol-001-print.html index a97ac9f5cda..0398110fe3d 100644 --- a/tests/wpt/tests/css/css-break/break-inside-avoid-multicol-001-print.html +++ b/tests/wpt/tests/css/css-break/break-inside-avoid-multicol-001-print.html @@ -13,6 +13,9 @@ size: 5in 3in; margin: 0.5in; } + :root { + print-color-adjust: exact; + } body { margin: 0; } diff --git a/tests/wpt/tests/css/css-break/break-nested-float-in-table-001-print-ref.html b/tests/wpt/tests/css/css-break/break-nested-float-in-table-001-print-ref.html index 3459e25edd6..ffd4196708f 100644 --- a/tests/wpt/tests/css/css-break/break-nested-float-in-table-001-print-ref.html +++ b/tests/wpt/tests/css/css-break/break-nested-float-in-table-001-print-ref.html @@ -10,6 +10,10 @@

      Test passes if there is two purple rectangles at the start of both page 2 and 3 when printing the page (Ctrl+P, with "print backgrounds" enabled).

      diff --git a/tests/wpt/tests/css/css-break/grid/monolithic-overflow-print.html b/tests/wpt/tests/css/css-break/grid/monolithic-overflow-print.html index 9b174caf96c..11ce95a34e7 100644 --- a/tests/wpt/tests/css/css-break/grid/monolithic-overflow-print.html +++ b/tests/wpt/tests/css/css-break/grid/monolithic-overflow-print.html @@ -2,6 +2,11 @@ +

      Test passes if there is two purple rectangles at the start of both page 2 and 3 when printing the page (Ctrl+P, with "print backgrounds" enabled).

      diff --git a/tests/wpt/tests/css/css-break/ink-overflow-001-print.html b/tests/wpt/tests/css/css-break/ink-overflow-001-print.html index 495153d3d42..7d7e8518999 100644 --- a/tests/wpt/tests/css/css-break/ink-overflow-001-print.html +++ b/tests/wpt/tests/css/css-break/ink-overflow-001-print.html @@ -9,6 +9,10 @@ margin: 0.5in; } +:root { + print-color-adjust: exact; +} + html,body { color:black; background-color:white; font:20px/1 monospace; padding:0; margin:0; } diff --git a/tests/wpt/tests/css/css-break/nested-fixedpos-in-inline-003-crash.html b/tests/wpt/tests/css/css-break/nested-fixedpos-in-inline-003-crash.html new file mode 100644 index 00000000000..88a04288464 --- /dev/null +++ b/tests/wpt/tests/css/css-break/nested-fixedpos-in-inline-003-crash.html @@ -0,0 +1,13 @@ + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-break/root-margin-001-print-ref.html b/tests/wpt/tests/css/css-break/root-margin-001-print-ref.html new file mode 100644 index 00000000000..320da2e0f55 --- /dev/null +++ b/tests/wpt/tests/css/css-break/root-margin-001-print-ref.html @@ -0,0 +1,6 @@ + + +
      +
      +This text should be on the second page. +The first page should be blank. diff --git a/tests/wpt/tests/css/css-break/root-margin-001-print.html b/tests/wpt/tests/css/css-break/root-margin-001-print.html new file mode 100644 index 00000000000..b2ec34fe079 --- /dev/null +++ b/tests/wpt/tests/css/css-break/root-margin-001-print.html @@ -0,0 +1,10 @@ + +Root block-start margin taller than the page + + + + +This text should be on the second page. +The first page should be blank. diff --git a/tests/wpt/tests/css/css-break/table/border-spacing.html b/tests/wpt/tests/css/css-break/table/border-spacing.html index fc5e87e35dd..9fd94760681 100644 --- a/tests/wpt/tests/css/css-break/table/border-spacing.html +++ b/tests/wpt/tests/css/css-break/table/border-spacing.html @@ -5,7 +5,7 @@ .cell > div { background:white; }

      No red should be seen below.

      -
      +
      1
      @@ -20,15 +20,20 @@ diff --git a/tests/wpt/tests/css/css-break/table/table-fragmentation-001a-print-ref.html b/tests/wpt/tests/css/css-break/table/table-fragmentation-001a-print-ref.html index d4229ebb873..dd0e61badef 100644 --- a/tests/wpt/tests/css/css-break/table/table-fragmentation-001a-print-ref.html +++ b/tests/wpt/tests/css/css-break/table/table-fragmentation-001a-print-ref.html @@ -5,6 +5,9 @@

      There should be a green square on the second page, and no red.

      diff --git a/tests/wpt/tests/css/css-break/transform-023-print.html b/tests/wpt/tests/css/css-break/transform-023-print.html index 7650571abe1..53abb1939bb 100644 --- a/tests/wpt/tests/css/css-break/transform-023-print.html +++ b/tests/wpt/tests/css/css-break/transform-023-print.html @@ -2,6 +2,11 @@ +

      There should be a green square on the second page, and no red.

      diff --git a/tests/wpt/tests/css/css-break/transform-024-print-ref.html b/tests/wpt/tests/css/css-break/transform-024-print-ref.html index 766c415a1be..582794ca046 100644 --- a/tests/wpt/tests/css/css-break/transform-024-print-ref.html +++ b/tests/wpt/tests/css/css-break/transform-024-print-ref.html @@ -1,6 +1,9 @@
      diff --git a/tests/wpt/tests/css/css-break/transform-024-print.html b/tests/wpt/tests/css/css-break/transform-024-print.html index 510b4c2e44f..22b6cd12075 100644 --- a/tests/wpt/tests/css/css-break/transform-024-print.html +++ b/tests/wpt/tests/css/css-break/transform-024-print.html @@ -3,6 +3,9 @@
      diff --git a/tests/wpt/tests/css/css-cascade/inline-style-background.html b/tests/wpt/tests/css/css-cascade/inline-style-background.html new file mode 100644 index 00000000000..11451f8cefd --- /dev/null +++ b/tests/wpt/tests/css/css-cascade/inline-style-background.html @@ -0,0 +1,16 @@ + + + + + + + +
      Test passes if it does not crash.
      + + diff --git a/tests/wpt/tests/css/css-color-hdr/computed.html b/tests/wpt/tests/css/css-color-hdr/computed.html index 47021144087..38a540bd66a 100644 --- a/tests/wpt/tests/css/css-color-hdr/computed.html +++ b/tests/wpt/tests/css/css-color-hdr/computed.html @@ -43,4 +43,6 @@ test_computed_value("dynamic-range-limit", "dynamic-range-limit-mix(dynamic-rang "dynamic-range-limit-mix(standard 15%, constrained 40%, no-limit 45%)"); test_computed_value("dynamic-range-limit", "dynamic-range-limit-mix(no-limit 10%, dynamic-range-limit-mix(standard 25%, constrained 75%) 20%, dynamic-range-limit-mix(constrained 10%, no-limit 30%) 20%)", "dynamic-range-limit-mix(standard 10%, constrained 40%, no-limit 50%)"); +test_computed_value("dynamic-range-limit", "dynamic-range-limit-mix(standard calc(50% * sign(10em - 1px)), constrained 50%)", + "dynamic-range-limit-mix(standard 50%, constrained 50%)"); diff --git a/tests/wpt/tests/css/css-color-hdr/parsing.html b/tests/wpt/tests/css/css-color-hdr/parsing.html index e165a60d7ed..2447891f2c4 100644 --- a/tests/wpt/tests/css/css-color-hdr/parsing.html +++ b/tests/wpt/tests/css/css-color-hdr/parsing.html @@ -20,6 +20,8 @@ test_valid_value("dynamic-range-limit", "dynamic-range-limit-mix(dynamic-range-limit-mix(constrained 90%, no-limit 10%) 1%, no-limit 80%)"); test_valid_value("dynamic-range-limit", "dynamic-range-limit-mix(no-limit 1%, dynamic-range-limit-mix(constrained 2%, no-limit 10%) 80%)"); test_valid_value("dynamic-range-limit", "dynamic-range-limit-mix(dynamic-range-limit-mix(no-limit 1%, standard 2%) 3%, dynamic-range-limit-mix(constrained 5%, no-limit 5%) 6%)"); + test_valid_value("dynamic-range-limit", "dynamic-range-limit-mix(standard calc(50% * sign(10em - 1px)), constrained 50%)"); + test_invalid_value("dynamic-range-limit", "dynamic-range-limit-mix(no-limit 80%, standard 20%, )"); test_invalid_value("dynamic-range-limit", "dynamic-range-limit-mix(no-limit, standard 20%)"); test_invalid_value("dynamic-range-limit", "dynamic-range-limit-mix(constrained, no-limit, 80%)"); diff --git a/tests/wpt/tests/css/css-color/WEB_FEATURES.yml b/tests/wpt/tests/css/css-color/WEB_FEATURES.yml index a7e2044aeaa..275611f531e 100644 --- a/tests/wpt/tests/css/css-color/WEB_FEATURES.yml +++ b/tests/wpt/tests/css/css-color/WEB_FEATURES.yml @@ -26,3 +26,6 @@ features: - name: hwb files: - hwb-* +- name: relative-color + files: + - relative-* diff --git a/tests/wpt/tests/css/css-color/parsing/WEB_FEATURES.yml b/tests/wpt/tests/css/css-color/parsing/WEB_FEATURES.yml index cc8273c2adc..0bb97b1e1d9 100644 --- a/tests/wpt/tests/css/css-color/parsing/WEB_FEATURES.yml +++ b/tests/wpt/tests/css/css-color/parsing/WEB_FEATURES.yml @@ -3,3 +3,7 @@ features: files: - "*-color-mix-*" - color-mix-out-of-gamut.html +- name: relative-color + files: + - "relative-*" + - "*-relative-*" diff --git a/tests/wpt/tests/css/css-color/parsing/color-computed-relative-color.html b/tests/wpt/tests/css/css-color/parsing/color-computed-relative-color.html index 9bb87459623..a5c489e40a6 100644 --- a/tests/wpt/tests/css/css-color/parsing/color-computed-relative-color.html +++ b/tests/wpt/tests/css/css-color/parsing/color-computed-relative-color.html @@ -871,6 +871,7 @@ fuzzy_test_computed_color(`lch(from var(--mycolor) l 0 h)`, `lch(62.75 0 326.96)`, 0.02); fuzzy_test_computed_color(`var(--mygray)`, `lch(62.75 0 326.96)`, 0.02); fuzzy_test_computed_color(`lch(from var(--mygray) l 30 h)`, `lch(62.75 30 326.96)`, 0.02); + fuzzy_test_computed_color(`LCH(from var(--accent) l c calc(h + 180 * sibling-index()))`, `lch(65.49 39.45 10.11)`, 0.02); diff --git a/tests/wpt/tests/css/css-conditional/container-queries/WEB_FEATURES.yml b/tests/wpt/tests/css/css-conditional/container-queries/WEB_FEATURES.yml index 445ceb373f0..7e15715f583 100644 --- a/tests/wpt/tests/css/css-conditional/container-queries/WEB_FEATURES.yml +++ b/tests/wpt/tests/css/css-conditional/container-queries/WEB_FEATURES.yml @@ -1,3 +1,144 @@ features: +# Note: container-queries in web-features is container size queries. - name: container-queries - files: "**" + files: + - animation-container-size.html + - animation-container-type-dynamic.html + - animation-nested-animation.html + - animation-nested-transition.html + - aspect-ratio-feature-evaluation.html + - at-container-parsing.html + - at-container-serialization.html + - auto-scrollbars.html + - backdrop-invalidation.html + - calc-evaluation.html + - canvas-as-container-001.html + - canvas-as-container-002.html + - canvas-as-container-003.html + - canvas-as-container-004.html + - canvas-as-container-005.html + - canvas-as-container-006.html + - change-display-in-container.html + - chrome-legacy-skip-recalc.html + - column-spanner-in-container.html + - conditional-container-status.html + - container-computed.html + - container-for-cue.html + - container-for-shadow-dom.html + - container-inheritance.html + - container-inner-at-rules.html + - container-inside-multicol-with-table.html + - container-longhand-animation-type.html + - container-name-computed.html + - container-name-invalidation.html + - container-name-parsing.html + - container-name-tree-scoped.html + - container-nested.html + - container-parsing.html + - container-selection.html + - container-size-* + - container-type-computed.html + - container-type-containment.html + - container-type-invalidation.html + - container-type-layout-invalidation.html + - container-type-parsing.html + - container-units-* + - counters-flex-circular.html + - counters-in-container-dynamic.html + - counters-in-container.html + - custom-layout-container-001.https.html + - deep-nested-inline-size-containers.html + - dialog-backdrop-create.html + - dialog-backdrop-remove.html + - display-contents.html + - display-in-container.html + - display-none.html + - fieldset-legend-change.html + - flex-basis-with-container-type.html + - font-relative-calc-dynamic.html + - font-relative-units-dynamic.html + - font-relative-units.html + - fragmented-container-001.html + - get-animations.html + - grid-container.html + - grid-item-container.html + - idlharness.html + - iframe-in-container-invalidation.html + - iframe-invalidation.html + - ineligible-containment.html + - inheritance-from-container.html + - inline-size-and-min-width.html + - inline-size-bfc-floats.html + - inline-size-containment-vertical-rl.html + - inline-size-containment.html + - inner-first-line-non-matching.html + - layout-dependent-focus.html + - multicol-container-001.html + - multicol-inside-container.html + - multiple-size-containers-comma-separated-queries.html + - nested-query-containers.html + - never-match-container.html + - no-layout-containment-abspos-dynamic.html + - no-layout-containment-abspos.html + - no-layout-containment-baseline.html + - no-layout-containment-fixedpos-dynamic.html + - no-layout-containment-fixedpos.html + - no-layout-containment-scroll.html + - no-layout-containment-subgrid-crash.html + - orthogonal-wm-container-query.html + - percentage-padding-orthogonal.html + - pseudo-elements-001.html + - pseudo-elements-002.html + - pseudo-elements-002b.html + - pseudo-elements-003.html + - pseudo-elements-004.html + - pseudo-elements-006.html + - pseudo-elements-007.html + - pseudo-elements-008.html + - pseudo-elements-009.html + - pseudo-elements-010.html + - pseudo-elements-011.html + - pseudo-elements-012.html + - pseudo-elements-013.html + - query-container-name.html + - query-content-box.html + - query-evaluation.html + - reattach-container-with-dirty-child.html + - resize-while-content-visibility-hidden.html + - scrollbar-container-units-block.html + - scrollbar-container-units-inline.html + - sibling-layout-dependency.html + - size-container-* + - size-feature-evaluation.html + - size-query-with-var.html + - style-change-in-container.html + - style-not-sharing-float.html + - svg-foreignobject-child-container.html + - svg-foreignobject-no-size-container.html + - svg-g-no-size-container.html + - svg-root-size-container.html + - table-inside-container-changing-display.html + - top-layer-dialog-backdrop.html + - top-layer-dialog-container.html + - top-layer-dialog.html + - top-layer-nested-dialog.html + - transition-scrollbars.html + - transition-style-change-event-002.html + - transition-style-change-event.html + - unsupported-axis.html + - var-evaluation.html + - viewport-units-dynamic.html + - viewport-units.html + - whitespace-update-after-removal.html +- name: container-style-queries + files: + - at-container-style-* + - custom-property-style-* + - display-contents-dynamic-style-queries.html + - multiple-style-containers-comma-separated-queries.html + - nested-size-style-container-invalidation.html + - pseudo-elements-005.html + - query-evaluation-style.html + - registered-color-style-queries.html + - style-container-* + - style-query-* diff --git a/tests/wpt/tests/css/css-conditional/container-queries/at-container-style-parsing.html b/tests/wpt/tests/css/css-conditional/container-queries/at-container-style-parsing.html index 2e6722335c2..2890727ad4c 100644 --- a/tests/wpt/tests/css/css-conditional/container-queries/at-container-style-parsing.html +++ b/tests/wpt/tests/css/css-conditional/container-queries/at-container-style-parsing.html @@ -18,7 +18,34 @@ test_cq_condition_known('style(--my-prop: )'); test_cq_condition_known('style(--foo: bar !important)'); test_cq_condition_known('style(--foo)'); + test_cq_condition_known('style(--my-prop: attr(data-foo))'); + test_cq_condition_known('style(--foo >= --bar)'); + test_cq_condition_known('style(--foo = --bar)'); + test_cq_condition_known('style(--foo <= --bar)'); + test_cq_condition_known('style(10px > 10em)'); + test_cq_condition_known('style(--foo >= 10em)'); + test_cq_condition_known('style(10px > --bar)'); + test_cq_condition_known('style(10px = --bar)'); + test_cq_condition_known('style(--foo < --bar)'); + test_cq_condition_known('style(10px <= 10em)'); + test_cq_condition_known('style(10px = 10em)'); + test_cq_condition_known('style(10px <= calc(10em + 20em))'); + test_cq_condition_known('style(calc(10em + 20em) < 10px)'); + test_cq_condition_known('style(--foo < 10em)'); + test_cq_condition_known('style(10px <= --bar)'); + test_cq_condition_known('style(--foo < --bar <= --baz)'); + test_cq_condition_known('style(--foo >= --bar > --baz)'); + test_cq_condition_known('style(--foo > 10px > 10em)'); + test_cq_condition_known('style(10px < --foo < 10em)'); + test_cq_condition_known('style(10px < --foo <= 10em)'); + test_cq_condition_known('style(10px <= --foo < 10em)'); + test_cq_condition_known('style(10px > 10em > --foo)'); + test_cq_condition_known('style(10px < 10em < 10)'); + test_cq_condition_known('style(var(--p) < calc(100 + 200))'); + test_cq_condition_known('style(attr(data-foo type()) < var(--p) < var(--q))'); test_cq_condition_unknown('style(--foo: bar;)'); test_cq_condition_unknown('style(style(--foo: bar))'); + test_cq_condition_unknown('style(10px < 10em > 10)'); + test_cq_condition_unknown('style( < 10em)'); diff --git a/tests/wpt/tests/css/css-conditional/container-queries/container-ident-function.html b/tests/wpt/tests/css/css-conditional/container-queries/container-ident-function.html new file mode 100644 index 00000000000..692efa757b3 --- /dev/null +++ b/tests/wpt/tests/css/css-conditional/container-queries/container-ident-function.html @@ -0,0 +1,24 @@ + +CSS Conditional: The ident() function in @container/container-name + + + + + + +
      + diff --git a/tests/wpt/tests/css/css-conditional/container-queries/font-relative-units.html b/tests/wpt/tests/css/css-conditional/container-queries/font-relative-units.html index 3d676402e1e..97f43ac466f 100644 --- a/tests/wpt/tests/css/css-conditional/container-queries/font-relative-units.html +++ b/tests/wpt/tests/css/css-conditional/container-queries/font-relative-units.html @@ -7,6 +7,7 @@ +
      + +
      +
      + +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-conditional/container-queries/scroll-state/scroll-state-snapped-snap-changing.html b/tests/wpt/tests/css/css-conditional/container-queries/scroll-state/scroll-state-snapped-snap-changing.html index 85f678f07e1..ded5656a046 100644 --- a/tests/wpt/tests/css/css-conditional/container-queries/scroll-state/scroll-state-snapped-snap-changing.html +++ b/tests/wpt/tests/css/css-conditional/container-queries/scroll-state/scroll-state-snapped-snap-changing.html @@ -57,6 +57,7 @@ .pointerMove(0, 0, {origin: "viewport"}) .pause(50) .send(); + await waitForAnimationFrames(2); assert_equals(changeTarget, snapped1, "snapchange has not been called for #snapped2"); assert_equals(changingTarget, snapped2, "snapchanging has been called for #snapped2"); assert_equals(getComputedStyle(target1).getPropertyValue("--snapped"), "", diff --git a/tests/wpt/tests/css/css-contain/contain-inline-size-grid-auto-fit.html b/tests/wpt/tests/css/css-contain/contain-inline-size-grid-auto-fit.html new file mode 100644 index 00000000000..b6401ac6640 --- /dev/null +++ b/tests/wpt/tests/css/css-contain/contain-inline-size-grid-auto-fit.html @@ -0,0 +1,32 @@ + + + + + + + + + + +

      Test passes if there is a filled green square.

      +
      +
      +
      +
      +
      +
      + + diff --git a/tests/wpt/tests/css/css-contain/contain-layout-stacking-context-001.html b/tests/wpt/tests/css/css-contain/contain-layout-stacking-context-001.html index 4ec3bcee6fd..5724fa1feac 100644 --- a/tests/wpt/tests/css/css-contain/contain-layout-stacking-context-001.html +++ b/tests/wpt/tests/css/css-contain/contain-layout-stacking-context-001.html @@ -1,66 +1,32 @@ - - - - CSS Test: 'contain: layout' with stacking contents. Z-index is defined only for siblings and children. - - - - - - - - -
      -

      -
      - -
      -
      -

      -
      - -
      -
      -
      - -
      -

      -
      - - +'contain: layout' establishes stacking context. + + + + + +
      +
      +
      +
      +Test succeeds if there is no red. diff --git a/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001-ref.html b/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001-ref.html index c7553716ab6..e2516f3d378 100644 --- a/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001-ref.html +++ b/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001-ref.html @@ -1,62 +1,13 @@ - - - - CSS Reftest Reference - - - - -
      -

      -
      - -
      -
      -

      -
      - -
      -
      -
      - -
      -

      -
      - - + +Paint Containment Stacking Context Reference + + +
      +Test succeeds if there is no red. diff --git a/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001a.html b/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001a.html index 71102b6c73a..5e588cb6174 100644 --- a/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001a.html +++ b/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001a.html @@ -1,66 +1,32 @@ - - - - CSS Test: 'contain: paint' with stacking contents. Z-index is defined only for siblings and children. - - - - - - - - -
      -

      -
      - -
      -
      -

      -
      - -
      -
      -
      - -
      -

      -
      - - +'contain: paint' establishes stacking context. + + + + + +
      +
      +
      +
      +Test succeeds if there is no red. diff --git a/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001b.html b/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001b.html index 0c4d3323bf7..e03323782a7 100644 --- a/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001b.html +++ b/tests/wpt/tests/css/css-contain/contain-paint-stacking-context-001b.html @@ -1,66 +1,32 @@ - - - - CSS Test: 'will-change: contain' with stacking contents. Z-index is defined only for siblings and children. - - - - - - - - -
      -

      -
      - -
      -
      -

      -
      - -
      -
      -
      - -
      -

      -
      - - +'will-change: contain' establishes stacking context. + + + + + +
      +
      +
      +
      +Test succeeds if there is no red. diff --git a/tests/wpt/tests/css/css-contain/content-visibility/content-visibility-auto-img-001-ref.html b/tests/wpt/tests/css/css-contain/content-visibility/content-visibility-auto-img-001-ref.html new file mode 100644 index 00000000000..d47ced1c33d --- /dev/null +++ b/tests/wpt/tests/css/css-contain/content-visibility/content-visibility-auto-img-001-ref.html @@ -0,0 +1,5 @@ + + +CSS Reference Case + + diff --git a/tests/wpt/tests/css/css-contain/content-visibility/content-visibility-auto-img-001.html b/tests/wpt/tests/css/css-contain/content-visibility/content-visibility-auto-img-001.html new file mode 100644 index 00000000000..ba6fdd316dc --- /dev/null +++ b/tests/wpt/tests/css/css-contain/content-visibility/content-visibility-auto-img-001.html @@ -0,0 +1,49 @@ + + + +CSS Test: img with content-visibility:auto honors its object-fit (restoring its aspect ratio) when coming back from offscreen + + + + + + + + + + + + + diff --git a/tests/wpt/tests/css/css-content/parsing/content-counter-valid.html b/tests/wpt/tests/css/css-content/parsing/content-counter-valid.html new file mode 100644 index 00000000000..6de60c9f1d8 --- /dev/null +++ b/tests/wpt/tests/css/css-content/parsing/content-counter-valid.html @@ -0,0 +1,13 @@ + + +CSS Content Test: counter() in alt text parsing + + + + +
      + diff --git a/tests/wpt/tests/css/css-display/display-contents-inline-002.html b/tests/wpt/tests/css/css-display/display-contents-inline-002.html new file mode 100644 index 00000000000..f40a34129f3 --- /dev/null +++ b/tests/wpt/tests/css/css-display/display-contents-inline-002.html @@ -0,0 +1,18 @@ + + +CSS Display: display:contents in inline layout should affect style of descendants + + + + + +

      You should see the word PASS and no red below.

      + + P
      AS
      S +
      diff --git a/tests/wpt/tests/css/css-display/display-contents-pseudo-click-target.html b/tests/wpt/tests/css/css-display/display-contents-pseudo-click-target.html new file mode 100644 index 00000000000..2620c97312f --- /dev/null +++ b/tests/wpt/tests/css/css-display/display-contents-pseudo-click-target.html @@ -0,0 +1,43 @@ + +Clicking a display: contents pseudo-element targets that element + + + + + + + + + + +
      + +
      + diff --git a/tests/wpt/tests/css/css-easing/linear-timing-functions-chrome-405004975-crash.html b/tests/wpt/tests/css/css-easing/linear-timing-functions-chrome-405004975-crash.html deleted file mode 100644 index 510b51716ce..00000000000 --- a/tests/wpt/tests/css/css-easing/linear-timing-functions-chrome-405004975-crash.html +++ /dev/null @@ -1,10 +0,0 @@ - -Chrome crash bug 405004975 - - -

      Passes if test does not fail any CHECKs or DCHECKs.

      diff --git a/tests/wpt/tests/css/css-easing/linear-timing-functions-chrome-406926307-crash.html b/tests/wpt/tests/css/css-easing/linear-timing-functions-chrome-406926307-crash.html new file mode 100644 index 00000000000..32d48dca76b --- /dev/null +++ b/tests/wpt/tests/css/css-easing/linear-timing-functions-chrome-406926307-crash.html @@ -0,0 +1,10 @@ + +Chrome crash bug 406926307 + + +

      Passes if test does not fail any CHECKs or DCHECKs.

      diff --git a/tests/wpt/tests/css/css-easing/linear-timing-functions-syntax.html b/tests/wpt/tests/css/css-easing/linear-timing-functions-syntax.html index 0c949e9ea7d..408264b58df 100644 --- a/tests/wpt/tests/css/css-easing/linear-timing-functions-syntax.html +++ b/tests/wpt/tests/css/css-easing/linear-timing-functions-syntax.html @@ -26,6 +26,7 @@ test_valid_value("animation-timing-function", "linear(0, 0.5 25% 75%, 1 100% 100 test_valid_value("animation-timing-function", "linear(0, 1.3, 1, 0.92, 1, 0.99, 1, 0.998, 1 100% 100%)"); test_valid_value("animation-timing-function", "linear(0, 0 40%, 1, 0.5, 1)"); test_valid_value("animation-timing-function", "linear(0, 1.3, 1, 0.92, 1, 0.99, 1, 1.004, 0.998, 1 100% 100%)"); +test_valid_value("animation-timing-function", "linear(calc(0/0), 1)", "linear(0 0%, 1 100%)"); test_invalid_value("animation-timing-function", "linear()"); test_invalid_value("animation-timing-function", "linear(0)"); diff --git a/tests/wpt/tests/css/css-easing/step-timing-functions-syntax.html b/tests/wpt/tests/css/css-easing/step-timing-functions-syntax.html index 4e8b21e4413..e8465ff7a33 100644 --- a/tests/wpt/tests/css/css-easing/step-timing-functions-syntax.html +++ b/tests/wpt/tests/css/css-easing/step-timing-functions-syntax.html @@ -29,6 +29,7 @@ test_invalid_value("animation-timing-function", "steps(0, jump-start)"); test_invalid_value("animation-timing-function", "steps(0, jump-end)"); test_invalid_value("animation-timing-function", "steps(0, jump-both)"); test_invalid_value("animation-timing-function", "steps(1, jump-none)"); +test_invalid_value("animation-timing-function", "steps(calc(0/0), jump-none)"); diff --git a/tests/wpt/tests/css/css-easing/timing-functions-syntax-valid.html b/tests/wpt/tests/css/css-easing/timing-functions-syntax-valid.html index b858f45f4a4..3ebe1f513be 100644 --- a/tests/wpt/tests/css/css-easing/timing-functions-syntax-valid.html +++ b/tests/wpt/tests/css/css-easing/timing-functions-syntax-valid.html @@ -21,6 +21,7 @@ test_valid_value("animation-timing-function", "cubic-bezier(0.1, 0.2, 0.8, 0.9)" test_valid_value("animation-timing-function", "cubic-bezier(0, -2, 1, 3)"); test_valid_value("animation-timing-function", "cubic-bezier(0, 0.7, 1, 1.3)"); test_valid_value("animation-timing-function", "cubic-bezier(calc(-2), calc(0.7 / 2), calc(1.5), calc(0))", "cubic-bezier(calc(-2), calc(0.35), calc(1.5), calc(0))"); +test_valid_value("animation-timing-function", "cubic-bezier(0, sibling-index(), 1, sign(2em - 20px))"); test_valid_value("animation-timing-function", "steps(4, start)"); test_valid_value("animation-timing-function", "steps(2, end)", "steps(2)"); diff --git a/tests/wpt/tests/css/css-exclusions/wrap-flow-001.html b/tests/wpt/tests/css/css-exclusions/wrap-flow-001.html index f87c5baf8b2..5750d8b73c2 100644 --- a/tests/wpt/tests/css/css-exclusions/wrap-flow-001.html +++ b/tests/wpt/tests/css/css-exclusions/wrap-flow-001.html @@ -41,8 +41,9 @@
      + + + + + + + +
      +
      +
      + + + diff --git a/tests/wpt/tests/css/css-flexbox/break-nested-float-in-flex-item-001-print.html b/tests/wpt/tests/css/css-flexbox/break-nested-float-in-flex-item-001-print.html index 2fbf939f6a5..29796771451 100644 --- a/tests/wpt/tests/css/css-flexbox/break-nested-float-in-flex-item-001-print.html +++ b/tests/wpt/tests/css/css-flexbox/break-nested-float-in-flex-item-001-print.html @@ -13,6 +13,10 @@ + + + +

      Test passes if there is a filled green square.

      +
      + +
      +
      + + diff --git a/tests/wpt/tests/css/css-flexbox/flex-container-max-content-002.tentative.html b/tests/wpt/tests/css/css-flexbox/flex-container-max-content-002.tentative.html new file mode 100644 index 00000000000..2f489f3be1b --- /dev/null +++ b/tests/wpt/tests/css/css-flexbox/flex-container-max-content-002.tentative.html @@ -0,0 +1,169 @@ + + +Flex Container Max-Content Sizes + + + + + + + + + +
      +
      X
      +
      +
      +
      X
      +
      +
      +
      X
      +
      + +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      + +
      + + +
      +
      X
      +
      +
      +
      X
      +
      +
      +
      X
      +
      + +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      + +
      + + +
      +
      X
      +
      +
      +
      X
      +
      +
      +
      X
      +
      + +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      + +
      + + +
      +
      X
      +
      +
      +
      X
      +
      +
      +
      X
      +
      + +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      + + + + + diff --git a/tests/wpt/tests/css/css-flexbox/flex-container-min-content-002.tentative.html b/tests/wpt/tests/css/css-flexbox/flex-container-min-content-002.tentative.html new file mode 100644 index 00000000000..f7e5016b59e --- /dev/null +++ b/tests/wpt/tests/css/css-flexbox/flex-container-min-content-002.tentative.html @@ -0,0 +1,169 @@ + + +Flex Container Min-Content Sizes + + + + + + + + + +
      +
      X
      +
      +
      +
      X
      +
      +
      +
      X
      +
      + +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      + +
      + + +
      +
      X
      +
      +
      +
      X
      +
      +
      +
      X
      +
      + +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      + +
      + + +
      +
      X
      +
      +
      +
      X
      +
      +
      +
      X
      +
      + +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      + +
      + + +
      +
      X
      +
      +
      +
      X
      +
      +
      +
      X
      +
      + +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      +
      +
      X X
      +
      X X
      +
      + + + + + diff --git a/tests/wpt/tests/css/css-flexbox/flex-direction.html b/tests/wpt/tests/css/css-flexbox/flex-direction.html index 709b1b036a2..01c6778a122 100644 --- a/tests/wpt/tests/css/css-flexbox/flex-direction.html +++ b/tests/wpt/tests/css/css-flexbox/flex-direction.html @@ -5,7 +5,7 @@ - + diff --git a/tests/wpt/tests/css/css-flexbox/stretched-child-in-nested-flexbox.html b/tests/wpt/tests/css/css-flexbox/stretched-child-in-nested-flexbox-001.html similarity index 100% rename from tests/wpt/tests/css/css-flexbox/stretched-child-in-nested-flexbox.html rename to tests/wpt/tests/css/css-flexbox/stretched-child-in-nested-flexbox-001.html diff --git a/tests/wpt/tests/css/css-flexbox/stretched-child-in-nested-flexbox-002.html b/tests/wpt/tests/css/css-flexbox/stretched-child-in-nested-flexbox-002.html new file mode 100644 index 00000000000..9a00b3c40a3 --- /dev/null +++ b/tests/wpt/tests/css/css-flexbox/stretched-child-in-nested-flexbox-002.html @@ -0,0 +1,19 @@ + + + +css-flexbox: Tests nested flex item with `align-items: stretch` + + + + + + +

      Test passes if there is a filled green square and no red.

      +
      +
      +
      +
      +
      +
      + + diff --git a/tests/wpt/tests/css/css-font-loading/WEB_FEATURES.yml b/tests/wpt/tests/css/css-font-loading/WEB_FEATURES.yml new file mode 100644 index 00000000000..22137bf8279 --- /dev/null +++ b/tests/wpt/tests/css/css-font-loading/WEB_FEATURES.yml @@ -0,0 +1,4 @@ +features: +- name: font-metric-overrides + files: + - fontface-override-descriptor-getter-setter.sub.html diff --git a/tests/wpt/tests/css/css-fonts/WEB_FEATURES.yml b/tests/wpt/tests/css/css-fonts/WEB_FEATURES.yml index dd313f81ed6..856ded1a75b 100644 --- a/tests/wpt/tests/css/css-fonts/WEB_FEATURES.yml +++ b/tests/wpt/tests/css/css-fonts/WEB_FEATURES.yml @@ -31,3 +31,8 @@ features: - name: font-variant-position files: - font-variant-position-* +- name: font-metric-overrides + files: + - ascent-descent-override.html + - line-gap-override.html + - metrics-override-normal-keyword.html diff --git a/tests/wpt/tests/css/css-fonts/font-face-range-order.html b/tests/wpt/tests/css/css-fonts/font-face-range-order.html index b198a9fb572..feae4803e7d 100644 --- a/tests/wpt/tests/css/css-fonts/font-face-range-order.html +++ b/tests/wpt/tests/css/css-fonts/font-face-range-order.html @@ -16,7 +16,7 @@ font-family: reversed-range-test; font-stretch: 200% 50%; font-style: oblique 90deg -90deg; font-weight: 900 100; -src: local(Ahem); +src: url(/fonts/Ahem.ttf); } diff --git a/tests/wpt/tests/css/css-fonts/generic-family-keywords-001.html b/tests/wpt/tests/css/css-fonts/generic-family-keywords-001.html index bd39bac3ff8..17a635bd8e8 100644 --- a/tests/wpt/tests/css/css-fonts/generic-family-keywords-001.html +++ b/tests/wpt/tests/css/css-fonts/generic-family-keywords-001.html @@ -24,12 +24,14 @@ div { let ahem = document.getElementById('ahem'); let ahem_expected_width = ahem.offsetWidth; kGenericFontFamilyKeywords.forEach(keyword => { - test(() => { + promise_test(async function() { let element = document.getElementById('test'); element.setAttribute("style", `font-family: ${keyword};`); let expected_width = element.offsetWidth; // Insert the @font-face rules for quoted and unquoted keywords. + // NOTE that we have to wait for font loads again at each step, + // as new asynchronous loads may be initiated when the style changes. document.documentElement.insertAdjacentHTML('beforeend', ` `); - + await document.fonts.ready; assert_equals(element.offsetWidth, expected_width, `unquoted ${keyword} does not match @font-face rule`); element.setAttribute("style", `font-family: "${keyword}";`); + await document.fonts.ready; assert_equals(element.offsetWidth, ahem_expected_width, `quoted ${keyword} matches @font-face rule`); }, `@font-face matching for quoted and unquoted ${keyword}`); }); diff --git a/tests/wpt/tests/css/css-fonts/generic-family-keywords-003.html b/tests/wpt/tests/css/css-fonts/generic-family-keywords-003.html index c787b59fb28..4b79b58a563 100644 --- a/tests/wpt/tests/css/css-fonts/generic-family-keywords-003.html +++ b/tests/wpt/tests/css/css-fonts/generic-family-keywords-003.html @@ -10,7 +10,7 @@ diff --git a/tests/wpt/tests/css/css-fonts/parsing/font-stretch-computed.html b/tests/wpt/tests/css/css-fonts/parsing/font-stretch-computed.html deleted file mode 100644 index 42e1989b1ae..00000000000 --- a/tests/wpt/tests/css/css-fonts/parsing/font-stretch-computed.html +++ /dev/null @@ -1,41 +0,0 @@ - - - - -CSS Fonts Module Level 4: getComputedStyle().fontStretch - - - - - - - - -
      -
      -
      - - - diff --git a/tests/wpt/tests/css/css-fonts/parsing/font-stretch-invalid.html b/tests/wpt/tests/css/css-fonts/parsing/font-stretch-invalid.html deleted file mode 100644 index 0f7f7c88975..00000000000 --- a/tests/wpt/tests/css/css-fonts/parsing/font-stretch-invalid.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - -CSS Fonts Module Level 4: parsing font-stretch with invalid values - - - - - - - - - - - diff --git a/tests/wpt/tests/css/css-fonts/parsing/font-stretch-valid.html b/tests/wpt/tests/css/css-fonts/parsing/font-stretch-valid.html deleted file mode 100644 index 90cde603133..00000000000 --- a/tests/wpt/tests/css/css-fonts/parsing/font-stretch-valid.html +++ /dev/null @@ -1,32 +0,0 @@ - - - - -CSS Fonts Module Level 4: parsing font-stretch with valid values - - - - - - - - - - diff --git a/tests/wpt/tests/css/css-fonts/parsing/font-width-computed.html b/tests/wpt/tests/css/css-fonts/parsing/font-width-computed.html new file mode 100644 index 00000000000..4e38a5d5e9a --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/parsing/font-width-computed.html @@ -0,0 +1,43 @@ + + + + +CSS Fonts Module Level 4: getComputedStyle().fontWidth + + + + + + + + +
      +
      +
      + + + diff --git a/tests/wpt/tests/css/css-fonts/parsing/font-width-invalid.html b/tests/wpt/tests/css/css-fonts/parsing/font-width-invalid.html new file mode 100644 index 00000000000..7f30eab3e5b --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/parsing/font-width-invalid.html @@ -0,0 +1,23 @@ + + + + +CSS Fonts Module Level 4: parsing font-width with invalid values + + + + + + + + + + + diff --git a/tests/wpt/tests/css/css-fonts/parsing/font-width-valid.html b/tests/wpt/tests/css/css-fonts/parsing/font-width-valid.html new file mode 100644 index 00000000000..44cbb37fd60 --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/parsing/font-width-valid.html @@ -0,0 +1,34 @@ + + + + +CSS Fonts Module Level 4: parsing font-width with valid values + + + + + + + + + + diff --git a/tests/wpt/tests/css/css-fonts/test-synthetic-bold-2-notref.html b/tests/wpt/tests/css/css-fonts/test-synthetic-bold-2-notref.html new file mode 100644 index 00000000000..ec5051519d7 --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/test-synthetic-bold-2-notref.html @@ -0,0 +1,24 @@ + + + + CSS Fonts: font-weight: bold; with bold font face without explicit font weight + + + + +
      Test fonts must be installed for this test: FAIL
      +

      Test passes if the second line is bolder than the first:

      +
      Filler text
      +
      Filler text
      + + diff --git a/tests/wpt/tests/css/css-fonts/test-synthetic-bold-2.html b/tests/wpt/tests/css/css-fonts/test-synthetic-bold-2.html new file mode 100644 index 00000000000..b7930c172c2 --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/test-synthetic-bold-2.html @@ -0,0 +1,32 @@ + + + + CSS Fonts: font-weight: bold; with bold font face without explicit font weight + + + + + + + + +
      Test fonts must be installed for this test: FAIL
      +

      Test passes if the second line is bolder than the first:

      +
      Filler text
      +
      Filler text
      + + diff --git a/tests/wpt/tests/css/css-fonts/test-synthetic-bold-notref.html b/tests/wpt/tests/css/css-fonts/test-synthetic-bold-notref.html new file mode 100644 index 00000000000..4a952acb46f --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/test-synthetic-bold-notref.html @@ -0,0 +1,18 @@ + + + + CSS Test: Test for synthetic bold rendering + + + +
      Test fonts must be installed for this test: PASS
      +

      Browser supports synthetic bolding if PASS appears on both lines and the second line appears bolder:

      +
      PASS
      +
      PASS
      + + diff --git a/tests/wpt/tests/css/css-fonts/test-synthetic-bold.html b/tests/wpt/tests/css/css-fonts/test-synthetic-bold.html new file mode 100644 index 00000000000..c2337c0a3ce --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/test-synthetic-bold.html @@ -0,0 +1,29 @@ + + + + CSS Test: Test for synthetic bold rendering + + + + + + + + + +
      Test fonts must be installed for this test: FAIL
      +

      Browser supports synthetic bolding if PASS appears on both lines and the second line appears bolder:

      +
      FAIL
      +
      FAIL
      + + diff --git a/tests/wpt/tests/css/css-fonts/test-synthetic-bold.xht b/tests/wpt/tests/css/css-fonts/test-synthetic-bold.xht deleted file mode 100644 index 2d34f02a92a..00000000000 --- a/tests/wpt/tests/css/css-fonts/test-synthetic-bold.xht +++ /dev/null @@ -1,28 +0,0 @@ - - - - CSS Test: Test for synthetic bold rendering - - - - - - - - -
      Test fonts must be installed for this test: FAIL
      -

      Browser supports synthetic bolding if PASS appears on both lines and the second line appears bolder:

      -
      FAIL
      -
      FAIL
      - - \ No newline at end of file diff --git a/tests/wpt/tests/css/css-fonts/test-synthetic-italic-4-notref.html b/tests/wpt/tests/css/css-fonts/test-synthetic-italic-4-notref.html new file mode 100644 index 00000000000..d6f9050be30 --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/test-synthetic-italic-4-notref.html @@ -0,0 +1,24 @@ + + + + CSS Fonts: font-style: italic; with italic font face without explicit font style + + + + +
      Test fonts must be installed for this test: FAIL
      +

      Test passes if the second line is more italic than the first:

      +
      Filler text
      +
      Filler text
      + + diff --git a/tests/wpt/tests/css/css-fonts/test-synthetic-italic-4.html b/tests/wpt/tests/css/css-fonts/test-synthetic-italic-4.html new file mode 100644 index 00000000000..fa1f815b421 --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/test-synthetic-italic-4.html @@ -0,0 +1,32 @@ + + + + CSS Fonts: font-style: italic; with italic font face without explicit font style + + + + + + + + +
      Test fonts must be installed for this test: FAIL
      +

      Test passes if the second line is more italic than the first:

      +
      Filler text
      +
      Filler text
      + + diff --git a/tests/wpt/tests/css/css-fonts/test-synthetic-italic-notref.html b/tests/wpt/tests/css/css-fonts/test-synthetic-italic-notref.html new file mode 100644 index 00000000000..3684f3ae804 --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/test-synthetic-italic-notref.html @@ -0,0 +1,19 @@ + + + + CSS Test: Test for synthetic italic rendering + + + +
      Test fonts must be installed for this test: FAIL
      +

      Browser supports synthetic italics if PASS appears on both lines and the second line is slanted right:

      +
      FAIL
      +
      FAIL
      + + diff --git a/tests/wpt/tests/css/css-fonts/test-synthetic-italic.html b/tests/wpt/tests/css/css-fonts/test-synthetic-italic.html new file mode 100644 index 00000000000..3f6764ecfa3 --- /dev/null +++ b/tests/wpt/tests/css/css-fonts/test-synthetic-italic.html @@ -0,0 +1,29 @@ + + + + CSS Test: Test for synthetic italic rendering + + + + + + + + + +
      Test fonts must be installed for this test: FAIL
      +

      Browser supports synthetic italics if PASS appears on both lines and the second line is slanted right:

      +
      FAIL
      +
      FAIL
      + + diff --git a/tests/wpt/tests/css/css-fonts/test-synthetic-italic.xht b/tests/wpt/tests/css/css-fonts/test-synthetic-italic.xht deleted file mode 100644 index 05b9cf67984..00000000000 --- a/tests/wpt/tests/css/css-fonts/test-synthetic-italic.xht +++ /dev/null @@ -1,28 +0,0 @@ - - - - CSS Test: Test for synthetic italic rendering - - - - - - - - -
      Test fonts must be installed for this test: FAIL
      -

      Browser supports synthetic italics if PASS appears on both lines and the second line is slanted right:

      -
      FAIL
      -
      FAIL
      - - \ No newline at end of file diff --git a/tests/wpt/tests/css/css-fonts/test_font_feature_values_parsing.html b/tests/wpt/tests/css/css-fonts/test_font_feature_values_parsing.html index 7a5844d5600..08c20142449 100644 --- a/tests/wpt/tests/css/css-fonts/test_font_feature_values_parsing.html +++ b/tests/wpt/tests/css/css-fonts/test_font_feature_values_parsing.html @@ -81,6 +81,7 @@ var testrules = [ { rule: _("@styleset { ok-1: 1; }"), serializationSame: true }, { rule: _("@annotation { ok-1: 3; }"), serializationSame: true }, { rule: _("@stylistic { blah: 3; }"), serializationSame: true }, + { rule: _("@stylistic { blah: sibling-index(); }"), serializationNoValueDefn: true }, { rule: makeRule("bongo", "\n@styleset\n { blah: 3; super-blah: 4 5;\n more-blah: 5 6 7;\n }"), serializationSame: true }, { rule: makeRule("bongo", "\n@styleset\n {\n blah:\n 3\n;\n super-blah:\n 4\n 5\n;\n more-blah:\n 5 6\n 7;\n }"), serializationSame: true }, diff --git a/tests/wpt/tests/css/css-fonts/variations/font-weight-matching.html b/tests/wpt/tests/css/css-fonts/variations/font-weight-matching.html index 5eb9a99f874..2788039a2d4 100644 --- a/tests/wpt/tests/css/css-fonts/variations/font-weight-matching.html +++ b/tests/wpt/tests/css/css-fonts/variations/font-weight-matching.html @@ -18,43 +18,43 @@ @font-face { font-family: fontMatch; - src: local('CSSTest Weights 900'), url('./resources/csstest-weights-900-kerned.ttf'); + src: url('./resources/csstest-weights-900-kerned.ttf'); font-weight: 100; } @font-face { font-family: fontMatch; - src: local('CSSTest Weights 800'), url('./resources/csstest-weights-800-kerned.ttf'); + src: url('./resources/csstest-weights-800-kerned.ttf'); font-weight: 250; } @font-face { font-family: fontMatch; - src: local('CSSTest Weights 700'), url('./resources/csstest-weights-700-kerned.ttf'); + src: url('./resources/csstest-weights-700-kerned.ttf'); font-weight: 400; } @font-face { font-family: fontMatch; - src: local('CSSTest Weights 600'), url('./resources/csstest-weights-600-kerned.ttf'); + src: url('./resources/csstest-weights-600-kerned.ttf'); font-weight: 450; } @font-face { font-family: fontMatch; - src: local('CSSTest Weights 300'), url('./resources/csstest-weights-300-kerned.ttf'); + src: url('./resources/csstest-weights-300-kerned.ttf'); font-weight: 500; } @font-face { font-family: fontMatch; - src: local('CSSTest Weights 200'), url('./resources/csstest-weights-200-kerned.ttf'); + src: url('./resources/csstest-weights-200-kerned.ttf'); font-weight: 750; } @font-face { font-family: fontMatch; - src: local('CSSTest Weights 100'), url('./resources/csstest-weights-100-kerned.ttf'); + src: url('./resources/csstest-weights-100-kerned.ttf'); font-weight: 900; } diff --git a/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-001-ref.html b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-001-ref.html new file mode 100644 index 00000000000..a7314fc7dca --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-001-ref.html @@ -0,0 +1,51 @@ + + + + +
      +
      +
      +
      +
      +
      + +
      +
      \ No newline at end of file diff --git a/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-002-ref.html b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-002-ref.html new file mode 100644 index 00000000000..42c5ef128b4 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-002-ref.html @@ -0,0 +1,51 @@ + + + + +
      +
      +
      +
      +
      +
      + +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-003-ref.html b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-003-ref.html new file mode 100644 index 00000000000..52e45c4f65f --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-003-ref.html @@ -0,0 +1,32 @@ + + + +
      +
      +
      +
      +
      +
      +
      +
      + + diff --git a/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-003.html b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-003.html new file mode 100644 index 00000000000..2ec4a45beb7 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-003.html @@ -0,0 +1,51 @@ + + + + column-rule-color invalidates and paints correctly on multi-col + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      + + + diff --git a/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-004-ref.html b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-004-ref.html new file mode 100644 index 00000000000..52e45c4f65f --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-004-ref.html @@ -0,0 +1,32 @@ + + + +
      +
      +
      +
      +
      +
      +
      +
      + + diff --git a/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-004.html b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-004.html new file mode 100644 index 00000000000..c5d1e3f99cc --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-004.html @@ -0,0 +1,51 @@ + + + + column-rule-color declared with repeaters doesn't crash when recomputing + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      + + + diff --git a/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-006-ref.html b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-006-ref.html new file mode 100644 index 00000000000..09bef415d40 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/agnostic/gap-decorations-006-ref.html @@ -0,0 +1,52 @@ + + + + +
      +
      +
      +
      +
      +
      + + +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-001.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-001.html similarity index 91% rename from tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-001.html rename to tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-001.html index 0667693f38c..b32e2f13d8c 100644 --- a/tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-001.html +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-001.html @@ -3,7 +3,7 @@ CSS Gap Decorations: column and row gaps are painted. - + + +
      +
      One
      +
      Two
      +
      Three
      +
      Four
      +
      Five
      +
      Six
      +
      Seven
      +
      Eight
      +
      + +
      +
      + +
      +
      +
      +
      + +
      +
      +
      +
      + +
      \ No newline at end of file diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-012.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-012.html new file mode 100644 index 00000000000..2f1f95c00c8 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-012.html @@ -0,0 +1,50 @@ + + + CSS Gap Decorations: flex column and are painted with column over rows. + + + + + + +
      +
      One
      +
      Two
      +
      Three
      +
      Four
      +
      Five
      +
      Six
      +
      Seven
      +
      Eight
      +
      \ No newline at end of file diff --git a/tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-013-ref.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-013-ref.html similarity index 100% rename from tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-013-ref.html rename to tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-013-ref.html diff --git a/tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-013.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-013.html similarity index 100% rename from tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-013.html rename to tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-013.html diff --git a/tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-014-ref.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-014-ref.html similarity index 100% rename from tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-014-ref.html rename to tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-014-ref.html index ab28707785e..59ce37deaec 100644 --- a/tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-014-ref.html +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-014-ref.html @@ -63,9 +63,6 @@
      Eight
      -
      -
      -
      @@ -77,3 +74,6 @@
      + +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-014.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-014.html similarity index 100% rename from tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-014.html rename to tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-014.html diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-015-ref.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-015-ref.html new file mode 100644 index 00000000000..48745eff931 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-015-ref.html @@ -0,0 +1,74 @@ + + + + + +
      +
      One
      +
      Two
      +
      Three
      +
      Four
      +
      Five
      +
      Six
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-015.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-015.html similarity index 89% rename from tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-015.html rename to tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-015.html index 6cba13ba216..8d100978205 100644 --- a/tests/wpt/tests/css/css-gaps/tentative/flex/flex-gap-decorations-015.html +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-015.html @@ -1,9 +1,9 @@ - CSS Gap Decorations: flex column and row gaps are painted column flex direction + CSS Gap Decorations: flex column and row gaps are painted column flex direction. - + + +
      +
      One
      +
      Two
      +
      Three
      +
      Four
      +
      Five
      +
      Six
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-016.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-016.html new file mode 100644 index 00000000000..9f956369d48 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-016.html @@ -0,0 +1,47 @@ + + + CSS Gap Decorations: flex column and row gaps are painted in the column-reverse flex direction. + + + + + + +
      +
      One
      +
      Two
      +
      Three
      +
      Four
      +
      Five
      +
      Six
      +
      diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-017-ref.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-017-ref.html new file mode 100644 index 00000000000..031be2abaf1 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-017-ref.html @@ -0,0 +1,36 @@ + + + + + +
      +
      One
      +
      Two
      +
      Three
      +
      diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-017.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-017.html new file mode 100644 index 00000000000..21fb643a241 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-017.html @@ -0,0 +1,40 @@ + + + CSS Gap Decorations: column-rule-style inset should paint as ridge. + + + + + + +
      +
      One
      +
      Two
      +
      Three
      +
      diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-018-ref.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-018-ref.html new file mode 100644 index 00000000000..644a409bd97 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-018-ref.html @@ -0,0 +1,36 @@ + + + + + +
      +
      One
      +
      Two
      +
      Three
      +
      diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-018.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-018.html new file mode 100644 index 00000000000..073fa1b9af7 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-018.html @@ -0,0 +1,40 @@ + + + CSS Gap Decorations: column-rule-style outset should paint as groove. + + + + + + +
      +
      One
      +
      Two
      +
      Three
      +
      diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-019.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-019.html new file mode 100644 index 00000000000..5bad5f00d49 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-019.html @@ -0,0 +1,33 @@ + + + CSS Gap Decorations: Painting flex gaps with bi-directional `rule-color` shorthand. + + + + + +

      Test passes if there is a filled green square and no red.

      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-020-ref.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-020-ref.html new file mode 100644 index 00000000000..b708a78a347 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-020-ref.html @@ -0,0 +1,55 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-020.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-020.html new file mode 100644 index 00000000000..6c07493d14d --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-020.html @@ -0,0 +1,49 @@ + + + CSS Gap Decorations: flex gaps are painted when container scrolls. + + + + + +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-021-ref.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-021-ref.html new file mode 100644 index 00000000000..1e28d73b4e2 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-021-ref.html @@ -0,0 +1,55 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-021.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-021.html new file mode 100644 index 00000000000..c59db4f635b --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-021.html @@ -0,0 +1,49 @@ + + + CSS Gap Decorations: flex gaps are painted with overflow hidden. + + + + + +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-022.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-022.html new file mode 100644 index 00000000000..5f3b512ef76 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-022.html @@ -0,0 +1,46 @@ + + + CSS Gap Decorations: flex column gaps are painted with different sized gaps and row-rule-outset 0. + + + + + +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-023.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-023.html new file mode 100644 index 00000000000..9a7c429a1a1 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-023.html @@ -0,0 +1,57 @@ + + + CSS Gap Decorations: flex gaps are painted when going from no gap rule to a gap rule that would be visible. + + + + + + + + +
      +
      +
      +
      +
      +
      + + diff --git a/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-024.html b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-024.html new file mode 100644 index 00000000000..66cb0e86062 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/flex/flex-gap-decorations-024.html @@ -0,0 +1,38 @@ + + + + CSS Gap Decorations: shorthands column rules with !important are painted and overriden correctly. + + + + + +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/tentative/grid/grid-gap-decorations-001.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-001.html similarity index 92% rename from tests/wpt/tests/css/css-gaps/tentative/grid/grid-gap-decorations-001.html rename to tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-001.html index 5fe13a11bbf..648f3f26e56 100644 --- a/tests/wpt/tests/css/css-gaps/tentative/grid/grid-gap-decorations-001.html +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-001.html @@ -3,7 +3,7 @@ CSS Gap Decorations: column and row gaps are painted. - + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      + +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-016.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-016.html new file mode 100644 index 00000000000..a0618e44b56 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-016.html @@ -0,0 +1,60 @@ + + + CSS Gap Decorations: Grid gaps are painted with multiple line-style values for *-rule-style. + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-017-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-017-ref.html new file mode 100644 index 00000000000..ac0bbd1f1e1 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-017-ref.html @@ -0,0 +1,135 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-017.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-017.html new file mode 100644 index 00000000000..be0f7fc2751 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-017.html @@ -0,0 +1,69 @@ + + + CSS Gap Decorations: Grid gaps are painted with multiple line-style values for *-rule-style. + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-018-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-018-ref.html new file mode 100644 index 00000000000..072dc508ea3 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-018-ref.html @@ -0,0 +1,134 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-018.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-018.html new file mode 100644 index 00000000000..dc5e2e38e0b --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-018.html @@ -0,0 +1,71 @@ + + + CSS Gap Decorations: Grid gaps are painted with multiple line-width values for *-rule-width. + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-019-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-019-ref.html new file mode 100644 index 00000000000..e9f8a87979d --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-019-ref.html @@ -0,0 +1,135 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-019.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-019.html new file mode 100644 index 00000000000..637e0467a80 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-019.html @@ -0,0 +1,71 @@ + + + CSS Gap Decorations: Grid gaps are painted with multiple line-width values for *-rule-width. + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-020-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-020-ref.html new file mode 100644 index 00000000000..6f31b5a09fc --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-020-ref.html @@ -0,0 +1,37 @@ + + + + +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-020.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-020.html new file mode 100644 index 00000000000..eb91644053f --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-020.html @@ -0,0 +1,41 @@ + + + CSS Gap Decorations: row-rule-style inset should paint as ridge. + + + + + +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-021-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-021-ref.html new file mode 100644 index 00000000000..de36d206606 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-021-ref.html @@ -0,0 +1,37 @@ + + + + +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-021.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-021.html new file mode 100644 index 00000000000..63db3b4e048 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-021.html @@ -0,0 +1,41 @@ + + + CSS Gap Decorations: row-rule-style outset should paint as groove. + + + + + +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-022.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-022.html new file mode 100644 index 00000000000..ad73239bade --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-022.html @@ -0,0 +1,33 @@ + + + CSS Gap Decorations: Painting grid gaps with bi-directional `rule-color` shorthand. + + + + + +

      Test passes if there is a filled green square and no red.

      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-023-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-023-ref.html new file mode 100644 index 00000000000..e630694c6a5 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-023-ref.html @@ -0,0 +1,88 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-023.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-023.html new file mode 100644 index 00000000000..706f66ebcfa --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-023.html @@ -0,0 +1,45 @@ + + + + CSS Gap Decorations: Painting column over rows. + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-024-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-024-ref.html new file mode 100644 index 00000000000..94b1eccd747 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-024-ref.html @@ -0,0 +1,67 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-024.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-024.html new file mode 100644 index 00000000000..104df87c7e2 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-024.html @@ -0,0 +1,47 @@ + + + CSS Gap Decorations: Grid column gaps are painted with :visited styles when a single color is used. + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-025-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-025-ref.html new file mode 100644 index 00000000000..3834da46a5d --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-025-ref.html @@ -0,0 +1,63 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-025.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-025.html new file mode 100644 index 00000000000..fc48f15f5c2 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-025.html @@ -0,0 +1,47 @@ + + + CSS Gap Decorations: Grid column gaps are painted with default :visited styles when multiple colors are used. + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-026-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-026-ref.html new file mode 100644 index 00000000000..d57c622810d --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-026-ref.html @@ -0,0 +1,59 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-026.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-026.html new file mode 100644 index 00000000000..17e0a99dd84 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-026.html @@ -0,0 +1,43 @@ + + + CSS Gap Decorations: Grid gaps are painted properly when currentColor is used. + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-027-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-027-ref.html new file mode 100644 index 00000000000..7e0d9cbcd6f --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-027-ref.html @@ -0,0 +1,64 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-027.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-027.html new file mode 100644 index 00000000000..e39305f74b8 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-027.html @@ -0,0 +1,46 @@ + + + CSS Gap Decorations: Grid gaps are painted properly when currentColor is used in :visited styles. + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-028.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-028.html new file mode 100644 index 00000000000..125a34ca966 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-028.html @@ -0,0 +1,41 @@ + + + + +CSS Gap Decoration: :visited column-rule-color style is not leaked via getComputedStyle + + + + + + + +
      +
      + + + + diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029-ref.html new file mode 100644 index 00000000000..35450e07ae5 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029-ref.html @@ -0,0 +1,70 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029.html new file mode 100644 index 00000000000..6da75548d94 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-029.html @@ -0,0 +1,48 @@ + + + CSS Gap Decorations: Gaps are painted when items overflow container. + + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030-ref.html new file mode 100644 index 00000000000..d4953ae2ddd --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030-ref.html @@ -0,0 +1,45 @@ + + + + +
      +
      +
      +
      +
      + +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030.html new file mode 100644 index 00000000000..ef4507ef926 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-030.html @@ -0,0 +1,37 @@ + + + CSS Gap Decorations: Gaps are painted when items overflow container - no row gaps. + + + + + + + +
      +
      +
      +
      +
      + diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031-ref.html new file mode 100644 index 00000000000..0615305bc9f --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031-ref.html @@ -0,0 +1,45 @@ + + + + +
      +
      +
      +
      +
      + +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031.html new file mode 100644 index 00000000000..530b661bfe3 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-031.html @@ -0,0 +1,38 @@ + + + CSS Gap Decorations: Gaps are painted when items overflow container - no column gaps. + + + + + + + +
      +
      +
      +
      +
      + diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032-ref.html new file mode 100644 index 00000000000..f90e3dd6813 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032-ref.html @@ -0,0 +1,71 @@ + + + + +
      + +
      +
      + +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032.html new file mode 100644 index 00000000000..ac2d38fdda0 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-032.html @@ -0,0 +1,39 @@ + + + CSS Gap Decorations: Gaps are painted when rows are dynamically added and overflow container. + + + + + + + +
      + + diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033-ref.html new file mode 100644 index 00000000000..e26143a6b93 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033-ref.html @@ -0,0 +1,72 @@ + + + + +
      + +
      +
      + +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033.html new file mode 100644 index 00000000000..47ef35cc368 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-033.html @@ -0,0 +1,38 @@ + + + CSS Gap Decorations: Decorations are painted when rule thickness is greater than gap size. + + + + + + + +
      + + diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-034-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-034-ref.html new file mode 100644 index 00000000000..4e964879e79 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-034-ref.html @@ -0,0 +1,71 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-034.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-034.html new file mode 100644 index 00000000000..afe20ef5117 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-034.html @@ -0,0 +1,48 @@ + + + CSS Gap Decorations: Gaps are painted with container with overflow hidden, with JS scroll + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + + + diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-035-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-035-ref.html new file mode 100644 index 00000000000..851360a99a4 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-035-ref.html @@ -0,0 +1,71 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-035.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-035.html new file mode 100644 index 00000000000..d2fa76094a9 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-035.html @@ -0,0 +1,44 @@ + + + CSS Gap Decorations: Gaps are painted when container scrolls. + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-036-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-036-ref.html new file mode 100644 index 00000000000..29ed684d85f --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-036-ref.html @@ -0,0 +1,71 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-036.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-036.html new file mode 100644 index 00000000000..8b1e1e2e1c2 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-036.html @@ -0,0 +1,44 @@ + + + CSS Gap Decorations: Gaps are painted with container with overflow hidden. + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-037-ref.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-037-ref.html new file mode 100644 index 00000000000..4a2ee5bd5c6 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-037-ref.html @@ -0,0 +1,70 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-037.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-037.html new file mode 100644 index 00000000000..656bfee7dfc --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-037.html @@ -0,0 +1,48 @@ + + + CSS Gap Decorations: Gaps are painted with container with overflow scroll and no background. + + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-039.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-039.html new file mode 100644 index 00000000000..c03d482ac49 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-039.html @@ -0,0 +1,63 @@ + + + CSS Gap Decorations: grid column gaps are painted with solid styling. + + + + + + + + +
      +
      +
      +
      +
      +
      + + diff --git a/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-38.html b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-38.html new file mode 100644 index 00000000000..8b87bcd4860 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/grid/grid-gap-decorations-38.html @@ -0,0 +1,44 @@ + + + CSS Gap Decorations: grid column gaps are painted with different sized gaps and column-rule-outset 0. + + + + + +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-001-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-001-ref.html new file mode 100644 index 00000000000..834c96ccd39 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-001-ref.html @@ -0,0 +1,49 @@ + + + + + +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-001.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-001.html new file mode 100644 index 00000000000..c3752156c7d --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-001.html @@ -0,0 +1,45 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with column-wrap. + + + + + + +
      +

      +

      +

      +

      +

      +

      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-002-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-002-ref.html new file mode 100644 index 00000000000..d132f0ca677 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-002-ref.html @@ -0,0 +1,96 @@ + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-002.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-002.html new file mode 100644 index 00000000000..0b632fa94ac --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-002.html @@ -0,0 +1,59 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with column-wrap and a spanner. + + + + + + +
      +

      +

      +

      + +

      +

      +

      +

      +

      +

      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-003-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-003-ref.html new file mode 100644 index 00000000000..cd26da24389 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-003-ref.html @@ -0,0 +1,59 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-003.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-003.html new file mode 100644 index 00000000000..2f2473fd046 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-003.html @@ -0,0 +1,35 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted basic case. + + + + + + +
      +

      +

      +

      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-004-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-004-ref.html new file mode 100644 index 00000000000..002b1f2b594 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-004-ref.html @@ -0,0 +1,78 @@ + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-004.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-004.html new file mode 100644 index 00000000000..0dcbeef4df8 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-004.html @@ -0,0 +1,46 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with smaller width than gap and column-wrap. + + + + + + +
      +

      +

      +

      +

      +

      +

      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-005-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-005-ref.html new file mode 100644 index 00000000000..6543021c979 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-005-ref.html @@ -0,0 +1,64 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-005.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-005.html new file mode 100644 index 00000000000..daa65e904e7 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-005.html @@ -0,0 +1,64 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with nested multicol with column wrap. + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-006-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-006-ref.html new file mode 100644 index 00000000000..4cb56d207fe --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-006-ref.html @@ -0,0 +1,52 @@ + + + + +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-006.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-006.html new file mode 100644 index 00000000000..64b3b007ab5 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-006.html @@ -0,0 +1,39 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with nested fragmented multicol. + + + + + +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-007-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-007-ref.html new file mode 100644 index 00000000000..09973ddc690 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-007-ref.html @@ -0,0 +1,133 @@ + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-007.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-007.html new file mode 100644 index 00000000000..1f23ed7f7ca --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-007.html @@ -0,0 +1,44 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with nested fragmented multicol. + + + + + + +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-008-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-008-ref.html new file mode 100644 index 00000000000..d49c2f50f19 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-008-ref.html @@ -0,0 +1,115 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-008.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-008.html new file mode 100644 index 00000000000..ed299b5d9f6 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-008.html @@ -0,0 +1,63 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with 0px outset and rule-break intersection. + + + + + + +
      +

      +

      +

      + +

      +

      +

      +

      +

      +

      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-009-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-009-ref.html new file mode 100644 index 00000000000..124bf7eaeb1 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-009-ref.html @@ -0,0 +1,116 @@ + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-009.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-009.html new file mode 100644 index 00000000000..b00c6a70f8a --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-009.html @@ -0,0 +1,63 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with 2px outset and rule-break intersection. + + + + + + +
      +

      +

      +

      + +

      +

      +

      +

      +

      +

      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-010-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-010-ref.html new file mode 100644 index 00000000000..9582ab4103d --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-010-ref.html @@ -0,0 +1,135 @@ + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-010.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-010.html new file mode 100644 index 00000000000..61bb4a9b933 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-010.html @@ -0,0 +1,63 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with -2px outset and rule-break intersection. + + + + + + +
      +

      +

      +

      + +

      +

      +

      +

      +

      +

      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-011-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-011-ref.html new file mode 100644 index 00000000000..3387918d399 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-011-ref.html @@ -0,0 +1,115 @@ + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-011.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-011.html new file mode 100644 index 00000000000..cf588ded41e --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-011.html @@ -0,0 +1,63 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with 100% outset and rule-break intersection. + + + + + + +
      +

      +

      +

      + +

      +

      +

      +

      +

      +

      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-012-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-012-ref.html new file mode 100644 index 00000000000..9fb5e85e920 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-012-ref.html @@ -0,0 +1,104 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-012.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-012.html new file mode 100644 index 00000000000..078b810cf8e --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-012.html @@ -0,0 +1,60 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with 2px width and rule-break none. + + + + + +
      +

      +

      +

      + +

      +

      +

      +

      +

      +

      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-013-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-013-ref.html new file mode 100644 index 00000000000..8e426d8ed32 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-013-ref.html @@ -0,0 +1,41 @@ + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-013.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-013.html new file mode 100644 index 00000000000..d4170084f06 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-013.html @@ -0,0 +1,28 @@ + + + + + + +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-014-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-014-ref.html new file mode 100644 index 00000000000..9d9674bd476 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-014-ref.html @@ -0,0 +1,62 @@ + + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-014.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-014.html new file mode 100644 index 00000000000..1dcd393d99a --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-014.html @@ -0,0 +1,47 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with column-wrap, rule-break-intersection, and fewer + columns in last row. + + + + + + +
      +

      +

      +

      +

      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-015-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-015-ref.html new file mode 100644 index 00000000000..2fee77be84c --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-015-ref.html @@ -0,0 +1,70 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      + +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-015.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-015.html new file mode 100644 index 00000000000..65af5112097 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-015.html @@ -0,0 +1,55 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with different num of columns per row. + + + + + +
      +
      +
      + +
      +
      +
      + +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-016-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-016-ref.html new file mode 100644 index 00000000000..8b2458fccce --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-016-ref.html @@ -0,0 +1,70 @@ + + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      + +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-016.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-016.html new file mode 100644 index 00000000000..da205e0b88b --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-016.html @@ -0,0 +1,55 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with different num of columns per row. + + + + + +
      +
      +
      +
      +
      + +
      +
      +
      + +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017-ref.html new file mode 100644 index 00000000000..23fd089ac8e --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017-ref.html @@ -0,0 +1,49 @@ + + + + + +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017.html new file mode 100644 index 00000000000..c7d1fe42584 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-017.html @@ -0,0 +1,48 @@ + + + CSS Gap Decorations: Multicolumn gap decorations painted with column-wrap and with different sized gaps and row-rule-outset 0. + + + + + + +
      +

      +

      +

      +

      +

      +

      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-018-ref.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-018-ref.html new file mode 100644 index 00000000000..521d3925062 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-018-ref.html @@ -0,0 +1,50 @@ + + + + + +
      +
      +
      +
      +
      + +
      +
      +
      diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-019.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-019.html new file mode 100644 index 00000000000..c4f81bb95ea --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-decorations-019.html @@ -0,0 +1,55 @@ + + + + column-rule-color declared with repeaters that depend on currentcolor should be recomputed when currentcolor changes + + + + + + + +
      +
      +
      +
      +
      +
      +
      +
      + + + diff --git a/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-intersections-018.html b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-intersections-018.html new file mode 100644 index 00000000000..b8f8e1619a0 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/multicol/multicol-gap-intersections-018.html @@ -0,0 +1,60 @@ + + + CSS Gap Decorations: Multicolumn gap decorations that are added later are painted. + + + + + + + + +
      +

      +

      +

      +

      +

      +

      +
      + + diff --git a/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-bidirectional-shorthands.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-bidirectional-shorthands.html new file mode 100644 index 00000000000..2ace9f255fb --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-bidirectional-shorthands.html @@ -0,0 +1,67 @@ + + + + +CSS Gap Decoration: shorthand rule-* computed style values from longhands + + + + + + +
      +
      + + + + diff --git a/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-col-rule-width.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-col-rule-width.html new file mode 100644 index 00000000000..db7b97e74a5 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-col-rule-width.html @@ -0,0 +1,51 @@ + + + + +CSS Gap Decorations: Ensure getComputedStyle for column-rule-width is as specified with multiple values + + + + + + +
      +
      +
      + + + + diff --git a/tests/wpt/tests/css/css-gaps/tentative/parsing/gap-decorations-color-computed.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-color-computed.html similarity index 95% rename from tests/wpt/tests/css/css-gaps/tentative/parsing/gap-decorations-color-computed.html rename to tests/wpt/tests/css/css-gaps/parsing/gap-decorations-color-computed.html index 3ef68de7392..fbc3c3f4c97 100644 --- a/tests/wpt/tests/css/css-gaps/tentative/parsing/gap-decorations-color-computed.html +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-color-computed.html @@ -18,7 +18,7 @@ } + + + +
      + + + diff --git a/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-computed.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-computed.html new file mode 100644 index 00000000000..63e108d2886 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-computed.html @@ -0,0 +1,61 @@ + + + + +CSS Gap Decoration: *rule getComputedStyle() + + + + + + + + +
      +
      + + + + diff --git a/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-from-longhands.tentative.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-from-longhands.tentative.html new file mode 100644 index 00000000000..ed7c9a317e6 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-from-longhands.tentative.html @@ -0,0 +1,89 @@ + + + + CSS Gap Decorations: individual separate longhands form shorthand + + + + + + +
      + diff --git a/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-invalid.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-invalid.html new file mode 100644 index 00000000000..bbc347cc79e --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-invalid.html @@ -0,0 +1,30 @@ + + + + +CSS Gap Decorations: *-rule parsing + + + + + + + + + + + diff --git a/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-roundtrip.tentative.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-roundtrip.tentative.html new file mode 100644 index 00000000000..5955af8923c --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-roundtrip.tentative.html @@ -0,0 +1,21 @@ + + + + CSS Gap Decorations: *rule properties round trips properly + + + + +
      + diff --git a/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-valid.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-valid.html new file mode 100644 index 00000000000..cc05cf3b9e6 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand-valid.html @@ -0,0 +1,59 @@ + + + + +CSS Gap Decorations: parsing column-rule with valid values + + + + + + + + + + + diff --git a/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand.html new file mode 100644 index 00000000000..c6c2a375167 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-rule-shorthand.html @@ -0,0 +1,195 @@ + + + + +CSS Gap Decorations: *-rule sets longhands + + + + + + + + + + diff --git a/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-style-computed.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-style-computed.html new file mode 100644 index 00000000000..8c78bf618e3 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-style-computed.html @@ -0,0 +1,43 @@ + + + + +CSS Gap Decoration: column-rule-style getComputedStyle() + + + + + + + + +
      + + + diff --git a/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-style-invalid.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-style-invalid.html new file mode 100644 index 00000000000..48ac0680338 --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-style-invalid.html @@ -0,0 +1,26 @@ + + + + +CSS Gap Decorations: column-rule-style parsing + + + + + + + + + + + diff --git a/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-style-valid.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-style-valid.html new file mode 100644 index 00000000000..1eb259a82bc --- /dev/null +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-style-valid.html @@ -0,0 +1,47 @@ + + + + +CSS Gap Decorations: parsing column-rule-style with valid values + + + + + + + + + + + diff --git a/tests/wpt/tests/css/css-gaps/tentative/parsing/gap-decorations-width-computed.html b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-width-computed.html similarity index 95% rename from tests/wpt/tests/css/css-gaps/tentative/parsing/gap-decorations-width-computed.html rename to tests/wpt/tests/css/css-gaps/parsing/gap-decorations-width-computed.html index f24565ba5ee..299cbe0f3bb 100644 --- a/tests/wpt/tests/css/css-gaps/tentative/parsing/gap-decorations-width-computed.html +++ b/tests/wpt/tests/css/css-gaps/parsing/gap-decorations-width-computed.html @@ -21,7 +21,7 @@ - - - - -
      - - - diff --git a/tests/wpt/tests/css/css-gaps/tentative/parsing/gap-decorations-style-invalid.html b/tests/wpt/tests/css/css-gaps/tentative/parsing/gap-decorations-style-invalid.html deleted file mode 100644 index af1d5ccae57..00000000000 --- a/tests/wpt/tests/css/css-gaps/tentative/parsing/gap-decorations-style-invalid.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - -CSS Gap Decorations: column-rule-style parsing - - - - - - - - - - - diff --git a/tests/wpt/tests/css/css-gaps/tentative/parsing/gap-decorations-style-valid.html b/tests/wpt/tests/css/css-gaps/tentative/parsing/gap-decorations-style-valid.html deleted file mode 100644 index 1f41238345c..00000000000 --- a/tests/wpt/tests/css/css-gaps/tentative/parsing/gap-decorations-style-valid.html +++ /dev/null @@ -1,58 +0,0 @@ - - - - -CSS Gap Decorations: parsing column-rule-style with valid values - - - - - - - - - - - diff --git a/tests/wpt/tests/css/css-grid/abspos/abspos-in-flexbox-in-grid-crash.html b/tests/wpt/tests/css/css-grid/abspos/abspos-in-flexbox-in-grid-crash.html new file mode 100644 index 00000000000..a2f71fb78e7 --- /dev/null +++ b/tests/wpt/tests/css/css-grid/abspos/abspos-in-flexbox-in-grid-crash.html @@ -0,0 +1,12 @@ + + + +
      +
      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-grid/firefox-bug-1958131-crash.html b/tests/wpt/tests/css/css-grid/firefox-bug-1958131-crash.html new file mode 100644 index 00000000000..6cfb2069f4d --- /dev/null +++ b/tests/wpt/tests/css/css-grid/firefox-bug-1958131-crash.html @@ -0,0 +1,5 @@ + + + +
      +
      diff --git a/tests/wpt/tests/css/css-grid/grid-definition/grid-auto-fit-with-calc.html b/tests/wpt/tests/css/css-grid/grid-definition/grid-auto-fit-with-calc.html new file mode 100644 index 00000000000..4b655c1e121 --- /dev/null +++ b/tests/wpt/tests/css/css-grid/grid-definition/grid-auto-fit-with-calc.html @@ -0,0 +1,23 @@ + + + + + + + + + +

      Test passes if there is a filled green square.

      +
      +
      +
      +
      + + diff --git a/tests/wpt/tests/css/css-grid/grid-extrinsically-sized-mutations.html b/tests/wpt/tests/css/css-grid/grid-extrinsically-sized-mutations.html new file mode 100644 index 00000000000..ea9eb7ca242 --- /dev/null +++ b/tests/wpt/tests/css/css-grid/grid-extrinsically-sized-mutations.html @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + +
      +
      +
      x x x x
      +
      +
      + + +
      +
      +
      x x x
      +
      +
      + + +
      +
      xx
      +
      + + +
      +
      xx
      +
      + + +
      +
      +
      x x x
      +
      +
      + + +
      +
      +
      xx
      +
      +
      + + +
      +
      +
      x x x
      +
      +
      + + +
      +
      +
      xx xx
      +
      +
      + + + + diff --git a/tests/wpt/tests/css/css-grid/grid-fragmentation-between-rows-001-print-ref.tentative.html b/tests/wpt/tests/css/css-grid/grid-fragmentation-between-rows-001-print-ref.tentative.html index 78464712c5c..ba97d33c8b6 100644 --- a/tests/wpt/tests/css/css-grid/grid-fragmentation-between-rows-001-print-ref.tentative.html +++ b/tests/wpt/tests/css/css-grid/grid-fragmentation-between-rows-001-print-ref.tentative.html @@ -1,4 +1,9 @@ +
      Test passes if there is two purple boxes on both page 1 and page 2 in print mode. (Ctrl+P, with "print backgrounds" enabled)
      diff --git a/tests/wpt/tests/css/css-grid/grid-fragmentation-between-rows-001-print.tentative.html b/tests/wpt/tests/css/css-grid/grid-fragmentation-between-rows-001-print.tentative.html index 01fd97528fc..af345718e6e 100644 --- a/tests/wpt/tests/css/css-grid/grid-fragmentation-between-rows-001-print.tentative.html +++ b/tests/wpt/tests/css/css-grid/grid-fragmentation-between-rows-001-print.tentative.html @@ -2,6 +2,11 @@ +
      Test passes if there is two purple boxes on both page 1 and page 2 in print mode. (Ctrl+P, with "print backgrounds" enabled)
      diff --git a/tests/wpt/tests/css/css-grid/grid-items/replaced-element-016.tentative.html b/tests/wpt/tests/css/css-grid/grid-items/replaced-element-016.tentative.html new file mode 100644 index 00000000000..2d5b401b8d2 --- /dev/null +++ b/tests/wpt/tests/css/css-grid/grid-items/replaced-element-016.tentative.html @@ -0,0 +1,13 @@ + + + + + + + + + +

      Test passes if there is a filled green square.

      +
      + +
      diff --git a/tests/wpt/tests/css/css-grid/grid-items/replaced-element-017.tentative.html b/tests/wpt/tests/css/css-grid/grid-items/replaced-element-017.tentative.html new file mode 100644 index 00000000000..575fa8c7817 --- /dev/null +++ b/tests/wpt/tests/css/css-grid/grid-items/replaced-element-017.tentative.html @@ -0,0 +1,13 @@ + + + + + + + + + +

      Test passes if there is a filled green square.

      +
      + +
      diff --git a/tests/wpt/tests/css/css-grid/layout-algorithm/grid-intrinsic-track-sizes-min-size-001.html b/tests/wpt/tests/css/css-grid/layout-algorithm/grid-intrinsic-track-sizes-min-size-001.html new file mode 100644 index 00000000000..d2c8f504bac --- /dev/null +++ b/tests/wpt/tests/css/css-grid/layout-algorithm/grid-intrinsic-track-sizes-min-size-001.html @@ -0,0 +1,29 @@ + + +CSS Grid Layout Test: Test that spanning grid items with auto size that should not have content-based automatic minimum size. + + + + + +

      Test passes if there is a filled green square and no red.

      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-grid/parsing/grid-columns-rows-get-set-multiple.html b/tests/wpt/tests/css/css-grid/parsing/grid-columns-rows-get-set-multiple.html index 99af0110be7..c08290af9d5 100644 --- a/tests/wpt/tests/css/css-grid/parsing/grid-columns-rows-get-set-multiple.html +++ b/tests/wpt/tests/css/css-grid/parsing/grid-columns-rows-get-set-multiple.html @@ -268,7 +268,6 @@ setup({ explicit_done: true }); document.fonts.ready.then(() => { testGridDefinitionsValues("gridWithFixedElement", "7px 11px", "17px 2px"); testGridDefinitionsValues("gridWithPercentElement", "400px 800px", "150px 450px"); - // This test failing in Firefox is caused by https://bugzilla.mozilla.org/show_bug.cgi?id=1481876 testGridDefinitionsValues("gridWithPercentWithoutSize", "3.5px 7px", "4px 12px"); testGridDefinitionsValues("gridWithAutoElement", "0px 17px", "0px 3px"); testGridDefinitionsValues("gridWithEMElement", "100px 120px", "150px 170px"); diff --git a/tests/wpt/tests/css/css-grid/subgrid/line-names-014.html b/tests/wpt/tests/css/css-grid/subgrid/line-names-014.html new file mode 100644 index 00000000000..fe1d5d46633 --- /dev/null +++ b/tests/wpt/tests/css/css-grid/subgrid/line-names-014.html @@ -0,0 +1,36 @@ + + +CSS Grid Test: Clamping a nested subgrid's grid-template-areas + + + + +

      Test passes if there is a filled green square and no red.

      +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-grid/subgrid/line-names-015.html b/tests/wpt/tests/css/css-grid/subgrid/line-names-015.html new file mode 100644 index 00000000000..6f5479abf89 --- /dev/null +++ b/tests/wpt/tests/css/css-grid/subgrid/line-names-015.html @@ -0,0 +1,34 @@ + + +CSS Grid Test: Clamping a nested subgrid's grid-template-areas in one dimension + + + + +

      Test passes if there is a filled green square and no red.

      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-images/conic-gradient-angle-negative.html b/tests/wpt/tests/css/css-images/conic-gradient-angle-negative.html index 5a7030d0ff0..faf9a2d2033 100644 --- a/tests/wpt/tests/css/css-images/conic-gradient-angle-negative.html +++ b/tests/wpt/tests/css/css-images/conic-gradient-angle-negative.html @@ -3,7 +3,7 @@ Conic gradient with negative angle parameter - + - - -
      -
      -
      -
      -
      - - + .test1 { + background: linear-gradient(90deg in srgb, hsl(60deg 0 50%), yellow); + } + + .test2 { + background: linear-gradient(90deg in srgb, hsl(20deg 0 50%), yellow); + } + + .test3 { + background: linear-gradient(90deg in srgb, hsl(60deg none 50%), yellow); + } + + .test4 { + background: linear-gradient(90deg in srgb, hsl(none 0 50%), yellow); + } + + .test5 { + background: linear-gradient(90deg in srgb, hsl(none none 50%), yellow); + } + + +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-images/gradient/gradient-analogous-missing-components-002.html b/tests/wpt/tests/css/css-images/gradient/gradient-analogous-missing-components-002.html index 96038650136..881ba723f91 100644 --- a/tests/wpt/tests/css/css-images/gradient/gradient-analogous-missing-components-002.html +++ b/tests/wpt/tests/css/css-images/gradient/gradient-analogous-missing-components-002.html @@ -1,40 +1,39 @@ - - - - - Gradient analogous missing components carry forward logic tests - - - - - - - - -

      They should be equivalent to `background: color-mix(in srgb, color(srgb none 1 none), lime)`

      -
      This should be a lime background.
      -
      This should be a lime background.
      -
      This should be a lime background.
      -
      This should be a lime background.
      - - + + +Gradient analogous missing components carry forward logic tests + + + + + + +

      They should be equivalent to `background: color-mix(in srgb, color(srgb none 1 none), lime)`

      +
      This should be a lime background.
      +
      This should be a lime background.
      +
      This should be a lime background.
      +
      This should be a lime background.
      diff --git a/tests/wpt/tests/css/css-images/gradient/gradient-analogous-missing-components-003.html b/tests/wpt/tests/css/css-images/gradient/gradient-analogous-missing-components-003.html index c4c8995c1ef..c97571f54b9 100644 --- a/tests/wpt/tests/css/css-images/gradient/gradient-analogous-missing-components-003.html +++ b/tests/wpt/tests/css/css-images/gradient/gradient-analogous-missing-components-003.html @@ -1,35 +1,33 @@ - - - - - Gradient analogous missing components carry forward logic tests - - - - - - - - -
      This should be a lime background.
      -
      This should be a lime background.
      -
      This should be a lime background.
      - - + + +Gradient analogous missing components carry forward logic tests + + + + + + + +
      This should be a lime background.
      +
      This should be a lime background.
      +
      This should be a lime background.
      diff --git a/tests/wpt/tests/css/css-images/gradient/gradient-infinity-003-ref.html b/tests/wpt/tests/css/css-images/gradient/gradient-infinity-003-ref.html new file mode 100644 index 00000000000..798df7bbbb6 --- /dev/null +++ b/tests/wpt/tests/css/css-images/gradient/gradient-infinity-003-ref.html @@ -0,0 +1,12 @@ + +All boxes should have a lime background. + +
      This should be a lime background.
      +
      This should be a lime background.
      +
      This should be a lime background.
      \ No newline at end of file diff --git a/tests/wpt/tests/css/css-images/gradient/gradient-infinity-003.html b/tests/wpt/tests/css/css-images/gradient/gradient-infinity-003.html new file mode 100644 index 00000000000..74008e41691 --- /dev/null +++ b/tests/wpt/tests/css/css-images/gradient/gradient-infinity-003.html @@ -0,0 +1,27 @@ + + + + + +All boxes should have a lime background. + +
      This should be a lime background.
      +
      This should be a lime background.
      +
      This should be a lime background.
      \ No newline at end of file diff --git a/tests/wpt/tests/css/css-images/gradient/gradient-single-stop-none-interpolation.html b/tests/wpt/tests/css/css-images/gradient/gradient-single-stop-none-interpolation.html index 14cd9d76302..449bde6e25a 100644 --- a/tests/wpt/tests/css/css-images/gradient/gradient-single-stop-none-interpolation.html +++ b/tests/wpt/tests/css/css-images/gradient/gradient-single-stop-none-interpolation.html @@ -1,40 +1,40 @@ - - - Gradient interpolation with single stop that has missing components - - - - - - - -
      -
      -
      -
      -
      - - + +Gradient interpolation with single stop that has missing components + + + + + +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-images/image-set/image-set-parsing.html b/tests/wpt/tests/css/css-images/image-set/image-set-parsing.html index 83c0d73ab95..ee57c8599eb 100644 --- a/tests/wpt/tests/css/css-images/image-set/image-set-parsing.html +++ b/tests/wpt/tests/css/css-images/image-set/image-set-parsing.html @@ -119,6 +119,19 @@ function test_resolution_units_parsing() { 'image-set(url("example.png") calc(1 * 96dpi))', 'image-set(url("example.png") calc(1dppx))' ); + test_valid_value_variants( + 'background-image', + 'image-set(url("example.png") calc(1dppx * sibling-index()))', + ); + test_valid_value_variants( + 'background-image', + 'image-set(url("example.png") calc(1dppx * sign(1em - 10px)))', + ); + test_valid_value_variants( + 'background-image', + 'image-set(url("example.png") calc(1dppx * sign(10px)))', + 'image-set(url("example.png") calc(1dppx))', + ); test_invalid_value_variants( 'background-image', diff --git a/tests/wpt/tests/css/css-images/infinite-radial-gradient-refcrash.html b/tests/wpt/tests/css/css-images/infinite-radial-gradient-refcrash.html index a3a1e7fb069..a150b619116 100644 --- a/tests/wpt/tests/css/css-images/infinite-radial-gradient-refcrash.html +++ b/tests/wpt/tests/css/css-images/infinite-radial-gradient-refcrash.html @@ -2,6 +2,7 @@ CSS Images Test: repeating-radial-gradient with huge size crashes Chrome + + diff --git a/tests/wpt/tests/css/css-images/linear-gradient-body-sibling-index.html b/tests/wpt/tests/css/css-images/linear-gradient-body-sibling-index.html new file mode 100644 index 00000000000..299b86d464a --- /dev/null +++ b/tests/wpt/tests/css/css-images/linear-gradient-body-sibling-index.html @@ -0,0 +1,15 @@ + +CSS Images Test: Linear gradient with sibling-index() + + + + + + diff --git a/tests/wpt/tests/css/css-images/linear-gradient-calc-em-units-ref.html b/tests/wpt/tests/css/css-images/linear-gradient-calc-em-units-ref.html new file mode 100644 index 00000000000..be13be8e260 --- /dev/null +++ b/tests/wpt/tests/css/css-images/linear-gradient-calc-em-units-ref.html @@ -0,0 +1,7 @@ + +CSS Test Reference + +
      +
      diff --git a/tests/wpt/tests/css/css-images/linear-gradient-calc-em-units.html b/tests/wpt/tests/css/css-images/linear-gradient-calc-em-units.html new file mode 100644 index 00000000000..e764e62890d --- /dev/null +++ b/tests/wpt/tests/css/css-images/linear-gradient-calc-em-units.html @@ -0,0 +1,19 @@ + +CSS Images Test: Linear gradient with em in calc() + + + +
      +
      diff --git a/tests/wpt/tests/css/css-images/linear-gradient-sibling-index-ref.html b/tests/wpt/tests/css/css-images/linear-gradient-sibling-index-ref.html new file mode 100644 index 00000000000..eea8518d95b --- /dev/null +++ b/tests/wpt/tests/css/css-images/linear-gradient-sibling-index-ref.html @@ -0,0 +1,7 @@ + +CSS Test Reference + +
      +
      diff --git a/tests/wpt/tests/css/css-images/linear-gradient-sibling-index.html b/tests/wpt/tests/css/css-images/linear-gradient-sibling-index.html new file mode 100644 index 00000000000..b92897e0697 --- /dev/null +++ b/tests/wpt/tests/css/css-images/linear-gradient-sibling-index.html @@ -0,0 +1,19 @@ + +CSS Images Test: Linear gradient with sibling-index() + + + + +
      +
      +
      +
      +
      +
      +
      diff --git a/tests/wpt/tests/css/css-images/multiple-position-color-stop-conic.html b/tests/wpt/tests/css/css-images/multiple-position-color-stop-conic.html index 41aa505c879..50ec959e221 100644 --- a/tests/wpt/tests/css/css-images/multiple-position-color-stop-conic.html +++ b/tests/wpt/tests/css/css-images/multiple-position-color-stop-conic.html @@ -2,7 +2,7 @@ Conic gradient with a two position color stop - + diff --git a/tests/wpt/tests/css/css-masking/clip-path-svg-content/WEB_FEATURES.yml b/tests/wpt/tests/css/css-masking/clip-path-svg-content/WEB_FEATURES.yml new file mode 100644 index 00000000000..a694bbb9907 --- /dev/null +++ b/tests/wpt/tests/css/css-masking/clip-path-svg-content/WEB_FEATURES.yml @@ -0,0 +1,8 @@ +features: +- name: clip-path-boxes + files: + - clip-path-inset-stroke-001.svg + - clip-path-inset-stroke-002.svg + - clip-path-shape-circle-003.svg + - clip-path-shape-circle-004.svg + - clip-path-shape-circle-005.svg diff --git a/tests/wpt/tests/css/css-masking/clip-path/WEB_FEATURES.yml b/tests/wpt/tests/css/css-masking/clip-path/WEB_FEATURES.yml new file mode 100644 index 00000000000..b098d35ca73 --- /dev/null +++ b/tests/wpt/tests/css/css-masking/clip-path/WEB_FEATURES.yml @@ -0,0 +1,12 @@ +features: +- name: clip-path-boxes + files: + - clip-path-fillBox-1a.html + - clip-path-fillBox-1b.html + - clip-path-strokeBox-1a.html + - clip-path-strokeBox-1b.html + - clip-path-strokeBox-1c.html + - clip-path-viewBox-1a.html + - clip-path-viewBox-1b.html + - clip-path-viewBox-1c.html + - clip-path-viewBox-1d.html diff --git a/tests/wpt/tests/css/css-masking/clip-path/animations/clip-path-animation-svg-zoom.html b/tests/wpt/tests/css/css-masking/clip-path/animations/clip-path-animation-svg-zoom.html index f7be6d25324..0accfd2102b 100644 --- a/tests/wpt/tests/css/css-masking/clip-path/animations/clip-path-animation-svg-zoom.html +++ b/tests/wpt/tests/css/css-masking/clip-path/animations/clip-path-animation-svg-zoom.html @@ -2,6 +2,7 @@ + diff --git a/tests/wpt/tests/css/css-masonry/tentative/parsing/masonry-slack-invalid.html b/tests/wpt/tests/css/css-masonry/tentative/parsing/masonry-slack-invalid.html index 75bae04011a..6cdd0bd270c 100644 --- a/tests/wpt/tests/css/css-masonry/tentative/parsing/masonry-slack-invalid.html +++ b/tests/wpt/tests/css/css-masonry/tentative/parsing/masonry-slack-invalid.html @@ -2,23 +2,23 @@ -CSS Masonry: masonry-slack parsing +CSS Masonry: item-tolerance parsing - - + + diff --git a/tests/wpt/tests/css/css-masonry/tentative/parsing/masonry-slack-valid.html b/tests/wpt/tests/css/css-masonry/tentative/parsing/masonry-slack-valid.html index 55044a98f6b..9dc6721541e 100644 --- a/tests/wpt/tests/css/css-masonry/tentative/parsing/masonry-slack-valid.html +++ b/tests/wpt/tests/css/css-masonry/tentative/parsing/masonry-slack-valid.html @@ -2,23 +2,23 @@ -CSS Masonry: masonry-slack parsing +CSS Masonry: item-tolerance parsing - + diff --git a/tests/wpt/tests/css/css-mixins/dashed-function-cycles.html b/tests/wpt/tests/css/css-mixins/dashed-function-cycles.html new file mode 100644 index 00000000000..e711c9ed367 --- /dev/null +++ b/tests/wpt/tests/css/css-mixins/dashed-function-cycles.html @@ -0,0 +1,408 @@ + +Custom Functions: Handling cycles + + + + + + +
      +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/wpt/tests/css/css-mixins/dashed-function-cycles.tentative.html b/tests/wpt/tests/css/css-mixins/dashed-function-cycles.tentative.html deleted file mode 100644 index 11e653e9b7d..00000000000 --- a/tests/wpt/tests/css/css-mixins/dashed-function-cycles.tentative.html +++ /dev/null @@ -1,408 +0,0 @@ - -Custom Functions: Handling cycles - - - - - - -
      -
      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/tests/wpt/tests/css/css-mixins/local-attr-substitution.html b/tests/wpt/tests/css/css-mixins/local-attr-substitution.html index 575372a9669..de2a20fa470 100644 --- a/tests/wpt/tests/css/css-mixins/local-attr-substitution.html +++ b/tests/wpt/tests/css/css-mixins/local-attr-substitution.html @@ -82,13 +82,13 @@ -