diff options
128 files changed, 2099 insertions, 1758 deletions
diff --git a/.github/workflows/dispatch-workflow.yml b/.github/workflows/dispatch-workflow.yml index 472f4813cd7..2e4b8ef7076 100644 --- a/.github/workflows/dispatch-workflow.yml +++ b/.github/workflows/dispatch-workflow.yml @@ -8,6 +8,9 @@ on: profile: required: true type: string + build-args: + required: true + type: string wpt-args: required: true type: string @@ -35,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 }} @@ -46,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 }} @@ -59,6 +64,7 @@ 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 }} diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 033f9795a23..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 @@ -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 }} 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 1545dfa2450..1d11033a326 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,6 +56,7 @@ 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 }} diff --git a/.github/workflows/ohos.yml b/.github/workflows/ohos.yml index 8728d31865c..505186903b0 100644 --- a/.github/workflows/ohos.yml +++ b/.github/workflows/ohos.yml @@ -223,10 +223,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 diff --git a/.github/workflows/try-label.yml b/.github/workflows/try-label.yml index b1ca27a6f58..d4ce3f944f6 100644 --- a/.github/workflows/try-label.yml +++ b/.github/workflows/try-label.yml @@ -128,6 +128,7 @@ 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 }} diff --git a/.github/workflows/try.yml b/.github/workflows/try.yml index cba4fc03e06..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,6 +112,7 @@ 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 }} 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/Cargo.lock b/Cargo.lock index 8b2f157cc1a..fb0e0679b69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -526,7 +526,7 @@ dependencies = [ "bitflags 2.9.0", "cexpr", "clang-sys", - "itertools 0.13.0", + "itertools 0.10.5", "proc-macro2", "quote", "regex", @@ -964,18 +964,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071" +checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.37" +version = "4.5.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2" +checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120" dependencies = [ "anstyle", "clap_lex", @@ -1065,7 +1065,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] @@ -2032,7 +2032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2551,15 +2551,15 @@ 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", "libc", "system-deps", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -2584,9 +2584,9 @@ 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", "futures-channel", @@ -2605,9 +2605,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", @@ -2618,9 +2618,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", @@ -2664,9 +2664,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", @@ -2732,9 +2732,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", @@ -2742,7 +2742,7 @@ dependencies = [ "futures-util", "glib", "gstreamer-sys", - "itertools 0.13.0", + "itertools 0.14.0", "libc", "muldiv", "num-integer", @@ -2785,9 +2785,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", @@ -2801,9 +2801,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", @@ -2815,9 +2815,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", @@ -2829,9 +2829,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", @@ -2842,9 +2842,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", @@ -2857,9 +2857,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", @@ -2870,9 +2870,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", @@ -2882,9 +2882,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", @@ -2972,9 +2972,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", @@ -2984,9 +2984,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", @@ -3001,9 +3001,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", @@ -4001,7 +4001,7 @@ checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ "hermit-abi 0.5.0", "libc", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -4021,15 +4021,6 @@ dependencies = [ [[package]] name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" @@ -4258,12 +4249,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +checksum = "6a793df0d7afeac54f95b471d3af7f0d4fb975699f972341a4b76988d49cdf0c" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -6191,7 +6182,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -6524,7 +6515,7 @@ dependencies = [ [[package]] name = "selectors" version = "0.28.0" -source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea" dependencies = [ "bitflags 2.9.0", "cssparser", @@ -6819,7 +6810,7 @@ dependencies = [ [[package]] name = "servo_arc" version = "0.4.1" -source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea" dependencies = [ "serde", "stable_deref_trait", @@ -7280,7 +7271,7 @@ dependencies = [ [[package]] name = "stylo" version = "0.3.0" -source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea" dependencies = [ "app_units", "arrayvec", @@ -7338,7 +7329,7 @@ dependencies = [ [[package]] name = "stylo_atoms" version = "0.3.0" -source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea" dependencies = [ "string_cache", "string_cache_codegen", @@ -7347,12 +7338,12 @@ dependencies = [ [[package]] name = "stylo_config" version = "0.3.0" -source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea" [[package]] name = "stylo_derive" version = "0.3.0" -source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea" dependencies = [ "darling", "proc-macro2", @@ -7364,7 +7355,7 @@ dependencies = [ [[package]] name = "stylo_dom" version = "0.3.0" -source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea" dependencies = [ "bitflags 2.9.0", "stylo_malloc_size_of", @@ -7373,7 +7364,7 @@ dependencies = [ [[package]] name = "stylo_malloc_size_of" version = "0.3.0" -source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea" dependencies = [ "app_units", "cssparser", @@ -7390,12 +7381,12 @@ dependencies = [ [[package]] name = "stylo_static_prefs" version = "0.3.0" -source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea" [[package]] name = "stylo_traits" version = "0.3.0" -source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea" dependencies = [ "app_units", "bitflags 2.9.0", @@ -7559,7 +7550,7 @@ dependencies = [ "getrandom", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7778,7 +7769,7 @@ dependencies = [ [[package]] name = "to_shmem" version = "0.2.0" -source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea" dependencies = [ "cssparser", "servo_arc", @@ -7791,7 +7782,7 @@ dependencies = [ [[package]] name = "to_shmem_derive" version = "0.1.0" -source = "git+https://github.com/servo/stylo?branch=2025-05-01#7edd19e2f09570c6734161b4bb7d47859c8699bf" +source = "git+https://github.com/servo/stylo?branch=2025-05-01#bc815af4b5ae01768eaef64d21cebe6d66be06ea" dependencies = [ "darling", "proc-macro2", @@ -8916,7 +8907,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.48.0", ] [[package]] diff --git a/components/compositing/webview_renderer.rs b/components/compositing/webview_renderer.rs index f76dc68013d..a51dd5f8cda 100644 --- a/components/compositing/webview_renderer.rs +++ b/components/compositing/webview_renderer.rs @@ -689,11 +689,6 @@ impl WebViewRenderer { action: MouseButtonAction::Up, point, })); - self.dispatch_input_event(InputEvent::MouseButton(MouseButtonEvent { - button, - action: MouseButtonAction::Click, - point, - })); } pub(crate) fn notify_scroll_event( diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index e493a97d184..5db37800c42 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -129,9 +129,10 @@ use embedder_traits::resources::{self, Resource}; use embedder_traits::user_content_manager::UserContentManager; use embedder_traits::{ AnimationState, CompositorHitTestResult, Cursor, EmbedderMsg, EmbedderProxy, - FocusSequenceNumber, ImeEvent, InputEvent, MediaSessionActionType, MediaSessionEvent, - MediaSessionPlaybackState, MouseButton, MouseButtonAction, MouseButtonEvent, Theme, - ViewportDetails, WebDriverCommandMsg, WebDriverLoadStatus, + FocusSequenceNumber, ImeEvent, InputEvent, JSValue, JavaScriptEvaluationError, + JavaScriptEvaluationId, MediaSessionActionType, MediaSessionEvent, MediaSessionPlaybackState, + MouseButton, MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverCommandMsg, + WebDriverLoadStatus, }; use euclid::Size2D; use euclid::default::Size2D as UntypedSize2D; @@ -1477,6 +1478,52 @@ 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); + }, + } + } + + #[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), + ); } } @@ -1817,6 +1864,9 @@ where self.mem_profiler_chan .send(mem::ProfilerMsg::Report(sender)); }, + ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, result) => { + self.handle_finish_javascript_evaluation(evaluation_id, result) + }, } } @@ -3182,6 +3232,22 @@ where feature = "tracing", tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") )] + fn handle_finish_javascript_evaluation( + &mut self, + evaluation_id: JavaScriptEvaluationId, + result: Result<JSValue, JavaScriptEvaluationError>, + ) { + self.embedder_proxy + .send(EmbedderMsg::FinishJavaScriptEvaluation( + evaluation_id, + result, + )); + } + + #[cfg_attr( + feature = "tracing", + tracing::instrument(skip_all, fields(servo_profiling = true), level = "trace") + )] fn handle_subframe_loaded(&mut self, pipeline_id: PipelineId) { let browsing_context_id = match self.pipelines.get(&pipeline_id) { Some(pipeline) => pipeline.browsing_context_id, @@ -4691,6 +4757,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, diff --git a/components/constellation/tracing.rs b/components/constellation/tracing.rs index eff7f755c6b..fd7fe7dd251 100644 --- a/components/constellation/tracing.rs +++ b/components/constellation/tracing.rs @@ -77,6 +77,7 @@ mod from_compositor { Self::SetWebViewThrottled(_, _) => target!("SetWebViewThrottled"), Self::SetScrollStates(..) => target!("SetScrollStates"), Self::PaintMetric(..) => target!("PaintMetric"), + Self::EvaluateJavaScript(..) => target!("EvaluateJavaScript"), } } } @@ -176,6 +177,7 @@ mod from_script { Self::TitleChanged(..) => target!("TitleChanged"), Self::IFrameSizes(..) => target!("IFrameSizes"), Self::ReportMemory(..) => target!("ReportMemory"), + Self::FinishJavaScriptEvaluation(..) => target!("FinishJavaScriptEvaluation"), } } } @@ -238,6 +240,9 @@ mod from_script { Self::ShutdownComplete => target_variant!("ShutdownComplete"), Self::ShowNotification(..) => target_variant!("ShowNotification"), Self::ShowSelectElementMenu(..) => target_variant!("ShowSelectElementMenu"), + Self::FinishJavaScriptEvaluation(..) => { + target_variant!("FinishJavaScriptEvaluation") + }, } } } diff --git a/components/layout/construct_modern.rs b/components/layout/construct_modern.rs index 1489d82635b..8f1282ec9f6 100644 --- a/components/layout/construct_modern.rs +++ b/components/layout/construct_modern.rs @@ -142,7 +142,7 @@ impl<'a, 'dom> ModernContainerBuilder<'a, 'dom> { .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); diff --git a/components/layout/display_list/mod.rs b/components/layout/display_list/mod.rs index d6cbb50e4a1..f017642908d 100644 --- a/components/layout/display_list/mod.rs +++ b/components/layout/display_list/mod.rs @@ -14,6 +14,7 @@ use euclid::{Point2D, SideOffsets2D, Size2D, UnknownUnit}; use fonts::GlyphStore; use gradient::WebRenderGradient; use range::Range as ServoRange; +use servo_arc::Arc as ServoArc; use servo_geometry::MaxRect; use style::Zero; use style::color::{AbsoluteColor, ColorSpace}; @@ -470,18 +471,16 @@ impl Fragment { Fragment::AbsoluteOrFixedPositioned(_) => {}, Fragment::Positioning(positioning_fragment) => { let positioning_fragment = positioning_fragment.borrow(); - if let Some(style) = positioning_fragment.style.as_ref() { - let rect = positioning_fragment - .rect - .translate(containing_block.origin.to_vector()); - self.maybe_push_hit_test_for_style_and_tag( - builder, - style, - positioning_fragment.base.tag, - rect, - Cursor::Default, - ); - } + let rect = positioning_fragment + .rect + .translate(containing_block.origin.to_vector()); + self.maybe_push_hit_test_for_style_and_tag( + builder, + &positioning_fragment.style, + positioning_fragment.base.tag, + rect, + Cursor::Default, + ); }, Fragment::Image(image) => { let image = image.borrow(); @@ -544,7 +543,13 @@ impl Fragment { }, Fragment::Text(text) => { let text = &*text.borrow(); - match text.parent_style.get_inherited_box().visibility { + match text + .inline_styles + .style + .borrow() + .get_inherited_box() + .visibility + { Visibility::Visible => { self.build_display_list_for_text_fragment(text, builder, containing_block) }, @@ -604,22 +609,23 @@ impl Fragment { return; } + let parent_style = fragment.inline_styles.style.borrow(); self.maybe_push_hit_test_for_style_and_tag( builder, - &fragment.parent_style, + &parent_style, fragment.base.tag, rect, Cursor::Text, ); - let color = fragment.parent_style.clone_color(); + let color = parent_style.clone_color(); let font_metrics = &fragment.font_metrics; let dppx = builder.context.style_context.device_pixel_ratio().get(); - let common = builder.common_properties(rect.to_webrender(), &fragment.parent_style); + let common = builder.common_properties(rect.to_webrender(), &parent_style); // Shadows. According to CSS-BACKGROUNDS, text shadows render in *reverse* order (front to // back). - let shadows = &fragment.parent_style.get_inherited_text().text_shadow; + let shadows = &parent_style.get_inherited_text().text_shadow; for shadow in shadows.0.iter().rev() { builder.wr().push_shadow( &wr::SpaceAndClipInfo { @@ -642,7 +648,7 @@ impl Fragment { let mut rect = rect; rect.origin.y += font_metrics.ascent - font_metrics.underline_offset; rect.size.height = Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx)); - self.build_display_list_for_text_decoration(fragment, builder, &rect, &color); + self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color); } if fragment @@ -651,7 +657,7 @@ impl Fragment { { let mut rect = rect; rect.size.height = Au::from_f32_px(font_metrics.underline_size.to_nearest_pixel(dppx)); - self.build_display_list_for_text_decoration(fragment, builder, &rect, &color); + self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color); } // TODO: This caret/text selection implementation currently does not account for vertical text @@ -678,12 +684,13 @@ impl Fragment { Point2D::new(end.x.to_f32_px(), containing_block.max_y().to_f32_px()), ); if let Some(selection_color) = fragment - .selected_style + .inline_styles + .selected + .borrow() .clone_background_color() .as_absolute() { - let selection_common = - builder.common_properties(selection_rect, &fragment.parent_style); + let selection_common = builder.common_properties(selection_rect, &parent_style); builder.wr().push_rect( &selection_common, selection_rect, @@ -709,7 +716,7 @@ impl Fragment { ), ); let insertion_point_common = - builder.common_properties(insertion_point_rect, &fragment.parent_style); + builder.common_properties(insertion_point_rect, &parent_style); // TODO: The color of the caret is currently hardcoded to the text color. // We should be retrieving the caret color from the style properly. builder @@ -734,7 +741,7 @@ impl Fragment { let mut rect = rect; rect.origin.y += font_metrics.ascent - font_metrics.strikeout_offset; rect.size.height = Au::from_f32_px(font_metrics.strikeout_size.to_nearest_pixel(dppx)); - self.build_display_list_for_text_decoration(fragment, builder, &rect, &color); + self.build_display_list_for_text_decoration(&parent_style, builder, &rect, &color); } if !shadows.0.is_empty() { @@ -744,23 +751,22 @@ impl Fragment { fn build_display_list_for_text_decoration( &self, - fragment: &TextFragment, + parent_style: &ServoArc<ComputedValues>, builder: &mut DisplayListBuilder, rect: &PhysicalRect<Au>, color: &AbsoluteColor, ) { let rect = rect.to_webrender(); let wavy_line_thickness = (0.33 * rect.size().height).ceil(); - let text_decoration_color = fragment - .parent_style + let text_decoration_color = parent_style .clone_text_decoration_color() .resolve_to_absolute(color); - let text_decoration_style = fragment.parent_style.clone_text_decoration_style(); + let text_decoration_style = parent_style.clone_text_decoration_style(); if text_decoration_style == ComputedTextDecorationStyle::MozNone { return; } builder.display_list.wr.push_line( - &builder.common_properties(rect, &fragment.parent_style), + &builder.common_properties(rect, parent_style), &rect, wavy_line_thickness, wr::LineOrientation::Horizontal, @@ -1026,7 +1032,7 @@ impl<'a> BuilderForBoxFragment<'a> { for extra_background in extra_backgrounds { let positioning_area = extra_background.rect; let painter = BackgroundPainter { - style: &extra_background.style.borrow_mut().0, + style: &extra_background.style.borrow_mut(), painting_area_override: None, positioning_area_override: Some( positioning_area diff --git a/components/layout/dom.rs b/components/layout/dom.rs index 4400d78feb1..e3a22eb5197 100644 --- a/components/layout/dom.rs +++ b/components/layout/dom.rs @@ -19,14 +19,14 @@ use script_layout_interface::{ GenericLayoutDataTrait, LayoutElementType, LayoutNodeType as ScriptLayoutNodeType, }; use servo_arc::Arc as ServoArc; +use style::context::SharedStyleContext; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; use crate::cell::ArcRefCell; -use crate::context::LayoutContext; use crate::flexbox::FlexLevelBox; use crate::flow::BlockLevelBox; -use crate::flow::inline::InlineItem; +use crate::flow::inline::{InlineItem, SharedInlineStyles}; use crate::fragment_tree::Fragment; use crate::geom::PhysicalSize; use crate::replaced::CanvasInfo; @@ -59,7 +59,7 @@ impl InnerDOMLayoutData { /// A box that is stored in one of the `DOMLayoutData` slots. #[derive(MallocSizeOf)] pub(super) enum LayoutBox { - DisplayContents, + DisplayContents(SharedInlineStyles), BlockLevel(ArcRefCell<BlockLevelBox>), InlineLevel(Vec<ArcRefCell<InlineItem>>), FlexLevel(ArcRefCell<FlexLevelBox>), @@ -70,7 +70,7 @@ pub(super) enum LayoutBox { impl LayoutBox { fn invalidate_cached_fragment(&self) { match self { - LayoutBox::DisplayContents => {}, + LayoutBox::DisplayContents(..) => {}, LayoutBox::BlockLevel(block_level_box) => { block_level_box.borrow().invalidate_cached_fragment() }, @@ -91,7 +91,7 @@ impl LayoutBox { pub(crate) fn fragments(&self) -> Vec<Fragment> { match self { - LayoutBox::DisplayContents => vec![], + LayoutBox::DisplayContents(..) => vec![], LayoutBox::BlockLevel(block_level_box) => block_level_box.borrow().fragments(), LayoutBox::InlineLevel(inline_items) => inline_items .iter() @@ -102,6 +102,41 @@ impl LayoutBox { LayoutBox::TableLevelBox(table_box) => table_box.fragments(), } } + + fn repair_style( + &self, + context: &SharedStyleContext, + node: &ServoLayoutNode, + new_style: &ServoArc<ComputedValues>, + ) { + match self { + LayoutBox::DisplayContents(inline_shared_styles) => { + *inline_shared_styles.style.borrow_mut() = new_style.clone(); + *inline_shared_styles.selected.borrow_mut() = node.to_threadsafe().selected_style(); + }, + LayoutBox::BlockLevel(block_level_box) => { + block_level_box + .borrow_mut() + .repair_style(context, node, new_style); + }, + LayoutBox::InlineLevel(inline_items) => { + for inline_item in inline_items { + inline_item + .borrow_mut() + .repair_style(context, node, new_style); + } + }, + LayoutBox::FlexLevel(flex_level_box) => { + flex_level_box.borrow_mut().repair_style(context, new_style) + }, + LayoutBox::TableLevelBox(table_level_box) => { + table_level_box.repair_style(context, new_style) + }, + LayoutBox::TaffyItemBox(taffy_item_box) => { + taffy_item_box.borrow_mut().repair_style(context, new_style) + }, + } + } } /// A wrapper for [`InnerDOMLayoutData`]. This is necessary to give the entire data @@ -167,7 +202,7 @@ pub(crate) trait NodeExt<'dom> { fn as_iframe(&self) -> Option<(PipelineId, BrowsingContextId)>; fn as_video(&self) -> Option<(Option<webrender_api::ImageKey>, Option<PhysicalSize<f64>>)>; fn as_typeless_object_with_data_attribute(&self) -> Option<String>; - fn style(&self, context: &LayoutContext) -> ServoArc<ComputedValues>; + fn style(&self, context: &SharedStyleContext) -> ServoArc<ComputedValues>; fn layout_data_mut(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData>; fn layout_data(&self) -> Option<AtomicRef<'dom, InnerDOMLayoutData>>; @@ -180,6 +215,8 @@ pub(crate) trait NodeExt<'dom> { fn fragments_for_pseudo(&self, pseudo_element: Option<PseudoElement>) -> Vec<Fragment>; fn invalidate_cached_fragment(&self); + + fn repair_style(&self, context: &SharedStyleContext); } impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> { @@ -253,8 +290,8 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> { .map(|string| string.to_owned()) } - fn style(&self, context: &LayoutContext) -> ServoArc<ComputedValues> { - self.to_threadsafe().style(context.shared_context()) + fn style(&self, context: &SharedStyleContext) -> ServoArc<ComputedValues> { + self.to_threadsafe().style(context) } fn layout_data_mut(&self) -> AtomicRefMut<'dom, InnerDOMLayoutData> { @@ -339,4 +376,30 @@ impl<'dom> NodeExt<'dom> for ServoLayoutNode<'dom> { }) .unwrap_or_default() } + + fn repair_style(&self, context: &SharedStyleContext) { + let data = self.layout_data_mut(); + if let Some(layout_object) = &*data.self_box.borrow() { + let style = self.to_threadsafe().style(context); + layout_object.repair_style(context, self, &style); + } + + if let Some(layout_object) = &*data.pseudo_before_box.borrow() { + if let Some(node) = self.to_threadsafe().with_pseudo(PseudoElement::Before) { + layout_object.repair_style(context, self, &node.style(context)); + } + } + + if let Some(layout_object) = &*data.pseudo_after_box.borrow() { + if let Some(node) = self.to_threadsafe().with_pseudo(PseudoElement::After) { + layout_object.repair_style(context, self, &node.style(context)); + } + } + + if let Some(layout_object) = &*data.pseudo_marker_box.borrow() { + if let Some(node) = self.to_threadsafe().with_pseudo(PseudoElement::Marker) { + layout_object.repair_style(context, self, &node.style(context)); + } + } + } } diff --git a/components/layout/dom_traversal.rs b/components/layout/dom_traversal.rs index a75d699a1b3..0201d72dbe2 100644 --- a/components/layout/dom_traversal.rs +++ b/components/layout/dom_traversal.rs @@ -23,6 +23,7 @@ use style::values::specified::Quotes; use crate::context::LayoutContext; use crate::dom::{BoxSlot, LayoutBox, NodeExt}; +use crate::flow::inline::SharedInlineStyles; use crate::fragment_tree::{BaseFragmentInfo, FragmentFlags, Tag}; use crate::quotes::quotes_for_lang; use crate::replaced::ReplacedContents; @@ -185,6 +186,12 @@ pub(super) trait TraversalHandler<'dom> { contents: Contents, box_slot: BoxSlot<'dom>, ); + + /// Notify the handler that we are about to recurse into a `display: contents` element. + fn enter_display_contents(&mut self, _: SharedInlineStyles) {} + + /// Notify the handler that we have finished a `display: contents` element. + fn leave_display_contents(&mut self) {} } fn traverse_children_of<'dom>( @@ -205,7 +212,10 @@ fn traverse_children_of<'dom>( ); if is_text_input_element || is_textarea_element { - let info = NodeAndStyleInfo::new(parent_element, parent_element.style(context)); + let info = NodeAndStyleInfo::new( + parent_element, + parent_element.style(context.shared_context()), + ); let node_text_content = parent_element.to_threadsafe().node_text_content(); if node_text_content.is_empty() { // The addition of zero-width space here forces the text input to have an inline formatting @@ -224,7 +234,7 @@ fn traverse_children_of<'dom>( if !is_text_input_element && !is_textarea_element { for child in iter_child_nodes(parent_element) { if child.is_text_node() { - let info = NodeAndStyleInfo::new(child, child.style(context)); + let info = NodeAndStyleInfo::new(child, child.style(context.shared_context())); handler.handle_text(&info, child.to_threadsafe().node_text_content()); } else if child.is_element() { traverse_element(child, context, handler); @@ -245,7 +255,7 @@ fn traverse_element<'dom>( element.unset_pseudo_element_box(PseudoElement::Marker); let replaced = ReplacedContents::for_element(element, context); - let style = element.style(context); + let style = element.style(context.shared_context()); match Display::from(style.get_box().display) { Display::None => element.unset_all_boxes(), Display::Contents => { @@ -254,8 +264,15 @@ fn traverse_element<'dom>( // <https://drafts.csswg.org/css-display-3/#valdef-display-contents> element.unset_all_boxes() } else { - element.element_box_slot().set(LayoutBox::DisplayContents); - traverse_children_of(element, context, handler) + let shared_inline_styles: SharedInlineStyles = + (&NodeAndStyleInfo::new(element, style)).into(); + element + .element_box_slot() + .set(LayoutBox::DisplayContents(shared_inline_styles.clone())); + + handler.enter_display_contents(shared_inline_styles); + traverse_children_of(element, context, handler); + handler.leave_display_contents(); } }, Display::GeneratingBox(display) => { @@ -308,8 +325,12 @@ fn traverse_eager_pseudo_element<'dom>( Display::Contents => { let items = generate_pseudo_element_content(&info.style, node, context); let box_slot = node.pseudo_element_box_slot(pseudo_element_type); - box_slot.set(LayoutBox::DisplayContents); + let shared_inline_styles: SharedInlineStyles = (&info).into(); + box_slot.set(LayoutBox::DisplayContents(shared_inline_styles.clone())); + + handler.enter_display_contents(shared_inline_styles); traverse_pseudo_element_contents(&info, context, handler, items); + handler.leave_display_contents(); }, Display::GeneratingBox(display) => { let items = generate_pseudo_element_content(&info.style, node, context); diff --git a/components/layout/flexbox/layout.rs b/components/layout/flexbox/layout.rs index e69b792e272..3ddbb71ba89 100644 --- a/components/layout/flexbox/layout.rs +++ b/components/layout/flexbox/layout.rs @@ -29,7 +29,9 @@ use super::{FlexContainer, FlexContainerConfig, FlexItemBox, FlexLevelBox}; use crate::cell::ArcRefCell; use crate::context::LayoutContext; use crate::formatting_contexts::{Baselines, IndependentFormattingContextContents}; -use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags}; +use crate::fragment_tree::{ + BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags, SpecificLayoutInfo, +}; use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2, Size, Sizes}; use crate::layout_box_base::CacheableLayoutResult; use crate::positioned::{ @@ -142,6 +144,9 @@ struct FlexItemLayoutResult { // Whether or not this layout had a child that dependeded on block constraints. has_child_which_depends_on_block_constraints: bool, + + // The specific layout info that this flex item had. + specific_layout_info: Option<SpecificLayoutInfo>, } impl FlexItemLayoutResult { @@ -295,7 +300,8 @@ impl FlexLineItem<'_> { .sides_to_flow_relative(item_margin) .to_physical(container_writing_mode), None, /* clearance */ - ); + ) + .with_specific_layout_info(self.layout_result.specific_layout_info); // If this flex item establishes a containing block for absolutely-positioned // descendants, then lay out any relevant absolutely-positioned children. This @@ -1910,6 +1916,7 @@ impl FlexItem<'_> { // size can differ from the hypothetical cross size, we should defer // synthesizing until needed. baseline_relative_to_margin_box: None, + specific_layout_info: None, }) }, IndependentFormattingContextContents::NonReplaced(non_replaced) => { @@ -1944,6 +1951,7 @@ impl FlexItem<'_> { content_block_size, baselines: content_box_baselines, depends_on_block_constraints, + specific_layout_info, .. } = layout; @@ -2012,6 +2020,7 @@ impl FlexItem<'_> { containing_block_block_size: item_as_containing_block.size.block, depends_on_block_constraints, has_child_which_depends_on_block_constraints, + specific_layout_info, }) }, } diff --git a/components/layout/flexbox/mod.rs b/components/layout/flexbox/mod.rs index 27b69bf289f..91a12b31812 100644 --- a/components/layout/flexbox/mod.rs +++ b/components/layout/flexbox/mod.rs @@ -5,6 +5,7 @@ use geom::{FlexAxis, MainStartCrossStart}; use malloc_size_of_derive::MallocSizeOf; use servo_arc::Arc as ServoArc; +use style::context::SharedStyleContext; use style::logical_geometry::WritingMode; use style::properties::ComputedValues; use style::properties::longhands::align_items::computed_value::T as AlignItems; @@ -90,7 +91,6 @@ impl FlexContainerConfig { pub(crate) struct FlexContainer { children: Vec<ArcRefCell<FlexLevelBox>>, - #[conditional_malloc_size_of] style: ServoArc<ComputedValues>, /// The configuration of this [`FlexContainer`]. @@ -137,6 +137,11 @@ impl FlexContainer { config: FlexContainerConfig::new(&info.style), } } + + pub(crate) fn repair_style(&mut self, new_style: &ServoArc<ComputedValues>) { + self.config = FlexContainerConfig::new(new_style); + self.style = new_style.clone(); + } } #[derive(Debug, MallocSizeOf)] @@ -146,6 +151,22 @@ pub(crate) enum FlexLevelBox { } impl FlexLevelBox { + pub(crate) fn repair_style( + &mut self, + context: &SharedStyleContext, + new_style: &ServoArc<ComputedValues>, + ) { + match self { + FlexLevelBox::FlexItem(flex_item_box) => flex_item_box + .independent_formatting_context + .repair_style(context, new_style), + FlexLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box + .borrow_mut() + .context + .repair_style(context, new_style), + } + } + pub(crate) fn invalidate_cached_fragment(&self) { match self { FlexLevelBox::FlexItem(flex_item_box) => flex_item_box diff --git a/components/layout/flow/construct.rs b/components/layout/flow/construct.rs index a50464123bc..334da8ae2b0 100644 --- a/components/layout/flow/construct.rs +++ b/components/layout/flow/construct.rs @@ -13,9 +13,9 @@ use style::selector_parser::PseudoElement; use style::str::char_is_whitespace; use super::OutsideMarker; -use super::inline::InlineFormattingContext; use super::inline::construct::InlineFormattingContextBuilder; use super::inline::inline_box::InlineBox; +use super::inline::{InlineFormattingContext, SharedInlineStyles}; use crate::PropagatedBoxTreeData; use crate::cell::ArcRefCell; use crate::context::LayoutContext; @@ -137,16 +137,25 @@ pub(crate) struct BlockContainerBuilder<'dom, 'style> { /// The propagated data to use for BoxTree construction. propagated_data: PropagatedBoxTreeData, - inline_formatting_context_builder: InlineFormattingContextBuilder, + /// The [`InlineFormattingContextBuilder`] if we have encountered any inline items, + /// otherwise None. + /// + /// TODO: This can be `OnceCell` once `OnceCell::get_mut_or_init` is stabilized. + inline_formatting_context_builder: Option<InlineFormattingContextBuilder>, /// The [`NodeAndStyleInfo`] to use for anonymous block boxes pushed to the list of - /// block-level boxes, lazily initialized (see `end_ongoing_inline_formatting_context`). + /// block-level boxes, lazily initialized. anonymous_box_info: Option<NodeAndStyleInfo<'dom>>, /// A collection of content that is being added to an anonymous table. This is /// composed of any sequence of internal table elements or table captions that /// are found outside of a table. anonymous_table_content: Vec<AnonymousTableContent<'dom>>, + + /// Any [`InlineFormattingContexts`] created need to know about the ongoing `display: contents` + /// ancestors that have been processed. This `Vec` allows passing those into new + /// [`InlineFormattingContext`]s that we create. + display_contents_shared_styles: Vec<SharedInlineStyles>, } impl BlockContainer { @@ -194,26 +203,44 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> { have_already_seen_first_line_for_text_indent: false, anonymous_box_info: None, anonymous_table_content: Vec::new(), - inline_formatting_context_builder: InlineFormattingContextBuilder::new(), + inline_formatting_context_builder: None, + display_contents_shared_styles: Vec::new(), } } - pub(crate) fn finish(mut self) -> BlockContainer { - debug_assert!( - !self - .inline_formatting_context_builder - .currently_processing_inline_box() - ); + fn currently_processing_inline_box(&self) -> bool { + self.inline_formatting_context_builder + .as_ref() + .is_some_and(InlineFormattingContextBuilder::currently_processing_inline_box) + } - self.finish_anonymous_table_if_needed(); + fn ensure_inline_formatting_context_builder(&mut self) -> &mut InlineFormattingContextBuilder { + self.inline_formatting_context_builder + .get_or_insert_with(|| { + let mut builder = InlineFormattingContextBuilder::new(self.info); + for shared_inline_styles in self.display_contents_shared_styles.iter() { + builder.enter_display_contents(shared_inline_styles.clone()); + } + builder + }) + } - if let Some(inline_formatting_context) = self.inline_formatting_context_builder.finish( + fn finish_ongoing_inline_formatting_context(&mut self) -> Option<InlineFormattingContext> { + self.inline_formatting_context_builder.take()?.finish( self.context, self.propagated_data, !self.have_already_seen_first_line_for_text_indent, self.info.is_single_line_text_input(), self.info.style.writing_mode.to_bidi_level(), - ) { + ) + } + + pub(crate) fn finish(mut self) -> BlockContainer { + debug_assert!(!self.currently_processing_inline_box()); + + self.finish_anonymous_table_if_needed(); + + if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context() { // There are two options here. This block was composed of both one or more inline formatting contexts // and child blocks OR this block was a single inline formatting context. In the latter case, we // just return the inline formatting context as the block itself. @@ -251,9 +278,7 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> { // // Note that text content in the inline formatting context isn't enough to force the // creation of an inline table. It requires the parent to be an inline box. - let inline_table = self - .inline_formatting_context_builder - .currently_processing_inline_box(); + let inline_table = self.currently_processing_inline_box(); // Text decorations are not propagated to atomic inline-level descendants. // From https://drafts.csswg.org/css2/#lining-striking-props: @@ -276,10 +301,16 @@ impl<'dom, 'style> BlockContainerBuilder<'dom, 'style> { Table::construct_anonymous(self.context, self.info, contents, propagated_data); if inline_table { - self.inline_formatting_context_builder.push_atomic(ifc); + self.ensure_inline_formatting_context_builder() + .push_atomic(ifc); } else { let table_block = ArcRefCell::new(BlockLevelBox::Independent(ifc)); - self.end_ongoing_inline_formatting_context(); + + if let Some(inline_formatting_context) = self.finish_ongoing_inline_formatting_context() + { + self.push_block_level_job_for_inline_formatting_context(inline_formatting_context); + } + self.block_level_boxes.push(BlockLevelJob { info: table_info, box_slot: BoxSlot::dummy(), @@ -363,7 +394,22 @@ impl<'dom> TraversalHandler<'dom> for BlockContainerBuilder<'dom, '_> { self.finish_anonymous_table_if_needed(); } - self.inline_formatting_context_builder.push_text(text, info); + self.ensure_inline_formatting_context_builder() + .push_text(text, info); + } + + fn enter_display_contents(&mut self, styles: SharedInlineStyles) { + self.display_contents_shared_styles.push(styles.clone()); + if let Some(builder) = self.inline_formatting_context_builder.as_mut() { + builder.enter_display_contents(styles); + } + } + + fn leave_display_contents(&mut self) { + self.display_contents_shared_styles.pop(); + if let Some(builder) = self.inline_formatting_context_builder.as_mut() { + builder.leave_display_contents(); + } } } @@ -433,14 +479,16 @@ impl<'dom> BlockContainerBuilder<'dom, '_> { (display_inside, contents.is_replaced()) else { // If this inline element is an atomic, handle it and return. - let atomic = self.inline_formatting_context_builder.push_atomic( + let context = self.context; + let propagaged_data = self.propagated_data.without_text_decorations(); + let atomic = self.ensure_inline_formatting_context_builder().push_atomic( IndependentFormattingContext::construct( - self.context, + context, info, display_inside, contents, // Text decorations are not propagated to atomic inline-level descendants. - self.propagated_data.without_text_decorations(), + propagaged_data, ), ); box_slot.set(LayoutBox::InlineLevel(vec![atomic])); @@ -449,7 +497,7 @@ impl<'dom> BlockContainerBuilder<'dom, '_> { // Otherwise, this is just a normal inline box. Whatever happened before, all we need to do // before recurring is to remember this ongoing inline level box. - self.inline_formatting_context_builder + self.ensure_inline_formatting_context_builder() .start_inline_box(InlineBox::new(info), None); if is_list_item { @@ -476,7 +524,10 @@ impl<'dom> BlockContainerBuilder<'dom, '_> { // `InlineFormattingContextBuilder::end_inline_box()` is returning all of those box tree // items. box_slot.set(LayoutBox::InlineLevel( - self.inline_formatting_context_builder.end_inline_box(), + self.inline_formatting_context_builder + .as_mut() + .expect("Should be building an InlineFormattingContext") + .end_inline_box(), )); } @@ -495,12 +546,15 @@ impl<'dom> BlockContainerBuilder<'dom, '_> { // that we want to have after we push the block below. if let Some(inline_formatting_context) = self .inline_formatting_context_builder - .split_around_block_and_finish( - self.context, - self.propagated_data, - !self.have_already_seen_first_line_for_text_indent, - self.info.style.writing_mode.to_bidi_level(), - ) + .as_mut() + .and_then(|builder| { + builder.split_around_block_and_finish( + self.context, + self.propagated_data, + !self.have_already_seen_first_line_for_text_indent, + self.info.style.writing_mode.to_bidi_level(), + ) + }) { self.push_block_level_job_for_inline_formatting_context(inline_formatting_context); } @@ -555,17 +609,18 @@ impl<'dom> BlockContainerBuilder<'dom, '_> { contents: Contents, box_slot: BoxSlot<'dom>, ) { - if !self.inline_formatting_context_builder.is_empty() { - let inline_level_box = self - .inline_formatting_context_builder - .push_absolutely_positioned_box(AbsolutelyPositionedBox::construct( - self.context, - info, - display_inside, - contents, - )); - box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box])); - return; + if let Some(builder) = self.inline_formatting_context_builder.as_mut() { + if !builder.is_empty() { + let inline_level_box = + builder.push_absolutely_positioned_box(AbsolutelyPositionedBox::construct( + self.context, + info, + display_inside, + contents, + )); + box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box])); + return; + } } let kind = BlockLevelCreator::OutOfFlowAbsolutelyPositionedBox { @@ -587,18 +642,18 @@ impl<'dom> BlockContainerBuilder<'dom, '_> { contents: Contents, box_slot: BoxSlot<'dom>, ) { - if !self.inline_formatting_context_builder.is_empty() { - let inline_level_box = - self.inline_formatting_context_builder - .push_float_box(FloatBox::construct( - self.context, - info, - display_inside, - contents, - self.propagated_data, - )); - box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box])); - return; + if let Some(builder) = self.inline_formatting_context_builder.as_mut() { + if !builder.is_empty() { + let inline_level_box = builder.push_float_box(FloatBox::construct( + self.context, + info, + display_inside, + contents, + self.propagated_data, + )); + box_slot.set(LayoutBox::InlineLevel(vec![inline_level_box])); + return; + } } let kind = BlockLevelCreator::OutOfFlowFloatBox { @@ -613,18 +668,6 @@ impl<'dom> BlockContainerBuilder<'dom, '_> { }); } - fn end_ongoing_inline_formatting_context(&mut self) { - if let Some(inline_formatting_context) = self.inline_formatting_context_builder.finish( - self.context, - self.propagated_data, - !self.have_already_seen_first_line_for_text_indent, - self.info.is_single_line_text_input(), - self.info.style.writing_mode.to_bidi_level(), - ) { - self.push_block_level_job_for_inline_formatting_context(inline_formatting_context); - } - } - fn push_block_level_job_for_inline_formatting_context( &mut self, inline_formatting_context: InlineFormattingContext, diff --git a/components/layout/flow/inline/construct.rs b/components/layout/flow/inline/construct.rs index 74b0cf4ea7d..a99de1679a4 100644 --- a/components/layout/flow/inline/construct.rs +++ b/components/layout/flow/inline/construct.rs @@ -7,13 +7,15 @@ use std::char::{ToLowercase, ToUppercase}; use icu_segmenter::WordSegmenter; use itertools::izip; -use servo_arc::Arc; use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse; use style::values::specified::text::TextTransformCase; use unicode_bidi::Level; use super::text_run::TextRun; -use super::{InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem}; +use super::{ + InlineBox, InlineBoxIdentifier, InlineBoxes, InlineFormattingContext, InlineItem, + SharedInlineStyles, +}; use crate::PropagatedBoxTreeData; use crate::cell::ArcRefCell; use crate::context::LayoutContext; @@ -25,6 +27,12 @@ use crate::style_ext::ComputedValuesExt; #[derive(Default)] pub(crate) struct InlineFormattingContextBuilder { + /// A stack of [`SharedInlineStyles`] including one for the root, one for each inline box on the + /// inline box stack, and importantly, one for every `display: contents` element that we are + /// currently processing. Normally `display: contents` elements don't affect the structure of + /// the [`InlineFormattingContext`], but the styles they provide do style their children. + shared_inline_styles_stack: Vec<SharedInlineStyles>, + /// The collection of text strings that make up this [`InlineFormattingContext`] under /// construction. pub text_segments: Vec<String>, @@ -63,7 +71,7 @@ pub(crate) struct InlineFormattingContextBuilder { /// The traversal is at all times as deep in the tree as this stack is, /// which is why the code doesn't need to keep track of the actual /// container root (see `handle_inline_level_element`). - /// + //_ /// When an inline box ends, it's removed from this stack. inline_box_stack: Vec<InlineBoxIdentifier>, @@ -83,10 +91,17 @@ pub(crate) struct InlineFormattingContextBuilder { } impl InlineFormattingContextBuilder { - pub(crate) fn new() -> Self { - // For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary. + pub(crate) fn new(info: &NodeAndStyleInfo) -> Self { + Self::new_for_shared_styles(vec![info.into()]) + } + + pub(crate) fn new_for_shared_styles( + shared_inline_styles_stack: Vec<SharedInlineStyles>, + ) -> Self { Self { + // For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary. on_word_boundary: true, + shared_inline_styles_stack, ..Default::default() } } @@ -100,6 +115,13 @@ impl InlineFormattingContextBuilder { self.current_text_offset += string_to_push.len(); } + fn shared_inline_styles(&self) -> SharedInlineStyles { + self.shared_inline_styles_stack + .last() + .expect("Should always have at least one SharedInlineStyles") + .clone() + } + /// Return true if this [`InlineFormattingContextBuilder`] is empty for the purposes of ignoring /// during box tree construction. An IFC is empty if it only contains TextRuns with /// completely collapsible whitespace. When that happens it can be ignored completely. @@ -135,7 +157,7 @@ impl InlineFormattingContextBuilder { independent_formatting_context: IndependentFormattingContext, ) -> ArcRefCell<InlineItem> { let inline_level_box = ArcRefCell::new(InlineItem::Atomic( - Arc::new(independent_formatting_context), + ArcRefCell::new(independent_formatting_context), self.current_text_offset, Level::ltr(), /* This will be assigned later if necessary. */ )); @@ -166,7 +188,8 @@ impl InlineFormattingContextBuilder { } pub(crate) fn push_float_box(&mut self, float_box: FloatBox) -> ArcRefCell<InlineItem> { - let inline_level_box = ArcRefCell::new(InlineItem::OutOfFlowFloatBox(Arc::new(float_box))); + let inline_level_box = + ArcRefCell::new(InlineItem::OutOfFlowFloatBox(ArcRefCell::new(float_box))); self.inline_items.push(inline_level_box.clone()); self.contains_floats = true; inline_level_box @@ -179,6 +202,14 @@ impl InlineFormattingContextBuilder { ) { self.push_control_character_string(inline_box.base.style.bidi_control_chars().0); + // Don't push a `SharedInlineStyles` if we are pushing this box when splitting + // an IFC for a block-in-inline split. Shared styles are pushed as part of setting + // up the second split of the IFC. + if inline_box.is_first_split { + self.shared_inline_styles_stack + .push(inline_box.shared_inline_styles.clone()); + } + let (identifier, inline_box) = self.inline_boxes.start_inline_box(inline_box); let inline_level_box = ArcRefCell::new(InlineItem::StartInlineBox(inline_box)); self.inline_items.push(inline_level_box.clone()); @@ -194,6 +225,8 @@ impl InlineFormattingContextBuilder { /// a single box tree items may be produced for a single inline box when that inline /// box is split around a block-level element. pub(crate) fn end_inline_box(&mut self) -> Vec<ArcRefCell<InlineItem>> { + self.shared_inline_styles_stack.pop(); + let (identifier, block_in_inline_splits) = self.end_inline_box_internal(); let inline_level_box = self.inline_boxes.get(&identifier); { @@ -272,8 +305,6 @@ impl InlineFormattingContextBuilder { } let selection_range = info.get_selection_range(); - let selected_style = info.get_selected_style(); - if let Some(last_character) = new_text.chars().next_back() { self.on_word_boundary = last_character.is_whitespace(); self.last_inline_box_ended_with_collapsible_white_space = @@ -295,14 +326,21 @@ impl InlineFormattingContextBuilder { .push(ArcRefCell::new(InlineItem::TextRun(ArcRefCell::new( TextRun::new( info.into(), - info.style.clone(), + self.shared_inline_styles(), new_range, selection_range, - selected_style, ), )))); } + pub(crate) fn enter_display_contents(&mut self, shared_inline_styles: SharedInlineStyles) { + self.shared_inline_styles_stack.push(shared_inline_styles); + } + + pub(crate) fn leave_display_contents(&mut self) { + self.shared_inline_styles_stack.pop(); + } + pub(crate) fn split_around_block_and_finish( &mut self, layout_context: &LayoutContext, @@ -318,7 +356,8 @@ impl InlineFormattingContextBuilder { // context. It has the same inline box structure as this builder, except the boxes are // marked as not being the first fragment. No inline content is carried over to this new // builder. - let mut new_builder = InlineFormattingContextBuilder::new(); + let mut new_builder = Self::new_for_shared_styles(self.shared_inline_styles_stack.clone()); + let block_in_inline_splits = std::mem::take(&mut self.block_in_inline_splits); for (identifier, historical_inline_boxes) in izip!(self.inline_box_stack.iter(), block_in_inline_splits) @@ -356,7 +395,7 @@ impl InlineFormattingContextBuilder { /// Finish the current inline formatting context, returning [`None`] if the context was empty. pub(crate) fn finish( - &mut self, + self, layout_context: &LayoutContext, propagated_data: PropagatedBoxTreeData, has_first_formatted_line: bool, @@ -367,11 +406,9 @@ impl InlineFormattingContextBuilder { return None; } - let old_builder = std::mem::replace(self, InlineFormattingContextBuilder::new()); - assert!(old_builder.inline_box_stack.is_empty()); - + assert!(self.inline_box_stack.is_empty()); Some(InlineFormattingContext::new_with_builder( - old_builder, + self, layout_context, propagated_data, has_first_formatted_line, diff --git a/components/layout/flow/inline/inline_box.rs b/components/layout/flow/inline/inline_box.rs index 1c953c13074..b547f3b5935 100644 --- a/components/layout/flow/inline/inline_box.rs +++ b/components/layout/flow/inline/inline_box.rs @@ -7,8 +7,15 @@ use std::vec::IntoIter; use app_units::Au; use fonts::FontMetrics; use malloc_size_of_derive::MallocSizeOf; - -use super::{InlineContainerState, InlineContainerStateFlags, inline_container_needs_strut}; +use script::layout_dom::ServoLayoutNode; +use script_layout_interface::wrapper_traits::{LayoutNode, ThreadSafeLayoutNode}; +use servo_arc::Arc as ServoArc; +use style::properties::ComputedValues; + +use super::{ + InlineContainerState, InlineContainerStateFlags, SharedInlineStyles, + inline_container_needs_strut, +}; use crate::ContainingBlock; use crate::cell::ArcRefCell; use crate::context::LayoutContext; @@ -20,6 +27,9 @@ use crate::style_ext::{LayoutStyle, PaddingBorderMargin}; #[derive(Debug, MallocSizeOf)] pub(crate) struct InlineBox { pub base: LayoutBoxBase, + /// The [`SharedInlineStyles`] for this [`InlineBox`] that are used to share styles + /// with all [`super::TextRun`] children. + pub(super) shared_inline_styles: SharedInlineStyles, /// The identifier of this inline box in the containing [`super::InlineFormattingContext`]. pub(super) identifier: InlineBoxIdentifier, /// Whether or not this is the first instance of an [`InlineBox`] before a possible @@ -37,6 +47,7 @@ impl InlineBox { pub(crate) fn new(info: &NodeAndStyleInfo) -> Self { Self { base: LayoutBoxBase::new(info.into(), info.style.clone()), + shared_inline_styles: info.into(), // This will be assigned later, when the box is actually added to the IFC. identifier: InlineBoxIdentifier::default(), is_first_split: true, @@ -48,6 +59,7 @@ impl InlineBox { pub(crate) fn split_around_block(&self) -> Self { Self { base: LayoutBoxBase::new(self.base.base_fragment_info, self.base.style.clone()), + shared_inline_styles: self.shared_inline_styles.clone(), is_first_split: false, is_last_split: false, ..*self @@ -58,6 +70,16 @@ impl InlineBox { pub(crate) fn layout_style(&self) -> LayoutStyle { LayoutStyle::Default(&self.base.style) } + + pub(crate) fn repair_style( + &mut self, + node: &ServoLayoutNode, + new_style: &ServoArc<ComputedValues>, + ) { + self.base.repair_style(new_style); + *self.shared_inline_styles.style.borrow_mut() = new_style.clone(); + *self.shared_inline_styles.selected.borrow_mut() = node.to_threadsafe().selected_style(); + } } #[derive(Debug, Default, MallocSizeOf)] diff --git a/components/layout/flow/inline/line.rs b/components/layout/flow/inline/line.rs index 80bab1080ed..3b92078d67d 100644 --- a/components/layout/flow/inline/line.rs +++ b/components/layout/flow/inline/line.rs @@ -7,7 +7,6 @@ use bitflags::bitflags; use fonts::{ByteIndex, FontMetrics, GlyphStore}; use itertools::Either; use range::Range; -use servo_arc::Arc; use style::Zero; use style::computed_values::position::T as Position; use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse; @@ -21,7 +20,7 @@ use unicode_bidi::{BidiInfo, Level}; use webrender_api::FontInstanceKey; use super::inline_box::{InlineBoxContainerState, InlineBoxIdentifier, InlineBoxTreePathToken}; -use super::{InlineFormattingContextLayout, LineBlockSizes}; +use super::{InlineFormattingContextLayout, LineBlockSizes, SharedInlineStyles}; use crate::cell::ArcRefCell; use crate::fragment_tree::{BaseFragmentInfo, BoxFragment, Fragment, TextFragment}; use crate::geom::{LogicalRect, LogicalVec2, PhysicalRect, ToLogical}; @@ -568,7 +567,7 @@ impl LineItemLayout<'_, '_> { self.current_state.fragments.push(( Fragment::Text(ArcRefCell::new(TextFragment { base: text_item.base_fragment_info.into(), - parent_style: text_item.parent_style, + inline_styles: text_item.inline_styles.clone(), rect: PhysicalRect::zero(), font_metrics: text_item.font_metrics, font_key: text_item.font_key, @@ -576,7 +575,6 @@ impl LineItemLayout<'_, '_> { text_decoration_line: text_item.text_decoration_line, justification_adjustment: self.justification_adjustment, selection_range: text_item.selection_range, - selected_style: text_item.selected_style, })), content_rect, )); @@ -763,7 +761,7 @@ impl LineItem { pub(super) struct TextRunLineItem { pub base_fragment_info: BaseFragmentInfo, - pub parent_style: Arc<ComputedValues>, + pub inline_styles: SharedInlineStyles, pub text: Vec<std::sync::Arc<GlyphStore>>, pub font_metrics: FontMetrics, pub font_key: FontInstanceKey, @@ -771,13 +769,16 @@ pub(super) struct TextRunLineItem { /// The BiDi level of this [`TextRunLineItem`] to enable reordering. pub bidi_level: Level, pub selection_range: Option<Range<ByteIndex>>, - pub selected_style: Arc<ComputedValues>, } impl TextRunLineItem { fn trim_whitespace_at_end(&mut self, whitespace_trimmed: &mut Au) -> bool { if matches!( - self.parent_style.get_inherited_text().white_space_collapse, + self.inline_styles + .style + .borrow() + .get_inherited_text() + .white_space_collapse, WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces ) { return false; @@ -803,7 +804,11 @@ impl TextRunLineItem { fn trim_whitespace_at_start(&mut self, whitespace_trimmed: &mut Au) -> bool { if matches!( - self.parent_style.get_inherited_text().white_space_collapse, + self.inline_styles + .style + .borrow() + .get_inherited_text() + .white_space_collapse, WhiteSpaceCollapse::Preserve | WhiteSpaceCollapse::BreakSpaces ) { return false; diff --git a/components/layout/flow/inline/mod.rs b/components/layout/flow/inline/mod.rs index 2023f4e7174..7e69aa1aaae 100644 --- a/components/layout/flow/inline/mod.rs +++ b/components/layout/flow/inline/mod.rs @@ -90,12 +90,13 @@ use line::{ use line_breaker::LineBreaker; use malloc_size_of_derive::MallocSizeOf; use range::Range; +use script::layout_dom::ServoLayoutNode; use servo_arc::Arc; use style::Zero; use style::computed_values::text_wrap_mode::T as TextWrapMode; use style::computed_values::vertical_align::T as VerticalAlign; use style::computed_values::white_space_collapse::T as WhiteSpaceCollapse; -use style::context::QuirksMode; +use style::context::{QuirksMode, SharedStyleContext}; use style::properties::ComputedValues; use style::properties::style_structs::InheritedText; use style::values::generics::box_::VerticalAlignKeyword; @@ -118,6 +119,7 @@ use super::{ }; use crate::cell::ArcRefCell; use crate::context::LayoutContext; +use crate::dom_traversal::NodeAndStyleInfo; use crate::flow::CollapsibleWithParentStartMargin; use crate::flow::float::{FloatBox, SequentialLayoutState}; use crate::formatting_contexts::{ @@ -131,7 +133,7 @@ use crate::geom::{LogicalRect, LogicalVec2, ToLogical}; use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; use crate::sizing::{ComputeInlineContentSizes, ContentSizes, InlineContentSizesResult}; use crate::style_ext::{ComputedValuesExt, PaddingBorderMargin}; -use crate::{ConstraintSpace, ContainingBlock, PropagatedBoxTreeData}; +use crate::{ConstraintSpace, ContainingBlock, PropagatedBoxTreeData, SharedStyle}; // From gfxFontConstants.h in Firefox. static FONT_SUBSCRIPT_OFFSET_RATIO: f32 = 0.20; @@ -173,6 +175,25 @@ pub(crate) struct InlineFormattingContext { pub(super) has_right_to_left_content: bool, } +/// [`TextRun`] and `TextFragment`s need a handle on their parent inline box (or inline +/// formatting context root)'s style. In order to implement incremental layout, these are +/// wrapped in [`SharedStyle`]. This allows updating the parent box tree element without +/// updating every single descendant box tree node and fragment. +#[derive(Clone, Debug, MallocSizeOf)] +pub(crate) struct SharedInlineStyles { + pub style: SharedStyle, + pub selected: SharedStyle, +} + +impl From<&NodeAndStyleInfo<'_>> for SharedInlineStyles { + fn from(info: &NodeAndStyleInfo) -> Self { + Self { + style: SharedStyle::new(info.style.clone()), + selected: SharedStyle::new(info.get_selected_style()), + } + } +} + /// A collection of data used to cache [`FontMetrics`] in the [`InlineFormattingContext`] #[derive(Debug, MallocSizeOf)] pub(crate) struct FontKeyAndMetrics { @@ -190,15 +211,41 @@ pub(crate) enum InlineItem { ArcRefCell<AbsolutelyPositionedBox>, usize, /* offset_in_text */ ), - OutOfFlowFloatBox(#[conditional_malloc_size_of] Arc<FloatBox>), + OutOfFlowFloatBox(ArcRefCell<FloatBox>), Atomic( - #[conditional_malloc_size_of] Arc<IndependentFormattingContext>, + ArcRefCell<IndependentFormattingContext>, usize, /* offset_in_text */ Level, /* bidi_level */ ), } impl InlineItem { + pub(crate) fn repair_style( + &self, + context: &SharedStyleContext, + node: &ServoLayoutNode, + new_style: &Arc<ComputedValues>, + ) { + match self { + InlineItem::StartInlineBox(inline_box) => { + inline_box.borrow_mut().repair_style(node, new_style); + }, + InlineItem::EndInlineBox => {}, + // TextRun holds a handle the `InlineSharedStyles` which is updated when repairing inline box + // and `display: contents` styles. + InlineItem::TextRun(..) => {}, + InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => positioned_box + .borrow_mut() + .context + .repair_style(context, new_style), + InlineItem::OutOfFlowFloatBox(float_box) => float_box + .borrow_mut() + .contents + .repair_style(context, new_style), + InlineItem::Atomic(atomic, ..) => atomic.borrow_mut().repair_style(context, new_style), + } + } + pub(crate) fn invalidate_cached_fragment(&self) { match self { InlineItem::StartInlineBox(inline_box) => { @@ -212,11 +259,14 @@ impl InlineItem { .base .invalidate_cached_fragment(); }, - InlineItem::OutOfFlowFloatBox(float_box) => { - float_box.contents.base.invalidate_cached_fragment() - }, + InlineItem::OutOfFlowFloatBox(float_box) => float_box + .borrow() + .contents + .base + .invalidate_cached_fragment(), InlineItem::Atomic(independent_formatting_context, ..) => { independent_formatting_context + .borrow() .base .invalidate_cached_fragment(); }, @@ -232,9 +282,11 @@ impl InlineItem { InlineItem::OutOfFlowAbsolutelyPositionedBox(positioned_box, ..) => { positioned_box.borrow().context.base.fragments() }, - InlineItem::OutOfFlowFloatBox(float_box) => float_box.contents.base.fragments(), + InlineItem::OutOfFlowFloatBox(float_box) => { + float_box.borrow().contents.base.fragments() + }, InlineItem::Atomic(independent_formatting_context, ..) => { - independent_formatting_context.base.fragments() + independent_formatting_context.borrow().base.fragments() }, } } @@ -958,6 +1010,7 @@ impl InlineFormattingContextLayout<'_> { .as_physical(Some(self.containing_block)); self.fragments .push(Fragment::Positioning(PositioningFragment::new_anonymous( + self.root_nesting_level.style.clone(), physical_line_rect, fragments, ))); @@ -1313,7 +1366,7 @@ impl InlineFormattingContextLayout<'_> { ) { let inline_advance = glyph_store.total_advance(); let flags = if glyph_store.is_whitespace() { - SegmentContentFlags::from(text_run.parent_style.get_inherited_text()) + SegmentContentFlags::from(text_run.inline_styles.style.borrow().get_inherited_text()) } else { SegmentContentFlags::empty() }; @@ -1398,13 +1451,12 @@ impl InlineFormattingContextLayout<'_> { TextRunLineItem { text: vec![glyph_store], base_fragment_info: text_run.base_fragment_info, - parent_style: text_run.parent_style.clone(), + inline_styles: text_run.inline_styles.clone(), font_metrics, font_key: ifc_font_info.key, text_decoration_line: self.current_inline_container_state().text_decoration_line, bidi_level, selection_range, - selected_style: text_run.selected_style.clone(), }, )); } @@ -1751,7 +1803,7 @@ impl InlineFormattingContext { InlineItem::EndInlineBox => layout.finish_inline_box(), InlineItem::TextRun(run) => run.borrow().layout_into_line_items(&mut layout), InlineItem::Atomic(atomic_formatting_context, offset_in_text, bidi_level) => { - atomic_formatting_context.layout_into_line_items( + atomic_formatting_context.borrow().layout_into_line_items( &mut layout, *offset_in_text, *bidi_level, @@ -1766,7 +1818,7 @@ impl InlineFormattingContext { )); }, InlineItem::OutOfFlowFloatBox(float_box) => { - float_box.layout_into_line_items(&mut layout); + float_box.borrow().layout_into_line_items(&mut layout); }, } } @@ -2363,8 +2415,9 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { }, InlineItem::TextRun(text_run) => { let text_run = &*text_run.borrow(); + let parent_style = text_run.inline_styles.style.borrow(); for segment in text_run.shaped_text.iter() { - let style_text = text_run.parent_style.get_inherited_text(); + let style_text = parent_style.get_inherited_text(); let can_wrap = style_text.text_wrap_mode == TextWrapMode::Wrap; // TODO: This should take account whether or not the first and last character prevent @@ -2428,7 +2481,7 @@ impl<'layout_data> ContentSizesComputation<'layout_data> { let InlineContentSizesResult { sizes: outer, depends_on_block_constraints, - } = atomic.outer_inline_content_sizes( + } = atomic.borrow().outer_inline_content_sizes( self.layout_context, &self.constraint_space.into(), &LogicalVec2::zero(), diff --git a/components/layout/flow/inline/text_run.rs b/components/layout/flow/inline/text_run.rs index 0d0c6398017..591c7b9b5e2 100644 --- a/components/layout/flow/inline/text_run.rs +++ b/components/layout/flow/inline/text_run.rs @@ -26,7 +26,7 @@ use unicode_script::Script; use xi_unicode::linebreak_property; use super::line_breaker::LineBreaker; -use super::{FontKeyAndMetrics, InlineFormattingContextLayout}; +use super::{FontKeyAndMetrics, InlineFormattingContextLayout, SharedInlineStyles}; use crate::fragment_tree::BaseFragmentInfo; // These constants are the xi-unicode line breaking classes that are defined in @@ -37,22 +37,6 @@ pub(crate) const XI_LINE_BREAKING_CLASS_ZW: u8 = 28; pub(crate) const XI_LINE_BREAKING_CLASS_WJ: u8 = 30; pub(crate) const XI_LINE_BREAKING_CLASS_ZWJ: u8 = 42; -/// <https://www.w3.org/TR/css-display-3/#css-text-run> -#[derive(Debug, MallocSizeOf)] -pub(crate) struct TextRun { - pub base_fragment_info: BaseFragmentInfo, - #[conditional_malloc_size_of] - pub parent_style: Arc<ComputedValues>, - pub text_range: Range<usize>, - - /// The text of this [`TextRun`] with a font selected, broken into unbreakable - /// segments, and shaped. - pub shaped_text: Vec<TextRunSegment>, - pub selection_range: Option<ServoRange<ByteIndex>>, - #[conditional_malloc_size_of] - pub selected_style: Arc<ComputedValues>, -} - // There are two reasons why we might want to break at the start: // // 1. The line breaker told us that a break was necessary between two separate @@ -334,21 +318,49 @@ impl TextRunSegment { } } +/// A single [`TextRun`] for the box tree. These are all descendants of +/// [`super::InlineBox`] or the root of the [`super::InlineFormattingContext`]. During +/// box tree construction, text is split into [`TextRun`]s based on their font, script, +/// etc. When these are created text is already shaped. +/// +/// <https://www.w3.org/TR/css-display-3/#css-text-run> +#[derive(Debug, MallocSizeOf)] +pub(crate) struct TextRun { + /// The [`BaseFragmentInfo`] for this [`TextRun`]. Usually this comes from the + /// original text node in the DOM for the text. + pub base_fragment_info: BaseFragmentInfo, + + /// The [`crate::SharedStyle`] from this [`TextRun`]s parent element. This is + /// shared so that incremental layout can simply update the parent element and + /// this [`TextRun`] will be updated automatically. + pub inline_styles: SharedInlineStyles, + + /// The range of text in [`super::InlineFormattingContext::text_content`] of the + /// [`super::InlineFormattingContext`] that owns this [`TextRun`]. These are UTF-8 offsets. + pub text_range: Range<usize>, + + /// The text of this [`TextRun`] with a font selected, broken into unbreakable + /// segments, and shaped. + pub shaped_text: Vec<TextRunSegment>, + + /// The selection range for the DOM text node that originated this [`TextRun`]. This + /// comes directly from the DOM. + pub selection_range: Option<ServoRange<ByteIndex>>, +} + impl TextRun { pub(crate) fn new( base_fragment_info: BaseFragmentInfo, - parent_style: Arc<ComputedValues>, + inline_styles: SharedInlineStyles, text_range: Range<usize>, selection_range: Option<ServoRange<ByteIndex>>, - selected_style: Arc<ComputedValues>, ) -> Self { Self { base_fragment_info, - parent_style, + inline_styles, text_range, shaped_text: Vec::new(), selection_range, - selected_style, } } @@ -360,11 +372,12 @@ impl TextRun { font_cache: &mut Vec<FontKeyAndMetrics>, bidi_info: &BidiInfo, ) { - let inherited_text_style = self.parent_style.get_inherited_text().clone(); + let parent_style = self.inline_styles.style.borrow().clone(); + let inherited_text_style = parent_style.get_inherited_text().clone(); let letter_spacing = inherited_text_style .letter_spacing .0 - .resolve(self.parent_style.clone_font().font_size.computed_size()); + .resolve(parent_style.clone_font().font_size.computed_size()); let letter_spacing = if letter_spacing.px() != 0. { Some(app_units::Au::from(letter_spacing)) } else { @@ -384,7 +397,13 @@ impl TextRun { let style_word_spacing: Option<Au> = specified_word_spacing.to_length().map(|l| l.into()); let segments = self - .segment_text_by_font(formatting_context_text, font_context, font_cache, bidi_info) + .segment_text_by_font( + formatting_context_text, + font_context, + font_cache, + bidi_info, + &parent_style, + ) .into_iter() .map(|(mut segment, font)| { let word_spacing = style_word_spacing.unwrap_or_else(|| { @@ -407,7 +426,7 @@ impl TextRun { }; segment.shape_text( - &self.parent_style, + &parent_style, formatting_context_text, linebreaker, &shaping_options, @@ -430,8 +449,9 @@ impl TextRun { font_context: &FontContext, font_cache: &mut Vec<FontKeyAndMetrics>, bidi_info: &BidiInfo, + parent_style: &Arc<ComputedValues>, ) -> Vec<(TextRunSegment, FontRef)> { - let font_group = font_context.font_group(self.parent_style.clone_font()); + let font_group = font_context.font_group(parent_style.clone_font()); let mut current: Option<(TextRunSegment, FontRef)> = None; let mut results = Vec::new(); diff --git a/components/layout/flow/mod.rs b/components/layout/flow/mod.rs index e23193f3904..6adb63153d6 100644 --- a/components/layout/flow/mod.rs +++ b/components/layout/flow/mod.rs @@ -9,9 +9,11 @@ use app_units::{Au, MAX_AU}; use inline::InlineFormattingContext; use malloc_size_of_derive::MallocSizeOf; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; +use script::layout_dom::ServoLayoutNode; use servo_arc::Arc; use style::Zero; use style::computed_values::clear::T as StyleClear; +use style::context::SharedStyleContext; use style::logical_geometry::Direction; use style::properties::ComputedValues; use style::servo::selector_parser::PseudoElement; @@ -21,6 +23,7 @@ use style::values::specified::{Display, TextAlignKeyword}; use crate::cell::ArcRefCell; use crate::context::LayoutContext; +use crate::dom::NodeExt; use crate::flow::float::{ Clear, ContainingBlockPositionInfo, FloatBox, FloatSide, PlacementAmongFloats, SequentialLayoutState, @@ -91,6 +94,36 @@ pub(crate) enum BlockLevelBox { } impl BlockLevelBox { + pub(crate) fn repair_style( + &mut self, + context: &SharedStyleContext, + node: &ServoLayoutNode, + new_style: &Arc<ComputedValues>, + ) { + self.with_base_mut(|base| { + base.repair_style(new_style); + }); + + match self { + BlockLevelBox::Independent(independent_formatting_context) => { + independent_formatting_context.repair_style(context, new_style) + }, + BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box + .borrow_mut() + .context + .repair_style(context, new_style), + BlockLevelBox::OutOfFlowFloatBox(float_box) => { + float_box.contents.repair_style(context, new_style) + }, + BlockLevelBox::OutsideMarker(outside_marker) => { + outside_marker.repair_style(context, node, new_style) + }, + BlockLevelBox::SameFormattingContextBlock { base, .. } => { + base.repair_style(new_style); + }, + } + } + pub(crate) fn invalidate_cached_fragment(&self) { self.with_base(LayoutBoxBase::invalidate_cached_fragment); } @@ -113,6 +146,20 @@ impl BlockLevelBox { } } + pub(crate) fn with_base_mut<T>(&mut self, callback: impl Fn(&mut LayoutBoxBase) -> T) -> T { + match self { + BlockLevelBox::Independent(independent_formatting_context) => { + callback(&mut independent_formatting_context.base) + }, + BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(positioned_box) => { + callback(&mut positioned_box.borrow_mut().context.base) + }, + BlockLevelBox::OutOfFlowFloatBox(float_box) => callback(&mut float_box.contents.base), + BlockLevelBox::OutsideMarker(outside_marker) => callback(&mut outside_marker.base), + BlockLevelBox::SameFormattingContextBlock { base, .. } => callback(base), + } + } + fn contains_floats(&self) -> bool { match self { BlockLevelBox::SameFormattingContextBlock { @@ -249,7 +296,6 @@ pub(crate) struct CollapsibleWithParentStartMargin(bool); /// for a list that has `list-style-position: outside`. #[derive(Debug, MallocSizeOf)] pub(crate) struct OutsideMarker { - #[conditional_malloc_size_of] pub list_item_style: Arc<ComputedValues>, pub base: LayoutBoxBase, pub block_container: BlockContainer, @@ -361,6 +407,16 @@ impl OutsideMarker { None, ))) } + + fn repair_style( + &mut self, + context: &SharedStyleContext, + node: &ServoLayoutNode, + new_style: &Arc<ComputedValues>, + ) { + self.list_item_style = node.style(context); + self.base.repair_style(new_style); + } } impl BlockFormattingContext { diff --git a/components/layout/flow/root.rs b/components/layout/flow/root.rs index ec85f3574dc..a37db54065d 100644 --- a/components/layout/flow/root.rs +++ b/components/layout/flow/root.rs @@ -59,7 +59,7 @@ impl BoxTree { // > none, user agents must instead apply the overflow-* values of the first such child // > element to the viewport. The element from which the value is propagated must then have a // > used overflow value of visible. - let root_style = root_element.style(context); + let root_style = root_element.style(context.shared_context()); let mut viewport_overflow_x = root_style.clone_overflow_x(); let mut viewport_overflow_y = root_style.clone_overflow_y(); @@ -76,7 +76,7 @@ impl BoxTree { continue; } - let style = child.style(context); + let style = child.style(context.shared_context()); if !style.get_box().display.is_none() { viewport_overflow_x = style.clone_overflow_x(); viewport_overflow_y = style.clone_overflow_y(); @@ -174,7 +174,7 @@ impl BoxTree { let update_point = match &*AtomicRef::filter_map(layout_data.self_box.borrow(), Option::as_ref)? { - LayoutBox::DisplayContents => return None, + LayoutBox::DisplayContents(..) => return None, LayoutBox::BlockLevel(block_level_box) => match &*block_level_box.borrow() { BlockLevelBox::OutOfFlowAbsolutelyPositionedBox(_) if box_style.position.is_absolutely_positioned() => @@ -293,7 +293,7 @@ fn construct_for_root_element( context: &LayoutContext, root_element: ServoLayoutNode<'_>, ) -> Vec<ArcRefCell<BlockLevelBox>> { - let info = NodeAndStyleInfo::new(root_element, root_element.style(context)); + let info = NodeAndStyleInfo::new(root_element, root_element.style(context.shared_context())); let box_style = info.style.get_box(); let display_inside = match Display::from(box_style.display) { diff --git a/components/layout/formatting_contexts.rs b/components/layout/formatting_contexts.rs index 04a8c60f692..d704011d0e7 100644 --- a/components/layout/formatting_contexts.rs +++ b/components/layout/formatting_contexts.rs @@ -6,6 +6,7 @@ use app_units::Au; use malloc_size_of_derive::MallocSizeOf; use script::layout_dom::ServoLayoutElement; use servo_arc::Arc; +use style::context::SharedStyleContext; use style::properties::ComputedValues; use style::selector_parser::PseudoElement; @@ -217,6 +218,20 @@ impl IndependentFormattingContext { }, } } + + pub(crate) fn repair_style( + &mut self, + context: &SharedStyleContext, + new_style: &Arc<ComputedValues>, + ) { + self.base.repair_style(new_style); + match &mut self.contents { + IndependentFormattingContextContents::NonReplaced(content) => { + content.repair_style(context, new_style); + }, + IndependentFormattingContextContents::Replaced(..) => {}, + } + } } impl IndependentNonReplacedContents { @@ -334,6 +349,19 @@ impl IndependentNonReplacedContents { pub(crate) fn is_table(&self) -> bool { matches!(self, Self::Table(_)) } + + fn repair_style(&mut self, context: &SharedStyleContext, new_style: &Arc<ComputedValues>) { + match self { + IndependentNonReplacedContents::Flow(..) => {}, + IndependentNonReplacedContents::Flex(flex_container) => { + flex_container.repair_style(new_style) + }, + IndependentNonReplacedContents::Grid(taffy_container) => { + taffy_container.repair_style(new_style) + }, + IndependentNonReplacedContents::Table(table) => table.repair_style(context, new_style), + } + } } impl ComputeInlineContentSizes for IndependentNonReplacedContents { diff --git a/components/layout/fragment_tree/box_fragment.rs b/components/layout/fragment_tree/box_fragment.rs index 596556b296c..9b96b1c4fb4 100644 --- a/components/layout/fragment_tree/box_fragment.rs +++ b/components/layout/fragment_tree/box_fragment.rs @@ -17,7 +17,7 @@ use style::properties::ComputedValues; use style::values::specified::box_::DisplayOutside; use super::{BaseFragment, BaseFragmentInfo, CollapsedBlockMargins, Fragment, FragmentFlags}; -use crate::ArcRefCell; +use crate::SharedStyle; use crate::display_list::ToWebRender; use crate::formatting_contexts::Baselines; use crate::geom::{ @@ -40,15 +40,9 @@ pub(crate) enum BackgroundMode { /// Draw the background normally, getting information from the Fragment style. Normal, } - -#[derive(Debug, MallocSizeOf)] -pub(crate) struct BackgroundStyle(#[conditional_malloc_size_of] pub ServoArc<ComputedValues>); - -pub(crate) type SharedBackgroundStyle = ArcRefCell<BackgroundStyle>; - #[derive(MallocSizeOf)] pub(crate) struct ExtraBackground { - pub style: SharedBackgroundStyle, + pub style: SharedStyle, pub rect: PhysicalRect<Au>, } @@ -64,7 +58,6 @@ pub(crate) enum SpecificLayoutInfo { pub(crate) struct BoxFragment { pub base: BaseFragment, - #[conditional_malloc_size_of] pub style: ServoArc<ComputedValues>, pub children: Vec<Fragment>, diff --git a/components/layout/fragment_tree/fragment.rs b/components/layout/fragment_tree/fragment.rs index 1c5324fa1c4..1ebc7b3c989 100644 --- a/components/layout/fragment_tree/fragment.rs +++ b/components/layout/fragment_tree/fragment.rs @@ -22,6 +22,7 @@ use super::{ Tag, }; use crate::cell::ArcRefCell; +use crate::flow::inline::SharedInlineStyles; use crate::geom::{LogicalSides, PhysicalPoint, PhysicalRect}; use crate::style_ext::ComputedValuesExt; @@ -64,8 +65,7 @@ pub(crate) struct CollapsedMargin { #[derive(MallocSizeOf)] pub(crate) struct TextFragment { pub base: BaseFragment, - #[conditional_malloc_size_of] - pub parent_style: ServoArc<ComputedValues>, + pub inline_styles: SharedInlineStyles, pub rect: PhysicalRect<Au>, pub font_metrics: FontMetrics, pub font_key: FontInstanceKey, @@ -78,14 +78,11 @@ pub(crate) struct TextFragment { /// Extra space to add for each justification opportunity. pub justification_adjustment: Au, pub selection_range: Option<ServoRange<ByteIndex>>, - #[conditional_malloc_size_of] - pub selected_style: ServoArc<ComputedValues>, } #[derive(MallocSizeOf)] pub(crate) struct ImageFragment { pub base: BaseFragment, - #[conditional_malloc_size_of] pub style: ServoArc<ComputedValues>, pub rect: PhysicalRect<Au>, pub clip: PhysicalRect<Au>, @@ -97,7 +94,6 @@ pub(crate) struct IFrameFragment { pub base: BaseFragment, pub pipeline_id: PipelineId, pub rect: PhysicalRect<Au>, - #[conditional_malloc_size_of] pub style: ServoArc<ComputedValues>, } @@ -308,6 +304,25 @@ impl Fragment { _ => None, } } + + pub(crate) fn repair_style(&self, style: &ServoArc<ComputedValues>) { + match self { + Fragment::Box(box_fragment) | Fragment::Float(box_fragment) => { + box_fragment.borrow_mut().style = style.clone() + }, + Fragment::Positioning(positioning_fragment) => { + positioning_fragment.borrow_mut().style = style.clone(); + }, + Fragment::AbsoluteOrFixedPositioned(positioned_fragment) => { + if let Some(ref fragment) = positioned_fragment.borrow().fragment { + fragment.repair_style(style); + } + }, + Fragment::Text(..) => unreachable!("Should never try to repair style of TextFragment"), + Fragment::Image(image_fragment) => image_fragment.borrow_mut().style = style.clone(), + Fragment::IFrame(iframe_fragment) => iframe_fragment.borrow_mut().style = style.clone(), + } + } } impl TextFragment { diff --git a/components/layout/fragment_tree/positioning_fragment.rs b/components/layout/fragment_tree/positioning_fragment.rs index 0cf525a3479..e45a6137bff 100644 --- a/components/layout/fragment_tree/positioning_fragment.rs +++ b/components/layout/fragment_tree/positioning_fragment.rs @@ -24,9 +24,8 @@ pub(crate) struct PositioningFragment { /// The scrollable overflow of this anonymous fragment's children. pub scrollable_overflow: PhysicalRect<Au>, - /// If this fragment was created with a style, the style of the fragment. - #[conditional_malloc_size_of] - pub style: Option<ServoArc<ComputedValues>>, + /// The style of the fragment. + pub style: ServoArc<ComputedValues>, /// This [`PositioningFragment`]'s containing block rectangle in coordinates relative to /// the initial containing block, but not taking into account any transforms. @@ -34,8 +33,12 @@ pub(crate) struct PositioningFragment { } impl PositioningFragment { - pub fn new_anonymous(rect: PhysicalRect<Au>, children: Vec<Fragment>) -> ArcRefCell<Self> { - Self::new_with_base_fragment(BaseFragment::anonymous(), None, rect, children) + pub fn new_anonymous( + style: ServoArc<ComputedValues>, + rect: PhysicalRect<Au>, + children: Vec<Fragment>, + ) -> ArcRefCell<Self> { + Self::new_with_base_fragment(BaseFragment::anonymous(), style, rect, children) } pub fn new_empty( @@ -43,12 +46,12 @@ impl PositioningFragment { rect: PhysicalRect<Au>, style: ServoArc<ComputedValues>, ) -> ArcRefCell<Self> { - Self::new_with_base_fragment(base_fragment_info.into(), Some(style), rect, Vec::new()) + Self::new_with_base_fragment(base_fragment_info.into(), style, rect, Vec::new()) } fn new_with_base_fragment( base: BaseFragment, - style: Option<ServoArc<ComputedValues>>, + style: ServoArc<ComputedValues>, rect: PhysicalRect<Au>, children: Vec<Fragment>, ) -> ArcRefCell<Self> { diff --git a/components/layout/layout_box_base.rs b/components/layout/layout_box_base.rs index 71fbfdeced1..161aee0f9bb 100644 --- a/components/layout/layout_box_base.rs +++ b/components/layout/layout_box_base.rs @@ -27,7 +27,6 @@ use crate::{ConstraintSpace, ContainingBlockSize}; #[derive(MallocSizeOf)] pub(crate) struct LayoutBoxBase { pub base_fragment_info: BaseFragmentInfo, - #[conditional_malloc_size_of] pub style: Arc<ComputedValues>, pub cached_inline_content_size: AtomicRefCell<Option<Box<(SizeConstraint, InlineContentSizesResult)>>>, @@ -90,6 +89,13 @@ impl LayoutBoxBase { pub(crate) fn clear_fragments(&self) { self.fragments.borrow_mut().clear(); } + + pub(crate) fn repair_style(&mut self, new_style: &Arc<ComputedValues>) { + self.style = new_style.clone(); + for fragment in self.fragments.borrow_mut().iter_mut() { + fragment.repair_style(new_style); + } + } } impl Debug for LayoutBoxBase { diff --git a/components/layout/layout_impl.rs b/components/layout/layout_impl.rs index cdf76d3fed0..fcf658036b2 100644 --- a/components/layout/layout_impl.rs +++ b/components/layout/layout_impl.rs @@ -56,7 +56,7 @@ use style::media_queries::{Device, MediaList, MediaType}; use style::properties::style_structs::Font; use style::properties::{ComputedValues, PropertyId}; use style::queries::values::PrefersColorScheme; -use style::selector_parser::{PseudoElement, SnapshotMap}; +use style::selector_parser::{PseudoElement, RestyleDamage, SnapshotMap}; use style::servo::media_queries::FontMetricsProvider; use style::shared_lock::{SharedRwLock, SharedRwLockReadGuard, StylesheetGuards}; use style::stylesheets::{ @@ -83,7 +83,7 @@ use crate::query::{ process_content_boxes_request, process_node_scroll_area_request, process_offset_parent_query, process_resolved_font_style_query, process_resolved_style_request, process_text_index_request, }; -use crate::traversal::RecalcStyle; +use crate::traversal::{RecalcStyle, compute_damage_and_repair_style}; use crate::{BoxTree, FragmentTree}; // This mutex is necessary due to syncronisation issues between two different types of thread-local storage @@ -765,6 +765,12 @@ impl LayoutThread { driver::traverse_dom(&recalc_style_traversal, token, rayon_pool).as_node(); let root_node = root_element.as_node(); + let damage = compute_damage_and_repair_style(layout_context.shared_context(), root_node); + if damage == RestyleDamage::REPAINT { + layout_context.style_context.stylist.rule_tree().maybe_gc(); + return; + } + let mut box_tree = self.box_tree.borrow_mut(); let box_tree = &mut *box_tree; let mut build_box_tree = || { diff --git a/components/layout/lib.rs b/components/layout/lib.rs index af7d432c4d8..cd992387277 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -38,6 +38,7 @@ pub use flow::BoxTree; pub use fragment_tree::FragmentTree; pub use layout_impl::LayoutFactoryImpl; use malloc_size_of_derive::MallocSizeOf; +use servo_arc::Arc as ServoArc; use style::logical_geometry::WritingMode; use style::properties::ComputedValues; use style::values::computed::TextDecorationLine; @@ -45,6 +46,16 @@ use style::values::computed::TextDecorationLine; use crate::geom::{LogicalVec2, SizeConstraint}; use crate::style_ext::AspectRatio; +/// At times, a style is "owned" by more than one layout object. For example, text +/// fragments need a handle on their parent inline box's style. In order to make +/// incremental layout easier to implement, another layer of shared ownership is added via +/// [`SharedStyle`]. This allows updating the style in originating layout object and +/// having all "depdendent" objects update automatically. +/// +/// Note that this is not a cost-free data structure, so should only be +/// used when necessary. +pub(crate) type SharedStyle = ArcRefCell<ServoArc<ComputedValues>>; + /// Represents the set of constraints that we use when computing the min-content /// and max-content inline sizes of an element. pub(crate) struct ConstraintSpace { diff --git a/components/layout/positioned.rs b/components/layout/positioned.rs index bb5386dc696..b607462d6eb 100644 --- a/components/layout/positioned.rs +++ b/components/layout/positioned.rs @@ -473,7 +473,7 @@ impl HoistedAbsolutelyPositionedBox { false => shared_fragment.resolved_alignment.inline, }; - let mut inline_axis_solver = AbsoluteAxisSolver { + let inline_axis_solver = AbsoluteAxisSolver { axis: Direction::Inline, containing_size: cbis, padding_border_sum: pbm.padding_border_sums.inline, @@ -496,7 +496,7 @@ impl HoistedAbsolutelyPositionedBox { true => style.clone_align_self().0.0, false => shared_fragment.resolved_alignment.block, }; - let mut block_axis_solver = AbsoluteAxisSolver { + let block_axis_solver = AbsoluteAxisSolver { axis: Direction::Block, containing_size: cbbs, padding_border_sum: pbm.padding_border_sums.block, @@ -511,52 +511,6 @@ impl HoistedAbsolutelyPositionedBox { is_table, }; - if let IndependentFormattingContextContents::Replaced(replaced) = &context.contents { - // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width - // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height - let inset_sums = LogicalVec2 { - inline: inline_axis_solver.inset_sum(), - block: block_axis_solver.inset_sum(), - }; - let automatic_size = |alignment: AlignFlags, offsets: &LogicalSides1D<_>| { - if alignment.value() == AlignFlags::STRETCH && !offsets.either_auto() { - Size::Stretch - } else { - Size::FitContent - } - }; - let used_size = replaced.used_size_as_if_inline_element_from_content_box_sizes( - containing_block, - &style, - context.preferred_aspect_ratio(&pbm.padding_border_sums), - LogicalVec2 { - inline: &inline_axis_solver.computed_sizes, - block: &block_axis_solver.computed_sizes, - }, - LogicalVec2 { - inline: automatic_size(inline_alignment, &inline_axis_solver.box_offsets), - block: automatic_size(block_alignment, &block_axis_solver.box_offsets), - }, - pbm.padding_border_sums + pbm.margin.auto_is(Au::zero).sum() + inset_sums, - ); - inline_axis_solver.override_size(used_size.inline); - block_axis_solver.override_size(used_size.block); - } - - // The block axis can depend on layout results, so we only solve it tentatively, - // we may have to resolve it properly later on. - let mut block_axis = block_axis_solver.solve_tentatively(); - - // The inline axis can be fully resolved, computing intrinsic sizes using the - // tentative block size. - let mut inline_axis = inline_axis_solver.solve(Some(|| { - let ratio = context.preferred_aspect_ratio(&pbm.padding_border_sums); - let constraint_space = ConstraintSpace::new(block_axis.size, style.writing_mode, ratio); - context - .inline_content_sizes(layout_context, &constraint_space) - .sizes - })); - let mut positioning_context = PositioningContext::default(); let mut new_fragment = { let content_size: LogicalVec2<Au>; @@ -566,10 +520,34 @@ impl HoistedAbsolutelyPositionedBox { IndependentFormattingContextContents::Replaced(replaced) => { // https://drafts.csswg.org/css2/visudet.html#abs-replaced-width // https://drafts.csswg.org/css2/visudet.html#abs-replaced-height - content_size = LogicalVec2 { - inline: inline_axis.size.to_definite().unwrap(), - block: block_axis.size.to_definite().unwrap(), + let inset_sums = LogicalVec2 { + inline: inline_axis_solver.inset_sum(), + block: block_axis_solver.inset_sum(), + }; + let automatic_size = |alignment: AlignFlags, offsets: &LogicalSides1D<_>| { + if alignment.value() == AlignFlags::STRETCH && !offsets.either_auto() { + Size::Stretch + } else { + Size::FitContent + } }; + content_size = replaced.used_size_as_if_inline_element_from_content_box_sizes( + containing_block, + &style, + context.preferred_aspect_ratio(&pbm.padding_border_sums), + LogicalVec2 { + inline: &inline_axis_solver.computed_sizes, + block: &block_axis_solver.computed_sizes, + }, + LogicalVec2 { + inline: automatic_size( + inline_alignment, + &inline_axis_solver.box_offsets, + ), + block: automatic_size(block_alignment, &block_axis_solver.box_offsets), + }, + pbm.padding_border_sums + pbm.margin.auto_is(Au::zero).sum() + inset_sums, + ); fragments = replaced.make_fragments( layout_context, &style, @@ -579,11 +557,26 @@ impl HoistedAbsolutelyPositionedBox { IndependentFormattingContextContents::NonReplaced(non_replaced) => { // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width // https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height - let inline_size = inline_axis.size.to_definite().unwrap(); + + // The block size can depend on layout results, so we only solve it extrinsically, + // we may have to resolve it properly later on. + let extrinsic_block_size = block_axis_solver.solve_size_extrinsically(); + + // The inline axis can be fully resolved, computing intrinsic sizes using the + // extrinsic block size. + let inline_size = inline_axis_solver.solve_size(|| { + let ratio = context.preferred_aspect_ratio(&pbm.padding_border_sums); + let constraint_space = + ConstraintSpace::new(extrinsic_block_size, style.writing_mode, ratio); + context + .inline_content_sizes(layout_context, &constraint_space) + .sizes + }); + let containing_block_for_children = ContainingBlock { size: ContainingBlockSize { inline: inline_size, - block: block_axis.size, + block: extrinsic_block_size, }, style: &style, }; @@ -603,22 +596,14 @@ impl HoistedAbsolutelyPositionedBox { false, /* depends_on_block_constraints */ ); - let inline_size = if let Some(inline_size) = - independent_layout.content_inline_size_for_table - { - // Tables can become narrower than predicted due to collapsed columns, - // so we need to solve again to update margins. - inline_axis_solver.override_size(inline_size); - inline_axis = inline_axis_solver.solve_tentatively(); - inline_size - } else { - inline_size - }; + // Tables can become narrower than predicted due to collapsed columns + let inline_size = independent_layout + .content_inline_size_for_table + .unwrap_or(inline_size); // Now we can properly solve the block size. - block_axis = block_axis_solver - .solve(Some(|| independent_layout.content_block_size.into())); - let block_size = block_axis.size.to_definite().unwrap(); + let block_size = block_axis_solver + .solve_size(|| independent_layout.content_block_size.into()); content_size = LogicalVec2 { inline: inline_size, @@ -629,11 +614,13 @@ impl HoistedAbsolutelyPositionedBox { }, }; + let inline_margins = inline_axis_solver.solve_margins(content_size.inline); + let block_margins = block_axis_solver.solve_margins(content_size.block); let margin = LogicalSides { - inline_start: inline_axis.margin_start, - inline_end: inline_axis.margin_end, - block_start: block_axis.margin_start, - block_end: block_axis.margin_end, + inline_start: inline_margins.start, + inline_end: inline_margins.end, + block_start: block_margins.start, + block_end: block_margins.end, }; let pb = pbm.padding + pbm.border; @@ -715,12 +702,6 @@ impl LogicalRect<Au> { } } -struct AxisResult { - size: SizeConstraint, - margin_start: Au, - margin_end: Au, -} - struct AbsoluteAxisSolver<'a> { axis: Direction, containing_size: Au, @@ -763,101 +744,77 @@ impl AbsoluteAxisSolver<'_> { } } - /// This unifies some of the parts in common in: - /// - /// * <https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width> - /// * <https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-height> - /// - /// … and: - /// - /// * <https://drafts.csswg.org/css2/visudet.html#abs-replaced-width> - /// * <https://drafts.csswg.org/css2/visudet.html#abs-replaced-height> - /// - /// In the replaced case, `size` is never `Auto`. - fn solve(&self, get_content_size: Option<impl FnOnce() -> ContentSizes>) -> AxisResult { - let solve_size = |initial_behavior, stretch_size: Au| -> SizeConstraint { - let stretch_size = stretch_size.max(Au::zero()); - if let Some(get_content_size) = get_content_size { - SizeConstraint::Definite(self.computed_sizes.resolve( - self.axis, - initial_behavior, - Au::zero, - Some(stretch_size), - get_content_size, - self.is_table, - )) - } else { - self.computed_sizes.resolve_extrinsic( - initial_behavior, - Au::zero(), - Some(stretch_size), - ) - } - }; - if self.box_offsets.either_auto() { - let margin_start = self.computed_margin_start.auto_is(Au::zero); - let margin_end = self.computed_margin_end.auto_is(Au::zero); - let stretch_size = self.containing_size - + #[inline] + fn automatic_size(&self) -> Size<Au> { + match self.alignment.value() { + _ if self.box_offsets.either_auto() => Size::FitContent, + AlignFlags::NORMAL | AlignFlags::AUTO if !self.is_table => Size::Stretch, + AlignFlags::STRETCH => Size::Stretch, + _ => Size::FitContent, + } + } + + #[inline] + fn stretch_size(&self) -> Au { + Au::zero().max( + self.containing_size - self.inset_sum() - self.padding_border_sum - - margin_start - - margin_end; - let size = solve_size(Size::FitContent, stretch_size); - AxisResult { - size, - margin_start, - margin_end, - } - } else { - let mut free_space = self.containing_size - self.inset_sum() - self.padding_border_sum; - let stretch_size = free_space - self.computed_margin_start.auto_is(Au::zero) - - self.computed_margin_end.auto_is(Au::zero); - let initial_behavior = match self.alignment.value() { - AlignFlags::NORMAL | AlignFlags::AUTO if !self.is_table => Size::Stretch, - AlignFlags::STRETCH => Size::Stretch, - _ => Size::FitContent, - }; - let size = solve_size(initial_behavior, stretch_size); - if let Some(used_size) = size.to_definite() { - free_space -= used_size; - } else { - free_space = Au::zero(); - } - let (margin_start, margin_end) = - match (self.computed_margin_start, self.computed_margin_end) { - (AuOrAuto::Auto, AuOrAuto::Auto) => { - if self.avoid_negative_margin_start && free_space < Au::zero() { - (Au::zero(), free_space) - } else { - let margin_start = free_space / 2; - (margin_start, free_space - margin_start) - } - }, - (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => (free_space - end, end), - (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => { - (start, free_space - start) - }, - (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => { - (start, end) - }, - }; - AxisResult { - size, - margin_start, - margin_end, - } - } + self.computed_margin_end.auto_is(Au::zero), + ) } - fn solve_tentatively(&mut self) -> AxisResult { - self.solve(None::<fn() -> ContentSizes>) + #[inline] + fn solve_size_extrinsically(&self) -> SizeConstraint { + self.computed_sizes.resolve_extrinsic( + self.automatic_size(), + Au::zero(), + Some(self.stretch_size()), + ) + } + + #[inline] + fn solve_size(&self, get_content_size: impl FnOnce() -> ContentSizes) -> Au { + self.computed_sizes.resolve( + self.axis, + self.automatic_size(), + Au::zero, + Some(self.stretch_size()), + get_content_size, + self.is_table, + ) } - fn override_size(&mut self, size: Au) { - self.computed_sizes.preferred = Size::Numeric(size); - self.computed_sizes.min = Size::default(); - self.computed_sizes.max = Size::default(); + fn solve_margins(&self, size: Au) -> LogicalSides1D<Au> { + if self.box_offsets.either_auto() { + LogicalSides1D::new( + self.computed_margin_start.auto_is(Au::zero), + self.computed_margin_end.auto_is(Au::zero), + ) + } else { + let free_space = + self.containing_size - self.inset_sum() - self.padding_border_sum - size; + match (self.computed_margin_start, self.computed_margin_end) { + (AuOrAuto::Auto, AuOrAuto::Auto) => { + if self.avoid_negative_margin_start && free_space < Au::zero() { + LogicalSides1D::new(Au::zero(), free_space) + } else { + let margin_start = free_space / 2; + LogicalSides1D::new(margin_start, free_space - margin_start) + } + }, + (AuOrAuto::Auto, AuOrAuto::LengthPercentage(end)) => { + LogicalSides1D::new(free_space - end, end) + }, + (AuOrAuto::LengthPercentage(start), AuOrAuto::Auto) => { + LogicalSides1D::new(start, free_space - start) + }, + (AuOrAuto::LengthPercentage(start), AuOrAuto::LengthPercentage(end)) => { + LogicalSides1D::new(start, end) + }, + } + } } fn origin_for_margin_box( diff --git a/components/layout/table/construct.rs b/components/layout/table/construct.rs index 133904db7ae..0c238073df2 100644 --- a/components/layout/table/construct.rs +++ b/components/layout/table/construct.rs @@ -19,7 +19,6 @@ use super::{ Table, TableCaption, TableLevelBox, TableSlot, TableSlotCell, TableSlotCoordinates, TableSlotOffset, TableTrack, TableTrackGroup, TableTrackGroupType, }; -use crate::PropagatedBoxTreeData; use crate::cell::ArcRefCell; use crate::context::LayoutContext; use crate::dom::{BoxSlot, LayoutBox}; @@ -29,9 +28,10 @@ use crate::formatting_contexts::{ IndependentFormattingContext, IndependentFormattingContextContents, IndependentNonReplacedContents, }; -use crate::fragment_tree::{BackgroundStyle, BaseFragmentInfo, SharedBackgroundStyle}; +use crate::fragment_tree::BaseFragmentInfo; use crate::layout_box_base::LayoutBoxBase; use crate::style_ext::{DisplayGeneratingBox, DisplayLayoutInternal}; +use crate::{PropagatedBoxTreeData, SharedStyle}; /// A reference to a slot and its coordinates in the table #[derive(Debug)] @@ -725,7 +725,7 @@ impl<'style, 'dom> TableBuilderTraversal<'style, 'dom> { base: LayoutBoxBase::new((&anonymous_info).into(), style.clone()), group_index: self.current_row_group_index, is_anonymous: true, - shared_background_style: SharedBackgroundStyle::new(BackgroundStyle(style)), + shared_background_style: SharedStyle::new(style), })); } @@ -767,9 +767,7 @@ impl<'dom> TraversalHandler<'dom> for TableBuilderTraversal<'_, 'dom> { base: LayoutBoxBase::new(info.into(), info.style.clone()), group_type: internal.into(), track_range: next_row_index..next_row_index, - shared_background_style: SharedBackgroundStyle::new(BackgroundStyle( - info.style.clone(), - )), + shared_background_style: SharedStyle::new(info.style.clone()), }); self.builder.table.row_groups.push(row_group.clone()); @@ -812,9 +810,7 @@ impl<'dom> TraversalHandler<'dom> for TableBuilderTraversal<'_, 'dom> { base: LayoutBoxBase::new(info.into(), info.style.clone()), group_index: self.current_row_group_index, is_anonymous: false, - shared_background_style: SharedBackgroundStyle::new(BackgroundStyle( - info.style.clone(), - )), + shared_background_style: SharedStyle::new(info.style.clone()), }); self.push_table_row(row.clone()); box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::Track(row))); @@ -860,9 +856,7 @@ impl<'dom> TraversalHandler<'dom> for TableBuilderTraversal<'_, 'dom> { base: LayoutBoxBase::new(info.into(), info.style.clone()), group_type: internal.into(), track_range: first_column..self.builder.table.columns.len(), - shared_background_style: SharedBackgroundStyle::new(BackgroundStyle( - info.style.clone(), - )), + shared_background_style: SharedStyle::new(info.style.clone()), }); self.builder.table.column_groups.push(column_group.clone()); box_slot.set(LayoutBox::TableLevelBox(TableLevelBox::TrackGroup( @@ -1145,9 +1139,7 @@ fn add_column( base: LayoutBoxBase::new(column_info.into(), column_info.style.clone()), group_index, is_anonymous, - shared_background_style: SharedBackgroundStyle::new(BackgroundStyle( - column_info.style.clone(), - )), + shared_background_style: SharedStyle::new(column_info.style.clone()), }); collection.extend(repeat(column.clone()).take(span as usize)); column diff --git a/components/layout/table/layout.rs b/components/layout/table/layout.rs index 00dac210625..5b7e79d7fb0 100644 --- a/components/layout/table/layout.rs +++ b/components/layout/table/layout.rs @@ -2867,6 +2867,7 @@ impl TableSlotCell { block: vertical_align_offset, }; let vertical_align_fragment = PositioningFragment::new_anonymous( + self.base.style.clone(), vertical_align_fragment_rect.as_physical(None), layout.layout.fragments, ); diff --git a/components/layout/table/mod.rs b/components/layout/table/mod.rs index 8e2783e2919..72b67863e7d 100644 --- a/components/layout/table/mod.rs +++ b/components/layout/table/mod.rs @@ -76,16 +76,20 @@ pub(crate) use construct::AnonymousTableContent; pub use construct::TableBuilder; use euclid::{Point2D, Size2D, UnknownUnit, Vector2D}; use malloc_size_of_derive::MallocSizeOf; +use script::layout_dom::ServoLayoutElement; use servo_arc::Arc; +use style::context::SharedStyleContext; use style::properties::ComputedValues; use style::properties::style_structs::Font; +use style::selector_parser::PseudoElement; use style_traits::dom::OpaqueNode; use super::flow::BlockFormattingContext; +use crate::SharedStyle; use crate::cell::ArcRefCell; use crate::flow::BlockContainer; use crate::formatting_contexts::IndependentFormattingContext; -use crate::fragment_tree::{BaseFragmentInfo, Fragment, SharedBackgroundStyle}; +use crate::fragment_tree::{BaseFragmentInfo, Fragment}; use crate::geom::PhysicalVec; use crate::layout_box_base::LayoutBoxBase; use crate::style_ext::BorderStyleColor; @@ -98,12 +102,10 @@ pub struct Table { /// The style of this table. These are the properties that apply to the "wrapper" ie the element /// that contains both the grid and the captions. Not all properties are actually used on the /// wrapper though, such as background and borders, which apply to the grid. - #[conditional_malloc_size_of] style: Arc<ComputedValues>, /// The style of this table's grid. This is an anonymous style based on the table's style, but /// eliminating all the properties handled by the "wrapper." - #[conditional_malloc_size_of] grid_style: Arc<ComputedValues>, /// The [`BaseFragmentInfo`] for this table's grid. This is necessary so that when the @@ -192,6 +194,19 @@ impl Table { ), } } + + pub(crate) fn repair_style( + &mut self, + context: &SharedStyleContext, + new_style: &Arc<ComputedValues>, + ) { + self.style = new_style.clone(); + self.grid_style = context.stylist.style_for_anonymous::<ServoLayoutElement>( + &context.guards, + &PseudoElement::ServoTableGrid, + new_style, + ); + } } type TableSlotCoordinates = Point2D<usize, UnknownUnit>; @@ -233,6 +248,10 @@ impl TableSlotCell { pub fn node_id(&self) -> usize { self.base.base_fragment_info.tag.map_or(0, |tag| tag.node.0) } + + fn repair_style(&mut self, new_style: &Arc<ComputedValues>) { + self.base.repair_style(new_style); + } } /// A single table slot. It may be an actual cell, or a reference @@ -292,7 +311,14 @@ pub struct TableTrack { /// A shared container for this track's style, used to share the style for the purposes /// of drawing backgrounds in individual cells. This allows updating the style in a /// single place and having it affect all cell `Fragment`s. - shared_background_style: SharedBackgroundStyle, + shared_background_style: SharedStyle, +} + +impl TableTrack { + fn repair_style(&mut self, new_style: &Arc<ComputedValues>) { + self.base.repair_style(new_style); + self.shared_background_style = SharedStyle::new(new_style.clone()); + } } #[derive(Debug, MallocSizeOf, PartialEq)] @@ -317,13 +343,18 @@ pub struct TableTrackGroup { /// A shared container for this track's style, used to share the style for the purposes /// of drawing backgrounds in individual cells. This allows updating the style in a /// single place and having it affect all cell `Fragment`s. - shared_background_style: SharedBackgroundStyle, + shared_background_style: SharedStyle, } impl TableTrackGroup { pub(super) fn is_empty(&self) -> bool { self.track_range.is_empty() } + + fn repair_style(&mut self, new_style: &Arc<ComputedValues>) { + self.base.repair_style(new_style); + self.shared_background_style = SharedStyle::new(new_style.clone()); + } } #[derive(Debug, MallocSizeOf)] @@ -390,4 +421,22 @@ impl TableLevelBox { TableLevelBox::Track(track) => track.borrow().base.fragments(), } } + + pub(crate) fn repair_style( + &self, + context: &SharedStyleContext<'_>, + new_style: &Arc<ComputedValues>, + ) { + match self { + TableLevelBox::Caption(caption) => caption + .borrow_mut() + .context + .repair_style(context, new_style), + TableLevelBox::Cell(cell) => cell.borrow_mut().repair_style(new_style), + TableLevelBox::TrackGroup(track_group) => { + track_group.borrow_mut().repair_style(new_style); + }, + TableLevelBox::Track(track) => track.borrow_mut().repair_style(new_style), + } + } } diff --git a/components/layout/taffy/mod.rs b/components/layout/taffy/mod.rs index 50873fc3e66..ba80824fa99 100644 --- a/components/layout/taffy/mod.rs +++ b/components/layout/taffy/mod.rs @@ -8,6 +8,7 @@ use std::fmt; use app_units::Au; use malloc_size_of_derive::MallocSizeOf; use servo_arc::Arc; +use style::context::SharedStyleContext; use style::properties::ComputedValues; use stylo_taffy::TaffyStyloStyle; @@ -24,7 +25,6 @@ use crate::positioned::{AbsolutelyPositionedBox, PositioningContext}; #[derive(Debug, MallocSizeOf)] pub(crate) struct TaffyContainer { children: Vec<ArcRefCell<TaffyItemBox>>, - #[conditional_malloc_size_of] style: Arc<ComputedValues>, } @@ -69,6 +69,10 @@ impl TaffyContainer { style: info.style.clone(), } } + + pub(crate) fn repair_style(&mut self, new_style: &Arc<ComputedValues>) { + self.style = new_style.clone(); + } } #[derive(MallocSizeOf)] @@ -76,7 +80,6 @@ pub(crate) struct TaffyItemBox { pub(crate) taffy_layout: taffy::Layout, pub(crate) child_fragments: Vec<Fragment>, pub(crate) positioning_context: PositioningContext, - #[conditional_malloc_size_of] pub(crate) style: Arc<ComputedValues>, pub(crate) taffy_level_box: TaffyItemBoxInner, } @@ -145,6 +148,23 @@ impl TaffyItemBox { }, } } + + pub(crate) fn repair_style( + &mut self, + context: &SharedStyleContext, + new_style: &Arc<ComputedValues>, + ) { + self.style = new_style.clone(); + match &mut self.taffy_level_box { + TaffyItemBoxInner::InFlowBox(independent_formatting_context) => { + independent_formatting_context.repair_style(context, new_style) + }, + TaffyItemBoxInner::OutOfFlowAbsolutelyPositionedBox(positioned_box) => positioned_box + .borrow_mut() + .context + .repair_style(context, new_style), + } + } } /// Details from Taffy grid layout that will be stored diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index bf60c41d6ba..17c3d0b1c20 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -2,14 +2,18 @@ * 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 script::layout_dom::ServoLayoutNode; use script_layout_interface::wrapper_traits::LayoutNode; use style::context::{SharedStyleContext, StyleContext}; use style::data::ElementData; use style::dom::{NodeInfo, TElement, TNode}; +use style::selector_parser::RestyleDamage; use style::traversal::{DomTraversal, PerLevelTraversalData, recalc_style_at}; +use style::values::computed::Display; use crate::context::LayoutContext; -use crate::dom::DOMLayoutData; +use crate::dom::{DOMLayoutData, NodeExt}; +use crate::dom_traversal::iter_child_nodes; pub struct RecalcStyle<'a> { context: &'a LayoutContext<'a>, @@ -40,14 +44,33 @@ where ) where F: FnMut(E::ConcreteNode), { + if node.is_text_node() { + return; + } + + let had_style_data = node.style_data().is_some(); unsafe { node.initialize_style_and_layout_data::<DOMLayoutData>(); - if !node.is_text_node() { - let el = node.as_element().unwrap(); - let mut data = el.mutate_data().unwrap(); - recalc_style_at(self, traversal_data, context, el, &mut data, note_child); - el.unset_dirty_descendants(); - } + } + + let element = node.as_element().unwrap(); + let mut element_data = element.mutate_data().unwrap(); + + if !had_style_data { + element_data.damage = RestyleDamage::reconstruct(); + } + + recalc_style_at( + self, + traversal_data, + context, + element, + &mut element_data, + note_child, + ); + + unsafe { + element.unset_dirty_descendants(); } } @@ -68,3 +91,48 @@ where &self.context.style_context } } + +pub(crate) fn compute_damage_and_repair_style( + context: &SharedStyleContext, + node: ServoLayoutNode<'_>, +) -> RestyleDamage { + compute_damage_and_repair_style_inner(context, node, RestyleDamage::empty()) +} + +pub(crate) fn compute_damage_and_repair_style_inner( + context: &SharedStyleContext, + node: ServoLayoutNode<'_>, + parent_restyle_damage: RestyleDamage, +) -> RestyleDamage { + let original_damage; + let damage = { + let mut element_data = node + .style_data() + .expect("Should not run `compute_damage` before styling.") + .element_data + .borrow_mut(); + + if let Some(ref style) = element_data.styles.primary { + if style.get_box().display == Display::None { + return parent_restyle_damage; + } + } + + original_damage = std::mem::take(&mut element_data.damage); + element_data.damage |= parent_restyle_damage; + element_data.damage + }; + + let mut propagated_damage = damage; + for child in iter_child_nodes(node) { + if child.is_element() { + propagated_damage |= compute_damage_and_repair_style_inner(context, child, damage); + } + } + + if propagated_damage == RestyleDamage::REPAINT && original_damage == RestyleDamage::REPAINT { + node.repair_style(context); + } + + propagated_damage +} diff --git a/components/malloc_size_of/lib.rs b/components/malloc_size_of/lib.rs index 2bdeedf986d..ae951da97e5 100644 --- a/components/malloc_size_of/lib.rs +++ b/components/malloc_size_of/lib.rs @@ -53,6 +53,7 @@ use std::ops::Range; use std::rc::Rc; use std::sync::Arc; +use style::properties::ComputedValues; use style::values::generics::length::GenericLengthPercentageOrAuto; pub use stylo_malloc_size_of::MallocSizeOfOps; use uuid::Uuid; @@ -750,6 +751,12 @@ impl<T: MallocSizeOf> MallocSizeOf for accountable_refcell::RefCell<T> { } } +impl MallocSizeOf for servo_arc::Arc<ComputedValues> { + fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { + self.conditional_size_of(ops) + } +} + malloc_size_of_hash_map!(indexmap::IndexMap<K, V, S>); malloc_size_of_hash_set!(indexmap::IndexSet<T, S>); diff --git a/components/net/http_loader.rs b/components/net/http_loader.rs index e0867b8d07f..704901f6940 100644 --- a/components/net/http_loader.rs +++ b/components/net/http_loader.rs @@ -37,7 +37,7 @@ use hyper::ext::ReasonPhrase; use hyper::header::{HeaderName, TRANSFER_ENCODING}; use hyper_serde::Serde; use hyper_util::client::legacy::Client; -use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::ipc::{self, IpcSender, IpcSharedMemory}; use ipc_channel::router::ROUTER; use log::{debug, error, info, log_enabled, warn}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; @@ -462,7 +462,7 @@ fn auth_from_cache( /// used to fill the body with bytes coming-in over IPC. enum BodyChunk { /// A chunk of bytes. - Chunk(Vec<u8>), + Chunk(IpcSharedMemory), /// Body is done. Done, } @@ -489,12 +489,14 @@ enum BodySink { } impl BodySink { - fn transmit_bytes(&self, bytes: Vec<u8>) { + fn transmit_bytes(&self, bytes: IpcSharedMemory) { match self { BodySink::Chunked(sender) => { let sender = sender.clone(); HANDLE.spawn(async move { - let _ = sender.send(Ok(Frame::data(bytes.into()))).await; + let _ = sender + .send(Ok(Frame::data(Bytes::copy_from_slice(&bytes)))) + .await; }); }, BodySink::Buffered(sender) => { @@ -577,7 +579,7 @@ async fn obtain_response( body_port, Box::new(move |message| { info!("Received message"); - let bytes: Vec<u8> = match message.unwrap() { + let bytes = match message.unwrap() { BodyChunkResponse::Chunk(bytes) => bytes, BodyChunkResponse::Done => { // Step 3, abort these parallel steps. @@ -622,8 +624,8 @@ async fn obtain_response( let mut body = vec![]; loop { match receiver.recv().await { - Some(BodyChunk::Chunk(mut bytes)) => { - body.append(&mut bytes); + Some(BodyChunk::Chunk(bytes)) => { + body.extend_from_slice(&bytes); }, Some(BodyChunk::Done) => break, None => warn!("Failed to read all chunks from request body."), diff --git a/components/net/tests/http_loader.rs b/components/net/tests/http_loader.rs index 1fc2d1b662d..b1e90276472 100644 --- a/components/net/tests/http_loader.rs +++ b/components/net/tests/http_loader.rs @@ -31,7 +31,7 @@ use http::{HeaderName, Method, StatusCode}; use http_body_util::combinators::BoxBody; use hyper::body::{Body, Bytes, Incoming}; use hyper::{Request as HyperRequest, Response as HyperResponse}; -use ipc_channel::ipc; +use ipc_channel::ipc::{self, IpcSharedMemory}; use ipc_channel::router::ROUTER; use net::cookie::ServoCookie; use net::cookie_storage::CookieStorage; @@ -100,7 +100,7 @@ pub fn expect_devtools_http_response( } } -fn create_request_body_with_content(content: Vec<u8>) -> RequestBody { +fn create_request_body_with_content(content: IpcSharedMemory) -> RequestBody { let content_len = content.len(); let (chunk_request_sender, chunk_request_receiver) = ipc::channel().unwrap(); @@ -592,7 +592,7 @@ fn test_load_doesnt_send_request_body_on_any_redirect() { let (pre_server, pre_url) = make_server(pre_handler); let content = b"Body on POST!"; - let request_body = create_request_body_with_content(content.to_vec()); + let request_body = create_request_body_with_content(IpcSharedMemory::from_bytes(content)); let request = RequestBuilder::new(None, pre_url.clone(), Referrer::NoReferrer) .body(Some(request_body)) @@ -904,7 +904,7 @@ fn test_load_sets_content_length_to_length_of_request_body() { }; let (server, url) = make_server(handler); - let request_body = create_request_body_with_content(content.to_vec()); + let request_body = create_request_body_with_content(IpcSharedMemory::from_bytes(content)); let request = RequestBuilder::new(None, url.clone(), Referrer::NoReferrer) .method(Method::POST) diff --git a/components/script/body.rs b/components/script/body.rs index 113f3ac7adb..cc7870a0845 100644 --- a/components/script/body.rs +++ b/components/script/body.rs @@ -7,7 +7,7 @@ use std::{ptr, slice, str}; use constellation_traits::BlobImpl; use encoding_rs::{Encoding, UTF_8}; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory}; use ipc_channel::router::ROUTER; use js::jsapi::{Heap, JS_ClearPendingException, JSObject, Value as JSValue}; use js::jsval::{JSVal, UndefinedValue}; @@ -73,7 +73,7 @@ struct TransmitBodyConnectHandler { task_source: SendableTaskSource, bytes_sender: Option<IpcSender<BodyChunkResponse>>, control_sender: IpcSender<BodyChunkRequest>, - in_memory: Option<Vec<u8>>, + in_memory: Option<IpcSharedMemory>, in_memory_done: bool, source: BodySource, } @@ -83,7 +83,7 @@ impl TransmitBodyConnectHandler { stream: Trusted<ReadableStream>, task_source: SendableTaskSource, control_sender: IpcSender<BodyChunkRequest>, - in_memory: Option<Vec<u8>>, + in_memory: Option<IpcSharedMemory>, source: BodySource, ) -> TransmitBodyConnectHandler { TransmitBodyConnectHandler { @@ -160,7 +160,7 @@ impl TransmitBodyConnectHandler { .bytes_sender .as_ref() .expect("No bytes sender to transmit source.") - .send(BodyChunkResponse::Chunk(bytes.clone())); + .send(BodyChunkResponse::Chunk(bytes)); return; } warn!("Re-directs for file-based Blobs not supported yet."); @@ -310,7 +310,11 @@ impl Callback for TransmitBodyPromiseHandler { // Step 5.1 and 5.2, transmit chunk. // Send the chunk to the body transmitter in net::http_loader::obtain_response. // TODO: queue a fetch task on request to process request body for request. - let _ = self.bytes_sender.send(BodyChunkResponse::Chunk(chunk)); + let _ = self + .bytes_sender + .send(BodyChunkResponse::Chunk(IpcSharedMemory::from_bytes( + &chunk, + ))); } } diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs index 52d0ca7e20c..9f1520bd085 100644 --- a/components/script/dom/attr.rs +++ b/components/script/dom/attr.rs @@ -8,7 +8,7 @@ use std::mem; use devtools_traits::AttrInfo; use dom_struct::dom_struct; -use html5ever::{LocalName, Namespace, Prefix, ns}; +use html5ever::{LocalName, Namespace, Prefix, local_name, ns}; use style::attr::{AttrIdentifier, AttrValue}; use style::values::GenericAtomIdent; use stylo_atoms::Atom; @@ -179,7 +179,7 @@ impl Attr { assert_eq!(Some(owner), self.owner().as_deref()); owner.will_mutate_attr(self); self.swap_value(&mut value); - if *self.namespace() == ns!() { + if is_relevant_attribute(self.namespace(), self.local_name()) { vtable_for(owner.upcast()).attribute_mutated( self, AttributeMutation::Set(Some(&value)), @@ -283,3 +283,9 @@ impl<'dom> AttrHelpersForLayout<'dom> for LayoutDom<'dom, Attr> { &self.unsafe_get().identifier.namespace.0 } } + +/// A helper function to check if attribute is relevant. +pub(crate) fn is_relevant_attribute(namespace: &Namespace, local_name: &LocalName) -> bool { + // <https://svgwg.org/svg2-draft/linking.html#XLinkHrefAttribute> + namespace == &ns!() || (namespace == &ns!(xlink) && local_name == &local_name!("href")) +} diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs index c9a49ba00c9..70638238123 100644 --- a/components/script/dom/bindings/structuredclone.rs +++ b/components/script/dom/bindings/structuredclone.rs @@ -44,7 +44,7 @@ use crate::dom::dompointreadonly::DOMPointReadOnly; use crate::dom::globalscope::GlobalScope; use crate::dom::messageport::MessagePort; use crate::dom::readablestream::ReadableStream; -use crate::dom::types::DOMException; +use crate::dom::types::{DOMException, TransformStream}; use crate::dom::writablestream::WritableStream; use crate::realms::{AlreadyInRealm, InRealm, enter_realm}; use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; @@ -65,6 +65,7 @@ pub(super) enum StructuredCloneTags { ReadableStream = 0xFFFF8006, DomException = 0xFFFF8007, WritableStream = 0xFFFF8008, + TransformStream = 0xFFFF8009, Max = 0xFFFFFFFF, } @@ -85,6 +86,7 @@ impl From<TransferrableInterface> for StructuredCloneTags { TransferrableInterface::MessagePort => StructuredCloneTags::MessagePort, TransferrableInterface::ReadableStream => StructuredCloneTags::ReadableStream, TransferrableInterface::WritableStream => StructuredCloneTags::WritableStream, + TransferrableInterface::TransformStream => StructuredCloneTags::TransformStream, } } } @@ -265,6 +267,7 @@ fn receiver_for_type( TransferrableInterface::MessagePort => receive_object::<MessagePort>, TransferrableInterface::ReadableStream => receive_object::<ReadableStream>, TransferrableInterface::WritableStream => receive_object::<WritableStream>, + TransferrableInterface::TransformStream => receive_object::<TransformStream>, } } @@ -390,6 +393,7 @@ fn transfer_for_type(val: TransferrableInterface) -> TransferOperation { TransferrableInterface::MessagePort => try_transfer::<MessagePort>, TransferrableInterface::ReadableStream => try_transfer::<ReadableStream>, TransferrableInterface::WritableStream => try_transfer::<WritableStream>, + TransferrableInterface::TransformStream => try_transfer::<TransformStream>, } } @@ -438,6 +442,7 @@ unsafe fn can_transfer_for_type( TransferrableInterface::MessagePort => can_transfer::<MessagePort>(obj, cx), TransferrableInterface::ReadableStream => can_transfer::<ReadableStream>(obj, cx), TransferrableInterface::WritableStream => can_transfer::<WritableStream>(obj, cx), + TransferrableInterface::TransformStream => can_transfer::<TransformStream>(obj, cx), } } diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs index 5722dc4f6ac..2e7c4cf8def 100644 --- a/components/script/dom/create.rs +++ b/components/script/dom/create.rs @@ -85,6 +85,7 @@ use crate::dom::htmlulistelement::HTMLUListElement; use crate::dom::htmlunknownelement::HTMLUnknownElement; use crate::dom::htmlvideoelement::HTMLVideoElement; use crate::dom::svgelement::SVGElement; +use crate::dom::svgimageelement::SVGImageElement; use crate::dom::svgsvgelement::SVGSVGElement; use crate::realms::{InRealm, enter_realm}; use crate::script_runtime::CanGc; @@ -114,6 +115,7 @@ fn create_svg_element( } match name.local { + local_name!("image") => make!(SVGImageElement), local_name!("svg") => make!(SVGSVGElement), _ => make!(SVGElement), } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 5c79dbc0a5b..cb120f0b174 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -63,8 +63,9 @@ use xml5ever::serialize::TraversalScope::{ ChildrenOnly as XmlChildrenOnly, IncludeNode as XmlIncludeNode, }; +use crate::conversions::Convert; use crate::dom::activation::Activatable; -use crate::dom::attr::{Attr, AttrHelpersForLayout}; +use crate::dom::attr::{Attr, AttrHelpersForLayout, is_relevant_attribute}; use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut, ref_filter_map}; use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; @@ -80,7 +81,9 @@ use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{ use crate::dom::bindings::codegen::Bindings::WindowBinding::{ ScrollBehavior, ScrollToOptions, WindowMethods, }; -use crate::dom::bindings::codegen::UnionTypes::{NodeOrString, TrustedScriptURLOrUSVString}; +use crate::dom::bindings::codegen::UnionTypes::{ + NodeOrString, TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString, TrustedScriptURLOrUSVString, +}; use crate::dom::bindings::conversions::DerivedFrom; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; @@ -152,6 +155,7 @@ use crate::dom::raredata::ElementRareData; use crate::dom::servoparser::ServoParser; use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot}; use crate::dom::text::Text; +use crate::dom::trustedhtml::TrustedHTML; use crate::dom::validation::Validatable; use crate::dom::validitystate::ValidationFlags; use crate::dom::virtualmethods::{VirtualMethods, vtable_for}; @@ -355,7 +359,7 @@ impl Element { if damage == NodeDamage::OtherNodeDamage { doc.note_node_with_dirty_descendants(self.upcast()); - restyle.damage = RestyleDamage::rebuild_and_reflow(); + restyle.damage = RestyleDamage::reconstruct(); } } @@ -1701,7 +1705,7 @@ impl Element { assert!(attr.GetOwnerElement().as_deref() == Some(self)); self.will_mutate_attr(attr); self.attrs.borrow_mut().push(Dom::from_ref(attr)); - if attr.namespace() == &ns!() { + if is_relevant_attribute(attr.namespace(), attr.local_name()) { vtable_for(self.upcast()).attribute_mutated(attr, AttributeMutation::Set(None), can_gc); } } @@ -1843,7 +1847,7 @@ impl Element { local_name: &LocalName, value: DOMString, ) -> AttrValue { - if *namespace == ns!() { + if is_relevant_attribute(namespace, local_name) { vtable_for(self.upcast()).parse_plain_attribute(local_name, value) } else { AttrValue::String(value.into()) @@ -1898,7 +1902,7 @@ impl Element { self.attrs.borrow_mut().remove(idx); attr.set_owner(None); - if attr.namespace() == &ns!() { + if is_relevant_attribute(attr.namespace(), attr.local_name()) { vtable_for(self.upcast()).attribute_mutated( &attr, AttributeMutation::Removed, @@ -1992,6 +1996,15 @@ impl Element { .unwrap_or_else(|_| TrustedScriptURLOrUSVString::USVString(USVString(value.to_owned()))) } + pub(crate) fn get_trusted_html_attribute(&self, local_name: &LocalName) -> TrustedHTMLOrString { + assert_eq!(*local_name, local_name.to_ascii_lowercase()); + let value = match self.get_attribute(&ns!(), local_name) { + Some(attr) => (&**attr.value()).into(), + None => "".into(), + }; + TrustedHTMLOrString::String(value) + } + pub(crate) fn get_string_attribute(&self, local_name: &LocalName) -> DOMString { match self.get_attribute(&ns!(), local_name) { Some(x) => x.Value(), @@ -2322,18 +2335,25 @@ impl Element { Ok(fragment) } + /// Step 4 of <https://html.spec.whatwg.org/multipage/#dom-element-insertadjacenthtml> pub(crate) fn fragment_parsing_context( owner_doc: &Document, element: Option<&Self>, can_gc: CanGc, ) -> DomRoot<Self> { + // If context is not an Element or all of the following are true: match element { Some(elem) + // context's node document is an HTML document; + // context's local name is "html"; and + // context's namespace is the HTML namespace, if elem.local_name() != &local_name!("html") || !elem.html_element_in_html_document() => { DomRoot::from_ref(elem) }, + // set context to the result of creating an element + // given this's node document, "body", and the HTML namespace. _ => DomRoot::upcast(HTMLBodyElement::new( local_name!("body"), None, @@ -2446,6 +2466,13 @@ impl Element { Dom::from_ref(&*ElementInternals::new(elem, can_gc)) })) } + + pub(crate) fn outer_html(&self, can_gc: CanGc) -> Fallible<DOMString> { + match self.GetOuterHTML(can_gc)? { + TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(str) => Ok(str), + TrustedHTMLOrNullIsEmptyString::TrustedHTML(_) => unreachable!(), + } + } } impl ElementMethods<crate::DomTypeHolder> for Element { @@ -2704,7 +2731,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element { attr.set_owner(Some(self)); self.attrs.borrow_mut()[position] = Dom::from_ref(attr); old_attr.set_owner(None); - if attr.namespace() == &ns!() { + if is_relevant_attribute(attr.namespace(), attr.local_name()) { vtable.attribute_mutated( attr, AttributeMutation::Set(Some(&old_attr.value())), @@ -3100,7 +3127,17 @@ impl ElementMethods<crate::DomTypeHolder> for Element { } /// <https://html.spec.whatwg.org/multipage/#dom-element-sethtmlunsafe> - fn SetHTMLUnsafe(&self, html: DOMString, can_gc: CanGc) { + fn SetHTMLUnsafe(&self, html: TrustedHTMLOrString, can_gc: CanGc) -> ErrorResult { + // Step 1. Let compliantHTML be the result of invoking the + // Get Trusted Type compliant string algorithm with TrustedHTML, + // this's relevant global object, html, "Element setHTMLUnsafe", and "script". + let html = DOMString::from(TrustedHTML::get_trusted_script_compliant_string( + &self.owner_global(), + html, + "Element", + "setHTMLUnsafe", + can_gc, + )?); // Step 2. Let target be this's template contents if this is a template element; otherwise this. let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() { DomRoot::upcast(template.Content(can_gc)) @@ -3110,6 +3147,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element { // Step 3. Unsafely set HTML given target, this, and compliantHTML Node::unsafely_set_html(&target, self, html, can_gc); + Ok(()) } /// <https://html.spec.whatwg.org/multipage/#dom-element-gethtml> @@ -3125,7 +3163,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element { } /// <https://html.spec.whatwg.org/multipage/#dom-element-innerhtml> - fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<DOMString> { + fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> { let qname = QualName::new( self.prefix().clone(), self.namespace().clone(), @@ -3142,16 +3180,28 @@ impl ElementMethods<crate::DomTypeHolder> for Element { .xml_serialize(XmlChildrenOnly(Some(qname))) }; - Ok(result) + Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result)) } /// <https://html.spec.whatwg.org/multipage/#dom-element-innerhtml> - fn SetInnerHTML(&self, value: DOMString, can_gc: CanGc) -> ErrorResult { - // Step 2. + fn SetInnerHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult { + // Step 1: Let compliantString be the result of invoking the + // Get Trusted Type compliant string algorithm with TrustedHTML, + // this's relevant global object, the given value, "Element innerHTML", and "script". + let value = DOMString::from(TrustedHTML::get_trusted_script_compliant_string( + &self.owner_global(), + value.convert(), + "Element", + "innerHTML", + can_gc, + )?); // https://github.com/w3c/DOM-Parsing/issues/1 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() { + // Step 4: If context is a template element, then set context to + // the template element's template contents (a DocumentFragment). DomRoot::upcast(template.Content(can_gc)) } else { + // Step 2: Let context be this. DomRoot::from_ref(self.upcast()) }; @@ -3168,15 +3218,17 @@ impl ElementMethods<crate::DomTypeHolder> for Element { return Ok(()); } - // Step 1. + // Step 3: Let fragment be the result of invoking the fragment parsing algorithm steps + // with context and compliantString. let frag = self.parse_fragment(value, can_gc)?; + // Step 5: Replace all with fragment within context. Node::replace_all(Some(frag.upcast()), &target, can_gc); Ok(()) } /// <https://html.spec.whatwg.org/multipage/#dom-element-outerhtml> - fn GetOuterHTML(&self, can_gc: CanGc) -> Fallible<DOMString> { + fn GetOuterHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> { // FIXME: This should use the fragment serialization algorithm, which takes // care of distinguishing between html/xml documents let result = if self.owner_document().is_html_document() { @@ -3186,27 +3238,39 @@ impl ElementMethods<crate::DomTypeHolder> for Element { self.upcast::<Node>().xml_serialize(XmlIncludeNode) }; - Ok(result) + Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result)) } /// <https://html.spec.whatwg.org/multipage/#dom-element-outerhtml> - fn SetOuterHTML(&self, value: DOMString, can_gc: CanGc) -> ErrorResult { + fn SetOuterHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult { + // Step 1: Let compliantString be the result of invoking the + // Get Trusted Type compliant string algorithm with TrustedHTML, + // this's relevant global object, the given value, "Element outerHTML", and "script". + let value = DOMString::from(TrustedHTML::get_trusted_script_compliant_string( + &self.owner_global(), + value.convert(), + "Element", + "outerHTML", + can_gc, + )?); let context_document = self.owner_document(); let context_node = self.upcast::<Node>(); - // Step 1. + // Step 2: Let parent be this's parent. let context_parent = match context_node.GetParentNode() { None => { - // Step 2. + // Step 3: If parent is null, return. There would be no way to + // obtain a reference to the nodes created even if the remaining steps were run. return Ok(()); }, Some(parent) => parent, }; let parent = match context_parent.type_id() { - // Step 3. + // Step 4: If parent is a Document, throw a "NoModificationAllowedError" DOMException. NodeTypeId::Document(_) => return Err(Error::NoModificationAllowed), - // Step 4. + // Step 5: If parent is a DocumentFragment, set parent to the result of + // creating an element given this's node document, "body", and the HTML namespace. NodeTypeId::DocumentFragment(_) => { let body_elem = Element::create( QualName::new(None, ns!(html), local_name!("body")), @@ -3222,9 +3286,10 @@ impl ElementMethods<crate::DomTypeHolder> for Element { _ => context_node.GetParentElement().unwrap(), }; - // Step 5. + // Step 6: Let fragment be the result of invoking the + // fragment parsing algorithm steps given parent and compliantString. let frag = parent.parse_fragment(value, can_gc)?; - // Step 6. + // Step 7: Replace this with fragment within this's parent. context_parent.ReplaceChild(frag.upcast(), context_node, can_gc)?; Ok(()) } @@ -3391,38 +3456,57 @@ impl ElementMethods<crate::DomTypeHolder> for Element { fn InsertAdjacentHTML( &self, position: DOMString, - text: DOMString, + text: TrustedHTMLOrString, can_gc: CanGc, ) -> ErrorResult { - // Step 1. + // Step 1: Let compliantString be the result of invoking the + // Get Trusted Type compliant string algorithm with TrustedHTML, + // this's relevant global object, string, "Element insertAdjacentHTML", and "script". + let text = DOMString::from(TrustedHTML::get_trusted_script_compliant_string( + &self.owner_global(), + text, + "Element", + "insertAdjacentHTML", + can_gc, + )?); let position = position.parse::<AdjacentPosition>()?; + // Step 2: Let context be null. + // Step 3: Use the first matching item from this list: let context = match position { + // If position is an ASCII case-insensitive match for the string "beforebegin" + // If position is an ASCII case-insensitive match for the string "afterend" AdjacentPosition::BeforeBegin | AdjacentPosition::AfterEnd => { match self.upcast::<Node>().GetParentNode() { + // Step 3.2: If context is null or a Document, throw a "NoModificationAllowedError" DOMException. Some(ref node) if node.is::<Document>() => { return Err(Error::NoModificationAllowed); }, None => return Err(Error::NoModificationAllowed), + // Step 3.1: Set context to this's parent. Some(node) => node, } }, + // If position is an ASCII case-insensitive match for the string "afterbegin" + // If position is an ASCII case-insensitive match for the string "beforeend" AdjacentPosition::AfterBegin | AdjacentPosition::BeforeEnd => { + // Set context to this. DomRoot::from_ref(self.upcast::<Node>()) }, }; - // Step 2. + // Step 4. let context = Element::fragment_parsing_context( &context.owner_doc(), context.downcast::<Element>(), can_gc, ); - // Step 3. + // Step 5: Let fragment be the result of invoking the + // fragment parsing algorithm steps with context and compliantString. let fragment = context.parse_fragment(text, can_gc)?; - // Step 4. + // Step 6. self.insert_adjacent(position, fragment.upcast(), can_gc) .map(|_| ()) } diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 902d4622db9..55db2e4d248 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -3562,10 +3562,6 @@ impl GlobalScopeHelpers<crate::DomTypeHolder> for GlobalScope { GlobalScope::from_reflector(reflector, realm) } - unsafe fn from_object_maybe_wrapped(obj: *mut JSObject, cx: *mut JSContext) -> DomRoot<Self> { - GlobalScope::from_object_maybe_wrapped(obj, cx) - } - fn origin(&self) -> &MutableOrigin { GlobalScope::origin(self) } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 0fbff86e44a..18116eee8ae 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -27,6 +27,8 @@ use crate::dom::attr::Attr; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods; +use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString; +use crate::dom::bindings::error::Fallible; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::DomGlobal; use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom}; @@ -40,6 +42,7 @@ use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; use crate::dom::node::{Node, NodeDamage, NodeTraits, UnbindContext}; +use crate::dom::trustedhtml::TrustedHTML; use crate::dom::virtualmethods::VirtualMethods; use crate::dom::windowproxy::WindowProxy; use crate::script_runtime::CanGc; @@ -595,10 +598,29 @@ impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement { make_url_setter!(SetSrc, "src"); // https://html.spec.whatwg.org/multipage/#dom-iframe-srcdoc - make_getter!(Srcdoc, "srcdoc"); + fn Srcdoc(&self) -> TrustedHTMLOrString { + let element = self.upcast::<Element>(); + element.get_trusted_html_attribute(&local_name!("srcdoc")) + } // https://html.spec.whatwg.org/multipage/#dom-iframe-srcdoc - make_setter!(SetSrcdoc, "srcdoc"); + fn SetSrcdoc(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> Fallible<()> { + // Step 1: Let compliantString be the result of invoking the + // Get Trusted Type compliant string algorithm with TrustedHTML, + // this's relevant global object, the given value, "HTMLIFrameElement srcdoc", and "script". + let element = self.upcast::<Element>(); + let local_name = &local_name!("srcdoc"); + let value = TrustedHTML::get_trusted_script_compliant_string( + &element.owner_global(), + value, + "HTMLIFrameElement", + local_name, + can_gc, + )?; + // Step 2: Set an attribute value given this, srcdoc's local name, and compliantString. + element.set_attribute(local_name, AttrValue::String(value), can_gc); + Ok(()) + } // https://html.spec.whatwg.org/multipage/#dom-iframe-sandbox fn Sandbox(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> { diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 1622cf57b79..91a4e1b1359 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -547,6 +547,7 @@ pub(crate) mod submitevent; pub(crate) mod subtlecrypto; pub(crate) mod svgelement; pub(crate) mod svggraphicselement; +pub(crate) mod svgimageelement; pub(crate) mod svgsvgelement; pub(crate) mod testbinding; pub(crate) mod testbindingiterable; diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index ca785773b48..5f08abce354 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -4207,6 +4207,9 @@ impl From<ElementTypeIdWrapper> for LayoutElementType { LayoutElementType::HTMLTextAreaElement }, ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement( + SVGGraphicsElementTypeId::SVGImageElement, + )) => LayoutElementType::SVGImageElement, + ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement( SVGGraphicsElementTypeId::SVGSVGElement, )) => LayoutElementType::SVGSVGElement, _ => LayoutElementType::Element, diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs index 80b62f161bc..0efffbe6fe2 100644 --- a/components/script/dom/promise.rs +++ b/components/script/dom/promise.rs @@ -29,7 +29,6 @@ use js::rust::wrappers::{ ResolvePromise, SetAnyPromiseIsHandled, SetPromiseUserInputEventHandlingState, }; use js::rust::{HandleObject, HandleValue, MutableHandleObject, Runtime}; -use script_bindings::interfaces::PromiseHelpers; use crate::dom::bindings::conversions::root_from_object; use crate::dom::bindings::error::{Error, ErrorToJsval}; @@ -388,16 +387,6 @@ fn create_native_handler_function( } } -impl PromiseHelpers<crate::DomTypeHolder> for Promise { - fn new_resolved( - global: &GlobalScope, - cx: SafeJSContext, - value: impl ToJSValConvertible, - ) -> Rc<Promise> { - Promise::new_resolved(global, cx, value, CanGc::note()) - } -} - impl FromJSValConvertibleRc for Promise { #[allow(unsafe_code)] unsafe fn from_jsval( @@ -407,16 +396,12 @@ impl FromJSValConvertibleRc for Promise { if value.get().is_null() { return Ok(ConversionResult::Failure("null not allowed".into())); } - if !value.get().is_object() { - return Ok(ConversionResult::Failure("not an object".into())); - } - rooted!(in(cx) let obj = value.get().to_object()); let cx = SafeJSContext::from_ptr(cx); let in_realm_proof = AlreadyInRealm::assert_for_cx(cx); let global_scope = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)); - let promise = Promise::new_resolved(&global_scope, cx, *obj, CanGc::note()); + let promise = Promise::new_resolved(&global_scope, cx, value, CanGc::note()); Ok(ConversionResult::Success(promise)) } } diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 4982bfa32e3..d631a01e1e7 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -11,6 +11,7 @@ use std::rc::Rc; use base::id::{MessagePortId, MessagePortIndex}; use constellation_traits::MessagePortImpl; use dom_struct::dom_struct; +use ipc_channel::ipc::IpcSharedMemory; use js::conversions::ToJSValConvertible; use js::jsapi::{Heap, JSObject}; use js::jsval::{JSVal, NullValue, ObjectValue, UndefinedValue}; @@ -1131,12 +1132,14 @@ impl ReadableStream { /// Return bytes for synchronous use, if the stream has all data in memory. /// Useful for native source integration only. - pub(crate) fn get_in_memory_bytes(&self) -> Option<Vec<u8>> { + pub(crate) fn get_in_memory_bytes(&self) -> Option<IpcSharedMemory> { match self.controller.borrow().as_ref() { Some(ControllerType::Default(controller)) => controller .get() .expect("Stream should have controller.") - .get_in_memory_bytes(), + .get_in_memory_bytes() + .as_deref() + .map(IpcSharedMemory::from_bytes), _ => { unreachable!("Getting in-memory bytes for a stream with a non-default controller") }, diff --git a/components/script/dom/svgelement.rs b/components/script/dom/svgelement.rs index 9c8b990826d..0f36d942f3e 100644 --- a/components/script/dom/svgelement.rs +++ b/components/script/dom/svgelement.rs @@ -82,6 +82,9 @@ impl SVGElementMethods<crate::DomTypeHolder> for SVGElement { }) } + // <https://html.spec.whatwg.org/multipage/#globaleventhandlers> + global_event_handlers!(); + // FIXME: The nonce should be stored in an internal slot instead of an // attribute (https://html.spec.whatwg.org/multipage/#cryptographicnonce) // https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce diff --git a/components/script/dom/svgimageelement.rs b/components/script/dom/svgimageelement.rs new file mode 100644 index 00000000000..17a5a9149d8 --- /dev/null +++ b/components/script/dom/svgimageelement.rs @@ -0,0 +1,96 @@ +/* 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 dom_struct::dom_struct; +use html5ever::{LocalName, Prefix, local_name, ns}; +use js::rust::HandleObject; +use style::attr::AttrValue; + +use crate::dom::attr::Attr; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::document::Document; +use crate::dom::element::AttributeMutation; +use crate::dom::node::{Node, NodeTraits}; +use crate::dom::svggraphicselement::SVGGraphicsElement; +use crate::dom::virtualmethods::VirtualMethods; +use crate::script_runtime::CanGc; + +/// <https://svgwg.org/svg2-draft/embedded.html#Placement> +const DEFAULT_WIDTH: u32 = 300; +const DEFAULT_HEIGHT: u32 = 150; + +#[dom_struct] +pub(crate) struct SVGImageElement { + svggraphicselement: SVGGraphicsElement, +} + +impl SVGImageElement { + fn new_inherited( + local_name: LocalName, + prefix: Option<Prefix>, + document: &Document, + ) -> SVGImageElement { + SVGImageElement { + svggraphicselement: SVGGraphicsElement::new_inherited(local_name, prefix, document), + } + } + + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + pub(crate) fn new( + local_name: LocalName, + prefix: Option<Prefix>, + document: &Document, + proto: Option<HandleObject>, + can_gc: CanGc, + ) -> DomRoot<SVGImageElement> { + Node::reflect_node_with_proto( + Box::new(SVGImageElement::new_inherited(local_name, prefix, document)), + document, + proto, + can_gc, + ) + } + + /// <https://svgwg.org/svg2-draft/linking.html#processingURL> + fn fetch_image_resource(&self) { + // TODO: Process and fetch the image resource (as HTMLImageElement). + // Reject any resource fetching request immediately. + self.owner_global() + .task_manager() + .dom_manipulation_task_source() + .queue_simple_event(self.upcast(), atom!("error")); + } +} + +impl VirtualMethods for SVGImageElement { + fn super_type(&self) -> Option<&dyn VirtualMethods> { + Some(self.upcast::<SVGGraphicsElement>() as &dyn VirtualMethods) + } + + fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) { + self.super_type() + .unwrap() + .attribute_mutated(attr, mutation, can_gc); + if attr.local_name() == &local_name!("href") && + matches!(attr.namespace(), &ns!() | &ns!(xlink)) + { + if let AttributeMutation::Set(_) = mutation { + self.fetch_image_resource(); + } + } + } + + fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue { + match *name { + local_name!("width") => AttrValue::from_u32(value.into(), DEFAULT_WIDTH), + local_name!("height") => AttrValue::from_u32(value.into(), DEFAULT_HEIGHT), + _ => self + .super_type() + .unwrap() + .parse_plain_attribute(name, value), + } + } +} diff --git a/components/script/dom/transformstream.rs b/components/script/dom/transformstream.rs index 023fe7ac483..0251498980d 100644 --- a/components/script/dom/transformstream.rs +++ b/components/script/dom/transformstream.rs @@ -3,9 +3,12 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::Cell; +use std::collections::HashMap; use std::ptr::{self}; use std::rc::Rc; +use base::id::{MessagePortId, MessagePortIndex}; +use constellation_traits::MessagePortImpl; use dom_struct::dom_struct; use js::jsapi::{Heap, IsPromiseObject, JSObject}; use js::jsval::{JSVal, ObjectValue, UndefinedValue}; @@ -14,6 +17,9 @@ use script_bindings::callback::ExceptionHandling; use script_bindings::realms::InRealm; use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize; +use super::bindings::structuredclone::StructuredData; +use super::bindings::transferable::Transferable; +use super::messageport::MessagePort; use super::promisenativehandler::Callback; use super::types::{TransformStreamDefaultController, WritableStream}; use crate::dom::bindings::cell::DomRefCell; @@ -997,3 +1003,103 @@ impl TransformStreamMethods<crate::DomTypeHolder> for TransformStream { self.writable.get().expect("writable stream is not set") } } + +/// <https://streams.spec.whatwg.org/#ts-transfer> +impl Transferable for TransformStream { + type Index = MessagePortIndex; + type Data = MessagePortImpl; + + fn transfer(&self) -> Result<(MessagePortId, MessagePortImpl), ()> { + let global = self.global(); + let realm = enter_realm(&*global); + let comp = InRealm::Entered(&realm); + let cx = GlobalScope::get_cx(); + let can_gc = CanGc::note(); + + // Let readable be value.[[readable]]. + let readable = self.get_readable(); + + // Let writable be value.[[writable]]. + let writable = self.get_writable(); + + // If ! IsReadableStreamLocked(readable) is true, throw a "DataCloneError" DOMException. + if readable.is_locked() { + return Err(()); + } + + // If ! IsWritableStreamLocked(writable) is true, throw a "DataCloneError" DOMException. + if writable.is_locked() { + return Err(()); + } + + // Create the shared port pair + let port_1 = MessagePort::new(&global, can_gc); + global.track_message_port(&port_1, None); + let port_2 = MessagePort::new(&global, can_gc); + global.track_message_port(&port_2, None); + global.entangle_ports(*port_1.message_port_id(), *port_2.message_port_id()); + + // Create a proxy WritableStream wired to port_1 + let proxy_writable = WritableStream::new_with_proto(&global, None, can_gc); + proxy_writable.setup_cross_realm_transform_writable(cx, &port_1, can_gc); + + // Pipe readable into the proxy writable (→ port_1) + let pipe1 = readable.pipe_to( + cx, + &global, + &proxy_writable, + false, + false, + false, + comp, + can_gc, + ); + pipe1.set_promise_is_handled(); + + // Create a proxy ReadableStream wired to port_1 + let proxy_readable = ReadableStream::new_with_proto(&global, None, can_gc); + proxy_readable.setup_cross_realm_transform_readable(cx, &port_1, can_gc); + + // Pipe proxy readable (← port_1) into writable + let pipe2 = + proxy_readable.pipe_to(cx, &global, &writable, false, false, false, comp, can_gc); + pipe2.set_promise_is_handled(); + + // Set dataHolder.[[readable]] to ! StructuredSerializeWithTransfer(readable, « readable »). + // Set dataHolder.[[writable]] to ! StructuredSerializeWithTransfer(writable, « writable »). + port_2.transfer() + } + + fn transfer_receive( + owner: &GlobalScope, + id: MessagePortId, + port_impl: MessagePortImpl, + ) -> Result<DomRoot<Self>, ()> { + let can_gc = CanGc::note(); + + // Let readableRecord be ! StructuredDeserializeWithTransfer(dataHolder.[[readable]], the current Realm). + // Set value.[[readable]] to readableRecord.[[Deserialized]]. + let readable = ReadableStream::transfer_receive(owner, id, port_impl.clone())?; + + // Let writableRecord be ! StructuredDeserializeWithTransfer(dataHolder.[[writable]], the current Realm). + let writable = WritableStream::transfer_receive(owner, id, port_impl)?; + + // Set value.[[readable]] to readableRecord.[[Deserialized]]. + // Set value.[[writable]] to writableRecord.[[Deserialized]]. + // Set value.[[backpressure]], value.[[backpressureChangePromise]], and value.[[controller]] to undefined. + let stream = TransformStream::new_with_proto(owner, None, can_gc); + stream.readable.set(Some(&readable)); + stream.writable.set(Some(&writable)); + + Ok(stream) + } + + fn serialized_storage<'a>( + data: StructuredData<'a, '_>, + ) -> &'a mut Option<HashMap<MessagePortId, Self::Data>> { + match data { + StructuredData::Reader(r) => &mut r.port_impls, + StructuredData::Writer(w) => &mut w.ports, + } + } +} diff --git a/components/script/dom/trustedhtml.rs b/components/script/dom/trustedhtml.rs index 8508f28c150..d1ca3cd5e71 100644 --- a/components/script/dom/trustedhtml.rs +++ b/components/script/dom/trustedhtml.rs @@ -6,8 +6,11 @@ use std::fmt; use dom_struct::dom_struct; +use crate::conversions::Convert; use crate::dom::bindings::codegen::Bindings::TrustedHTMLBinding::TrustedHTMLMethods; -use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString; +use crate::dom::bindings::codegen::UnionTypes::{ + TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString, +}; use crate::dom::bindings::error::Fallible; use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; use crate::dom::bindings::root::DomRoot; @@ -80,3 +83,16 @@ impl TrustedHTMLMethods<crate::DomTypeHolder> for TrustedHTML { DOMString::from(&*self.data) } } + +impl Convert<TrustedHTMLOrString> for TrustedHTMLOrNullIsEmptyString { + fn convert(self) -> TrustedHTMLOrString { + match self { + TrustedHTMLOrNullIsEmptyString::TrustedHTML(trusted_html) => { + TrustedHTMLOrString::TrustedHTML(trusted_html) + }, + TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(str) => { + TrustedHTMLOrString::String(str) + }, + } + } +} diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs index 57ecba7b172..1d992b1f301 100644 --- a/components/script/dom/virtualmethods.rs +++ b/components/script/dom/virtualmethods.rs @@ -61,6 +61,7 @@ use crate::dom::htmlvideoelement::HTMLVideoElement; use crate::dom::node::{BindContext, ChildrenMutation, CloneChildrenFlag, Node, UnbindContext}; use crate::dom::shadowroot::ShadowRoot; use crate::dom::svgelement::SVGElement; +use crate::dom::svgimageelement::SVGImageElement; use crate::dom::svgsvgelement::SVGSVGElement; /// Trait to allow DOM nodes to opt-in to overriding (or adding to) common @@ -299,6 +300,9 @@ pub(crate) fn vtable_for(node: &Node) -> &dyn VirtualMethods { node.downcast::<HTMLTitleElement>().unwrap() as &dyn VirtualMethods }, NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement( + SVGGraphicsElementTypeId::SVGImageElement, + ))) => node.downcast::<SVGImageElement>().unwrap() as &dyn VirtualMethods, + NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement( SVGGraphicsElementTypeId::SVGSVGElement, ))) => node.downcast::<SVGSVGElement>().unwrap() as &dyn VirtualMethods, NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGElement)) => { diff --git a/components/script/messaging.rs b/components/script/messaging.rs index e0ea9e30af2..08d6fc841cf 100644 --- a/components/script/messaging.rs +++ b/components/script/messaging.rs @@ -91,6 +91,7 @@ impl MixedMessage { #[cfg(feature = "webgpu")] ScriptThreadMessage::SetWebGPUPort(..) => None, ScriptThreadMessage::SetScrollStates(id, ..) => Some(*id), + ScriptThreadMessage::EvaluateJavaScript(id, _, _) => Some(*id), }, MixedMessage::FromScript(inner_msg) => match inner_msg { MainThreadScriptMsg::Common(CommonScriptMsg::Task(_, _, pipeline_id, _)) => { diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index d6ab18be49b..4815e6feae1 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -50,9 +50,9 @@ use devtools_traits::{ }; use embedder_traits::user_content_manager::UserContentManager; use embedder_traits::{ - CompositorHitTestResult, EmbedderMsg, FocusSequenceNumber, InputEvent, MediaSessionActionType, - MouseButton, MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, - WebDriverScriptCommand, + CompositorHitTestResult, EmbedderMsg, FocusSequenceNumber, InputEvent, + JavaScriptEvaluationError, JavaScriptEvaluationId, MediaSessionActionType, MouseButton, + MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverScriptCommand, }; use euclid::Point2D; use euclid::default::Rect; @@ -156,6 +156,7 @@ use crate::script_runtime::{ }; use crate::task_queue::TaskQueue; use crate::task_source::{SendableTaskSource, TaskSourceName}; +use crate::webdriver_handlers::jsval_to_webdriver; use crate::{devtools, webdriver_handlers}; thread_local!(static SCRIPT_THREAD_ROOT: Cell<Option<*const ScriptThread>> = const { Cell::new(None) }); @@ -1878,6 +1879,9 @@ impl ScriptThread { ScriptThreadMessage::SetScrollStates(pipeline_id, scroll_states) => { self.handle_set_scroll_states(pipeline_id, scroll_states) }, + ScriptThreadMessage::EvaluateJavaScript(pipeline_id, evaluation_id, script) => { + self.handle_evaluate_javascript(pipeline_id, evaluation_id, script, can_gc); + }, } } @@ -3815,6 +3819,53 @@ impl ScriptThread { ) } } + + fn handle_evaluate_javascript( + &self, + pipeline_id: PipelineId, + evaluation_id: JavaScriptEvaluationId, + script: String, + can_gc: CanGc, + ) { + let Some(window) = self.documents.borrow().find_window(pipeline_id) else { + let _ = self.senders.pipeline_to_constellation_sender.send(( + pipeline_id, + ScriptToConstellationMessage::FinishJavaScriptEvaluation( + evaluation_id, + Err(JavaScriptEvaluationError::WebViewNotReady), + ), + )); + return; + }; + + let global_scope = window.as_global_scope(); + let realm = enter_realm(global_scope); + let context = window.get_cx(); + + rooted!(in(*context) let mut return_value = UndefinedValue()); + global_scope.evaluate_js_on_global_with_result( + &script, + return_value.handle_mut(), + ScriptFetchOptions::default_classic_script(global_scope), + global_scope.api_base_url(), + can_gc, + ); + let result = match jsval_to_webdriver( + context, + global_scope, + return_value.handle(), + (&realm).into(), + can_gc, + ) { + Ok(ref value) => Ok(value.into()), + Err(_) => Err(JavaScriptEvaluationError::SerializationError), + }; + + let _ = self.senders.pipeline_to_constellation_sender.send(( + pipeline_id, + ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, result), + )); + } } impl Drop for ScriptThread { diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 330ae4f0788..6b4264d945e 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -902,7 +902,7 @@ pub(crate) fn handle_get_page_source( .find_document(pipeline) .ok_or(ErrorStatus::UnknownError) .and_then(|document| match document.GetDocumentElement() { - Some(element) => match element.GetOuterHTML(can_gc) { + Some(element) => match element.outer_html(can_gc) { Ok(source) => Ok(source.to_string()), Err(_) => { match XMLSerializer::new(document.window(), None, can_gc) diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf index 2a9874a386f..92871bc54aa 100644 --- a/components/script_bindings/codegen/Bindings.conf +++ b/components/script_bindings/codegen/Bindings.conf @@ -371,7 +371,7 @@ DOMInterfaces = { }, 'HTMLIFrameElement': { - 'canGc': ['Sandbox'], + 'canGc': ['Sandbox', 'SetSrcdoc'], }, 'HTMLImageElement': { @@ -538,7 +538,7 @@ DOMInterfaces = { 'Promise': { 'spiderMonkeyInterface': True, - 'additionalTraits': ["crate::interfaces::PromiseHelpers<Self>", "js::conversions::FromJSValConvertibleRc"] + 'additionalTraits': ["js::conversions::FromJSValConvertibleRc"] }, 'Range': { diff --git a/components/script_bindings/codegen/CodegenRust.py b/components/script_bindings/codegen/CodegenRust.py index 48f024be70f..458aa7508b0 100644 --- a/components/script_bindings/codegen/CodegenRust.py +++ b/components/script_bindings/codegen/CodegenRust.py @@ -742,6 +742,19 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, "}") return templateBody + # A helper function for types that implement FromJSValConvertible trait + def fromJSValTemplate(config, errorHandler, exceptionCode): + return f"""match FromJSValConvertible::from_jsval(*cx, ${{val}}, {config}) {{ + Ok(ConversionResult::Success(value)) => value, + Ok(ConversionResult::Failure(error)) => {{ + {errorHandler} + }} + _ => {{ + {exceptionCode} + }}, +}} +""" + assert not (isEnforceRange and isClamp) # These are mutually exclusive if type.isSequence() or type.isRecord(): @@ -755,13 +768,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if type.nullable(): declType = CGWrapper(declType, pre="Option<", post=" >") - templateBody = (f"match FromJSValConvertible::from_jsval(*cx, ${{val}}, {config}) {{\n" - " Ok(ConversionResult::Success(value)) => value,\n" - " Ok(ConversionResult::Failure(error)) => {\n" - f"{indent(failOrPropagate, 8)}\n" - " }\n" - f" _ => {{ {exceptionCode} }},\n" - "}") + templateBody = fromJSValTemplate(config, failOrPropagate, exceptionCode) return handleOptional(templateBody, declType, handleDefault("None")) @@ -770,13 +777,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if type.nullable(): declType = CGWrapper(declType, pre="Option<", post=" >") - templateBody = ("match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {\n" - " Ok(ConversionResult::Success(value)) => value,\n" - " Ok(ConversionResult::Failure(error)) => {\n" - f"{indent(failOrPropagate, 8)}\n" - " }\n" - f" _ => {{ {exceptionCode} }},\n" - "}") + templateBody = fromJSValTemplate("()", failOrPropagate, exceptionCode) dictionaries = [ memberType @@ -836,21 +837,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, # once again be providing a Promise to signal completion of an # operation, which would then not be exposed to anyone other than # our own implementation code. - templateBody = fill( - """ - { // Scope for our JSAutoRealm. - - rooted!(in(*cx) let globalObj = CurrentGlobalOrNull(*cx)); - let promiseGlobal = D::GlobalScope::from_object_maybe_wrapped(globalObj.handle().get(), *cx); - - rooted!(in(*cx) let mut valueToResolve = $${val}.get()); - if !JS_WrapValue(*cx, valueToResolve.handle_mut()) { - $*{exceptionCode} - } - D::Promise::new_resolved(&promiseGlobal, cx, valueToResolve.handle()) - } - """, - exceptionCode=exceptionCode) + templateBody = fromJSValTemplate("()", failOrPropagate, exceptionCode) if isArgument: declType = CGGeneric("&D::Promise") @@ -960,14 +947,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if type.isDOMString(): nullBehavior = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs) - conversionCode = ( - f"match FromJSValConvertible::from_jsval(*cx, ${{val}}, {nullBehavior}) {{\n" - " Ok(ConversionResult::Success(strval)) => strval,\n" - " Ok(ConversionResult::Failure(error)) => {\n" - f"{indent(failOrPropagate, 8)}\n" - " }\n" - f" _ => {{ {exceptionCode} }},\n" - "}") + conversionCode = fromJSValTemplate(nullBehavior, failOrPropagate, exceptionCode) if defaultValue is None: default = None @@ -989,14 +969,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if type.isUSVString(): assert not isEnforceRange and not isClamp - conversionCode = ( - "match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {\n" - " Ok(ConversionResult::Success(strval)) => strval,\n" - " Ok(ConversionResult::Failure(error)) => {\n" - f"{indent(failOrPropagate, 8)}\n" - " }\n" - f" _ => {{ {exceptionCode} }},\n" - "}") + conversionCode = fromJSValTemplate("()", failOrPropagate, exceptionCode) if defaultValue is None: default = None @@ -1018,14 +991,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if type.isByteString(): assert not isEnforceRange and not isClamp - conversionCode = ( - "match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {\n" - " Ok(ConversionResult::Success(strval)) => strval,\n" - " Ok(ConversionResult::Failure(error)) => {\n" - f"{indent(failOrPropagate, 8)}\n" - " }\n" - f" _ => {{ {exceptionCode} }},\n" - "}") + conversionCode = fromJSValTemplate("()", failOrPropagate, exceptionCode) if defaultValue is None: default = None @@ -1056,12 +1022,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, else: handleInvalidEnumValueCode = "return true;" - template = ( - "match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {" - f" Err(_) => {{ {exceptionCode} }},\n" - " Ok(ConversionResult::Success(v)) => v,\n" - f" Ok(ConversionResult::Failure(error)) => {{ {handleInvalidEnumValueCode} }},\n" - "}") + template = fromJSValTemplate("()", handleInvalidEnumValueCode, exceptionCode) if defaultValue is not None: assert defaultValue.type.tag() == IDLType.Tags.domstring @@ -1192,14 +1153,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if type_needs_tracing(type): declType = CGTemplatedType("RootedTraceableBox", declType) - template = ( - "match FromJSValConvertible::from_jsval(*cx, ${val}, ()) {\n" - " Ok(ConversionResult::Success(dictionary)) => dictionary,\n" - " Ok(ConversionResult::Failure(error)) => {\n" - f"{indent(failOrPropagate, 8)}\n" - " }\n" - f" _ => {{ {exceptionCode} }},\n" - "}") + template = fromJSValTemplate("()", failOrPropagate, exceptionCode) return handleOptional(template, declType, handleDefault(empty)) @@ -1220,14 +1174,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if type.nullable(): declType = CGWrapper(declType, pre="Option<", post=">") - template = ( - f"match FromJSValConvertible::from_jsval(*cx, ${{val}}, {conversionBehavior}) {{\n" - " Ok(ConversionResult::Success(v)) => v,\n" - " Ok(ConversionResult::Failure(error)) => {\n" - f"{indent(failOrPropagate, 8)}\n" - " }\n" - f" _ => {{ {exceptionCode} }}\n" - "}") + template = fromJSValTemplate(conversionBehavior, failOrPropagate, exceptionCode) if defaultValue is not None: if isinstance(defaultValue, IDLNullValue): diff --git a/components/script_bindings/import.rs b/components/script_bindings/import.rs index 16cc92f07bf..25bd6c38669 100644 --- a/components/script_bindings/import.rs +++ b/components/script_bindings/import.rs @@ -11,12 +11,12 @@ pub(crate) mod base { }; pub(crate) use js::error::throw_type_error; pub(crate) use js::jsapi::{ - CurrentGlobalOrNull, HandleValue as RawHandleValue, HandleValueArray, Heap, IsCallable, - JS_NewObject, JSContext, JSObject, + HandleValue as RawHandleValue, HandleValueArray, Heap, IsCallable, JS_NewObject, JSContext, + JSObject, }; pub(crate) use js::jsval::{JSVal, NullValue, ObjectOrNullValue, ObjectValue, UndefinedValue}; pub(crate) use js::panic::maybe_resume_unwind; - pub(crate) use js::rust::wrappers::{Call, JS_WrapValue}; + pub(crate) use js::rust::wrappers::Call; pub(crate) use js::rust::{HandleObject, HandleValue, MutableHandleObject, MutableHandleValue}; pub(crate) use crate::callback::{ diff --git a/components/script_bindings/interfaces.rs b/components/script_bindings/interfaces.rs index b289737143e..58917283170 100644 --- a/components/script_bindings/interfaces.rs +++ b/components/script_bindings/interfaces.rs @@ -3,10 +3,8 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::RefCell; -use std::rc::Rc; use std::thread::LocalKey; -use js::conversions::ToJSValConvertible; use js::glue::JSPrincipalsCallbacks; use js::jsapi::{CallArgs, HandleObject as RawHandleObject, JSContext as RawJSContext, JSObject}; use js::rust::{HandleObject, MutableHandleObject}; @@ -78,14 +76,6 @@ pub trait GlobalScopeHelpers<D: DomTypes> { unsafe fn from_object(obj: *mut JSObject) -> DomRoot<D::GlobalScope>; fn from_reflector(reflector: &impl DomObject, realm: InRealm) -> DomRoot<D::GlobalScope>; - /// # Safety - /// `obj` must point to a valid, non-null JSObject. - /// `cx` must point to a valid, non-null RawJSContext. - unsafe fn from_object_maybe_wrapped( - obj: *mut JSObject, - cx: *mut RawJSContext, - ) -> DomRoot<D::GlobalScope>; - fn origin(&self) -> &MutableOrigin; fn incumbent() -> Option<DomRoot<D::GlobalScope>>; @@ -101,15 +91,6 @@ pub trait DocumentHelpers { fn ensure_safe_to_run_script_or_layout(&self); } -/// Operations that must be invoked from the generated bindings. -pub trait PromiseHelpers<D: crate::DomTypes> { - fn new_resolved( - global: &D::GlobalScope, - cx: JSContext, - value: impl ToJSValConvertible, - ) -> Rc<D::Promise>; -} - pub trait ServoInternalsHelpers { fn is_servo_internal(cx: JSContext, global: HandleObject) -> bool; } diff --git a/components/script_bindings/webidls/Element.webidl b/components/script_bindings/webidls/Element.webidl index 0d2e204ae52..42733b91929 100644 --- a/components/script_bindings/webidls/Element.webidl +++ b/components/script_bindings/webidls/Element.webidl @@ -82,7 +82,7 @@ interface Element : Node { [Throws] undefined insertAdjacentText(DOMString where_, DOMString data); [CEReactions, Throws] - undefined insertAdjacentHTML(DOMString position, DOMString html); + undefined insertAdjacentHTML(DOMString position, (TrustedHTML or DOMString) string); [Throws, Pref="dom_shadowdom_enabled"] ShadowRoot attachShadow(ShadowRootInit init); readonly attribute ShadowRoot? shadowRoot; @@ -122,11 +122,11 @@ partial interface Element { // https://html.spec.whatwg.org/multipage/#dom-parsing-and-serialization partial interface Element { - [CEReactions] undefined setHTMLUnsafe(DOMString html); + [CEReactions, Throws] undefined setHTMLUnsafe((TrustedHTML or DOMString) html); DOMString getHTML(optional GetHTMLOptions options = {}); - [CEReactions, Throws] attribute [LegacyNullToEmptyString] DOMString innerHTML; - [CEReactions, Throws] attribute [LegacyNullToEmptyString] DOMString outerHTML; + [CEReactions, Throws] attribute (TrustedHTML or [LegacyNullToEmptyString] DOMString) innerHTML; + [CEReactions, Throws] attribute (TrustedHTML or [LegacyNullToEmptyString] DOMString) outerHTML; }; dictionary GetHTMLOptions { diff --git a/components/script_bindings/webidls/HTMLIFrameElement.webidl b/components/script_bindings/webidls/HTMLIFrameElement.webidl index 8ba58a20f33..b083f51c0f1 100644 --- a/components/script_bindings/webidls/HTMLIFrameElement.webidl +++ b/components/script_bindings/webidls/HTMLIFrameElement.webidl @@ -9,8 +9,8 @@ interface HTMLIFrameElement : HTMLElement { [CEReactions] attribute USVString src; - [CEReactions] - attribute DOMString srcdoc; + [CEReactions, SetterThrows] + attribute (TrustedHTML or DOMString) srcdoc; [CEReactions] attribute DOMString name; diff --git a/components/script_bindings/webidls/SVGElement.webidl b/components/script_bindings/webidls/SVGElement.webidl index e6bc468d5dc..08bcb4a8c99 100644 --- a/components/script_bindings/webidls/SVGElement.webidl +++ b/components/script_bindings/webidls/SVGElement.webidl @@ -18,7 +18,7 @@ interface SVGElement : Element { //void blur(); }; -//SVGElement includes GlobalEventHandlers; +SVGElement includes GlobalEventHandlers; //SVGElement includes SVGElementInstance; SVGElement includes ElementCSSInlineStyle; SVGElement includes HTMLOrSVGElement; diff --git a/components/script_bindings/webidls/SVGImageElement.webidl b/components/script_bindings/webidls/SVGImageElement.webidl new file mode 100644 index 00000000000..bced6277c5e --- /dev/null +++ b/components/script_bindings/webidls/SVGImageElement.webidl @@ -0,0 +1,16 @@ +/* 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/. */ + +// https://svgwg.org/svg2-draft/embedded.html#InterfaceSVGImageElement +[Exposed=Window, Pref="dom_svg_enabled"] +interface SVGImageElement : SVGGraphicsElement { + //[SameObject] readonly attribute SVGAnimatedLength x; + //[SameObject] readonly attribute SVGAnimatedLength y; + //[SameObject] readonly attribute SVGAnimatedLength width; + //[SameObject] readonly attribute SVGAnimatedLength height; + //[SameObject] readonly attribute SVGAnimatedPreserveAspectRatio preserveAspectRatio; + //attribute DOMString? crossOrigin; +}; + +//SVGImageElement includes SVGURIReference; diff --git a/components/servo/javascript_evaluator.rs b/components/servo/javascript_evaluator.rs new file mode 100644 index 00000000000..41cb5539b05 --- /dev/null +++ b/components/servo/javascript_evaluator.rs @@ -0,0 +1,65 @@ +/* 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::collections::HashMap; + +use base::id::WebViewId; +use constellation_traits::EmbedderToConstellationMessage; +use embedder_traits::{JSValue, JavaScriptEvaluationError, JavaScriptEvaluationId}; + +use crate::ConstellationProxy; + +struct PendingEvaluation { + callback: Box<dyn FnOnce(Result<JSValue, JavaScriptEvaluationError>)>, +} + +pub(crate) struct JavaScriptEvaluator { + current_id: JavaScriptEvaluationId, + constellation_proxy: ConstellationProxy, + pending_evaluations: HashMap<JavaScriptEvaluationId, PendingEvaluation>, +} + +impl JavaScriptEvaluator { + pub(crate) fn new(constellation_proxy: ConstellationProxy) -> Self { + Self { + current_id: JavaScriptEvaluationId(0), + constellation_proxy, + pending_evaluations: Default::default(), + } + } + + fn generate_id(&mut self) -> JavaScriptEvaluationId { + let next_id = JavaScriptEvaluationId(self.current_id.0 + 1); + std::mem::replace(&mut self.current_id, next_id) + } + + pub(crate) fn evaluate( + &mut self, + webview_id: WebViewId, + script: String, + callback: Box<dyn FnOnce(Result<JSValue, JavaScriptEvaluationError>)>, + ) { + let evaluation_id = self.generate_id(); + self.constellation_proxy + .send(EmbedderToConstellationMessage::EvaluateJavaScript( + webview_id, + evaluation_id, + script, + )); + self.pending_evaluations + .insert(evaluation_id, PendingEvaluation { callback }); + } + + pub(crate) fn finish_evaluation( + &mut self, + evaluation_id: JavaScriptEvaluationId, + result: Result<JSValue, JavaScriptEvaluationError>, + ) { + (self + .pending_evaluations + .remove(&evaluation_id) + .expect("Received request to finish unknown JavaScript evaluation.") + .callback)(result) + } +} diff --git a/components/servo/lib.rs b/components/servo/lib.rs index b8210450cd8..d2c65429ba9 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -18,6 +18,7 @@ //! `WindowMethods` trait. mod clipboard_delegate; +mod javascript_evaluator; mod proxies; mod responders; mod servo_delegate; @@ -82,6 +83,7 @@ pub use gleam::gl; use gleam::gl::RENDERER; use ipc_channel::ipc::{self, IpcSender}; use ipc_channel::router::ROUTER; +use javascript_evaluator::JavaScriptEvaluator; pub use keyboard_types::*; use layout::LayoutFactoryImpl; use log::{Log, Metadata, Record, debug, warn}; @@ -196,6 +198,9 @@ pub struct Servo { compositor: Rc<RefCell<IOCompositor>>, constellation_proxy: ConstellationProxy, embedder_receiver: Receiver<EmbedderMsg>, + /// A struct that tracks ongoing JavaScript evaluations and is responsible for + /// calling the callback when the evaluation is complete. + javascript_evaluator: Rc<RefCell<JavaScriptEvaluator>>, /// Tracks whether we are in the process of shutting down, or have shut down. /// This is shared with `WebView`s and the `ServoRenderer`. shutdown_state: Rc<Cell<ShutdownState>>, @@ -487,10 +492,14 @@ impl Servo { opts.debug.convert_mouse_to_touch, ); + let constellation_proxy = ConstellationProxy::new(constellation_chan); Self { delegate: RefCell::new(Rc::new(DefaultServoDelegate)), compositor: Rc::new(RefCell::new(compositor)), - constellation_proxy: ConstellationProxy::new(constellation_chan), + javascript_evaluator: Rc::new(RefCell::new(JavaScriptEvaluator::new( + constellation_proxy.clone(), + ))), + constellation_proxy, embedder_receiver, shutdown_state, webviews: Default::default(), @@ -738,6 +747,11 @@ impl Servo { webview.delegate().request_unload(webview, request); } }, + EmbedderMsg::FinishJavaScriptEvaluation(evaluation_id, result) => { + self.javascript_evaluator + .borrow_mut() + .finish_evaluation(evaluation_id, result); + }, EmbedderMsg::Keyboard(webview_id, keyboard_event) => { if let Some(webview) = self.get_webview_handle(webview_id) { webview diff --git a/components/servo/tests/webview.rs b/components/servo/tests/webview.rs index 89fbe2025a3..41900015b94 100644 --- a/components/servo/tests/webview.rs +++ b/components/servo/tests/webview.rs @@ -11,12 +11,14 @@ mod common; -use std::cell::Cell; +use std::cell::{Cell, RefCell}; use std::rc::Rc; use anyhow::ensure; use common::{ServoTest, run_api_tests}; -use servo::{WebViewBuilder, WebViewDelegate}; +use servo::{ + JSValue, JavaScriptEvaluationError, LoadStatus, WebView, WebViewBuilder, WebViewDelegate, +}; #[derive(Default)] struct WebViewDelegateImpl { @@ -44,6 +46,81 @@ fn test_create_webview(servo_test: &ServoTest) -> Result<(), anyhow::Error> { Ok(()) } +fn evaluate_javascript( + servo_test: &ServoTest, + webview: WebView, + script: impl ToString, +) -> Result<JSValue, JavaScriptEvaluationError> { + let load_webview = webview.clone(); + let _ = servo_test.spin(move || Ok(load_webview.load_status() != LoadStatus::Complete)); + + let saved_result = Rc::new(RefCell::new(None)); + let callback_result = saved_result.clone(); + webview.evaluate_javascript(script, move |result| { + *callback_result.borrow_mut() = Some(result) + }); + + let spin_result = saved_result.clone(); + let _ = servo_test.spin(move || Ok(spin_result.borrow().is_none())); + + (*saved_result.borrow()) + .clone() + .expect("Should have waited until value available") +} + +fn test_evaluate_javascript_basic(servo_test: &ServoTest) -> Result<(), anyhow::Error> { + let delegate = Rc::new(WebViewDelegateImpl::default()); + let webview = WebViewBuilder::new(servo_test.servo()) + .delegate(delegate.clone()) + .build(); + + let result = evaluate_javascript(servo_test, webview.clone(), "undefined"); + ensure!(result == Ok(JSValue::Undefined)); + + let result = evaluate_javascript(servo_test, webview.clone(), "null"); + ensure!(result == Ok(JSValue::Null)); + + let result = evaluate_javascript(servo_test, webview.clone(), "42"); + ensure!(result == Ok(JSValue::Number(42.0))); + + let result = evaluate_javascript(servo_test, webview.clone(), "3 + 4"); + ensure!(result == Ok(JSValue::Number(7.0))); + + let result = evaluate_javascript(servo_test, webview.clone(), "'abc' + 'def'"); + ensure!(result == Ok(JSValue::String("abcdef".into()))); + + let result = evaluate_javascript(servo_test, webview.clone(), "let foo = {blah: 123}; foo"); + ensure!(matches!(result, Ok(JSValue::Object(_)))); + if let Ok(JSValue::Object(values)) = result { + ensure!(values.len() == 1); + ensure!(values.get("blah") == Some(&JSValue::Number(123.0))); + } + + let result = evaluate_javascript(servo_test, webview.clone(), "[1, 2, 3, 4]"); + let expected = JSValue::Array(vec![ + JSValue::Number(1.0), + JSValue::Number(2.0), + JSValue::Number(3.0), + JSValue::Number(4.0), + ]); + ensure!(result == Ok(expected)); + + let result = evaluate_javascript(servo_test, webview.clone(), "window"); + ensure!(matches!(result, Ok(JSValue::Window(..)))); + + let result = evaluate_javascript(servo_test, webview.clone(), "document.body"); + ensure!(matches!(result, Ok(JSValue::Element(..)))); + + let result = evaluate_javascript( + servo_test, + webview.clone(), + "document.body.innerHTML += '<iframe>'; frames[0]", + ); + ensure!(matches!(result, Ok(JSValue::Frame(..)))); + + Ok(()) +} + fn test_create_webview_and_immediately_drop_webview_before_shutdown( servo_test: &ServoTest, ) -> Result<(), anyhow::Error> { @@ -54,6 +131,7 @@ fn test_create_webview_and_immediately_drop_webview_before_shutdown( fn main() { run_api_tests!( test_create_webview, + test_evaluate_javascript_basic, // This test needs to be last, as it tests creating and dropping // a WebView right before shutdown. test_create_webview_and_immediately_drop_webview_before_shutdown diff --git a/components/servo/webview.rs b/components/servo/webview.rs index 95eb6dfd154..10786ad8b69 100644 --- a/components/servo/webview.rs +++ b/components/servo/webview.rs @@ -13,8 +13,8 @@ use compositing_traits::WebViewTrait; use constellation_traits::{EmbedderToConstellationMessage, TraversalDirection}; use dpi::PhysicalSize; use embedder_traits::{ - Cursor, InputEvent, LoadStatus, MediaSessionActionType, ScreenGeometry, Theme, TouchEventType, - ViewportDetails, + Cursor, InputEvent, JSValue, JavaScriptEvaluationError, LoadStatus, MediaSessionActionType, + ScreenGeometry, Theme, TouchEventType, ViewportDetails, }; use euclid::{Point2D, Scale, Size2D}; use servo_geometry::DeviceIndependentPixel; @@ -23,6 +23,7 @@ use webrender_api::ScrollLocation; use webrender_api::units::{DeviceIntPoint, DevicePixel, DeviceRect}; use crate::clipboard_delegate::{ClipboardDelegate, DefaultClipboardDelegate}; +use crate::javascript_evaluator::JavaScriptEvaluator; use crate::webview_delegate::{DefaultWebViewDelegate, WebViewDelegate}; use crate::{ConstellationProxy, Servo, WebRenderDebugOption}; @@ -75,6 +76,7 @@ pub(crate) struct WebViewInner { pub(crate) compositor: Rc<RefCell<IOCompositor>>, pub(crate) delegate: Rc<dyn WebViewDelegate>, pub(crate) clipboard_delegate: Rc<dyn ClipboardDelegate>, + javascript_evaluator: Rc<RefCell<JavaScriptEvaluator>>, rect: DeviceRect, hidpi_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>, @@ -117,9 +119,10 @@ impl WebView { compositor: servo.compositor.clone(), delegate: builder.delegate, clipboard_delegate: Rc::new(DefaultClipboardDelegate), + javascript_evaluator: servo.javascript_evaluator.clone(), rect: DeviceRect::from_origin_and_size(Point2D::origin(), size), hidpi_scale_factor: builder.hidpi_scale_factor, - load_status: LoadStatus::Complete, + load_status: LoadStatus::Started, url: None, status_text: None, page_title: None, @@ -549,6 +552,20 @@ impl WebView { pub fn paint(&self) -> bool { self.inner().compositor.borrow_mut().render() } + + /// Evaluate the specified string of JavaScript code. Once execution is complete or an error + /// occurs, Servo will call `callback`. + pub fn evaluate_javascript<T: ToString>( + &self, + script: T, + callback: impl FnOnce(Result<JSValue, JavaScriptEvaluationError>) + 'static, + ) { + self.inner().javascript_evaluator.borrow_mut().evaluate( + self.id(), + script.to_string(), + Box::new(callback), + ); + } } /// A structure used to expose a view of the [`WebView`] to the Servo diff --git a/components/shared/constellation/from_script_message.rs b/components/shared/constellation/from_script_message.rs index 21665c24e57..3856def660e 100644 --- a/components/shared/constellation/from_script_message.rs +++ b/components/shared/constellation/from_script_message.rs @@ -15,8 +15,8 @@ use base::id::{ use canvas_traits::canvas::{CanvasId, CanvasMsg}; use devtools_traits::{DevtoolScriptControlMsg, ScriptToDevtoolsControlMsg, WorkerId}; use embedder_traits::{ - AnimationState, EmbedderMsg, FocusSequenceNumber, MediaSessionEvent, TouchEventResult, - ViewportDetails, + AnimationState, EmbedderMsg, FocusSequenceNumber, JSValue, JavaScriptEvaluationError, + JavaScriptEvaluationId, MediaSessionEvent, TouchEventResult, ViewportDetails, }; use euclid::default::Size2D as UntypedSize2D; use http::{HeaderMap, Method}; @@ -644,6 +644,11 @@ pub enum ScriptToConstellationMessage { IFrameSizes(Vec<IFrameSizeMsg>), /// Request results from the memory reporter. ReportMemory(IpcSender<MemoryReportResult>), + /// Return the result of the evaluated JavaScript with the given [`JavaScriptEvaluationId`]. + FinishJavaScriptEvaluation( + JavaScriptEvaluationId, + Result<JSValue, JavaScriptEvaluationError>, + ), } impl fmt::Debug for ScriptToConstellationMessage { diff --git a/components/shared/constellation/lib.rs b/components/shared/constellation/lib.rs index 559bc2dd2d1..d85fbe31bdf 100644 --- a/components/shared/constellation/lib.rs +++ b/components/shared/constellation/lib.rs @@ -19,8 +19,8 @@ use base::Epoch; use base::cross_process_instant::CrossProcessInstant; use base::id::{MessagePortId, PipelineId, WebViewId}; use embedder_traits::{ - CompositorHitTestResult, Cursor, InputEvent, MediaSessionActionType, Theme, ViewportDetails, - WebDriverCommandMsg, + CompositorHitTestResult, Cursor, InputEvent, JavaScriptEvaluationId, MediaSessionActionType, + Theme, ViewportDetails, WebDriverCommandMsg, }; use euclid::Vector2D; pub use from_script_message::*; @@ -92,6 +92,9 @@ pub enum EmbedderToConstellationMessage { SetScrollStates(PipelineId, Vec<ScrollState>), /// Notify the constellation that a particular paint metric event has happened for the given pipeline. PaintMetric(PipelineId, PaintMetricEvent), + /// Evaluate a JavaScript string in the context of a `WebView`. When execution is complete or an + /// error is encountered, a correpsonding message will be sent to the embedding layer. + EvaluateJavaScript(WebViewId, JavaScriptEvaluationId, String), } /// A description of a paint metric that is sent from the Servo renderer to the @@ -149,7 +152,7 @@ pub enum TraversalDirection { } /// A task on the <https://html.spec.whatwg.org/multipage/#port-message-queue> -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] pub struct PortMessageTask { /// The origin of this task. pub origin: ImmutableOrigin, diff --git a/components/shared/constellation/structured_data/mod.rs b/components/shared/constellation/structured_data/mod.rs index 41fc05493a2..3fb9d0c5f67 100644 --- a/components/shared/constellation/structured_data/mod.rs +++ b/components/shared/constellation/structured_data/mod.rs @@ -20,7 +20,7 @@ pub use transferable::*; /// A data-holder for serialized data and transferred objects. /// <https://html.spec.whatwg.org/multipage/#structuredserializewithtransfer> -#[derive(Debug, Default, Deserialize, MallocSizeOf, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, MallocSizeOf, Serialize)] pub struct StructuredSerializedData { /// Data serialized by SpiderMonkey. pub serialized: Vec<u8>, @@ -43,6 +43,7 @@ impl StructuredSerializedData { Transferrable::MessagePort => is_field_empty(&self.ports), Transferrable::ReadableStream => is_field_empty(&self.ports), Transferrable::WritableStream => is_field_empty(&self.ports), + Transferrable::TransformStream => is_field_empty(&self.ports), } } diff --git a/components/shared/constellation/structured_data/serializable.rs b/components/shared/constellation/structured_data/serializable.rs index 22370087665..194f0567c51 100644 --- a/components/shared/constellation/structured_data/serializable.rs +++ b/components/shared/constellation/structured_data/serializable.rs @@ -88,7 +88,7 @@ impl Clone for BroadcastMsg { } /// File-based blob -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] pub struct FileBlob { #[ignore_malloc_size_of = "Uuid are hard(not really)"] id: Uuid, @@ -164,7 +164,7 @@ impl BroadcastClone for BlobImpl { } /// The data backing a DOM Blob. -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] pub struct BlobImpl { /// UUID of the blob. blob_id: BlobId, @@ -177,7 +177,7 @@ pub struct BlobImpl { } /// Different backends of Blob -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] pub enum BlobData { /// File-based blob, whose content lives in the net process File(FileBlob), diff --git a/components/shared/constellation/structured_data/transferable.rs b/components/shared/constellation/structured_data/transferable.rs index 7e4fe0e6d2d..528c1e79e65 100644 --- a/components/shared/constellation/structured_data/transferable.rs +++ b/components/shared/constellation/structured_data/transferable.rs @@ -24,9 +24,11 @@ pub enum Transferrable { ReadableStream, /// The `WritableStream` interface. WritableStream, + /// The `TransformStream` interface. + TransformStream, } -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] enum MessagePortState { /// <https://html.spec.whatwg.org/multipage/#detached> Detached, @@ -40,7 +42,7 @@ enum MessagePortState { Disabled(bool), } -#[derive(Debug, Deserialize, MallocSizeOf, Serialize)] +#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] /// The data and logic backing the DOM managed MessagePort. pub struct MessagePortImpl { /// The current state of the port. diff --git a/components/shared/embedder/lib.rs b/components/shared/embedder/lib.rs index c87fa9019ef..e9427fcc719 100644 --- a/components/shared/embedder/lib.rs +++ b/components/shared/embedder/lib.rs @@ -13,8 +13,10 @@ pub mod resources; pub mod user_content_manager; mod webdriver; +use std::collections::HashMap; use std::ffi::c_void; use std::fmt::{Debug, Display, Error, Formatter}; +use std::hash::Hash; use std::path::PathBuf; use std::sync::Arc; @@ -372,6 +374,12 @@ pub enum EmbedderMsg { DeviceIntRect, IpcSender<Option<usize>>, ), + /// Inform the embedding layer that a JavaScript evaluation has + /// finished with the given result. + FinishJavaScriptEvaluation( + JavaScriptEvaluationId, + Result<JSValue, JavaScriptEvaluationError>, + ), } impl Debug for EmbedderMsg { @@ -857,3 +865,59 @@ impl Display for FocusSequenceNumber { Display::fmt(&self.0, f) } } + +/// An identifier for a particular JavaScript evaluation that is used to track the +/// evaluation from the embedding layer to the script layer and then back. +#[derive(Clone, Copy, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct JavaScriptEvaluationId(pub usize); + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub enum JSValue { + Undefined, + Null, + Boolean(bool), + Number(f64), + String(String), + Element(String), + Frame(String), + Window(String), + Array(Vec<JSValue>), + Object(HashMap<String, JSValue>), +} + +impl From<&WebDriverJSValue> for JSValue { + fn from(value: &WebDriverJSValue) -> Self { + match value { + WebDriverJSValue::Undefined => Self::Undefined, + WebDriverJSValue::Null => Self::Null, + WebDriverJSValue::Boolean(value) => Self::Boolean(*value), + WebDriverJSValue::Int(value) => Self::Number(*value as f64), + WebDriverJSValue::Number(value) => Self::Number(*value), + WebDriverJSValue::String(value) => Self::String(value.clone()), + WebDriverJSValue::Element(web_element) => Self::Element(web_element.0.clone()), + WebDriverJSValue::Frame(web_frame) => Self::Frame(web_frame.0.clone()), + WebDriverJSValue::Window(web_window) => Self::Window(web_window.0.clone()), + WebDriverJSValue::ArrayLike(vector) => { + Self::Array(vector.iter().map(Into::into).collect()) + }, + WebDriverJSValue::Object(map) => Self::Object( + map.iter() + .map(|(key, value)| (key.clone(), value.into())) + .collect(), + ), + } + } +} + +#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +pub enum JavaScriptEvaluationError { + /// An internal Servo error prevented the JavaSript evaluation from completing properly. + /// This indicates a bug in Servo. + InternalError, + /// The `WebView` on which this evaluation request was triggered is not ready. This might + /// happen if the `WebView`'s `Document` is changing due to ongoing load events, for instance. + WebViewNotReady, + /// The script executed successfully, but Servo could not serialize the JavaScript return + /// value into a [`JSValue`]. + SerializationError, +} diff --git a/components/shared/net/request.rs b/components/shared/net/request.rs index 9c3693316b0..259132b55c4 100644 --- a/components/shared/net/request.rs +++ b/components/shared/net/request.rs @@ -8,7 +8,7 @@ use base::id::{PipelineId, WebViewId}; use content_security_policy::{self as csp}; use http::header::{AUTHORIZATION, HeaderName}; use http::{HeaderMap, Method}; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory}; use malloc_size_of_derive::MallocSizeOf; use mime::Mime; use serde::{Deserialize, Serialize}; @@ -156,7 +156,7 @@ pub enum BodySource { #[derive(Debug, Deserialize, Serialize)] pub enum BodyChunkResponse { /// A chunk of bytes. - Chunk(Vec<u8>), + Chunk(IpcSharedMemory), /// The body is done. Done, /// There was an error streaming the body, diff --git a/components/shared/script/lib.rs b/components/shared/script/lib.rs index 748c42400a8..29acc51765c 100644 --- a/components/shared/script/lib.rs +++ b/components/shared/script/lib.rs @@ -27,8 +27,8 @@ use crossbeam_channel::{RecvTimeoutError, Sender}; use devtools_traits::ScriptToDevtoolsControlMsg; use embedder_traits::user_content_manager::UserContentManager; use embedder_traits::{ - CompositorHitTestResult, FocusSequenceNumber, InputEvent, MediaSessionActionType, Theme, - ViewportDetails, WebDriverScriptCommand, + CompositorHitTestResult, FocusSequenceNumber, InputEvent, JavaScriptEvaluationId, + MediaSessionActionType, Theme, ViewportDetails, WebDriverScriptCommand, }; use euclid::{Rect, Scale, Size2D, UnknownUnit}; use ipc_channel::ipc::{IpcReceiver, IpcSender}; @@ -245,6 +245,9 @@ pub enum ScriptThreadMessage { /// The compositor scrolled and is updating the scroll states of the nodes in the given /// pipeline via the Constellation. SetScrollStates(PipelineId, Vec<ScrollState>), + /// Evaluate the given JavaScript and return a result via a corresponding message + /// to the Constellation. + EvaluateJavaScript(PipelineId, JavaScriptEvaluationId, String), } impl fmt::Debug for ScriptThreadMessage { diff --git a/components/shared/script_layout/lib.rs b/components/shared/script_layout/lib.rs index 8c5d4edc4e0..1f526b64240 100644 --- a/components/shared/script_layout/lib.rs +++ b/components/shared/script_layout/lib.rs @@ -114,6 +114,7 @@ pub enum LayoutElementType { HTMLTableRowElement, HTMLTableSectionElement, HTMLTextAreaElement, + SVGImageElement, SVGSVGElement, } diff --git a/components/webdriver_server/actions.rs b/components/webdriver_server/actions.rs index 9136e091472..842d9f0dbc2 100644 --- a/components/webdriver_server/actions.rs +++ b/components/webdriver_server/actions.rs @@ -18,7 +18,7 @@ use webdriver::actions::{ }; use webdriver::error::{ErrorStatus, WebDriverError}; -use crate::{Handler, wait_for_script_response}; +use crate::{Handler, WebElement, wait_for_script_response}; // Interval between wheelScroll and pointerMove increments in ms, based on common vsync static POINTERMOVE_INTERVAL: u64 = 17; @@ -393,41 +393,29 @@ impl Handler { let (x, y) = match action.origin { PointerOrigin::Viewport => (x_offset, y_offset), PointerOrigin::Pointer => (start_x + x_offset, start_y + y_offset), - PointerOrigin::Element(ref x) => { - let (sender, receiver) = ipc::channel().unwrap(); - self.browsing_context_script_command( - WebDriverScriptCommand::GetElementInViewCenterPoint(x.to_string(), sender), - ) - .unwrap(); - let response = match wait_for_script_response(receiver) { - Ok(response) => response, - Err(WebDriverError { error, .. }) => return Err(error), - }; - let Ok(Some(point)) = response else { - return Err(ErrorStatus::UnknownError); - }; - point + PointerOrigin::Element(ref web_element) => { + self.get_element_origin_relative_coordinates(web_element)? }, }; // Step 5 - 6 self.check_viewport_bound(x, y)?; - // Step 9 + // Step 7 let duration = match action.duration { Some(duration) => duration, None => tick_duration, }; - // Step 10 + // Step 8 if duration > 0 { thread::sleep(Duration::from_millis(POINTERMOVE_INTERVAL)); } - // Step 11 + // Step 9 - 18 self.perform_pointer_move(source_id, duration, start_x, start_y, x, y, tick_start); - // Step 12 + // Step 19 Ok(()) } @@ -482,6 +470,7 @@ impl Handler { // Step 7.2 let cmd_msg = WebDriverCommandMsg::MouseMoveAction(session.webview_id, x as f32, y as f32); + //TODO: Need Synchronization here before updating `pointer_input_state` self.constellation_chan .send(EmbedderToConstellationMessage::WebDriverCommand(cmd_msg)) .unwrap(); @@ -525,10 +514,13 @@ impl Handler { }; // Step 3 - 4 - // Get coordinates relative to an origin. Origin must be viewport. + // Get coordinates relative to an origin. let (x, y) = match action.origin { PointerOrigin::Viewport => (x_offset, y_offset), - _ => return Err(ErrorStatus::InvalidArgument), + PointerOrigin::Pointer => return Err(ErrorStatus::InvalidArgument), + PointerOrigin::Element(ref web_element) => { + self.get_element_origin_relative_coordinates(web_element)? + }, }; // Step 5 - 6 @@ -658,4 +650,24 @@ impl Handler { Ok(()) } } + + fn get_element_origin_relative_coordinates( + &self, + web_element: &WebElement, + ) -> Result<(i64, i64), ErrorStatus> { + let (sender, receiver) = ipc::channel().unwrap(); + self.browsing_context_script_command(WebDriverScriptCommand::GetElementInViewCenterPoint( + web_element.to_string(), + sender, + )) + .unwrap(); + let response = match wait_for_script_response(receiver) { + Ok(response) => response, + Err(WebDriverError { error, .. }) => return Err(error), + }; + match response? { + Some(point) => Ok(point), + None => Err(ErrorStatus::UnknownError), + } + } } diff --git a/python/servo/try_parser.py b/python/servo/try_parser.py index 9a6b31693b0..0d78599c06f 100644 --- a/python/servo/try_parser.py +++ b/python/servo/try_parser.py @@ -39,11 +39,12 @@ class JobConfig(object): unit_tests: bool = False build_libservo: bool = False bencher: bool = False + build_args: str = "" wpt_args: str = "" number_of_wpt_chunks: int = 20 # These are the fields that must match in between two JobConfigs for them to be able to be # merged. If you modify any of the fields above, make sure to update this line as well. - merge_compatibility_fields: ClassVar[List[str]] = ['workflow', 'profile', 'wpt_args'] + merge_compatibility_fields: ClassVar[List[str]] = ['workflow', 'profile', 'wpt_args', 'build_args'] def merge(self, other: JobConfig) -> bool: """Try to merge another job with this job. Returns True if merging is successful @@ -209,7 +210,8 @@ class TestParser(unittest.TestCase): 'build_libservo': False, 'workflow': 'linux', 'wpt': False, - 'wpt_args': '' + 'wpt_args': '', + 'build_args': '' }] }) @@ -225,7 +227,8 @@ class TestParser(unittest.TestCase): "unit_tests": True, 'build_libservo': False, 'bencher': True, - "wpt_args": "" + "wpt_args": "", + 'build_args': '' }, { "name": "MacOS (Unit Tests)", @@ -236,7 +239,8 @@ class TestParser(unittest.TestCase): "unit_tests": True, 'build_libservo': False, 'bencher': False, - "wpt_args": "" + "wpt_args": "", + 'build_args': '' }, { "name": "Windows (Unit Tests)", @@ -247,7 +251,8 @@ class TestParser(unittest.TestCase): "unit_tests": True, 'build_libservo': False, 'bencher': False, - "wpt_args": "" + "wpt_args": "", + 'build_args': '' }, { "name": "Android", @@ -258,7 +263,8 @@ class TestParser(unittest.TestCase): "unit_tests": False, 'build_libservo': False, 'bencher': False, - "wpt_args": "" + "wpt_args": "", + 'build_args': '' }, { "name": "OpenHarmony", @@ -269,7 +275,8 @@ class TestParser(unittest.TestCase): "unit_tests": False, 'build_libservo': False, 'bencher': False, - "wpt_args": "" + "wpt_args": "", + 'build_args': '' }, { "name": "Lint", @@ -280,7 +287,9 @@ class TestParser(unittest.TestCase): "unit_tests": False, 'build_libservo': False, 'bencher': False, - "wpt_args": ""} + "wpt_args": "", + 'build_args': '' + } ]}) def test_job_merging(self): @@ -295,7 +304,8 @@ class TestParser(unittest.TestCase): 'build_libservo': False, 'workflow': 'linux', 'wpt': True, - 'wpt_args': '' + 'wpt_args': '', + 'build_args': '' }] }) @@ -327,6 +337,11 @@ class TestParser(unittest.TestCase): self.assertFalse(a.merge(b), "Should not merge jobs that run different WPT tests.") self.assertEqual(a, JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True)) + a = JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True) + b = JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True, build_args="--help") + self.assertFalse(a.merge(b), "Should not merge jobs with different build arguments.") + self.assertEqual(a, JobConfig("Linux (Unit Tests)", Workflow.LINUX, unit_tests=True)) + def test_full(self): self.assertDictEqual(json.loads(Config("full").to_json()), json.loads(Config("").to_json())) diff --git a/tests/wpt/meta/MANIFEST.json b/tests/wpt/meta/MANIFEST.json index 277c0f4217d..72aed525d96 100644 --- a/tests/wpt/meta/MANIFEST.json +++ b/tests/wpt/meta/MANIFEST.json @@ -167223,6 +167223,19 @@ {} ] ], + "display-contents-inline-002.html": [ + "f40a34129f348aca37fca83f70598053cf22785b", + [ + null, + [ + [ + "/css/css-display/display-contents-pass-no-red-ref.html", + "==" + ] + ], + {} + ] + ], "display-contents-inline-flex-001.html": [ "43b502731aefbaa348671e32171f0e1eb4bca04b", [ @@ -618423,7 +618436,7 @@ ] ], "table-client-props.html": [ - "4af06d6bf71f0df75d3710ec0906445e943d340d", + "2895bebebbf1868ef60f947d230ecca96dd6e85a", [ null, {} diff --git a/tests/wpt/meta/content-security-policy/reporting/report-clips-sample.https.html.ini b/tests/wpt/meta/content-security-policy/reporting/report-clips-sample.https.html.ini index 783a58399e0..282ea4398ee 100644 --- a/tests/wpt/meta/content-security-policy/reporting/report-clips-sample.https.html.ini +++ b/tests/wpt/meta/content-security-policy/reporting/report-clips-sample.https.html.ini @@ -16,6 +16,3 @@ [AsyncGenerator Function constructor is also clipped.] expected: FAIL - - [Trusted Types violation sample is clipped to 40 characters excluded the sink name.] - expected: FAIL diff --git a/tests/wpt/meta/fetch/metadata/generated/svg-image.https.sub.html.ini b/tests/wpt/meta/fetch/metadata/generated/svg-image.https.sub.html.ini index 5d85add7f82..1e547ae5f21 100644 --- a/tests/wpt/meta/fetch/metadata/generated/svg-image.https.sub.html.ini +++ b/tests/wpt/meta/fetch/metadata/generated/svg-image.https.sub.html.ini @@ -1,70 +1,69 @@ [svg-image.https.sub.html] - expected: TIMEOUT [sec-fetch-site - Same origin no attributes] - expected: TIMEOUT + expected: FAIL [sec-fetch-site - Cross-site no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Same site no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Same-Origin -> Cross-Site -> Same-Origin redirect no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Same-Origin -> Same-Site -> Same-Origin redirect no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Cross-Site -> Same Origin no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Cross-Site -> Same-Site no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Cross-Site -> Cross-Site no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Same-Origin -> Same Origin no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Same-Origin -> Same-Site no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Same-Origin -> Cross-Site no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Same-Site -> Same Origin no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Same-Site -> Same-Site no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Same-Site -> Cross-Site no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - HTTPS downgrade-upgrade no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-mode no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-mode attributes: crossorigin] - expected: NOTRUN + expected: FAIL [sec-fetch-mode attributes: crossorigin=anonymous] - expected: NOTRUN + expected: FAIL [sec-fetch-mode attributes: crossorigin=use-credentials] - expected: NOTRUN + expected: FAIL [sec-fetch-dest no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-user no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-storage-access - Cross-site no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-storage-access - Same site no attributes] - expected: NOTRUN + expected: FAIL diff --git a/tests/wpt/meta/fetch/metadata/generated/svg-image.sub.html.ini b/tests/wpt/meta/fetch/metadata/generated/svg-image.sub.html.ini index dc3a74db79c..cec7a0548b1 100644 --- a/tests/wpt/meta/fetch/metadata/generated/svg-image.sub.html.ini +++ b/tests/wpt/meta/fetch/metadata/generated/svg-image.sub.html.ini @@ -1,55 +1,54 @@ [svg-image.sub.html] - expected: TIMEOUT [sec-fetch-site - Not sent to non-trustworthy same-origin destination no attributes] - expected: TIMEOUT + expected: FAIL [sec-fetch-site - Not sent to non-trustworthy same-site destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - Not sent to non-trustworthy cross-site destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-mode - Not sent to non-trustworthy same-origin destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-mode - Not sent to non-trustworthy same-site destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-mode - Not sent to non-trustworthy cross-site destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-dest - Not sent to non-trustworthy same-origin destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-dest - Not sent to non-trustworthy same-site destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-dest - Not sent to non-trustworthy cross-site destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-user - Not sent to non-trustworthy same-origin destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-user - Not sent to non-trustworthy same-site destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-user - Not sent to non-trustworthy cross-site destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - HTTPS downgrade (header not sent) no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - HTTPS upgrade no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-site - HTTPS downgrade-upgrade no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-storage-access - Not sent to non-trustworthy same-origin destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-storage-access - Not sent to non-trustworthy same-site destination no attributes] - expected: NOTRUN + expected: FAIL [sec-fetch-storage-access - Not sent to non-trustworthy cross-site destination no attributes] - expected: NOTRUN + expected: FAIL diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html.ini index c94490ae5ee..9cf5b2af86a 100644 --- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html.ini +++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-drawImage.html.ini @@ -1,13 +1,13 @@ [createImageBitmap-drawImage.html] expected: ERROR [createImageBitmap from an OffscreenCanvas resized, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a vector HTMLImageElement resized, and drawImage on the created ImageBitmap] expected: FAIL [createImageBitmap from an OffscreenCanvas, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an HTMLCanvasElement, and drawImage on the created ImageBitmap] expected: FAIL @@ -22,34 +22,34 @@ expected: FAIL [createImageBitmap from an ImageData scaled down, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an OffscreenCanvas scaled down, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a bitmap SVGImageElement, and drawImage on the created ImageBitmap] - expected: TIMEOUT + expected: FAIL [createImageBitmap from a bitmap SVGImageElement resized, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an HTMLCanvasElement scaled down, and drawImage on the created ImageBitmap] expected: FAIL [createImageBitmap from a vector SVGImageElement, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an ImageData scaled up, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an HTMLVideoElement with negative sw/sh, and drawImage on the created ImageBitmap] expected: FAIL [createImageBitmap from a bitmap SVGImageElement scaled up, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a vector SVGImageElement resized, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a bitmap HTMLImageElement scaled up, and drawImage on the created ImageBitmap] expected: FAIL @@ -58,10 +58,10 @@ expected: FAIL [createImageBitmap from a vector SVGImageElement scaled down, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a vector SVGImageElement with negative sw/sh, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a bitmap HTMLImageElement scaled down, and drawImage on the created ImageBitmap] expected: FAIL @@ -73,16 +73,16 @@ expected: FAIL [createImageBitmap from a Blob scaled down, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an ImageData resized, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a vector HTMLImageElement scaled down, and drawImage on the created ImageBitmap] expected: FAIL [createImageBitmap from an ImageData, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an HTMLCanvasElement with negative sw/sh, and drawImage on the created ImageBitmap] expected: FAIL @@ -91,16 +91,16 @@ expected: FAIL [createImageBitmap from a vector SVGImageElement scaled up, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an ImageBitmap, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a Blob scaled up, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a bitmap SVGImageElement scaled down, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an HTMLVideoElement scaled up, and drawImage on the created ImageBitmap] expected: FAIL @@ -109,7 +109,7 @@ expected: FAIL [createImageBitmap from a Blob, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an HTMLVideoElement resized, and drawImage on the created ImageBitmap] expected: FAIL @@ -118,31 +118,31 @@ expected: FAIL [createImageBitmap from an ImageBitmap scaled down, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a Blob with negative sw/sh, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a bitmap SVGImageElement with negative sw/sh, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an ImageData with negative sw/sh, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an ImageBitmap scaled up, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an ImageBitmap resized, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an OffscreenCanvas scaled up, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an HTMLCanvasElement scaled up, and drawImage on the created ImageBitmap] expected: FAIL [createImageBitmap from a Blob resized, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an HTMLVideoElement from a data URL, and drawImage on the created ImageBitmap] expected: FAIL @@ -154,10 +154,10 @@ expected: FAIL [createImageBitmap from an ImageBitmap with negative sw/sh, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an OffscreenCanvas with negative sw/sh, and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a vector HTMLImageElement with negative sw/sh, and drawImage on the created ImageBitmap] expected: FAIL diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html.ini index f08e8a2918a..0cb93c5abc9 100644 --- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html.ini +++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-flipY.html.ini @@ -4,7 +4,7 @@ expected: NOTRUN [createImageBitmap from a vector SVGImageElement imageOrientation: "flipY", and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an HTMLCanvasElement imageOrientation: "none", and drawImage on the created ImageBitmap] expected: FAIL @@ -16,7 +16,7 @@ expected: FAIL [createImageBitmap from an OffscreenCanvas imageOrientation: "flipY", and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a vector HTMLImageElement imageOrientation: "flipY", and drawImage on the created ImageBitmap] expected: FAIL @@ -25,7 +25,7 @@ expected: FAIL [createImageBitmap from a Blob imageOrientation: "flipY", and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an HTMLCanvasElement imageOrientation: "flipY", and drawImage on the created ImageBitmap] expected: FAIL @@ -34,7 +34,7 @@ expected: FAIL [createImageBitmap from an ImageData imageOrientation: "flipY", and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a bitmap HTMLImageElement imageOrientation: "flipY", and drawImage on the created ImageBitmap] expected: FAIL @@ -43,7 +43,7 @@ expected: NOTRUN [createImageBitmap from an ImageBitmap imageOrientation: "flipY", and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a vector HTMLImageElement imageOrientation: "none", and drawImage on the created ImageBitmap] expected: FAIL @@ -61,7 +61,7 @@ expected: FAIL [createImageBitmap from a bitmap SVGImageElement imageOrientation: "flipY", and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a bitmap SVGImageElement imageOrientation: "none", and drawImage on the created ImageBitmap] expected: TIMEOUT @@ -82,19 +82,19 @@ expected: FAIL [createImageBitmap from a bitmap SVGImageElement imageOrientation: "from-image", and drawImage on the created ImageBitmap] - expected: TIMEOUT + expected: FAIL [createImageBitmap from a vector SVGImageElement imageOrientation: "from-image", and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an OffscreenCanvas imageOrientation: "from-image", and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an ImageData imageOrientation: "from-image", and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from an ImageBitmap imageOrientation: "from-image", and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL [createImageBitmap from a Blob imageOrientation: "from-image", and drawImage on the created ImageBitmap] - expected: NOTRUN + expected: FAIL diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html.ini index 673fc1e4ffd..aba246039a0 100644 --- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html.ini +++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html.ini @@ -1,5 +1,5 @@ [createImageBitmap-invalid-args.html] - expected: ERROR + expected: CRASH [createImageBitmap with a vector HTMLImageElement source and sw set to 0] expected: FAIL diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini index 210d26f7740..d7226bfed74 100644 --- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini +++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html.ini @@ -1,34 +1,34 @@ [createImageBitmap-origin.sub.html] expected: TIMEOUT [redirected to cross-origin HTMLVideoElement: origin unclear 2dContext.drawImage] - expected: NOTRUN + expected: FAIL [redirected to cross-origin HTMLVideoElement: origin unclear bitmaprenderer.transferFromImageBitmap] - expected: NOTRUN + expected: FAIL [unclean HTMLCanvasElement: origin unclear bitmaprenderer.transferFromImageBitmap] - expected: NOTRUN + expected: FAIL [unclean HTMLCanvasElement: origin unclear getImageData] - expected: NOTRUN + expected: FAIL [cross-origin HTMLVideoElement: origin unclear getImageData] - expected: NOTRUN + expected: FAIL [cross-origin SVGImageElement: origin unclear bitmaprenderer.transferFromImageBitmap] - expected: NOTRUN + expected: FAIL [cross-origin HTMLVideoElement: origin unclear bitmaprenderer.transferFromImageBitmap] - expected: NOTRUN + expected: FAIL [redirected to same-origin HTMLVideoElement: origin unclear getImageData] - expected: NOTRUN + expected: FAIL [cross-origin HTMLImageElement: origin unclear 2dContext.drawImage] expected: FAIL [cross-origin SVGImageElement: origin unclear 2dContext.drawImage] - expected: NOTRUN + expected: FAIL [cross-origin HTMLImageElement: origin unclear getImageData] expected: FAIL @@ -37,28 +37,28 @@ expected: FAIL [redirected to same-origin HTMLVideoElement: origin unclear 2dContext.drawImage] - expected: NOTRUN + expected: FAIL [unclean ImageBitmap: origin unclear bitmaprenderer.transferFromImageBitmap] - expected: NOTRUN + expected: FAIL [redirected to same-origin HTMLVideoElement: origin unclear bitmaprenderer.transferFromImageBitmap] - expected: NOTRUN + expected: FAIL [redirected to cross-origin HTMLVideoElement: origin unclear getImageData] - expected: NOTRUN + expected: FAIL [unclean ImageBitmap: origin unclear getImageData] - expected: NOTRUN + expected: FAIL [unclean HTMLCanvasElement: origin unclear 2dContext.drawImage] - expected: NOTRUN + expected: FAIL [cross-origin HTMLVideoElement: origin unclear 2dContext.drawImage] - expected: NOTRUN + expected: FAIL [unclean ImageBitmap: origin unclear 2dContext.drawImage] - expected: NOTRUN + expected: FAIL [cross-origin SVGImageElement: origin unclear getImageData] - expected: TIMEOUT + expected: FAIL diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini index 7616121487c..4234bcb2133 100644 --- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini +++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-serializable.html.ini @@ -1,7 +1,6 @@ [createImageBitmap-serializable.html] - expected: TIMEOUT [Serialize ImageBitmap created from a vector SVGImageElement] - expected: NOTRUN + expected: FAIL [Serialize ImageBitmap created from an HTMLVideoElement] expected: FAIL @@ -13,25 +12,25 @@ expected: FAIL [Serialize ImageBitmap created from an OffscreenCanvas] - expected: NOTRUN + expected: FAIL [Serialize ImageBitmap created from a vector HTMLImageElement] expected: FAIL [Serialize ImageBitmap created from a Blob] - expected: NOTRUN + expected: FAIL [Serialize ImageBitmap created from a bitmap HTMLImageElement] expected: FAIL [Serializing a non-origin-clean ImageBitmap throws.] - expected: NOTRUN + expected: FAIL [Serialize ImageBitmap created from an ImageData] - expected: NOTRUN + expected: FAIL [Serialize ImageBitmap created from an ImageBitmap] - expected: NOTRUN + expected: FAIL [Serialize ImageBitmap created from a bitmap SVGImageElement] - expected: TIMEOUT + expected: FAIL diff --git a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini index 5d2657041d1..cff84fd3a26 100644 --- a/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini +++ b/tests/wpt/meta/html/canvas/element/manual/imagebitmap/createImageBitmap-transfer.html.ini @@ -1,22 +1,21 @@ [createImageBitmap-transfer.html] - expected: ERROR [Transfer ImageBitmap created from a vector HTMLImageElement] expected: FAIL [Transfer ImageBitmap created from an ImageData] - expected: NOTRUN + expected: FAIL [Transfer ImageBitmap created from a vector SVGImageElement] - expected: NOTRUN + expected: FAIL [Transfer ImageBitmap created from a Blob] - expected: NOTRUN + expected: FAIL [Transfer ImageBitmap created from an HTMLCanvasElement] expected: FAIL [Transfer ImageBitmap created from an OffscreenCanvas] - expected: NOTRUN + expected: FAIL [Transfer ImageBitmap created from a bitmap HTMLImageElement] expected: FAIL @@ -25,13 +24,13 @@ expected: FAIL [Transfer ImageBitmap created from a bitmap SVGImageElement] - expected: TIMEOUT + expected: FAIL [Transfer ImageBitmap created from an ImageBitmap] - expected: NOTRUN + expected: FAIL [Transfer ImageBitmap created from an HTMLVideoElement] expected: FAIL [Transferring a non-origin-clean ImageBitmap throws.] - expected: NOTRUN + expected: FAIL diff --git a/tests/wpt/meta/html/dom/idlharness.https.html.ini b/tests/wpt/meta/html/dom/idlharness.https.html.ini index 311e3abaf15..981cd249aa6 100644 --- a/tests/wpt/meta/html/dom/idlharness.https.html.ini +++ b/tests/wpt/meta/html/dom/idlharness.https.html.ini @@ -5123,231 +5123,6 @@ [External interface: window.external must inherit property "IsSearchProviderInstalled()" with the proper type] expected: FAIL - [SVGElement interface: attribute onabort] - expected: FAIL - - [SVGElement interface: attribute onauxclick] - expected: FAIL - - [SVGElement interface: attribute onbeforeinput] - expected: FAIL - - [SVGElement interface: attribute onbeforematch] - expected: FAIL - - [SVGElement interface: attribute onbeforetoggle] - expected: FAIL - - [SVGElement interface: attribute onblur] - expected: FAIL - - [SVGElement interface: attribute oncancel] - expected: FAIL - - [SVGElement interface: attribute oncanplay] - expected: FAIL - - [SVGElement interface: attribute oncanplaythrough] - expected: FAIL - - [SVGElement interface: attribute onchange] - expected: FAIL - - [SVGElement interface: attribute onclick] - expected: FAIL - - [SVGElement interface: attribute onclose] - expected: FAIL - - [SVGElement interface: attribute oncontextlost] - expected: FAIL - - [SVGElement interface: attribute oncontextmenu] - expected: FAIL - - [SVGElement interface: attribute oncontextrestored] - expected: FAIL - - [SVGElement interface: attribute oncopy] - expected: FAIL - - [SVGElement interface: attribute oncuechange] - expected: FAIL - - [SVGElement interface: attribute oncut] - expected: FAIL - - [SVGElement interface: attribute ondblclick] - expected: FAIL - - [SVGElement interface: attribute ondrag] - expected: FAIL - - [SVGElement interface: attribute ondragend] - expected: FAIL - - [SVGElement interface: attribute ondragenter] - expected: FAIL - - [SVGElement interface: attribute ondragleave] - expected: FAIL - - [SVGElement interface: attribute ondragover] - expected: FAIL - - [SVGElement interface: attribute ondragstart] - expected: FAIL - - [SVGElement interface: attribute ondrop] - expected: FAIL - - [SVGElement interface: attribute ondurationchange] - expected: FAIL - - [SVGElement interface: attribute onemptied] - expected: FAIL - - [SVGElement interface: attribute onended] - expected: FAIL - - [SVGElement interface: attribute onerror] - expected: FAIL - - [SVGElement interface: attribute onfocus] - expected: FAIL - - [SVGElement interface: attribute onformdata] - expected: FAIL - - [SVGElement interface: attribute oninput] - expected: FAIL - - [SVGElement interface: attribute oninvalid] - expected: FAIL - - [SVGElement interface: attribute onkeydown] - expected: FAIL - - [SVGElement interface: attribute onkeypress] - expected: FAIL - - [SVGElement interface: attribute onkeyup] - expected: FAIL - - [SVGElement interface: attribute onload] - expected: FAIL - - [SVGElement interface: attribute onloadeddata] - expected: FAIL - - [SVGElement interface: attribute onloadedmetadata] - expected: FAIL - - [SVGElement interface: attribute onloadstart] - expected: FAIL - - [SVGElement interface: attribute onmousedown] - expected: FAIL - - [SVGElement interface: attribute onmouseenter] - expected: FAIL - - [SVGElement interface: attribute onmouseleave] - expected: FAIL - - [SVGElement interface: attribute onmousemove] - expected: FAIL - - [SVGElement interface: attribute onmouseout] - expected: FAIL - - [SVGElement interface: attribute onmouseover] - expected: FAIL - - [SVGElement interface: attribute onmouseup] - expected: FAIL - - [SVGElement interface: attribute onpaste] - expected: FAIL - - [SVGElement interface: attribute onpause] - expected: FAIL - - [SVGElement interface: attribute onplay] - expected: FAIL - - [SVGElement interface: attribute onplaying] - expected: FAIL - - [SVGElement interface: attribute onprogress] - expected: FAIL - - [SVGElement interface: attribute onratechange] - expected: FAIL - - [SVGElement interface: attribute onreset] - expected: FAIL - - [SVGElement interface: attribute onresize] - expected: FAIL - - [SVGElement interface: attribute onscroll] - expected: FAIL - - [SVGElement interface: attribute onscrollend] - expected: FAIL - - [SVGElement interface: attribute onsecuritypolicyviolation] - expected: FAIL - - [SVGElement interface: attribute onseeked] - expected: FAIL - - [SVGElement interface: attribute onseeking] - expected: FAIL - - [SVGElement interface: attribute onselect] - expected: FAIL - - [SVGElement interface: attribute onslotchange] - expected: FAIL - - [SVGElement interface: attribute onstalled] - expected: FAIL - - [SVGElement interface: attribute onsubmit] - expected: FAIL - - [SVGElement interface: attribute onsuspend] - expected: FAIL - - [SVGElement interface: attribute ontimeupdate] - expected: FAIL - - [SVGElement interface: attribute ontoggle] - expected: FAIL - - [SVGElement interface: attribute onvolumechange] - expected: FAIL - - [SVGElement interface: attribute onwaiting] - expected: FAIL - - [SVGElement interface: attribute onwebkitanimationend] - expected: FAIL - - [SVGElement interface: attribute onwebkitanimationiteration] - expected: FAIL - - [SVGElement interface: attribute onwebkitanimationstart] - expected: FAIL - - [SVGElement interface: attribute onwebkittransitionend] - expected: FAIL - - [SVGElement interface: attribute onwheel] - expected: FAIL - [SVGElement interface: attribute dataset] expected: FAIL @@ -5444,9 +5219,6 @@ [CommandEvent interface: attribute command] expected: FAIL - [SVGElement interface: attribute oncommand] - expected: FAIL - [CanvasRenderingContext2D interface: attribute lang] expected: FAIL diff --git a/tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini b/tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini index cacfdf56967..81329bc1635 100644 --- a/tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini +++ b/tests/wpt/meta/html/semantics/embedded-content/the-canvas-element/security.pattern.fillStyle.sub.html.ini @@ -1,16 +1,16 @@ [security.pattern.fillStyle.sub.html] expected: TIMEOUT [cross-origin SVGImageElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean] - expected: TIMEOUT + expected: FAIL [cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean] - expected: NOTRUN + expected: FAIL [redirected to cross-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean] - expected: NOTRUN + expected: FAIL [redirected to same-origin HTMLVideoElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean] - expected: NOTRUN + expected: TIMEOUT [unclean HTMLCanvasElement: Setting fillStyle to an origin-unclean pattern makes the canvas origin-unclean] expected: NOTRUN diff --git a/tests/wpt/meta/html/webappapis/scripting/events/event-handler-all-global-events.html.ini b/tests/wpt/meta/html/webappapis/scripting/events/event-handler-all-global-events.html.ini index 4d4bbb8a39e..5c286c73da4 100644 --- a/tests/wpt/meta/html/webappapis/scripting/events/event-handler-all-global-events.html.ini +++ b/tests/wpt/meta/html/webappapis/scripting/events/event-handler-all-global-events.html.ini @@ -1,460 +1,16 @@ [event-handler-all-global-events.html] - [onabort: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onabort: the default value must be null] - expected: FAIL - - [onauxclick: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onauxclick: the default value must be null] - expected: FAIL - - [onblur: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onblur: the default value must be null] - expected: FAIL - - [oncancel: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [oncancel: the default value must be null] - expected: FAIL - - [oncanplay: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [oncanplay: the default value must be null] - expected: FAIL - - [oncanplaythrough: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [oncanplaythrough: the default value must be null] - expected: FAIL - - [onchange: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onchange: the default value must be null] - expected: FAIL - - [onclick: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onclick: the default value must be null] - expected: FAIL - - [onclose: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onclose: the default value must be null] - expected: FAIL - - [oncontextlost: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [oncontextlost: the default value must be null] - expected: FAIL - - [oncontextmenu: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [oncontextmenu: the default value must be null] - expected: FAIL - - [oncontextrestored: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [oncontextrestored: the default value must be null] - expected: FAIL - - [oncuechange: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [oncuechange: the default value must be null] - expected: FAIL - - [ondblclick: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [ondblclick: the default value must be null] - expected: FAIL - - [ondrag: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [ondrag: the default value must be null] - expected: FAIL - - [ondragend: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [ondragend: the default value must be null] - expected: FAIL - - [ondragenter: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [ondragenter: the default value must be null] - expected: FAIL - - [ondragleave: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [ondragleave: the default value must be null] - expected: FAIL - - [ondragover: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [ondragover: the default value must be null] - expected: FAIL - - [ondragstart: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [ondragstart: the default value must be null] - expected: FAIL - - [ondrop: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [ondrop: the default value must be null] - expected: FAIL - - [ondurationchange: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [ondurationchange: the default value must be null] - expected: FAIL - - [onemptied: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onemptied: the default value must be null] - expected: FAIL - - [onended: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onended: the default value must be null] - expected: FAIL - - [onfocus: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onfocus: the default value must be null] - expected: FAIL - - [onformdata: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onformdata: the default value must be null] - expected: FAIL - - [oninput: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [oninput: the default value must be null] - expected: FAIL - - [oninvalid: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [oninvalid: the default value must be null] - expected: FAIL - - [onkeydown: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onkeydown: the default value must be null] - expected: FAIL - - [onkeypress: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onkeypress: the default value must be null] - expected: FAIL - - [onkeyup: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onkeyup: the default value must be null] - expected: FAIL - - [onload: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onload: the default value must be null] - expected: FAIL - - [onloadeddata: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onloadeddata: the default value must be null] - expected: FAIL - - [onloadedmetadata: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onloadedmetadata: the default value must be null] - expected: FAIL - - [onloadstart: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onloadstart: the default value must be null] - expected: FAIL - - [onmousedown: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onmousedown: the default value must be null] - expected: FAIL - - [onmouseenter: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onmouseenter: the default value must be null] - expected: FAIL - - [onmouseleave: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onmouseleave: the default value must be null] - expected: FAIL - - [onmousemove: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onmousemove: the default value must be null] - expected: FAIL - - [onmouseout: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onmouseout: the default value must be null] - expected: FAIL - - [onmouseover: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onmouseover: the default value must be null] - expected: FAIL - - [onmouseup: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onmouseup: the default value must be null] - expected: FAIL - - [onpause: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onpause: the default value must be null] - expected: FAIL - - [onplay: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onplay: the default value must be null] - expected: FAIL - - [onplaying: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onplaying: the default value must be null] - expected: FAIL - - [onprogress: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onprogress: the default value must be null] - expected: FAIL - - [onratechange: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onratechange: the default value must be null] - expected: FAIL - - [onreset: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onreset: the default value must be null] - expected: FAIL - - [onresize: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onresize: the default value must be null] - expected: FAIL - - [onscroll: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onscroll: the default value must be null] - expected: FAIL - - [onsecuritypolicyviolation: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onsecuritypolicyviolation: the default value must be null] - expected: FAIL - - [onseeked: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onseeked: the default value must be null] - expected: FAIL - - [onseeking: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onseeking: the default value must be null] - expected: FAIL - - [onselect: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onselect: the default value must be null] - expected: FAIL - - [onslotchange: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onslotchange: the default value must be null] - expected: FAIL - - [onstalled: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onstalled: the default value must be null] - expected: FAIL - - [onsubmit: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onsubmit: the default value must be null] - expected: FAIL - - [onsuspend: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onsuspend: the default value must be null] - expected: FAIL - - [ontimeupdate: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [ontimeupdate: the default value must be null] - expected: FAIL - - [ontoggle: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [ontoggle: the default value must be null] - expected: FAIL - - [onvolumechange: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onvolumechange: the default value must be null] - expected: FAIL - - [onwaiting: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onwaiting: the default value must be null] - expected: FAIL - - [onwebkitanimationend: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onwebkitanimationend: the default value must be null] - expected: FAIL - [onwebkitanimationend: the content attribute must execute when an event is dispatched] expected: FAIL - [onwebkitanimationiteration: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onwebkitanimationiteration: the default value must be null] - expected: FAIL - [onwebkitanimationiteration: the content attribute must execute when an event is dispatched] expected: FAIL - [onwebkitanimationstart: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onwebkitanimationstart: the default value must be null] - expected: FAIL - [onwebkitanimationstart: the content attribute must execute when an event is dispatched] expected: FAIL - [onwebkittransitionend: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onwebkittransitionend: the default value must be null] - expected: FAIL - [onwebkittransitionend: the content attribute must execute when an event is dispatched] expected: FAIL - [onwheel: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onwheel: the default value must be null] - expected: FAIL - - [onbeforeinput: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onbeforeinput: the default value must be null] - expected: FAIL - - [onbeforematch: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onbeforematch: the default value must be null] - expected: FAIL - - [onscrollend: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onscrollend: the default value must be null] - expected: FAIL - - [oncopy: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [oncopy: the default value must be null] - expected: FAIL - - [oncut: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [oncut: the default value must be null] - expected: FAIL - - [onpaste: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onpaste: the default value must be null] - expected: FAIL - - [onbeforetoggle: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [onbeforetoggle: the default value must be null] - expected: FAIL - [onwebkitanimationend: dispatching an Event at a <meta> element must trigger element.onwebkitanimationend] expected: FAIL @@ -466,9 +22,3 @@ [onwebkittransitionend: dispatching an Event at a <meta> element must trigger element.onwebkittransitionend] expected: FAIL - - [oncommand: must be on the appropriate locations for GlobalEventHandlers] - expected: FAIL - - [oncommand: the default value must be null] - expected: FAIL diff --git a/tests/wpt/meta/streams/transferable/transfer-with-messageport.window.js.ini b/tests/wpt/meta/streams/transferable/transfer-with-messageport.window.js.ini deleted file mode 100644 index a2502281be7..00000000000 --- a/tests/wpt/meta/streams/transferable/transfer-with-messageport.window.js.ini +++ /dev/null @@ -1,9 +0,0 @@ -[transfer-with-messageport.window.html] - [Transferring a MessagePort with a TransformStream should set `.ports`] - expected: FAIL - - [Transferring a MessagePort with a TransformStream should set `.ports`, advanced] - expected: FAIL - - [Transferring a MessagePort with multiple streams should set `.ports`] - expected: FAIL diff --git a/tests/wpt/meta/streams/transferable/transform-stream.html.ini b/tests/wpt/meta/streams/transferable/transform-stream.html.ini index af9a1d42ae7..a5097f80874 100644 --- a/tests/wpt/meta/streams/transferable/transform-stream.html.ini +++ b/tests/wpt/meta/streams/transferable/transform-stream.html.ini @@ -1,6 +1,3 @@ [transform-stream.html] - [window.postMessage should be able to transfer a TransformStream] - expected: FAIL - [piping through transferred transforms should work] expected: FAIL diff --git a/tests/wpt/meta/trusted-types/HTMLElement-generic.html.ini b/tests/wpt/meta/trusted-types/HTMLElement-generic.html.ini deleted file mode 100644 index 8ed1a875db3..00000000000 --- a/tests/wpt/meta/trusted-types/HTMLElement-generic.html.ini +++ /dev/null @@ -1,24 +0,0 @@ -[HTMLElement-generic.html] - [TT enabled: div.innerHTML\n = String on a\n connected element\n ] - expected: FAIL - - [TT enabled: div.innerHTML\n = String on a\n non-connected element\n ] - expected: FAIL - - [TT enabled: iframe.srcdoc\n = String on a\n connected element\n ] - expected: FAIL - - [TT enabled: iframe.srcdoc\n = String on a\n non-connected element\n ] - expected: FAIL - - [TT enabled: div.innerHTML\n = String on a\n connected element\n after removing the "require-trusted-types-for 'script' directive] - expected: FAIL - - [TT enabled: div.innerHTML\n = String on a\n non-connected element\n after removing the "require-trusted-types-for 'script' directive] - expected: FAIL - - [TT enabled: iframe.srcdoc\n = String on a\n connected element\n after removing the "require-trusted-types-for 'script' directive] - expected: FAIL - - [TT enabled: iframe.srcdoc\n = String on a\n non-connected element\n after removing the "require-trusted-types-for 'script' directive] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.html.ini deleted file mode 100644 index 1306015ff46..00000000000 --- a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-insertAdjacentHTML.html.ini +++ /dev/null @@ -1,12 +0,0 @@ -[block-string-assignment-to-Element-insertAdjacentHTML.html] - [`insertAdjacentHTML(string)` throws.] - expected: FAIL - - [`insertAdjacentHTML(string)` still throws TypeError when position invalid.] - expected: FAIL - - [`insertAdjacentHTML(null)` throws.] - expected: FAIL - - [`insertAdjacentHTML(string)` assigned via default policy (successful HTML transformation).] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-outerHTML.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-outerHTML.html.ini deleted file mode 100644 index e5abefcc766..00000000000 --- a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-outerHTML.html.ini +++ /dev/null @@ -1,15 +0,0 @@ -[block-string-assignment-to-Element-outerHTML.html] - [`outerHTML = string` throws.] - expected: FAIL - - [`outerHTML = string` throws TypeError even when parent is a document.] - expected: FAIL - - [`outerHTML = null` throws.] - expected: FAIL - - [`outerHTML = string` assigned via default policy (successful HTML transformation).] - expected: FAIL - - [`outerHTML = null` assigned via default policy does not throw] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setAttribute.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setAttribute.html.ini index 7ad472ef325..0df917b86be 100644 --- a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setAttribute.html.ini +++ b/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setAttribute.html.ini @@ -20,9 +20,6 @@ [div.onclick's mutationobservers receive the default policy's value.] expected: FAIL - [iframe.srcdoc accepts string and null after default policy was created.] - expected: FAIL - [div.onclick accepts string and null after default policy was created.] expected: FAIL diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setHTMLUnsafe.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setHTMLUnsafe.html.ini deleted file mode 100644 index e6b59ba6714..00000000000 --- a/tests/wpt/meta/trusted-types/block-string-assignment-to-Element-setHTMLUnsafe.html.ini +++ /dev/null @@ -1,12 +0,0 @@ -[block-string-assignment-to-Element-setHTMLUnsafe.html] - [`element.setHTMLUnsafe(string)` throws.] - expected: FAIL - - [`element.setHTMLUnsafe(null)` throws.] - expected: FAIL - - [`element.setHTMLUnsafe(string)` assigned via default policy (successful HTML transformation).] - expected: FAIL - - [`element.setHTMLUnsafe(string)` assigned via default policy does not throw] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLElement-generic.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLElement-generic.html.ini deleted file mode 100644 index ed3a70b31ab..00000000000 --- a/tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLElement-generic.html.ini +++ /dev/null @@ -1,12 +0,0 @@ -[block-string-assignment-to-HTMLElement-generic.html] - [div.innerHTML accepts only TrustedHTML] - expected: FAIL - - [iframe.srcdoc accepts only TrustedHTML] - expected: FAIL - - [div.innerHTML accepts string and null after default policy was created] - expected: FAIL - - [iframe.srcdoc accepts string and null after default policy was created] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLIFrameElement-srcdoc.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLIFrameElement-srcdoc.html.ini deleted file mode 100644 index e7747a96001..00000000000 --- a/tests/wpt/meta/trusted-types/block-string-assignment-to-HTMLIFrameElement-srcdoc.html.ini +++ /dev/null @@ -1,9 +0,0 @@ -[block-string-assignment-to-HTMLIFrameElement-srcdoc.html] - [`iframe.srcdoc = string` throws.] - expected: FAIL - - [`iframe.srcdoc = null` throws.] - expected: FAIL - - [`iframe.srcdoc = string` assigned via default policy (successful HTML transformation).] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/block-string-assignment-to-text-and-url-sinks.html.ini b/tests/wpt/meta/trusted-types/block-string-assignment-to-text-and-url-sinks.html.ini deleted file mode 100644 index 1e9f6e44f44..00000000000 --- a/tests/wpt/meta/trusted-types/block-string-assignment-to-text-and-url-sinks.html.ini +++ /dev/null @@ -1,9 +0,0 @@ -[block-string-assignment-to-text-and-url-sinks.html] - [Setting HTMLDivElement.innerHTML to a plain string] - expected: FAIL - - [Setting HTMLScriptElement.innerHTML to a plain string] - expected: FAIL - - [Setting SVGScriptElement.innerHTML to a plain string] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/default-policy.html.ini b/tests/wpt/meta/trusted-types/default-policy.html.ini deleted file mode 100644 index cf57031ddbe..00000000000 --- a/tests/wpt/meta/trusted-types/default-policy.html.ini +++ /dev/null @@ -1,22 +0,0 @@ -[default-policy.html] - expected: OK - [Count SecurityPolicyViolation events.] - expected: FAIL - - [div.innerHTML no default policy] - expected: FAIL - - [div.innerHTML default] - expected: FAIL - - [div.innerHTML null] - expected: FAIL - - [div.innerHTML throw] - expected: FAIL - - [div.innerHTML undefined] - expected: FAIL - - [div.innerHTML typeerror] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/empty-default-policy.html.ini b/tests/wpt/meta/trusted-types/empty-default-policy.html.ini deleted file mode 100644 index c3f34522557..00000000000 --- a/tests/wpt/meta/trusted-types/empty-default-policy.html.ini +++ /dev/null @@ -1,7 +0,0 @@ -[empty-default-policy.html] - expected: OK - [Count SecurityPolicyViolation events.] - expected: FAIL - - [div.innerHTML default] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/require-trusted-types-for-TypeError-belongs-to-the-global-object-realm.html.ini b/tests/wpt/meta/trusted-types/require-trusted-types-for-TypeError-belongs-to-the-global-object-realm.html.ini index df0dfe485ed..63099aaaaff 100644 --- a/tests/wpt/meta/trusted-types/require-trusted-types-for-TypeError-belongs-to-the-global-object-realm.html.ini +++ b/tests/wpt/meta/trusted-types/require-trusted-types-for-TypeError-belongs-to-the-global-object-realm.html.ini @@ -1,6 +1,3 @@ [require-trusted-types-for-TypeError-belongs-to-the-global-object-realm.html] - [Setting innerHTML on a node inserted by the parser.] - expected: FAIL - [Setting innerHTML on a node adopted from a subframe.] expected: FAIL diff --git a/tests/wpt/meta/trusted-types/require-trusted-types-for-report-only.html.ini b/tests/wpt/meta/trusted-types/require-trusted-types-for-report-only.html.ini deleted file mode 100644 index 39ec281d5f2..00000000000 --- a/tests/wpt/meta/trusted-types/require-trusted-types-for-report-only.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[require-trusted-types-for-report-only.html] - [Require trusted types for 'script' block create HTML.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/require-trusted-types-for.html.ini b/tests/wpt/meta/trusted-types/require-trusted-types-for.html.ini deleted file mode 100644 index 38d5f9eb35a..00000000000 --- a/tests/wpt/meta/trusted-types/require-trusted-types-for.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[require-trusted-types-for.html] - [Require trusted types for 'script' block create HTML.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/should-sink-type-mismatch-violation-be-blocked-by-csp-001.html.ini b/tests/wpt/meta/trusted-types/should-sink-type-mismatch-violation-be-blocked-by-csp-001.html.ini index 57cd759f8bf..d3218647e91 100644 --- a/tests/wpt/meta/trusted-types/should-sink-type-mismatch-violation-be-blocked-by-csp-001.html.ini +++ b/tests/wpt/meta/trusted-types/should-sink-type-mismatch-violation-be-blocked-by-csp-001.html.ini @@ -1,20 +1,5 @@ [should-sink-type-mismatch-violation-be-blocked-by-csp-001.html] expected: TIMEOUT - [Multiple enforce require-trusted-types-for directives.] - expected: FAIL - - [Multiple report-only require-trusted-types-for directives.] - expected: FAIL - - [One violated report-only require-trusted-types-for directive followed by multiple enforce directives] - expected: FAIL - - [One violated enforce require-trusted-types-for directive followed by multiple report-only directives] - expected: FAIL - - [Mixing enforce and report-only require-trusted-types-for directives.] - expected: FAIL - [directive "require-trusted-types-for 'script'%09'script'%0A'script'%0C'script'%0D'script'%20'script'" (required-ascii-whitespace)] expected: TIMEOUT @@ -33,9 +18,6 @@ [directive "require-trusted-types-for unquoted-invalid 'script' also-unquoted-invalid (unknown sink group)] expected: NOTRUN - [directive "require-trusted-types-for 'invalid'%09'script'" (required-ascii-whitespace)] - expected: FAIL - [directive "require-trusted-types-for 'invalid'%0A%20'script'" (required-ascii-whitespace)] expected: TIMEOUT diff --git a/tests/wpt/meta/trusted-types/trusted-types-createHTMLDocument.html.ini b/tests/wpt/meta/trusted-types/trusted-types-createHTMLDocument.html.ini deleted file mode 100644 index cca7dc42f2b..00000000000 --- a/tests/wpt/meta/trusted-types/trusted-types-createHTMLDocument.html.ini +++ /dev/null @@ -1,24 +0,0 @@ -[trusted-types-createHTMLDocument.html] - [Trusted Type instances created in the main doc can be used. (document)] - expected: FAIL - - [Trusted Type instances created in the main doc can be used. (createHTMLDocument)] - expected: FAIL - - [Trusted Type instances created in the main doc can be used. (DOMParser)] - expected: FAIL - - [Trusted Type instances created in the main doc can be used. (XHR)] - expected: FAIL - - [Default policy applies. (document)] - expected: FAIL - - [Default policy applies. (createHTMLDocument)] - expected: FAIL - - [Default policy applies. (DOMParser)] - expected: FAIL - - [Default policy applies. (XHR)] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/trusted-types-report-only.html.ini b/tests/wpt/meta/trusted-types/trusted-types-report-only.html.ini deleted file mode 100644 index 253b126c18f..00000000000 --- a/tests/wpt/meta/trusted-types/trusted-types-report-only.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[trusted-types-report-only.html] - [Trusted Type violation report-only: assign string to html] - expected: FAIL - - [Trusted Type violation report-only: assign string to script content] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-innerHTML.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-innerHTML.html.ini deleted file mode 100644 index ae31cf41b62..00000000000 --- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-innerHTML.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[trusted-types-reporting-for-Element-innerHTML.html] - [Violation report for plain string.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-insertAdjacentHTML.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-insertAdjacentHTML.html.ini deleted file mode 100644 index ea9822131e6..00000000000 --- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-insertAdjacentHTML.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[trusted-types-reporting-for-Element-insertAdjacentHTML.html] - [Violation report for plain string.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-outerHTML.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-outerHTML.html.ini deleted file mode 100644 index ccf4d385ddc..00000000000 --- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-outerHTML.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[trusted-types-reporting-for-Element-outerHTML.html] - [Violation report for plain string.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-setHTMLUnsafe.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-setHTMLUnsafe.html.ini deleted file mode 100644 index a288f0fc52b..00000000000 --- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-Element-setHTMLUnsafe.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[trusted-types-reporting-for-Element-setHTMLUnsafe.html] - [Violation report for plain string.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLIFrameElement-srcdoc.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLIFrameElement-srcdoc.html.ini deleted file mode 100644 index 9f2a73c0900..00000000000 --- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLIFrameElement-srcdoc.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[trusted-types-reporting-for-HTMLIFrameElement-srcdoc.html] - [Violation report for plain string.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLScriptElement-innerHTML.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLScriptElement-innerHTML.html.ini deleted file mode 100644 index 74b6b2803cd..00000000000 --- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-HTMLScriptElement-innerHTML.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[trusted-types-reporting-for-HTMLScriptElement-innerHTML.html] - [Violation report for plain string.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-SVGScriptElement-innerHTML.html.ini b/tests/wpt/meta/trusted-types/trusted-types-reporting-for-SVGScriptElement-innerHTML.html.ini deleted file mode 100644 index 55b972f6001..00000000000 --- a/tests/wpt/meta/trusted-types/trusted-types-reporting-for-SVGScriptElement-innerHTML.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[trusted-types-reporting-for-SVGScriptElement-innerHTML.html] - [Violation report for plain string.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/trusted-types-sandbox-allow-scripts.html.ini b/tests/wpt/meta/trusted-types/trusted-types-sandbox-allow-scripts.html.ini deleted file mode 100644 index 7053da4f6d0..00000000000 --- a/tests/wpt/meta/trusted-types/trusted-types-sandbox-allow-scripts.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[trusted-types-sandbox-allow-scripts.html] - [Default Trusted Types policy in a sandboxed page with allow-scripts.] - expected: FAIL diff --git a/tests/wpt/meta/trusted-types/trusted-types-source-file-path.html.ini b/tests/wpt/meta/trusted-types/trusted-types-source-file-path.html.ini deleted file mode 100644 index a05cfef0db5..00000000000 --- a/tests/wpt/meta/trusted-types/trusted-types-source-file-path.html.ini +++ /dev/null @@ -1,9 +0,0 @@ -[trusted-types-source-file-path.html] - [same-document script] - expected: FAIL - - [same-origin script] - expected: FAIL - - [cross-origin script] - expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 033a13106ac..2797b63f2ae 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -13582,7 +13582,7 @@ ] ], "interfaces.https.html": [ - "72918e837726b58740a491a9223eeeb625055ae5", + "eee8c799727b91e00b512795756b693a5f121f86", [ null, {} diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html index 72918e83772..eee8c799727 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.https.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.https.html @@ -315,6 +315,7 @@ test_interfaces([ "SubtleCrypto", "SVGElement", "SVGGraphicsElement", + "SVGImageElement", "SVGRect", "SVGSVGElement", "Text", 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 @@ +<!DOCTYPE html> +<meta charset="utf-8"> +<title>CSS Display: display:contents in inline layout should affect style of descendants</title> +<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com"> +<link rel="author" title="Oriol Brufau" href="mailto:obrufau@igalia.com"> +<link rel="help" href="https://drafts.csswg.org/css-display-3/#valdef-display-contents"> +<link rel="match" href="display-contents-pass-no-red-ref.html"> +<style> + #contents { + display: contents; + color: black; + } + .red { color: red; } +</style> +<p>You should see the word PASS and no red below.</p> +<span> + P<span class="red"><div id="contents">AS</div></span>S +</span> diff --git a/tests/wpt/tests/css/cssom-view/table-client-props.html b/tests/wpt/tests/css/cssom-view/table-client-props.html index 4af06d6bf71..2895bebebbf 100644 --- a/tests/wpt/tests/css/cssom-view/table-client-props.html +++ b/tests/wpt/tests/css/cssom-view/table-client-props.html @@ -43,6 +43,19 @@ <tr><td>`, 26, 34, "Table with collapsed border" ], + [ `<div style="display: flex"> + <table style="width: 20px; height: 30px; + border-width: 1px 2px 3px 4px; border-style: solid; + border-collapse: separate; box-sizing: content-box">`, + 26, 34, + "Flex-level table with separated border" ], + [ `<div style="display: flex"> + <table style="width: 20px; height: 30px; + border-width: 2px 4px 6px 8px; border-style: solid; + border-collapse: collapse; box-sizing: content-box"> + <tr><td>`, + 26, 34, + "Flex-level table with collapsed border" ], [ `<table> <caption style="width: 40px; height: 50px; padding: 1px 2px 3px 4px">`, 46, 54, |